summaryrefslogtreecommitdiff
path: root/vendor/oojs/oojs-ui
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/oojs/oojs-ui')
-rw-r--r--vendor/oojs/oojs-ui/.csscomb.json24
-rw-r--r--vendor/oojs/oojs-ui/.csslintrc11
-rw-r--r--vendor/oojs/oojs-ui/.mailmap18
-rw-r--r--vendor/oojs/oojs-ui/.npmignore2
-rw-r--r--vendor/oojs/oojs-ui/AUTHORS.txt34
-rw-r--r--vendor/oojs/oojs-ui/Doxyfile33
-rw-r--r--vendor/oojs/oojs-ui/Gemfile.lock25
-rw-r--r--vendor/oojs/oojs-ui/Gruntfile.js401
-rw-r--r--vendor/oojs/oojs-ui/History.md668
-rw-r--r--vendor/oojs/oojs-ui/LICENSE-MIT20
-rw-r--r--vendor/oojs/oojs-ui/README.md79
-rw-r--r--vendor/oojs/oojs-ui/bin/doccomparer.rb165
-rw-r--r--vendor/oojs/oojs-ui/bin/docparser.rb243
-rw-r--r--vendor/oojs/oojs-ui/bin/generate-JSPHP-for-karma.php50
-rw-r--r--vendor/oojs/oojs-ui/bin/testsuitegenerator.rb146
-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
-rw-r--r--vendor/oojs/oojs-ui/composer.json24
-rw-r--r--vendor/oojs/oojs-ui/demos/demo.js504
-rw-r--r--vendor/oojs/oojs-ui/demos/index.html39
-rw-r--r--vendor/oojs/oojs-ui/demos/infusion.js66
-rw-r--r--vendor/oojs/oojs-ui/demos/pages/dialogs.js602
-rw-r--r--vendor/oojs/oojs-ui/demos/pages/icons.js295
-rw-r--r--vendor/oojs/oojs-ui/demos/pages/toolbars.js338
-rw-r--r--vendor/oojs/oojs-ui/demos/pages/widgets.js1428
-rw-r--r--vendor/oojs/oojs-ui/demos/styles/demo.css250
-rw-r--r--vendor/oojs/oojs-ui/demos/widgets.php830
-rw-r--r--vendor/oojs/oojs-ui/i18n/ace.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/af.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/am.json7
-rw-r--r--vendor/oojs/oojs-ui/i18n/ar.json28
-rw-r--r--vendor/oojs/oojs-ui/i18n/arc.json7
-rw-r--r--vendor/oojs/oojs-ui/i18n/arq.json16
-rw-r--r--vendor/oojs/oojs-ui/i18n/ast.json18
-rw-r--r--vendor/oojs/oojs-ui/i18n/awa.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/az.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/ba.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/bcc.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/bcl.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/be-tarask.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/be.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/bg.json12
-rw-r--r--vendor/oojs/oojs-ui/i18n/bn.json26
-rw-r--r--vendor/oojs/oojs-ui/i18n/br.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/bs.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/ca.json28
-rw-r--r--vendor/oojs/oojs-ui/i18n/ce.json17
-rw-r--r--vendor/oojs/oojs-ui/i18n/ckb.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/co.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/crh-cyrl.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/crh-latn.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/cs.json29
-rw-r--r--vendor/oojs/oojs-ui/i18n/cu.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/cy.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/da.json16
-rw-r--r--vendor/oojs/oojs-ui/i18n/de.json28
-rw-r--r--vendor/oojs/oojs-ui/i18n/diq.json16
-rw-r--r--vendor/oojs/oojs-ui/i18n/dsb.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/egl.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/el.json26
-rw-r--r--vendor/oojs/oojs-ui/i18n/eml.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/en.json31
-rw-r--r--vendor/oojs/oojs-ui/i18n/eo.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/es.json33
-rw-r--r--vendor/oojs/oojs-ui/i18n/et.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/eu.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/fa.json29
-rw-r--r--vendor/oojs/oojs-ui/i18n/fi.json31
-rw-r--r--vendor/oojs/oojs-ui/i18n/fo.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/fr.json45
-rw-r--r--vendor/oojs/oojs-ui/i18n/frr.json12
-rw-r--r--vendor/oojs/oojs-ui/i18n/fur.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/fy.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/gd.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/gl.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/gu.json17
-rw-r--r--vendor/oojs/oojs-ui/i18n/he.json30
-rw-r--r--vendor/oojs/oojs-ui/i18n/hi.json24
-rw-r--r--vendor/oojs/oojs-ui/i18n/hr.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/hsb.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/hu.json23
-rw-r--r--vendor/oojs/oojs-ui/i18n/hy.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/ia.json16
-rw-r--r--vendor/oojs/oojs-ui/i18n/id.json26
-rw-r--r--vendor/oojs/oojs-ui/i18n/ie.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/ilo.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/is.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/it.json30
-rw-r--r--vendor/oojs/oojs-ui/i18n/ja.json24
-rw-r--r--vendor/oojs/oojs-ui/i18n/jv.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/ka.json26
-rw-r--r--vendor/oojs/oojs-ui/i18n/kk-cyrl.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/km.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/kn.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/ko.json25
-rw-r--r--vendor/oojs/oojs-ui/i18n/krc.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/ksh.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/ku-latn.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/kw.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/ky.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/lb.json25
-rw-r--r--vendor/oojs/oojs-ui/i18n/lmo.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/lt.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/lv.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/lzh.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/mg.json7
-rw-r--r--vendor/oojs/oojs-ui/i18n/min.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/mk.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/ml.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/mr.json15
-rw-r--r--vendor/oojs/oojs-ui/i18n/ms.json17
-rw-r--r--vendor/oojs/oojs-ui/i18n/nap.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/nb.json25
-rw-r--r--vendor/oojs/oojs-ui/i18n/nds-nl.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/nds.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/ne.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/nl.json35
-rw-r--r--vendor/oojs/oojs-ui/i18n/nn.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/oc.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/om.json20
-rw-r--r--vendor/oojs/oojs-ui/i18n/or.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/pa.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/pfl.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/pl.json34
-rw-r--r--vendor/oojs/oojs-ui/i18n/pms.json12
-rw-r--r--vendor/oojs/oojs-ui/i18n/ps.json18
-rw-r--r--vendor/oojs/oojs-ui/i18n/pt-br.json18
-rw-r--r--vendor/oojs/oojs-ui/i18n/pt.json28
-rw-r--r--vendor/oojs/oojs-ui/i18n/qqq.json35
-rw-r--r--vendor/oojs/oojs-ui/i18n/qu.json12
-rw-r--r--vendor/oojs/oojs-ui/i18n/ro.json23
-rw-r--r--vendor/oojs/oojs-ui/i18n/roa-tara.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/ru.json34
-rw-r--r--vendor/oojs/oojs-ui/i18n/sah.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/scn.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/sco.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/sh.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/si.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/sk.json12
-rw-r--r--vendor/oojs/oojs-ui/i18n/sl.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/sq.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/sr-ec.json21
-rw-r--r--vendor/oojs/oojs-ui/i18n/sr-el.json19
-rw-r--r--vendor/oojs/oojs-ui/i18n/sv.json29
-rw-r--r--vendor/oojs/oojs-ui/i18n/sw.json15
-rw-r--r--vendor/oojs/oojs-ui/i18n/ta.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/te.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/tg-cyrl.json11
-rw-r--r--vendor/oojs/oojs-ui/i18n/th.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/tl.json14
-rw-r--r--vendor/oojs/oojs-ui/i18n/tr.json27
-rw-r--r--vendor/oojs/oojs-ui/i18n/tt-cyrl.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/ug-arab.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/uk.json33
-rw-r--r--vendor/oojs/oojs-ui/i18n/uz.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/vec.json13
-rw-r--r--vendor/oojs/oojs-ui/i18n/vi.json22
-rw-r--r--vendor/oojs/oojs-ui/i18n/vo.json8
-rw-r--r--vendor/oojs/oojs-ui/i18n/wuu.json9
-rw-r--r--vendor/oojs/oojs-ui/i18n/yi.json18
-rw-r--r--vendor/oojs/oojs-ui/i18n/yo.json10
-rw-r--r--vendor/oojs/oojs-ui/i18n/yue.json16
-rw-r--r--vendor/oojs/oojs-ui/i18n/zh-hans.json34
-rw-r--r--vendor/oojs/oojs-ui/i18n/zh-hant.json32
-rw-r--r--vendor/oojs/oojs-ui/jsduck.categories.json80
-rw-r--r--vendor/oojs/oojs-ui/jsduck.eg-iframe.html33
-rw-r--r--vendor/oojs/oojs-ui/jsduck.external.js26
-rw-r--r--vendor/oojs/oojs-ui/jsduck.json16
-rw-r--r--vendor/oojs/oojs-ui/php/Element.php297
-rw-r--r--vendor/oojs/oojs-ui/php/ElementMixin.php55
-rw-r--r--vendor/oojs/oojs-ui/php/Exception.php6
-rw-r--r--vendor/oojs/oojs-ui/php/HtmlSnippet.php36
-rw-r--r--vendor/oojs/oojs-ui/php/Layout.php21
-rw-r--r--vendor/oojs/oojs-ui/php/Tag.php365
-rw-r--r--vendor/oojs/oojs-ui/php/Theme.php58
-rw-r--r--vendor/oojs/oojs-ui/php/Widget.php71
-rw-r--r--vendor/oojs/oojs-ui/php/elements/ButtonElement.php102
-rw-r--r--vendor/oojs/oojs-ui/php/elements/FlaggedElement.php133
-rw-r--r--vendor/oojs/oojs-ui/php/elements/GroupElement.php129
-rw-r--r--vendor/oojs/oojs-ui/php/elements/IconElement.php76
-rw-r--r--vendor/oojs/oojs-ui/php/elements/IndicatorElement.php78
-rw-r--r--vendor/oojs/oojs-ui/php/elements/LabelElement.php77
-rw-r--r--vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php88
-rw-r--r--vendor/oojs/oojs-ui/php/elements/TitledElement.php74
-rw-r--r--vendor/oojs/oojs-ui/php/layouts/FieldLayout.php140
-rw-r--r--vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php32
-rw-r--r--vendor/oojs/oojs-ui/php/layouts/FormLayout.php47
-rw-r--r--vendor/oojs/oojs-ui/php/layouts/PanelLayout.php61
-rw-r--r--vendor/oojs/oojs-ui/php/themes/ApexTheme.php6
-rw-r--r--vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php39
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php28
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php110
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php166
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php70
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php99
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/IconWidget.php34
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php32
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/InputWidget.php144
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/LabelWidget.php48
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php60
-rw-r--r--vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php151
-rw-r--r--vendor/oojs/oojs-ui/src/ActionSet.js504
-rw-r--r--vendor/oojs/oojs-ui/src/Dialog.js323
-rw-r--r--vendor/oojs/oojs-ui/src/Element.js748
-rw-r--r--vendor/oojs/oojs-ui/src/Error.js91
-rw-r--r--vendor/oojs/oojs-ui/src/HtmlSnippet.js29
-rw-r--r--vendor/oojs/oojs-ui/src/Layout.js33
-rw-r--r--vendor/oojs/oojs-ui/src/Process.js166
-rw-r--r--vendor/oojs/oojs-ui/src/Theme.js48
-rw-r--r--vendor/oojs/oojs-ui/src/Tool.js279
-rw-r--r--vendor/oojs/oojs-ui/src/ToolFactory.js120
-rw-r--r--vendor/oojs/oojs-ui/src/ToolGroup.js338
-rw-r--r--vendor/oojs/oojs-ui/src/ToolGroupFactory.js38
-rw-r--r--vendor/oojs/oojs-ui/src/Toolbar.js481
-rw-r--r--vendor/oojs/oojs-ui/src/Widget.js102
-rw-r--r--vendor/oojs/oojs-ui/src/Window.js628
-rw-r--r--vendor/oojs/oojs-ui/src/WindowManager.js675
-rw-r--r--vendor/oojs/oojs-ui/src/core.js286
-rw-r--r--vendor/oojs/oojs-ui/src/dialogs/MessageDialog.js325
-rw-r--r--vendor/oojs/oojs-ui/src/dialogs/ProcessDialog.js296
-rw-r--r--vendor/oojs/oojs-ui/src/elements/ButtonElement.js263
-rw-r--r--vendor/oojs/oojs-ui/src/elements/ClippableElement.js205
-rw-r--r--vendor/oojs/oojs-ui/src/elements/DraggableElement.js142
-rw-r--r--vendor/oojs/oojs-ui/src/elements/DraggableGroupElement.js261
-rw-r--r--vendor/oojs/oojs-ui/src/elements/FlaggedElement.js209
-rw-r--r--vendor/oojs/oojs-ui/src/elements/GroupElement.js290
-rw-r--r--vendor/oojs/oojs-ui/src/elements/IconElement.js187
-rw-r--r--vendor/oojs/oojs-ui/src/elements/IndicatorElement.js168
-rw-r--r--vendor/oojs/oojs-ui/src/elements/LabelElement.js152
-rw-r--r--vendor/oojs/oojs-ui/src/elements/LookupElement.js352
-rw-r--r--vendor/oojs/oojs-ui/src/elements/PendingElement.js84
-rw-r--r--vendor/oojs/oojs-ui/src/elements/PopupElement.js36
-rw-r--r--vendor/oojs/oojs-ui/src/elements/TabIndexedElement.js138
-rw-r--r--vendor/oojs/oojs-ui/src/elements/TitledElement.js106
-rw-r--r--vendor/oojs/oojs-ui/src/intro.js.txt3
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/ActionFieldLayout.js81
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/BookletLayout.js542
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/CardLayout.js138
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/FieldLayout.js160
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/FieldsetLayout.js86
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/FormLayout.js119
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/IndexLayout.js452
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/MenuLayout.js156
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/PageLayout.js138
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/PanelLayout.js55
-rw-r--r--vendor/oojs/oojs-ui/src/layouts/StackLayout.js214
-rw-r--r--vendor/oojs/oojs-ui/src/outro.js.txt1
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Dialog.less35
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Element.less9
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Layout.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Tool.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/ToolGroup.less21
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Toolbar.less52
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Widget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/Window.less32
-rw-r--r--vendor/oojs/oojs-ui/src/styles/WindowManager.less39
-rw-r--r--vendor/oojs/oojs-ui/src/styles/common.less102
-rw-r--r--vendor/oojs/oojs-ui/src/styles/core.less114
-rw-r--r--vendor/oojs/oojs-ui/src/styles/dialogs/MessageDialog.less45
-rw-r--r--vendor/oojs/oojs-ui/src/styles/dialogs/ProcessDialog.less52
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/ButtonElement.less62
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/ClippableElement.less7
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/DraggableElement.less22
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/DraggableGroupElement.less11
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/FlaggedElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/GroupElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/IconElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/IndicatorElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/LabelElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/LookupElement.less10
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/PopupElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/TabIndexedElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/elements/TitledElement.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/images/grab.curbin0 -> 326 bytes
-rw-r--r--vendor/oojs/oojs-ui/src/styles/images/grabbing.curbin0 -> 326 bytes
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/ActionFieldLayout.less26
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/BookletLayout.less43
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/CardLayout.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/FieldLayout.less59
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/FieldsetLayout.less31
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/FormLayout.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/IndexLayout.less13
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/MenuLayout.less108
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/PageLayout.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/PanelLayout.less19
-rw-r--r--vendor/oojs/oojs-ui/src/styles/layouts/StackLayout.less10
-rw-r--r--vendor/oojs/oojs-ui/src/styles/theme.less91
-rw-r--r--vendor/oojs/oojs-ui/src/styles/toolgroups/BarToolGroup.less51
-rw-r--r--vendor/oojs/oojs-ui/src/styles/toolgroups/ListToolGroup.less21
-rw-r--r--vendor/oojs/oojs-ui/src/styles/toolgroups/MenuToolGroup.less19
-rw-r--r--vendor/oojs/oojs-ui/src/styles/toolgroups/PopupToolGroup.less73
-rw-r--r--vendor/oojs/oojs-ui/src/styles/tools/PopupTool.less12
-rw-r--r--vendor/oojs/oojs-ui/src/styles/tools/ToolGroupTool.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ActionWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ButtonGroupWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ButtonInputWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ButtonOptionWidget.less18
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ButtonSelectWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ButtonWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/CheckboxInputWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ComboBoxWidget.less13
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/DecoratedOptionWidget.less12
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/DropdownInputWidget.less17
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/DropdownWidget.less33
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/IconWidget.less10
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/IndicatorWidget.less10
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/InputWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/LabelWidget.less7
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/MenuOptionWidget.less21
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/MenuSectionOptionWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/MenuSelectWidget.less15
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/OptionWidget.less20
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/OutlineControlsWidget.less32
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/OutlineOptionWidget.less9
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/OutlineSelectWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/PopupButtonWidget.less12
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/PopupWidget.less49
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ProgressBarWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/RadioInputWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/RadioOptionWidget.less13
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/RadioSelectWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/SearchWidget.less25
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/SelectWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/TabOptionWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/TabSelectWidget.less9
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/TextInputMenuSelectWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/TextInputWidget.less71
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ToggleButtonWidget.less8
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ToggleSwitchWidget.less41
-rw-r--r--vendor/oojs/oojs-ui/src/styles/widgets/ToggleWidget.less5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/ApexTheme.js18
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/common.less27
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/core.less12
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/elements.less247
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-editing-advanced.json75
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-editing-core.json24
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-editing-list.json22
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-editing-styling.json72
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-moderation.json33
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons-movement.json27
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/icons.json50
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/add.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/advanced.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/alert.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-center.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-left.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-right.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/block.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-a.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-ain.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-dad.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-armn-to.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-b.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-be.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-te.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-zhe.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-f.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-g.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-geor-man.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-l.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-n.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-v.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/cancel.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretDown.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretUp.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/case-sensitive.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/check.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/circle.svg2
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/close.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/code.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/collapse.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/comment.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/downTriangle.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-rtl.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/ellipsis.svg14
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/expand.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-ltr.svg16
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-rtl.svg16
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-ltr.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-rtl.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/history.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-ltr.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/info.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/insert.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-a.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-keheh-jeem.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-meem.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-armn-sha.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-c.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-d.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-e.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-geor-kan.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-i.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-k.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-s.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/language.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/link.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/lock.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/menu.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/move.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-ltr.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/picture.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/regular-expression.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/remove.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/search.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/secure-link.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/settings.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/specialCharacter.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/star.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-a.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-s.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-y.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-ltr.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-rtl.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-ltr.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-rtl.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-caption.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-after.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-before.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-merge-cells.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/table.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/tag.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-lefttoright.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-righttoleft.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-style.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/trash.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/unStar.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-a.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-u.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/upTriangle.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/wikiText.svg15
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/icons/window.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/alert.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-down.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-up.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/required.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/textures/pending.gifbin0 -> 2032 bytes
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/textures/transparency.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/images/toolbar-shadow.pngbin0 -> 131 bytes
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/indicators.json22
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/layouts.less135
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/textures.json8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/tools.less422
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/widgets.less797
-rw-r--r--vendor/oojs/oojs-ui/src/themes/apex/windows.less307
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/BlankTheme.js32
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/common.less10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/core.less12
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/elements.less29
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/images/icons/README.txt1
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/images/indicators/README.txt1
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/images/textures/README.txt1
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/layouts.less26
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/tools.less20
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/widgets.less77
-rw-r--r--vendor/oojs/oojs-ui/src/themes/blank/windows.less11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/MediaWikiTheme.js56
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/common.less69
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/core.less12
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/elements.less319
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-alerts.json33
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-content.json50
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-advanced.json75
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-core.json45
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-list.json22
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-styling.json72
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-interactions.json52
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-layout.json43
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-location.json19
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-media.json27
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-moderation.json52
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-movement.json27
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-user.json19
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons-wikimedia.json9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/icons.json75
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/add.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/advanced.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/alert.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-center.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-left.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-right.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-ltr.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bell.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/beta.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/betaLaunch.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/block.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-a.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-ain.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-dad.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-armn-to.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-b.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-be.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-te.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-zhe.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-f.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-g.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-geor-man.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-l.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-n.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-v.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/cancel.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretDown.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretUp.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/case-sensitive.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/check.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/circle.svg2
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clear.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clock.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/code.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/collapse.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/comment.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/downTriangle.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-rtl.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ellipsis.svg14
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/expand.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eye.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eyeClosed.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-ltr.svg16
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-rtl.svg16
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/heart.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-ltr.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-rtl.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/history.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/info.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/insert.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-a.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-keheh-jeem.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-meem.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-armn-sha.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-c.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-d.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-e.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-geor-kan.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-i.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-k.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-s.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/language.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-ltr.svg13
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-cc.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikimediaCommons.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikipedia.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPin.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-ltr.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/menu.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/picture.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/regular-expression.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/remove.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ribbonPrize.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/secure-link.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/settings.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/specialCharacter.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/star.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stop.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-a.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-s.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-y.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSideMenu.svg12
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-ltr.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-rtl.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-ltr.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-rtl.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-ltr.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-rtl.svg5
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-caption.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-ltr.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-rtl.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-after.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-before.svg11
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-merge-cells.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/tag.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-ltr.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-rtl.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-lefttoright.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-righttoleft.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-style.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trash.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unStar.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-a.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-u.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upTriangle.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userAvatar.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-ltr.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-rtl.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewCompact.svg14
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-ltr.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-rtl.svg9
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/visionSimulator.svg4
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikiText.svg15
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-rtl.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/window.svg7
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/alert.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-down.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-ltr.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-rtl.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-up.svg8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/required.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-ltr.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-rtl.svg6
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/pending.gifbin0 -> 2032 bytes
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/transparency.svg10
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/indicators.json29
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/layouts.less135
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/textures.json8
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/tools.less422
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/widgets.less978
-rw-r--r--vendor/oojs/oojs-ui/src/themes/mediawiki/windows.less300
-rw-r--r--vendor/oojs/oojs-ui/src/toolgroups/BarToolGroup.js35
-rw-r--r--vendor/oojs/oojs-ui/src/toolgroups/ListToolGroup.js133
-rw-r--r--vendor/oojs/oojs-ui/src/toolgroups/MenuToolGroup.js58
-rw-r--r--vendor/oojs/oojs-ui/src/toolgroups/PopupToolGroup.js187
-rw-r--r--vendor/oojs/oojs-ui/src/tools/PopupTool.js59
-rw-r--r--vendor/oojs/oojs-ui/src/tools/ToolGroupTool.js96
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ActionWidget.js170
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ButtonGroupWidget.js53
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ButtonInputWidget.js121
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ButtonOptionWidget.js60
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ButtonSelectWidget.js62
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ButtonWidget.js215
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/CheckboxInputWidget.js110
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ComboBoxWidget.js230
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/DecoratedOptionWidget.js55
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/DropdownInputWidget.js137
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/DropdownWidget.js149
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/GroupWidget.js48
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/IconWidget.js54
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/IndicatorWidget.js52
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/InputWidget.js207
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ItemWidget.js48
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/LabelWidget.js84
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/MenuOptionWidget.js33
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/MenuSectionOptionWidget.js55
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js254
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/OptionWidget.js177
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/OutlineControlsWidget.js145
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/OutlineOptionWidget.js130
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/OutlineSelectWidget.js34
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/PopupButtonWidget.js57
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/PopupWidget.js395
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ProgressBarWidget.js96
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/RadioInputWidget.js94
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/RadioOptionWidget.js66
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/RadioSelectWidget.js58
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/SearchWidget.js176
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/SelectWidget.js630
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/TabOptionWidget.js31
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/TabSelectWidget.js33
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/TextInputMenuSelectWidget.js103
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js535
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ToggleButtonWidget.js114
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ToggleSwitchWidget.js92
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/ToggleWidget.js71
-rw-r--r--vendor/oojs/oojs-ui/tests/Element.test.js52
-rw-r--r--vendor/oojs/oojs-ui/tests/JSPHP.test.karma.js61
-rw-r--r--vendor/oojs/oojs-ui/tests/JSPHP.test.standalone.js55
-rw-r--r--vendor/oojs/oojs-ui/tests/Process.test.js179
-rw-r--r--vendor/oojs/oojs-ui/tests/QUnit.assert.equalDomElement.js113
-rw-r--r--vendor/oojs/oojs-ui/tests/elements/FlaggedElement.test.js64
-rw-r--r--vendor/oojs/oojs-ui/tests/index.php77
861 files changed, 39319 insertions, 0 deletions
diff --git a/vendor/oojs/oojs-ui/.csscomb.json b/vendor/oojs/oojs-ui/.csscomb.json
new file mode 100644
index 00000000..c4e215ec
--- /dev/null
+++ b/vendor/oojs/oojs-ui/.csscomb.json
@@ -0,0 +1,24 @@
+{
+ "remove-empty-rulesets": true,
+ "always-semicolon": true,
+ "color-case": "lower",
+ "block-indent": "\t",
+ "color-shorthand": false,
+ "element-case": "lower",
+ "eof-newline": true,
+ "leading-zero": true,
+ "quotes": "double",
+ "space-before-colon": "",
+ "space-after-colon": " ",
+ "space-before-combinator": " ",
+ "space-after-combinator": " ",
+ "space-between-declarations": "\n",
+ "space-before-opening-brace": " ",
+ "space-after-opening-brace": "\n",
+ "space-after-selector-delimiter": "\n",
+ "space-before-selector-delimiter": "",
+ "space-before-closing-brace": "\n",
+ "strip-spaces": true,
+ "unitless-zero": true,
+ "vendor-prefix-align": true
+}
diff --git a/vendor/oojs/oojs-ui/.csslintrc b/vendor/oojs/oojs-ui/.csslintrc
new file mode 100644
index 00000000..e777c7f3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/.csslintrc
@@ -0,0 +1,11 @@
+{
+ "adjoining-classes": false,
+ "box-model": false,
+ "box-sizing": false,
+ "fallback-colors": false,
+ "important": false,
+ "outline-none": false,
+ "qualified-headings": false,
+ "universal-selector": false,
+ "unqualified-attributes": false
+}
diff --git a/vendor/oojs/oojs-ui/.mailmap b/vendor/oojs/oojs-ui/.mailmap
new file mode 100644
index 00000000..84db0c6e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/.mailmap
@@ -0,0 +1,18 @@
+Alex Monk <krenair@gmail.com>
+Alex Monk <krenair@gmail.com> <krenair@wikimedia.org>
+Bartosz Dziewoński <matma.rex@gmail.com>
+Christian Williams <christian@wikia-inc.com>
+David Chan <david@sheetmusic.org.uk>
+Ed Sanders <esanders@wikimedia.org>
+Inez Korczynski <inez@wikia-inc.com>
+James D. Forrester <jforrester@wikimedia.org>
+Kirsten Menger-Anderson <kmenger@wikimedia.org>
+Marielle Volz <marielle.volz@gmail.com>
+Moriel Schottlender <moriel@gmail.com>
+Roan Kattouw <roan.kattouw@gmail.com>
+Roan Kattouw <roan.kattouw@gmail.com> <roan@wikimedia.org>
+Rob Moen <rmoen@wikimedia.org>
+Sucheta Ghoshal <sghoshal@wikimedia.org>
+Timo Tijhof <krinklemail@gmail.com>
+Trevor Parscal <trevorparscal@gmail.com>
+
diff --git a/vendor/oojs/oojs-ui/.npmignore b/vendor/oojs/oojs-ui/.npmignore
new file mode 100644
index 00000000..67aebbfc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/.npmignore
@@ -0,0 +1,2 @@
+/docs
+vendor
diff --git a/vendor/oojs/oojs-ui/AUTHORS.txt b/vendor/oojs/oojs-ui/AUTHORS.txt
new file mode 100644
index 00000000..6813d169
--- /dev/null
+++ b/vendor/oojs/oojs-ui/AUTHORS.txt
@@ -0,0 +1,34 @@
+Principal Authors (major contributors, alphabetically)
+
+Bartosz Dziewoński <matma.rex@gmail.com>
+Ed Sanders <esanders@wikimedia.org>
+James D. Forrester <jforrester@wikimedia.org>
+Kirsten Menger-Anderson <kmenger@wikimedia.org>
+Roan Kattouw <roan@wikimedia.org>
+Rob Moen <rmoen@wikimedia.org>
+Timo Tijhof <timo@wikimedia.org>
+Trevor Parscal <trevor@wikimedia.org>
+
+Patch Contributors (minor contributors, alphabetically)
+
+Alex Monk <krenair@wikimedia.org>
+Amir E. Aharoni <aaharoni@wikimedia.org>
+Antoine Musso <hashar@free.fr>
+Derk-Jan Hartman <hartman.wiki@gmail.com>
+eranroz <eranroz89@gmail.com>
+Erik Moeller <erik@wikimedia.org>
+Erick Guan <fantasticfears@gmail.com>
+Gilles Dubuc <gdubuc@wikimedia.org>
+Inez Korczyński <inez@wikia-inc.com>
+Juliusz Gonera <jgonera@wikimedia.org>
+Kunal Mehta <legoktm@gmail.com>
+Kyle Florence <kflorence@wikia-inc.com>
+May Tee-Galloway <mgalloway@wikimedia.org>
+Moriel Schottlender <moriel@gmail.com>
+Niklas Laxström <nlaxstrom@wikimedia.org>
+Ricordisamoa <ricordisamoa@openmailbox.org>
+rillke <rillke@wikipedia.de>
+Ryan Kaldari <rkaldari@wikimedia.org>
+Sam Reed <reedy@wikimedia.org>
+Sucheta Ghoshal <sghoshal@wikimedia.org>
+Željko Filipin <zeljko.filipin@gmail.com>
diff --git a/vendor/oojs/oojs-ui/Doxyfile b/vendor/oojs/oojs-ui/Doxyfile
new file mode 100644
index 00000000..cf831926
--- /dev/null
+++ b/vendor/oojs/oojs-ui/Doxyfile
@@ -0,0 +1,33 @@
+# Configuration file for Doxygen
+
+PROJECT_NAME = OOjs UI
+PROJECT_BRIEF = Object-Oriented JavaScript – User Interface
+
+OUTPUT_DIRECTORY = docs
+HTML_OUTPUT = php
+
+JAVADOC_AUTOBRIEF = YES
+QT_AUTOBRIEF = YES
+
+WARN_NO_PARAMDOC = YES
+
+INPUT = README.md php/
+FILE_PATTERNS = *.php
+RECURSIVE = YES
+# Requires doxygen 1.8.3+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+HTML_DYNAMIC_SECTIONS = YES
+GENERATE_TREEVIEW = YES
+TREEVIEW_WIDTH = 250
+
+GENERATE_LATEX = NO
+
+HAVE_DOT = YES
+DOT_FONTNAME = Helvetica
+DOT_FONTSIZE = 10
+TEMPLATE_RELATIONS = YES
+CALL_GRAPH = NO
+CALLER_GRAPH = NO
+# Makes dot run faster. Requires graphviz >1.8.10
+DOT_MULTI_TARGETS = YES
diff --git a/vendor/oojs/oojs-ui/Gemfile.lock b/vendor/oojs/oojs-ui/Gemfile.lock
new file mode 100644
index 00000000..a5a0e197
--- /dev/null
+++ b/vendor/oojs/oojs-ui/Gemfile.lock
@@ -0,0 +1,25 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.0.0)
+ astrolabe (1.3.0)
+ parser (>= 2.2.0.pre.3, < 3.0)
+ parser (2.2.0.pre.7)
+ ast (>= 1.1, < 3.0)
+ slop (~> 3.4, >= 3.4.5)
+ powerpack (0.0.9)
+ rainbow (2.0.0)
+ rubocop (0.27.1)
+ astrolabe (~> 1.3)
+ parser (>= 2.2.0.pre.7, < 3.0)
+ powerpack (~> 0.0.6)
+ rainbow (>= 1.99.1, < 3.0)
+ ruby-progressbar (~> 1.4)
+ ruby-progressbar (1.7.0)
+ slop (3.6.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rubocop
diff --git a/vendor/oojs/oojs-ui/Gruntfile.js b/vendor/oojs/oojs-ui/Gruntfile.js
new file mode 100644
index 00000000..0aa41e42
--- /dev/null
+++ b/vendor/oojs/oojs-ui/Gruntfile.js
@@ -0,0 +1,401 @@
+/*!
+ * Grunt file
+ */
+
+/*jshint node:true */
+module.exports = function ( grunt ) {
+ grunt.loadNpmTasks( 'grunt-banana-checker' );
+ grunt.loadNpmTasks( 'grunt-contrib-clean' );
+ grunt.loadNpmTasks( 'grunt-contrib-concat-sourcemaps' );
+ grunt.loadNpmTasks( 'grunt-contrib-copy' );
+ grunt.loadNpmTasks( 'grunt-contrib-csslint' );
+ grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
+ grunt.loadNpmTasks( 'grunt-contrib-jshint' );
+ grunt.loadNpmTasks( 'grunt-contrib-less' );
+ grunt.loadNpmTasks( 'grunt-contrib-uglify' );
+ grunt.loadNpmTasks( 'grunt-contrib-watch' );
+ grunt.loadNpmTasks( 'grunt-csscomb' );
+ grunt.loadNpmTasks( 'grunt-exec' );
+ grunt.loadNpmTasks( 'grunt-file-exists' );
+ grunt.loadNpmTasks( 'grunt-cssjanus' );
+ grunt.loadNpmTasks( 'grunt-jscs' );
+ grunt.loadNpmTasks( 'grunt-karma' );
+ grunt.loadNpmTasks( 'grunt-svg2png' );
+ grunt.loadTasks( 'build/tasks' );
+
+ var modules = grunt.file.readJSON( 'build/modules.json' ),
+ pgk = grunt.file.readJSON( 'package.json' ),
+ lessFiles = {
+ raster: {},
+ vector: {},
+ mixed: {}
+ },
+ colorizeSvgFiles = {},
+ requiredFiles = modules[ 'oojs-ui' ].scripts.slice(),
+ concatCssFiles = {},
+ rtlFiles = {},
+ minBanner = '/*! OOjs UI v<%= pkg.version %> | http://oojs.mit-license.org */';
+
+ ( function () {
+ var distFile, target, module, moduleStyleFiles;
+ function rtlPath( fileName ) {
+ return fileName.replace( /\.(\w+)$/, '.rtl.$1' );
+ }
+ // Generate all task targets required to process given file into a pair of CSS files (for LTR
+ // and RTL), and return file name of LTR file.
+ function processFile( fileName ) {
+ var lessFileName, cssFileName, theme, path;
+ path = require( 'path' );
+ if ( path.extname( fileName ) === '.json' ) {
+ lessFileName = fileName.replace( /\.json$/, '.less' ).replace( /^src/, 'dist/tmp' );
+ theme = path.basename( path.dirname( fileName ) );
+
+ colorizeSvgFiles[ fileName.replace( /.+\/(\w+)\/([\w-]+)\.(?:json|less)$/, '$1-$2' ) ] = {
+ options: grunt.file.readJSON( fileName ),
+ srcDir: 'src/themes/' + theme,
+ destDir: 'dist/themes/' + theme,
+ // This should not be needed, but our dist directory structure is weird
+ cssPrependPath: 'themes/' + theme + '/',
+ destLessFile: {
+ ltr: lessFileName,
+ rtl: rtlPath( lessFileName )
+ }
+ };
+
+ cssFileName = fileName.replace( /\.json$/, '.css' ).replace( /^src/, 'dist/tmp/' + target );
+ lessFiles[ target ][ cssFileName ] = [ lessFileName ];
+ lessFiles[ target ][ rtlPath( cssFileName ) ] = [ rtlPath( lessFileName ) ];
+ } else {
+ cssFileName = fileName.replace( /\.less$/, '.css' ).replace( /^src/, 'dist/tmp/' + target );
+ lessFiles[ target ][ cssFileName ] = [ fileName ];
+ rtlFiles[ rtlPath( cssFileName ) ] = cssFileName;
+ }
+ return cssFileName;
+ }
+ for ( module in modules ) {
+ if ( modules[ module ].styles ) {
+ moduleStyleFiles = modules[ module ].styles;
+ for ( target in lessFiles ) {
+ requiredFiles.push.apply( requiredFiles, moduleStyleFiles );
+
+ distFile = 'dist/' + module + ( target !== 'mixed' ? '.' + target : '' ) + '.css';
+
+ concatCssFiles[ distFile ] = moduleStyleFiles.map( processFile );
+ concatCssFiles[ rtlPath( distFile ) ] = concatCssFiles[ distFile ].map( rtlPath );
+ }
+ }
+ }
+ }() );
+
+ function strip( str ) {
+ var path = require( 'path' );
+ // http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
+ // http://gruntjs.com/api/grunt.file#grunt.file.expandmapping
+ return function ( dest, src ) {
+ return path.join( dest, src.replace( str, '' ) );
+ };
+ }
+
+ grunt.initConfig( {
+ pkg: pgk,
+
+ // Build
+ clean: {
+ build: 'dist/*',
+ doc: 'docs/*',
+ tmp: 'dist/tmp'
+ },
+ fileExists: {
+ src: requiredFiles
+ },
+ typos: {
+ options: {
+ typos: 'build/typos.json'
+ },
+ src: '{src,php}/**/*.{js,json,less,css}'
+ },
+ concat: {
+ options: {
+ banner: grunt.file.read( 'build/banner.txt' )
+ },
+ js: {
+ files: {
+ 'dist/oojs-ui.js': modules[ 'oojs-ui' ].scripts,
+ 'dist/oojs-ui-apex.js': modules[ 'oojs-ui-apex' ].scripts,
+ 'dist/oojs-ui-mediawiki.js': modules[ 'oojs-ui-mediawiki' ].scripts
+ }
+ },
+ css: {
+ files: concatCssFiles
+ },
+ demoCss: {
+ options: {
+ banner: '/** This file is generated automatically. Do not modify it. */\n\n'
+ },
+ files: {
+ 'demos/styles/demo.rtl.css': 'demos/styles/demo.rtl.css'
+ }
+ }
+ },
+
+ // Build – Code
+ uglify: {
+ options: {
+ banner: minBanner,
+ sourceMap: true,
+ sourceMapIncludeSources: true,
+ report: 'gzip'
+ },
+ js: {
+ expand: true,
+ src: 'dist/*.js',
+ ext: '.min.js',
+ extDot: 'last'
+ }
+ },
+
+ // Build – Styling
+ less: {
+ distRaster: {
+ options: {
+ ieCompat: true,
+ report: 'gzip',
+ modifyVars: {
+ 'oo-ui-distribution': 'raster',
+ 'oo-ui-default-image-ext': 'png'
+ }
+ },
+ files: lessFiles.raster
+ },
+ distVector: {
+ options: {
+ ieCompat: false,
+ report: 'gzip',
+ modifyVars: {
+ 'oo-ui-distribution': 'vector',
+ 'oo-ui-default-image-ext': 'svg'
+ }
+ },
+ files: lessFiles.vector
+ },
+ distMixed: {
+ options: {
+ ieCompat: false,
+ report: 'gzip',
+ modifyVars: {
+ 'oo-ui-distribution': 'mixed',
+ 'oo-ui-default-image-ext': 'png'
+ }
+ },
+ files: lessFiles.mixed
+ }
+ },
+ cssjanus: {
+ options: {
+ generateExactDuplicates: true
+ },
+ dist: {
+ files: rtlFiles
+ },
+ demoCss: {
+ files: {
+ 'demos/styles/demo.rtl.css': 'demos/styles/demo.css'
+ }
+ }
+ },
+ csscomb: {
+ dist: {
+ expand: true,
+ src: 'dist/*.css'
+ }
+ },
+ copy: {
+ imagesCommon: {
+ src: 'src/styles/images/*.cur',
+ dest: 'dist/images/',
+ expand: true,
+ flatten: true
+ },
+ imagesApex: {
+ src: 'src/themes/apex/images/**/*.{png,gif}',
+ dest: 'dist/themes/apex/images/',
+ expand: true,
+ rename: strip( 'src/themes/apex/images/' )
+ },
+ imagesMediaWiki: {
+ src: 'src/themes/mediawiki/images/**/*.{png,gif}',
+ dest: 'dist/themes/mediawiki/images/',
+ expand: true,
+ rename: strip( 'src/themes/mediawiki/images/' )
+ },
+ i18n: {
+ src: 'i18n/*.json',
+ expand: true,
+ dest: 'dist/'
+ },
+ jsduck: {
+ // Don't publish devDependencies
+ src: '{dist,node_modules/{' + Object.keys( pgk.dependencies ).join( ',' ) + '}}/**/*',
+ dest: 'docs/',
+ expand: true
+ }
+ },
+ colorizeSvg: colorizeSvgFiles,
+ svg2png: {
+ dist: {
+ src: 'dist/{images,themes}/**/*.svg'
+ }
+ },
+ cssmin: {
+ options: {
+ keepSpecialComments: 0,
+ banner: minBanner,
+ compatibility: 'ie8',
+ report: 'gzip'
+ },
+ dist: {
+ expand: true,
+ src: 'dist/*.css',
+ ext: '.min.css',
+ extDot: 'last'
+ }
+ },
+
+ // Lint – Code
+ jshint: {
+ options: {
+ jshintrc: true
+ },
+ dev: [
+ '*.js',
+ '{build,demos,src,tests}/**/*.js',
+ '!tests/JSPHP.test.js'
+ ]
+ },
+ jscs: {
+ dev: [
+ '<%= jshint.dev %>',
+ '!demos/dist/**'
+ ]
+ },
+
+ // Lint – Styling
+ csslint: {
+ options: {
+ csslintrc: '.csslintrc'
+ },
+ all: [
+ '{demos,src}/**/*.css',
+ '!demos/dist/**'
+ ]
+ },
+
+ // Lint – i18n
+ banana: {
+ all: 'i18n/'
+ },
+
+ // Test
+ exec: {
+ rubyTestSuiteGenerator: {
+ command: 'ruby bin/testsuitegenerator.rb src php > tests/JSPHP-suite.json'
+ },
+ phpGenerateJSPHPForKarma: {
+ command: 'php ../bin/generate-JSPHP-for-karma.php > JSPHP.test.js',
+ cwd: './tests'
+ }
+ },
+ karma: {
+ options: {
+ frameworks: [ 'qunit' ],
+ files: [
+ 'node_modules/jquery/dist/jquery.js',
+ 'node_modules/oojs/dist/oojs.jquery.js',
+ 'dist/oojs-ui.js',
+ 'dist/oojs-ui-apex.js',
+ 'dist/oojs-ui-mediawiki.js',
+ 'tests/QUnit.assert.equalDomElement.js',
+ 'tests/**/*.test.js'
+ ],
+ reporters: [ 'dots' ],
+ singleRun: true,
+ browserDisconnectTimeout: 5000,
+ browserDisconnectTolerance: 2,
+ autoWatch: false
+ },
+ main: {
+ browsers: [ 'Chrome' ],
+ preprocessors: {
+ 'dist/*.js': [ 'coverage' ]
+ },
+ reporters: [ 'dots', 'coverage' ],
+ coverageReporter: { reporters: [
+ { type: 'html', dir: 'coverage/' },
+ { type: 'text-summary', dir: 'coverage/' }
+ ] }
+ },
+ other: {
+ browsers: [ 'Firefox' ]
+ }
+ },
+
+ // Development
+ watch: {
+ files: [
+ '<%= jshint.dev %>',
+ '<%= csslint.all %>',
+ '{demos,src}/**/*.less',
+ '.{csslintrc,jscsrc,jshintignore,jshintrc}'
+ ],
+ tasks: 'quick-build'
+ }
+ } );
+
+ grunt.registerTask( 'pre-test', function () {
+ // Only create Source maps when doing a git-build for testing and local
+ // development. Distributions for export should not, as the map would
+ // be pointing at "../src".
+ grunt.config.set( 'concat.js.options.sourceMap', true );
+ grunt.config.set( 'concat.js.options.sourceMapStyle', 'link' );
+ } );
+
+ grunt.registerTask( 'pre-git-build', function () {
+ var done = this.async();
+ require( 'child_process' ).exec( 'git rev-parse HEAD', function ( err, stout, stderr ) {
+ if ( !stout || err || stderr ) {
+ grunt.log.err( err || stderr );
+ done( false );
+ return;
+ }
+ grunt.config.set( 'pkg.version', grunt.config( 'pkg.version' ) + '-pre (' + stout.slice( 0, 10 ) + ')' );
+ grunt.verbose.writeln( 'Added git HEAD to pgk.version' );
+ done();
+ } );
+ } );
+
+ grunt.registerTask( 'build-code', [ 'concat:js', 'uglify' ] );
+ grunt.registerTask( 'build-styling', [
+ 'colorizeSvg', 'less', 'cssjanus',
+ 'concat:css', 'concat:demoCss', 'csscomb', 'cssmin',
+ 'copy:imagesCommon', 'copy:imagesApex', 'copy:imagesMediaWiki', 'svg2png'
+ ] );
+ grunt.registerTask( 'build-i18n', [ 'copy:i18n' ] );
+ grunt.registerTask( 'build-tests', [ 'exec:rubyTestSuiteGenerator', 'exec:phpGenerateJSPHPForKarma' ] );
+ grunt.registerTask( 'build', [ 'clean:build', 'fileExists', 'typos', 'build-code', 'build-styling', 'build-i18n', 'build-tests', 'clean:tmp' ] );
+
+ grunt.registerTask( 'git-build', [ 'pre-git-build', 'build' ] );
+
+ // Quickly build a no-frills vector-only ltr-only version for development
+ grunt.registerTask( 'quick-build', [
+ 'pre-git-build', 'clean:build', 'fileExists', 'typos',
+ 'concat:js',
+ 'colorizeSvg', 'less:distVector', 'concat:css',
+ 'copy:imagesCommon', 'copy:imagesApex', 'copy:imagesMediaWiki',
+ 'build-i18n'
+ ] );
+
+ grunt.registerTask( 'lint', [ 'jshint', 'jscs', 'csslint', 'banana' ] );
+ grunt.registerTask( 'test', [ 'lint', 'pre-test', 'git-build', 'karma:main', 'karma:other' ] );
+
+ grunt.registerTask( 'default', 'test' );
+};
diff --git a/vendor/oojs/oojs-ui/History.md b/vendor/oojs/oojs-ui/History.md
new file mode 100644
index 00000000..905b6b11
--- /dev/null
+++ b/vendor/oojs/oojs-ui/History.md
@@ -0,0 +1,668 @@
+# OOjs UI Release History
+
+## v0.11.3 / 2015-05-12
+* BarToolGroup: Don't use "pointer" cursor for disabled tools in enabled toolgroups (Bartosz Dziewoński)
+* Tool: Support icon+label in bar tool groups (Bartosz Dziewoński)
+* ToolGroupTool: Correct opacity of disabled nested tool group handle (Bartosz Dziewoński)
+* ToolGroupTool: Synchronize inner ToolGroup disabledness state (Bartosz Dziewoński)
+* MediaWiki theme: Add a powerful default text color for tools (Trevor Parscal)
+* MediaWiki theme: Adjust quotes icon to match other icons (nirzar)
+* MediaWiki theme: Give names to some more toolbar colours (Bartosz Dziewoński)
+* MediaWiki theme: Provide all variants of the 'tag' icon (James D. Forrester)
+* MediaWiki theme: Rejigger some toolbar coloring (Bartosz Dziewoński)
+* MediaWiki theme: Remove box-shadow from nested toolbars (Bartosz Dziewoński)
+* MediaWiki theme: Remove unusued toolbar shadow (Trevor Parscal)
+* MediaWiki theme: Update button specification (nirzar)
+
+## v0.11.2 / 2015-05-11
+* Don't select lookup items on initialize (Ed Sanders)
+* ListToolGroup, MenuToolGroup: Set accelTooltips = false (Bartosz Dziewoński)
+* PopupWidget: Add setAlignment (Moriel Schottlender)
+* SelectWidget: Mark as @abstract, which it is (Bartosz Dziewoński)
+* Simplify default action prevention in buttons and forms (Bartosz Dziewoński)
+* TabOptionWidget: Fix disabled styles to not react to hover/select (Ed Sanders)
+* TextInputWidget: Allow override of #setValidityFlag (Ed Sanders)
+* TextInputWidget: Use aria-required along with the required attribute (Prateek Saxena)
+* Toolbar: Fix shadow styling (Bartosz Dziewoński)
+* Toolbar: Move some tweaks from demo to actual implementation (Bartosz Dziewoński)
+* Toolbar: Remove some useless code from the example (Bartosz Dziewoński)
+* Toolbar: Rework example and add 'menu' tool group example (Bartosz Dziewoński)
+* MediaWiki theme: Change highlight color for selected menu option (nirzar)
+* MediaWiki theme: Polish the toolbar design (nirzar)
+* MediaWiki theme: Remove accidentally duplicated styles for SelectWidget (Bartosz Dziewoński)
+
+## v0.11.1 / 2015-05-04
+* Add IndexLayout (Trevor Parscal)
+* CardLayout: Fix typo (Kirsten Menger-Anderson)
+* LabelElement: Document that label config option can take an HtmlSnippet (Roan Kattouw)
+* PopupButtonWidget: Update align config in example (Kirsten Menger-Anderson)
+* Remove GridLayout remnants (Bartosz Dziewoński)
+* SelectWidget: Add #selectItemByData method (Moriel Schottlender)
+* TabOptionWidget: Change link to card layout (Kirsten Menger-Anderson)
+* TextInputWidget: Annotate input validation with aria-invalid (Prateek Saxena)
+* TextInputWidget: Don't set 'invalid' flag on first focus, even if invalid (Bartosz Dziewoński)
+* TextInputWidget: Support 'required' config option in PHP (Bartosz Dziewoński)
+* MediaWiki theme: Add 'destructive' variant to block icon (Moriel Schottlender)
+* MediaWiki theme: Better vertical alignment of 'search' icon (Ed Sanders)
+* MediaWiki theme: Tweak 'search' icon size (Ed Sanders)
+* MediaWiki theme: Use variable for transition time and easing function (Prateek Saxena)
+* MediaWiki theme: input: Use variable for transition time and easing function (Prateek Saxena)
+* MediaWiki theme: radio/checkbox: Use variable for transition time and easing function (Prateek Saxena)
+* MediaWiki, Apex themes: Switch icons: clear → cancel, closeInput → clear (Bartosz Dziewoński)
+* MediaWiki, Apex themes: Switch over 'magnifyingGlass' icon to be 'search' (James D. Forrester)
+* build: Add clean:doc task (Bartosz Dziewoński)
+* build: Bump grunt-jscs to latest version (James D. Forrester)
+* core: Add OO.ui.debounce() utility (Roan Kattouw)
+* demo: Add icons with variants to icons demo (Bartosz Dziewoński)
+
+## v0.11.0 / 2015-04-29
+* [BREAKING CHANGE] Do not set font-size: 0.8em anywhere in the library (Bartosz Dziewoński)
+
+* [DEPRECATING CHANGE] Create rtl-ready alignments in PopupWidget (Moriel Schottlender)
+
+* MediaWiki theme: Adding variants to several icons (Moriel Schottlender)
+* TextInputWidget: Allow functions to be passed as 'validate' config option (Bartosz Dziewoński)
+* TextInputWidget: Styles for 'invalid' flag (Bartosz Dziewoński)
+* Update OOjs to v1.1.7 (James D. Forrester)
+* Update jQuery from v1.11.1 to v1.11.3 (James D. Forrester)
+* build: Use jquery and oojs from npm instead of embedded lib (Timo Tijhof)
+
+## v0.10.1 / 2015-04-27
+* Correct 'tabindex' attribute setting (Bartosz Dziewoński)
+* Make toolbars keyboard-accessible (Bartosz Dziewoński)
+* ToggleButtonWidget: Unbreak horizontal alignment (Bartosz Dziewoński)
+
+## v0.10.0 / 2015-04-22
+* [BREAKING CHANGE] ButtonWidget: remove deprecated nofollow option alias (C. Scott Ananian)
+* [BREAKING CHANGE] Convert ToggleWidget from a mixin to an abstract class (Bartosz Dziewoński)
+* [BREAKING CHANGE] MenuLayout: Reimplement without inline styles (Bartosz Dziewoński)
+
+* BarToolGroup: Allow tools with labels instead of icons (Bartosz Dziewoński)
+* BookletLayout: Find first focusable element and add focusable utility (Moriel Schottlender)
+* ButtonInputWidget: Don't double-mixin FlaggedElement (Bartosz Dziewoński)
+* ButtonWidget: Remove href to make unclickable when disabled (Bartosz Dziewoński)
+* ButtonWidget: Remove pointless #isHyperlink property (Bartosz Dziewoński)
+* FormLayout: Better document how this works with InputWidgets (Bartosz Dziewoński)
+* MenuLayout: Add example (Kirsten Menger-Anderson)
+* MenuLayout: Fix initialization order (Bartosz Dziewoński)
+* PHP: More useful debugging information on unsafe tag attributes (Chad Horohoe)
+* SelectWidget#getTargetItem: Simplify (Ed Sanders)
+* Toolbar: Add example (Bartosz Dziewoński)
+* themes: Add viewCompact, viewDetails, visionSimulator icons for iconography page (Mun May Tee)
+* demo: Remove VisualEditor references from toolbar demo, use generic icons (Ed Sanders)
+* demo: Remove outline controls from outlined BookletLayout demo (Bartosz Dziewoński)
+* demo: Simplify ButtonGroupWidget and ButtonSelectWidget examples (Bartosz Dziewoński)
+
+## v0.9.8 / 2015-04-12
+* Apex, MediaWiki: Correct or delete unused SVG files (James D. Forrester)
+* Apex theme: Correctly position popups in RTL; follows-up v0.9.5 (Moriel Schottlender)
+* BookletLayout: Allow focus on any item (Moriel Schottlender)
+* Error: Add description (Kirsten Menger-Anderson)
+* Move coverage output from "/dist/coverage" to "/coverage" (Timo Tijhof)
+* ProcessDialog: Remove stray 'this.$' from documentation code example (Roan Kattouw)
+* ProgressBarWidget: Remove spurious styles from CSS output (Bartosz Dziewoński)
+* build: Add explicit dependency upon grunt-cli (Kunal Mehta)
+* build: Run lint before build in grunt-test (Timo Tijhof)
+* colorize-svg: Generate language-specific rules for images even if equal to default ones (Bartosz Dziewoński)
+* colorize-svg: Sprinkle `/* @noflip */` on language-specific rules (Bartosz Dziewoński)
+* demo: Change html dir property when direction changes (Moriel Schottlender)
+
+## v0.9.7 / 2015-04-03
+* build: Generate correct paths to fallback images (Bartosz Dziewoński)
+
+## v0.9.5 / 2015-04-02
+* [DEPRECATING CHANGE] Deprecate search widget event re-emission (Ed Sanders)
+
+* ActionFieldLayout: Add description and example (Kirsten Menger-Anderson)
+* Add vertical spacing to RadioSelectWidget in MW theme (Ed Sanders)
+* Allow rejecting Process with single Error (Matthew Flaschen)
+* Apex theme: Tweak 'check.svg' syntax (Bartosz Dziewoński)
+* Balance padding now that focus highlight is balanced (Ed Sanders)
+* BookletLayout: Add description and example (Kirsten Menger-Anderson)
+* Bring in remaining VisualEditor icons for Apex and MediaWiki themes (James D. Forrester)
+* Choose can't emit with a null item (Ed Sanders)
+* Correctly position popups in RTL (Moriel Schottlender)
+* Fix opacity of icons/indicators in disabled DecoratedOptionWidget (Ed Sanders)
+* IconWidget: Mix in FlaggedElement (Bartosz Dziewoński)
+* Increase specificity of ButtonElement icon and indicator styles (Bartosz Dziewoński)
+* Make colorize-svg.js actually work more often (Bartosz Dziewoński)
+* MediaWiki theme: Allow intention flags for non-buttons (Andrew Garrett)
+* MediaWiki theme: Fix icon opacity for disabled ButtonOptionWidgets (Bartosz Dziewoński)
+* MediaWiki theme: Use checkbox icon per mockups (Bartosz Dziewoński)
+* MediaWiki, Apex: Provide an RTL variant for the help icon (James D. Forrester)
+* MenuLayout: Correct documentation (Bartosz Dziewoński)
+* OutlineOption: Add description (Kirsten Menger-Anderson)
+* PageLayout: Add description (Kirsten Menger-Anderson)
+* Process: Add description (Kirsten Menger-Anderson)
+* Properly support LTR/RTL icon versions in colorize-svg.js (Bartosz Dziewoński)
+* Refactor icon handling again (Bartosz Dziewoński)
+* Remove line height reset for windows (Ed Sanders)
+* Restore font family definitions to form elements (Ed Sanders)
+* Revert "Button styles between OOJS and MW" (Bartosz Dziewoński)
+* StackLayout: Add description and example (Kirsten Menger-Anderson)
+* build: Add a 'generated automatically' banner to demo.rtl.css (Bartosz Dziewoński)
+* build: Generate prettier task names for 'colorizeSvg' (Bartosz Dziewoński)
+* build: Have separate 'cssjanus' target for demo.rtl.css (Bartosz Dziewoński)
+* build: Simplify 'fileExists' task configuration (Bartosz Dziewoński)
+* build: Support (poorly) per-language icon versions in colorize-svg.js (Bartosz Dziewoński)
+* build: Update grunt-banana-checker to v0.2.1 (James D. Forrester)
+
+## v0.9.4 / 2015-03-25
+* ButtonElement: Clarify description (Kirsten Menger-Anderson)
+* ButtonElement: Disable line wrapping on buttons (Ed Sanders)
+* Compensate for loss of margin when opening modals (Ed Sanders)
+* FieldLayout: Clarify description and mark private methods (Kirsten Menger-Anderson)
+* FieldsetLayout: Add description and example (Kirsten Menger-Anderson)
+* FormLayout: Add description, example, and mark private method (Kirsten Menger-Anderson)
+* Layout: Add description (Kirsten Menger-Anderson)
+* LookupElement: Add description and mark private and protected methods (Kirsten Menger-Anderson)
+* LookupElement: Fix typo in docs (Bartosz Dziewoński)
+* Make outline controls abilities configurable (Trevor Parscal)
+* MenuLayout: Reorder styles (Bartosz Dziewoński)
+* MenuSectionOptionWidget: Add description and example (Kirsten Menger-Anderson)
+* ProcessDialog#executeAction: Don't eat parent's return value (Roan Kattouw)
+* PanelLayout: Add description and example (Kirsten Menger-Anderson)
+* Reduce thickness of toolbar border in MediaWiki (Ed Sanders)
+* SearchWidget: Add description and mark private methods (Kirsten Menger-Anderson)
+* TabIndexElement: Mark private method (Kirsten Menger-Anderson)
+
+## v0.9.3 / 2015-03-19
+* Add .mailmap file (Roan Kattouw)
+* Add Kirsten to AUTHORS.txt (Roan Kattouw)
+* Dialog: Fix links to static properties (Kirsten Menger-Anderson)
+* DraggableGroupElement: Clarify description and mark private methods (Kirsten Menger-Anderson)
+* Fix code style in @examples (Ed Sanders)
+* FlaggedElement: Add example and clarify description (Kirsten Menger-Anderson)
+* GroupElement: Clarify description (Kirsten Menger-Anderson)
+* IndicatorElement: Clarify description (Kirsten Menger-Anderson)
+* LookupElement: Add optional config field for suggestions when empty (Matthew Flaschen)
+* MenuSelectWidget: Clarify description (Kirsten Menger-Anderson)
+* ProcessDialog: send an array to showErrors in failed executeAction (Moriel Schottlender)
+* TabIndexedElement: Clarify description (Kirsten Menger-Anderson)
+* TitledElement: Clarify description (Kirsten Menger-Anderson)
+* Update OOjs to v1.1.6 (James D. Forrester)
+* Widget: Clarify description (Kirsten Menger-Anderson)
+* Window: Clarify description of setDimensions method (Kirsten Menger-Anderson)
+* WindowManager: Clarify description and mark private methods (Kirsten Menger-Anderson)
+* demo: Add one more toolbars demo (Bartosz Dziewoński)
+
+## v0.9.2 / 2015-03-12
+* Toolbar: Be less aggressive with 'white-space: nowrap' (Bartosz Dziewoński)
+* Window: Revert changes from 521061dd (Bartosz Dziewoński)
+
+## v0.9.1 / 2015-03-11
+* Syncing some button styles with MediaWiki UI (kaldari)
+* MediaWiki Theme: Add the progressive variant to the check icon (Prateek Saxena)
+* demo: Fix typo in toolbars demo (Bartosz Dziewoński)
+* TextInputWidget: Use MutationObserver for #onElementAttach support (Bartosz Dziewoński)
+* TextInputWidget: Adjust size and label on first focus, too (Bartosz Dziewoński)
+* jsduck: Add MouseEvent and KeyboardEvent to externals (Timo Tijhof)
+* jsduck: Set --processes=0 to fix warnings-exit-nonzero (Timo Tijhof)
+* Dialog: Mark private methods and add description of methods and configs (Kirsten Menger-Anderson)
+* ProcessDialog: Add description and example and mark private methods (Kirsten Menger-Anderson)
+* MessageDialog: Add description, example, and mark private methods (Kirsten Menger-Anderson)
+* build: Remove obsolete 'build' task from grunt-doc (Timo Tijhof)
+* build: Move pre/post 'doc' task into package.json (Timo Tijhof)
+* Remove remnants of window isolation (Bartosz Dziewoński)
+* demo: Simplify @media styles (Bartosz Dziewoński)
+* PanelLayout: Add 'framed' config option (Bartosz Dziewoński)
+* TextInputMenuSelectWidget: Add description and mark private methods (Kirsten Menger-Anderson)
+* Toolbar: Tighten whitespace on narrow displays (Bartosz Dziewoński)
+* demo: Use popup with head in the toolbars demo (Bartosz Dziewoński)
+* Remove half-baked touch event handling (Bartosz Dziewoński)
+* ButtonElement: Use #setButtonElement correctly (Bartosz Dziewoński)
+* WindowManager: Documentation typo (Ed Sanders)
+* ButtonInputWidget: Clarify description of configs and methods (Kirsten Menger-Anderson)
+* Icon width should only be applied if there is an icon (Moriel Schottlender)
+* package.json: Bump grunt-svg2png to 0.2.7 (Bartosz Dziewoński)
+* Add warning variant to MediaWiki set (Mark Holmquist)
+* Button styles between OOJS and MW (nirzar)
+* AUTHORS: Add Derk-Jan Hartman (Derk-Jan Hartman)
+* ActionSet: Add description for events and clarify method descriptions (Kirsten Menger-Anderson)
+* demo: Load styles before building demo widgets (not asynchronously) (Bartosz Dziewoński)
+* ActionWidget: Fix bad copy-paste in documentation (Bartosz Dziewoński)
+* Window: Clarify descriptions of methods and configs (Kirsten Menger-Anderson)
+* OutlineSelectWidget: Add description (Kirsten Menger-Anderson)
+* OutlineControlsWidget: Add description (Kirsten Menger-Anderson)
+* MediaWiki Theme: Add "Wikicon" icons (James D. Forrester)
+* build: Set 'generateExactDuplicates: true' for CSSJanus (Bartosz Dziewoński)
+* build: Implement basic image flipping support in colorize-svg (Bartosz Dziewoński)
+* ActionWidget: Clarify description and mark private method (Kirsten Menger-Anderson)
+* ActionSet: Clarify description (Kirsten Menger-Anderson)
+* InputWidget: Clarify description (Kirsten Menger-Anderson)
+* MediaWiki Theme: textInputWidget: Update focus state (Prateek Saxena)
+* Only prevent default for handled keypresses (Brad Jorsch)
+
+## v0.9.0 / 2015-03-04
+* [BREAKING CHANGE] Remove innerOverlay (Ed Sanders)
+* [BREAKING CHANGE] TextInputWidget: Remove 'icon' and 'indicator' events (Bartosz Dziewoński)
+* [BREAKING CHANGE] Remove deprecated LookupInputWidget (Bartosz Dziewoński)
+* [BREAKING CHANGE] Remove deprecated GridLayout (Bartosz Dziewoński)
+
+* Only modify body class when first/last window opens/closes (Ed Sanders)
+* InputWidget: Focus checkboxes and radios, too, when the label is clicked (Bartosz Dziewoński)
+* Move `OO.ui.infuse` to `OO.ui.Element.static.infuse`. (C. Scott Ananian)
+* Don't call LabelElement constructor twice for ActionFieldLayouts (Roan Kattouw)
+* PopupElement: Add description (Kirsten Menger-Anderson)
+* Make icon and indicator container sizes consistent (Ed Sanders)
+* SelectWidget: Marked protected methods and clarified choose/press descriptions (Kirsten Menger-Anderson)
+* demo: Call Toolbar#initialize in toolbar demo (Bartosz Dziewoński)
+* demo: Add PopupTool to toolbar demo (Bartosz Dziewoński)
+* PopupTool: Tool constructor takes a toolGroup, not a toolbar (Bartosz Dziewoński)
+* Infer retry button action flags from symbolic name (Trevor Parscal)
+* Revert "Unbreak form submission in JavaScript" (Bartosz Dziewoński)
+* PopupWidget: Add description, example, and mark private methods (Kirsten Menger-Anderson)
+* ToggleButtonWidget: Add description, example, and mark private method (Kirsten Menger-Anderson)
+* ToggleWidget: Add description (Kirsten Menger-Anderson)
+* ToggleSwitchWidget: Add description, example, and mark private methods (Kirsten Menger-Anderson)
+* build: Add disconnect tolerance to karma config (James D. Forrester)
+* ProgressBar: Add description and example (Kirsten Menger-Anderson)
+* CheckboxInputWidget: Add description and example (Kirsten Menger-Anderson)
+* Use the correct color for gray buttons (Prateek Saxena)
+* RadioInputWidget: Add description and example (Kirsten Menger-Anderson)
+* Fix invalid use of border shorthand syntax (Timo Tijhof)
+* Restore previous toolbar items margins and padding (Bartosz Dziewoński)
+* ProcessDialog: Dismiss errors on teardown (Moriel Schottlender)
+* build: Remove footer override from jsduck (Timo Tijhof)
+* FieldLayout: Fix display of bulleted list (Kirsten Menger-Anderson)
+* Use only two variables each for each semantic color (Prateek Saxena)
+* tests: Run JS/PHP tests for widgets with required parameters, too (Bartosz Dziewoński)
+* TextInputWidget: Add description, example, and mark private methods (Kirsten Menger-Anderson)
+* tests: Add infusion tests (Bartosz Dziewoński)
+* PopupWidget: Add keydown listener and hide popup on ESC (Prateek Saxena)
+* DropdownInputWidget: Add description, example, and mark private method (Kirsten Menger-Anderson)
+* ComboBoxWidget: Add description, example, and mark private methods (Kirsten Menger-Anderson)
+* DecoratedOptionWidget: Add description and example (Kirsten Menger-Anderson)
+* SelectWidget: Add example and link to decorated option widget (Kirsten Menger-Anderson)
+* GroupWidget and ItemWidget: Mark private (Kirsten Menger-Anderson)
+* Fake toolbar group nesting (Bartosz Dziewoński)
+* LabelWidget: Add description, example, and mark private method (Kirsten Menger-Anderson)
+* IndicatorWidget: Add description and example (Kirsten Menger-Anderson)
+
+## v0.8.3 / 2015-02-26
+* Revert "Unbreak form submission in JavaScript" (Bartosz Dziewoński)
+
+## v0.8.2 / 2015-02-26
+* testsuitegenerator: Exclude 'text' parameter from tests, like 'content' (Bartosz Dziewoński)
+* PHP TitledElement: Actually set $this->title (Bartosz Dziewoński)
+* PHP PanelLayout: Fix getConfig() for 'expanded' config option (Bartosz Dziewoński)
+* WindowManager: Don't pass 'this' to window factory method (Bartosz Dziewoński)
+
+## v0.8.1 / 2015-02-25
+* [DEPRECATING CHANGE] Rename setPosition to setLabelPosition (Ed Sanders)
+
+* Element.php: Add "data" property (C. Scott Ananian)
+* Element.php: Add "text" configuration option (C. Scott Ananian)
+* Work around Safari 8 misrendering checkboxes in SVG-only distribution (Bartosz Dziewoński)
+* TextInputWidget: Fix appearance of icons and labels when disabled (Ed Sanders)
+* Remove '$: this.$' from code examples (Bartosz Dziewoński)
+* Prefer OO.isPlainObject to $.isPlainObject (Bartosz Dziewoński)
+* Error: Fix function name (Bartosz Dziewoński)
+* build: Use grunt-contrib-copy instead of custom 'copy' task (Timo Tijhof)
+* Element.php: Tweak docs (Bartosz Dziewoński)
+* docparser.rb: Ruby 1.9.3 compatibility (Bartosz Dziewoński)
+* Move toggle() from Widget to Element (Moriel Schottlender)
+* build: Include 'lib' and 'dist' in jsduck output (Timo Tijhof)
+* Fix typo: contian → contain (Bartosz Dziewoński)
+* MediaWiki Theme: Drop unnecessary pseudo-element of CheckboxInputWidget (Timo Tijhof)
+* MediaWiki Theme: Drop unnecessary pseudo-element of RadioInputWidget (Timo Tijhof)
+* MediaWiki theme: Simplify spacing of checkboxes/radios in FieldLayouts (Bartosz Dziewoński)
+* Add disabled RadioInputWidget to demo (Bartosz Dziewoński)
+* RadioOptionWidget: Make disabling single options work (Bartosz Dziewoński)
+* composer.json: Add description field (Kunal Mehta)
+* IconElement: Add description for config options (Kirsten Menger-Anderson)
+* IndicatorElement: Add description for configs and static properties (Kirsten Menger-Anderson)
+* DraggableElement: Mark private methods and add description to events (Kirsten Menger-Anderson)
+* Element: Add description for configs and static property (Kirsten Menger-Anderson)
+* LabelElement: Add description, config description, static property description (Kirsten Menger-Anderson)
+* TitledElement: Add description and config and static descriptions (Kirsten Menger-Anderson)
+* ComboBox: Use combobox role (Derk-Jan Hartman)
+* IconElement: Add description of methods (Kirsten Menger-Anderson)
+* Follow-up bade83bfdfc: actually remove ../ (Roan Kattouw)
+* Remove loop length check (Ed Sanders)
+* PopupButtonWidget: Add description and example and mark private method (Kirsten Menger-Anderson)
+* FlaggedElement: Add description of event and config option (Kirsten Menger-Anderson)
+* Unbreak docparser.rb (Bartosz Dziewoński)
+* Allow passing positional parameters inside the config object (Bartosz Dziewoński)
+* Run JS/PHP comparison tests using karma (Bartosz Dziewoński)
+* test: Reduce timeout in Process test from 100 to 10 (Timo Tijhof)
+* OptionWidget: Add description and descriptions of methods (Kirsten Menger-Anderson)
+* FormLayout: Allow adding child layouts via config (Bartosz Dziewoński)
+* Teach docparser about @member, @see, and PHP pass-by-reference (&$foo). (C. Scott Ananian)
+* Element: Add `content` config option, matching PHP side. (C. Scott Ananian)
+* SelectWidget: Add description for config, methods, events (Kirsten Menger-Anderson)
+* Serialize PHP widget state into data-ooui attribute (C. Scott Ananian)
+* Implement OO.ui.infuse to reconstitute PHP widgets in client-side JS (C. Scott Ananian)
+* ButtonSelectWidget: Add description and example (Kirsten Menger-Anderson)
+* RadioOptionWidget: Add description (Kirsten Menger-Anderson)
+* ButtonOptionWidget: Add description (Kirsten Menger-Anderson)
+* RadioSelectWidget: Add description and example (Kirsten Menger-Anderson)
+* Set proper spacing between interleaved FieldsetLayouts and FormLayouts (Bartosz Dziewoński)
+* MenuOptionWidget: Add description (Kirsten Menger-Anderson)
+* Unbreak form submission in JavaScript (Bartosz Dziewoński)
+* MenuSelectWidget: Add description and mark protected method (Kirsten Menger-Anderson)
+* TabIndexelement: Add description, example, and mark private method (Kirsten Menger-Anderson)
+* Add "composer test" command to lint PHP files and run phpcs (Kunal Mehta)
+* Update OOjs to v1.1.5 (James D. Forrester)
+
+
+## v0.8.0 / 2015-02-18
+* [BREAKING CHANGE] Make default distribution provide SVG with PNG fallback (Bartosz Dziewoński)
+
+* [DEPRECATING CHANGE] TextInputWidget: Deprecate 'icon' and 'indicator' events (Bartosz Dziewoński)
+
+* ButtonElement: add protected to event handlers (Kirsten Menger-Anderson)
+* docs: Make @example documentation tag work (Roan Kattouw)
+* TextInputWidget: Hide mixin components when unused (Ed Sanders)
+* DropdownWidget: Simplify redundant code (Bartosz Dziewoński)
+* Update PHP widgets for accessibility-related changes in JS widgets (Bartosz Dziewoński)
+* TabIndexedElement: Allow tabIndex property to be null (C. Scott Ananian)
+* ButtonElement: Add description (Kirsten Menger-Anderson)
+* Add missing ButtonInputWidget.less and corresponding mixin (Bartosz Dziewoński)
+* Various fixes to the PHP implementation (C. Scott Ananian)
+* Use Array.isArray instead of $.isArray (C. Scott Ananian)
+* TextInputWidget: Use margins for moving the label (Ed Sanders)
+* DraggableGroupElement: Add description (Kirsten Menger-Anderson)
+* demo: Add horizontal alignment test (Bartosz Dziewoński)
+* build: Pass RuboCop, customize settings (Bartosz Dziewoński)
+* Widget: Add description (Kirsten Menger-Anderson)
+* ButtonInputWidget: Add description and example (Kirsten Menger-Anderson)
+* Dialog: Add description and example (Kirsten Menger-Anderson)
+* DraggableElement: Add description (Kirsten Menger-Anderson)
+* docparser: Add support for 'protected' methods (Bartosz Dziewoński)
+* testsuitegenerator: Only test every pair of config options rather than every triple (Bartosz Dziewoński)
+* TextInputWidget: Don't add label position classes when there's no label (Bartosz Dziewoński)
+* Update JS/PHP comparison test suite (Bartosz Dziewoński)
+* tests: Fix the check for properties (Bartosz Dziewoński)
+* TextInputWidget: Add missing LabelElement mixin documentation (Ed Sanders)
+* Follow-up c762da42: fix ProcessDialog error handling (Roan Kattouw)
+* MediaWiki Theme: Add focus state for frameless button (Prateek Saxena)
+* TextInputWidget: Allow maxLength of 0 in JS (matching PHP) (Bartosz Dziewoński)
+* TextInputWidget: Only put $label in the DOM if needed (Bartosz Dziewoński)
+* MediaWiki Theme: Use white icons for disabled buttons (Bartosz Dziewoński)
+* Follow-up 6a6bb90ab: Update CSS file path in eg-iframe.html (Roan Kattouw)
+* Element: Add description (Kirsten Menger-Anderson)
+* PHP: Remove redundant documentation for getInputElement() (Bartosz Dziewoński)
+* Some documentation tweaks (Bartosz Dziewoński)
+* FieldLayout: Add description (Kirsten Menger-Anderson)
+* FieldLayout: Clean up and remove lies (Bartosz Dziewoński)
+* FlaggedElement: Add description (Kirsten Menger-Anderson)
+* PHP demo: Correct path to CSS files (Bartosz Dziewoński)
+* MediaWikiTheme: Resynchronize PHP with JS (Bartosz Dziewoński)
+* ButtonWidget: Rename nofollow config option to noFollow (C. Scott Ananian)
+* GroupElement: Add description (Kirsten Menger-Anderson)
+* IconElement: Add description (Kirsten Menger-Anderson)
+* IconWidget: Add description and example (Kirsten Menger-Anderson)
+* IndicatorElement: Add description (Kirsten Menger-Anderson)
+* InputWidget: Add description (Kirsten Menger-Anderson)
+* SelectWidget: Add description (Kirsten Menger-Anderson)
+* MediaWiki Theme: Fix border width for frameless buttons' focus state (Prateek Saxena)
+* Window: Add description (Kirsten Menger-Anderson)
+* WindowManager: Add description (Kirsten Menger-Anderson)
+* ButtonGroupWidget: Add description and example (Kirsten Menger-Anderson)
+* DropdownWidget: Add @private to private methods (Kirsten Menger-Anderson)
+* Refactor keyboard accessibility of SelectWidgets (Bartosz Dziewoński)
+* ActionSet: Add description and example (Kirsten Menger-Anderson)
+* ActionSet: Add @private to onActionChange method (Kirsten Menger-Anderson)
+* ActionWidget: Add description (Kirsten Menger-Anderson)
+* ActionSet: Add description for specialFlags property (Kirsten Menger-Anderson)
+* DropdownWidget: Add description and example (Kirsten Menger-Anderson)
+* ButtonWidget: Add example and link (Kirsten Menger-Anderson)
+* IconElement: Add description and fix display of static properties (Kirsten Menger-Anderson)
+
+
+## v0.7.0 / 2015-02-11
+* [BREAKING CHANGE] Remove window isolation (Trevor Parscal)
+
+* [DEPRECATING CHANGE] GridLayout should no longer be used, instead use MenuLayout (Bartosz Dziewoński)
+
+* Fade in window frames separately from window overlays (Ed Sanders)
+* Fix initialisation of window visible (Ed Sanders)
+* SelectWidget: 'listbox' wrapper role, 'aria-selected' state on contents (Derk-Jan Hartman)
+* Cleanup unreachable code from DraggableGroupElement (Moriel Schottlender)
+* DraggableGroupElement: Unset dragged item when dropped (Moriel Schottlender)
+* Remove inline spacing from ButtonWidget (Roan Kattouw)
+* Make sure DraggableGroupElement supports button widgets (Moriel Schottlender)
+* demo: Use properties instead of attributes for <link> (Timo Tijhof)
+* Revert "Remove inline spacing from ButtonWidget" (Bartosz Dziewoński)
+* ToggleSwitchWidget: Accessibility improvements (Bartosz Dziewoński)
+* TextInputWidget: Add required attribute on the basis of required config (Prateek Saxena)
+* DropdownInputWidget: Fix undefined variable in PHP (Bartosz Dziewoński)
+* PHP demo: Just echo the autoload error message, don't trigger_error() (Bartosz Dziewoński)
+* demo: Stop inline consoles from generating white space (Bartosz Dziewoński)
+* demo: Reorder widgets into somewhat logical groupings (Bartosz Dziewoński)
+* demo: Add button style showcase from PHP demo (Bartosz Dziewoński)
+* PHP demo: Resynchronize with JS demo (Bartosz Dziewoński)
+* Stop treating ApexTheme class unfairly and make it proper (Bartosz Dziewoński)
+* PHP demo: Add Vector/Raster and MediaWiki/Apex controls (Bartosz Dziewoński)
+* Delete unused src/themes/apex/{raster,vector}.less (Bartosz Dziewoński)
+* {Checkbox,Radio}InputWidget: Add missing configuration initialization (Bartosz Dziewoński)
+* MediaWiki theme: Use distribution's image type for backgrounds (Bartosz Dziewoński)
+* tests: Just echo the autoload error message, don't trigger_error() (Bartosz Dziewoński)
+* MediaWiki theme: Fix non-clickability of radios and checkboxes (Bartosz Dziewoński)
+* Fix text input auto-height calculation (Ed Sanders)
+* MediaWiki theme: Consistent toggle button 'active' state (Bartosz Dziewoński)
+* RadioOptionWidget: Make it a <label/> (Bartosz Dziewoński)
+* MediaWiki theme: Correct flagged primary button text color when pressed (Bartosz Dziewoński)
+* FieldsetLayout: Tweak positioning of help icon (Bartosz Dziewoński)
+* TextInputWidget: Use aria-hidden for extra autosize textarea (Prateek Saxena)
+* Refactor clickability of buttons (Bartosz Dziewoński)
+* Remove usage of this.$ and config.$ (Trevor Parscal)
+* build: Bump various devDependencies (James D. Forrester)
+* History: Fix date typos (James D. Forrester)
+* TabIndexedElement: Actually allow tabIndex of -1 (Bartosz Dziewoński)
+* ListToolGroup: Remove hack for jQuery's .show()/.hide() (Bartosz Dziewoński)
+* PopupWidget: Set $clippable only once, correctly (Bartosz Dziewoński)
+* TextInputMenuSelectWidget: Correct documentation (Bartosz Dziewoński)
+* PopupElement: Correct documentation (Bartosz Dziewoński)
+* MediaWiki Theme: Rename @active to @pressed in button mixins (Prateek Saxena)
+* tools.less: Use distribution's image type and path for background (Prateek Saxena)
+* MediaWiki Theme: Fix background color for disabled buttons (Prateek Saxena)
+* MenuSelectWidget: Don't clobber other events when unbinding (Bartosz Dziewoński)
+* MenuSelectWidget: Codify current behavior of Tab closing the menu (Bartosz Dziewoński)
+* DropdownWidget, ComboBoxWidget: Make keyboard-accessible (Bartosz Dziewoński)
+* MenuSelectWidget: Remove dead code (Bartosz Dziewoński)
+* MediaWiki Theme: Rename active-* variables to pressed-* (Prateek Saxena)
+* ButtonWidget: Better handle non-string parameters in setHref/setTarget (C. Scott Ananian)
+* Make better use of 'scrollIntoViewOnSelect' in OptionWidgets (Bartosz Dziewoński)
+* ButtonWidget: Add "nofollow" option (C. Scott Ananian)
+* MediaWiki Theme: Rename @highlight to @active (Prateek Saxena)
+* MediaWiki Theme: Use darker color for frameless buttons (Prateek Saxena)
+* ButtonWidget: Add documentation (Kirsten Menger-Anderson)
+
+## v0.6.6 / 2015-02-04
+* TextInputWidget: Mostly revert "Don't try adjusting size when detached" (Bartosz Dziewoński)
+* Use css class instead of jQuery .show()/hide()/toggle() (Moriel Schottlender)
+* build: Use karma to v0.12.31 (Timo Tijhof)
+* Use standard border colours for progress bars (Ed Sanders)
+* Remove disabled elements from keyboard navigation flow (Derk-Jan Hartman)
+* Fix BookletLayout#toggleOutline to use MenuLayout method (Ed Sanders)
+* Use CSS overriding trick to support RTL in menu layouts (Ed Sanders)
+
+## v0.6.5 / 2015-02-01
+* Make BookletLayout inherit from MenuLayout instead of embedding a GridLayout (Ed Sanders)
+* ButtonElement: Unbreak 'pressed' state (Bartosz Dziewoński)
+
+## v0.6.4 / 2015-01-30
+* InputWidget: Resynchronize our internal .value with DOM .value in #getValue (eranroz)
+* demo: Remove nonexistent 'align' config option for a DropdownWidget (Bartosz Dziewoński)
+* MediaWiki theme: Reduce size of checkboxes and radio buttons by 20% (Ed Sanders)
+* MediaWiki theme: Remove SearchWidget's border now dialogs have outline (Ed Sanders)
+* TextInputWidget: Accept 'maxLength' configuration option (Bartosz Dziewoński)
+* MediaWiki Theme: Adjust ButtonSelectWidget, ButtonGroupWidget highlights (Prateek Saxena)
+* Update OOjs to v1.1.4 and switch to the jQuery-optimised version (James D. Forrester)
+* build: Bump devDependencies and fix up (James D. Forrester)
+* Seriously work around the Chromium scrollbar bug for good this time (Bartosz Dziewoński)
+* Introduce and use TabIndexedElement (Bartosz Dziewoński)
+* AUTHORS: Update for the last six months' work (James D. Forrester)
+* Set input direction in html prop rather than css rule (Moriel Schottlender)
+* Introduce DropdownInputWidget (Bartosz Dziewoński)
+* Remove the 'flash' feature from MenuSelectWidget and OptionWidget (Bartosz Dziewoński)
+* InputWidget: Clarify documentation of #getInputElement (Bartosz Dziewoński)
+* Make sure there is a page before focusing in BookletLayout (Moriel Schottlender)
+* Provide default margins for buttons and other widgets (Bartosz Dziewoński)
+* OptionWidget: Unbreak 'pressed' state (Bartosz Dziewoński)
+* TextInputWidget: Remove superfluous role=textbox (Derk-Jan Hartman)
+* MediaWiki theme: Tweak some more border-radii (Bartosz Dziewoński)
+* Widget: Set aria-disabled too in #setDisabled (Derk-Jan Hartman)
+* Twiddle things (Ed Sanders)
+* Add help icon for FieldsetLayout (Moriel Schottlender)
+* PopupButtonWidget: Set aria-haspopup to true (Prateek Saxena)
+* ToggleButtonWidget: Set aria-pressed when changing value (Derk-Jan Hartman)
+* ActionFieldLayout: Add 'nowrap' to the button (Moriel Schottlender)
+* demo: Have multiline text in multiline widgets (Bartosz Dziewoński)
+* Add inline labels to text widgets (Ed Sanders)
+* TextInputWidget: Don't try adjusting size when detached (Bartosz Dziewoński)
+* MediaWiki Theme: Adjust MenuOptionWidget selected state (Bartosz Dziewoński)
+* ToggleWidget: Use aria-checked (Prateek Saxena)
+* MediaWiki theme: Unbreak disabled buttons (Bartosz Dziewoński)
+* MediaWiki theme: Fix background issues with disabled buttons (Roan Kattouw)
+* ButtonOptionWidget: Add the TabIndexedElement mixin (Derk-Jan Hartman)
+* Remove labelPosition check (Ed Sanders)
+* Fix opening/closing animation on windows (Roan Kattouw)
+* Add MenuLayout (Ed Sanders)
+* Add simpler window#updateSize API (Ed Sanders)
+
+## v0.6.3 / 2015-01-14
+* [DEPRECATING CHANGE] LookupInputWidget should no longer be used, instead use LookupElement
+
+* MediaWiki Theme: Adjust toolbar popups' border and shadows (Bartosz Dziewoński)
+* MediaWiki Theme: Don't use 'box-shadow' to produce thin grey lines in dialogs (Bartosz Dziewoński)
+* demo: Switch the default theme from 'Apex' to 'MediaWiki' (Ricordisamoa)
+* Toolbar: Update #initialize docs (Bartosz Dziewoński)
+* Add an ActionFieldLayout (Moriel Schottlender)
+* dialog: Provide a 'larger' size for things for which 'large' isn't enough (James D. Forrester)
+* Synchronize ComboBoxWidget and DropdownWidget styles (Bartosz Dziewoński)
+* Replace old&busted LookupInputWidget with new&hot LookupElement (Bartosz Dziewoński)
+
+## v0.6.2 / 2015-01-09
+* WindowManager#removeWindows: Documentation fix (Ed Sanders)
+* Clear windows when destroying window manager (Ed Sanders)
+* MediaWiki theme: Slightly reduce size of indicator arrows (Ed Sanders)
+* MediaWiki Theme: Remove text-shadow on button (Prateek Saxena)
+* MediaWiki Theme: Fix focus state for buttons (Prateek Saxena)
+* MediaWiki Theme: Fix disabled state of buttons (Prateek Saxena)
+* MediaWiki Theme: Fix overlap between hover and active states (Prateek Saxena)
+* Make @anchor-size a less variable and calculate borders from it (Ed Sanders)
+* PHP LabelElement: Actually allow non-plaintext labels (Bartosz Dziewoński)
+* MediaWiki Theme: Add state change transition to checkbox (Prateek Saxena)
+* Synchronize @abstract class annotations between PHP and JS (Bartosz Dziewoński)
+* Add 'lock' icon (Trevor Parscal)
+* Don't test abstract classes (Bartosz Dziewoński)
+* Element: Add support for 'id' config option (Bartosz Dziewoński)
+* testsuitegenerator.rb: Handle inheritance chains (Bartosz Dziewoński)
+* TextInputWidget: Add support for 'autofocus' config option (Bartosz Dziewoński)
+* tests: Don't overwrite 'id' attribute (Bartosz Dziewoński)
+
+## v0.6.1 / 2015-01-05
+* Remove use of Math.round() for offset and position pixel values (Bartosz Dziewoński)
+* Update JSPHP-suite.json (Bartosz Dziewoński)
+* ButtonElement: Inherit all 'font' styles, not only 'font-family' (Bartosz Dziewoński)
+* FieldsetLayout: Shrink size of label and bump the weight to compensate (James D. Forrester)
+* IndicatorElement: Fix 'indicatorTitle' config option (Bartosz Dziewoński)
+* Error: Unmark as @abstract (Bartosz Dziewoński)
+* build: Update various devDependencies (James D. Forrester)
+* readme: Update badges (Timo Tijhof)
+* readme: No need to put the same heading in twice (James D. Forrester)
+
+## v0.6.0 / 2014-12-16
+* [BREAKING CHANGE] PopupToolGroup and friends: Pay off technical debt (Bartosz Dziewoński)
+* ButtonGroupWidget: Remove weird margin-bottom: -1px; from theme styles (Bartosz Dziewoński)
+* Prevent parent window scroll in modal mode using overflow hidden (Ed Sanders)
+* MediaWiki theme: RadioInputWidget tweaks (Bartosz Dziewoński)
+* ClippableElement: Handle clipping with left edge (Bartosz Dziewoński)
+* Sprinkle some child selectors around in BookletLayout styles (Roan Kattouw)
+
+## v0.5.0 / 2014-12-12
+* [BREAKING CHANGE] FieldLayout: Handle 'inline' alignment better (Bartosz Dziewoński)
+* [BREAKING CHANGE] Split primary flag into primary and progressive (Trevor Parscal)
+* [BREAKING CHANGE] CheckboxInputWidget: Allow setting HTML 'value' attribute (Bartosz Dziewoński)
+* MediaWiki theme: checkbox: Fix states according to spec (Prateek Saxena)
+* MediaWiki theme: Add radio buttons (Prateek Saxena)
+* MediaWiki theme: Use gray instead of blue for select and highlight (Trevor Parscal)
+* MediaWiki theme: Copy .theme-oo-ui-outline{Controls,Option}Widget from Apex (Bartosz Dziewoński)
+* MediaWiki theme: Add thematic border to the bottom of toolbars (Bartosz Dziewoński)
+* MediaWiki theme: Extract @active-color variable (Bartosz Dziewoński)
+* MediaWiki theme: Add hover state to listToolGroup (Trevor Parscal)
+* MediaWiki theme: Add state transition to radio buttons (Prateek Saxena)
+* MediaWiki theme: Make button sizes match Apex (Trevor Parscal)
+* MediaWiki theme: Improve search widget styling (Trevor Parscal)
+* build: Use String#slice instead of discouraged String#substr (Timo Tijhof)
+* Element.getClosestScrollableContainer: Use 'body' or 'documentElement' based on browser (Prateek Saxena)
+* testsuitegenerator: Actually filter out non-unique combinations (Bartosz Dziewoński)
+* Fix primary button description text (Niklas Laxström)
+* Give non-isolated windows a tabIndex for selection holding (Ed Sanders)
+* Call .off() correctly in setButtonElement() (Roan Kattouw)
+* RadioInputWidget: Remove documentation lies (Bartosz Dziewoński)
+* Don't set line-height of unset button labels (Bartosz Dziewoński)
+* Temporarily remove position:absolute on body when resizing (Ed Sanders)
+* Kill the escape keydown event after handling a window close (Ed Sanders)
+* PopupWidget: Remove box-shadow rule that generates invisible shadow (Bartosz Dziewoński)
+* ClippableElement: 7 is a better number than 10 (Bartosz Dziewoński)
+* FieldLayout: In styles, don't assume that label is given (Bartosz Dziewoński)
+* TextInputWidget: Set vertical-align: middle, like buttons (Bartosz Dziewoński)
+* FieldLayout: Synchronise PHP with JS (Bartosz Dziewoński)
+* FieldLayout: Use <label> for this.$body, not this.$element (Bartosz Dziewoński)
+* Account for <html> rather than <body> being the scrollable root in Chrome (Bartosz Dziewoński)
+* GridLayout: Don't round to 1% (Bartosz Dziewoński)
+* README.md: Drop localisation update auto-commits from release notes (James D. Forrester)
+* README.md: Point to Phabricator, not Bugzilla (James D. Forrester)
+
+## v0.4.0 / 2014-12-05
+* [BREAKING CHANGE] Remove deprecated Element#onDOMEvent and #offDOMEvent (Bartosz Dziewoński)
+* [BREAKING CHANGE] Make a number of Element getters static (Bartosz Dziewoński)
+* [BREAKING CHANGE] Rename BookletLayout#getPageName → #getCurrentPageName (Bartosz Dziewoński)
+* demo: Don't put buttons in a FieldsetLayout without FieldLayouts around them (Bartosz Dziewoński)
+* IconElement: Add missing #getIconTitle (Bartosz Dziewoński)
+* SelectWidget: Rewrite #getRelativeSelectableItem (Bartosz Dziewoński)
+* Follow-up I859ff276e: Add cursor files to repo (Trevor Parscal)
+
+## v0.3.0 / 2014-12-04
+* [BREAKING CHANGE] ButtonWidget: Don't default 'target' to 'blank' (Bartosz Dziewoński)
+* InputWidget: Update DOM value before firing 'change' event (Bartosz Dziewoński)
+* TextInputWidget: Reuse a single clone instead of appending and removing new ones (Prateek Saxena)
+* build: Have grunt watch run 'quick-build' instead of 'build' (Prateek Saxena)
+* MediaWiki Theme: Reduce indentation in theme-oo-ui-checkboxInputWidget (Prateek Saxena)
+* Adding DraggableGroupElement and DraggableElement mixins (Moriel Schottlender)
+* Remove window even if closing promise rejects (Ed Sanders)
+* Fix lies in documentation (Trevor Parscal)
+
+## v0.2.4 / 2014-12-02
+* TextInputWidget: Use .css( propertyName, value ) instead of .css( properties) for single property (Prateek Saxena)
+* TextInputWidget: Stop adjustSize if the value of the textarea is the same (Prateek Saxena)
+* Window: Avoid height flickering when resizing dialogs (Bartosz Dziewoński)
+* MessageDialog: Fit actions again when the dialog is resized (Bartosz Dziewoński)
+
+## v0.2.3 / 2014-11-26
+* Dialog: Only handle escape events when open (Alex Monk)
+* Pass original event with TextInputWidget#enter (Ed Sanders)
+* Add missing documentation to ToolFactory (Ed Sanders)
+* BookletLayout: Make #focus not crash when there are zero pages or when there is no outline (Roan Kattouw)
+* Window: Disable transitions when changing window height to calculate content height (Bartosz Dziewoński)
+* MessageDialog: Add Firefox hack for scrollbars when sizing dialogs (Bartosz Dziewoński)
+* Fix RadioOptionWidget demos (Trevor Parscal)
+* RadioOptionWidget: Remove lies from documentation (Trevor Parscal)
+* RadioOptionWidget: Increase rule specificity to match OptionWidget (Bartosz Dziewoński)
+* MessageDialog: Actually correctly calculate and set height (Bartosz Dziewoński)
+
+## v0.2.2 / 2014-11-25
+* LabelWidget: Add missing documentation for input configuration option (Ed Sanders)
+* MessageDialog: Fit actions after updating window size, not before (Bartosz Dziewoński)
+* MessageDialog: Use the right superclass (Bartosz Dziewoński)
+* ProcessDialog, MessageDialog: Support iconed actions (Bartosz Dziewoński)
+* Remove padding from undecorated option widgets (Ed Sanders)
+* build: Add .npmignore (Timo Tijhof)
+
+## v0.2.1 / 2014-11-24
+* Start the window opening transition before ready, not after (Roan Kattouw)
+* Add focus method to BookletLayout (Roan Kattouw)
+* Add missing History.md file now we're a proper repo (James D. Forrester)
+* README.md: Update introduction, badges, advice (James D. Forrester)
+* LabelElement: Kill inline styles (Bartosz Dziewoński)
+* composer: Rename package to 'oojs-ui' and require php 5.3.3 (Timo Tijhof)
+
+## v0.2.0 / 2014-11-17
+* First versioned release
+
+## v0.1.0 / 2013-11-13
+* Initial export of repo
diff --git a/vendor/oojs/oojs-ui/LICENSE-MIT b/vendor/oojs/oojs-ui/LICENSE-MIT
new file mode 100644
index 00000000..1eef0125
--- /dev/null
+++ b/vendor/oojs/oojs-ui/LICENSE-MIT
@@ -0,0 +1,20 @@
+Copyright 2011-2015 OOjs Team and other contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/oojs/oojs-ui/README.md b/vendor/oojs/oojs-ui/README.md
new file mode 100644
index 00000000..e537752e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/README.md
@@ -0,0 +1,79 @@
+[![npm](https://img.shields.io/npm/v/oojs-ui.svg?style=flat)](https://www.npmjs.com/package/oojs-ui) [![Packagist](https://img.shields.io/packagist/v/oojs/oojs-ui.svg?style=flat)](https://packagist.org/packages/oojs/oojs-ui) [![David](https://img.shields.io/david/dev/wikimedia/oojs-ui.svg?style=flat)](https://david-dm.org/wikimedia/oojs-ui#info=devDependencies)
+
+OOjs UI
+=================
+
+OOjs UI is a modern JavaScript UI toolkit for browsers. It provides a library of common widgets, layouts and windows that are ready to use, as well as many foundational classes for constructing custom user interfaces. The library was originally created for use by [VisualEditor](https://www.mediawiki.org/wiki/VisualEditor), which uses it for its entire user interface, and is now completely independent, and more useful and convenient for other use cases.
+
+Quick start
+----------
+
+This library is available as an [npm](https://npmjs.org/) package! Install it right away:
+<pre lang="bash">
+npm install oojs-ui
+</pre>
+
+If you don't want to use npm, you can:
+
+1. Clone the repo, `git clone https://git.wikimedia.org/git/oojs/ui.git`.
+
+1. Install Grunt command-line utility:<br/>`$ npm install -g grunt-cli`
+
+1. Install dev dependencies and build the distribution files:<br/>`$ npm install`
+
+1. You can now copy the distribution files from the dist directory into your project.
+
+
+Versioning
+----------
+
+We use the Semantic Versioning guidelines as much as possible.
+
+Releases will be numbered in the following format:
+
+`<major>.<minor>.<patch>`
+
+For more information on SemVer, please visit http://semver.org/.
+
+
+Issue tracker
+-------------
+
+Found a bug or missing feature? Please report it in the [issue tracker](https://phabricator.wikimedia.org/maniphest/task/create/?projects=PHID-PROJ-dgmoevjqeqlerleqzzx5)!
+
+
+Release
+----------
+
+Release process:
+<pre lang="bash">
+$ cd path/to/oojs-ui/
+$ git remote update
+$ git checkout -B release -t origin/master
+
+# Ensure tests pass
+$ npm install && npm test
+
+# Avoid using "npm version patch" because that creates
+# both a commit and a tag, and we shouldn't tag until after
+# the commit is merged.
+
+# Update release notes
+# Copy the resulting list into a new section on History.md
+$ git log --format='* %s (%aN)' --no-merges --reverse v$(node -e 'console.log(require("./package.json").version);')...HEAD | grep -v "Localisation updates from" | sort
+$ edit History.md
+
+# Update the version number
+$ edit package.json
+
+$ git add -p
+$ git commit -m "Tag vX.X.X"
+$ git review
+
+# After merging:
+$ git remote update
+$ git checkout origin/master
+$ git tag "vX.X.X"
+$ git push --tags
+$ npm publish
+</pre>
diff --git a/vendor/oojs/oojs-ui/bin/doccomparer.rb b/vendor/oojs/oojs-ui/bin/doccomparer.rb
new file mode 100644
index 00000000..cd3623df
--- /dev/null
+++ b/vendor/oojs/oojs-ui/bin/doccomparer.rb
@@ -0,0 +1,165 @@
+require 'pp'
+require_relative 'docparser'
+
+# convert [ {name: 'foo'}, … ] to { foo: {name: 'foo'}, … }
+def reindex arg
+ if arg.is_a?(Array) && arg.all?{|v| v.is_a? Hash }
+ Hash[ arg.map{|v| [ v[:name], reindex(v) ] } ]
+ elsif arg.is_a? Hash
+ arg.each_pair{|k, v| arg[k] = reindex(v) }
+ else
+ arg
+ end
+end
+
+def indent text, tabs
+ text == '' ? text : text.gsub(/^/, ' ' * tabs)
+end
+
+# whitespace-insensitive strings
+def canonicalize value
+ if value.is_a? String
+ value.strip.gsub(/\s+/, ' ')
+ elsif value.is_a? Array
+ value.map{|v| canonicalize v }
+ elsif value.is_a? Hash
+ value.each_pair{|k, v| value[k] = canonicalize v }
+ else
+ value
+ end
+end
+
+def sanitize_description text
+ cleanup_class_name(text)
+ .gsub('null and undefined', 'null')
+ .gsub('undefined and null', 'null')
+ .gsub('array()', '[]')
+ .gsub('jQuery|string|Function', 'string')
+ .gsub('jQuery', 'Tag')
+ .gsub('string|Function', 'string')
+ .gsub('object', 'array')
+ .gsub(/#(\w+)/, '\1()')
+ .gsub(/Object\.<.+?>/, 'array')
+end
+
+def smart_compare_process val, type
+ val[:description] = sanitize_description val[:description]
+
+ case type
+ when :class
+ val = val.dup
+ val[:mixins].delete 'OO.EventEmitter' # JS only
+ val[:mixins].delete 'PendingElement' # JS only
+ val.delete :parent if val[:parent] == 'ElementMixin' # PHP only
+ val.delete :methods
+ val.delete :properties
+ val.delete :events
+
+ when :method
+ if val[:name] == '#constructor'
+ val[:params].delete 'element' # PHP only
+ end
+ val[:config].each_pair{|_k, v|
+ default = v.delete :default
+ v[:description] << " (default: #{default})" if default
+ v[:description] = sanitize_description v[:description]
+ v[:type] = sanitize_description v[:type]
+ }
+ val[:params].each_pair{|_k, v|
+ default = v.delete :default
+ v[:description] << " (default: #{default})" if default
+ v[:description] = sanitize_description v[:description]
+ v[:type] = sanitize_description v[:type]
+ }
+ if val[:return]
+ val[:return][:description] = sanitize_description val[:return][:description]
+ val[:return][:type] = sanitize_description val[:return][:type]
+ end
+
+ when :property
+ val[:description] = sanitize_description val[:description]
+ val[:type] = sanitize_description val[:type]
+
+ end
+ val
+end
+
+def smart_compare a, b, a_name, b_name, type
+ a = smart_compare_process a, type
+ b = smart_compare_process b, type
+ compare_hash a, b, a_name, b_name
+end
+
+def smart_compare_methods a, b, a_name, b_name
+ smart_compare a, b, a_name, b_name, :method
+end
+
+def smart_compare_properties a, b, a_name, b_name
+ smart_compare a, b, a_name, b_name, :property
+end
+
+def compare_hash a, b, a_name, b_name, nested=:compare_hash
+ keys = (a ? a.keys : []) + (b ? b.keys : [])
+ out = keys.to_a.sort.uniq.map do |key|
+ a_val = a ? canonicalize(a[key]) : nil
+ b_val = b ? canonicalize(b[key]) : nil
+
+ if [a_val, b_val] == [{}, []] || [a_val, b_val] == [[], {}]
+ a_val, b_val = {}, {}
+ end
+
+ if a_val.is_a?(Hash) && b_val.is_a?(Hash)
+ comparison_result = indent method(nested).call(a_val, b_val, a_name, b_name), 2
+ if comparison_result.strip == ''
+ "#{key}: match" if $VERBOSE
+ else
+ "#{key}: MISMATCH\n#{comparison_result}"
+ end
+ else
+ if a_val == b_val
+ "#{key}: match" if $VERBOSE
+ elsif a_val.nil?
+ "#{key}: #{a_name} missing"
+ elsif b_val.nil?
+ "#{key}: #{b_name} missing"
+ else
+ "#{key}: MISMATCH\n #{a_name}: #{a_val.inspect}\n #{b_name}: #{b_val.inspect}"
+ end
+ end
+ end
+ out.compact.join "\n"
+end
+
+if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help']
+ $stderr.puts "usage: ruby [-v] #{$PROGRAM_NAME} <dirA> <dirB> <nameA> <nameB>"
+ $stderr.puts " ruby #{$PROGRAM_NAME} src php JS PHP > compare.txt"
+else
+ dir_a, dir_b, name_a, name_b = ARGV
+
+ js = parse_any_path dir_a
+ php = parse_any_path dir_b
+
+ js = reindex js
+ php = reindex php
+
+ (js.keys + php.keys).sort.uniq.each do |class_name|
+ where = [js.key?(class_name) ? name_a : nil, php.key?(class_name) ? name_b : nil].compact
+ puts "\n#{class_name}: #{where.join '/'}" if $VERBOSE || where.length == 2
+
+ if where.length == 2
+ data = {
+ 'Basic:' =>
+ smart_compare(js[class_name], php[class_name], name_a, name_b, :class),
+ 'Methods:' =>
+ compare_hash(js[class_name][:methods], php[class_name][:methods], name_a, name_b, :smart_compare_methods),
+ 'Properties:' =>
+ compare_hash(js[class_name][:properties], php[class_name][:properties], name_a, name_b, :smart_compare_properties),
+ }
+ data = data
+ .select{|_k, v| v != ''}
+ .map{|k, v| "#{k}\n#{indent v, 2}" }
+ .join("\n")
+ puts indent data, 2
+ end
+ end
+end
diff --git a/vendor/oojs/oojs-ui/bin/docparser.rb b/vendor/oojs/oojs-ui/bin/docparser.rb
new file mode 100644
index 00000000..9f58549b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/bin/docparser.rb
@@ -0,0 +1,243 @@
+require 'pp'
+require 'json'
+
+def parse_dir dirname
+ Dir.entries(dirname).map{|filename|
+ if filename == '.' || filename == '..'
+ nil
+ else
+ parse_any_path "#{dirname}/#{filename}"
+ end
+ }.compact.inject(:+)
+end
+
+def cleanup_class_name class_name
+ class_name.sub(/OO\.ui\./, '')
+end
+
+def parse_file filename
+ if filename !~ /\.(php|js)$/
+ return nil
+ end
+ filetype = filename[/\.(php|js)$/, 1].to_sym
+
+ text = File.read filename, encoding: 'utf-8'
+
+ # ewwww
+ # some docblocks are missing and we really need them
+ text = text.sub(/(?<!\*\/\n)^class/, "/**\n*/\nclass")
+ # text = text.sub('public static $targetPropertyName', "/**\n*/\npublic static $targetPropertyName")
+
+ # find all documentation blocks, together with the following line (unless it contains another docblock)
+ docblocks = text.scan(/\/\*\*[\s\S]+?\*\/\n[ \t]*(?:(?=\/\*\*)|.*)/)
+
+ current_class = nil
+ output = []
+ previous_item = {} # dummy
+
+ docblocks.each{|d|
+ kind = nil
+ previous_item = data = {
+ name: nil,
+ description: '',
+ parent: nil,
+ mixins: [],
+ methods: [],
+ properties: [],
+ events: [],
+ params: [],
+ config: [],
+ visibility: :public,
+ type: nil,
+ }
+ valid_for_all = %w[name description].map(&:to_sym)
+ valid_per_kind = {
+ class: valid_for_all + %w[parent mixins methods properties events abstract].map(&:to_sym),
+ method: valid_for_all + %w[params config return visibility static].map(&:to_sym),
+ property: valid_for_all + %w[type static].map(&:to_sym),
+ event: valid_for_all + %w[params].map(&:to_sym),
+ }
+
+ js_class_constructor = false
+ js_class_constructor_desc = ''
+ ignore = false
+
+ comment, code_line = d.split '*/'
+ comment.split("\n").each{|c|
+ next if c.strip == '/**'
+ c.sub!(/^[ \t]*\*[ \t]?/, '') # strip leading *
+
+ m = c.match(/^@(\w+)[ \t]*(.*)/)
+ unless m
+ previous_item[:description] << c + "\n"
+ next
+ end
+
+ keyword, content = m.captures
+
+ # handle JS class/constructor conundrum
+ if keyword == 'class' || keyword == 'constructor'
+ js_class_constructor = true
+ end
+
+ case keyword
+ when 'constructor'
+ # handle JS class/constructor conundrum
+ js_class_constructor_desc = data[:description]
+ data[:description] = ''
+ kind = :method
+ when 'class'
+ kind = :class
+ when 'method'
+ kind = :method
+ when 'property', 'var'
+ kind = :property
+ m = content.match(/^\{?(.+?)\}?( .+)?$/)
+ if m.captures
+ type, description = m.captures
+ data[:type] = type
+ data[:description] = description if description
+ end
+ when 'event'
+ kind = :event
+ data[:name] = content.strip
+ when 'extends'
+ data[:parent] = cleanup_class_name(content.strip)
+ when 'mixins'
+ data[:mixins] << cleanup_class_name(content.strip)
+ when 'param'
+ case filetype
+ when :js
+ type, name, default, description = content.match(/^\{(.+?)\} \[?([\w.$]+?)(?:=(.+?))?\]?( .+)?$/).captures
+ next if type == 'Object' && name == 'config'
+ data[:params] << {name: name, type: cleanup_class_name(type), description: description || '', default: default}
+ previous_item = data[:params][-1]
+ when :php
+ type, name, config, description = content.match(/^(\S+) \&?\$(\w+)(?:\['(\w+)'\])?( .+)?$/).captures
+ next if type == 'array' && name == 'config' && !config
+ if config && name == 'config'
+ data[:config] << {name: config, type: cleanup_class_name(type), description: description || ''}
+ previous_item = data[:config][-1]
+ else
+ data[:params] << {name: name, type: cleanup_class_name(type), description: description || ''}
+ previous_item = data[:params][-1]
+ end
+ end
+ when 'cfg' # JS only
+ type, name, default, description = content.match(/^\{(.+?)\} \[?([\w.$]+?)(?:=(.+?))?\]?( .+)?$/).captures
+ data[:config] << {name: name, type: cleanup_class_name(type), description: description || '', default: default}
+ previous_item = data[:config][-1]
+ when 'return'
+ case filetype
+ when :js
+ type, description = content.match(/^\{(.+?)\}( .+)?$/).captures
+ data[:return] = {type: cleanup_class_name(type), description: description || ''}
+ previous_item = data[:return]
+ when :php
+ type, description = content.match(/^(\S+)( .+)?$/).captures
+ data[:return] = {type: cleanup_class_name(type), description: description || ''}
+ previous_item = data[:return]
+ end
+ when 'private'
+ data[:visibility] = :private
+ when 'protected'
+ data[:visibility] = :protected
+ when 'static'
+ data[:static] = true
+ when 'abstract'
+ data[:abstract] = true
+ when 'ignore'
+ ignore = true
+ when 'inheritable', 'deprecated', 'singleton', 'throws',
+ 'chainable', 'fires', 'localdoc', 'inheritdoc', 'member',
+ 'see'
+ # skip
+ else
+ fail "unrecognized keyword: #{keyword}"
+ end
+ }
+
+ next if ignore
+
+ if code_line && code_line.strip != ''
+ case filetype
+ when :js
+ m = code_line.match(/(?:(static|prototype)\.)?(\w+) =/)
+ kind_, name = m.captures
+ data[:static] = true if kind_ == 'static'
+ kind = {'static' => :property, 'prototype' => :method}[ kind_.strip ] if kind_ && !kind
+ data[:name] = cleanup_class_name(name)
+ when :php
+ m = code_line.match(/
+ \s*
+ (?:(public|protected|private)\s)?
+ (?:(static)\s)?(function\s|class\s|\$)
+ (\w+)
+ (?:\sextends\s(\w+))?
+ /x)
+ visibility, static, kind_, name, parent = m.captures
+ kind = {'$' => :property, 'function' => :method, 'class' => :class}[ kind_.strip ]
+ data[:visibility] = {'private' => :private, 'protected' => :protected, 'public' => :public}[ visibility ] || :public
+ data[:static] = true if static
+ data[:parent] = cleanup_class_name(parent) if parent
+ data[:name] = cleanup_class_name(name)
+ end
+ end
+
+ # handle JS class/constructor conundrum
+ if kind == :class || js_class_constructor
+ if current_class
+ output << current_class
+ end
+ current_class = data.select{|k, _v| valid_per_kind[:class].include? k }
+ current_class[:description] = js_class_constructor_desc if js_class_constructor_desc != ''
+ previous_item = current_class
+ end
+
+ # standardize
+ if data[:name] == '__construct' || js_class_constructor
+ data[:name] = '#constructor'
+ end
+
+ # put into the current class
+ if kind && kind != :class
+ keys = {
+ method: :methods,
+ property: :properties,
+ event: :events,
+ }
+ current_class[keys[kind]] << data.select{|k, _v| valid_per_kind[kind].include? k }
+ previous_item = current_class[keys[kind]]
+ end
+ }
+
+ # this is evil, assumes we only have one class in a file, but we'd need a proper parser to do it better
+ if current_class
+ current_class[:mixins] +=
+ text.scan(/\$this->mixin\( .*?new (\w+)\( \$this/).flatten.map(&method(:cleanup_class_name))
+ end
+
+ output << current_class if current_class
+ output
+end
+
+def parse_any_path path
+ if File.directory? path
+ parse_dir path
+ else
+ parse_file path
+ end
+end
+
+if __FILE__ == $PROGRAM_NAME
+ if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help']
+ $stderr.puts "usage: ruby #{$PROGRAM_NAME} <files...>"
+ $stderr.puts " ruby #{$PROGRAM_NAME} src > docs-js.json"
+ $stderr.puts " ruby #{$PROGRAM_NAME} php > docs-php.json"
+ else
+ out = JSON.pretty_generate ARGV.map{|a| parse_any_path a }.inject(:+)
+ # ew
+ out = out.gsub(/\{\s+\}/, '{}').gsub(/\[\s+\]/, '[]')
+ puts out
+ end
+end
diff --git a/vendor/oojs/oojs-ui/bin/generate-JSPHP-for-karma.php b/vendor/oojs/oojs-ui/bin/generate-JSPHP-for-karma.php
new file mode 100644
index 00000000..445da65c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/bin/generate-JSPHP-for-karma.php
@@ -0,0 +1,50 @@
+<?php
+
+// Quick and dirty autoloader to make it possible to run without Composer.
+spl_autoload_register( function ( $class ) {
+ $class = preg_replace( '/^OOUI\\\\/', '', $class );
+ foreach ( array( 'elements', 'layouts', 'themes', 'widgets', '.' ) as $dir ) {
+ $path = "../php/$dir/$class.php";
+ if ( file_exists( $path ) ) {
+ require_once $path;
+ return;
+ }
+ }
+} );
+
+$testSuiteJSON = file_get_contents( 'JSPHP-suite.json' );
+$testSuite = json_decode( $testSuiteJSON, true );
+$testSuiteOutput = array();
+
+function new_OOUI( $class, $config = array() ) {
+ $class = "OOUI\\" . $class;
+ return new $class( $config );
+}
+function unstub( &$value ) {
+ if ( is_string( $value ) && substr( $value, 0, 13 ) === '_placeholder_' ) {
+ $value = json_decode( substr( $value, 13 ), true );
+ array_walk_recursive( $value['config'], 'unstub' );
+ $value = new_OOUI( $value['class'], $value['config'] );
+ }
+}
+// Keep synchronized with tests/index.php
+$themes = array( 'ApexTheme', 'MediaWikiTheme' );
+foreach ( $themes as $theme ) {
+ OOUI\Theme::setSingleton( new_OOUI( $theme ) );
+ foreach ( $testSuite as $className => $tests ) {
+ foreach ( $tests as $test ) {
+ // Unstub placeholders
+ $config = $test['config'];
+ array_walk_recursive( $config, 'unstub' );
+ $config['infusable'] = true;
+ $instance = new_OOUI( $test['class'], $config );
+ $testSuiteOutput[$theme][$className][] = "$instance";
+ }
+ }
+}
+
+$testSuiteOutputJSON = json_encode( $testSuiteOutput, JSON_PRETTY_PRINT );
+
+echo "var testSuiteConfigs = $testSuiteJSON;\n\n";
+echo "var testSuitePHPOutput = $testSuiteOutputJSON;\n\n";
+echo file_get_contents( 'JSPHP.test.karma.js' );
diff --git a/vendor/oojs/oojs-ui/bin/testsuitegenerator.rb b/vendor/oojs/oojs-ui/bin/testsuitegenerator.rb
new file mode 100644
index 00000000..28ab1a85
--- /dev/null
+++ b/vendor/oojs/oojs-ui/bin/testsuitegenerator.rb
@@ -0,0 +1,146 @@
+require 'pp'
+require_relative 'docparser'
+
+if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help']
+ $stderr.puts "usage: ruby #{$PROGRAM_NAME} <dirA> <dirB>"
+ $stderr.puts " ruby #{$PROGRAM_NAME} src php > tests/JSPHP-suite.json"
+else
+ dir_a, dir_b = ARGV
+ js = parse_any_path dir_a
+ php = parse_any_path dir_b
+
+ class_names = (js + php).map{|c| c[:name] }.sort.uniq
+
+ tests = []
+ classes = php.select{|c| class_names.include? c[:name] }
+
+ testable_classes = classes
+ .reject{|c| c[:abstract] } # can't test abstract classes
+ .reject{|c| !c[:parent] || c[:parent] == 'ElementMixin' || c[:parent] == 'Theme' } # can't test abstract
+ .reject{|c| %w[Element Widget Layout Theme].include? c[:name] } # no toplevel
+ .reject{|c| c[:name] == 'DropdownInputWidget' } # different PHP and JS implementations
+
+ # values to test for each type
+ expandos = {
+ 'null' => [nil],
+ 'number' => [0, -1, 300],
+ 'boolean' => [true, false],
+ 'string' => ['Foo bar', '<b>HTML?</b>'],
+ }
+
+ # values to test for names
+ sensible_values = {
+ 'href' => ['http://example.com/'],
+ ['TextInputWidget', 'type'] => %w[text password],
+ ['ButtonInputWidget', 'type'] => %w[button input],
+ ['FieldLayout', 'help'] => true, # different PHP and JS implementations
+ ['FieldsetLayout', 'help'] => true, # different PHP and JS implementations
+ 'type' => %w[text button],
+ 'method' => %w[GET POST],
+ 'action' => [],
+ 'enctype' => true,
+ 'target' => ['_blank'],
+ 'accessKey' => ['k'],
+ 'name' => true,
+ 'autofocus' => true, # usually makes no sense in JS
+ 'tabIndex' => [-1, 0, 100],
+ 'icon' => ['picture'],
+ 'indicator' => ['down'],
+ 'flags' => %w[constructive],
+ 'label' => expandos['string'] + ['', ' '],
+ # these are defined by Element and would bloat the tests
+ 'classes' => true,
+ 'id' => true,
+ 'content' => true,
+ 'text' => true,
+ }
+
+ find_class = lambda do |klass|
+ return classes.find{|c| c[:name] == klass }
+ end
+
+ expand_types_to_values = lambda do |types|
+ return types.map{|t|
+ as_array = true if t.sub! '[]', ''
+ t = 'ButtonWidget' if t == 'Widget' # Widget is not "testable", use a subclass
+ if expandos[t]
+ # Primitive. Run tests with the provided values.
+ vals = expandos[t]
+ elsif testable_classes.find{|c| c[:name] == t }
+ # OOUI object. Test suite will instantiate one and run the test with it.
+ params = find_class.call(t)[:methods][0][:params] || []
+ config = params.map{|config_option|
+ types = config_option[:type].split '|'
+ values = expand_types_to_values.call(types)
+ { config_option[:name] => values[0] }
+ }
+ vals = [ '_placeholder_' + {
+ class: t,
+ config: config.inject({}, :merge)
+ }.to_json ]
+ else
+ # We don't know how to test this. The empty value will result in no
+ # tests being generated for this combination of config values.
+ vals = []
+ end
+ as_array ? vals.map{|v| [v] } : vals
+ }.inject(:+)
+ end
+
+ find_config_sources = lambda do |klass_name|
+ return [] unless klass_name
+ klass_names = [klass_name]
+ while klass_name
+ klass = find_class.call(klass_name)
+ break unless klass
+ klass_names +=
+ find_config_sources.call(klass[:parent]) +
+ klass[:mixins].map(&find_config_sources).flatten
+ klass_name = klass[:parent]
+ end
+ return klass_names.uniq
+ end
+
+ testable_classes.each do |klass|
+ config_sources = find_config_sources.call(klass[:name])
+ .map{|c| find_class.call(c)[:methods][0] }
+ config = config_sources.map{|c| c[:config] }.compact.inject(:+)
+ required_config = klass[:methods][0][:params] || []
+
+ # generate every possible configuration of configuration option sets
+ maxlength = [config.length, 2].min
+ config_combinations = (0..maxlength).map{|l| config.combination(l).to_a }.inject(:+)
+ # for each set, generate all possible values to use based on option's type
+ config_combinations = config_combinations.map{|config_comb|
+ config_comb += required_config
+ expanded = config_comb.map{|config_option|
+ types = config_option[:type].split '|'
+ sensible = sensible_values[ [ klass[:name], config_option[:name] ] ] ||
+ sensible_values[ config_option[:name] ]
+ if sensible == true
+ [] # the empty value will result in no tests being generated
+ else
+ values = sensible || expand_types_to_values.call(types)
+ values.map{|v| config_option.dup.merge(value: v) } + [nil]
+ end
+ }
+ expanded.length > 0 ? expanded[0].product(*expanded[1..-1]) : []
+ }.inject(:concat).map(&:compact).uniq
+
+ # really require the required ones
+ config_combinations = config_combinations.select{|config_comb|
+ required_config.all?{|r| config_comb.find{|c| c[:name] == r[:name] } }
+ }
+
+ config_combinations.each do |config_comb|
+ tests << {
+ class: klass[:name],
+ config: Hash[ config_comb.map{|c| [ c[:name], c[:value] ] } ]
+ }
+ end
+ end
+
+ tests = tests.group_by{|t| t[:class] }
+
+ puts JSON.pretty_generate tests
+end
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" ]
+ ]
+}
diff --git a/vendor/oojs/oojs-ui/composer.json b/vendor/oojs/oojs-ui/composer.json
new file mode 100644
index 00000000..0907de12
--- /dev/null
+++ b/vendor/oojs/oojs-ui/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "oojs/oojs-ui",
+ "description": "Provides library of common widgets, layouts, and windows.",
+ "homepage": "https://www.mediawiki.org/wiki/OOjs_UI",
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "0.8.*",
+ "mediawiki/mediawiki-codesniffer": "0.1.0",
+ "squizlabs/php_codesniffer": "2.1.*"
+ },
+ "autoload": {
+ "classmap": ["php/"]
+ },
+ "scripts": {
+ "test": [
+ "parallel-lint . --exclude vendor",
+ "phpcs $PHPCS_ARGS --standard=vendor/mediawiki/mediawiki-codesniffer/MediaWiki --ignore=vendor --extensions=php,php5,inc -v ."
+ ]
+ }
+}
+
diff --git a/vendor/oojs/oojs-ui/demos/demo.js b/vendor/oojs/oojs-ui/demos/demo.js
new file mode 100644
index 00000000..a1d39314
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/demo.js
@@ -0,0 +1,504 @@
+/**
+ * @class
+ * @extends {OO.ui.Element}
+ *
+ * @constructor
+ */
+OO.ui.Demo = function OoUiDemo() {
+ // Parent
+ OO.ui.Demo.super.call( this );
+
+ // Normalization
+ this.normalizeHash();
+
+ // Properties
+ this.stylesheetLinks = this.getStylesheetLinks();
+ this.mode = this.getCurrentMode();
+ this.$menu = $( '<div>' );
+ this.pageDropdown = new OO.ui.DropdownWidget( {
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( { data: 'dialogs', label: 'Dialogs' } ),
+ new OO.ui.MenuOptionWidget( { data: 'icons', label: 'Icons' } ),
+ new OO.ui.MenuOptionWidget( { data: 'toolbars', label: 'Toolbars' } ),
+ new OO.ui.MenuOptionWidget( { data: 'widgets', label: 'Widgets' } )
+ ]
+ },
+ classes: [ 'oo-ui-demo-pageDropdown' ]
+ } );
+ this.pageMenu = this.pageDropdown.getMenu();
+ this.themeSelect = new OO.ui.ButtonSelectWidget().addItems( [
+ new OO.ui.ButtonOptionWidget( { data: 'mediawiki', label: 'MediaWiki' } ),
+ new OO.ui.ButtonOptionWidget( { data: 'apex', label: 'Apex' } )
+ ] );
+ this.graphicsSelect = new OO.ui.ButtonSelectWidget().addItems( [
+ new OO.ui.ButtonOptionWidget( { data: 'mixed', label: 'Mixed' } ),
+ new OO.ui.ButtonOptionWidget( { data: 'vector', label: 'Vector' } ),
+ new OO.ui.ButtonOptionWidget( { data: 'raster', label: 'Raster' } )
+ ] );
+ this.directionSelect = new OO.ui.ButtonSelectWidget().addItems( [
+ new OO.ui.ButtonOptionWidget( { data: 'ltr', label: 'LTR' } ),
+ new OO.ui.ButtonOptionWidget( { data: 'rtl', label: 'RTL' } )
+ ] );
+
+ // Events
+ this.pageMenu.on( 'choose', OO.ui.bind( this.onModeChange, this ) );
+ this.themeSelect.on( 'choose', OO.ui.bind( this.onModeChange, this ) );
+ this.graphicsSelect.on( 'choose', OO.ui.bind( this.onModeChange, this ) );
+ this.directionSelect.on( 'choose', OO.ui.bind( this.onModeChange, this ) );
+
+ // Initialization
+ this.pageMenu.selectItemByData( this.mode.page );
+ this.themeSelect.selectItemByData( this.mode.theme );
+ this.graphicsSelect.selectItemByData( this.mode.graphics );
+ this.directionSelect.selectItemByData( this.mode.direction );
+ this.$menu
+ .addClass( 'oo-ui-demo-menu' )
+ .append(
+ this.pageDropdown.$element,
+ this.themeSelect.$element,
+ this.graphicsSelect.$element,
+ this.directionSelect.$element
+ );
+ this.$element
+ .addClass( 'oo-ui-demo' )
+ .append( this.$menu );
+ $( 'body' ).addClass( 'oo-ui-' + this.mode.direction );
+ // Correctly apply direction to the <html> tags as well
+ $( 'html' ).attr( 'dir', this.mode.direction );
+ $( 'head' ).append( this.stylesheetLinks );
+ OO.ui.theme = new ( this.constructor.static.themes[ this.mode.theme ].theme )();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Demo, OO.ui.Element );
+
+/* Static Properties */
+
+/**
+ * Available pages.
+ *
+ * Populated by each of the page scripts in the `pages` directory.
+ *
+ * @static
+ * @property {Object.<string,Function>} pages List of functions that render a page, keyed by
+ * symbolic page name
+ */
+OO.ui.Demo.static.pages = {};
+
+/**
+ * Available themes.
+ *
+ * List of theme descriptions, each containing a `fileSuffix` property used for linking to the
+ * correct stylesheet file and a `theme` property containing a theme class
+ *
+ * @static
+ * @property {Object.<string,Object>}
+ */
+OO.ui.Demo.static.themes = {
+ mediawiki: {
+ fileSuffix: '-mediawiki',
+ additionalSuffixes: [
+ '-icons-movement',
+ '-icons-content',
+ '-icons-alerts',
+ '-icons-interactions',
+ '-icons-moderation',
+ '-icons-editing-core',
+ '-icons-editing-styling',
+ '-icons-editing-list',
+ '-icons-editing-advanced',
+ '-icons-media',
+ '-icons-location',
+ '-icons-user',
+ '-icons-layout',
+ '-icons-wikimedia'
+ ],
+ theme: OO.ui.MediaWikiTheme
+ },
+ apex: {
+ fileSuffix: '-apex',
+ additionalSuffixes: [
+ '-icons-movement',
+ '-icons-moderation',
+ '-icons-editing-core',
+ '-icons-editing-styling',
+ '-icons-editing-list',
+ '-icons-editing-advanced'
+ ],
+ theme: OO.ui.ApexTheme
+ }
+};
+
+/**
+ * Available graphics formats.
+ *
+ * List of graphics format descriptions, each containing a `fileSuffix` property used for linking
+ * to the correct stylesheet file.
+ *
+ * @static
+ * @property {Object.<string,Object>}
+ */
+OO.ui.Demo.static.graphics = {
+ mixed: { fileSuffix: '' },
+ vector: { fileSuffix: '.vector' },
+ raster: { fileSuffix: '.raster' }
+};
+
+/**
+ * Available text directions.
+ *
+ * List of text direction descriptions, each containing a `fileSuffix` property used for linking to
+ * the correct stylesheet file.
+ *
+ * @static
+ * @property {Object.<string,Object>}
+ */
+OO.ui.Demo.static.directions = {
+ ltr: { fileSuffix: '' },
+ rtl: { fileSuffix: '.rtl' }
+};
+
+/**
+ * Default page.
+ *
+ * Set by one of the page scripts in the `pages` directory.
+ *
+ * @static
+ * @property {string|null}
+ */
+OO.ui.Demo.static.defaultPage = null;
+
+/**
+ * Default page.
+ *
+ * Set by one of the page scripts in the `pages` directory.
+ *
+ * @static
+ * @property {string}
+ */
+OO.ui.Demo.static.defaultTheme = 'mediawiki';
+
+/**
+ * Default page.
+ *
+ * Set by one of the page scripts in the `pages` directory.
+ *
+ * @static
+ * @property {string}
+ */
+OO.ui.Demo.static.defaultGraphics = 'vector';
+
+/**
+ * Default page.
+ *
+ * Set by one of the page scripts in the `pages` directory.
+ *
+ * @static
+ * @property {string}
+ */
+OO.ui.Demo.static.defaultDirection = 'ltr';
+
+/* Methods */
+
+/**
+ * Load the demo page. Must be called after $element is attached.
+ */
+OO.ui.Demo.prototype.initialize = function () {
+ var demo = this,
+ promises = $( this.stylesheetLinks ).map( function () {
+ return $( this ).data( 'load-promise' );
+ } );
+ $.when.apply( $, promises )
+ .done( function () {
+ demo.constructor.static.pages[ demo.mode.page ]( demo );
+ } )
+ .fail( function () {
+ demo.$element.append( $( '<p>' ).text( 'Demo styles failed to load.' ) );
+ } );
+};
+
+/**
+ * Handle mode change events.
+ *
+ * Will load a new page.
+ */
+OO.ui.Demo.prototype.onModeChange = function () {
+ var page = this.pageMenu.getSelectedItem().getData(),
+ theme = this.themeSelect.getSelectedItem().getData(),
+ direction = this.directionSelect.getSelectedItem().getData(),
+ graphics = this.graphicsSelect.getSelectedItem().getData();
+
+ location.hash = '#' + [ page, theme, graphics, direction ].join( '-' );
+};
+
+/**
+ * Get a list of mode factors.
+ *
+ * Factors are a mapping between symbolic names used in the URL hash and internal information used
+ * to act on those symbolic names.
+ *
+ * Factor lists are in URL order: page, theme, graphics, direction. Page contains the symbolic
+ * page name, others contain file suffixes.
+ *
+ * @return {Object[]} List of mode factors, keyed by symbolic name
+ */
+OO.ui.Demo.prototype.getFactors = function () {
+ var key,
+ factors = [ {}, {}, {}, {} ];
+
+ for ( key in this.constructor.static.pages ) {
+ factors[ 0 ][ key ] = key;
+ }
+ for ( key in this.constructor.static.themes ) {
+ factors[ 1 ][ key ] = this.constructor.static.themes[ key ].fileSuffix;
+ }
+ for ( key in this.constructor.static.graphics ) {
+ factors[ 2 ][ key ] = this.constructor.static.graphics[ key ].fileSuffix;
+ }
+ for ( key in this.constructor.static.directions ) {
+ factors[ 3 ][ key ] = this.constructor.static.directions[ key ].fileSuffix;
+ }
+
+ return factors;
+};
+
+/**
+ * Get a list of default factors.
+ *
+ * Factor defaults are in URL order: page, theme, graphics, direction. Each contains a symbolic
+ * factor name which should be used as a fallback when the URL hash is missing or invalid.
+ *
+ * @return {Object[]} List of default factors
+ */
+OO.ui.Demo.prototype.getDefaultFactorValues = function () {
+ return [
+ this.constructor.static.defaultPage,
+ this.constructor.static.defaultTheme,
+ this.constructor.static.defaultGraphics,
+ this.constructor.static.defaultDirection
+ ];
+};
+
+/**
+ * Parse the current URL hash into factor values.
+ *
+ * @return {string[]} Factor values in URL order: page, theme, graphics, direction
+ */
+OO.ui.Demo.prototype.getCurrentFactorValues = function () {
+ return location.hash.slice( 1 ).split( '-' );
+};
+
+/**
+ * Get the current mode.
+ *
+ * Generated from parsed URL hash values.
+ *
+ * @return {Object} List of factor values keyed by factor name
+ */
+OO.ui.Demo.prototype.getCurrentMode = function () {
+ var factorValues = this.getCurrentFactorValues();
+
+ return {
+ page: factorValues[ 0 ],
+ theme: factorValues[ 1 ],
+ graphics: factorValues[ 2 ],
+ direction: factorValues[ 3 ]
+ };
+};
+
+/**
+ * Get link elements for the current mode.
+ *
+ * @return {HTMLElement[]} List of link elements
+ */
+OO.ui.Demo.prototype.getStylesheetLinks = function () {
+ var i, len, links, fragments,
+ factors = this.getFactors(),
+ theme = this.getCurrentFactorValues()[ 1 ],
+ suffixes = this.constructor.static.themes[ theme ].additionalSuffixes || [],
+ urls = [];
+
+ // Translate modes to filename fragments
+ fragments = this.getCurrentFactorValues().map( function ( val, index ) {
+ return factors[ index ][ val ];
+ } );
+
+ // Theme styles
+ urls.push( '../dist/oojs-ui' + fragments.slice( 1 ).join( '' ) + '.css' );
+ for ( i = 0, len = suffixes.length; i < len; i++ ) {
+ urls.push( '../dist/oojs-ui' + fragments[1] + suffixes[i] + fragments.slice( 2 ).join( '' ) + '.css' );
+ }
+
+ // Demo styles
+ urls.push( 'styles/demo' + fragments[ 3 ] + '.css' );
+
+ // Add link tags
+ links = urls.map( function ( url ) {
+ var
+ link = document.createElement( 'link' ),
+ $link = $( link ),
+ deferred = $.Deferred();
+ $link.data( 'load-promise', deferred.promise() );
+ $link.on( {
+ load: deferred.resolve,
+ error: deferred.reject
+ } );
+ link.rel = 'stylesheet';
+ link.href = url;
+ return link;
+ } );
+
+ return links;
+};
+
+/**
+ * Normalize the URL hash.
+ */
+OO.ui.Demo.prototype.normalizeHash = function () {
+ var i, len, factorValues,
+ modes = [],
+ factors = this.getFactors(),
+ defaults = this.getDefaultFactorValues();
+
+ factorValues = this.getCurrentFactorValues();
+ for ( i = 0, len = factors.length; i < len; i++ ) {
+ modes[ i ] = factors[ i ][ factorValues[ i ] ] !== undefined ? factorValues[ i ] : defaults[ i ];
+ }
+
+ // Update hash
+ location.hash = modes.join( '-' );
+};
+
+/**
+ * Destroy demo.
+ */
+OO.ui.Demo.prototype.destroy = function () {
+ $( 'body' ).removeClass( 'oo-ui-ltr oo-ui-rtl' );
+ $( this.stylesheetLinks ).remove();
+ this.$element.remove();
+};
+
+/**
+ * Build a console for interacting with an element.
+ *
+ * @param {OO.ui.Element} item
+ * @param {string} key Variable name for item
+ * @param {string} [item.label=""]
+ * @return {jQuery} Console interface element
+ */
+OO.ui.Demo.prototype.buildConsole = function ( item, key ) {
+ var $toggle, $log, $label, $input, $submit, $console, $form,
+ console = window.console;
+
+ function exec( str ) {
+ var func, ret;
+ /*jshint evil:true */
+ if ( str.indexOf( 'return' ) !== 0 ) {
+ str = 'return ' + str;
+ }
+ try {
+ func = new Function( key, 'item', str );
+ ret = { value: func( item, item ) };
+ } catch ( error ) {
+ ret = {
+ value: undefined,
+ error: error
+ };
+ }
+ return ret;
+ }
+
+ function submit() {
+ var val, result, logval;
+
+ val = $input.val();
+ $input.val( '' );
+ $input[ 0 ].focus();
+ result = exec( val );
+
+ logval = String( result.value );
+ if ( logval === '' ) {
+ logval = '""';
+ }
+
+ $log.append(
+ $( '<div>' )
+ .addClass( 'oo-ui-demo-console-log-line oo-ui-demo-console-log-line-input' )
+ .text( val ),
+ $( '<div>' )
+ .addClass( 'oo-ui-demo-console-log-line oo-ui-demo-console-log-line-return' )
+ .text( logval || result.value )
+ );
+
+ if ( result.error ) {
+ $log.append( $( '<div>' ).addClass( 'oo-ui-demo-console-log-line oo-ui-demo-console-log-line-error' ).text( result.error ) );
+ }
+
+ if ( console && console.log ) {
+ console.log( '[demo]', result.value );
+ if ( result.error ) {
+ if ( console.error ) {
+ console.error( '[demo]', String( result.error ), result.error );
+ } else {
+ console.log( '[demo] Error: ', result.error );
+ }
+ }
+ }
+
+ // Scrol to end
+ $log.prop( 'scrollTop', $log.prop( 'scrollHeight' ) );
+ }
+
+ $toggle = $( '<span>' )
+ .addClass( 'oo-ui-demo-console-toggle' )
+ .attr( 'title', 'Toggle console' )
+ .on( 'click', function ( e ) {
+ e.preventDefault();
+ $console.toggleClass( 'oo-ui-demo-console-collapsed oo-ui-demo-console-expanded' );
+ if ( $input.is( ':visible' ) ) {
+ $input[ 0 ].focus();
+ if ( console && console.log ) {
+ window[ key ] = item;
+ console.log( '[demo]', 'Global ' + key + ' has been set' );
+ console.log( '[demo]', item );
+ }
+ }
+ } );
+
+ $log = $( '<div>' )
+ .addClass( 'oo-ui-demo-console-log' );
+
+ $label = $( '<label>' )
+ .addClass( 'oo-ui-demo-console-label' );
+
+ $input = $( '<input>' )
+ .addClass( 'oo-ui-demo-console-input' )
+ .prop( 'placeholder', '... (predefined: ' + key + ')' );
+
+ $submit = $( '<div>' )
+ .addClass( 'oo-ui-demo-console-submit' )
+ .text( '↵' )
+ .on( 'click', submit );
+
+ $form = $( '<form>' ).on( 'submit', function ( e ) {
+ e.preventDefault();
+ submit();
+ } );
+
+ $console = $( '<div>' )
+ .addClass( 'oo-ui-demo-console oo-ui-demo-console-collapsed' )
+ .append(
+ $toggle,
+ $log,
+ $form.append(
+ $label.append(
+ $input
+ ),
+ $submit
+ )
+ );
+
+ return $console;
+};
diff --git a/vendor/oojs/oojs-ui/demos/index.html b/vendor/oojs/oojs-ui/demos/index.html
new file mode 100644
index 00000000..35ccc69a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/index.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+ <meta charset="UTF-8">
+ <title>OOjs UI Demos</title>
+ <!-- Prevent scaling on mobile devices which cause problems with dialog sizing -->
+ <meta name="viewport" content="width=device-width, user-scalable=no">
+</head>
+<body>
+ <script src="../node_modules/jquery/dist/jquery.js"></script>
+ <script src="../node_modules/oojs/dist/oojs.jquery.js"></script>
+ <script src="../dist/oojs-ui.js"></script>
+ <script src="../dist/oojs-ui-apex.js"></script>
+ <script src="../dist/oojs-ui-mediawiki.js"></script>
+ <script src="demo.js"></script>
+ <script src="pages/dialogs.js"></script>
+ <script src="pages/icons.js"></script>
+ <script src="pages/widgets.js"></script>
+ <script src="pages/toolbars.js"></script>
+ <script>
+ $( function () {
+ var demo;
+
+ function setup() {
+ if ( demo ) {
+ demo.destroy();
+ }
+ demo = new OO.ui.Demo();
+ $( 'body' ).append( demo.$element );
+ demo.initialize();
+ }
+
+ setup();
+
+ $( window ).on( 'hashchange', setup );
+ } )
+ </script>
+</body>
+</html>
diff --git a/vendor/oojs/oojs-ui/demos/infusion.js b/vendor/oojs/oojs-ui/demos/infusion.js
new file mode 100644
index 00000000..4fa0f64a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/infusion.js
@@ -0,0 +1,66 @@
+// Demonstrate JavaScript 'infusion' of PHP-generated widgets.
+// Used by widgets.php.
+
+var $menu, themeButtons, themes;
+
+// Helper function to get high resolution profiling data, where available.
+function now() {
+ /* global performance */
+ return ( typeof performance !== 'undefined' ) ? performance.now() :
+ Date.now ? Date.now() : new Date().getTime();
+}
+
+// Add a button to infuse everything!
+// (You wouldn't typically do this: you'd only infuse those objects which
+// you needed to attach client-side behaviors to. But our theme-setting
+// code needs a list of all widgets, and it's a good overall test.)
+function infuseAll() {
+ var start, end, all;
+ start = now();
+ all = $( '*[data-ooui]' ).map( function ( _, e ) {
+ return OO.ui.infuse( e.id );
+ } );
+ end = now();
+ window.console.log( 'Infusion time: ' + ( end - start ) );
+ return all;
+}
+$menu = $( '.oo-ui-demo-menu' );
+$menu.append(
+ new OO.ui.ButtonGroupWidget().addItems( [
+ new OO.ui.ButtonWidget( { label: 'Infuse' } )
+ .on( 'click', infuseAll )
+ ] ).$element );
+
+// Hook up the theme switch buttons.
+// This is more like a typical use case: we are just infusing specific
+// named UI elements.
+themeButtons = [
+ // If you're lazy, you can just use OO.ui.infuse. But if you name the
+ // Element type you're expecting, you can get some type checking.
+ OO.ui.ButtonWidget.static.infuse( 'theme-mediawiki' ),
+ OO.ui.ButtonWidget.static.infuse( 'theme-apex' )
+];
+themes = {
+ mediawiki: new OO.ui.MediaWikiTheme(),
+ apex: new OO.ui.ApexTheme()
+};
+function updateTheme( theme ) {
+ OO.ui.theme = themes[theme];
+ $.each( infuseAll(), function ( _, el ) {
+ // FIXME: this isn't really supported, but it makes a neat demo.
+ OO.ui.theme.updateElementClasses( el );
+ } );
+ // A bit of a hack, but it will do for a demo.
+ $( 'link[rel="stylesheet"][title="theme"]' ).attr(
+ 'href',
+ '../dist/oojs-ui-' + theme + '.vector.css'
+ );
+}
+// This is another more typical usage: we take the existing server-side
+// buttons and replace the href with a JS click handler.
+$.each( themeButtons, function ( _, b ) {
+ // Get rid of the old server-side click handling.
+ b.setHref( null );
+ // Add new client-side click handling.
+ b.on( 'click', function () { updateTheme( b.getData() ); } );
+} );
diff --git a/vendor/oojs/oojs-ui/demos/pages/dialogs.js b/vendor/oojs/oojs-ui/demos/pages/dialogs.js
new file mode 100644
index 00000000..6e9da3f8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/pages/dialogs.js
@@ -0,0 +1,602 @@
+OO.ui.Demo.static.pages.dialogs = function ( demo ) {
+ var i, l, name, openButton, DialogClass, config,
+ $demo = demo.$element,
+ fieldset = new OO.ui.FieldsetLayout( { label: 'Dialogs' } ),
+ windows = {},
+ windowManager = new OO.ui.WindowManager();
+
+ function SimpleDialog( config ) {
+ SimpleDialog.super.call( this, config );
+ }
+ OO.inheritClass( SimpleDialog, OO.ui.Dialog );
+ SimpleDialog.static.title = 'Simple dialog';
+ SimpleDialog.prototype.initialize = function () {
+ var closeButton,
+ dialog = this;
+
+ SimpleDialog.super.prototype.initialize.apply( this, arguments );
+ this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ this.content.$element.append( '<p>Dialog content</p>' );
+
+ closeButton = new OO.ui.ButtonWidget( {
+ label: OO.ui.msg( 'ooui-dialog-process-dismiss' )
+ } );
+ closeButton.on( 'click', function () {
+ dialog.close();
+ } );
+
+ this.content.$element.append( closeButton.$element );
+ this.$body.append( this.content.$element );
+ };
+ SimpleDialog.prototype.getBodyHeight = function () {
+ return this.content.$element.outerHeight( true );
+ };
+
+ function ProcessDialog( config ) {
+ ProcessDialog.super.call( this, config );
+ }
+ OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
+ ProcessDialog.static.title = 'Process dialog';
+ ProcessDialog.static.actions = [
+ { action: 'save', label: 'Done', flags: [ 'primary', 'progressive' ] },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ ProcessDialog.prototype.initialize = function () {
+ ProcessDialog.super.prototype.initialize.apply( this, arguments );
+ this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ this.content.$element.append( '<p>Dialog content</p>' );
+ this.$body.append( this.content.$element );
+ };
+ ProcessDialog.prototype.getActionProcess = function ( action ) {
+ var dialog = this;
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ dialog.close( { action: action } );
+ } );
+ }
+ return ProcessDialog.super.prototype.getActionProcess.call( this, action );
+ };
+ ProcessDialog.prototype.getBodyHeight = function () {
+ return this.content.$element.outerHeight( true );
+ };
+
+ function SearchWidgetDialog( config ) {
+ SearchWidgetDialog.super.call( this, config );
+ this.broken = false;
+ }
+ OO.inheritClass( SearchWidgetDialog, OO.ui.ProcessDialog );
+ SearchWidgetDialog.static.title = 'Search widget dialog';
+ SearchWidgetDialog.prototype.initialize = function () {
+ SearchWidgetDialog.super.prototype.initialize.apply( this, arguments );
+ var i,
+ items = [],
+ searchWidget = new OO.ui.SearchWidget();
+ for ( i = 1; i <= 20; i++ ) {
+ items.push( new OO.ui.OptionWidget( { data: i, label: 'Item ' + i } ) );
+ }
+ searchWidget.results.addItems( items );
+ searchWidget.onQueryChange = function () {};
+ this.$body.append( searchWidget.$element );
+ };
+ SearchWidgetDialog.prototype.getBodyHeight = function () {
+ return 300;
+ };
+ SearchWidgetDialog.static.actions = [
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ SearchWidgetDialog.prototype.getActionProcess = function ( action ) {
+ var dialog = this;
+ return new OO.ui.Process( function () {
+ dialog.close( { action: action } );
+ } );
+ };
+
+ function BrokenDialog( config ) {
+ BrokenDialog.super.call( this, config );
+ this.broken = false;
+ }
+ OO.inheritClass( BrokenDialog, OO.ui.ProcessDialog );
+ BrokenDialog.static.title = 'Broken dialog';
+ BrokenDialog.static.actions = [
+ { action: 'save', label: 'Save', flags: [ 'primary', 'constructive' ] },
+ { action: 'delete', label: 'Delete', flags: 'destructive' },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ BrokenDialog.prototype.getBodyHeight = function () {
+ return 250;
+ };
+ BrokenDialog.prototype.initialize = function () {
+ BrokenDialog.super.prototype.initialize.apply( this, arguments );
+ this.content = new OO.ui.PanelLayout( { padded: true } );
+ this.fieldset = new OO.ui.FieldsetLayout( {
+ label: 'Dialog with error handling', icon: 'alert'
+ } );
+ this.description = new OO.ui.LabelWidget( {
+ label: 'Deleting will fail and will not be recoverable. ' +
+ 'Saving will fail the first time, but succeed the second time.'
+ } );
+ this.fieldset.addItems( [ this.description ] );
+ this.content.$element.append( this.fieldset.$element );
+ this.$body.append( this.content.$element );
+ };
+ BrokenDialog.prototype.getSetupProcess = function ( data ) {
+ return BrokenDialog.super.prototype.getSetupProcess.call( this, data )
+ .next( function () {
+ this.broken = true;
+ }, this );
+ };
+ BrokenDialog.prototype.getActionProcess = function ( action ) {
+ return BrokenDialog.super.prototype.getActionProcess.call( this, action )
+ .next( function () {
+ return 1000;
+ }, this )
+ .next( function () {
+ var closing;
+
+ if ( action === 'save' ) {
+ if ( this.broken ) {
+ this.broken = false;
+ return new OO.ui.Error( 'Server did not respond' );
+ }
+ } else if ( action === 'delete' ) {
+ return new OO.ui.Error( 'Permission denied', { recoverable: false } );
+ }
+
+ closing = this.close( { action: action } );
+ if ( action === 'save' ) {
+ // Return a promise to remaing pending while closing
+ return closing;
+ }
+ return BrokenDialog.super.prototype.getActionProcess.call( this, action );
+ }, this );
+ };
+
+ function SamplePage( name, config ) {
+ config = $.extend( { label: 'Sample page', icon: 'Sample icon' }, config );
+ OO.ui.PageLayout.call( this, name, config );
+ this.label = config.label;
+ this.icon = config.icon;
+ this.$element.text( this.label );
+ }
+ OO.inheritClass( SamplePage, OO.ui.PageLayout );
+ SamplePage.prototype.setupOutlineItem = function ( outlineItem ) {
+ SamplePage.super.prototype.setupOutlineItem.call( this, outlineItem );
+ this.outlineItem
+ .setMovable( true )
+ .setRemovable( true )
+ .setIcon( this.icon )
+ .setLabel( this.label );
+ };
+
+ function BookletDialog( config ) {
+ BookletDialog.super.call( this, config );
+ }
+ OO.inheritClass( BookletDialog, OO.ui.ProcessDialog );
+ BookletDialog.static.title = 'Booklet dialog';
+ BookletDialog.static.actions = [
+ { action: 'save', label: 'Done', flags: [ 'primary', 'progressive' ] },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ BookletDialog.prototype.getBodyHeight = function () {
+ return 250;
+ };
+ BookletDialog.prototype.initialize = function () {
+ BookletDialog.super.prototype.initialize.apply( this, arguments );
+
+ var dialog = this;
+
+ function changePage( direction ) {
+ var pageIndex = dialog.pages.indexOf( dialog.bookletLayout.getCurrentPage() );
+ pageIndex = ( dialog.pages.length + pageIndex + direction ) % dialog.pages.length;
+ dialog.bookletLayout.setPage( dialog.pages[ pageIndex ].getName() );
+ }
+
+ this.navigationField = new OO.ui.FieldLayout(
+ new OO.ui.ButtonGroupWidget( {
+ items: [
+ new OO.ui.ButtonWidget( {
+ data: 'previous',
+ icon: 'previous'
+ } ).on( 'click', function () {
+ changePage( -1 );
+ } ),
+ new OO.ui.ButtonWidget( {
+ data: 'next',
+ icon: 'next'
+ } ).on( 'click', function () {
+ changePage( 1 );
+ } )
+ ]
+ } ),
+ {
+ label: 'Change page',
+ align: 'top'
+ }
+ );
+
+ this.bookletLayout = new OO.ui.BookletLayout();
+ this.pages = [
+ new SamplePage( 'page-1', { label: 'Page 1', icon: 'window' } ),
+ new SamplePage( 'page-2', { label: 'Page 2', icon: 'window' } ),
+ new SamplePage( 'page-3', { label: 'Page 3', icon: 'window' } )
+ ];
+ this.bookletLayout.addPages( this.pages );
+ this.bookletLayout.connect( this, { set: 'onBookletLayoutSet' } );
+ this.bookletLayout.setPage( 'page-1' );
+
+ this.$body.append( this.bookletLayout.$element );
+ };
+ BookletDialog.prototype.getActionProcess = function ( action ) {
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ this.close( { action: action } );
+ }, this );
+ }
+ return BookletDialog.super.prototype.getActionProcess.call( this, action );
+ };
+ BookletDialog.prototype.onBookletLayoutSet = function ( page ) {
+ page.$element.append( this.navigationField.$element );
+ };
+
+ function OutlinedBookletDialog( config ) {
+ OutlinedBookletDialog.super.call( this, config );
+ }
+ OO.inheritClass( OutlinedBookletDialog, OO.ui.ProcessDialog );
+ OutlinedBookletDialog.static.title = 'Booklet dialog';
+ OutlinedBookletDialog.static.actions = [
+ { action: 'save', label: 'Done', flags: [ 'primary', 'progressive' ] },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ OutlinedBookletDialog.prototype.getBodyHeight = function () {
+ return 250;
+ };
+ OutlinedBookletDialog.prototype.initialize = function () {
+ OutlinedBookletDialog.super.prototype.initialize.apply( this, arguments );
+ this.bookletLayout = new OO.ui.BookletLayout( {
+ outlined: true
+ } );
+ this.pages = [
+ new SamplePage( 'small', { label: 'Small', icon: 'window' } ),
+ new SamplePage( 'medium', { label: 'Medium', icon: 'window' } ),
+ new SamplePage( 'large', { label: 'Large', icon: 'window' } ),
+ new SamplePage( 'larger', { label: 'Larger', icon: 'window' } ),
+ new SamplePage( 'full', { label: 'Full', icon: 'window' } )
+ ];
+
+ this.bookletLayout.addPages( this.pages );
+ this.bookletLayout.connect( this, { set: 'onBookletLayoutSet' } );
+ this.$body.append( this.bookletLayout.$element );
+ };
+ OutlinedBookletDialog.prototype.getActionProcess = function ( action ) {
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ this.close( { action: action } );
+ }, this );
+ }
+ return OutlinedBookletDialog.super.prototype.getActionProcess.call( this, action );
+ };
+ OutlinedBookletDialog.prototype.onBookletLayoutSet = function ( page ) {
+ this.setSize( page.getName() );
+ };
+ OutlinedBookletDialog.prototype.getSetupProcess = function ( data ) {
+ return OutlinedBookletDialog.super.prototype.getSetupProcess.call( this, data )
+ .next( function () {
+ this.bookletLayout.setPage( this.getSize() );
+ }, this );
+ };
+
+ function SampleCard( name, config ) {
+ config = $.extend( { label: 'Sample card' }, config );
+ OO.ui.CardLayout.call( this, name, config );
+ this.label = config.label;
+ this.$element.text( this.label );
+ }
+ OO.inheritClass( SampleCard, OO.ui.CardLayout );
+ SampleCard.prototype.setupTabItem = function ( tabItem ) {
+ SampleCard.super.prototype.setupTabItem.call( this, tabItem );
+ this.tabItem.setLabel( this.label );
+ };
+
+ function IndexedDialog( config ) {
+ IndexedDialog.super.call( this, config );
+ }
+ OO.inheritClass( IndexedDialog, OO.ui.ProcessDialog );
+ IndexedDialog.static.title = 'Index dialog';
+ IndexedDialog.static.actions = [
+ { action: 'save', label: 'Done', flags: [ 'primary', 'progressive' ] },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ IndexedDialog.prototype.getBodyHeight = function () {
+ return 250;
+ };
+ IndexedDialog.prototype.initialize = function () {
+ IndexedDialog.super.prototype.initialize.apply( this, arguments );
+ this.indexLayout = new OO.ui.IndexLayout();
+ this.cards = [
+ new SampleCard( 'first', { label: 'One' } ),
+ new SampleCard( 'second', { label: 'Two' } ),
+ new SampleCard( 'third', { label: 'Three' } ),
+ new SampleCard( 'fourth', { label: 'Four' } )
+ ];
+
+ this.indexLayout.addCards( this.cards );
+ this.$body.append( this.indexLayout.$element );
+
+ this.indexLayout.getTabs().getItemFromData( 'fourth' ).setDisabled( true );
+ };
+ IndexedDialog.prototype.getActionProcess = function ( action ) {
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ this.close( { action: action } );
+ }, this );
+ }
+ return IndexedDialog.super.prototype.getActionProcess.call( this, action );
+ };
+
+ function MenuDialog( config ) {
+ MenuDialog.super.call( this, config );
+ }
+ OO.inheritClass( MenuDialog, OO.ui.ProcessDialog );
+ MenuDialog.static.title = 'Menu dialog';
+ MenuDialog.static.actions = [
+ { action: 'save', label: 'Done', flags: [ 'primary', 'progressive' ] },
+ { action: 'cancel', label: 'Cancel', flags: 'safe' }
+ ];
+ MenuDialog.prototype.getBodyHeight = function () {
+ return 350;
+ };
+ MenuDialog.prototype.initialize = function () {
+ MenuDialog.super.prototype.initialize.apply( this, arguments );
+ var menuLayout = new OO.ui.MenuLayout(),
+ positionField = new OO.ui.FieldLayout(
+ new OO.ui.ButtonSelectWidget( {
+ items: [
+ new OO.ui.ButtonOptionWidget( {
+ data: 'before',
+ label: 'Before'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'after',
+ label: 'After'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'top',
+ label: 'Top'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'bottom',
+ label: 'Bottom'
+ } )
+ ]
+ } ).on( 'select', function ( item ) {
+ menuLayout.setMenuPosition( item.getData() );
+ } ),
+ {
+ label: 'Menu position',
+ align: 'top'
+ }
+ ),
+ showField = new OO.ui.FieldLayout(
+ new OO.ui.ToggleSwitchWidget( { value: true } ).on( 'change', function ( value ) {
+ menuLayout.toggleMenu( value );
+ } ),
+ {
+ label: 'Show menu',
+ align: 'top'
+ }
+ ),
+ menuPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
+ contentPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } );
+
+ menuLayout.$menu.append(
+ menuPanel.$element.append( 'Menu panel' )
+ );
+ menuLayout.$content.append(
+ contentPanel.$element.append(
+ positionField.$element,
+ showField.$element
+ )
+ );
+
+ this.$body.append( menuLayout.$element );
+ };
+ MenuDialog.prototype.getActionProcess = function ( action ) {
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ this.close( { action: action } );
+ }, this );
+ }
+ return MenuDialog.super.prototype.getActionProcess.call( this, action );
+ };
+
+ config = [
+ {
+ name: 'Simple dialog (small)',
+ config: {
+ size: 'small'
+ },
+ data: {
+ title: 'Sample dialog with very long title that does not fit'
+ }
+ },
+ {
+ name: 'Simple dialog (medium)',
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Simple dialog (large)',
+ config: {
+ size: 'large'
+ }
+ },
+ {
+ name: 'Simple dialog (larger)',
+ config: {
+ size: 'larger'
+ }
+ },
+ {
+ name: 'Simple dialog (full)',
+ config: {
+ size: 'full'
+ }
+ },
+ {
+ name: 'Process dialog (medium)',
+ dialogClass: ProcessDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Process dialog (full)',
+ dialogClass: ProcessDialog,
+ config: {
+ size: 'full'
+ }
+ },
+ {
+ name: 'Search widget dialog (medium)',
+ dialogClass: SearchWidgetDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Broken dialog (error handling)',
+ dialogClass: BrokenDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Booklet dialog',
+ dialogClass: BookletDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Outlined booklet dialog',
+ dialogClass: OutlinedBookletDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Indexed dialog',
+ dialogClass: IndexedDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Menu dialog',
+ dialogClass: MenuDialog,
+ config: {
+ size: 'medium'
+ }
+ },
+ {
+ name: 'Message dialog (generic)',
+ dialogClass: OO.ui.MessageDialog,
+ data: {
+ title: 'Continue?',
+ message: 'It may be risky'
+ }
+ },
+ {
+ name: 'Message dialog (verbose)',
+ dialogClass: OO.ui.MessageDialog,
+ data: {
+ title: 'Continue?',
+ message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis laoreet elit. Nam eu velit ullamcorper, volutpat elit sed, viverra massa. Aenean congue aliquam lorem, et laoreet risus condimentum vel. Praesent nec imperdiet mauris. Nunc eros magna, iaculis sit amet ante id, dapibus tristique lorem. Praesent in feugiat lorem, sit amet porttitor eros. Donec sapien turpis, pretium eget ligula id, scelerisque tincidunt diam. Pellentesque a venenatis tortor, at luctus nisl. Quisque vel urna a enim mattis rutrum. Morbi eget consequat nisl. Nam tristique molestie diam ac consequat. Nam varius adipiscing mattis. Praesent sodales volutpat nulla lobortis iaculis. Quisque vel odio eget diam posuere imperdiet. Fusce et iaculis odio. Donec in nibh ut dui accumsan vehicula quis et massa.',
+ verbose: true
+ }
+ },
+ {
+ name: 'Message dialog (1 action)',
+ dialogClass: OO.ui.MessageDialog,
+ data: {
+ title: 'Storage limit reached',
+ message: 'You are out of disk space',
+ actions: [
+ {
+ action: 'accept',
+ label: 'Dismiss',
+ flags: 'primary'
+ }
+ ]
+ }
+ },
+ {
+ name: 'Message dialog (2 actions)',
+ dialogClass: OO.ui.MessageDialog,
+ data: {
+ title: 'Cannot save data',
+ message: 'The server is not responding',
+ actions: [
+ {
+ action: 'reject',
+ label: 'Cancel',
+ flags: 'safe'
+ },
+ {
+ action: 'repeat',
+ label: 'Try again',
+ flags: [ 'primary', 'constructive' ]
+ }
+ ]
+ }
+ },
+ {
+ name: 'Message dialog (3 actions)',
+ dialogClass: OO.ui.MessageDialog,
+ data: {
+ title: 'Delete file?',
+ message: 'The file will be irreversably obliterated. Proceed with caution.',
+ actions: [
+ { action: 'reject', label: 'Cancel', flags: 'safe' },
+ { action: 'reject', label: 'Move file to trash' },
+ {
+ action: 'accept',
+ label: 'Obliterate',
+ flags: [ 'primary', 'destructive' ]
+ }
+ ]
+ }
+ }
+ ];
+
+ function openDialog( name, data ) {
+ windowManager.openWindow( name, data );
+ }
+
+ for ( i = 0, l = config.length; i < l; i++ ) {
+ name = 'window_' + i;
+ DialogClass = config[ i ].dialogClass || SimpleDialog;
+ windows[ name ] = new DialogClass( config[ i ].config );
+ openButton = new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'window',
+ label: $( '<span dir="ltr"></span>' ).text( config[ i ].name )
+ } );
+ openButton.on(
+ 'click', OO.ui.bind( openDialog, this, name, config[ i ].data )
+ );
+ fieldset.addItems( [ new OO.ui.FieldLayout( openButton, { align: 'inline' } ) ] );
+ }
+ windowManager.addWindows( windows );
+
+ $demo.append(
+ new OO.ui.PanelLayout( {
+ expanded: false,
+ framed: true
+ } ).$element
+ .addClass( 'oo-ui-demo-container' )
+ .append( fieldset.$element ),
+ windowManager.$element
+ );
+};
diff --git a/vendor/oojs/oojs-ui/demos/pages/icons.js b/vendor/oojs/oojs-ui/demos/pages/icons.js
new file mode 100644
index 00000000..aec2204c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/pages/icons.js
@@ -0,0 +1,295 @@
+OO.ui.Demo.static.pages.icons = function ( demo ) {
+ var i, len, iconSet, iconsFieldset, iconButton, selector,
+ icons = {
+ core: [
+ 'add',
+ 'advanced',
+ 'alert',
+ 'cancel',
+ 'check',
+ 'circle',
+ 'close',
+ 'code',
+ 'collapse',
+ 'comment',
+ 'ellipsis',
+ 'expand',
+ 'help',
+ 'history',
+ 'info',
+ 'menu',
+ 'next',
+ 'picture',
+ 'previous',
+ 'redo',
+ 'remove',
+ 'search',
+ 'settings',
+ 'tag',
+ 'undo',
+ 'window'
+ ],
+ movement: [
+ 'arrowLast',
+ 'arrowNext',
+ 'downTriangle',
+ 'upTriangle',
+ 'caretLast',
+ 'caretNext',
+ 'caretDown',
+ 'caretUp',
+ 'move'
+ ],
+ content: [
+ 'article',
+ 'articleCheck',
+ 'articleSearch',
+ 'citeArticle',
+ 'book',
+ 'journal',
+ 'newspaper',
+ 'folderPlaceholder',
+ 'die',
+ 'download',
+ 'upload'
+ ],
+ alerts: [
+ 'bell',
+ 'bellOn',
+ 'eye',
+ 'eyeClosed',
+ 'message',
+ 'signature',
+ 'speechBubble',
+ 'speechBubbleAdd',
+ 'speechBubbles'
+ ],
+ interactions: [
+ 'beta',
+ 'betaLaunch',
+ 'bookmark',
+ 'browser',
+ 'clear',
+ 'clock',
+ 'funnel',
+ 'heart',
+ 'key',
+ 'keyboard',
+ 'logOut',
+ 'newWindow',
+ 'printer',
+ 'ribbonPrize',
+ 'sun',
+ 'watchlist'
+ ],
+ moderation: [
+ 'block',
+ 'blockUndo',
+ 'flag',
+ 'flagUndo',
+ 'lock',
+ 'star',
+ 'trash',
+ 'trashUndo',
+ 'unStar',
+ 'unLock'
+ ],
+ 'editing-core': [
+ 'edit',
+ 'editLock',
+ 'editUndo',
+ 'link',
+ 'linkExternal',
+ 'linkSecure'
+ ],
+ 'editing-styling': [
+ 'bigger',
+ 'smaller',
+ 'subscript',
+ 'superscript',
+ 'bold',
+ 'italic',
+ 'strikethrough',
+ 'underline',
+ 'textLanguage',
+ 'textDirLTR',
+ 'textDirRTL',
+ 'textStyle'
+ ],
+ 'editing-list': [
+ 'indent',
+ 'listBullet',
+ 'listNumbered',
+ 'outdent'
+ ],
+ 'editing-advanced': [
+ 'alignCentre',
+ 'alignLeft',
+ 'alignRight',
+ 'find',
+ 'insert',
+ 'layout',
+ 'newline',
+ 'noWikiText',
+ 'outline',
+ 'puzzle',
+ 'quotes',
+ 'quotesAdd',
+ 'redirect',
+ 'searchCaseSensitive',
+ 'searchRegularExpression',
+ 'specialCharacter',
+ 'table',
+ 'tableAddColumnAfter',
+ 'tableAddColumnBefore',
+ 'tableAddRowAfter',
+ 'tableAddRowBefore',
+ 'tableCaption',
+ 'tableMergeCells',
+ 'templateAdd',
+ 'translation',
+ 'wikiText'
+ ],
+ media: [
+ 'image',
+ 'imageAdd',
+ 'imageLock',
+ 'photoGallery',
+ 'play',
+ 'stop'
+ ],
+ location: [
+ 'map',
+ 'mapPin',
+ 'mapPinAdd',
+ 'wikitrail'
+ ],
+ user: [
+ 'userActive',
+ 'userAvatar',
+ 'userInactive',
+ 'userTalk'
+ ],
+ layout: [
+ 'stripeFlow',
+ 'stripeSideMenu',
+ 'stripeSummary',
+ 'stripeToC',
+ 'viewCompact',
+ 'viewDetails',
+ 'visionSimulator'
+ ],
+ wikimedia: [
+ 'logoCC',
+ 'logoWikimediaCommons',
+ 'logoWikipedia'
+ ]
+ },
+ indicators = [
+ 'alert',
+ 'down',
+ 'next',
+ 'previous',
+ 'required',
+ 'search',
+ 'up'
+ ],
+ iconsFieldsets = [],
+ iconsButtons = [],
+ indicatorsFieldset = new OO.ui.FieldsetLayout( { label: 'Indicators' } );
+
+ for ( i = 0, len = indicators.length; i < len; i++ ) {
+ indicatorsFieldset.addItems( [
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ indicator: indicators[ i ],
+ framed: false,
+ label: indicators[ i ]
+ } ),
+ { align: 'top' }
+ )
+ ] );
+ }
+ for ( iconSet in icons ) {
+ iconsFieldset = new OO.ui.FieldsetLayout( { label: 'Icons – ' + iconSet } );
+ iconsFieldsets.push( iconsFieldset );
+
+ for ( i = 0, len = icons[ iconSet ].length; i < len; i++ ) {
+ iconButton = new OO.ui.ButtonWidget( {
+ icon: icons[ iconSet ][ i ],
+ framed: false,
+ label: icons[ iconSet ][ i ]
+ } );
+ iconsButtons.push( iconButton );
+ iconsFieldset.addItems( [
+ new OO.ui.FieldLayout(
+ iconButton,
+ { align: 'top' }
+ )
+ ] );
+ }
+ }
+
+ selector = new OO.ui.ButtonSelectWidget( {
+ items: [
+ new OO.ui.ButtonOptionWidget( {
+ label: 'None',
+ flags: [],
+ data: {
+ progressive: false,
+ constructive: false,
+ destructive: false
+ }
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ label: 'Progressive',
+ flags: [ 'progressive' ],
+ data: {
+ progressive: true,
+ constructive: false,
+ destructive: false
+ }
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ label: 'Constructive',
+ flags: [ 'constructive' ],
+ data: {
+ progressive: false,
+ constructive: true,
+ destructive: false
+ }
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ label: 'Destructive',
+ flags: [ 'destructive' ],
+ data: {
+ progressive: false,
+ constructive: false,
+ destructive: true
+ }
+ } )
+ ]
+ } )
+ .on( 'select', function ( selected ) {
+ iconsButtons.forEach( function ( iconButton ) {
+ iconButton.setFlags( selected.getData() );
+ } );
+ } )
+ .selectItemByData( {
+ progressive: false,
+ constructive: false,
+ destructive: false
+ } );
+
+ demo.$element.append(
+ new OO.ui.PanelLayout( {
+ expanded: false,
+ framed: true
+ } ).$element
+ .addClass( 'oo-ui-demo-container oo-ui-demo-icons' )
+ .append(
+ selector.$element,
+ indicatorsFieldset.$element,
+ iconsFieldsets.map( function ( item ) { return item.$element[0]; } )
+ ) );
+};
diff --git a/vendor/oojs/oojs-ui/demos/pages/toolbars.js b/vendor/oojs/oojs-ui/demos/pages/toolbars.js
new file mode 100644
index 00000000..550d8128
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/pages/toolbars.js
@@ -0,0 +1,338 @@
+OO.ui.Demo.static.pages.toolbars = function ( demo ) {
+ var i, toolGroups, saveButton, actionButton, actionButtonDisabled, PopupTool, ToolGroupTool,
+ $demo = demo.$element,
+ $containers = $(),
+ toolFactories = [],
+ toolGroupFactories = [],
+ toolbars = [];
+
+ // Show some random accelerator keys that don't actually work
+ function getToolAccelerator( name ) {
+ return {
+ listTool1: 'Ctrl+Shift+1',
+ listTool2: 'Ctrl+Alt+2',
+ listTool3: 'Cmd+Enter',
+ listTool5: 'Shift+Down',
+ menuTool: 'Ctrl+M'
+ }[ name ];
+ }
+
+ for ( i = 0; i < 4; i++ ) {
+ toolFactories.push( new OO.ui.ToolFactory() );
+ toolGroupFactories.push( new OO.ui.ToolGroupFactory() );
+ toolbars.push( new OO.ui.Toolbar( toolFactories[ i ], toolGroupFactories[ i ], { actions: true } ) );
+ toolbars[ i ].getToolAccelerator = getToolAccelerator;
+ }
+
+ function createTool( toolbar, group, name, icon, title, init, onSelect, displayBothIconAndLabel ) {
+ var Tool = function () {
+ Tool.super.apply( this, arguments );
+ this.toggled = false;
+ if ( init ) {
+ init.call( this );
+ }
+ };
+
+ OO.inheritClass( Tool, OO.ui.Tool );
+
+ Tool.prototype.onSelect = function () {
+ if ( onSelect ) {
+ onSelect.call( this );
+ } else {
+ this.toggled = !this.toggled;
+ this.setActive( this.toggled );
+ }
+ toolbars[ toolbar ].emit( 'updateState' );
+ };
+ Tool.prototype.onUpdateState = function () {};
+
+ Tool.static.name = name;
+ Tool.static.group = group;
+ Tool.static.icon = icon;
+ Tool.static.title = title;
+ Tool.static.displayBothIconAndLabel = !!displayBothIconAndLabel;
+ return Tool;
+ }
+
+ function createToolGroup( toolbar, group ) {
+ $.each( toolGroups[ group ], function ( i, tool ) {
+ var args = tool.slice();
+ args.splice( 0, 0, toolbar, group );
+ toolFactories[ toolbar ].register( createTool.apply( null, args ) );
+ } );
+ }
+
+ function createDisabledToolGroup( parent, name ) {
+ var DisabledToolGroup = function () {
+ DisabledToolGroup.super.apply( this, arguments );
+ this.setDisabled( true );
+ };
+
+ OO.inheritClass( DisabledToolGroup, parent );
+
+ DisabledToolGroup.static.name = name;
+
+ DisabledToolGroup.prototype.onUpdateState = function () {
+ this.setLabel( 'Disabled' );
+ };
+
+ return DisabledToolGroup;
+ }
+
+ toolGroupFactories[ 0 ].register( createDisabledToolGroup( OO.ui.BarToolGroup, 'disabledBar' ) );
+ toolGroupFactories[ 0 ].register( createDisabledToolGroup( OO.ui.ListToolGroup, 'disabledList' ) );
+ toolGroupFactories[ 1 ].register( createDisabledToolGroup( OO.ui.MenuToolGroup, 'disabledMenu' ) );
+
+ PopupTool = function ( toolGroup, config ) {
+ // Parent constructor
+ OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
+ padded: true,
+ label: 'Popup head',
+ head: true
+ } }, config ) );
+
+ this.popup.$body.append( '<p>Popup contents</p>' );
+ };
+
+ OO.inheritClass( PopupTool, OO.ui.PopupTool );
+
+ PopupTool.static.name = 'popupTool';
+ PopupTool.static.group = 'popupTools';
+ PopupTool.static.icon = 'help';
+
+ toolFactories[ 2 ].register( PopupTool );
+
+ ToolGroupTool = function ( toolGroup, config ) {
+ // Parent constructor
+ OO.ui.ToolGroupTool.call( this, toolGroup, config );
+ };
+
+ OO.inheritClass( ToolGroupTool, OO.ui.ToolGroupTool );
+
+ ToolGroupTool.static.name = 'toolGroupTool';
+ ToolGroupTool.static.group = 'barTools';
+ ToolGroupTool.static.groupConfig = {
+ indicator: 'down',
+ include: [ { group: 'moreListTools' } ]
+ };
+
+ toolFactories[ 0 ].register( ToolGroupTool );
+ toolFactories[ 3 ].register( ToolGroupTool );
+
+ // Toolbar
+ toolbars[ 0 ].setup( [
+ {
+ type: 'bar',
+ include: [ { group: 'barTools' } ],
+ demote: [ 'toolGroupTool' ]
+ },
+ {
+ type: 'disabledBar',
+ include: [ { group: 'disabledBarTools' } ]
+ },
+ {
+ type: 'list',
+ indicator: 'down',
+ label: 'List',
+ icon: 'picture',
+ include: [ { group: 'listTools' } ],
+ allowCollapse: [ 'listTool1', 'listTool6' ]
+ },
+ {
+ type: 'disabledList',
+ indicator: 'down',
+ label: 'List',
+ icon: 'picture',
+ include: [ { group: 'disabledListTools' } ]
+ },
+ {
+ type: 'list',
+ indicator: 'down',
+ label: 'Auto-disabling list',
+ icon: 'picture',
+ include: [ { group: 'autoDisableListTools' } ]
+ },
+ {
+ label: 'Catch-all',
+ include: '*'
+ }
+ ] );
+ // Toolbar with action buttons
+ toolbars[ 1 ].setup( [
+ {
+ type: 'menu',
+ indicator: 'down',
+ icon: 'picture',
+ include: [ { group: 'menuTools' } ]
+ },
+ {
+ type: 'disabledMenu',
+ indicator: 'down',
+ icon: 'picture',
+ include: [ { group: 'disabledMenuTools' } ]
+ }
+ ] );
+ // Fake toolbar to be injected into the first toolbar
+ // demonstrating right-aligned menus
+ toolbars[ 2 ].setup( [
+ {
+ include: [ { group: 'popupTools' } ]
+ },
+ {
+ type: 'list',
+ icon: 'menu',
+ include: [ { group: 'listTools' } ]
+ }
+ ] );
+ toolbars[ 3 ].setup( [
+ {
+ type: 'bar',
+ include: [ { group: 'history' } ]
+ },
+ {
+ type: 'menu',
+ indicator: 'down',
+ include: [ { group: 'menuTools' } ]
+ },
+ {
+ type: 'list',
+ indicator: 'down',
+ icon: 'comment',
+ include: [ { group: 'listTools' } ],
+ allowCollapse: [ 'listTool1', 'listTool6' ]
+ },
+ {
+ type: 'bar',
+ include: [ { group: 'link' } ]
+ },
+ {
+ type: 'bar',
+ include: [ { group: 'cite' } ]
+ },
+ {
+ type: 'list',
+ indicator: 'down',
+ label: 'Insert',
+ include: [ { group: 'autoDisableListTools' }, { group: 'unusedStuff' } ]
+ }
+ ] );
+
+ saveButton = new OO.ui.ButtonWidget( { label: 'Save', flags: [ 'progressive', 'primary' ] } );
+ actionButton = new OO.ui.ButtonWidget( { label: 'Action' } );
+ actionButtonDisabled = new OO.ui.ButtonWidget( { label: 'Disabled', disabled: true } );
+ toolbars[ 1 ].$actions
+ .append( actionButton.$element, actionButtonDisabled.$element );
+
+ toolbars[ 3 ].$actions
+ .append( toolbars[ 2 ].$element, saveButton.$element );
+
+ for ( i = 0; i < toolbars.length; i++ ) {
+ toolbars[ i ].emit( 'updateState' );
+ }
+
+ toolGroups = {
+ barTools: [
+ [ 'barTool', 'picture', 'Basic tool in bar' ],
+ [ 'disabledBarTool', 'picture', 'Basic tool in bar disabled', function () { this.setDisabled( true ); } ]
+ ],
+
+ disabledBarTools: [
+ [ 'barToolInDisabled', 'picture', 'Basic tool in disabled bar' ]
+ ],
+
+ listTools: [
+ [ 'listTool', 'picture', 'First basic tool in list' ],
+ [ 'listTool1', 'picture', 'Basic tool in list' ],
+ [ 'listTool3', 'picture', 'Basic disabled tool in list', function () { this.setDisabled( true ); } ],
+ [ 'listTool6', 'picture', 'A final tool' ]
+ ],
+
+ moreListTools: [
+ [ 'listTool2', 'code', 'Another basic tool' ],
+ [ 'listTool4', 'picture', 'More basic tools' ],
+ [ 'listTool5', 'ellipsis', 'And even more' ]
+ ],
+
+ popupTools: [
+ [ 'popupTool' ]
+ ],
+
+ disabledListTools: [
+ [ 'listToolInDisabled', 'picture', 'Basic tool in disabled list' ]
+ ],
+
+ autoDisableListTools: [
+ [ 'autoDisableListTool', 'picture', 'Click to disable this tool', null, function () { this.setDisabled( true ); } ]
+ ],
+
+ menuTools: [
+ [ 'menuTool', 'picture', 'Basic tool' ],
+ [ 'disabledMenuTool', 'picture', 'Basic tool disabled', function () { this.setDisabled( true ); } ]
+ ],
+
+ disabledMenuTools: [
+ [ 'menuToolInDisabled', 'picture', 'Basic tool' ]
+ ],
+
+ unusedStuff: [
+ [ 'unusedTool', 'help', 'This tool is not explicitly used anywhere' ],
+ [ 'unusedTool1', 'help', 'And neither is this one' ]
+ ],
+
+ history: [
+ [ 'undoTool', 'undo', 'Undo' ],
+ [ 'redoTool', 'redo', 'Redo' ]
+ ],
+
+ link: [
+ [ 'linkTool', 'link', 'Link' ]
+ ],
+
+ cite: [
+ [ 'citeTool', 'citeArticle', 'Cite', null, null, true ]
+ ]
+ };
+
+ createToolGroup( 0, 'unusedStuff' );
+ createToolGroup( 0, 'barTools' );
+ createToolGroup( 0, 'disabledBarTools' );
+ createToolGroup( 0, 'listTools' );
+ createToolGroup( 0, 'moreListTools' );
+ createToolGroup( 0, 'disabledListTools' );
+ createToolGroup( 0, 'autoDisableListTools' );
+ createToolGroup( 1, 'menuTools' );
+ createToolGroup( 1, 'disabledMenuTools' );
+ createToolGroup( 2, 'listTools' );
+ createToolGroup( 3, 'history' );
+ createToolGroup( 3, 'link' );
+ createToolGroup( 3, 'cite' );
+ createToolGroup( 3, 'menuTools' );
+ createToolGroup( 3, 'listTools' );
+ createToolGroup( 3, 'moreListTools' );
+ createToolGroup( 3, 'autoDisableListTools' );
+ createToolGroup( 3, 'unusedStuff' );
+
+ for ( i = 0; i < toolbars.length; i++ ) {
+ $containers = $containers.add(
+ new OO.ui.PanelLayout( {
+ expanded: false,
+ framed: true
+ } ).$element
+ .addClass( 'oo-ui-demo-container oo-ui-demo-toolbars' )
+ );
+
+ if ( i === 2 ) {
+ continue;
+ }
+ $containers.eq( i ).append( toolbars[ i ].$element );
+ }
+ $containers.append( '' );
+ $demo.append(
+ $containers.eq( 0 ).append( '<div class="oo-ui-demo-toolbars-contents">Toolbar</div>' ),
+ $containers.eq( 1 ).append( '<div class="oo-ui-demo-toolbars-contents">Toolbar with action buttons</div>' ),
+ $containers.eq( 3 ).append( '<div class="oo-ui-demo-toolbars-contents">Word processor toolbar</div>' )
+ );
+ for ( i = 0; i < toolbars.length; i++ ) {
+ toolbars[ i ].initialize();
+ }
+};
diff --git a/vendor/oojs/oojs-ui/demos/pages/widgets.js b/vendor/oojs/oojs-ui/demos/pages/widgets.js
new file mode 100644
index 00000000..52c6ee87
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/pages/widgets.js
@@ -0,0 +1,1428 @@
+OO.ui.Demo.static.pages.widgets = function ( demo ) {
+ var styles, states, buttonStyleShowcaseWidget, horizontalAlignmentWidget, fieldsets,
+ $demo = demo.$element;
+
+ /**
+ * Draggable group widget containing drag/drop items
+ * @param {Object} [config] Configuration options
+ */
+ function DragDropGroupWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ DragDropGroupWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.DraggableGroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+ // Respond to reorder event
+ this.connect( this, { reorder: 'onReorder' } );
+ }
+ /* Setup */
+ OO.inheritClass( DragDropGroupWidget, OO.ui.Widget );
+ OO.mixinClass( DragDropGroupWidget, OO.ui.DraggableGroupElement );
+
+ /**
+ * Respond to order event
+ * @param {OO.ui.DraggableElement} item Reordered item
+ * @param {number} newIndex New index
+ */
+ DragDropGroupWidget.prototype.onReorder = function ( item, newIndex ) {
+ this.addItems( [ item ], newIndex );
+ };
+
+ /**
+ * Drag/drop items
+ * @param {Object} [config] Configuration options
+ */
+ function DragDropItemWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ DragDropItemWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.DraggableElement.call( this, config );
+ }
+ /* Setup */
+ OO.inheritClass( DragDropItemWidget, OO.ui.OptionWidget );
+ OO.mixinClass( DragDropItemWidget, OO.ui.DraggableElement );
+
+ /**
+ * Demo for LookupElement.
+ * @extends OO.ui.TextInputWidget
+ * @mixins OO.ui.LookupElement
+ */
+ function NumberLookupTextInputWidget() {
+ // Parent constructor
+ OO.ui.TextInputWidget.call( this, { validate: 'integer' } );
+ // Mixin constructors
+ OO.ui.LookupElement.call( this );
+ }
+ OO.inheritClass( NumberLookupTextInputWidget, OO.ui.TextInputWidget );
+ OO.mixinClass( NumberLookupTextInputWidget, OO.ui.LookupElement );
+
+ /**
+ * @inheritdoc
+ */
+ NumberLookupTextInputWidget.prototype.getLookupRequest = function () {
+ var
+ value = this.getValue(),
+ deferred = $.Deferred(),
+ delay = 500 + Math.floor( Math.random() * 500 );
+
+ this.isValid().done( function ( valid ) {
+ if ( valid ) {
+ // Resolve with results after a faked delay
+ setTimeout( function () {
+ deferred.resolve( [ value * 1, value * 2, value * 3, value * 4, value * 5 ] );
+ }, delay );
+ } else {
+ // No results when the input contains invalid content
+ deferred.resolve( [] );
+ }
+ } );
+
+ return deferred.promise( { abort: function () {} } );
+ };
+
+ /**
+ * @inheritdoc
+ */
+ NumberLookupTextInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
+ return response || [];
+ };
+
+ /**
+ * @inheritdoc
+ */
+ NumberLookupTextInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
+ var
+ items = [],
+ i, number;
+ for ( i = 0; i < data.length; i++ ) {
+ number = String( data[ i ] );
+ items.push( new OO.ui.MenuOptionWidget( {
+ data: number,
+ label: number
+ } ) );
+ }
+
+ return items;
+ };
+
+ styles = [
+ {},
+ {
+ flags: [ 'progressive' ]
+ },
+ {
+ flags: [ 'constructive' ]
+ },
+ {
+ flags: [ 'destructive' ]
+ },
+ {
+ flags: [ 'primary', 'progressive' ]
+ },
+ {
+ flags: [ 'primary', 'constructive' ]
+ },
+ {
+ flags: [ 'primary', 'destructive' ]
+ }
+ ];
+ states = [
+ {
+ label: 'Button'
+ },
+ {
+ label: 'Button',
+ icon: 'tag'
+ },
+ {
+ label: 'Button',
+ icon: 'tag',
+ indicator: 'down'
+ },
+ {
+ icon: 'tag',
+ title: 'Title text'
+ },
+ {
+ indicator: 'down'
+ },
+ {
+ icon: 'tag',
+ indicator: 'down'
+ },
+ {
+ label: 'Button',
+ disabled: true
+ },
+ {
+ icon: 'tag',
+ title: 'Title text',
+ disabled: true
+ },
+ {
+ indicator: 'down',
+ disabled: true
+ }
+ ];
+ buttonStyleShowcaseWidget = new OO.ui.Widget();
+ $.each( styles, function ( i, style ) {
+ $.each( states, function ( j, state ) {
+ buttonStyleShowcaseWidget.$element.append(
+ new OO.ui.ButtonWidget( $.extend( {}, style, state ) ).$element
+ );
+ } );
+ buttonStyleShowcaseWidget.$element.append( $( '<br>' ) );
+ } );
+
+ horizontalAlignmentWidget = new OO.ui.Widget( {
+ classes: [ 'oo-ui-demo-horizontal-alignment' ]
+ } );
+ horizontalAlignmentWidget.$element.append(
+ new OO.ui.ButtonWidget( { label: 'Button' } ).$element,
+ new OO.ui.ButtonGroupWidget( { items: [
+ new OO.ui.ToggleButtonWidget( { label: 'A' } ),
+ new OO.ui.ToggleButtonWidget( { label: 'B' } )
+ ] } ).$element,
+ new OO.ui.ButtonInputWidget( { label: 'ButtonInput' } ).$element,
+ new OO.ui.TextInputWidget( { value: 'TextInput' } ).$element,
+ new OO.ui.DropdownInputWidget( { options: [
+ {
+ label: 'DropdownInput',
+ data: null
+ }
+ ] } ).$element,
+ new OO.ui.CheckboxInputWidget( { selected: true } ).$element,
+ new OO.ui.RadioInputWidget( { selected: true } ).$element,
+ new OO.ui.LabelWidget( { label: 'Label' } ).$element
+ );
+
+ fieldsets = [
+ new OO.ui.FieldsetLayout( {
+ label: 'Simple buttons',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( { label: 'Normal' } ),
+ {
+ label: 'ButtonWidget (normal)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Progressive',
+ flags: [ 'progressive' ]
+ } ),
+ {
+ label: 'ButtonWidget (progressive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Constructive',
+ flags: [ 'constructive' ]
+ } ),
+ {
+ label: 'ButtonWidget (constructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Destructive',
+ flags: [ 'destructive' ]
+ } ),
+ {
+ label: 'ButtonWidget (destructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Primary progressive',
+ flags: [ 'primary', 'progressive' ]
+ } ),
+ {
+ label: 'ButtonWidget (primary, progressive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Primary constructive',
+ flags: [ 'primary', 'constructive' ]
+ } ),
+ {
+ label: 'ButtonWidget (primary, constructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Primary destructive',
+ flags: [ 'primary', 'destructive' ]
+ } ),
+ {
+ label: 'ButtonWidget (primary, destructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Disabled',
+ disabled: true
+ } ),
+ {
+ label: 'ButtonWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Constructive',
+ flags: [ 'constructive' ],
+ disabled: true
+ } ),
+ {
+ label: 'ButtonWidget (constructive, disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Constructive',
+ icon: 'tag',
+ flags: [ 'constructive' ],
+ disabled: true
+ } ),
+ {
+ label: 'ButtonWidget (constructive, icon, disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Icon',
+ icon: 'tag'
+ } ),
+ {
+ label: 'ButtonWidget (icon)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Icon',
+ icon: 'tag',
+ flags: [ 'progressive' ]
+ } ),
+ {
+ label: 'ButtonWidget (icon, progressive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Indicator',
+ indicator: 'down'
+ } ),
+ {
+ label: 'ButtonWidget (indicator)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Indicator',
+ indicator: 'down',
+ flags: [ 'constructive' ]
+ } ),
+ {
+ label: 'ButtonWidget (indicator, constructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'help',
+ title: 'Icon only'
+ } ),
+ {
+ label: 'ButtonWidget (icon only)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ indicator: 'alert',
+ title: 'Indicator only'
+ } ),
+ {
+ label: 'ButtonWidget (indicator only)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ icon: 'help',
+ title: 'Icon only, framed'
+ } ),
+ {
+ label: 'ButtonWidget (icon only, framed)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ indicator: 'alert',
+ title: 'Indicator only, framed'
+ } ),
+ {
+ label: 'ButtonWidget (indicator only, framed)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'tag',
+ label: 'Labeled'
+ } ),
+ {
+ label: 'ButtonWidget (frameless)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ flags: [ 'progressive' ],
+ icon: 'check',
+ label: 'Progressive'
+ } ),
+ {
+ label: 'ButtonWidget (frameless, progressive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ flags: [ 'warning' ],
+ icon: 'alert',
+ label: 'Warning'
+ } ),
+ {
+ label: 'ButtonWidget (frameless, warning)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ flags: [ 'destructive' ],
+ icon: 'remove',
+ label: 'Destructive'
+ } ),
+ {
+ label: 'ButtonWidget (frameless, destructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ flags: [ 'constructive' ],
+ icon: 'add',
+ label: 'Constructive'
+ } ),
+ {
+ label: 'ButtonWidget (frameless, constructive)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'tag',
+ label: 'Disabled',
+ disabled: true
+ } ),
+ {
+ label: 'ButtonWidget (frameless, disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ flags: [ 'constructive' ],
+ icon: 'tag',
+ label: 'Constructive',
+ disabled: true
+ } ),
+ {
+ label: 'ButtonWidget (frameless, constructive, disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'tag',
+ indicator: 'down',
+ label: 'Labeled'
+ } ),
+ {
+ label: 'ButtonWidget (frameless, indicator)\u200E',
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Button sets',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonGroupWidget( {
+ items: [
+ new OO.ui.ButtonWidget( {
+ icon: 'tag',
+ label: 'One'
+ } ),
+ new OO.ui.ButtonWidget( {
+ label: 'Two'
+ } ),
+ new OO.ui.ButtonWidget( {
+ indicator: 'required',
+ label: 'Three'
+ } )
+ ]
+ } ),
+ {
+ label: 'ButtonGroupWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonSelectWidget( {
+ items: [
+ new OO.ui.ButtonOptionWidget( {
+ data: 'b',
+ icon: 'tag',
+ label: 'One'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'c',
+ label: 'Two'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'd',
+ indicator: 'required',
+ label: 'Three'
+ } )
+ ]
+ } ),
+ {
+ label: 'ButtonSelectWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonSelectWidget( {
+ disabled: true,
+ items: [
+ new OO.ui.ButtonOptionWidget( {
+ data: 'b',
+ icon: 'tag',
+ label: 'One'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'c',
+ label: 'Two'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'd',
+ indicator: 'required',
+ label: 'Three'
+ } )
+ ]
+ } ),
+ {
+ label: 'ButtonSelectWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonSelectWidget( {
+ items: [
+ new OO.ui.ButtonOptionWidget( {
+ data: 'b',
+ icon: 'tag',
+ label: 'One',
+ disabled: true
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'c',
+ label: 'Two'
+ } ),
+ new OO.ui.ButtonOptionWidget( {
+ data: 'd',
+ indicator: 'required',
+ label: 'Three'
+ } )
+ ]
+ } ),
+ {
+ label: 'ButtonSelectWidget (disabled items)\u200E',
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Button style showcase',
+ items: [
+ new OO.ui.FieldLayout(
+ buttonStyleShowcaseWidget,
+ {
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Form widgets',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.CheckboxInputWidget( {
+ selected: true
+ } ),
+ {
+ align: 'inline',
+ label: 'CheckboxInputWidget'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.CheckboxInputWidget( {
+ selected: true,
+ disabled: true
+ } ),
+ {
+ align: 'inline',
+ label: 'CheckboxInputWidget (disabled)\u200E'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.RadioInputWidget( {
+ name: 'oojs-ui-radio-demo'
+ } ),
+ {
+ align: 'inline',
+ label: 'Connected RadioInputWidget #1'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.RadioInputWidget( {
+ name: 'oojs-ui-radio-demo',
+ selected: true
+ } ),
+ {
+ align: 'inline',
+ label: 'Connected RadioInputWidget #2'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.RadioInputWidget( {
+ selected: true,
+ disabled: true
+ } ),
+ {
+ align: 'inline',
+ label: 'RadioInputWidget (disabled)\u200E'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.RadioSelectWidget( {
+ items: [
+ new OO.ui.RadioOptionWidget( {
+ data: 'Cat',
+ label: 'Cat'
+ } ),
+ new OO.ui.RadioOptionWidget( {
+ data: 'Dog',
+ label: 'Dog'
+ } ),
+ new OO.ui.RadioOptionWidget( {
+ data: 'Goldfish',
+ label: 'Goldfish',
+ disabled: true
+ } )
+ ]
+ } ),
+ {
+ align: 'top',
+ label: 'RadioSelectWidget'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleSwitchWidget(),
+ {
+ label: 'ToggleSwitchWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleSwitchWidget( { disabled: true } ),
+ {
+ label: 'ToggleSwitchWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleSwitchWidget( { disabled: true, value: true } ),
+ {
+ label: 'ToggleSwitchWidget (disabled, checked)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleButtonWidget( { label: 'Toggle' } ),
+ {
+ label: 'ToggleButtonWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleButtonWidget( { label: 'Toggle', value: true } ),
+ {
+ label: 'ToggleButtonWidget (initially active)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ToggleButtonWidget( { icon: 'next' } ),
+ {
+ label: 'ToggleButtonWidget (icon only)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { value: 'Text input' } ),
+ {
+ label: 'TextInputWidget\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { icon: 'search' } ),
+ {
+ label: 'TextInputWidget (icon)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ indicator: 'required',
+ required: true,
+ validate: 'non-empty'
+ } ),
+ {
+ label: 'TextInputWidget (indicator, required)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ validate: function ( value ) {
+ return value.length % 2 === 0;
+ }
+ } ),
+ {
+ label: 'TextInputWidget (only allows even number of characters)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { placeholder: 'Placeholder' } ),
+ {
+ label: 'TextInputWidget (placeholder)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ value: 'Readonly',
+ readOnly: true
+ } ),
+ {
+ label: 'TextInputWidget (readonly)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ multiline: true,
+ value: 'Multiline\nMultiline'
+ } ),
+ {
+ label: 'TextInputWidget (multiline)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ multiline: true,
+ autosize: true,
+ value: 'Autosize\nAutosize\nAutosize\nAutosize'
+ } ),
+ {
+ label: 'TextInputWidget (autosize)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ icon: 'tag',
+ indicator: 'alert',
+ value: 'Text input with label',
+ label: 'Inline label'
+ } ),
+ {
+ label: 'TextInputWidget (label)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ value: 'Disabled',
+ icon: 'tag',
+ indicator: 'required',
+ label: 'Inline label',
+ disabled: true
+ } ),
+ {
+ label: 'TextInputWidget (icon, indicator, label, disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.DropdownWidget( {
+ label: 'Select one',
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( {
+ data: 'a',
+ label: 'First'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'b',
+ label: 'Second',
+ indicator: 'required'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'c',
+ label: 'Third'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'c',
+ label: 'The fourth option has a long label'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'd',
+ label: 'Fifth'
+ } )
+ ]
+ }
+ } ),
+ {
+ label: 'DropdownWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.DropdownWidget( {
+ label: 'Select one',
+ icon: 'tag',
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( {
+ data: 'a',
+ label: 'First'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'b',
+ label: 'Disabled second option',
+ indicator: 'required',
+ disabled: true
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'c',
+ label: 'Third'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'd',
+ label: 'Disabled fourth option with long label',
+ disabled: true
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'c',
+ label: 'Third'
+ } )
+ ]
+ }
+ } ),
+ {
+ label: 'DropdownWidget (disabled options)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.DropdownWidget( {
+ label: 'Select one',
+ disabled: true,
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( {
+ data: 'a',
+ label: 'First'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'b',
+ label: 'Second'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'c',
+ label: 'Third'
+ } ),
+ new OO.ui.MenuOptionWidget( {
+ data: 'd',
+ label: 'Fourth'
+ } )
+ ]
+ }
+ } ),
+ {
+ label: 'DropdownWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.DropdownInputWidget( {
+ options: [
+ {
+ data: 'a',
+ label: 'First'
+ },
+ {
+ data: 'b',
+ label: 'Second'
+ },
+ {
+ data: 'c',
+ label: 'Third'
+ }
+ ],
+ value: 'b'
+ } ),
+ {
+ label: 'DropdownInputWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget( {
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( { data: 'asd', label: 'Label for asd' } ),
+ new OO.ui.MenuOptionWidget( { data: 'fgh', label: 'Label for fgh' } ),
+ new OO.ui.MenuOptionWidget( { data: 'jkl', label: 'Label for jkl' } ),
+ new OO.ui.MenuOptionWidget( { data: 'zxc', label: 'Label for zxc' } ),
+ new OO.ui.MenuOptionWidget( { data: 'vbn', label: 'Label for vbn' } )
+ ]
+ }
+ } ),
+ {
+ label: 'ComboBoxWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget( {
+ disabled: true,
+ menu: {
+ items: [
+ new OO.ui.MenuOptionWidget( { data: 'asd', label: 'Label for asd' } ),
+ new OO.ui.MenuOptionWidget( { data: 'fgh', label: 'Label for fgh' } ),
+ new OO.ui.MenuOptionWidget( { data: 'jkl', label: 'Label for jkl' } ),
+ new OO.ui.MenuOptionWidget( { data: 'zxc', label: 'Label for zxc' } ),
+ new OO.ui.MenuOptionWidget( { data: 'vbn', label: 'Label for vbn' } )
+ ]
+ }
+ } ),
+ {
+ label: 'ComboBoxWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget(),
+ {
+ label: 'ComboBoxWidget (empty)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonInputWidget( {
+ label: 'Submit the form',
+ type: 'submit'
+ } ),
+ {
+ align: 'top',
+ label: 'ButtonInputWidget'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonInputWidget( {
+ label: 'Submit the form',
+ type: 'submit',
+ useInputTag: true
+ } ),
+ {
+ align: 'top',
+ label: 'ButtonInputWidget (using <input/>)\u200E'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Horizontal alignment',
+ items: [
+ new OO.ui.FieldLayout(
+ horizontalAlignmentWidget,
+ {
+ label: 'Multiple widgets shown as a single line, ' +
+ 'as used in compact forms or in parts of a bigger widget.',
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Draggable',
+ items: [
+ new OO.ui.FieldLayout(
+ new DragDropGroupWidget( {
+ orientation: 'horizontal',
+ items: [
+ new DragDropItemWidget( {
+ data: 'item1',
+ label: 'Item 1'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item2',
+ label: 'Item 2'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item3',
+ label: 'Item 3'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item4',
+ label: 'Item 4'
+ } )
+ ]
+ } ),
+ {
+ label: 'DraggableGroupWidget (horizontal)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new DragDropGroupWidget( {
+ items: [
+ new DragDropItemWidget( {
+ data: 'item1',
+ label: 'Item 1'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item2',
+ label: 'Item 2'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item3',
+ label: 'Item 3'
+ } ),
+ new DragDropItemWidget( {
+ data: 'item4',
+ label: 'Item 4'
+ } )
+ ]
+ } ),
+ {
+ label: 'DraggableGroupWidget (vertical)\u200E',
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Other widgets',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.IconWidget( {
+ icon: 'picture',
+ title: 'Picture icon'
+ } ),
+ {
+ label: 'IconWidget (normal)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.IconWidget( {
+ icon: 'remove',
+ flags: 'destructive',
+ title: 'Remove icon'
+ } ),
+ {
+ label: 'IconWidget (flagged)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.IconWidget( {
+ icon: 'picture',
+ title: 'Picture icon',
+ disabled: true
+ } ),
+ {
+ label: 'IconWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.IndicatorWidget( {
+ indicator: 'required',
+ title: 'Required indicator'
+ } ),
+ {
+ label: 'IndicatorWidget (normal)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.IndicatorWidget( {
+ indicator: 'required',
+ title: 'Required indicator',
+ disabled: true
+ } ),
+ {
+ label: 'IndicatorWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.LabelWidget( {
+ label: 'Label'
+ } ),
+ {
+ label: 'LabelWidget (normal)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.LabelWidget( {
+ label: 'Label',
+ disabled: true
+ } ),
+ {
+ label: 'LabelWidget (disabled)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.LabelWidget( {
+ label: new OO.ui.HtmlSnippet( '<b>Fancy</b> <i>text</i> <u>formatting</u>!' )
+ } ),
+ {
+ label: 'LabelWidget (with html)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.PopupButtonWidget( {
+ icon: 'info',
+ framed: false,
+ popup: {
+ head: true,
+ label: 'More information',
+ $content: $( '<p>Extra information here.</p>' ),
+ padded: true,
+ align: 'force-left'
+ }
+ } ),
+ {
+ label: 'PopupButtonWidget (frameless, with popup head, align: force-left)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.PopupButtonWidget( {
+ icon: 'info',
+ framed: false,
+ popup: {
+ head: true,
+ label: 'More information',
+ $content: $( '<p>Extra information here.</p>' ),
+ padded: true,
+ align: 'force-right'
+ }
+ } ),
+ {
+ label: 'PopupButtonWidget (frameless, with popup head align: force-right)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.PopupButtonWidget( {
+ icon: 'info',
+ framed: false,
+ popup: {
+ head: true,
+ label: 'More information',
+ $content: $( '<p>Extra information here.</p>' ),
+ padded: true,
+ align: 'backwards'
+ }
+ } ),
+ {
+ label: 'PopupButtonWidget (frameless, with popup head align: backwards)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.PopupButtonWidget( {
+ icon: 'info',
+ framed: false,
+ popup: {
+ head: true,
+ label: 'More information',
+ $content: $( '<p>Extra information here.</p>' ),
+ padded: true,
+ align: 'forwards'
+ }
+ } ),
+ {
+ label: 'PopupButtonWidget (frameless, with popup head align: forwards)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.PopupButtonWidget( {
+ icon: 'menu',
+ label: 'Options',
+ popup: {
+ $content: $( '<p>Additional options here.</p>' ),
+ padded: true,
+ align: 'left'
+ }
+ } ),
+ {
+ label: 'PopupButtonWidget (framed, no popup head)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new NumberLookupTextInputWidget(),
+ {
+ label: 'LookupElement (try inputting an integer)\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ProgressBarWidget( {
+ progress: 33
+ } ),
+ {
+ label: 'Progress bar',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ProgressBarWidget( {
+ progress: false
+ } ),
+ {
+ label: 'Progress bar (indeterminate)\u200E',
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FieldsetLayout( {
+ label: 'Field layouts',
+ help: 'I am an additional, helpful information. Lorem ipsum dolor sit amet, cibo pri ' +
+ 'in, duo ex inimicus perpetua complectitur, mel periculis similique at.\u200E',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'FieldLayout with help',
+ help: 'I am an additional, helpful information. Lorem ipsum dolor sit amet, cibo pri ' +
+ 'in, duo ex inimicus perpetua complectitur, mel periculis similique at.\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'ActionFieldLayout aligned left',
+ align: 'left'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'ActionFieldLayout aligned inline',
+ align: 'inline'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'ActionFieldLayout aligned right',
+ align: 'right'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'ActionFieldLayout aligned top',
+ align: 'top'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: 'ActionFieldLayout aligned top with help',
+ help: 'I am an additional, helpful information. Lorem ipsum dolor sit amet, cibo pri ' +
+ 'in, duo ex inimicus perpetua complectitur, mel periculis similique at.\u200E',
+ align: 'top'
+ }
+ ),
+ new OO.ui.ActionFieldLayout(
+ new OO.ui.TextInputWidget( {} ),
+ new OO.ui.ButtonWidget( {
+ label: 'Button'
+ } ),
+ {
+ label: $( '<i>' ).text( 'ActionFieldLayout aligned top with rich text label' ),
+ align: 'top'
+ }
+ )
+ ]
+ } ),
+ new OO.ui.FormLayout( {
+ method: 'GET',
+ action: 'widgets.php',
+ items: [
+ new OO.ui.FieldsetLayout( {
+ label: 'Form layout',
+ items: [
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ name: 'username'
+ } ),
+ {
+ label: 'User name',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ name: 'password',
+ type: 'password'
+ } ),
+ {
+ label: 'Password',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.CheckboxInputWidget( {
+ name: 'rememberme',
+ selected: true
+ } ),
+ {
+ label: 'Remember me',
+ align: 'inline'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ButtonInputWidget( {
+ name: 'login',
+ label: 'Log in',
+ type: 'submit',
+ flags: [ 'primary', 'progressive' ],
+ icon: 'check'
+ } ),
+ {
+ label: null,
+ align: 'top'
+ }
+ )
+ ]
+ } )
+ ]
+ } )
+ ];
+
+ $.each( fieldsets, function ( i, fieldsetLayout ) {
+ $.each( fieldsetLayout.getItems(), function ( j, fieldLayout ) {
+ fieldLayout.$element.append(
+ demo.buildConsole( fieldLayout.fieldWidget, 'widget' )
+ );
+ } );
+ } );
+
+ $demo.append(
+ new OO.ui.PanelLayout( {
+ expanded: false,
+ framed: true
+ } ).$element
+ .addClass( 'oo-ui-demo-container' )
+ .append(
+ $( fieldsets.map( function ( fieldset ) { return fieldset.$element[ 0 ]; } ) )
+ )
+ );
+};
+
+OO.ui.Demo.static.defaultPage = 'widgets';
diff --git a/vendor/oojs/oojs-ui/demos/styles/demo.css b/vendor/oojs/oojs-ui/demos/styles/demo.css
new file mode 100644
index 00000000..1c8d6139
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/styles/demo.css
@@ -0,0 +1,250 @@
+body {
+ font-size: 0.8em;
+ font-family: sans-serif;
+}
+
+/* Layout */
+
+.oo-ui-demo-menu > .oo-ui-widget {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 1em;
+}
+
+.oo-ui-demo-menu .oo-ui-demo-pageDropdown {
+ width: 10em;
+}
+
+.oo-ui-demo {
+ max-width: 62.5em;
+ margin: 0 auto;
+}
+
+.oo-ui-demo-container {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+@media (max-width: 750px) {
+ .oo-ui-demo-menu {
+ margin-bottom: 1em;
+ }
+ .oo-ui-demo-container {
+ padding: 1em 2em;
+ }
+}
+
+@media (min-width: 751px) {
+ .oo-ui-demo-menu {
+ margin-bottom: 2em;
+ }
+ .oo-ui-demo-container {
+ padding: 2em 4em;
+ }
+}
+
+/* Toolbars demo */
+
+.oo-ui-demo-toolbars {
+ padding: 0;
+ margin-bottom: 2em;
+}
+
+.oo-ui-demo-toolbars-contents {
+ padding: 2em;
+ min-height: 100px;
+}
+
+/* Icons demo */
+
+.oo-ui-demo-icons .oo-ui-buttonSelectWidget {
+ margin-bottom: 2em;
+}
+
+.oo-ui-demo-icons .oo-ui-fieldLayout {
+ display: inline-block;
+ margin-bottom: 0;
+}
+
+.oo-ui-demo-icons .oo-ui-fieldLayout .oo-ui-labelElement-label {
+ display: none;
+}
+
+.oo-ui-demo-icons .oo-ui-fieldLayout .oo-ui-buttonElement-button {
+ margin: 1em 1em 1em 0;
+ width: 12em;
+ opacity: 0.8;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.oo-ui-demo-icons .oo-ui-fieldLayout .oo-ui-buttonElement-button .oo-ui-labelElement-label {
+ text-transform: capitalize;
+ display: inline !important;
+}
+
+.oo-ui-demo-icons .oo-ui-fieldLayout .oo-ui-buttonElement-button:hover {
+ opacity: 1;
+}
+
+/* Widgets demo */
+
+.oo-ui-demo-horizontal-alignment > .oo-ui-checkboxInputWidget,
+.oo-ui-demo-horizontal-alignment > .oo-ui-radioInputWidget,
+.oo-ui-demo-horizontal-alignment > .oo-ui-buttonInputWidget,
+.oo-ui-demo-horizontal-alignment > .oo-ui-textInputWidget,
+.oo-ui-demo-horizontal-alignment > .oo-ui-dropdownInputWidget {
+ display: inline-block;
+}
+
+.oo-ui-demo-horizontal-alignment > .oo-ui-textInputWidget,
+.oo-ui-demo-horizontal-alignment > .oo-ui-dropdownInputWidget {
+ max-width: 10em;
+}
+
+/* Console */
+
+.oo-ui-demo-console {
+ clear: both;
+ position: relative;
+ border: 1px solid transparent;
+ margin-bottom: -2px;
+ line-height: 1.4;
+}
+
+.oo-ui-demo-console-expanded {
+ width: 100%;
+ border-color: #aaa;
+ margin: 0.5em 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.oo-ui-demo-console-toggle {
+ float: right;
+ margin-top: -2em;
+ margin-right: -2em;
+ cursor: pointer;
+}
+
+.oo-ui-demo-console-expanded .oo-ui-demo-console-toggle {
+ margin-top: -2.5em;
+}
+
+.oo-ui-demo-console-toggle::after {
+ position: relative;
+ display: inline-block;
+ width: 1.6em;
+ background: #f9f9f9;
+ border-radius: 50%;
+ line-height: 1.6em;
+ color: #999;
+ text-align: center;
+ content: "→";
+}
+
+.oo-ui-demo-console-toggle:hover::after,
+.oo-ui-demo-console-expanded .oo-ui-demo-console-toggle::after {
+ background: #eee;
+ color: #333;
+}
+
+.oo-ui-demo-console-expanded .oo-ui-demo-console-toggle::after {
+ content: "↑";
+}
+
+.oo-ui-demo-console-collapsed .oo-ui-demo-console-log,
+.oo-ui-demo-console-collapsed .oo-ui-demo-console-label,
+.oo-ui-demo-console-collapsed .oo-ui-demo-console-submit {
+ display: none;
+}
+
+.oo-ui-demo-console-log {
+ border: 1px solid #eee;
+ border-width: 0 0 1px 0;
+ max-height: 5em;
+ overflow: auto;
+}
+
+.oo-ui-demo-console-log-line {
+ position: relative;
+ padding-left: 20px;
+}
+
+.oo-ui-demo-console-log-line-input {
+ font-style: italic;
+ color: #555;
+}
+
+.oo-ui-demo-console-log-line-input::before {
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 20px;
+ text-align: center;
+ bottom: 0;
+ content: "→";
+}
+
+.oo-ui-demo-console-log-line-error {
+ color: red;
+}
+
+.oo-ui-demo-console-label {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-right: -20px;
+ cursor: pointer;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.oo-ui-demo-console-label::before {
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 20px;
+ text-align: center;
+ content: '→';
+ color: blue;
+}
+
+.oo-ui-demo-console-input {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ width: 100%;
+}
+
+.oo-ui-demo-console-input:focus {
+ outline: 0;
+}
+
+.oo-ui-demo-console-submit {
+ display: inline-block;
+ width: 20px;
+ line-height: 1.6em;
+ text-align: center;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background: #f9f9f9;
+ color: #888;
+ cursor: pointer;
+}
+
+.oo-ui-demo-console-submit:hover {
+ background: #eee;
+ color: #333;
+}
diff --git a/vendor/oojs/oojs-ui/demos/widgets.php b/vendor/oojs/oojs-ui/demos/widgets.php
new file mode 100644
index 00000000..494a7ac1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/demos/widgets.php
@@ -0,0 +1,830 @@
+<?php
+ $autoload = '../vendor/autoload.php';
+ if ( !file_exists( $autoload ) ) {
+ echo '<h1>Did you forget to run <code>composer install</code>?</h1>';
+ exit();
+ }
+ require_once $autoload;
+
+ $theme = ( isset( $_GET['theme'] ) && $_GET['theme'] === 'apex' ) ? 'apex' : 'mediawiki';
+ $themeClass = 'OOUI\\' . ( $theme === 'apex' ? 'Apex' : 'MediaWiki' ) . 'Theme';
+ OOUI\Theme::setSingleton( new $themeClass() );
+
+ $graphicSuffixMap = array(
+ 'mixed' => '',
+ 'vector' => '.vector',
+ 'raster' => '.raster',
+ );
+ $graphic = ( isset( $_GET['graphic'] ) && isset( $graphicSuffixMap[ $_GET['graphic'] ] ) )
+ ? $_GET['graphic'] : 'vector';
+ $graphicSuffix = $graphicSuffixMap[ $graphic ];
+
+ $direction = ( isset( $_GET['direction'] ) && $_GET['direction'] === 'rtl' ) ? 'rtl' : 'ltr';
+ $directionSuffix = $direction === 'rtl' ? '.rtl' : '';
+ OOUI\Element::setDefaultDir( $direction );
+
+ $query = array(
+ 'theme' => $theme,
+ 'graphic' => $graphic,
+ 'direction' => $direction,
+ );
+ $styleFileName = "oojs-ui-$theme$graphicSuffix$directionSuffix.css";
+?>
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+ <meta charset="UTF-8">
+ <title>OOjs UI Widget Demo</title>
+ <link rel="stylesheet" href="../dist/<?php echo $styleFileName; ?>" title="theme">
+ <link rel="stylesheet" href="styles/demo<?php echo $directionSuffix; ?>.css">
+</head>
+<body class="oo-ui-<?php echo $direction; ?>">
+ <div class="oo-ui-demo">
+ <div class="oo-ui-demo-menu">
+ <?php
+ echo new OOUI\ButtonGroupWidget( array(
+ 'infusable' => true,
+ 'items' => array(
+ new OOUI\ButtonWidget( array(
+ 'id' => 'theme-mediawiki',
+ 'label' => 'MediaWiki',
+ 'data' => 'mediawiki',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'theme' => 'mediawiki' ) ) ),
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'id' => 'theme-apex',
+ 'label' => 'Apex',
+ 'data' => 'apex',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'theme' => 'apex' ) ) ),
+ ) ),
+ )
+ ) );
+ echo new OOUI\ButtonGroupWidget( array(
+ 'infusable' => true,
+ 'items' => array(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Mixed',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'graphic' => 'mixed' ) ) ),
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Vector',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'graphic' => 'vector' ) ) ),
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Raster',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'graphic' => 'raster' ) ) ),
+ ) ),
+ )
+ ) );
+ echo new OOUI\ButtonGroupWidget( array(
+ 'infusable' => true,
+ 'items' => array(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'LTR',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'direction' => 'ltr' ) ) ),
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'label' => 'RTL',
+ 'href' => '?' . http_build_query( array_merge( $query, array( 'direction' => 'rtl' ) ) ),
+ ) ),
+ )
+ ) );
+ ?>
+ </div>
+ <?php
+ $demoContainer = new OOUI\PanelLayout( array(
+ 'expanded' => false,
+ 'padded' => true,
+ 'framed' => true,
+ ) );
+ $demoContainer->addClasses( array( 'oo-ui-demo-container' ) );
+
+ $styles = array(
+ array(),
+ array(
+ 'flags' => array( 'progressive' ),
+ ),
+ array(
+ 'flags' => array( 'constructive' ),
+ ),
+ array(
+ 'flags' => array( 'destructive' ),
+ ),
+ array(
+ 'flags' => array( 'primary', 'progressive' ),
+ ),
+ array(
+ 'flags' => array( 'primary', 'constructive' ),
+ ),
+ array(
+ 'flags' => array( 'primary', 'destructive' ),
+ ),
+ );
+ $states = array(
+ array(
+ 'label' => 'Button',
+ ),
+ array(
+ 'label' => 'Button',
+ 'icon' => 'tag',
+ ),
+ array(
+ 'label' => 'Button',
+ 'icon' => 'tag',
+ 'indicator' => 'down',
+ ),
+ array(
+ 'icon' => 'tag',
+ 'title' => "Title text",
+ ),
+ array(
+ 'indicator' => 'down',
+ ),
+ array(
+ 'icon' => 'tag',
+ 'indicator' => 'down',
+ ),
+ array(
+ 'label' => 'Button',
+ 'disabled' => true,
+ ),
+ array(
+ 'icon' => 'tag',
+ 'title' => "Title text",
+ 'disabled' => true,
+ ),
+ array(
+ 'indicator' => 'down',
+ 'disabled' => true,
+ ),
+ );
+ $buttonStyleShowcaseWidget = new OOUI\Widget();
+ foreach ( $styles as $style ) {
+ foreach ( $states as $state ) {
+ $buttonStyleShowcaseWidget->appendContent(
+ new OOUI\ButtonWidget( array_merge( $style, $state, array( 'infusable' => true ) ) )
+ );
+ }
+ $buttonStyleShowcaseWidget->appendContent( new OOUI\HtmlSnippet( '<br />' ) );
+ }
+
+ $horizontalAlignmentWidget = new OOUI\Widget( array(
+ 'classes' => array( 'oo-ui-demo-horizontal-alignment' )
+ ) );
+ # Adding content after the fact does not play well with
+ # infusability. We should be using a proper Layout here.
+ $horizontalAlignmentWidget->appendContent(
+ new OOUI\ButtonWidget( array( 'label' => 'Button' ) ),
+ new OOUI\ButtonGroupWidget( array( 'items' => array(
+ new OOUI\ButtonWidget( array( 'label' => 'A' ) ),
+ new OOUI\ButtonWidget( array( 'label' => 'B' ) )
+ ) ) ),
+ new OOUI\ButtonInputWidget( array( 'label' => 'ButtonInput' ) ),
+ new OOUI\TextInputWidget( array( 'value' => 'TextInput' ) ),
+ new OOUI\DropdownInputWidget( array( 'options' => array(
+ array(
+ 'label' => 'DropdownInput',
+ 'data' => null
+ )
+ ) ) ),
+ new OOUI\CheckboxInputWidget( array( 'selected' => true ) ),
+ new OOUI\RadioInputWidget( array( 'selected' => true ) ),
+ new OOUI\LabelWidget( array( 'label' => 'Label' ) )
+ );
+
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Simple buttons',
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array( 'label' => 'Normal' ) ),
+ array(
+ 'label' => "ButtonWidget (normal)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Progressive',
+ 'flags' => array( 'progressive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (progressive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Constructive',
+ 'flags' => array( 'constructive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (constructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Destructive',
+ 'flags' => array( 'destructive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (destructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Primary progressive',
+ 'flags' => array( 'primary', 'progressive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (primary, progressive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Primary constructive',
+ 'flags' => array( 'primary', 'constructive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (primary, constructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Primary destructive',
+ 'flags' => array( 'primary', 'destructive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (primary, destructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Disabled',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "ButtonWidget (disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Constructive',
+ 'flags' => array( 'constructive' ),
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "ButtonWidget (constructive, disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Constructive',
+ 'icon' => 'tag',
+ 'flags' => array( 'constructive' ),
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "ButtonWidget (constructive, icon, disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Icon',
+ 'icon' => 'tag'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (icon)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Icon',
+ 'icon' => 'tag',
+ 'flags' => array( 'progressive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (icon, progressive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Indicator',
+ 'indicator' => 'down'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (indicator)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Indicator',
+ 'indicator' => 'down',
+ 'flags' => array( 'constructive' )
+ ) ),
+ array(
+ 'label' => "ButtonWidget (indicator, constructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'icon' => 'help',
+ 'title' => 'Icon only'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (icon only)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'icon' => 'tag',
+ 'label' => 'Labeled'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'flags' => array( 'progressive' ),
+ 'icon' => 'check',
+ 'label' => 'Progressive'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless, progressive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'flags' => array( 'destructive' ),
+ 'icon' => 'remove',
+ 'label' => 'Destructive'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless, destructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'flags' => array( 'constructive' ),
+ 'icon' => 'add',
+ 'label' => 'Constructive'
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless, constructive)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'icon' => 'tag',
+ 'label' => 'Disabled',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless, disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'framed' => false,
+ 'flags' => array( 'constructive' ),
+ 'icon' => 'tag',
+ 'label' => 'Constructive',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "ButtonWidget (frameless, constructive, disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ )
+ )
+ ) ) );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Button sets',
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\ButtonGroupWidget( array(
+ 'items' => array(
+ new OOUI\ButtonWidget( array(
+ 'icon' => 'tag',
+ 'label' => 'One'
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Two'
+ ) ),
+ new OOUI\ButtonWidget( array(
+ 'indicator' => 'required',
+ 'label' => 'Three'
+ ) )
+ )
+ ) ),
+ array(
+ 'label' => 'ButtonGroupWidget',
+ 'align' => 'top'
+ )
+ )
+ )
+ ) ) );
+ # Note that $buttonStyleShowcaseWidget is not infusable,
+ # because the contents would not be preserved -- we assume
+ # that widgets will manage their own contents by default,
+ # but here we've manually appended content to the widget.
+ # If we embed it in an infusable FieldsetLayout, it will be
+ # (recursively) made infusable. We protect the FieldLayout
+ # by wrapping it with a new <div> Tag, so that it won't get
+ # rebuilt during infusion.
+ $wrappedFieldLayout = new OOUI\Tag( 'div' );
+ $wrappedFieldLayout->appendContent(
+ new OOUI\FieldLayout(
+ $buttonStyleShowcaseWidget,
+ array(
+ 'align' => 'top'
+ )
+ )
+ );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Button style showcase',
+ 'items' => array( $wrappedFieldLayout ),
+ ) ) );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Form widgets',
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\CheckboxInputWidget( array(
+ 'selected' => true
+ ) ),
+ array(
+ 'align' => 'inline',
+ 'label' => 'CheckboxInputWidget'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\CheckboxInputWidget( array(
+ 'selected' => true,
+ 'disabled' => true
+ ) ),
+ array(
+ 'align' => 'inline',
+ 'label' => "CheckboxInputWidget (disabled)\xE2\x80\x8E"
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\RadioInputWidget( array(
+ 'name' => 'oojs-ui-radio-demo'
+ ) ),
+ array(
+ 'align' => 'inline',
+ 'label' => 'Connected RadioInputWidget #1'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\RadioInputWidget( array(
+ 'name' => 'oojs-ui-radio-demo',
+ 'selected' => true
+ ) ),
+ array(
+ 'align' => 'inline',
+ 'label' => 'Connected RadioInputWidget #2'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\RadioInputWidget( array(
+ 'selected' => true,
+ 'disabled' => true
+ ) ),
+ array(
+ 'align' => 'inline',
+ 'label' => "RadioInputWidget (disabled)\xE2\x80\x8E"
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array( 'value' => 'Text input' ) ),
+ array(
+ 'label' => "TextInputWidget\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array( 'icon' => 'search' ) ),
+ array(
+ 'label' => "TextInputWidget (icon)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'indicator' => 'required',
+ 'required' => true
+ ) ),
+ array(
+ 'label' => "TextInputWidget (indicator, required)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array( 'placeholder' => 'Placeholder' ) ),
+ array(
+ 'label' => "TextInputWidget (placeholder)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'value' => 'Readonly',
+ 'readOnly' => true
+ ) ),
+ array(
+ 'label' => "TextInputWidget (readonly)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'value' => 'Disabled',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "TextInputWidget (disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'multiline' => true,
+ 'value' => "Multiline\nMultiline"
+ ) ),
+ array(
+ 'label' => "TextInputWidget (multiline)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\DropdownInputWidget( array(
+ 'options' => array(
+ array(
+ 'data' => 'a',
+ 'label' => 'First'
+ ),
+ array(
+ 'data' => 'b',
+ 'label' => 'Second'
+ ),
+ array(
+ 'data' => 'c',
+ 'label' => 'Third'
+ )
+ ),
+ 'value' => 'b'
+ ) ),
+ array(
+ 'label' => 'DropdownInputWidget',
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonInputWidget( array(
+ 'label' => 'Submit the form',
+ 'type' => 'submit'
+ ) ),
+ array(
+ 'align' => 'top',
+ 'label' => "ButtonInputWidget\xE2\x80\x8E"
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonInputWidget( array(
+ 'label' => 'Submit the form',
+ 'type' => 'submit',
+ 'useInputTag' => true
+ ) ),
+ array(
+ 'align' => 'top',
+ 'label' => "ButtonInputWidget (using <input/>)\xE2\x80\x8E"
+ )
+ )
+ )
+ ) ) );
+ # Again, $horizontalAlignmentWidget is not infusable because
+ # it manually added content after creation. If we embed it
+ # in an infusable FieldsetLayout, it will (recursively) be made
+ # infusable. So protect the widget by wrapping it in a
+ # <div> Tag.
+ $wrappedFieldLayout = new OOUI\Tag( 'div' );
+ $wrappedFieldLayout->appendContent(
+ new OOUI\FieldLayout(
+ $horizontalAlignmentWidget,
+ array(
+ 'label' => 'Multiple widgets shown as a single line, ' .
+ 'as used in compact forms or in parts of a bigger widget.',
+ 'align' => 'top'
+ )
+ )
+ );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Horizontal alignment',
+ 'items' => array( $wrappedFieldLayout ),
+ ) ) );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Other widgets',
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\IconWidget( array(
+ 'icon' => 'picture',
+ 'title' => 'Picture icon'
+ ) ),
+ array(
+ 'label' => "IconWidget (normal)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\IconWidget( array(
+ 'icon' => 'remove',
+ 'flags' => 'destructive',
+ 'title' => 'Remove icon'
+ ) ),
+ array(
+ 'label' => "IconWidget (flagged)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\IconWidget( array(
+ 'icon' => 'picture',
+ 'title' => 'Picture icon',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "IconWidget (disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\IndicatorWidget( array(
+ 'indicator' => 'required',
+ 'title' => 'Required indicator'
+ ) ),
+ array(
+ 'label' => "IndicatorWidget (normal)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\IndicatorWidget( array(
+ 'indicator' => 'required',
+ 'title' => 'Required indicator',
+ 'disabled' => true
+ ) ),
+ array(
+ 'label' => "IndicatorWidget (disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\LabelWidget( array(
+ 'label' => 'Label'
+ ) ),
+ array(
+ 'label' => "LabelWidget (normal)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\LabelWidget( array(
+ 'label' => 'Label',
+ 'disabled' => true,
+ ) ),
+ array(
+ 'label' => "LabelWidget (disabled)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\LabelWidget( array(
+ 'label' => new OOUI\HtmlSnippet( '<b>Fancy</b> <i>text</i> <u>formatting</u>!' ),
+ ) ),
+ array(
+ 'label' => "LabelWidget (with html)\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ )
+ )
+ ) ) );
+ $demoContainer->appendContent( new OOUI\FieldsetLayout( array(
+ 'infusable' => true,
+ 'label' => 'Field layouts',
+ 'help' => 'I am an additional, helpful information. Lorem ipsum dolor sit amet, cibo pri ' .
+ "in, duo ex inimicus perpetua complectitur, mel periculis similique at.\xE2\x80\x8E",
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\ButtonWidget( array(
+ 'label' => 'Button'
+ ) ),
+ array(
+ 'label' => 'FieldLayout with help',
+ 'help' => 'I am an additional, helpful information. Lorem ipsum dolor sit amet, cibo pri ' .
+ "in, duo ex inimicus perpetua complectitur, mel periculis similique at.\xE2\x80\x8E",
+ 'align' => 'top'
+ )
+ )
+ )
+ ) ) );
+
+ $demoContainer->appendContent( new OOUI\FormLayout( array(
+ 'infusable' => true,
+ 'method' => 'GET',
+ 'action' => 'widgets.php',
+ 'items' => array(
+ new OOUI\FieldsetLayout( array(
+ 'label' => 'Form layout',
+ 'items' => array(
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'name' => 'username',
+ ) ),
+ array(
+ 'label' => 'User name',
+ 'align' => 'top',
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( array(
+ 'name' => 'password',
+ 'type' => 'password',
+ ) ),
+ array(
+ 'label' => 'Password',
+ 'align' => 'top',
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\CheckboxInputWidget( array(
+ 'name' => 'rememberme',
+ 'selected' => true,
+ ) ),
+ array(
+ 'label' => 'Remember me',
+ 'align' => 'inline',
+ )
+ ),
+ new OOUI\FieldLayout(
+ new OOUI\ButtonInputWidget( array(
+ 'name' => 'login',
+ 'label' => 'Log in',
+ 'type' => 'submit',
+ 'flags' => array( 'primary', 'progressive' ),
+ 'icon' => 'check',
+ ) ),
+ array(
+ 'label' => null,
+ 'align' => 'top',
+ )
+ ),
+ )
+ ) )
+ )
+ ) ) );
+
+ echo $demoContainer;
+
+ ?>
+ </div>
+
+ <!-- Demonstrate JavaScript "infusion" of PHP widgets -->
+ <script src="../node_modules/jquery/dist/jquery.js"></script>
+ <script src="../node_modules/oojs/dist/oojs.jquery.js"></script>
+ <script src="../dist/oojs-ui.js"></script>
+ <script src="../dist/oojs-ui-apex.js"></script>
+ <script src="../dist/oojs-ui-mediawiki.js"></script>
+ <script src="./infusion.js"></script>
+</body>
+</html>
diff --git a/vendor/oojs/oojs-ui/i18n/ace.json b/vendor/oojs/oojs-ui/i18n/ace.json
new file mode 100644
index 00000000..0fdc1a89
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ace.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Si Gam Acèh"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pinah item u yup",
+ "ooui-outline-control-move-up": "Pinah item u ateuëh",
+ "ooui-toolbar-more": "Lom"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/af.json b/vendor/oojs/oojs-ui/i18n/af.json
new file mode 100644
index 00000000..6f79e370
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/af.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Naudefj"
+ ]
+ },
+ "ooui-outline-control-move-down": "Skuif item af",
+ "ooui-outline-control-move-up": "Skuif item op",
+ "ooui-outline-control-remove": "Verwyder item",
+ "ooui-toolbar-more": "Meer",
+ "ooui-toolgroup-expand": "Meer",
+ "ooui-toolgroup-collapse": "Minder",
+ "ooui-dialog-message-accept": "Regso",
+ "ooui-dialog-message-reject": "Kanselleer",
+ "ooui-dialog-process-error": "Iets het verkeerd gegaan",
+ "ooui-dialog-process-dismiss": "Sluit",
+ "ooui-dialog-process-retry": "Probeer weer",
+ "ooui-dialog-process-continue": "Gaan voort"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/am.json b/vendor/oojs/oojs-ui/i18n/am.json
new file mode 100644
index 00000000..bfe9d5c3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/am.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": [
+ "Elfalem"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ar.json b/vendor/oojs/oojs-ui/i18n/ar.json
new file mode 100644
index 00000000..058a1491
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ar.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ciphers",
+ "Claw eg",
+ "Elfalem",
+ "Jdforrester",
+ "Mido",
+ "OsamaK",
+ "زكريا",
+ "مشعل الحربي",
+ "ترجمان05",
+ "Abanima"
+ ]
+ },
+ "ooui-outline-control-move-down": "انقل العنصر للأسفل",
+ "ooui-outline-control-move-up": "انقل العنصر للأعلى",
+ "ooui-outline-control-remove": "أزل العنصر",
+ "ooui-toolbar-more": "مزيد",
+ "ooui-toolgroup-expand": "مزيد",
+ "ooui-toolgroup-collapse": "أقل",
+ "ooui-dialog-message-accept": "موافق",
+ "ooui-dialog-message-reject": "ألغ",
+ "ooui-dialog-process-error": "حدث خطأ",
+ "ooui-dialog-process-dismiss": "أغلق",
+ "ooui-dialog-process-retry": "حاول مرة أخرى",
+ "ooui-dialog-process-continue": "استمر"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/arc.json b/vendor/oojs/oojs-ui/i18n/arc.json
new file mode 100644
index 00000000..de5b7aff
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/arc.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": [
+ "Basharh"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/arq.json b/vendor/oojs/oojs-ui/i18n/arq.json
new file mode 100644
index 00000000..80987818
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/arq.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bachounda"
+ ]
+ },
+ "ooui-outline-control-move-down": "هبط الشيئ للتحت",
+ "ooui-outline-control-move-up": "طلع الشيئ للفوق",
+ "ooui-outline-control-remove": "أمحي العنصر",
+ "ooui-toolbar-more": "زيادة",
+ "ooui-dialog-message-accept": "مليح",
+ "ooui-dialog-message-reject": "رجَع",
+ "ooui-dialog-process-error": "حاجه ما خدمتش مليح",
+ "ooui-dialog-process-dismiss": "أرفضها",
+ "ooui-dialog-process-retry": "عاود جرب"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ast.json b/vendor/oojs/oojs-ui/i18n/ast.json
new file mode 100644
index 00000000..87d7688a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ast.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Basharh",
+ "Bishnu Saikia",
+ "Xuacu"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mover abaxo l'elementu",
+ "ooui-outline-control-move-up": "Mover arriba l'elementu",
+ "ooui-outline-control-remove": "Desaniciar elementu",
+ "ooui-toolbar-more": "Más",
+ "ooui-dialog-message-accept": "Aceutar",
+ "ooui-dialog-message-reject": "Encaboxar",
+ "ooui-dialog-process-error": "Daqué funcionó mal",
+ "ooui-dialog-process-dismiss": "Descartar",
+ "ooui-dialog-process-retry": "Vuelvi a intentalo"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/awa.json b/vendor/oojs/oojs-ui/i18n/awa.json
new file mode 100644
index 00000000..f78ed326
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/awa.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "1AnuraagPandey"
+ ]
+ },
+ "ooui-toolbar-more": "अउर"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/az.json b/vendor/oojs/oojs-ui/i18n/az.json
new file mode 100644
index 00000000..fc12d1b3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/az.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cekli829",
+ "Interfase",
+ "Jduranboger"
+ ]
+ },
+ "ooui-outline-control-move-down": "Bəndi aşağı apar",
+ "ooui-outline-control-move-up": "Bəndi yuxarı apar",
+ "ooui-outline-control-remove": "Bəndi sil",
+ "ooui-toolbar-more": "Daha artıq"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ba.json b/vendor/oojs/oojs-ui/i18n/ba.json
new file mode 100644
index 00000000..ff915b06
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ba.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "AiseluRB",
+ "Amire80",
+ "Assele",
+ "Haqmar",
+ "Sagan",
+ "Рустам Нурыев"
+ ]
+ },
+ "ooui-outline-control-move-down": "Аҫҡа күсерергә",
+ "ooui-outline-control-move-up": "Өҫкә күсерергә"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/bcc.json b/vendor/oojs/oojs-ui/i18n/bcc.json
new file mode 100644
index 00000000..a340a881
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/bcc.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baloch Afghanistan"
+ ]
+ },
+ "ooui-dialog-message-accept": "اوکی",
+ "ooui-dialog-process-retry": "پدا کوشش کورتین"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/bcl.json b/vendor/oojs/oojs-ui/i18n/bcl.json
new file mode 100644
index 00000000..bc2251e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/bcl.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geopoet",
+ "Sky Harbor"
+ ]
+ },
+ "ooui-outline-control-move-down": "Balyuhon an aytem paibaba",
+ "ooui-outline-control-move-up": "Balyuhon an aytem paitaas",
+ "ooui-toolbar-more": "Kadugangan"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/be-tarask.json b/vendor/oojs/oojs-ui/i18n/be-tarask.json
new file mode 100644
index 00000000..c5475f85
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/be-tarask.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "EugeneZelenko",
+ "Wizardist",
+ "Чаховіч Уладзіслаў",
+ "Zedlik"
+ ]
+ },
+ "ooui-outline-control-move-down": "Перасунуць ніжэй",
+ "ooui-outline-control-move-up": "Перасунуць вышэй",
+ "ooui-toolbar-more": "Болей"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/be.json b/vendor/oojs/oojs-ui/i18n/be.json
new file mode 100644
index 00000000..fb0f6880
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/be.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Чаховіч Уладзіслаў",
+ "Artificial123"
+ ]
+ },
+ "ooui-dialog-message-accept": "ОК",
+ "ooui-dialog-message-reject": "Адмяніць"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/bg.json b/vendor/oojs/oojs-ui/i18n/bg.json
new file mode 100644
index 00000000..02d95b52
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/bg.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "DCLXVI",
+ "Hristofor.mirchev",
+ "පසිඳු කාවින්ද",
+ "Mitzev"
+ ]
+ },
+ "ooui-outline-control-remove": "Премахване на обекта",
+ "ooui-toolbar-more": "Още"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/bn.json b/vendor/oojs/oojs-ui/i18n/bn.json
new file mode 100644
index 00000000..1cfa6c45
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/bn.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aftab1995",
+ "Bellayet",
+ "Jayantanth",
+ "Nasir8891",
+ "Runab",
+ "Sayak Sarkar",
+ "Aftabuzzaman",
+ "RYasmeen (WMF)"
+ ]
+ },
+ "ooui-outline-control-move-down": "আইটেম নিচে স্থানান্তর",
+ "ooui-outline-control-move-up": "আইটেম উপরে স্থানান্তর",
+ "ooui-outline-control-remove": "আইটেম সরান",
+ "ooui-toolbar-more": "আরও",
+ "ooui-toolgroup-expand": "আরও",
+ "ooui-toolgroup-collapse": "কম দেখাও",
+ "ooui-dialog-message-accept": "ঠিক আছে",
+ "ooui-dialog-message-reject": "বাতিল",
+ "ooui-dialog-process-error": "কিছু একটায় ত্রুটি হয়েছে",
+ "ooui-dialog-process-dismiss": "বাতিল করুন",
+ "ooui-dialog-process-retry": "আবার চেষ্টা করুন",
+ "ooui-dialog-process-continue": "অগ্রসর হোন"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/br.json b/vendor/oojs/oojs-ui/i18n/br.json
new file mode 100644
index 00000000..83af863c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/br.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fohanno",
+ "Fulup",
+ "Y-M D",
+ "Maoris"
+ ]
+ },
+ "ooui-outline-control-move-down": "Lakaat an elfenn da ziskenn",
+ "ooui-outline-control-move-up": "Lakaat an elfenn da bignat",
+ "ooui-outline-control-remove": "Tennañ an elfenn",
+ "ooui-toolbar-more": "Muioc'h",
+ "ooui-toolgroup-expand": "Muioc'h",
+ "ooui-toolgroup-collapse": "Nebeutoc'h",
+ "ooui-dialog-message-accept": "Mat eo",
+ "ooui-dialog-message-reject": "Nullañ",
+ "ooui-dialog-process-error": "Un dra bennak a-dreuz a zo bet",
+ "ooui-dialog-process-dismiss": "Disteurel",
+ "ooui-dialog-process-retry": "Klask en-dro",
+ "ooui-dialog-process-continue": "Kenderc'hel"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/bs.json b/vendor/oojs/oojs-ui/i18n/bs.json
new file mode 100644
index 00000000..130bd8e5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/bs.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "DzWiki"
+ ]
+ },
+ "ooui-outline-control-move-down": "Premjesti stavku dole",
+ "ooui-outline-control-move-up": "Premjesti stavku gore",
+ "ooui-outline-control-remove": "Ukloni stavku",
+ "ooui-toolbar-more": "Više",
+ "ooui-toolgroup-expand": "Više",
+ "ooui-toolgroup-collapse": "Manje",
+ "ooui-dialog-message-accept": "U redu",
+ "ooui-dialog-message-reject": "Otkaži",
+ "ooui-dialog-process-error": "Nešto je pošlo naopako",
+ "ooui-dialog-process-dismiss": "Odbaci",
+ "ooui-dialog-process-retry": "Pokušajte ponovo",
+ "ooui-dialog-process-continue": "Nastavi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ca.json b/vendor/oojs/oojs-ui/i18n/ca.json
new file mode 100644
index 00000000..ce3afa43
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ca.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alvaro Vidal-Abarca",
+ "Amire80",
+ "Arnaugir",
+ "Pginer",
+ "QuimGil",
+ "SMP",
+ "Vriullop",
+ "Toniher",
+ "Edustus",
+ "Davidpar"
+ ]
+ },
+ "ooui-outline-control-move-down": "Baixa l'element",
+ "ooui-outline-control-move-up": "Puja l'element",
+ "ooui-outline-control-remove": "Esborra l'ítem",
+ "ooui-toolbar-more": "Més",
+ "ooui-toolgroup-expand": "Més",
+ "ooui-toolgroup-collapse": "Menys",
+ "ooui-dialog-message-accept": "D'acord",
+ "ooui-dialog-message-reject": "Cancel·la",
+ "ooui-dialog-process-error": "Alguna cosa no ha funcionat",
+ "ooui-dialog-process-dismiss": "Descarta",
+ "ooui-dialog-process-retry": "Torneu-ho a provar",
+ "ooui-dialog-process-continue": "Continua"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ce.json b/vendor/oojs/oojs-ui/i18n/ce.json
new file mode 100644
index 00000000..562dc3d5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ce.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Умар"
+ ]
+ },
+ "ooui-outline-control-move-down": "Лаха яккха элемент",
+ "ooui-outline-control-move-up": "Лаккха яккха элемент",
+ "ooui-outline-control-remove": "ДӀадаха меттиг",
+ "ooui-toolbar-more": "Кхин",
+ "ooui-toolgroup-expand": "Дукха",
+ "ooui-toolgroup-collapse": "КӀезиг",
+ "ooui-dialog-message-accept": "ХӀаъ",
+ "ooui-dialog-message-reject": "Цаоьшу",
+ "ooui-dialog-process-continue": "Кхин дӀа"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ckb.json b/vendor/oojs/oojs-ui/i18n/ckb.json
new file mode 100644
index 00000000..0c66619d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ckb.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Calak",
+ "Muhammed taha",
+ "Serwan"
+ ]
+ },
+ "ooui-dialog-message-accept": "باشە",
+ "ooui-dialog-message-reject": "پاشگەزبوونەوە"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/co.json b/vendor/oojs/oojs-ui/i18n/co.json
new file mode 100644
index 00000000..01d181d7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/co.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Paulu"
+ ]
+ },
+ "ooui-outline-control-move-down": "Fà falà l'ogettu",
+ "ooui-outline-control-move-up": "Fà cullà l'ogettu"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/crh-cyrl.json b/vendor/oojs/oojs-ui/i18n/crh-cyrl.json
new file mode 100644
index 00000000..ccc00269
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/crh-cyrl.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Don Alessandro"
+ ]
+ },
+ "ooui-toolbar-more": "Даа зияде"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/crh-latn.json b/vendor/oojs/oojs-ui/i18n/crh-latn.json
new file mode 100644
index 00000000..7ad7b0bb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/crh-latn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Don Alessandro"
+ ]
+ },
+ "ooui-toolbar-more": "Daa ziyade"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/cs.json b/vendor/oojs/oojs-ui/i18n/cs.json
new file mode 100644
index 00000000..1db9aed5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/cs.json
@@ -0,0 +1,29 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chmee2",
+ "Jkjk",
+ "Juandev",
+ "Koo6",
+ "Littledogboy",
+ "Michaelbrabec",
+ "Mormegil",
+ "Polda18",
+ "Tchoř",
+ "ශ්වෙත",
+ "Vojtěch Dostál"
+ ]
+ },
+ "ooui-outline-control-move-down": "Přesunout položku dolů",
+ "ooui-outline-control-move-up": "Přesunout položku nahoru",
+ "ooui-outline-control-remove": "Odstranit položku",
+ "ooui-toolbar-more": "Další",
+ "ooui-toolgroup-expand": "Více",
+ "ooui-toolgroup-collapse": "Méně",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Storno",
+ "ooui-dialog-process-error": "Něco se pokazilo",
+ "ooui-dialog-process-dismiss": "Zavřít",
+ "ooui-dialog-process-retry": "Zkusit znovu",
+ "ooui-dialog-process-continue": "Pokračovat"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/cu.json b/vendor/oojs/oojs-ui/i18n/cu.json
new file mode 100644
index 00000000..aa916af0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/cu.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "ОйЛ"
+ ]
+ },
+ "ooui-toolbar-more": "вѧщє",
+ "ooui-toolgroup-expand": "вѧщє"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/cy.json b/vendor/oojs/oojs-ui/i18n/cy.json
new file mode 100644
index 00000000..b74cd064
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/cy.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lloffiwr",
+ "Robin Owain",
+ "ОйЛ",
+ "DChan (WMF)"
+ ]
+ },
+ "ooui-outline-control-move-down": "Symud yr eitem i lawr",
+ "ooui-outline-control-move-up": "Symud yr eitem i fyny",
+ "ooui-outline-control-remove": "Tynnu'r eitem",
+ "ooui-toolbar-more": "Rhagor"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/da.json b/vendor/oojs/oojs-ui/i18n/da.json
new file mode 100644
index 00000000..0b847be1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/da.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cgtdk",
+ "Christian List",
+ "EileenSanda",
+ "Laketown",
+ "Palnatoke",
+ "Simeondahl",
+ "Tehnix"
+ ]
+ },
+ "ooui-outline-control-move-down": "Flyt ned",
+ "ooui-outline-control-move-up": "Flyt op",
+ "ooui-toolbar-more": "Mere"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/de.json b/vendor/oojs/oojs-ui/i18n/de.json
new file mode 100644
index 00000000..15624fd4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/de.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "APPER",
+ "G.Hagedorn",
+ "Inkowik",
+ "Jcornelius",
+ "Jdforrester",
+ "Kghbln",
+ "Metalhead64",
+ "Murma174",
+ "Se4598",
+ "Tomabrafix"
+ ]
+ },
+ "ooui-outline-control-move-down": "Element nach unten verschieben",
+ "ooui-outline-control-move-up": "Element nach oben verschieben",
+ "ooui-outline-control-remove": "Element entfernen",
+ "ooui-toolbar-more": "Mehr",
+ "ooui-toolgroup-expand": "Mehr",
+ "ooui-toolgroup-collapse": "Weniger",
+ "ooui-dialog-message-accept": "Okay",
+ "ooui-dialog-message-reject": "Abbrechen",
+ "ooui-dialog-process-error": "Etwas ist schief gelaufen",
+ "ooui-dialog-process-dismiss": "Ausblenden",
+ "ooui-dialog-process-retry": "Erneut versuchen",
+ "ooui-dialog-process-continue": "Fortfahren"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/diq.json b/vendor/oojs/oojs-ui/i18n/diq.json
new file mode 100644
index 00000000..881ff674
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/diq.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Erdemaslancan",
+ "Gorizon",
+ "Kghbln",
+ "Marmase",
+ "Mirzali",
+ "Se4598"
+ ]
+ },
+ "ooui-outline-control-move-down": "Bendi bere cêr",
+ "ooui-outline-control-move-up": "Bendi bere cor",
+ "ooui-outline-control-remove": "Obcey wedare",
+ "ooui-toolbar-more": "Zewbi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/dsb.json b/vendor/oojs/oojs-ui/i18n/dsb.json
new file mode 100644
index 00000000..7ad3f200
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/dsb.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Michawiki"
+ ]
+ },
+ "ooui-outline-control-move-down": "Element dołoj pśesunuś",
+ "ooui-outline-control-move-up": "Element górjej pśesunuś",
+ "ooui-outline-control-remove": "Zapisk wótpóraś",
+ "ooui-toolbar-more": "Wěcej"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/egl.json b/vendor/oojs/oojs-ui/i18n/egl.json
new file mode 100644
index 00000000..624ecaa3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/egl.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lévi",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Spôsta in bâs",
+ "ooui-outline-control-move-up": "Spôsta in êlt",
+ "ooui-outline-control-remove": "Armōv l'elemèint",
+ "ooui-toolbar-more": "Êter",
+ "ooui-dialog-message-accept": "'D acòrdi",
+ "ooui-dialog-message-reject": "Scanślèr"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/el.json b/vendor/oojs/oojs-ui/i18n/el.json
new file mode 100644
index 00000000..6fb7dbad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/el.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Astralnet",
+ "Dipa1965",
+ "Evropi",
+ "FocalPoint",
+ "Geraki",
+ "Glavkos",
+ "Nikosguard",
+ "Tifa93"
+ ]
+ },
+ "ooui-outline-control-move-down": "Μετακίνηση στοιχείου προς τα κάτω",
+ "ooui-outline-control-move-up": "Μετακίνηση στοιχείου προς τα επάνω",
+ "ooui-outline-control-remove": "Αφαίρεση στοιχείου",
+ "ooui-toolbar-more": "Περισσότερα",
+ "ooui-toolgroup-expand": "Περισσότερα",
+ "ooui-toolgroup-collapse": "Λιγότερα",
+ "ooui-dialog-message-accept": "ΟΚ",
+ "ooui-dialog-message-reject": "Ακύρωση",
+ "ooui-dialog-process-error": "Κάτι πήγε στραβά",
+ "ooui-dialog-process-dismiss": "Απόρριψη",
+ "ooui-dialog-process-retry": "Δοκιμάστε ξανά",
+ "ooui-dialog-process-continue": "Συνέχεια"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/eml.json b/vendor/oojs/oojs-ui/i18n/eml.json
new file mode 100644
index 00000000..6d9e8bf0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/eml.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gloria sah",
+ "Lévi"
+ ]
+ },
+ "ooui-outline-control-move-down": "Spôsta in bâs",
+ "ooui-outline-control-move-up": "Spôsta in êlta",
+ "ooui-outline-control-remove": "Tór vìa 'l elemèint",
+ "ooui-toolbar-more": "Êter",
+ "ooui-dialog-message-accept": "'D acòrdi",
+ "ooui-dialog-message-reject": "Scanślèr"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/en.json b/vendor/oojs/oojs-ui/i18n/en.json
new file mode 100644
index 00000000..1db3fd85
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/en.json
@@ -0,0 +1,31 @@
+{
+ "@metadata": {
+ "authors": [
+ "Trevor Parscal",
+ "Ed Sanders",
+ "James D. Forrester",
+ "Raimond Spekking",
+ "Erik Moeller",
+ "Moriel Schottlender",
+ "Yuki Shira",
+ "Siebrand Mazeland",
+ "Rob Moen",
+ "Timo Tijhof",
+ "Roan Kattouw",
+ "Christian Williams",
+ "Amir E. Aharoni"
+ ]
+ },
+ "ooui-outline-control-move-down": "Move item down",
+ "ooui-outline-control-move-up": "Move item up",
+ "ooui-outline-control-remove": "Remove item",
+ "ooui-toolbar-more": "More",
+ "ooui-toolgroup-expand": "More",
+ "ooui-toolgroup-collapse": "Fewer",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Cancel",
+ "ooui-dialog-process-error": "Something went wrong",
+ "ooui-dialog-process-dismiss": "Dismiss",
+ "ooui-dialog-process-retry": "Try again",
+ "ooui-dialog-process-continue": "Continue"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/eo.json b/vendor/oojs/oojs-ui/i18n/eo.json
new file mode 100644
index 00000000..8d9714c6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/eo.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Happy5214",
+ "KuboF",
+ "Shirayuki",
+ "Yekrats"
+ ]
+ },
+ "ooui-outline-control-move-down": "Movi eron suben",
+ "ooui-outline-control-move-up": "Movi eron supren",
+ "ooui-toolbar-more": "Pli"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/es.json b/vendor/oojs/oojs-ui/i18n/es.json
new file mode 100644
index 00000000..915791e6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/es.json
@@ -0,0 +1,33 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armando-Martin",
+ "Aruizdr",
+ "Benfutbol10",
+ "DJ Nietzsche",
+ "Erdemaslancan",
+ "Fitoschido",
+ "Imre",
+ "Invadinado",
+ "Jdforrester",
+ "Jduranboger",
+ "PoLuX124",
+ "Ralgis",
+ "Thehelpfulone",
+ "Gloria sah",
+ "Macofe"
+ ]
+ },
+ "ooui-outline-control-move-down": "Bajar elemento",
+ "ooui-outline-control-move-up": "Subir elemento",
+ "ooui-outline-control-remove": "Eliminar elemento",
+ "ooui-toolbar-more": "Más",
+ "ooui-toolgroup-expand": "Más",
+ "ooui-toolgroup-collapse": "Menos",
+ "ooui-dialog-message-accept": "Aceptar",
+ "ooui-dialog-message-reject": "Cancelar",
+ "ooui-dialog-process-error": "Algo salió mal",
+ "ooui-dialog-process-dismiss": "Descartar",
+ "ooui-dialog-process-retry": "Intentar de nuevo",
+ "ooui-dialog-process-continue": "Continuar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/et.json b/vendor/oojs/oojs-ui/i18n/et.json
new file mode 100644
index 00000000..6a212b6b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/et.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Avjoska",
+ "Pikne"
+ ]
+ },
+ "ooui-outline-control-move-down": "Liiguta üksust allapoole",
+ "ooui-outline-control-move-up": "Liiguta üksust ülespoole",
+ "ooui-outline-control-remove": "Eemalda üksus",
+ "ooui-toolbar-more": "Veel",
+ "ooui-toolgroup-expand": "Veel",
+ "ooui-toolgroup-collapse": "Vähem",
+ "ooui-dialog-message-accept": "Sobib",
+ "ooui-dialog-message-reject": "Loobu",
+ "ooui-dialog-process-error": "Midagi läks valesti",
+ "ooui-dialog-process-dismiss": "Hülga",
+ "ooui-dialog-process-retry": "Proovi uuesti",
+ "ooui-dialog-process-continue": "Jätka"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/eu.json b/vendor/oojs/oojs-ui/i18n/eu.json
new file mode 100644
index 00000000..e947582d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/eu.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "An13sa",
+ "Unai Fdz. de Betoño",
+ "Xabier Armendaritz",
+ "Subi"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mugitu itema beherantz",
+ "ooui-outline-control-move-up": "Mugitu itema gorantz",
+ "ooui-toolbar-more": "Gehiago",
+ "ooui-toolgroup-expand": "Gehiago",
+ "ooui-toolgroup-collapse": "Gutxiago",
+ "ooui-dialog-message-accept": "Ados",
+ "ooui-dialog-message-reject": "Utzi",
+ "ooui-dialog-process-error": "Zerbaitek huts egin du",
+ "ooui-dialog-process-retry": "Saiatu berriro",
+ "ooui-dialog-process-continue": "Jarraitu"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fa.json b/vendor/oojs/oojs-ui/i18n/fa.json
new file mode 100644
index 00000000..7cfcfa21
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fa.json
@@ -0,0 +1,29 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dalba",
+ "Ebraminio",
+ "Jdforrester",
+ "Ladsgroup",
+ "Mjbmr",
+ "Nojan Madinehi",
+ "Reza1615",
+ "Taha",
+ "درفش کاویانی",
+ "Armin1392",
+ "Alirezaaa"
+ ]
+ },
+ "ooui-outline-control-move-down": "انتقال مورد به پایین",
+ "ooui-outline-control-move-up": "انتقال مورد به بالا",
+ "ooui-outline-control-remove": "حذف مورد",
+ "ooui-toolbar-more": "بیشتر",
+ "ooui-toolgroup-expand": "بیشتر",
+ "ooui-toolgroup-collapse": "کمتر",
+ "ooui-dialog-message-accept": "تأیید",
+ "ooui-dialog-message-reject": "لغو",
+ "ooui-dialog-process-error": "مشکلی وجود دارد",
+ "ooui-dialog-process-dismiss": "نپذیرفتن",
+ "ooui-dialog-process-retry": "دوباره امتحان کن",
+ "ooui-dialog-process-continue": "ادامه"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fi.json b/vendor/oojs/oojs-ui/i18n/fi.json
new file mode 100644
index 00000000..3fb4110c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fi.json
@@ -0,0 +1,31 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beluga",
+ "Crt",
+ "Harriv",
+ "Linnea",
+ "Nedergard",
+ "Nike",
+ "Olli",
+ "Pxos",
+ "Samoasambia",
+ "Silvonen",
+ "Skalman",
+ "Stryn",
+ "VezonThunder"
+ ]
+ },
+ "ooui-outline-control-move-down": "Siirrä kohdetta alaspäin",
+ "ooui-outline-control-move-up": "Siirrä kohdetta ylöspäin",
+ "ooui-outline-control-remove": "Poista kohde",
+ "ooui-toolbar-more": "Lisää",
+ "ooui-toolgroup-expand": "Enemmän",
+ "ooui-toolgroup-collapse": "Vähemmän",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Peruuta",
+ "ooui-dialog-process-error": "Jokin meni pieleen",
+ "ooui-dialog-process-dismiss": "Hylkää",
+ "ooui-dialog-process-retry": "Yritä uudelleen",
+ "ooui-dialog-process-continue": "Jatka"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fo.json b/vendor/oojs/oojs-ui/i18n/fo.json
new file mode 100644
index 00000000..6230cc9b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fo.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "EileenSanda"
+ ]
+ },
+ "ooui-outline-control-move-down": "Flyt lutin niður",
+ "ooui-outline-control-move-up": "Flyt lutin upp",
+ "ooui-outline-control-remove": "Tak ein lut burtur",
+ "ooui-toolbar-more": "Meira",
+ "ooui-toolgroup-expand": "Meira",
+ "ooui-toolgroup-collapse": "Færri",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Avbrót",
+ "ooui-dialog-process-error": "Okkurt gekk galið",
+ "ooui-dialog-process-dismiss": "Lat aftur",
+ "ooui-dialog-process-retry": "Royn aftur",
+ "ooui-dialog-process-continue": "Halt fram"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fr.json b/vendor/oojs/oojs-ui/i18n/fr.json
new file mode 100644
index 00000000..9144cb01
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fr.json
@@ -0,0 +1,45 @@
+{
+ "@metadata": {
+ "authors": [
+ "Automatik",
+ "Benoit Rochon",
+ "Boniface",
+ "Brunoperel",
+ "Crochet.david",
+ "DavidL",
+ "Dereckson",
+ "Gomoko",
+ "Guillom",
+ "Hello71",
+ "Jean-Frédéric",
+ "Linedwell",
+ "Ltrlg",
+ "Metroitendo",
+ "NemesisIII",
+ "Nicolas NALLET",
+ "Npettiaux",
+ "Rastus Vernon",
+ "Seb35",
+ "Sherbrooke",
+ "Tpt",
+ "Trizek",
+ "Urhixidur",
+ "Verdy p",
+ "Wyz",
+ "SnowedEarth",
+ "Jdforrester"
+ ]
+ },
+ "ooui-outline-control-move-down": "Faire descendre l’élément",
+ "ooui-outline-control-move-up": "Faire monter l’élément",
+ "ooui-outline-control-remove": "Supprimer l’élément",
+ "ooui-toolbar-more": "Plus",
+ "ooui-toolgroup-expand": "Plus",
+ "ooui-toolgroup-collapse": "Moins",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Annuler",
+ "ooui-dialog-process-error": "Quelque chose a mal tourné",
+ "ooui-dialog-process-dismiss": "Rejeter",
+ "ooui-dialog-process-retry": "Réessayez",
+ "ooui-dialog-process-continue": "Continuer"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/frr.json b/vendor/oojs/oojs-ui/i18n/frr.json
new file mode 100644
index 00000000..54d0fb22
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/frr.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "ChrisPtDe",
+ "Murma174"
+ ]
+ },
+ "ooui-outline-control-move-down": "Element efter onern sküüw",
+ "ooui-outline-control-move-up": "Element efter boowen sküüw",
+ "ooui-outline-control-remove": "Element wechnem",
+ "ooui-toolbar-more": "Muar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fur.json b/vendor/oojs/oojs-ui/i18n/fur.json
new file mode 100644
index 00000000..83c2fd9e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fur.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Klenje",
+ "Tocaibon"
+ ]
+ },
+ "ooui-outline-control-move-down": "sposte sot",
+ "ooui-outline-control-move-up": "sposte in su",
+ "ooui-toolbar-more": "Altri"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/fy.json b/vendor/oojs/oojs-ui/i18n/fy.json
new file mode 100644
index 00000000..ddf9ff75
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/fy.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Robin0van0der0vliet"
+ ]
+ },
+ "ooui-toolbar-more": "Mear",
+ "ooui-toolgroup-expand": "Mear",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Annulearje"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/gd.json b/vendor/oojs/oojs-ui/i18n/gd.json
new file mode 100644
index 00000000..6a83c9c0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/gd.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "GunChleoc"
+ ]
+ },
+ "ooui-outline-control-move-down": "Gluais nì sìos",
+ "ooui-outline-control-move-up": "Gluais nì suas",
+ "ooui-outline-control-remove": "Thoir air falbh an nì",
+ "ooui-toolbar-more": "Barrachd",
+ "ooui-dialog-message-accept": "Ceart ma-thà",
+ "ooui-dialog-message-reject": "Sguir dheth"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/gl.json b/vendor/oojs/oojs-ui/i18n/gl.json
new file mode 100644
index 00000000..a4339f47
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/gl.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alison",
+ "Kscanne",
+ "Toliño",
+ "Elisardojm"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mover o elemento abaixo",
+ "ooui-outline-control-move-up": "Mover o elemento arriba",
+ "ooui-outline-control-remove": "Eliminar o elemento",
+ "ooui-toolbar-more": "Máis",
+ "ooui-toolgroup-expand": "Máis",
+ "ooui-toolgroup-collapse": "Menos",
+ "ooui-dialog-message-accept": "Aceptar",
+ "ooui-dialog-message-reject": "Cancelar",
+ "ooui-dialog-process-error": "Algo foi mal",
+ "ooui-dialog-process-dismiss": "Agochar",
+ "ooui-dialog-process-retry": "Inténteo de novo",
+ "ooui-dialog-process-continue": "Continuar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/gu.json b/vendor/oojs/oojs-ui/i18n/gu.json
new file mode 100644
index 00000000..2d8315bf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/gu.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ashok modhvadia",
+ "KartikMistry",
+ "The Discoverer"
+ ]
+ },
+ "ooui-outline-control-move-down": "વસ્તુ નીચે ખસેડો",
+ "ooui-outline-control-move-up": "વસ્તુ ઉપર ખસેડો",
+ "ooui-outline-control-remove": "વસ્તુ હટાવો",
+ "ooui-toolbar-more": "વધુ",
+ "ooui-dialog-message-accept": "બરાબર",
+ "ooui-dialog-message-reject": "રદ કરો",
+ "ooui-dialog-process-error": "કંઇક ગરબડ થઇ",
+ "ooui-dialog-process-retry": "ફરી પ્રયત્ન કરો"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/he.json b/vendor/oojs/oojs-ui/i18n/he.json
new file mode 100644
index 00000000..cadc416c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/he.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "ExampleTomer",
+ "Guycn2",
+ "Matanya",
+ "Mooeypoo",
+ "Orsa",
+ "Shimmin Beg",
+ "אור שפירא",
+ "חיים",
+ "ערן",
+ "פוילישער",
+ "קיפודנחש"
+ ]
+ },
+ "ooui-outline-control-move-down": "להזיז את הפריט מטה",
+ "ooui-outline-control-move-up": "להזיז את הפריט מעלה",
+ "ooui-outline-control-remove": "להסיר את הפריט",
+ "ooui-toolbar-more": "עוד",
+ "ooui-toolgroup-expand": "יותר",
+ "ooui-toolgroup-collapse": "פחות",
+ "ooui-dialog-message-accept": "אישור",
+ "ooui-dialog-message-reject": "ביטול",
+ "ooui-dialog-process-error": "משהו השתבש",
+ "ooui-dialog-process-dismiss": "לוותר",
+ "ooui-dialog-process-retry": "לנסות שוב",
+ "ooui-dialog-process-continue": "המשך"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/hi.json b/vendor/oojs/oojs-ui/i18n/hi.json
new file mode 100644
index 00000000..ce86aaab
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/hi.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ansumang",
+ "Devayon",
+ "Rajesh",
+ "Siddhartha Ghai",
+ "Goelujjwal",
+ "Ankita-ks"
+ ]
+ },
+ "ooui-outline-control-move-down": "प्रविष्टि नीचे ले जाएँ",
+ "ooui-outline-control-move-up": "प्रविष्टि ऊपर ले जाएँ",
+ "ooui-outline-control-remove": "आइटम हटाएँ",
+ "ooui-toolbar-more": "अधिक",
+ "ooui-toolgroup-expand": "अधिक",
+ "ooui-toolgroup-collapse": "कम",
+ "ooui-dialog-message-accept": "ठीक है",
+ "ooui-dialog-message-reject": "रद्द करें",
+ "ooui-dialog-process-error": "कुछ गलत हुअा है",
+ "ooui-dialog-process-dismiss": "ख़ारिज करें",
+ "ooui-dialog-process-retry": "पुनः प्रयास करें",
+ "ooui-dialog-process-continue": "जारी रखें"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/hr.json b/vendor/oojs/oojs-ui/i18n/hr.json
new file mode 100644
index 00000000..91188984
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/hr.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "MaGa",
+ "Roberta F.",
+ "SpeedyGonsales",
+ "Zeljko.filipin"
+ ]
+ },
+ "ooui-outline-control-move-down": "Premjesti stavku dolje",
+ "ooui-outline-control-move-up": "Premjesti stavku gore",
+ "ooui-outline-control-remove": "Ukloni",
+ "ooui-toolbar-more": "Više",
+ "ooui-toolgroup-expand": "Više",
+ "ooui-toolgroup-collapse": "Manje",
+ "ooui-dialog-message-accept": "U redu",
+ "ooui-dialog-message-reject": "Odustani",
+ "ooui-dialog-process-error": "Nešto je pošlo po zlu",
+ "ooui-dialog-process-dismiss": "Zatvori",
+ "ooui-dialog-process-retry": "Pokušajte ponovo"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/hsb.json b/vendor/oojs/oojs-ui/i18n/hsb.json
new file mode 100644
index 00000000..00894e4e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/hsb.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "J budissin",
+ "Michawiki"
+ ]
+ },
+ "ooui-outline-control-move-down": "Zapisk dele přesunyć",
+ "ooui-outline-control-move-up": "Zapisk horje přesunyć",
+ "ooui-outline-control-remove": "Zapisk wotstronić",
+ "ooui-toolbar-more": "Wjace",
+ "ooui-toolgroup-expand": "Wjace",
+ "ooui-toolgroup-collapse": "Mjenje",
+ "ooui-dialog-message-accept": "W porjadku",
+ "ooui-dialog-message-reject": "Přetorhnyć",
+ "ooui-dialog-process-error": "Něšto je so nimokuliło",
+ "ooui-dialog-process-dismiss": "Schować",
+ "ooui-dialog-process-retry": "Hišće raz spytać",
+ "ooui-dialog-process-continue": "Dale"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/hu.json b/vendor/oojs/oojs-ui/i18n/hu.json
new file mode 100644
index 00000000..d50e62da
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/hu.json
@@ -0,0 +1,23 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dj",
+ "Einstein2",
+ "Misibacsi",
+ "ViDam",
+ "Tacsipacsi",
+ "Csega"
+ ]
+ },
+ "ooui-outline-control-move-down": "Elem mozgatása lefelé",
+ "ooui-outline-control-move-up": "Elem mozgatása felfelé",
+ "ooui-outline-control-remove": "Elem eltávolítása",
+ "ooui-toolbar-more": "Tovább...",
+ "ooui-toolgroup-expand": "Több",
+ "ooui-toolgroup-collapse": "Kevesebb",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Mégse",
+ "ooui-dialog-process-dismiss": "Elrejt",
+ "ooui-dialog-process-retry": "Próbáld újra",
+ "ooui-dialog-process-continue": "Folytatás"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/hy.json b/vendor/oojs/oojs-ui/i18n/hy.json
new file mode 100644
index 00000000..2aaf4e46
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/hy.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vacio",
+ "Xelgen",
+ "Դավիթ Սարոյան"
+ ]
+ },
+ "ooui-outline-control-move-down": "Իջեցնել կետը",
+ "ooui-outline-control-move-up": "Բարձրացնել կետը",
+ "ooui-outline-control-remove": "Հեռացնել տարրը",
+ "ooui-toolbar-more": "Ավելին",
+ "ooui-toolgroup-expand": "Ավելին",
+ "ooui-toolgroup-collapse": "Պակաս",
+ "ooui-dialog-message-accept": "Լավ",
+ "ooui-dialog-message-reject": "Չեղարկել",
+ "ooui-dialog-process-error": "Ինչ-որ սխալ է տեղի ունեցել",
+ "ooui-dialog-process-dismiss": "Փակել",
+ "ooui-dialog-process-retry": "Կրկին փորձել",
+ "ooui-dialog-process-continue": "Շարունակել"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ia.json b/vendor/oojs/oojs-ui/i18n/ia.json
new file mode 100644
index 00000000..b374b6f6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ia.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "McDutchie"
+ ]
+ },
+ "ooui-outline-control-move-down": "Displaciar elemento in basso",
+ "ooui-outline-control-move-up": "Displaciar elemento in alto",
+ "ooui-outline-control-remove": "Remover elemento",
+ "ooui-toolbar-more": "Plus",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Cancellar",
+ "ooui-dialog-process-error": "Qualcosa ha vadite mal",
+ "ooui-dialog-process-dismiss": "Clauder",
+ "ooui-dialog-process-retry": "Reprobar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/id.json b/vendor/oojs/oojs-ui/i18n/id.json
new file mode 100644
index 00000000..bd65e71a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/id.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Farras",
+ "Ilham151096",
+ "Iwan Novirion",
+ "Iyan",
+ "Kenrick95",
+ "McDutchie",
+ "Rv77ax",
+ "William Surya Permana"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pindahkan butir ke bawah",
+ "ooui-outline-control-move-up": "Pindahkan butir ke atas",
+ "ooui-outline-control-remove": "Hapus butir",
+ "ooui-toolbar-more": "Lainnya",
+ "ooui-toolgroup-expand": "Selengkapnya",
+ "ooui-toolgroup-collapse": "Secukupnya",
+ "ooui-dialog-message-accept": "Oke",
+ "ooui-dialog-message-reject": "Batal",
+ "ooui-dialog-process-error": "Ada yang tidak beres",
+ "ooui-dialog-process-dismiss": "Tutup",
+ "ooui-dialog-process-retry": "Coba lagi",
+ "ooui-dialog-process-continue": "Lanjutkan"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ie.json b/vendor/oojs/oojs-ui/i18n/ie.json
new file mode 100644
index 00000000..241cc331
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ie.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Makuba"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mover element a infra",
+ "ooui-outline-control-move-up": "Mover element a supra",
+ "ooui-toolbar-more": "Plu"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ilo.json b/vendor/oojs/oojs-ui/i18n/ilo.json
new file mode 100644
index 00000000..b37beae1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ilo.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lam-ang"
+ ]
+ },
+ "ooui-outline-control-move-down": "Ipababa ti banag",
+ "ooui-outline-control-move-up": "Ipangato ti banag",
+ "ooui-outline-control-remove": "Ikkaten ti banag",
+ "ooui-toolbar-more": "Adu pay",
+ "ooui-toolgroup-expand": "Adu pay",
+ "ooui-toolgroup-collapse": "Basbassit",
+ "ooui-dialog-message-accept": "Sige",
+ "ooui-dialog-message-reject": "Ukasen",
+ "ooui-dialog-process-error": "Adda madi a napasamak",
+ "ooui-dialog-process-dismiss": "Pugsayen",
+ "ooui-dialog-process-retry": "Padasen manen",
+ "ooui-dialog-process-continue": "Agtuloy"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/is.json b/vendor/oojs/oojs-ui/i18n/is.json
new file mode 100644
index 00000000..3a4e1454
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/is.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Maxí",
+ "Snævar"
+ ]
+ },
+ "ooui-outline-control-move-down": "Færa atriði niður",
+ "ooui-outline-control-move-up": "Færa atriði upp",
+ "ooui-outline-control-remove": "Fjarlægja atriði",
+ "ooui-toolbar-more": "Fleira",
+ "ooui-toolgroup-expand": "Fleira",
+ "ooui-toolgroup-collapse": "Færra",
+ "ooui-dialog-message-accept": "Í lagi",
+ "ooui-dialog-message-reject": "Hætta við",
+ "ooui-dialog-process-error": "Eitthvað mistókst",
+ "ooui-dialog-process-dismiss": "Loka",
+ "ooui-dialog-process-retry": "Reyna aftur",
+ "ooui-dialog-process-continue": "Halda áfram"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/it.json b/vendor/oojs/oojs-ui/i18n/it.json
new file mode 100644
index 00000000..0ff8af8f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/it.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beta16",
+ "Darth Kule",
+ "Doc.mari",
+ "Eleonora negri",
+ "Elitre",
+ "F. Cosoleto",
+ "FRacco",
+ "Gianfranco",
+ "Minerva Titani",
+ "Raoli",
+ "Una giornata uggiosa '94",
+ "Ontsed"
+ ]
+ },
+ "ooui-outline-control-move-down": "Sposta in basso",
+ "ooui-outline-control-move-up": "Sposta in alto",
+ "ooui-outline-control-remove": "Rimuovi elemento",
+ "ooui-toolbar-more": "Altro",
+ "ooui-toolgroup-expand": "Più",
+ "ooui-toolgroup-collapse": "Meno",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Annulla",
+ "ooui-dialog-process-error": "Qualcosa è andato storto",
+ "ooui-dialog-process-dismiss": "Nascondi",
+ "ooui-dialog-process-retry": "Riprova",
+ "ooui-dialog-process-continue": "Continua"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ja.json b/vendor/oojs/oojs-ui/i18n/ja.json
new file mode 100644
index 00000000..ec86124e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ja.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fryed-peach",
+ "Miya",
+ "Penn Station",
+ "Shirayuki",
+ "Takot",
+ "Los688"
+ ]
+ },
+ "ooui-outline-control-move-down": "項目を下に移動させる",
+ "ooui-outline-control-move-up": "項目を上に移動させる",
+ "ooui-outline-control-remove": "項目を除去",
+ "ooui-toolbar-more": "その他",
+ "ooui-toolgroup-expand": "続き",
+ "ooui-toolgroup-collapse": "折り畳む",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "キャンセル",
+ "ooui-dialog-process-error": "エラーが発生しました…",
+ "ooui-dialog-process-dismiss": "閉じる",
+ "ooui-dialog-process-retry": "もう一度お試しください",
+ "ooui-dialog-process-continue": "続行"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/jv.json b/vendor/oojs/oojs-ui/i18n/jv.json
new file mode 100644
index 00000000..8827af38
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/jv.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gleki",
+ "NoiX180",
+ "Pras"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pindhahaken butir mangandhap"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ka.json b/vendor/oojs/oojs-ui/i18n/ka.json
new file mode 100644
index 00000000..60ef661b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ka.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "BRUTE",
+ "David1010",
+ "Gleki",
+ "ITshnik",
+ "MIKHEIL",
+ "NoiX180",
+ "Pras",
+ "Tokoko"
+ ]
+ },
+ "ooui-outline-control-move-down": "ელემენტის ქვემოთ გადატანა",
+ "ooui-outline-control-move-up": "ელემენტის ზემოთ გადატანა",
+ "ooui-outline-control-remove": "წაშლა",
+ "ooui-toolbar-more": "მეტი",
+ "ooui-toolgroup-expand": "მეტი",
+ "ooui-toolgroup-collapse": "რამდენიმე",
+ "ooui-dialog-message-accept": "კარგი",
+ "ooui-dialog-message-reject": "გაუქმება",
+ "ooui-dialog-process-error": "მოხდა რაღაც შეცდომა",
+ "ooui-dialog-process-dismiss": "დამალვა",
+ "ooui-dialog-process-retry": "კიდევ სცადეთ",
+ "ooui-dialog-process-continue": "გაგრძელება"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/kk-cyrl.json b/vendor/oojs/oojs-ui/i18n/kk-cyrl.json
new file mode 100644
index 00000000..1d7317b2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/kk-cyrl.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arystanbek"
+ ]
+ },
+ "ooui-outline-control-move-down": "Элементті төмен жылжыту",
+ "ooui-outline-control-move-up": "Элементті жоғары жылжыту",
+ "ooui-outline-control-remove": "Элементті алып тастау",
+ "ooui-toolbar-more": "толығырақ",
+ "ooui-toolgroup-expand": "Тағы",
+ "ooui-toolgroup-collapse": "Азырақ",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Қажет емес",
+ "ooui-dialog-process-error": "Бірдеңеден қате кетті",
+ "ooui-dialog-process-dismiss": "Тоқтату",
+ "ooui-dialog-process-retry": "Қайта байқап көріңіз",
+ "ooui-dialog-process-continue": "Жалғастыру"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/km.json b/vendor/oojs/oojs-ui/i18n/km.json
new file mode 100644
index 00000000..c0d72c4f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/km.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sovichet"
+ ]
+ },
+ "ooui-outline-control-move-down": "រុញ​ទៅ​ក្រោម",
+ "ooui-outline-control-move-up": "រុញ​ទៅ​លើ",
+ "ooui-outline-control-remove": "ដក​វត្ថុ​ចេញ",
+ "ooui-toolbar-more": "បន្ថែម"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/kn.json b/vendor/oojs/oojs-ui/i18n/kn.json
new file mode 100644
index 00000000..982a3cdf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/kn.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vikassy",
+ "Nayvik",
+ "Omshivaprakash",
+ "Pavanaja"
+ ]
+ },
+ "ooui-outline-control-move-down": "ವಸ್ತುವನ್ನು ಕೆಳಗೆ ಸರಿಸು",
+ "ooui-outline-control-move-up": "ವಸ್ತುವನ್ನು ಮೇಲೆ ಸರಿಸು",
+ "ooui-outline-control-remove": "ವಸ್ತುವನ್ನು ತೆಗೆ",
+ "ooui-toolbar-more": "ಇನ್ನಷ್ಟು",
+ "ooui-toolgroup-expand": "ಇನ್ನಷ್ಟು",
+ "ooui-toolgroup-collapse": "ಕೆಲವೇ ಕೆಲವು",
+ "ooui-dialog-message-accept": "ಸರಿ",
+ "ooui-dialog-message-reject": "ರದ್ದುಮಾಡು",
+ "ooui-dialog-process-error": "ಏನೋ ಎಡವಟ್ಟಾಗಿದೆ....",
+ "ooui-dialog-process-dismiss": "ತೆಗೆದುಹಾಕು",
+ "ooui-dialog-process-retry": "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ",
+ "ooui-dialog-process-continue": "ಮುಂದುವರೆಸು"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ko.json b/vendor/oojs/oojs-ui/i18n/ko.json
new file mode 100644
index 00000000..196dc2c3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ko.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Freebiekr",
+ "Hym411",
+ "Kwj2772",
+ "LFM",
+ "아라",
+ "고기랑",
+ "Ryuch",
+ "Revi",
+ "Infinity"
+ ]
+ },
+ "ooui-outline-control-move-down": "항목을 아래로 옮기기",
+ "ooui-outline-control-move-up": "항목을 위로 옮기기",
+ "ooui-outline-control-remove": "항목 지우기",
+ "ooui-toolbar-more": "더 보기",
+ "ooui-toolgroup-expand": "더 보기",
+ "ooui-dialog-message-accept": "확인",
+ "ooui-dialog-message-reject": "취소",
+ "ooui-dialog-process-error": "무언가가 잘못되었습니다",
+ "ooui-dialog-process-dismiss": "숨기기",
+ "ooui-dialog-process-retry": "다시 시도하세요"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/krc.json b/vendor/oojs/oojs-ui/i18n/krc.json
new file mode 100644
index 00000000..ef92e49f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/krc.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Iltever"
+ ]
+ },
+ "ooui-outline-control-move-down": "Элементни тюбюне кёчюр",
+ "ooui-outline-control-move-up": "Элементни башына кёчюр",
+ "ooui-outline-control-remove": "Пунктну кетер",
+ "ooui-toolbar-more": "Энтда",
+ "ooui-toolgroup-expand": "Энтда",
+ "ooui-toolgroup-collapse": "Артха",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Ызына ал",
+ "ooui-dialog-process-error": "Не эсе да табсыз кетди",
+ "ooui-dialog-process-dismiss": "Джаб",
+ "ooui-dialog-process-retry": "Энтда сынаб кёр",
+ "ooui-dialog-process-continue": "Бардыр"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ksh.json b/vendor/oojs/oojs-ui/i18n/ksh.json
new file mode 100644
index 00000000..c975e825
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ksh.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Purodha"
+ ]
+ },
+ "ooui-outline-control-move-down": "Öm eine Plaz noh onge schiehbe",
+ "ooui-outline-control-move-up": "Öm eine Plaz noh bovve schiehbe",
+ "ooui-outline-control-remove": "Dä Plaz läddesch maache → fott domet!",
+ "ooui-toolbar-more": "Mih",
+ "ooui-toolgroup-expand": "Mih",
+ "ooui-toolgroup-collapse": "Winnijer",
+ "ooui-dialog-message-accept": "Lohß Jonn!",
+ "ooui-dialog-message-reject": "Ophühre",
+ "ooui-dialog-process-error": "Öhnsjädd es scheif jejange",
+ "ooui-dialog-process-dismiss": "Maach fott, ha_sch jelässe",
+ "ooui-dialog-process-retry": "Norr_ens versöhke",
+ "ooui-dialog-process-continue": "Wigger maache"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ku-latn.json b/vendor/oojs/oojs-ui/i18n/ku-latn.json
new file mode 100644
index 00000000..be9a8abd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ku-latn.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "George Animal"
+ ]
+ },
+ "ooui-toolgroup-expand": "Bêhtir",
+ "ooui-toolgroup-collapse": "Kêmtir",
+ "ooui-dialog-message-accept": "Baş e",
+ "ooui-dialog-message-reject": "Betal bike",
+ "ooui-dialog-process-retry": "Dîsa hewl bide",
+ "ooui-dialog-process-continue": "Bidomîne"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/kw.json b/vendor/oojs/oojs-ui/i18n/kw.json
new file mode 100644
index 00000000..a6c6d8ab
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/kw.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "George Animal",
+ "Nrowe",
+ "Purodha"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ky.json b/vendor/oojs/oojs-ui/i18n/ky.json
new file mode 100644
index 00000000..e2b8ab7a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ky.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chorobek",
+ "George Animal",
+ "Nrowe",
+ "Tynchtyk Chorotegin",
+ "Викиней"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/lb.json b/vendor/oojs/oojs-ui/i18n/lb.json
new file mode 100644
index 00000000..119d1be9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/lb.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Autokrator",
+ "Chorobek",
+ "Robby",
+ "Soued031",
+ "Tynchtyk Chorotegin",
+ "UV",
+ "Викиней"
+ ]
+ },
+ "ooui-outline-control-move-down": "Element erof réckelen",
+ "ooui-outline-control-move-up": "Element erop réckelen",
+ "ooui-outline-control-remove": "Element ewechhuelen",
+ "ooui-toolbar-more": "Méi",
+ "ooui-toolgroup-expand": "Méi",
+ "ooui-toolgroup-collapse": "Manner",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Ofbriechen",
+ "ooui-dialog-process-error": "Et ass eppes schif gaang",
+ "ooui-dialog-process-dismiss": "Verwerfen",
+ "ooui-dialog-process-retry": "Nach eng Kéier probéieren",
+ "ooui-dialog-process-continue": "Virufueren"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/lmo.json b/vendor/oojs/oojs-ui/i18n/lmo.json
new file mode 100644
index 00000000..87309db0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/lmo.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ninonino"
+ ]
+ },
+ "ooui-outline-control-move-down": "Spòsta 'n zó",
+ "ooui-outline-control-move-up": "Spòsta 'n sö",
+ "ooui-toolbar-more": "Amò"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/lt.json b/vendor/oojs/oojs-ui/i18n/lt.json
new file mode 100644
index 00000000..ecd06a8a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/lt.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Audriusa",
+ "Eitvys200",
+ "Mantak111"
+ ]
+ },
+ "ooui-outline-control-remove": "Šalinti elementus"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/lv.json b/vendor/oojs/oojs-ui/i18n/lv.json
new file mode 100644
index 00000000..9ff787ac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/lv.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Admresdeserv.",
+ "Audriusa",
+ "Eitvys200",
+ "Papuass",
+ "PeterisP"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pārvietot vienumu uz leju",
+ "ooui-outline-control-move-up": "Pārvietot vienumu uz augšu",
+ "ooui-toolbar-more": "Vairāk",
+ "ooui-toolgroup-expand": "Vairāk",
+ "ooui-toolgroup-collapse": "Mazāk",
+ "ooui-dialog-message-accept": "Labi",
+ "ooui-dialog-message-reject": "Atcelt",
+ "ooui-dialog-process-error": "Kaut kas nogāja greizi",
+ "ooui-dialog-process-retry": "Mēģināt vēlreiz",
+ "ooui-dialog-process-continue": "Turpināt"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/lzh.json b/vendor/oojs/oojs-ui/i18n/lzh.json
new file mode 100644
index 00000000..2b3ad53c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/lzh.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joe young yu"
+ ]
+ },
+ "ooui-dialog-message-accept": "可"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/mg.json b/vendor/oojs/oojs-ui/i18n/mg.json
new file mode 100644
index 00000000..af97d171
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/mg.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jagwar"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/min.json b/vendor/oojs/oojs-ui/i18n/min.json
new file mode 100644
index 00000000..b8790d31
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/min.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Iwan Novirion",
+ "Jagwar"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pindahan ko ka bawah",
+ "ooui-outline-control-move-up": "Pindahan ko ka ateh",
+ "ooui-outline-control-remove": "Hapuih ko",
+ "ooui-toolbar-more": "Lainnyo",
+ "ooui-dialog-message-accept": "Yo",
+ "ooui-dialog-message-reject": "Batal"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/mk.json b/vendor/oojs/oojs-ui/i18n/mk.json
new file mode 100644
index 00000000..7962336c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/mk.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bjankuloski06",
+ "Brest",
+ "Iwan Novirion"
+ ]
+ },
+ "ooui-outline-control-move-down": "Помести надолу",
+ "ooui-outline-control-move-up": "Помести нагоре",
+ "ooui-outline-control-remove": "Отстрани ставка",
+ "ooui-toolbar-more": "Повеќе",
+ "ooui-toolgroup-expand": "Повеќе",
+ "ooui-toolgroup-collapse": "Помалку",
+ "ooui-dialog-message-accept": "ОК",
+ "ooui-dialog-message-reject": "Откажи",
+ "ooui-dialog-process-error": "Нешто не е во ред",
+ "ooui-dialog-process-dismiss": "Тргни",
+ "ooui-dialog-process-retry": "Обиди се пак",
+ "ooui-dialog-process-continue": "Продолжи"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ml.json b/vendor/oojs/oojs-ui/i18n/ml.json
new file mode 100644
index 00000000..0ce0c3fd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ml.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kavya Manohar",
+ "Praveenp",
+ "Santhosh.thottingal",
+ "Vssun"
+ ]
+ },
+ "ooui-outline-control-move-down": "ഇനം താഴേയ്ക്ക് മാറ്റുക",
+ "ooui-outline-control-move-up": "ഇനം മുകളിലേയ്ക്ക് മാറ്റുക",
+ "ooui-toolbar-more": "കൂടുതൽ"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/mr.json b/vendor/oojs/oojs-ui/i18n/mr.json
new file mode 100644
index 00000000..70061907
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/mr.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kaajawa",
+ "Mahitgar",
+ "Praju23",
+ "V.narsikar",
+ "Ydyashad",
+ "संतोष दहिवळ"
+ ]
+ },
+ "ooui-outline-control-move-down": "घटक (आयटम) खाली सरकवा",
+ "ooui-outline-control-move-up": "घटक (आयटम) वर सरकवा",
+ "ooui-toolbar-more": "अधिक"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ms.json b/vendor/oojs/oojs-ui/i18n/ms.json
new file mode 100644
index 00000000..823d4936
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ms.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Aurora",
+ "Pizza1016"
+ ]
+ },
+ "ooui-outline-control-move-down": "Alihkan perkara ke bawah",
+ "ooui-outline-control-move-up": "Alihkan perkara ke atas",
+ "ooui-outline-control-remove": "Buang perkara",
+ "ooui-toolbar-more": "Selebihnya",
+ "ooui-dialog-message-reject": "Batal",
+ "ooui-dialog-process-error": "Ada masalah",
+ "ooui-dialog-process-dismiss": "Singkir",
+ "ooui-dialog-process-retry": "Cuba lagi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nap.json b/vendor/oojs/oojs-ui/i18n/nap.json
new file mode 100644
index 00000000..91660373
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nap.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chelin",
+ "Chrisportelli",
+ "PiRSquared17"
+ ]
+ },
+ "ooui-toolbar-more": "Atro"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nb.json b/vendor/oojs/oojs-ui/i18n/nb.json
new file mode 100644
index 00000000..9e773924
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nb.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Danmichaelo",
+ "Event",
+ "Jeblad",
+ "Laaknor",
+ "Njardarlogar",
+ "Jdforrester",
+ "Apple farmer"
+ ]
+ },
+ "ooui-outline-control-move-down": "Flytt ned",
+ "ooui-outline-control-move-up": "Flytt opp",
+ "ooui-outline-control-remove": "Fjern element",
+ "ooui-toolbar-more": "Mer",
+ "ooui-toolgroup-expand": "Mer",
+ "ooui-toolgroup-collapse": "Færre",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Avbryt",
+ "ooui-dialog-process-error": "Noe gikk galt",
+ "ooui-dialog-process-dismiss": "Lukk",
+ "ooui-dialog-process-retry": "Prøv igjen",
+ "ooui-dialog-process-continue": "Fortsett"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nds-nl.json b/vendor/oojs/oojs-ui/i18n/nds-nl.json
new file mode 100644
index 00000000..d3db318b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nds-nl.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Servien"
+ ]
+ },
+ "ooui-outline-control-move-down": "Onderwarp ummeneer zetten",
+ "ooui-outline-control-move-up": "Onderwarp umhoge zetten",
+ "ooui-outline-control-remove": "Element vortdoon",
+ "ooui-toolbar-more": "Meer",
+ "ooui-toolgroup-expand": "Meer",
+ "ooui-toolgroup-collapse": "Minder",
+ "ooui-dialog-message-accept": "Okee",
+ "ooui-dialog-message-reject": "Aofbreken",
+ "ooui-dialog-process-error": "Der gung iets fout",
+ "ooui-dialog-process-dismiss": "Sluten",
+ "ooui-dialog-process-retry": "Opniej proberen",
+ "ooui-dialog-process-continue": "Deurgaon"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nds.json b/vendor/oojs/oojs-ui/i18n/nds.json
new file mode 100644
index 00000000..1e5b83dd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nds.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Zylbath"
+ ]
+ },
+ "ooui-outline-control-move-down": "Element na ünnen schuven",
+ "ooui-outline-control-move-up": "Element na baven schuven",
+ "ooui-toolbar-more": "Mehr"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ne.json b/vendor/oojs/oojs-ui/i18n/ne.json
new file mode 100644
index 00000000..4ca5ab72
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ne.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "RajeshPandey",
+ "सरोज कुमार ढकाल",
+ "Ganesh Paudel"
+ ]
+ },
+ "ooui-outline-control-move-down": "वस्तुलाई तल सार्ने",
+ "ooui-outline-control-move-up": "वस्तुलाई माथि सार्ने",
+ "ooui-outline-control-remove": "वस्तुलाई हटाउने",
+ "ooui-toolbar-more": "थप",
+ "ooui-toolgroup-expand": "थप",
+ "ooui-toolgroup-collapse": "कम",
+ "ooui-dialog-message-accept": "हुन्छ",
+ "ooui-dialog-message-reject": "रद्द गर्ने",
+ "ooui-dialog-process-dismiss": "खारेज गर्ने",
+ "ooui-dialog-process-retry": "पुन प्रयास गर्नुहोस",
+ "ooui-dialog-process-continue": "जारी राख्ने"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nl.json b/vendor/oojs/oojs-ui/i18n/nl.json
new file mode 100644
index 00000000..7c7b1767
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nl.json
@@ -0,0 +1,35 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bluyten",
+ "Breghtje",
+ "Catrope",
+ "Flightmare",
+ "Hansmuller",
+ "Jdforrester",
+ "Keegan",
+ "Konovalov",
+ "RajeshPandey",
+ "Romaine",
+ "SPQRobin",
+ "Saruman",
+ "Siebrand",
+ "Southparkfan",
+ "सरोज कुमार ढकाल",
+ "Sjoerddebruin",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Item omlaag verplaatsen",
+ "ooui-outline-control-move-up": "Item omhoog verplaatsen",
+ "ooui-outline-control-remove": "Item verwijderen",
+ "ooui-toolbar-more": "Meer",
+ "ooui-toolgroup-expand": "Meer",
+ "ooui-toolgroup-collapse": "Minder",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Annuleren",
+ "ooui-dialog-process-error": "Er is iets misgegaan",
+ "ooui-dialog-process-dismiss": "Sluiten",
+ "ooui-dialog-process-retry": "Opnieuw proberen",
+ "ooui-dialog-process-continue": "Doorgaan"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/nn.json b/vendor/oojs/oojs-ui/i18n/nn.json
new file mode 100644
index 00000000..943e6adc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/nn.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jeblad",
+ "Njardarlogar"
+ ]
+ },
+ "ooui-outline-control-move-down": "Flytt element ned",
+ "ooui-outline-control-move-up": "Flytt element opp",
+ "ooui-toolbar-more": "Fleire"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/oc.json b/vendor/oojs/oojs-ui/i18n/oc.json
new file mode 100644
index 00000000..4d35b6c9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/oc.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Far davalar l’element",
+ "ooui-outline-control-move-up": "Far montar l’element",
+ "ooui-outline-control-remove": "Suprimir l’element",
+ "ooui-toolbar-more": "Mai",
+ "ooui-dialog-message-reject": "Anullar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/om.json b/vendor/oojs/oojs-ui/i18n/om.json
new file mode 100644
index 00000000..ecf95971
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/om.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "Tumsaa"
+ ]
+ },
+ "ooui-outline-control-move-down": "Gad buusi",
+ "ooui-outline-control-move-up": "Ol baasi",
+ "ooui-outline-control-remove": "Balleessi",
+ "ooui-toolbar-more": "Dabalata",
+ "ooui-toolgroup-expand": "Dabalata",
+ "ooui-toolgroup-collapse": "Xiqqaa",
+ "ooui-dialog-message-accept": "Tole",
+ "ooui-dialog-message-reject": "Dhiisi",
+ "ooui-dialog-process-error": "Dogoggorri wayii ummameera",
+ "ooui-dialog-process-dismiss": "Didi",
+ "ooui-dialog-process-retry": "Itti deebi'ii yaali",
+ "ooui-dialog-process-continue": "Itti fufi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/or.json b/vendor/oojs/oojs-ui/i18n/or.json
new file mode 100644
index 00000000..dde49bf1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/or.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Odisha1",
+ "Psubhashish",
+ "ଶିତିକଣ୍ଠ ଦାଶ"
+ ]
+ },
+ "ooui-outline-control-move-down": "ବସ୍ତୁଟିକୁ ତଳକୁ ଘୁଞ୍ଚାନ୍ତୁ",
+ "ooui-outline-control-move-up": "ବସ୍ତୁଟିକୁ ଉପରକୁ ଘୁଞ୍ଚାନ୍ତୁ",
+ "ooui-outline-control-remove": "ବସ୍ତୁଟିକୁ ଲିଭାନ୍ତୁ",
+ "ooui-toolbar-more": "ଅଧିକ",
+ "ooui-toolgroup-expand": "ଅଧିକ",
+ "ooui-toolgroup-collapse": "ଅଳ୍ପ",
+ "ooui-dialog-message-accept": "ହେଉ",
+ "ooui-dialog-message-reject": "ନାକଚ",
+ "ooui-dialog-process-error": "ଅସୁବିଧାଟିଏ ଘଟିଲା",
+ "ooui-dialog-process-dismiss": "ଖାରଜ",
+ "ooui-dialog-process-retry": "ଆଉ ଥରେ ଚେଷ୍ଟା କରନ୍ତୁ",
+ "ooui-dialog-process-continue": "ଚାଲୁରଖିବେ"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pa.json b/vendor/oojs/oojs-ui/i18n/pa.json
new file mode 100644
index 00000000..8c7a1e7c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pa.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amikeco",
+ "Babanwalia",
+ "Bouron",
+ "Nasir8891",
+ "Satdeep gill"
+ ]
+ },
+ "ooui-outline-control-move-down": "ਨੀਚੇ ਲੈਕੇ ਜਾਓ",
+ "ooui-outline-control-move-up": "ਉੱਤੇ ਲੈਕੇ ਜਾਓ",
+ "ooui-toolbar-more": "ਹੋਰ",
+ "ooui-toolgroup-expand": "ਹੋਰ",
+ "ooui-toolgroup-collapse": "ਥੋੜੇ",
+ "ooui-dialog-message-accept": "ਠੀਕ ਹੈ",
+ "ooui-dialog-message-reject": "ਰੱਦ ਕਰੋ",
+ "ooui-dialog-process-error": "ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ",
+ "ooui-dialog-process-dismiss": "ਰੱਦ ਕਰੋ",
+ "ooui-dialog-process-retry": "ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ",
+ "ooui-dialog-process-continue": "ਜਾਰੀ ਰੱਖੋ"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pfl.json b/vendor/oojs/oojs-ui/i18n/pfl.json
new file mode 100644
index 00000000..02d08426
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pfl.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Manuae"
+ ]
+ },
+ "ooui-outline-control-move-down": "Bweeschs nunna",
+ "ooui-outline-control-move-up": "Bweeschs nuff",
+ "ooui-outline-control-remove": "Leschs",
+ "ooui-toolbar-more": "Mea",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Abbresche"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pl.json b/vendor/oojs/oojs-ui/i18n/pl.json
new file mode 100644
index 00000000..fbd0c8cc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pl.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Babanwalia",
+ "Chrumps",
+ "Matma Rex",
+ "Mikołka",
+ "Nasir8891",
+ "Odie2",
+ "Rzuwig",
+ "Tar Lócesilion",
+ "Ty221",
+ "WTM",
+ "Woytecr",
+ "Wpedzich",
+ "Jacenty359",
+ "Matik7",
+ "Gloria sah",
+ "Andrzej aa"
+ ]
+ },
+ "ooui-outline-control-move-down": "Przenieś niżej",
+ "ooui-outline-control-move-up": "Przenieś wyżej",
+ "ooui-outline-control-remove": "Usuń element",
+ "ooui-toolbar-more": "Więcej",
+ "ooui-toolgroup-expand": "Więcej",
+ "ooui-toolgroup-collapse": "Mniej",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Anuluj",
+ "ooui-dialog-process-error": "Coś poszło nie tak",
+ "ooui-dialog-process-dismiss": "Ukryj",
+ "ooui-dialog-process-retry": "Spróbuj ponownie",
+ "ooui-dialog-process-continue": "Kontynuuj"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pms.json b/vendor/oojs/oojs-ui/i18n/pms.json
new file mode 100644
index 00000000..b8fd3a58
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pms.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Borichèt",
+ "Dragonòt",
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "ooui-outline-control-move-down": "Fé calé giù l'element",
+ "ooui-outline-control-move-up": "Fé monté l'element",
+ "ooui-toolbar-more": "Ëd pi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ps.json b/vendor/oojs/oojs-ui/i18n/ps.json
new file mode 100644
index 00000000..ebffe539
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ps.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahmed-Najib-Biabani-Ibrahimkhel"
+ ]
+ },
+ "ooui-outline-control-move-down": "توکی ښکته راوړل",
+ "ooui-outline-control-move-up": "توکی پورته راوړل",
+ "ooui-outline-control-remove": "توکی غورځول",
+ "ooui-toolbar-more": "نور",
+ "ooui-toolgroup-expand": "نور",
+ "ooui-toolgroup-collapse": "لږ تر لږ",
+ "ooui-dialog-message-accept": "ښه",
+ "ooui-dialog-message-reject": "ناگارل",
+ "ooui-dialog-process-error": "يوه ستونزه رامنځ ته شوه",
+ "ooui-dialog-process-dismiss": "تړل",
+ "ooui-dialog-process-retry": "بيا هڅه"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pt-br.json b/vendor/oojs/oojs-ui/i18n/pt-br.json
new file mode 100644
index 00000000..94ea0895
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pt-br.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cainamarques",
+ "Dianakc",
+ "Fúlvio",
+ "Helder.wiki",
+ "HenriqueCrang",
+ "Jaideraf",
+ "Luckas",
+ "OTAVIO1981",
+ 555
+ ]
+ },
+ "ooui-outline-control-move-down": "Mover item para baixo",
+ "ooui-outline-control-move-up": "Mover item para cima",
+ "ooui-toolbar-more": "Mais"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/pt.json b/vendor/oojs/oojs-ui/i18n/pt.json
new file mode 100644
index 00000000..7b3176fb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/pt.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cainamarques",
+ "Fúlvio",
+ "GoEThe",
+ "Hamilton Abreu",
+ "Helder.wiki",
+ "Jaideraf",
+ "Jdforrester",
+ "Luckas",
+ "Vitorvicentevalente",
+ "SandroHc"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mover item para baixo",
+ "ooui-outline-control-move-up": "Mover item para cima",
+ "ooui-outline-control-remove": "Remover elemento",
+ "ooui-toolbar-more": "Mais",
+ "ooui-toolgroup-expand": "Mais",
+ "ooui-toolgroup-collapse": "Menos",
+ "ooui-dialog-message-accept": "Aceitar",
+ "ooui-dialog-message-reject": "Cancelar",
+ "ooui-dialog-process-error": "Algo correu mal",
+ "ooui-dialog-process-dismiss": "Ignorar",
+ "ooui-dialog-process-retry": "Tentar novamente",
+ "ooui-dialog-process-continue": "Continuar"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/qqq.json b/vendor/oojs/oojs-ui/i18n/qqq.json
new file mode 100644
index 00000000..c1b794ab
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/qqq.json
@@ -0,0 +1,35 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Beta16",
+ "Erik Moeller",
+ "Jdforrester",
+ "Lloffiwr",
+ "Mooeypoo",
+ "Mormegil",
+ "Nike",
+ "PoLuX124",
+ "Purodha",
+ "Raymond",
+ "Sagan",
+ "Sayak Sarkar",
+ "Shirayuki",
+ "Siebrand",
+ "Trevor Parscal",
+ "Liuxinyu970226"
+ ]
+ },
+ "ooui-outline-control-move-down": "Tool tip for a button that moves items in a list down one place",
+ "ooui-outline-control-move-up": "Tool tip for a button that moves items in a list up one place",
+ "ooui-outline-control-remove": "Tool tip for a button that removes items from a list.\n{{Identical|Remove item}}",
+ "ooui-toolbar-more": "Label for the toolbar group that contains a list of all other available tools.\n{{Identical|More}}",
+ "ooui-toolgroup-expand": "Label for the fake tool that expands the full list of tools in a toolbar group.\n\nSee also:\n* {{msg-mw|Ooui-toolgroup-collapse}}\n{{Identical|More}}",
+ "ooui-toolgroup-collapse": "Label for the fake tool that collapses the full list of tools in a toolbar group.\n\nSee also:\n* {{msg-mw|Ooui-toolgroup-expand}}\n{{Identical|Fewer}}",
+ "ooui-dialog-message-accept": "Default label for the accept button of a message dialog\n{{Identical|OK}}",
+ "ooui-dialog-message-reject": "Default label for the reject button of a message dialog\n{{Identical|Cancel}}",
+ "ooui-dialog-process-error": "Title for process dialog error description",
+ "ooui-dialog-process-dismiss": "Label for process dialog dismiss error button, visible when describing errors\n{{Identical|Dismiss}}",
+ "ooui-dialog-process-retry": "Label for process dialog retry action button, visible when describing recoverable errors\n{{Identical|Try again}}",
+ "ooui-dialog-process-continue": "Label for process dialog retry action button, visible when describing only warnings\n{{Identical|Continue}}"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/qu.json b/vendor/oojs/oojs-ui/i18n/qu.json
new file mode 100644
index 00000000..cb0b2c33
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/qu.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "AlimanRuna",
+ "Jduranboger"
+ ]
+ },
+ "ooui-outline-control-move-down": "Qallawata uraykuchiy",
+ "ooui-outline-control-move-up": "Qallawata huqariy",
+ "ooui-outline-control-remove": "P'anqa sutikunata qichuy",
+ "ooui-toolbar-more": "Aswan"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ro.json b/vendor/oojs/oojs-ui/i18n/ro.json
new file mode 100644
index 00000000..258f3e74
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ro.json
@@ -0,0 +1,23 @@
+{
+ "@metadata": {
+ "authors": [
+ "AlimanRuna",
+ "Firilacroco",
+ "Minisarm",
+ "Stelistcristi",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Mută elementul mai jos",
+ "ooui-outline-control-move-up": "Mută elementul mai sus",
+ "ooui-outline-control-remove": "Elimină elementul",
+ "ooui-toolbar-more": "Mai mult",
+ "ooui-toolgroup-expand": "Mai multe",
+ "ooui-toolgroup-collapse": "Mai puține",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Revocare",
+ "ooui-dialog-process-error": "Ceva nu a funcționat",
+ "ooui-dialog-process-dismiss": "Renunțare",
+ "ooui-dialog-process-retry": "Reîncearcă",
+ "ooui-dialog-process-continue": "Continuă"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/roa-tara.json b/vendor/oojs/oojs-ui/i18n/roa-tara.json
new file mode 100644
index 00000000..f6f422a2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/roa-tara.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joetaras"
+ ]
+ },
+ "ooui-outline-control-move-down": "Spuèste 'a vôsce sotte",
+ "ooui-outline-control-move-up": "Spuèste 'a vôsce sus",
+ "ooui-outline-control-remove": "Live 'a vôsce",
+ "ooui-toolbar-more": "De cchiù",
+ "ooui-toolgroup-expand": "De cchiù",
+ "ooui-toolgroup-collapse": "De mene",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Annulle",
+ "ooui-dialog-process-error": "Quacche cose ha sciute stuèrte",
+ "ooui-dialog-process-dismiss": "Scitte",
+ "ooui-dialog-process-retry": "Pruève arrete",
+ "ooui-dialog-process-continue": "Condinue"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ru.json b/vendor/oojs/oojs-ui/i18n/ru.json
new file mode 100644
index 00000000..129dd6a2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ru.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "DR",
+ "Eugrus",
+ "Iluvatar",
+ "KPu3uC B Poccuu",
+ "Kalan",
+ "MaxBioHazard",
+ "NBS",
+ "Niklem",
+ "Okras",
+ "Ole Yves",
+ "Putnik",
+ "Sunpriat",
+ "Yury Katkov",
+ "Умар",
+ "Камалист"
+ ]
+ },
+ "ooui-outline-control-move-down": "Переместить элемент вниз",
+ "ooui-outline-control-move-up": "Переместить элемент вверх",
+ "ooui-outline-control-remove": "Удалить пункт",
+ "ooui-toolbar-more": "Ещё",
+ "ooui-toolgroup-expand": "Больше",
+ "ooui-toolgroup-collapse": "Меньше",
+ "ooui-dialog-message-accept": "ОК",
+ "ooui-dialog-message-reject": "Отмена",
+ "ooui-dialog-process-error": "Что-то пошло не так",
+ "ooui-dialog-process-dismiss": "Закрыть",
+ "ooui-dialog-process-retry": "Попробовать ещё раз",
+ "ooui-dialog-process-continue": "Продолжить"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sah.json b/vendor/oojs/oojs-ui/i18n/sah.json
new file mode 100644
index 00000000..85a94cd5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sah.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gazeb",
+ "HalanTul"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/scn.json b/vendor/oojs/oojs-ui/i18n/scn.json
new file mode 100644
index 00000000..22a212f9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/scn.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gazeb",
+ "Gmelfi",
+ "HalanTul",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Sposta di sutta",
+ "ooui-outline-control-move-up": "Sposta di supra",
+ "ooui-toolbar-more": "Àutri cosi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sco.json b/vendor/oojs/oojs-ui/i18n/sco.json
new file mode 100644
index 00000000..0a26a5c8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sco.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "John Reid"
+ ]
+ },
+ "ooui-outline-control-move-down": "Muiv eetem doon",
+ "ooui-outline-control-move-up": "Muiv eetem up",
+ "ooui-outline-control-remove": "Remuiv eetem",
+ "ooui-toolbar-more": "Mair"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sh.json b/vendor/oojs/oojs-ui/i18n/sh.json
new file mode 100644
index 00000000..b40fa04e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sh.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "OC Ripper"
+ ]
+ },
+ "ooui-outline-control-move-down": "Pomakni stavku dolje",
+ "ooui-outline-control-move-up": "Pomakni stavku gore"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/si.json b/vendor/oojs/oojs-ui/i18n/si.json
new file mode 100644
index 00000000..5988773b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/si.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Singhalawap",
+ "පසිඳු කාවින්ද",
+ "ශ්වෙත"
+ ]
+ },
+ "ooui-outline-control-move-down": "අයිතමය පහලටදමන්න",
+ "ooui-outline-control-move-up": "අයිතමය ඉහලටදමන්න"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sk.json b/vendor/oojs/oojs-ui/i18n/sk.json
new file mode 100644
index 00000000..c8246da9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sk.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mimarik",
+ "Teslaton"
+ ]
+ },
+ "ooui-outline-control-move-down": "Posunúť položku nadol",
+ "ooui-outline-control-move-up": "Posunúť položku nahor",
+ "ooui-outline-control-remove": "Odstrániť položku",
+ "ooui-toolbar-more": "Viac"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sl.json b/vendor/oojs/oojs-ui/i18n/sl.json
new file mode 100644
index 00000000..a40728ad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sl.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dbc334",
+ "Eleassar",
+ "Pinky sl",
+ "Yerpo"
+ ]
+ },
+ "ooui-outline-control-move-down": "Prestavi predmet nižje",
+ "ooui-outline-control-move-up": "Prestavi predmet višje",
+ "ooui-outline-control-remove": "Odstrani vnos",
+ "ooui-toolbar-more": "Več",
+ "ooui-toolgroup-expand": "Več",
+ "ooui-toolgroup-collapse": "Manj",
+ "ooui-dialog-message-accept": "V redu",
+ "ooui-dialog-message-reject": "Prekliči",
+ "ooui-dialog-process-error": "Nekaj je šlo narobe",
+ "ooui-dialog-process-dismiss": "Skrij",
+ "ooui-dialog-process-retry": "Poskusi znova",
+ "ooui-dialog-process-continue": "Nadaljuj"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sq.json b/vendor/oojs/oojs-ui/i18n/sq.json
new file mode 100644
index 00000000..ec180199
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sq.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Euriditi",
+ "Kushtrim",
+ "Elioqoshi",
+ "GretaDoci",
+ "Gertakapllani"
+ ]
+ },
+ "ooui-outline-control-move-down": "Zhvendose artikullin më poshtë",
+ "ooui-outline-control-move-up": "Zhvendose artikullin më lart",
+ "ooui-outline-control-remove": "Hiq artikullin",
+ "ooui-toolbar-more": "Më tepër...",
+ "ooui-dialog-message-accept": "Në rregull",
+ "ooui-dialog-message-reject": "Anullo",
+ "ooui-dialog-process-error": "Diçka shkoi keq",
+ "ooui-dialog-process-retry": "Provo përsëri"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sr-ec.json b/vendor/oojs/oojs-ui/i18n/sr-ec.json
new file mode 100644
index 00000000..c827554e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sr-ec.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01",
+ "Nikola Smolenski",
+ "Милан Јелисавчић"
+ ]
+ },
+ "ooui-outline-control-move-down": "Премести ставку на доле",
+ "ooui-outline-control-move-up": "Премести ставку на горе",
+ "ooui-outline-control-remove": "Уклони ставку",
+ "ooui-toolbar-more": "Више",
+ "ooui-toolgroup-expand": "Више",
+ "ooui-toolgroup-collapse": "Мање",
+ "ooui-dialog-message-accept": "У реду",
+ "ooui-dialog-message-reject": "Откажи",
+ "ooui-dialog-process-error": "Нешто је пошло наопако",
+ "ooui-dialog-process-dismiss": "Одбаци",
+ "ooui-dialog-process-retry": "Покушај поново",
+ "ooui-dialog-process-continue": "Настави"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sr-el.json b/vendor/oojs/oojs-ui/i18n/sr-el.json
new file mode 100644
index 00000000..704a1860
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sr-el.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01"
+ ]
+ },
+ "ooui-outline-control-move-down": "Premesti stavku na dole",
+ "ooui-outline-control-move-up": "Premesti stavku na gore",
+ "ooui-outline-control-remove": "Ukloni stavku",
+ "ooui-toolbar-more": "Više",
+ "ooui-toolgroup-expand": "Više",
+ "ooui-toolgroup-collapse": "Manje",
+ "ooui-dialog-message-accept": "U redu",
+ "ooui-dialog-message-reject": "Otkaži",
+ "ooui-dialog-process-error": "Nešto je pošlo naopako",
+ "ooui-dialog-process-dismiss": "Odbaci",
+ "ooui-dialog-process-retry": "Pokušaj ponovo",
+ "ooui-dialog-process-continue": "Nastavi"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sv.json b/vendor/oojs/oojs-ui/i18n/sv.json
new file mode 100644
index 00000000..d499427c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sv.json
@@ -0,0 +1,29 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ainali",
+ "Haxpett",
+ "Jopparn",
+ "Knuckles",
+ "Magol",
+ "Milicevic01",
+ "Per",
+ "Sendelbach",
+ "Skalman",
+ "WikiPhoenix",
+ "Lokal Profil"
+ ]
+ },
+ "ooui-outline-control-move-down": "Flytta ned objekt",
+ "ooui-outline-control-move-up": "Flytta upp objekt",
+ "ooui-outline-control-remove": "Ta bort objekt",
+ "ooui-toolbar-more": "Mer",
+ "ooui-toolgroup-expand": "Fler",
+ "ooui-toolgroup-collapse": "Färre",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Avbryt",
+ "ooui-dialog-process-error": "Något gick fel",
+ "ooui-dialog-process-dismiss": "Stäng",
+ "ooui-dialog-process-retry": "Försök igen",
+ "ooui-dialog-process-continue": "Fortsätt"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/sw.json b/vendor/oojs/oojs-ui/i18n/sw.json
new file mode 100644
index 00000000..510e468d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/sw.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lloffiwr",
+ "Muddyb Blast Producer"
+ ]
+ },
+ "ooui-outline-control-move-down": "Sogeza kipengee chini",
+ "ooui-outline-control-move-up": "Sogeza kipengee juu",
+ "ooui-outline-control-remove": "Toa kitu",
+ "ooui-toolbar-more": "Zaidi",
+ "ooui-dialog-message-accept": "Sawa",
+ "ooui-dialog-message-reject": "Batilisha",
+ "ooui-dialog-process-retry": "Jaribu tena"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ta.json b/vendor/oojs/oojs-ui/i18n/ta.json
new file mode 100644
index 00000000..122d4a27
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ta.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jayarathina",
+ "Sank",
+ "Shanmugamp7",
+ "மதனாஹரன்",
+ "ElangoRamanujam"
+ ]
+ },
+ "ooui-toolgroup-expand": "மேலும்",
+ "ooui-dialog-process-continue": "தொடரவும்"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/te.json b/vendor/oojs/oojs-ui/i18n/te.json
new file mode 100644
index 00000000..d4868706
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/te.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arjunaraoc",
+ "Jayarathina",
+ "Sank",
+ "Shanmugamp7",
+ "Veeven",
+ "Visdaviva",
+ "மதனாஹரன்"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/tg-cyrl.json b/vendor/oojs/oojs-ui/i18n/tg-cyrl.json
new file mode 100644
index 00000000..1429bedd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/tg-cyrl.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ibrahim"
+ ]
+ },
+ "ooui-outline-control-move-down": "Ҳаракати мавод ба поён",
+ "ooui-outline-control-move-up": "Ҳаракати мавод ба боло",
+ "ooui-outline-control-remove": "Ҳазви мавод",
+ "ooui-toolbar-more": "Бештар"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/th.json b/vendor/oojs/oojs-ui/i18n/th.json
new file mode 100644
index 00000000..94527935
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/th.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Supasate",
+ "Taweetham"
+ ]
+ },
+ "ooui-outline-control-move-down": "เลื่อนรายการลง",
+ "ooui-outline-control-move-up": "ย้ายรายการขึ้น"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/tl.json b/vendor/oojs/oojs-ui/i18n/tl.json
new file mode 100644
index 00000000..b3a9f246
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/tl.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "AnakngAraw",
+ "Sky Harbor"
+ ]
+ },
+ "ooui-outline-control-move-down": "Ilipat ang aytem pababa",
+ "ooui-outline-control-move-up": "Ilipat ang aytem pataas",
+ "ooui-outline-control-remove": "Tanggalin ang aytem",
+ "ooui-toolbar-more": "Marami pa",
+ "ooui-dialog-message-accept": "Sige",
+ "ooui-dialog-message-reject": "Huwag ituloy"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/tr.json b/vendor/oojs/oojs-ui/i18n/tr.json
new file mode 100644
index 00000000..7b4d4921
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/tr.json
@@ -0,0 +1,27 @@
+{
+ "@metadata": {
+ "authors": [
+ "Emperyan",
+ "Incelemeelemani",
+ "LuCKY",
+ "Maidis",
+ "Rapsar",
+ "Talha Samil Cakir",
+ "TurkishStyles",
+ "Sayginer",
+ "Meelo"
+ ]
+ },
+ "ooui-outline-control-move-down": "Ögeyi aşağı taşı",
+ "ooui-outline-control-move-up": "Ögeyi yukarı taşı",
+ "ooui-outline-control-remove": "Ögeyi kaldır",
+ "ooui-toolbar-more": "Dahası",
+ "ooui-toolgroup-expand": "Dahası",
+ "ooui-toolgroup-collapse": "Daha az",
+ "ooui-dialog-message-accept": "Tamam",
+ "ooui-dialog-message-reject": "İptal",
+ "ooui-dialog-process-error": "Bir şeyler yanlış gitti",
+ "ooui-dialog-process-dismiss": "Kapat",
+ "ooui-dialog-process-retry": "Tekrar dene",
+ "ooui-dialog-process-continue": "Devam et"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/tt-cyrl.json b/vendor/oojs/oojs-ui/i18n/tt-cyrl.json
new file mode 100644
index 00000000..408a1b4a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/tt-cyrl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ajdar"
+ ]
+ },
+ "ooui-outline-control-move-down": "Элементны аска күчерү",
+ "ooui-outline-control-move-up": "Элементны өскә күчерү"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/ug-arab.json b/vendor/oojs/oojs-ui/i18n/ug-arab.json
new file mode 100644
index 00000000..19725cc2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/ug-arab.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sahran",
+ "Tel'et",
+ "Tifinaghes"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/i18n/uk.json b/vendor/oojs/oojs-ui/i18n/uk.json
new file mode 100644
index 00000000..0197a4ce
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/uk.json
@@ -0,0 +1,33 @@
+{
+ "@metadata": {
+ "authors": [
+ "AS",
+ "Aced",
+ "Ahonc",
+ "Andriykopanytsia",
+ "Base",
+ "Perohanych",
+ "RLuts",
+ "Sahran",
+ "Sergento",
+ "Steve.rusyn",
+ "SteveR",
+ "Tel'et",
+ "Tifinaghes",
+ "Ата",
+ "Piramidion"
+ ]
+ },
+ "ooui-outline-control-move-down": "Перемістити елемент униз",
+ "ooui-outline-control-move-up": "Перемістити елемент вгору",
+ "ooui-outline-control-remove": "Видалити елемент",
+ "ooui-toolbar-more": "Більше",
+ "ooui-toolgroup-expand": "Більше",
+ "ooui-toolgroup-collapse": "Менше",
+ "ooui-dialog-message-accept": "Готово",
+ "ooui-dialog-message-reject": "Скасувати",
+ "ooui-dialog-process-error": "Щось пішло не так",
+ "ooui-dialog-process-dismiss": "Приховати",
+ "ooui-dialog-process-retry": "Спробуйте ще раз",
+ "ooui-dialog-process-continue": "Продовжити"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/uz.json b/vendor/oojs/oojs-ui/i18n/uz.json
new file mode 100644
index 00000000..7c6263e4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/uz.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "CoderSI",
+ "Noor2020",
+ "Sociologist",
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "ooui-outline-control-move-down": "Elementni pastga koʻchirish",
+ "ooui-outline-control-move-up": "Elementni yuqoriga koʻchirish",
+ "ooui-toolbar-more": "Yana"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/vec.json b/vendor/oojs/oojs-ui/i18n/vec.json
new file mode 100644
index 00000000..4de584bf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/vec.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Candalua",
+ "GatoSelvadego",
+ "Gloria sah"
+ ]
+ },
+ "ooui-outline-control-move-down": "Sposta in baso",
+ "ooui-outline-control-move-up": "Sposta in sima",
+ "ooui-toolbar-more": "Altro",
+ "ooui-dialog-message-accept": "Va ben"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/vi.json b/vendor/oojs/oojs-ui/i18n/vi.json
new file mode 100644
index 00000000..d5c1e364
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/vi.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cheers!",
+ "Jdforrester",
+ "Minh Nguyen",
+ "Max20091"
+ ]
+ },
+ "ooui-outline-control-move-down": "Chuyển mục xuống",
+ "ooui-outline-control-move-up": "Chuyển mục lên",
+ "ooui-outline-control-remove": "Xóa khoản",
+ "ooui-toolbar-more": "Thêm",
+ "ooui-toolgroup-expand": "Mở rộng",
+ "ooui-toolgroup-collapse": "Rút gọn",
+ "ooui-dialog-message-accept": "OK",
+ "ooui-dialog-message-reject": "Hủy bỏ",
+ "ooui-dialog-process-error": "Đã bị trục trặc",
+ "ooui-dialog-process-dismiss": "Bỏ qua",
+ "ooui-dialog-process-retry": "Thử lại",
+ "ooui-dialog-process-continue": "Tiếp tục"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/vo.json b/vendor/oojs/oojs-ui/i18n/vo.json
new file mode 100644
index 00000000..3510ca93
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/vo.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya"
+ ]
+ },
+ "ooui-toolbar-more": "Pluikos"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/wuu.json b/vendor/oojs/oojs-ui/i18n/wuu.json
new file mode 100644
index 00000000..61e3d01e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/wuu.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya",
+ "十弌"
+ ]
+ },
+ "ooui-toolbar-more": "還多"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/yi.json b/vendor/oojs/oojs-ui/i18n/yi.json
new file mode 100644
index 00000000..a850fce2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/yi.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya",
+ "פוילישער",
+ "十弌"
+ ]
+ },
+ "ooui-outline-control-move-down": "רוקן עלעמענט אראפ",
+ "ooui-outline-control-move-up": "רוקן עלעמענט ארויף",
+ "ooui-outline-control-remove": "אַראָפנעמען איינס",
+ "ooui-toolbar-more": "נאך",
+ "ooui-dialog-message-accept": "יאָ",
+ "ooui-dialog-message-reject": "אַנולירן",
+ "ooui-dialog-process-error": "עפעס איז דורכגעפאלן",
+ "ooui-dialog-process-dismiss": "צומאַכן",
+ "ooui-dialog-process-retry": "פרובירט נאכאמאל"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/yo.json b/vendor/oojs/oojs-ui/i18n/yo.json
new file mode 100644
index 00000000..d979fc13
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/yo.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Demmy"
+ ]
+ },
+ "ooui-outline-control-move-down": "Sún onítòún sí sàlẹ̀",
+ "ooui-outline-control-move-up": "Sún onítòún s'ókè",
+ "ooui-toolbar-more": "Míràn"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/yue.json b/vendor/oojs/oojs-ui/i18n/yue.json
new file mode 100644
index 00000000..81ad9a95
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/yue.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Deryck Chan"
+ ]
+ },
+ "ooui-outline-control-move-down": "向下搬",
+ "ooui-outline-control-move-up": "向上搬",
+ "ooui-outline-control-remove": "拎走",
+ "ooui-toolbar-more": "仲有...",
+ "ooui-dialog-message-accept": "好",
+ "ooui-dialog-message-reject": "取消",
+ "ooui-dialog-process-error": "唔對路",
+ "ooui-dialog-process-dismiss": "閂咗佢",
+ "ooui-dialog-process-retry": "再試過"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/zh-hans.json b/vendor/oojs/oojs-ui/i18n/zh-hans.json
new file mode 100644
index 00000000..ed2f61e4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/zh-hans.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Bencmq",
+ "Demmy",
+ "Hydra",
+ "Hzy980512",
+ "Liangent",
+ "Liuxinyu970226",
+ "Qiyue2001",
+ "Shirayuki",
+ "Shizhao",
+ "TianyinLee",
+ "Xiaomingyan",
+ "Yfdyh000",
+ "Zhangjintao",
+ "乌拉跨氪",
+ "Great Brightstar"
+ ]
+ },
+ "ooui-outline-control-move-down": "项目下移",
+ "ooui-outline-control-move-up": "项目上移",
+ "ooui-outline-control-remove": "移除项目",
+ "ooui-toolbar-more": "更多",
+ "ooui-toolgroup-expand": "更多",
+ "ooui-toolgroup-collapse": "更少",
+ "ooui-dialog-message-accept": "确定",
+ "ooui-dialog-message-reject": "取消",
+ "ooui-dialog-process-error": "发生一些错误",
+ "ooui-dialog-process-dismiss": "解除",
+ "ooui-dialog-process-retry": "重试",
+ "ooui-dialog-process-continue": "继续"
+}
diff --git a/vendor/oojs/oojs-ui/i18n/zh-hant.json b/vendor/oojs/oojs-ui/i18n/zh-hant.json
new file mode 100644
index 00000000..3fd8d361
--- /dev/null
+++ b/vendor/oojs/oojs-ui/i18n/zh-hant.json
@@ -0,0 +1,32 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Ch.Andrew",
+ "Hydra",
+ "Justincheng12345",
+ "Liflon",
+ "Liuxinyu970226",
+ "Qiyue2001",
+ "Radish10cm",
+ "Shirayuki",
+ "Simon Shek",
+ "Spring Roll Conan",
+ "Waihorace",
+ "Cwlin0416",
+ "LNDDYL"
+ ]
+ },
+ "ooui-outline-control-move-down": "項目下移",
+ "ooui-outline-control-move-up": "項目上移",
+ "ooui-outline-control-remove": "移除項目",
+ "ooui-toolbar-more": "更多",
+ "ooui-toolgroup-expand": "更多",
+ "ooui-toolgroup-collapse": "更少",
+ "ooui-dialog-message-accept": "確定",
+ "ooui-dialog-message-reject": "取消",
+ "ooui-dialog-process-error": "發生不明錯誤",
+ "ooui-dialog-process-dismiss": "關閉",
+ "ooui-dialog-process-retry": "再試一次",
+ "ooui-dialog-process-continue": "繼續"
+}
diff --git a/vendor/oojs/oojs-ui/jsduck.categories.json b/vendor/oojs/oojs-ui/jsduck.categories.json
new file mode 100644
index 00000000..c079f073
--- /dev/null
+++ b/vendor/oojs/oojs-ui/jsduck.categories.json
@@ -0,0 +1,80 @@
+[
+ {
+ "name": "OOjs UI",
+ "groups": [
+ {
+ "name": "General",
+ "classes": [
+ "OO.ui",
+ "OO.ui.Element",
+ "OO.ui.HtmlSnippet",
+ "OO.ui.Toolbar",
+ "OO.ui.Window",
+ "OO.ui.Dialog",
+ "OO.ui.WindowManager",
+ "OO.ui.Process",
+ "OO.ui.Error",
+ "OO.ui.ActionSet"
+ ]
+ },
+ {
+ "name": "Factories",
+ "classes": ["OO.ui.*Factory"]
+ },
+ {
+ "name": "Tools",
+ "classes": ["OO.ui.*Tool"]
+ },
+ {
+ "name": "Elements",
+ "classes": ["OO.ui.*Element"]
+ },
+ {
+ "name": "Layouts",
+ "classes": ["OO.ui.*Layout"]
+ },
+ {
+ "name": "Tool groups",
+ "classes": ["OO.ui.*ToolGroup"]
+ },
+ {
+ "name": "Widgets",
+ "classes": ["OO.ui.*Widget"]
+ },
+ {
+ "name": "Dialogs",
+ "classes": ["OO.ui.*Dialog"]
+ },
+ {
+ "name": "Themes",
+ "classes": ["OO.ui.*Theme"]
+ }
+ ]
+ },
+ {
+ "name": "Upstream",
+ "groups": [
+ {
+ "name": "OOJS",
+ "classes": ["OO", "OO.EventEmitter", "OO.Factory", "OO.Registry"]
+ },
+ {
+ "name": "jQuery",
+ "classes": ["jQuery", "jQuery.Event", "jQuery.Promise", "jQuery.Deferred", "jQuery.jqXHR"]
+ },
+ {
+ "name": "JavaScript",
+ "classes": [
+ "Array",
+ "Boolean",
+ "Date",
+ "Function",
+ "Number",
+ "Object",
+ "RegExp",
+ "String"
+ ]
+ }
+ ]
+ }
+]
diff --git a/vendor/oojs/oojs-ui/jsduck.eg-iframe.html b/vendor/oojs/oojs-ui/jsduck.eg-iframe.html
new file mode 100644
index 00000000..bd9c9dde
--- /dev/null
+++ b/vendor/oojs/oojs-ui/jsduck.eg-iframe.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+ <meta charset="UTF-8">
+ <title>OOjs UI example</title>
+ <!-- Prevent scaling on mobile devices which cause problems with dialog sizing -->
+ <meta name="viewport" content="width=device-width, user-scalable=no">
+ <style>
+ body {
+ font-size: 0.8em;
+ font-family: sans-serif;
+ }
+ </style>
+ <link rel="stylesheet" href="dist/oojs-ui-apex.css">
+</head>
+<body>
+ <script src="node_modules/jquery/dist/jquery.js"></script>
+ <script src="node_modules/oojs/dist/oojs.jquery.js"></script>
+ <script src="dist/oojs-ui.js"></script>
+ <script src="dist/oojs-ui-apex.js"></script>
+ <script>
+ function loadInlineExample( code, options, callback ) {
+ try {
+ eval( code );
+ callback && callback( true );
+ } catch (e) {
+ document.body.appendChild( document.createTextNode( e ) );
+ callback && callback( false, e );
+ }
+ }
+ </script>
+</body>
+</html>
diff --git a/vendor/oojs/oojs-ui/jsduck.external.js b/vendor/oojs/oojs-ui/jsduck.external.js
new file mode 100644
index 00000000..655c043f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/jsduck.external.js
@@ -0,0 +1,26 @@
+/**
+ * @class jQuery
+ * <http://api.jquery.com/>
+ */
+
+/**
+ * @class jQuery.Event
+ * <http://api.jquery.com/Types/#Event>
+ */
+
+/**
+ * @class jQuery.Promise
+ * <http://api.jquery.com/Types/#Promise>
+ */
+
+/**
+ * @class jQuery.Deferred
+ * @mixins jQuery.Promise
+ * <http://api.jquery.com/jQuery.Deferred/>
+ */
+
+/**
+ * @class jQuery.jqXHR
+ * @alternateClassName jqXHR
+ * <http://api.jquery.com/Types/#jqXHR>
+ */
diff --git a/vendor/oojs/oojs-ui/jsduck.json b/vendor/oojs/oojs-ui/jsduck.json
new file mode 100644
index 00000000..918b5702
--- /dev/null
+++ b/vendor/oojs/oojs-ui/jsduck.json
@@ -0,0 +1,16 @@
+{
+ "--title": "OOjs UI - Documentation",
+ "--output": "docs",
+ "--categories": "./jsduck.categories.json",
+ "--eg-iframe": "./jsduck.eg-iframe.html",
+ "--processes": "0",
+ "--warnings-exit-nonzero": true,
+ "--builtin-classes": true,
+ "--external": "HTMLDocument,Window,MouseEvent,KeyboardEvent",
+ "--warnings": ["-nodoc(class,public)"],
+ "--": [
+ "jsduck.external.js",
+ "node_modules/oojs/dist/oojs.jquery.js",
+ "src"
+ ]
+}
diff --git a/vendor/oojs/oojs-ui/php/Element.php b/vendor/oojs/oojs-ui/php/Element.php
new file mode 100644
index 00000000..eaa8c825
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Element.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * DOM element abstraction.
+ *
+ * @abstract
+ */
+class Element extends Tag {
+
+ /* Static properties */
+
+ /**
+ * HTML tag name.
+ *
+ * This may be ignored if getTagName() is overridden.
+ *
+ * @var string
+ */
+ public static $tagName = 'div';
+
+ /**
+ * Default text direction, used for some layout calculations. Use setDefaultDir() to change.
+ *
+ * Currently only per-document directionality is supported.
+ *
+ * @var string
+ */
+ public static $defaultDir = 'ltr';
+
+ /* Members */
+
+ /**
+ * Element data.
+ *
+ * @var mixed
+ */
+ protected $data = null;
+
+ /**
+ * Mixins.
+ *
+ * @var array List mixed in objects.
+ */
+ protected $mixins = array();
+
+ /* Methods */
+
+ /**
+ * @param array $config Configuration options
+ * @param string[] $config['classes'] CSS class names to add
+ * @param string $config['id'] HTML id attribute
+ * @param string $config['text'] Text to insert
+ * @param array $config['content'] Content to append (after text), strings
+ * or Element objects. Strings will be HTML-escaped for output, use an
+ * HtmlSnippet instance to prevent that.
+ * @param mixed $config['data'] Element data
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $this->getTagName() );
+
+ // Initialization
+ if ( isset( $config['infusable'] ) && is_bool( $config['infusable'] ) ) {
+ $this->setInfusable( $config['infusable'] );
+ }
+ if ( isset( $config['data'] ) ) {
+ $this->setData( $config['data'] );
+ }
+ if ( isset( $config['classes'] ) && is_array( $config['classes'] ) ) {
+ $this->addClasses( $config['classes'] );
+ }
+ if ( isset( $config['id'] ) ) {
+ $this->setAttributes( array( 'id' => $config['id'] ) );
+ }
+ if ( isset( $config['text'] ) ) {
+ // JS compatibility
+ $this->appendContent( $config['text'] );
+ }
+ if ( isset( $config['content'] ) ) {
+ $this->appendContent( $config['content'] );
+ }
+ }
+
+ /**
+ * Call a mixed-in method.
+ *
+ * This makes the methods of a mixin accessible through the element being mixed into.
+ *
+ * Triggers an error if the method is not found, as normal.
+ *
+ * @param string $method Method name
+ * @param array $arguments Method arguments
+ * @return mixed Result of method call
+ */
+ public function __call( $method, $arguments ) {
+ // Search mixins for methods
+ foreach ( $this->mixins as $mixin ) {
+ if ( method_exists( $mixin, $method ) ) {
+ return call_user_func_array( array( $mixin, $method ), $arguments );
+ }
+ }
+ // Fail normally
+ trigger_error(
+ 'Call to undefined method ' . __CLASS__ . '::' . $method . '()',
+ E_USER_ERROR
+ );
+ }
+
+ /**
+ * Get a mixed-in target property.
+ *
+ * This makes the target of a mixin accessible through the element being mixed into.
+ *
+ * The target's property name is statically configured by the mixin class.
+ *
+ * Triggers a notice if the property is not found, as normal.
+ *
+ * @param string $name Property name
+ * @return Tag|null Target property or null if not found
+ */
+ public function __get( $name ) {
+ // Search mixins for methods
+ foreach ( $this->mixins as $mixin ) {
+ if ( isset( $mixin::$targetPropertyName ) && $mixin::$targetPropertyName === $name ) {
+ return $mixin->target;
+ }
+ }
+ // Fail normally
+ trigger_error( 'Undefined property: ' . $name, E_USER_NOTICE );
+ return null;
+ }
+
+ /**
+ * Get the HTML tag name.
+ *
+ * Override this method to base the result on instance information.
+ *
+ * @return string HTML tag name
+ */
+ public function getTagName() {
+ return $this::$tagName;
+ }
+
+ /**
+ * Get element data.
+ *
+ * @return mixed Element data
+ */
+ public function getData() {
+ return $this->data;
+ }
+
+ /**
+ * Set element data.
+ *
+ * @param mixed $data Element data
+ * @chainable
+ */
+ public function setData( $data ) {
+ $this->data = $data;
+ return $this;
+ }
+
+ /**
+ * Check if element supports one or more methods.
+ *
+ * @param string|string[] $methods Method or list of methods to check
+ * @return boolean All methods are supported
+ */
+ public function supports( $methods ) {
+ $support = 0;
+ $methods = (array)$methods;
+
+ foreach ( $methods as $method ) {
+ if ( method_exists( $this, $method ) ) {
+ $support++;
+ continue;
+ }
+
+ // Search mixins for methods
+ foreach ( $this->mixins as $mixin ) {
+ if ( method_exists( $mixin, $method ) ) {
+ $support++;
+ break;
+ }
+ }
+ }
+
+ return count( $methods ) === $support;
+ }
+
+ /**
+ * Mixin a class.
+ *
+ * @param ElementMixin $mixin Mixin object
+ */
+ public function mixin( ElementMixin $mixin ) {
+ $this->mixins[] = $mixin;
+ }
+
+ /**
+ * Add the necessary properties to the given `$config` array to allow
+ * reconstruction of this widget via its constructor.
+ * @param array &$config
+ * An array which will be mutated to add the necessary configuration
+ * properties. Unless you are implementing a subclass, you should
+ * always pass a new empty `array()`.
+ * @return array
+ * A configuration array which can be passed to this object's
+ * constructor to recreate it. This is a return value to allow
+ * the safe use of copy-by-value functions like `array_merge` in
+ * the implementation.
+ */
+ public function getConfig( &$config ) {
+ foreach ( $this->mixins as $mixin ) {
+ $config = $mixin->getConfig( $config );
+ }
+ if ( $this->data !== null ) {
+ $config['data'] = $this->data;
+ }
+ return $config;
+ }
+
+ /**
+ * Create a modified version of the configuration array suitable for
+ * JSON serialization by replacing `Tag` references and
+ * `HtmlSnippet`s.
+ *
+ * @return array
+ * A serialized configuration array.
+ */
+ private function getSerializedConfig() {
+ // Ensure that '_' comes first in the output.
+ $config = array( '_' => true );
+ $config = $this->getConfig( $config );
+ // Post-process config array to turn Tag references into ID references
+ // and HtmlSnippet references into a { html: 'string' } JSON form.
+ $replaceElements = function( &$item ) {
+ if ( $item instanceof Tag ) {
+ $item->ensureInfusableId();
+ $item = array( 'tag' => $item->getAttribute( 'id' ) );
+ } elseif ( $item instanceof HtmlSnippet ) {
+ $item = array( 'html' => (string) $item );
+ }
+ };
+ array_walk_recursive( $config, $replaceElements );
+ // Set '_' last to ensure that subclasses can't accidentally step on it.
+ $config['_'] = preg_replace( '/^OOUI\\\\/', '', get_class( $this ) );
+ return $config;
+ }
+
+ protected function getGeneratedAttributes() {
+ $attributesArray = parent::getGeneratedAttributes();
+ // Add `data-ooui` attribute from serialized config array.
+ if ( $this->infusable ) {
+ $serialized = $this->getSerializedConfig();
+ $attributesArray['data-ooui'] = json_encode( $serialized );
+ }
+ return $attributesArray;
+ }
+
+ /**
+ * Render element into HTML.
+ *
+ * @return string HTML serialization
+ */
+ public function toString() {
+ Theme::singleton()->updateElementClasses( $this );
+ if ( $this->isInfusable() ) {
+ $this->ensureInfusableId();
+ }
+ return parent::toString();
+ }
+
+ /**
+ * Get the direction of the user interface for a given element.
+ *
+ * Currently only per-document directionality is supported.
+ *
+ * @param Tag $element Element to check
+ * @return string Text direction, either 'ltr' or 'rtl'
+ */
+ public static function getDir( Tag $element ) {
+ return self::$defaultDir;
+ }
+
+ /**
+ * Set the default direction of the user interface.
+ *
+ * @return string Text direction, either 'ltr' or 'rtl'
+ */
+ public static function setDefaultDir( $dir ) {
+ self::$defaultDir = $dir === 'rtl' ? 'rtl' : 'ltr';
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/ElementMixin.php b/vendor/oojs/oojs-ui/php/ElementMixin.php
new file mode 100644
index 00000000..7f54076e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/ElementMixin.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace OOUI;
+
+class ElementMixin {
+
+ /* Properties */
+
+ /**
+ * Tag being targeted.
+ *
+ * @var Tag
+ */
+ public $target = null;
+
+ /**
+ * Element being mixed into.
+ *
+ * @var Element
+ */
+ protected $element = null;
+
+ /**
+ * Property name for accessing the target on the element.
+ *
+ * @var string
+ */
+ public static $targetPropertyName = '';
+
+ /* Methods */
+
+ /**
+ * Create element.
+ *
+ * @param Element $element Element being mixed into
+ * @param Tag $tag Tag being targeted
+ * @param array $config Configuration options
+ */
+ public function __construct( Element $element, Tag $target, array $config = array() ) {
+ $this->element = $element;
+ $this->target = $target;
+ }
+
+ /**
+ * Add properties to the given `$config` array to allow reconstruction
+ * of this widget via its constructor. This method is meant to be
+ * overridden by subclasses of `ElementMixin`.
+ *
+ * @return array A configuration array.
+ * @see Element::getConfig()
+ */
+ public function getConfig( &$config ) {
+ return $config;
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/Exception.php b/vendor/oojs/oojs-ui/php/Exception.php
new file mode 100644
index 00000000..2f5ba1b4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Exception.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace OOUI;
+
+class Exception extends \Exception {
+}
diff --git a/vendor/oojs/oojs-ui/php/HtmlSnippet.php b/vendor/oojs/oojs-ui/php/HtmlSnippet.php
new file mode 100644
index 00000000..e0889fca
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/HtmlSnippet.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Wraps a HTML snippet for use with Tag::appendContent() and Tag::prependContent().
+ */
+class HtmlSnippet {
+
+ /* Members */
+
+ /**
+ * HTML snippet this instance represents.
+ *
+ * @var string
+ */
+ protected $content;
+
+ /* Methods */
+
+ /**
+ * @param string $content
+ */
+ public function __construct( $content ) {
+ $this->content = $content;
+ }
+
+ /**
+ * Render into HTML.
+ *
+ * @return string Unchanged HTML snippet
+ */
+ public function __toString() {
+ return $this->content;
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/Layout.php b/vendor/oojs/oojs-ui/php/Layout.php
new file mode 100644
index 00000000..7a4e58d0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Layout.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Container for elements.
+ *
+ * @abstract
+ */
+class Layout extends Element {
+ /**
+ * @param array $config Configuration options
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-layout' ) );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/Tag.php b/vendor/oojs/oojs-ui/php/Tag.php
new file mode 100644
index 00000000..da8c2bfa
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Tag.php
@@ -0,0 +1,365 @@
+<?php
+
+namespace OOUI;
+
+class Tag {
+
+ /* Members */
+
+ /**
+ * Tag name for this instance.
+ *
+ * @var string HTML tag name
+ */
+ protected $tag = '';
+
+ /**
+ * Attributes.
+ *
+ * @var array HTML attributes
+ */
+ protected $attributes = array();
+
+ /**
+ * Classes.
+ *
+ * @var array CSS classes
+ */
+ protected $classes = array();
+
+ /**
+ * Content.
+ *
+ * @var array Content text and elements
+ */
+ protected $content = array();
+
+ /**
+ * Group.
+ *
+ * @var GroupElement|null Group element is in
+ */
+ protected $elementGroup = null;
+
+ /**
+ * Infusion support.
+ *
+ * @var boolean Whether to serialize tag/element/widget state for client-side use.
+ */
+ protected $infusable = false;
+
+ /* Methods */
+
+ /**
+ * Create element.
+ *
+ * @param string $tag HTML tag name
+ */
+ public function __construct( $tag = 'div' ) {
+ $this->tag = $tag;
+ }
+
+ /**
+ * Check for CSS class.
+ *
+ * @param string $name CSS class name
+ * @return boolean
+ */
+ public function hasClass( $class ) {
+ return in_array( $class, $this->classes );
+ }
+
+ /**
+ * Add CSS classes.
+ *
+ * @param array $classes List of classes to add
+ * @chainable
+ */
+ public function addClasses( array $classes ) {
+ $this->classes = array_merge( $this->classes, $classes );
+ return $this;
+ }
+
+ /**
+ * Remove CSS classes.
+ *
+ * @param array $classes List of classes to remove
+ * @chainable
+ */
+ public function removeClasses( array $classes ) {
+ $this->classes = array_diff( $this->classes, $classes );
+ return $this;
+ }
+
+ /**
+ * Toggle CSS classes.
+ *
+ * @param array $classes List of classes to add
+ * @param boolean $toggle Add classes
+ * @chainable
+ */
+ public function toggleClasses( array $classes, $toggle = null ) {
+ if ( $toggle === null ) {
+ $this->classes = array_diff(
+ array_merge( $this->classes, $classes ),
+ array_intersect( $this->classes, $classes )
+ );
+ } elseif ( $toggle ) {
+ $this->classes = array_merge( $this->classes, $classes );
+ } else {
+ $this->classes = array_diff( $this->classes, $classes );
+ }
+ return $this;
+ }
+
+ /**
+ * Get HTML attribute value.
+ *
+ * @param string $key HTML attribute name
+ * @return string|null
+ */
+ public function getAttribute( $key ) {
+ return isset( $this->attributes[$key] ) ? $this->attributes[$key] : null;
+ }
+
+ /**
+ * Add HTML attributes.
+ *
+ * @param array $attributes List of attribute key/value pairs to add
+ * @chainable
+ */
+ public function setAttributes( array $attributes ) {
+ foreach ( $attributes as $key => $value ) {
+ $this->attributes[$key] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Set value of input element ('value' attribute for most, element content for textarea).
+ *
+ * @param string $value Value to set
+ * @chainable
+ */
+ public function setValue( $value ) {
+ if ( strtolower( $this->tag ) === 'textarea' ) {
+ $this->clearContent();
+ $this->appendContent( $value );
+ } else {
+ $this->setAttributes( array( 'value' => $value ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Remove HTML attributes.
+ *
+ * @param array $keys List of attribute keys to remove
+ * @chainable
+ */
+ public function removeAttributes( array $keys ) {
+ foreach ( $keys as $key ) {
+ unset( $this->attributes[$key] );
+ }
+ return $this;
+ }
+
+ /**
+ * Add content to the end.
+ *
+ * Accepts variadic arguments (the $content argument can be repeated any number of times).
+ *
+ * @param string|Tag|HtmlSnippet $content Content to append. Strings will be HTML-escaped
+ * for output, use a HtmlSnippet instance to prevent that.
+ * @chainable
+ */
+ public function appendContent( /* $content... */ ) {
+ $contents = func_get_args();
+ $this->content = array_merge( $this->content, $contents );
+ return $this;
+ }
+
+ /**
+ * Add content to the beginning.
+ *
+ * Accepts variadic arguments (the $content argument can be repeated any number of times).
+ *
+ * @param string|Tag|HtmlSnippet $content Content to prepend. Strings will be HTML-escaped
+ * for output, use a HtmlSnippet instance to prevent that.
+ * @chainable
+ */
+ public function prependContent( /* $content... */ ) {
+ $contents = func_get_args();
+ array_splice( $this->content, 0, 0, $contents );
+ return $this;
+ }
+
+ /**
+ * Remove all content.
+ *
+ * @chainable
+ */
+ public function clearContent() {
+ $this->content = array();
+ return $this;
+ }
+
+ /**
+ * Get group element is in.
+ *
+ * @return GroupElement|null Group element, null if none
+ */
+ public function getElementGroup() {
+ return $this->elementGroup;
+ }
+
+ /**
+ * Set group element is in.
+ *
+ * @param GroupElement|null $group Group element, null if none
+ * @chainable
+ */
+ public function setElementGroup( $group ) {
+ $this->elementGroup = $group;
+ return $this;
+ }
+
+ /**
+ * Enable widget for client-side infusion.
+ *
+ * @param boolean $infusable True to allow tag/element/widget to be referenced client-side.
+ * @chainable
+ */
+ public function setInfusable( $infusable ) {
+ $this->infusable = $infusable;
+ return $this;
+ }
+
+ /**
+ * Get client-side infusability.
+ *
+ * @return boolean If this tag/element/widget can be referenced client-side.
+ */
+ public function isInfusable() {
+ return $this->infusable;
+ }
+
+ private static $id_cnt = 0;
+ /**
+ * Ensure that this given Tag is infusable and has a unique `id`
+ * attribute.
+ * @chainable
+ */
+ public function ensureInfusableId() {
+ $this->setInfusable( true );
+ if ( $this->getAttribute( 'id' ) === null ) {
+ $this->setAttributes( array( 'id' => "ooui-" . ( self::$id_cnt++ ) ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Return an augmented `attributes` array, including synthetic attributes
+ * which are created from other properties (like the `classes` array)
+ * but which shouldn't be retained in the user-visible `attributes`.
+ * @return array An attributes array.
+ */
+ protected function getGeneratedAttributes() {
+ // Copy attributes, add `class` attribute from `$this->classes` array.
+ $attributesArray = $this->attributes;
+ if ( $this->classes ) {
+ $attributesArray['class'] = implode( ' ', array_unique( $this->classes ) );
+ }
+ if ( $this->infusable ) {
+ // Indicate that this is "just" a tag (not a widget)
+ $attributesArray['data-ooui'] = json_encode( array( '_' => 'Tag' ) );
+ }
+ return $attributesArray;
+ }
+
+ /**
+ * Render element into HTML.
+ *
+ * @return string HTML serialization
+ */
+ public function toString() {
+ $attributes = '';
+ foreach ( $this->getGeneratedAttributes() as $key => $value ) {
+ if ( !preg_match( '/^[0-9a-zA-Z-]+$/', $key ) ) {
+ throw new Exception( 'Attribute name must consist of only ASCII letters, numbers and dash' );
+ }
+
+ if ( $key === 'href' || $key === 'action' ) {
+ // Make it impossible to point a link or a form to a 'javascript:' URL. There's no good way
+ // to blacklist them because of very lax parsing, so instead we whitelist known-good
+ // protocols (and also accept protocol-less and protocol-relative links). There are no good
+ // reasons to ever use 'javascript:' URLs anyway.
+ $protocolWhitelist = array(
+ // Sourced from MediaWiki's $wgUrlProtocols
+ 'bitcoin', 'ftp', 'ftps', 'geo', 'git', 'gopher', 'http', 'https', 'irc', 'ircs',
+ 'magnet', 'mailto', 'mms', 'news', 'nntp', 'redis', 'sftp', 'sip', 'sips', 'sms', 'ssh',
+ 'svn', 'tel', 'telnet', 'urn', 'worldwind', 'xmpp',
+ );
+
+ // Protocol-relative URLs are handled really badly by parse_url()
+ if ( substr( $value, 0, 2 ) === '//' ) {
+ $url = "http:$value";
+ } else {
+ $url = $value;
+ }
+ // Must suppress warnings when the value is not a valid URL. parse_url() returns false then.
+ // @codingStandardsIgnoreStart
+ $scheme = @parse_url( $url, PHP_URL_SCHEME );
+ // @codingStandardsIgnoreEnd
+
+ if ( !( $scheme === null || in_array( strtolower( $scheme ), $protocolWhitelist ) ) ) {
+ throw new Exception( "Potentially unsafe '$key' attribute value. " .
+ "Scheme: '$scheme'; value: '$value'." );
+ }
+ }
+
+ // Use single-quotes around the attribute value in HTML, because
+ // some of the values might be JSON strings
+ // 1. Encode both single and double quotes (and other special chars)
+ $value = htmlspecialchars( $value, ENT_QUOTES );
+ // 2. Decode double quotes, for readability.
+ $value = str_replace( '&quot;', '"', $value );
+ // 3. Wrap attribute value in single quotes in the HTML.
+ $attributes .= ' ' . $key . "='" . $value . "'";
+ }
+
+ // Content
+ $content = '';
+ foreach ( $this->content as $part ) {
+ if ( is_string( $part ) ) {
+ $content .= htmlspecialchars( $part );
+ } elseif ( $part instanceof Tag || $part instanceof HtmlSnippet ) {
+ $content .= (string)$part;
+ }
+ }
+
+ if ( !preg_match( '/^[0-9a-zA-Z]+$/', $this->tag ) ) {
+ throw new Exception( 'Tag name must consist of only ASCII letters and numbers' );
+ }
+
+ // Tag
+ return '<' . $this->tag . $attributes . '>' . $content . '</' . $this->tag . '>';
+ }
+
+ /**
+ * Magic method implementation.
+ *
+ * PHP doesn't allow __toString to throw exceptions and will trigger a fatal error if it does.
+ * This is a wrapper around the real toString() to convert them to errors instead.
+ *
+ * @return string
+ */
+ public function __toString() {
+ try {
+ return $this->toString();
+ } catch ( Exception $ex ) {
+ trigger_error( (string)$ex, E_USER_ERROR );
+ return '';
+ }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/Theme.php b/vendor/oojs/oojs-ui/php/Theme.php
new file mode 100644
index 00000000..d36b6d82
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Theme.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Theme logic.
+ *
+ * @abstract
+ */
+class Theme {
+
+ /* Members */
+
+ private static $singleton;
+
+ /* Static Methods */
+
+ public static function setSingleton( Theme $theme ) {
+ self::$singleton = $theme;
+ }
+
+ public static function singleton() {
+ if ( !self::$singleton ) {
+ throw new Exception( __METHOD__ . ' was called with no singleton theme set.' );
+ }
+
+ return self::$singleton;
+ }
+
+ /**
+ * Get a list of classes to be applied to a widget.
+ *
+ * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
+ * otherwise state transitions will not work properly.
+ *
+ * @param Element $element Element for which to get classes
+ * @return array Categorized class names with `on` and `off` lists
+ */
+ public function getElementClasses( Element $element ) {
+ return array( 'on' => array(), 'off' => array() );
+ }
+
+ /**
+ * Update CSS classes provided by the theme.
+ *
+ * For elements with theme logic hooks, this should be called any time there's a state change.
+ *
+ * @param Element $element Element for which to update classes
+ * @return array Categorized class names with `on` and `off` lists
+ */
+ public function updateElementClasses( Element $element ) {
+ $classes = $this->getElementClasses( $element );
+
+ $element
+ ->removeClasses( $classes['off'] )
+ ->addClasses( $classes['on'] );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/Widget.php b/vendor/oojs/oojs-ui/php/Widget.php
new file mode 100644
index 00000000..04152aa5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/Widget.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * User interface control.
+ *
+ * @abstract
+ */
+class Widget extends Element {
+
+ /* Properties */
+
+ /**
+ * Disabled.
+ *
+ * @var boolean Widget is disabled
+ */
+ protected $disabled = false;
+
+ /* Methods */
+
+ /**
+ * @param array $config Configuration options
+ * @param boolean $config['disabled'] Disable (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Initialize config
+ $config = array_merge( array( 'disabled' => false ), $config );
+
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-widget' ) );
+ $this->setDisabled( $config['disabled'] );
+ }
+
+ /**
+ * Check if the widget is disabled.
+ *
+ * @return boolean Button is disabled
+ */
+ public function isDisabled() {
+ return $this->disabled;
+ }
+
+ /**
+ * Set the disabled state of the widget.
+ *
+ * This should probably change the widgets' appearance and prevent it from being used.
+ *
+ * @param boolean $disabled Disable widget
+ * @chainable
+ */
+ public function setDisabled( $disabled ) {
+ $this->disabled = !!$disabled;
+ $this->toggleClasses( array( 'oo-ui-widget-disabled' ), $this->disabled );
+ $this->toggleClasses( array( 'oo-ui-widget-enabled' ), !$this->disabled );
+ $this->setAttributes( array( 'aria-disabled' => $this->disabled ? 'true' : 'false' ) );
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->disabled ) {
+ $config['disabled'] = $this->disabled;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/ButtonElement.php b/vendor/oojs/oojs-ui/php/elements/ButtonElement.php
new file mode 100644
index 00000000..f9acf2d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/ButtonElement.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element with a button.
+ *
+ * Buttons are used for controls which can be clicked. They can be configured to use tab indexing
+ * and access keys for accessibility purposes.
+ *
+ * @abstract
+ */
+class ButtonElement extends ElementMixin {
+ /**
+ * Button is framed.
+ *
+ * @var boolean
+ */
+ protected $framed = false;
+
+ /**
+ * Button's access key.
+ *
+ * @var string
+ */
+ protected $accessKey = null;
+
+ public static $targetPropertyName = 'button';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param boolean $config['framed'] Render button with a frame (default: true)
+ * @param string $config['accessKey'] Button's access key
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ $target = isset( $config['button'] ) ? $config['button'] : new Tag( 'a' );
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->element->addClasses( array( 'oo-ui-buttonElement' ) );
+ $this->target->addClasses( array( 'oo-ui-buttonElement-button' ) );
+ $this->toggleFramed( isset( $config['framed'] ) ? $config['framed'] : true );
+ $this->setAccessKey( isset( $config['accessKey'] ) ? $config['accessKey'] : null );
+ $this->target->setAttributes( array(
+ 'role' => 'button',
+ ) );
+ }
+
+ /**
+ * Toggle frame.
+ *
+ * @param boolean $framed Make button framed, omit to toggle
+ * @chainable
+ */
+ public function toggleFramed( $framed = null ) {
+ $this->framed = $framed !== null ? !!$framed : !$this->framed;
+ $this->element->toggleClasses( array( 'oo-ui-buttonElement-framed' ), $this->framed );
+ $this->element->toggleClasses( array( 'oo-ui-buttonElement-frameless' ), !$this->framed );
+ }
+
+ /**
+ * Check if button has a frame.
+ *
+ * @return boolean Button is framed
+ */
+ public function isFramed() {
+ return $this->framed;
+ }
+
+ /**
+ * Set access key.
+ *
+ * @param string $accessKey Button's access key, use empty string to remove
+ * @chainable
+ */
+ public function setAccessKey( $accessKey ) {
+ $accessKey = is_string( $accessKey ) && strlen( $accessKey ) ? $accessKey : null;
+
+ if ( $this->accessKey !== $accessKey ) {
+ if ( $accessKey !== null ) {
+ $this->target->setAttributes( array( 'accesskey' => $accessKey ) );
+ } else {
+ $this->target->removeAttributes( array( 'accesskey' ) );
+ }
+ $this->accessKey = $accessKey;
+ }
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->framed !== true ) {
+ $config['framed'] = $this->framed;
+ }
+ if ( $this->accessKey !== null ) {
+ $config['accessKey'] = $this->accessKey;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php b/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php
new file mode 100644
index 00000000..bd5dc80d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element with named flags that can be added, removed, listed and checked.
+ *
+ * A flag, when set, adds a CSS class on the `$element` by combining `oo-ui-flaggedElement-` with
+ * the flag name. Flags are primarily useful for styling.
+ *
+ * @abstract
+ */
+class FlaggedElement extends ElementMixin {
+ /**
+ * Flags.
+ *
+ * @var string
+ */
+ protected $flags = array();
+
+ public static $targetPropertyName = 'flagged';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param string|string[] $config['flags'] Flags describing importance and functionality, e.g.
+ * 'primary', 'safe', 'progressive', 'destructive' or 'constructive'
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ $target = isset( $config['flagged'] ) ? $config['flagged'] : $element;
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->setFlags( isset( $config['flags'] ) ? $config['flags'] : null );
+ }
+
+ /**
+ * Check if a flag is set.
+ *
+ * @param string $flag Name of flag
+ * @return boolean Has flag
+ */
+ public function hasFlag( $flag ) {
+ return isset( $this->flags[$flag] );
+ }
+
+ /**
+ * Get the names of all flags set.
+ *
+ * @return string[] Flag names
+ */
+ public function getFlags() {
+ return array_keys( $this->flags );
+ }
+
+ /**
+ * Clear all flags.
+ *
+ * @chainable
+ */
+ public function clearFlags() {
+ $remove = array();
+ $classPrefix = 'oo-ui-flaggedElement-';
+
+ foreach ( $this->flags as $flag ) {
+ $remove[] = $classPrefix . $flag;
+ }
+
+ $this->target->removeClasses( $remove );
+ $this->flags = array();
+
+ return $this;
+ }
+
+ /**
+ * Add one or more flags.
+ *
+ * @param string|array $flags One or more flags to add, or an array keyed by flag name
+ * containing boolean set/remove instructions.
+ * @chainable
+ */
+ public function setFlags( $flags ) {
+ $add = array();
+ $remove = array();
+ $classPrefix = 'oo-ui-flaggedElement-';
+
+ if ( is_string( $flags ) ) {
+ // Set
+ if ( !isset( $this->flags[$flags] ) ) {
+ $this->flags[$flags] = true;
+ $add[] = $classPrefix . $flags;
+ }
+ } elseif ( is_array( $flags ) ) {
+ foreach ( $flags as $key => $value ) {
+ if ( is_numeric( $key ) ) {
+ // Set
+ if ( !isset( $this->flags[$value] ) ) {
+ $this->flags[$value] = true;
+ $add[] = $classPrefix . $value;
+ }
+ } else {
+ if ( $value ) {
+ // Set
+ if ( !isset( $this->flags[$key] ) ) {
+ $this->flags[$key] = true;
+ $add[] = $classPrefix . $key;
+ }
+ } else {
+ // Remove
+ if ( isset( $this->flags[$key] ) ) {
+ unset( $this->flags[$key] );
+ $remove[] = $classPrefix . $key;
+ }
+ }
+ }
+ }
+ }
+
+ $this->target
+ ->addClasses( $add )
+ ->removeClasses( $remove );
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ if ( !empty( $this->flags ) ) {
+ $config['flags'] = $this->getFlags();
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/GroupElement.php b/vendor/oojs/oojs-ui/php/elements/GroupElement.php
new file mode 100644
index 00000000..93d3c7a1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/GroupElement.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element containing a sequence of child elements.
+ *
+ * @abstract
+ */
+class GroupElement extends ElementMixin {
+ /**
+ * List of items in the group.
+ *
+ * @var Element[]
+ */
+ protected $items = array();
+
+ public static $targetPropertyName = 'group';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ $target = isset( $config['group'] ) ? $config['group'] : new Tag( 'div' );
+ parent::__construct( $element, $target, $config );
+ }
+
+ /**
+ * Check if there are no items.
+ *
+ * @return boolean Group is empty
+ */
+ public function isEmpty() {
+ return !count( $this->items );
+ }
+
+ /**
+ * Get items.
+ *
+ * @return Element[] Items
+ */
+ public function getItems() {
+ return $this->items;
+ }
+
+ /**
+ * Add items.
+ *
+ * Adding an existing item will move it.
+ *
+ * @param Element[] $items Items
+ * @param number $index Index to insert items at
+ * @chainable
+ */
+ public function addItems( array $items, $index = null ) {
+ foreach ( $items as $item ) {
+ // Check if item exists then remove it first, effectively "moving" it
+ $currentIndex = array_search( $item, $this->items );
+ if ( $currentIndex !== false ) {
+ $this->removeItems( array( $item ) );
+ // Adjust index to compensate for removal
+ if ( $currentIndex < $index ) {
+ $index--;
+ }
+ }
+ // Add the item
+ $item->setElementGroup( $this );
+ }
+
+ if ( $index === null || $index < 0 || $index >= count( $this->items ) ) {
+ $this->items = array_merge( $this->items, $items );
+ } else {
+ array_splice( $this->items, $index, 0, $items );
+ }
+
+ // Update actual target element contents to reflect our list
+ $this->target->clearContent();
+ call_user_func_array( array( $this->target, 'appendContent' ), $this->items );
+
+ return $this;
+ }
+
+ /**
+ * Remove items.
+ *
+ * @param Element[] $items Items to remove
+ * @chainable
+ */
+ public function removeItems( $items ) {
+ foreach ( $items as $item ) {
+ $index = array_search( $item, $this->items );
+ if ( $index !== false ) {
+ $item->setElementGroup( null );
+ array_splice( $this->items, $index, 1 );
+ }
+ }
+
+ // Update actual target element contents to reflect our list
+ $this->target->clearContent();
+ call_user_func_array( array( $this->target, 'appendContent' ), $this->items );
+
+ return $this;
+ }
+
+ /**
+ * Clear all items.
+ *
+ * Items will be detached, not removed, so they can be used later.
+ *
+ * @chainable
+ */
+ public function clearItems() {
+ foreach ( $this->items as $item ) {
+ $item->setElementGroup( null );
+ }
+
+ $this->items = array();
+ $this->target->clearContent();
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ $config['items'] = $this->items;
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/IconElement.php b/vendor/oojs/oojs-ui/php/elements/IconElement.php
new file mode 100644
index 00000000..b6d27376
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/IconElement.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element containing an icon.
+ *
+ * Icons are graphics, about the size of normal text. They can be used to aid the user in locating
+ * a control or convey information in a more space efficient way. Icons should rarely be used
+ * without labels; such as in a toolbar where space is at a premium or within a context where the
+ * meaning is very clear to the user.
+ *
+ * @abstract
+ */
+class IconElement extends ElementMixin {
+ /**
+ * Symbolic icon name.
+ *
+ * @var string
+ */
+ protected $icon = null;
+
+ public static $targetPropertyName = 'icon';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param string $config['icon'] Symbolic icon name
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ // FIXME 'iconElement' is a very stupid way to call '$icon'
+ $target = isset( $config['iconElement'] ) ? $config['iconElement'] : new Tag( 'span' );
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->target->addClasses( array( 'oo-ui-iconElement-icon' ) );
+ $this->setIcon( isset( $config['icon'] ) ? $config['icon'] : null );
+ }
+
+ /**
+ * Set icon name.
+ *
+ * @param string|null $icon Symbolic icon name
+ * @chainable
+ */
+ public function setIcon( $icon = null ) {
+ if ( $this->icon !== null ) {
+ $this->target->removeClasses( array( 'oo-ui-icon-' . $this->icon ) );
+ }
+ if ( $icon !== null ) {
+ $this->target->addClasses( array( 'oo-ui-icon-' . $icon ) );
+ }
+
+ $this->icon = $icon;
+ $this->element->toggleClasses( array( 'oo-ui-iconElement' ), (bool)$this->icon );
+
+ return $this;
+ }
+
+ /**
+ * Get icon name.
+ *
+ * @return string Icon name
+ */
+ public function getIcon() {
+ return $this->icon;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->icon !== null ) {
+ $config['icon'] = $this->icon;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php b/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php
new file mode 100644
index 00000000..56238b6c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element containing an indicator.
+ *
+ * Indicators are graphics, smaller than normal text. They can be used to describe unique status or
+ * behavior. Indicators should only be used in exceptional cases; such as a button that opens a menu
+ * instead of performing an action directly, or an item in a list which has errors that need to be
+ * resolved.
+ *
+ * @abstract
+ */
+class IndicatorElement extends ElementMixin {
+ /**
+ * Symbolic indicator name
+ *
+ * @var string|null
+ */
+ protected $indicator = null;
+
+ public static $targetPropertyName = 'indicator';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param string $config['indicator'] Symbolic indicator name
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ // FIXME 'indicatorElement' is a very stupid way to call '$indicator'
+ $target = isset( $config['indicatorElement'] )
+ ? $config['indicatorElement']
+ : new Tag( 'span' );
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->target->addClasses( array( 'oo-ui-indicatorElement-indicator' ) );
+ $this->setIndicator( isset( $config['indicator'] ) ? $config['indicator'] : null );
+ }
+
+ /**
+ * Set indicator name.
+ *
+ * @param string|null $indicator Symbolic name of indicator to use or null for no indicator
+ * @chainable
+ */
+ public function setIndicator( $indicator = null ) {
+ if ( $this->indicator !== null ) {
+ $this->target->removeClasses( array( 'oo-ui-indicator-' . $this->indicator ) );
+ }
+ if ( $indicator !== null ) {
+ $this->target->addClasses( array( 'oo-ui-indicator-' . $indicator ) );
+ }
+
+ $this->indicator = $indicator;
+ $this->element->toggleClasses( array( 'oo-ui-indicatorElement' ), (bool)$this->indicator );
+
+ return $this;
+ }
+
+ /**
+ * Get indicator name.
+ *
+ * @return string Symbolic name of indicator
+ */
+ public function getIndicator() {
+ return $this->indicator;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->indicator !== null ) {
+ $config['indicator'] = $this->indicator;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/LabelElement.php b/vendor/oojs/oojs-ui/php/elements/LabelElement.php
new file mode 100644
index 00000000..d5cf7bee
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/LabelElement.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element containing a label.
+ *
+ * @abstract
+ */
+class LabelElement extends ElementMixin {
+ /**
+ * Label value.
+ *
+ * @var string|HtmlSnippet|null
+ */
+ protected $label = null;
+
+ public static $targetPropertyName = 'label';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param string|HtmlSnippet $config['label'] Label text
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ // FIXME 'labelElement' is a very stupid way to call '$label'
+ $target = isset( $config['labelElement'] ) ? $config['labelElement'] : new Tag( 'span' );
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->target->addClasses( array( 'oo-ui-labelElement-label' ) );
+ $this->setLabel( isset( $config['label'] ) ? $config['label'] : null );
+ }
+
+ /**
+ * Set the label.
+ *
+ * An empty string will result in the label being hidden. A string containing only whitespace will
+ * be converted to a single `&nbsp;`.
+ *
+ * @param string|HtmlSnippet|null $label Label text
+ * @chainable
+ */
+ public function setLabel( $label ) {
+ $this->label = $label;
+
+ $this->target->clearContent();
+ if ( $this->label !== null ) {
+ if ( is_string( $this->label ) && $this->label !== '' && trim( $this->label ) === '' ) {
+ $this->target->appendContent( new HtmlSnippet( '&nbsp;' ) );
+ } else {
+ $this->target->appendContent( $label );
+ }
+ }
+
+ $this->element->toggleClasses( array( 'oo-ui-labelElement' ), !!$this->label );
+
+ return $this;
+ }
+
+ /**
+ * Get the label.
+ *
+ * @return string|HtmlSnippet|null Label text
+ */
+ public function getLabel() {
+ return $this->label;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->label !== null ) {
+ $config['label'] = $this->label;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php b/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php
new file mode 100644
index 00000000..223b5371
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element supporting "sequential focus navigation" using the 'tabindex' attribute.
+ *
+ * @abstract
+ */
+class TabIndexedElement extends ElementMixin {
+ /**
+ * Tab index value.
+ *
+ * @var number|null
+ */
+ protected $tabIndex = null;
+
+ public static $targetPropertyName = 'tabIndexed';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param number|null $config['tabIndex'] Tab index value. Use 0 to use default ordering, use -1 to
+ * prevent tab focusing, use null to suppress the `tabindex` attribute. (default: 0)
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ $target = isset( $config['tabIndexed'] ) ? $config['tabIndexed'] : $element;
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->setTabIndex( isset( $config['tabIndex'] ) ? $config['tabIndex'] : 0 );
+ }
+
+ /**
+ * Set tab index value.
+ *
+ * @param number|null $tabIndex Tab index value or null for no tab index
+ * @chainable
+ */
+ public function setTabIndex( $tabIndex ) {
+ $tabIndex = is_numeric( $tabIndex ) ? $tabIndex : null;
+
+ if ( $this->tabIndex !== $tabIndex ) {
+ $this->tabIndex = $tabIndex;
+ $this->updateTabIndex();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Update the tabIndex attribute, in case of changes to tabIndex or disabled
+ * state.
+ *
+ * @chainable
+ */
+ public function updateTabIndex() {
+ $disabled = $this->element->isDisabled();
+ if ( $this->tabIndex !== null ) {
+ $this->target->setAttributes( array(
+ // Do not index over disabled elements
+ 'tabindex' => $disabled ? -1 : $this->tabIndex,
+ // ChromeVox and NVDA do not seem to inherit this from parent elements
+ 'aria-disabled' => ( $disabled ? 'true' : 'false' )
+ ) );
+ } else {
+ $this->target->removeAttributes( array( 'tabindex', 'aria-disabled' ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Get tab index value.
+ *
+ * @return number|null Tab index value
+ */
+ public function getTabIndex() {
+ return $this->tabIndex;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->tabIndex !== 0 ) {
+ $config['tabIndex'] = $this->tabIndex;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/elements/TitledElement.php b/vendor/oojs/oojs-ui/php/elements/TitledElement.php
new file mode 100644
index 00000000..5f1317c4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/elements/TitledElement.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Element with a title.
+ *
+ * Titles are rendered by the browser and are made visible when hovering the element. Titles are
+ * not visible on touch devices.
+ *
+ * @abstract
+ */
+class TitledElement extends ElementMixin {
+ /**
+ * Title text.
+ *
+ * @var string
+ */
+ protected $title = null;
+
+ public static $targetPropertyName = 'titled';
+
+ /**
+ * @param Element $element Element being mixed into
+ * @param array $config Configuration options
+ * @param string $config['title'] Title. If not provided, the static property 'title' is used.
+ */
+ public function __construct( Element $element, array $config = array() ) {
+ // Parent constructor
+ $target = isset( $config['titled'] ) ? $config['titled'] : $element;
+ parent::__construct( $element, $target, $config );
+
+ // Initialization
+ $this->setTitle(
+ isset( $config['title'] ) ? $config['title'] :
+ ( isset( $element::$title ) ? $element::$title : null )
+ );
+ }
+
+ /**
+ * Set title.
+ *
+ * @param string|null $title Title text or null for no title
+ * @chainable
+ */
+ public function setTitle( $title ) {
+ if ( $this->title !== $title ) {
+ $this->title = $title;
+ if ( $title !== null ) {
+ $this->target->setAttributes( array( 'title' => $title ) );
+ } else {
+ $this->target->removeAttributes( array( 'title' ) );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get title.
+ *
+ * @return string Title string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->title !== null ) {
+ $config['title'] = $this->title;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php b/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php
new file mode 100644
index 00000000..ef0d4c6c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Layout made of a field and optional label.
+ *
+ * Available label alignment modes include:
+ * - left: Label is before the field and aligned away from it, best for when the user will be
+ * scanning for a specific label in a form with many fields
+ * - right: Label is before the field and aligned toward it, best for forms the user is very
+ * familiar with and will tab through field checking quickly to verify which field they are in
+ * - top: Label is before the field and above it, best for when the user will need to fill out all
+ * fields from top to bottom in a form with few fields
+ * - inline: Label is after the field and aligned toward it, best for small boolean fields like
+ * checkboxes or radio buttons
+ */
+class FieldLayout extends Layout {
+
+ /**
+ * Alignment.
+ *
+ * @var string
+ */
+ protected $align;
+
+ /**
+ * Field widget to be laid out.
+ *
+ * @var Widget
+ */
+ protected $fieldWidget;
+
+ private $field, $body, $help;
+
+ /**
+ * @param Widget $fieldWidget Field widget
+ * @param array $config Configuration options
+ * @param string $config['align'] Alignment mode, either 'left', 'right', 'top' or 'inline'
+ * (default: 'left')
+ * @param string $config['help'] Explanatory text shown as a '?' icon.
+ */
+ public function __construct( $fieldWidget, array $config = array() ) {
+ // Allow passing positional parameters inside the config array
+ if ( is_array( $fieldWidget ) && isset( $fieldWidget['fieldWidget'] ) ) {
+ $config = $fieldWidget;
+ $fieldWidget = $config['fieldWidget'];
+ }
+
+ $hasInputWidget = $fieldWidget instanceof InputWidget;
+
+ // Config initialization
+ $config = array_merge( array( 'align' => 'left' ), $config );
+
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Properties
+ $this->fieldWidget = $fieldWidget;
+ $this->field = new Tag( 'div' );
+ $this->body = new Tag( $hasInputWidget ? 'label' : 'div' );
+ if ( isset( $config['help'] ) ) {
+ $this->help = new ButtonWidget( array(
+ 'classes' => array( 'oo-ui-fieldLayout-help' ),
+ 'framed' => false,
+ 'icon' => 'info',
+ 'title' => $config['help'],
+ ) );
+ } else {
+ $this->help = '';
+ }
+
+ // Mixins
+ $this->mixin( new LabelElement( $this, $config ) );
+
+ // Initialization
+ $this
+ ->addClasses( array( 'oo-ui-fieldLayout' ) )
+ ->appendContent( $this->help, $this->body );
+ $this->body->addClasses( array( 'oo-ui-fieldLayout-body' ) );
+ $this->field
+ ->addClasses( array( 'oo-ui-fieldLayout-field' ) )
+ ->toggleClasses( array( 'oo-ui-fieldLayout-disable' ), $this->fieldWidget->isDisabled() )
+ ->appendContent( $this->fieldWidget );
+
+ $this->setAlignment( $config['align'] );
+ }
+
+ /**
+ * Get the field.
+ *
+ * @return Widget Field widget
+ */
+ public function getField() {
+ return $this->fieldWidget;
+ }
+
+ /**
+ * Set the field alignment mode.
+ *
+ * @param string $value Alignment mode, either 'left', 'right', 'top' or 'inline'
+ * @chainable
+ */
+ protected function setAlignment( $value ) {
+ if ( $value !== $this->align ) {
+ // Default to 'left'
+ if ( !in_array( $value, array( 'left', 'right', 'top', 'inline' ) ) ) {
+ $value = 'left';
+ }
+ // Reorder elements
+ $this->body->clearContent();
+ if ( $value === 'inline' ) {
+ $this->body->appendContent( $this->field, $this->label );
+ } else {
+ $this->body->appendContent( $this->label, $this->field );
+ }
+ // Set classes. The following classes can be used here:
+ // * oo-ui-fieldLayout-align-left
+ // * oo-ui-fieldLayout-align-right
+ // * oo-ui-fieldLayout-align-top
+ // * oo-ui-fieldLayout-align-inline
+ if ( $this->align ) {
+ $this->removeClasses( array( 'oo-ui-fieldLayout-align-' . $this->align ) );
+ }
+ $this->addClasses( array( 'oo-ui-fieldLayout-align-' . $value ) );
+ $this->align = $value;
+ }
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ $config['fieldWidget'] = $this->fieldWidget;
+ $config['align'] = $this->align;
+ if ( $this->help !== '' ) {
+ $config['help'] = $this->help->getTitle();
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php b/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php
new file mode 100644
index 00000000..f9faa353
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Layout made of a fieldset and optional legend.
+ *
+ * Just add FieldLayout items.
+ */
+class FieldsetLayout extends Layout {
+ /**
+ * @param array $config Configuration options
+ * @param FieldLayout[] $config['items'] Items to add
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new IconElement( $this, $config ) );
+ $this->mixin( new LabelElement( $this, $config ) );
+ $this->mixin( new GroupElement( $this, $config ) );
+
+ // Initialization
+ $this
+ ->addClasses( array( 'oo-ui-fieldsetLayout' ) )
+ ->prependContent( $this->icon, $this->label, $this->group );
+ if ( isset( $config['items'] ) ) {
+ $this->addItems( $config['items'] );
+ }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/layouts/FormLayout.php b/vendor/oojs/oojs-ui/php/layouts/FormLayout.php
new file mode 100644
index 00000000..ebeb89de
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/layouts/FormLayout.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Layout with an HTML form.
+ */
+class FormLayout extends Layout {
+
+ /* Static properties */
+
+ public static $tagName = 'form';
+
+ /**
+ * @param array $config Configuration options
+ * @param string $config['method'] HTML form `method` attribute
+ * @param string $config['action'] HTML form `action` attribute
+ * @param string $config['enctype'] HTML form `enctype` attribute
+ * @param FieldsetLayout[] $config['items'] Items to add
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new GroupElement( $this, array_merge( $config, array( 'group' => $this ) ) ) );
+
+ // Initialization
+ $attributeWhitelist = array( 'method', 'action', 'enctype' );
+ $this
+ ->addClasses( array( 'oo-ui-formLayout' ) )
+ ->setAttributes( array_intersect_key( $config, array_flip( $attributeWhitelist ) ) );
+ if ( isset( $config['items'] ) ) {
+ $this->addItems( $config['items'] );
+ }
+ }
+
+ public function getConfig( &$config ) {
+ foreach ( array( 'method', 'action', 'enctype' ) as $attr ) {
+ $value = $this->getAttribute( $attr );
+ if ( $value !== null ) {
+ $config[$attr] = $value;
+ }
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php b/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php
new file mode 100644
index 00000000..64931374
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Layout that expands to cover the entire area of its parent, with optional scrolling and padding.
+ */
+class PanelLayout extends Layout {
+ /**
+ * @param array $config Configuration options
+ * @param boolean $config['scrollable'] Allow vertical scrolling (default: false)
+ * @param boolean $config['padded'] Pad the content from the edges (default: false)
+ * @param boolean $config['expanded'] Expand size to fill the entire parent element
+ * (default: true)
+ * @param boolean $config['framed'] Wrap in a frame to visually separate from outside content
+ * (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Config initialization
+ $config = array_merge( array(
+ 'scrollable' => false,
+ 'padded' => false,
+ 'expanded' => true,
+ 'framed' => false,
+ ), $config );
+
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-panelLayout' ) );
+ if ( $config['scrollable'] ) {
+ $this->addClasses( array( 'oo-ui-panelLayout-scrollable' ) );
+ }
+ if ( $config['padded'] ) {
+ $this->addClasses( array( 'oo-ui-panelLayout-padded' ) );
+ }
+ if ( $config['expanded'] ) {
+ $this->addClasses( array( 'oo-ui-panelLayout-expanded' ) );
+ }
+ if ( $config['framed'] ) {
+ $this->addClasses( array( 'oo-ui-panelLayout-framed' ) );
+ }
+ }
+ public function getConfig( &$config ) {
+ if ( $this->hasClass( 'oo-ui-panelLayout-scrollable' ) ) {
+ $config['scrollable'] = true;
+ }
+ if ( $this->hasClass( 'oo-ui-panelLayout-padded' ) ) {
+ $config['padded'] = true;
+ }
+ if ( !$this->hasClass( 'oo-ui-panelLayout-expanded' ) ) {
+ $config['expanded'] = false;
+ }
+ if ( $this->hasClass( 'oo-ui-panelLayout-framed' ) ) {
+ $config['framed'] = true;
+ }
+ $config['content'] = $this->content;
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/themes/ApexTheme.php b/vendor/oojs/oojs-ui/php/themes/ApexTheme.php
new file mode 100644
index 00000000..e812736e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/themes/ApexTheme.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace OOUI;
+
+class ApexTheme extends Theme {
+}
diff --git a/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php b/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php
new file mode 100644
index 00000000..86e4c353
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace OOUI;
+
+class MediaWikiTheme extends Theme {
+
+ /* Methods */
+
+ public function getElementClasses( Element $element ) {
+ $variants = array(
+ 'warning' => false,
+ 'invert' => false,
+ 'progressive' => false,
+ 'constructive' => false,
+ 'destructive' => false
+ );
+
+ // Parent method
+ $classes = parent::getElementClasses( $element );
+
+ if ( $element->supports( array( 'hasFlag' ) ) ) {
+ $isFramed = $element->supports( array( 'isFramed' ) ) && $element->isFramed();
+ if ( $isFramed && ( $element->isDisabled() || $element->hasFlag( 'primary' ) ) ) {
+ $variants['invert'] = true;
+ } else {
+ $variants['progressive'] = $element->hasFlag( 'progressive' );
+ $variants['constructive'] = $element->hasFlag( 'constructive' );
+ $variants['destructive'] = $element->hasFlag( 'destructive' );
+ $variants['warning'] = $element->hasFlag( 'warning' );
+ }
+ }
+
+ foreach ( $variants as $variant => $toggle ) {
+ $classes[$toggle ? 'on' : 'off'][] = 'oo-ui-image-' . $variant;
+ }
+
+ return $classes;
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php
new file mode 100644
index 00000000..79d3aaa1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Group widget for multiple related buttons.
+ *
+ * Use together with ButtonWidget.
+ */
+class ButtonGroupWidget extends Widget {
+ /**
+ * @param array $config Configuration options
+ * @param ButtonWidget[] $config['items'] Buttons to add
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new GroupElement( $this, array_merge( $config, array( 'group' => $this ) ) ) );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-buttonGroupWidget' ) );
+ if ( isset( $config['items'] ) ) {
+ $this->addItems( $config['items'] );
+ }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php
new file mode 100644
index 00000000..b3bcb63b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * A button that is an input widget. Intended to be used within a FormLayout.
+ */
+class ButtonInputWidget extends InputWidget {
+ /* Properties */
+
+ /**
+ * Whether to use `<input/>` rather than `<button/>`.
+ *
+ * @var boolean
+ */
+ protected $useInputTag;
+
+ private $labelElementMixin;
+
+ /**
+ * @param array $config Configuration options
+ * @param string $config['type'] HTML tag `type` attribute, may be 'button', 'submit' or 'reset'
+ * (default: 'button')
+ * @param boolean $config['useInputTag'] Whether to use `<input/>` rather than `<button/>`. Only
+ * useful if you need IE 6 support in a form with multiple buttons. If you use this option,
+ * icons and indicators will not be displayed, it won't be possible to have a non-plaintext
+ * label, and it won't be possible to set a value (which will internally become identical to the
+ * label). (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Configuration initialization
+ $config = array_merge( array( 'type' => 'button', 'useInputTag' => false ), $config );
+
+ // Properties (must be set before parent constructor, which calls setValue())
+ $this->useInputTag = $config['useInputTag'];
+
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new ButtonElement( $this,
+ array_merge( $config, array( 'button' => $this->input ) ) ) );
+ $this->mixin( new IconElement( $this, $config ) );
+ $this->mixin( new IndicatorElement( $this, $config ) );
+ // HACK: We need to have access to the mixin to override the setLabel() method
+ $this->mixin( $this->labelElementMixin = new LabelElement( $this, $config ) );
+ $this->mixin( new TitledElement( $this,
+ array_merge( $config, array( 'titled' => $this->input ) ) ) );
+
+ // Initialization
+ if ( !$config['useInputTag'] ) {
+ $this->input->appendContent( $this->icon, $this->label, $this->indicator );
+ }
+
+ // HACK: This is done in LabelElement mixin, but doesn't call our overridden method because of
+ // how we implement mixins. Switching to traits will fix that.
+ $this->setLabel( isset( $config['label'] ) ? $config['label'] : null );
+
+ $this->addClasses( array( 'oo-ui-buttonInputWidget' ) );
+ }
+
+ protected function getInputElement( $config ) {
+ $input = new Tag( $config['useInputTag'] ? 'input' : 'button' );
+ $input->setAttributes( array( 'type' => $config['type'] ) );
+ return $input;
+ }
+
+ /**
+ * Set label value.
+ *
+ * Overridden to support setting the 'value' of `<input/>` elements.
+ *
+ * @param string|null $label Label text
+ * @chainable
+ */
+ public function setLabel( $label ) {
+ $this->labelElementMixin->setLabel( $label );
+
+ if ( $this->useInputTag ) {
+ // Discard non-plaintext labels
+ $label = is_string( $label ) ? $label : '';
+ $this->input->setValue( $label );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the value of the input.
+ *
+ * Overridden to disable for `<input/>` elements, which have value identical to the label.
+ *
+ * @param string $value New value
+ * @chainable
+ */
+ public function setValue( $value ) {
+ if ( !$this->useInputTag ) {
+ parent::setValue( $value );
+ }
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->useInputTag ) {
+ $config['useInputTag'] = true;
+ }
+ $config['type'] = $this->input->getAttribute( 'type' );
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php
new file mode 100644
index 00000000..f26608b1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Generic widget for buttons.
+ */
+class ButtonWidget extends Widget {
+
+ /**
+ * Hyperlink to visit when clicked.
+ *
+ * @var string
+ */
+ protected $href = null;
+
+ /**
+ * Target to open hyperlink in.
+ *
+ * @var string
+ */
+ protected $target = null;
+
+ /**
+ * Search engine traversal hint.
+ *
+ * True if search engines should avoid following this hyperlink.
+ *
+ * @var boolean
+ */
+ protected $noFollow = true;
+
+ /**
+ * @param array $config Configuration options
+ * @param string $config['href'] Hyperlink to visit when clicked
+ * @param string $config['target'] Target to open hyperlink in
+ * @param boolean $config['noFollow'] Search engine traversal hint (default: true)
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new ButtonElement( $this, $config ) );
+ $this->mixin( new IconElement( $this, $config ) );
+ $this->mixin( new IndicatorElement( $this, $config ) );
+ $this->mixin( new LabelElement( $this, $config ) );
+ $this->mixin( new TitledElement( $this,
+ array_merge( $config, array( 'titled' => $this->button ) ) ) );
+ $this->mixin( new FlaggedElement( $this, $config ) );
+ $this->mixin( new TabIndexedElement( $this,
+ array_merge( $config, array( 'tabIndexed' => $this->button ) ) ) );
+
+ // Initialization
+ $this->button->appendContent( $this->icon, $this->label, $this->indicator );
+ $this
+ ->addClasses( array( 'oo-ui-buttonWidget' ) )
+ ->appendContent( $this->button );
+
+ $this->setHref( isset( $config['href'] ) ? $config['href'] : null );
+ $this->setTarget( isset( $config['target'] ) ? $config['target'] : null );
+ $this->setNoFollow( isset( $config['noFollow'] ) ? $config['noFollow'] : true );
+ }
+
+ /**
+ * Get hyperlink location.
+ *
+ * @return string Hyperlink location
+ */
+ public function getHref() {
+ return $this->href;
+ }
+
+ /**
+ * Get hyperlink target.
+ *
+ * @return string Hyperlink target
+ */
+ public function getTarget() {
+ return $this->target;
+ }
+
+ /**
+ * Get search engine traversal hint.
+ *
+ * @return boolean Whether search engines should avoid traversing this hyperlink
+ */
+ public function getNoFollow() {
+ return $this->noFollow;
+ }
+
+ /**
+ * Set hyperlink location.
+ *
+ * @param string|null $href Hyperlink location, null to remove
+ */
+ public function setHref( $href ) {
+ $this->href = is_string( $href ) ? $href : null;
+
+ $this->updateHref();
+
+ return $this;
+ }
+
+ /**
+ * Update the href attribute, in case of changes to href or disabled
+ * state.
+ *
+ * @chainable
+ */
+ public function updateHref() {
+ if ( $this->href !== null && !$this->isDisabled() ) {
+ $this->button->setAttributes( array( 'href' => $this->href ) );
+ } else {
+ $this->button->removeAttributes( array( 'href' ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Set hyperlink target.
+ *
+ * @param string|null $target Hyperlink target, null to remove
+ */
+ public function setTarget( $target ) {
+ $this->target = is_string( $target ) ? $target : null;
+
+ if ( $this->target !== null ) {
+ $this->button->setAttributes( array( 'target' => $target ) );
+ } else {
+ $this->button->removeAttributes( array( 'target' ) );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set search engine traversal hint.
+ *
+ * @param boolean $noFollow True if search engines should avoid traversing this hyperlink
+ */
+ public function setNoFollow( $noFollow ) {
+ $this->noFollow = is_bool( $noFollow ) ? $noFollow : true;
+
+ if ( $this->noFollow ) {
+ $this->button->setAttributes( array( 'rel' => 'nofollow' ) );
+ } else {
+ $this->button->removeAttributes( array( 'rel' ) );
+ }
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->href !== null ) {
+ $config['href'] = $this->href;
+ }
+ if ( $this->target !== null ) {
+ $config['target'] = $this->target;
+ }
+ if ( $this->noFollow !== true ) {
+ $config['noFollow'] = $this->noFollow;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php
new file mode 100644
index 00000000..bda09c66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Checkbox input widget.
+ */
+class CheckboxInputWidget extends InputWidget {
+
+ /* Properties */
+
+ /**
+ * Whether the checkbox is selected.
+ *
+ * @var boolean
+ */
+ protected $selected;
+
+ /**
+ * @param array $config Configuration options
+ * @param boolean $config['selected'] Whether the checkbox is initially selected
+ * (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-checkboxInputWidget' ) );
+ $this->setSelected( isset( $config['selected'] ) ? $config['selected'] : false );
+ }
+
+ protected function getInputElement( $config ) {
+ $input = new Tag( 'input' );
+ $input->setAttributes( array( 'type' => 'checkbox' ) );
+ return $input;
+ }
+
+ /**
+ * Set selection state of this checkbox.
+ *
+ * @param boolean $state Whether the checkbox is selected
+ * @chainable
+ */
+ public function setSelected( $state ) {
+ $this->selected = (bool)$state;
+ if ( $this->selected ) {
+ $this->input->setAttributes( array( 'checked' => 'checked' ) );
+ } else {
+ $this->input->removeAttributes( array( 'checked' ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Check if this checkbox is selected.
+ *
+ * @return boolean Checkbox is selected
+ */
+ public function isSelected() {
+ return $this->selected;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->selected ) {
+ $config['selected'] = $this->selected;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php
new file mode 100644
index 00000000..ae541a66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Dropdown input widget, wrapping a `<select>` element. Intended to be used within a
+ * OO.ui.FormLayout.
+ */
+class DropdownInputWidget extends InputWidget {
+
+ /**
+ * HTML `<option>` tags for this widget.
+ * @var Tag[]
+ */
+ protected $options = array();
+
+ /**
+ * @param array $config Configuration options
+ * @param array[] $config['options'] Array of menu options in the format
+ * `array( 'data' => …, 'label' => … )`
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->setOptions( isset( $config['options'] ) ? $config['options'] : array() );
+ $this->addClasses( array( 'oo-ui-dropdownInputWidget' ) );
+ }
+
+ protected function getInputElement( $config ) {
+ return new Tag( 'select' );
+ }
+
+ public function setValue( $value ) {
+ $this->value = $this->cleanUpValue( $value );
+ foreach ( $this->options as &$opt ) {
+ if ( $opt->getAttribute( 'value' ) === $this->value ) {
+ $opt->setAttributes( array( 'selected' => 'selected' ) );
+ } else {
+ $opt->removeAttributes( array( 'selected' ) );
+ }
+ }
+ return $this;
+ }
+
+
+ /**
+ * Set the options available for this input.
+ *
+ * @param array[] $options Array of menu options in the format
+ * `array( 'data' => …, 'label' => … )`
+ * @chainable
+ */
+ public function setOptions( $options ) {
+ $value = $this->getValue();
+ $isValueAvailable = false;
+ $this->options = array();
+
+ // Rebuild the dropdown menu
+ $this->input->clearContent();
+ foreach ( $options as $opt ) {
+ $option = new Tag( 'option' );
+ $option->setAttributes( array( 'value' => $opt['data'] ) );
+ $option->appendContent( isset( $opt['label'] ) ? $opt['label'] : $opt['data'] );
+
+ if ( $value === $opt['data'] ) {
+ $isValueAvailable = true;
+ }
+
+ $this->options[] = $option;
+ $this->input->appendContent( $option );
+ }
+
+ // Restore the previous value, or reset to something sensible
+ if ( $isValueAvailable ) {
+ // Previous value is still available
+ $this->setValue( $value );
+ } else {
+ // No longer valid, reset
+ if ( count( $options ) ) {
+ $this->setValue( $options[0]['data'] );
+ }
+ }
+
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ $o = array();
+ foreach ( $this->options as $option ) {
+ $label = $option->content[0];
+ $data = $option->getAttribute( 'value' );
+ $o[] = array( 'data' => $data, 'label' => $label );
+ }
+ $config['options'] = $o;
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/IconWidget.php b/vendor/oojs/oojs-ui/php/widgets/IconWidget.php
new file mode 100644
index 00000000..f8273f37
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/IconWidget.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Icon widget.
+ *
+ * See IconElement for more information.
+ */
+class IconWidget extends Widget {
+
+ /* Static properties */
+
+ public static $tagName = 'span';
+
+ /**
+ * @param array $config Configuration options
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new IconElement( $this,
+ array_merge( $config, array( 'iconElement' => $this ) ) ) );
+ $this->mixin( new TitledElement( $this,
+ array_merge( $config, array( 'titled' => $this ) ) ) );
+ $this->mixin( new FlaggedElement( $this,
+ array_merge( $config, array( 'flagged' => $this ) ) ) );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-iconWidget' ) );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php b/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php
new file mode 100644
index 00000000..01f2055d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Indicator widget.
+ *
+ * See IndicatorElement for more information.
+ */
+class IndicatorWidget extends Widget {
+
+ /* Static properties */
+
+ public static $tagName = 'span';
+
+ /**
+ * @param array $config Configuration options
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new IndicatorElement( $this,
+ array_merge( $config, array( 'indicatorElement' => $this ) ) ) );
+ $this->mixin( new TitledElement( $this,
+ array_merge( $config, array( 'titled' => $this ) ) ) );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-indicatorWidget' ) );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/InputWidget.php b/vendor/oojs/oojs-ui/php/widgets/InputWidget.php
new file mode 100644
index 00000000..234d3145
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/InputWidget.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Base class for input widgets.
+ *
+ * @abstract
+ */
+class InputWidget extends Widget {
+
+ /* Properties */
+
+ /**
+ * Input element.
+ *
+ * @var Tag
+ */
+ protected $input;
+
+ /**
+ * Input value.
+ *
+ * @var string
+ */
+ protected $value = '';
+
+ /**
+ * @param array $config Configuration options
+ * @param string $config['name'] HTML input name (default: '')
+ * @param string $config['value'] Input value (default: '')
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Properties
+ $this->input = $this->getInputElement( $config );
+
+ // Mixins
+ $this->mixin( new FlaggedElement( $this,
+ array_merge( $config, array( 'flagged' => $this ) ) ) );
+ $this->mixin( new TabIndexedElement( $this,
+ array_merge( $config, array( 'tabIndexed' => $this->input ) ) ) );
+
+ // Initialization
+ if ( isset( $config['name'] ) ) {
+ $this->input->setAttributes( array( 'name' => $config['name'] ) );
+ }
+ if ( $this->isDisabled() ) {
+ $this->input->setAttributes( array( 'disabled' => 'disabled' ) );
+ }
+ $this
+ ->addClasses( array( 'oo-ui-inputWidget' ) )
+ ->appendContent( $this->input );
+ $this->appendContent( new Tag( 'span' ) );
+ $this->setValue( isset( $config['value'] ) ? $config['value'] : null );
+ }
+
+ /**
+ * Get input element.
+ *
+ * @param array $config Configuration options
+ * @return Tag Input element
+ */
+ protected function getInputElement( $config ) {
+ return new Tag( 'input' );
+ }
+
+ /**
+ * Get the value of the input.
+ *
+ * @return string Input value
+ */
+ public function getValue() {
+ return $this->value;
+ }
+
+ /**
+ * Sets the direction of the current input, either RTL or LTR
+ *
+ * @param boolean $isRTL
+ */
+ public function setRTL( $isRTL ) {
+ if ( $isRTL ) {
+ $this->input->removeClasses( array( 'oo-ui-ltr' ) );
+ $this->input->addClasses( array( 'oo-ui-rtl' ) );
+ } else {
+ $this->input->removeClasses( array( 'oo-ui-rtl' ) );
+ $this->input->addClasses( array( 'oo-ui-ltr' ) );
+ }
+ }
+
+ /**
+ * Set the value of the input.
+ *
+ * @param string $value New value
+ * @chainable
+ */
+ public function setValue( $value ) {
+ $this->value = $this->cleanUpValue( $value );
+ $this->input->setValue( $this->value );
+ return $this;
+ }
+
+ /**
+ * Clean up incoming value.
+ *
+ * Ensures value is a string, and converts null to empty string.
+ *
+ * @param string $value Original value
+ * @return string Cleaned up value
+ */
+ protected function cleanUpValue( $value ) {
+ if ( $value === null ) {
+ return '';
+ } else {
+ return (string)$value;
+ }
+ }
+
+ public function setDisabled( $state ) {
+ parent::setDisabled( $state );
+ if ( isset( $this->input ) ) {
+ if ( $this->isDisabled() ) {
+ $this->input->setAttributes( array( 'disabled' => 'disabled' ) );
+ } else {
+ $this->input->removeAttributes( array( 'disabled' ) );
+ }
+ }
+ return $this;
+ }
+
+ public function getConfig( &$config ) {
+ $name = $this->input->getAttribute( 'name' );
+ if ( $name !== null ) {
+ $config['name'] = $name;
+ }
+ if ( $this->value !== '' ) {
+ $config['value'] = $this->value;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php b/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php
new file mode 100644
index 00000000..b59a5f25
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Label widget.
+ */
+class LabelWidget extends Widget {
+
+ /* Static properties */
+
+ public static $tagName = 'span';
+
+ /* Properties */
+
+ /**
+ * Associated input element.
+ *
+ * @var InputWidget|null
+ */
+ protected $input;
+
+ /**
+ * @param array $config Configuration options
+ * @param InputWidget $config['input'] Input widget this label is for
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Mixins
+ $this->mixin( new LabelElement( $this,
+ array_merge( $config, array( 'labelElement' => $this ) ) ) );
+
+ // Properties
+ $this->input = isset( $config['input'] ) ? $config['input'] : null;
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-labelWidget' ) );
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->input !== null ) {
+ $config['input'] = $this->input;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php
new file mode 100644
index 00000000..26da29d0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Radio input widget.
+ */
+class RadioInputWidget extends InputWidget {
+
+ /**
+ * @param array $config Configuration options
+ * @param boolean $config['selected'] Whether the radio button is initially selected
+ * (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Initialization
+ $this->addClasses( array( 'oo-ui-radioInputWidget' ) );
+ $this->setSelected( isset( $config['selected'] ) ? $config['selected'] : false );
+ }
+
+ protected function getInputElement( $config ) {
+ $input = new Tag( 'input' );
+ $input->setAttributes( array( 'type' => 'radio' ) );
+ return $input;
+ }
+
+ /**
+ * Set selection state of this radio button.
+ *
+ * @param boolean $state Whether the button is selected
+ */
+ public function setSelected( $state ) {
+ // RadioInputWidget doesn't track its state.
+ if ( $state ) {
+ $this->input->setAttributes( array( 'checked' => 'checked' ) );
+ } else {
+ $this->input->removeAttributes( array( 'checked' ) );
+ }
+ return $this;
+ }
+
+ /**
+ * Check if this radio button is selected.
+ *
+ * @return boolean Radio is selected
+ */
+ public function isSelected() {
+ return $this->input->getAttribute( 'checked' ) === 'checked';
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->isSelected() ) {
+ $config['selected'] = true;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php
new file mode 100644
index 00000000..a5f31f74
--- /dev/null
+++ b/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace OOUI;
+
+/**
+ * Input widget with a text field.
+ */
+class TextInputWidget extends InputWidget {
+
+ /* Properties */
+
+ /**
+ * Prevent changes.
+ *
+ * @var boolean
+ */
+ protected $readOnly = false;
+
+ /**
+ * Allow multiple lines of text.
+ *
+ * @var boolean
+ */
+ protected $multiline = false;
+
+ /**
+ * @param array $config Configuration options
+ * @param string $config['type'] HTML tag `type` attribute (default: 'text')
+ * @param string $config['placeholder'] Placeholder text
+ * @param boolean $config['autofocus'] Ask the browser to focus this widget, using the 'autofocus'
+ * HTML attribute (default: false)
+ * @param boolean $config['readOnly'] Prevent changes (default: false)
+ * @param number $config['maxLength'] Maximum allowed number of characters to input
+ * @param boolean $config['multiline'] Allow multiple lines of text (default: false)
+ * @param boolean $config['required'] Mark the field as required (default: false)
+ */
+ public function __construct( array $config = array() ) {
+ // Config initialization
+ $config = array_merge( array(
+ 'readOnly' => false,
+ 'autofocus' => false,
+ 'required' => false,
+ ), $config );
+
+ // Parent constructor
+ parent::__construct( $config );
+
+ // Properties
+ $this->multiline = isset( $config['multiline'] ) ? (bool)$config['multiline'] : false;
+
+ // Mixins
+ $this->mixin( new IconElement( $this, $config ) );
+ $this->mixin( new IndicatorElement( $this, $config ) );
+
+ // Initialization
+ $this
+ ->addClasses( array( 'oo-ui-textInputWidget' ) )
+ ->appendContent( $this->icon, $this->indicator );
+ $this->setReadOnly( $config['readOnly'] );
+ if ( isset( $config['placeholder'] ) ) {
+ $this->input->setAttributes( array( 'placeholder' => $config['placeholder'] ) );
+ }
+ if ( isset( $config['maxLength'] ) ) {
+ $this->input->setAttributes( array( 'maxlength' => $config['maxLength'] ) );
+ }
+ if ( $config['autofocus'] ) {
+ $this->input->setAttributes( array( 'autofocus' => 'autofocus' ) );
+ }
+ if ( $config['required'] ) {
+ $this->input->setAttributes( array( 'required' => 'required', 'aria-required' => 'true' ) );
+ }
+ }
+
+ /**
+ * Check if the widget is read-only.
+ *
+ * @return boolean
+ */
+ public function isReadOnly() {
+ return $this->readOnly;
+ }
+
+ /**
+ * Set the read-only state of the widget. This should probably change the widget's appearance and
+ * prevent it from being used.
+ *
+ * @param boolean $state Make input read-only
+ * @chainable
+ */
+ public function setReadOnly( $state ) {
+ $this->readOnly = (bool)$state;
+ if ( $this->readOnly ) {
+ $this->input->setAttributes( array( 'readonly' => 'readonly' ) );
+ } else {
+ $this->input->removeAttributes( array( 'readonly' ) );
+ }
+ return $this;
+ }
+
+ protected function getInputElement( $config ) {
+ if ( isset( $config['multiline'] ) && $config['multiline'] ) {
+ return new Tag( 'textarea' );
+ } else {
+ $type = isset( $config['type'] ) ? $config['type'] : 'text';
+ $input = new Tag( 'input' );
+ $input->setAttributes( array( 'type' => $type ) );
+ return $input;
+ }
+ }
+
+ /**
+ * Check if input supports multiple lines.
+ *
+ * @return boolean
+ */
+ public function isMultiline() {
+ return (bool)$this->multiline;
+ }
+
+ public function getConfig( &$config ) {
+ if ( $this->isMultiline() ) {
+ $config['multiline'] = true;
+ } else {
+ $type = $this->input->getAttribute( 'type' );
+ if ( $type !== 'text' ) {
+ $config['type'] = $type;
+ }
+ }
+ if ( $this->isReadOnly() ) {
+ $config['readOnly'] = true;
+ }
+ $placeholder = $this->input->getAttribute( 'placeholder' );
+ if ( $placeholder !== null ) {
+ $config['placeholder'] = $placeholder;
+ }
+ $maxlength = $this->input->getAttribute( 'maxlength' );
+ if ( $maxlength !== null ) {
+ $config['maxLength'] = $maxlength;
+ }
+ $autofocus = $this->input->getAttribute( 'autofocus' );
+ if ( $autofocus !== null ) {
+ $config['autofocus'] = true;
+ }
+ $required = $this->input->getAttribute( 'required' );
+ $ariarequired = $this->input->getAttribute( 'aria-required' );
+ if ( ( $required !== null ) || ( $ariarequired !== null ) ) {
+ $config['required'] = true;
+ }
+ return parent::getConfig( $config );
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/ActionSet.js b/vendor/oojs/oojs-ui/src/ActionSet.js
new file mode 100644
index 00000000..875ca605
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/ActionSet.js
@@ -0,0 +1,504 @@
+/**
+ * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them.
+ * Actions can be made available for specific contexts (modes) and circumstances
+ * (abilities). Action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ *
+ * ActionSets contain two types of actions:
+ *
+ * - Special: Special actions are the first visible actions with special flags, such as 'safe' and 'primary', the default special flags. Additional special flags can be configured in subclasses with the static #specialFlags property.
+ * - Other: Other actions include all non-special visible actions.
+ *
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ * @example
+ * // Example: An action set used in a process dialog
+ * function MyProcessDialog( config ) {
+ * MyProcessDialog.super.call( this, config );
+ * }
+ * OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
+ * MyProcessDialog.static.title = 'An action set in a process dialog';
+ * // An action set that uses modes ('edit' and 'help' mode, in this example).
+ * MyProcessDialog.static.actions = [
+ * { action: 'continue', modes: 'edit', label: 'Continue', flags: [ 'primary', 'constructive' ] },
+ * { action: 'help', modes: 'edit', label: 'Help' },
+ * { modes: 'edit', label: 'Cancel', flags: 'safe' },
+ * { action: 'back', modes: 'help', label: 'Back', flags: 'safe' }
+ * ];
+ *
+ * MyProcessDialog.prototype.initialize = function () {
+ * MyProcessDialog.super.prototype.initialize.apply( this, arguments );
+ * this.panel1 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ * this.panel1.$element.append( '<p>This dialog uses an action set (continue, help, cancel, back) configured with modes. This is edit mode. Click \'help\' to see help mode.</p>' );
+ * this.panel2 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ * this.panel2.$element.append( '<p>This is help mode. Only the \'back\' action widget is configured to be visible here. Click \'back\' to return to \'edit\' mode.</p>' );
+ * this.stackLayout = new OO.ui.StackLayout( {
+ * items: [ this.panel1, this.panel2 ]
+ * } );
+ * this.$body.append( this.stackLayout.$element );
+ * };
+ * MyProcessDialog.prototype.getSetupProcess = function ( data ) {
+ * return MyProcessDialog.super.prototype.getSetupProcess.call( this, data )
+ * .next( function () {
+ * this.actions.setMode( 'edit' );
+ * }, this );
+ * };
+ * MyProcessDialog.prototype.getActionProcess = function ( action ) {
+ * if ( action === 'help' ) {
+ * this.actions.setMode( 'help' );
+ * this.stackLayout.setItem( this.panel2 );
+ * } else if ( action === 'back' ) {
+ * this.actions.setMode( 'edit' );
+ * this.stackLayout.setItem( this.panel1 );
+ * } else if ( action === 'continue' ) {
+ * var dialog = this;
+ * return new OO.ui.Process( function () {
+ * dialog.close();
+ * } );
+ * }
+ * return MyProcessDialog.super.prototype.getActionProcess.call( this, action );
+ * };
+ * MyProcessDialog.prototype.getBodyHeight = function () {
+ * return this.panel1.$element.outerHeight( true );
+ * };
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ * var dialog = new MyProcessDialog( {
+ * size: 'medium'
+ * } );
+ * windowManager.addWindows( [ dialog ] );
+ * windowManager.openWindow( dialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @abstract
+ * @class
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ActionSet = function OoUiActionSet( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
+ // Properties
+ this.list = [];
+ this.categories = {
+ actions: 'getAction',
+ flags: 'getFlags',
+ modes: 'getModes'
+ };
+ this.categorized = {};
+ this.special = {};
+ this.others = [];
+ this.organized = false;
+ this.changing = false;
+ this.changed = false;
+};
+
+/* Setup */
+
+OO.mixinClass( OO.ui.ActionSet, OO.EventEmitter );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the flags used to identify special actions. Special actions are displayed in the
+ * header of a {@link OO.ui.ProcessDialog process dialog}.
+ * See the [OOjs UI documentation on MediaWiki][2] for more information and examples.
+ *
+ * [2]:https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.ActionSet.static.specialFlags = [ 'safe', 'primary' ];
+
+/* Events */
+
+/**
+ * @event click
+ *
+ * A 'click' event is emitted when an action is clicked.
+ *
+ * @param {OO.ui.ActionWidget} action Action that was clicked
+ */
+
+/**
+ * @event resize
+ *
+ * A 'resize' event is emitted when an action widget is resized.
+ *
+ * @param {OO.ui.ActionWidget} action Action that was resized
+ */
+
+/**
+ * @event add
+ *
+ * An 'add' event is emitted when actions are {@link #method-add added} to the action set.
+ *
+ * @param {OO.ui.ActionWidget[]} added Actions added
+ */
+
+/**
+ * @event remove
+ *
+ * A 'remove' event is emitted when actions are {@link #method-remove removed}
+ * or {@link #clear cleared}.
+ *
+ * @param {OO.ui.ActionWidget[]} added Actions removed
+ */
+
+/**
+ * @event change
+ *
+ * A 'change' event is emitted when actions are {@link #method-add added}, {@link #clear cleared},
+ * or {@link #method-remove removed} from the action set or when the {@link #setMode mode} is changed.
+ *
+ */
+
+/* Methods */
+
+/**
+ * Handle action change events.
+ *
+ * @private
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.onActionChange = function () {
+ this.organized = false;
+ if ( this.changing ) {
+ this.changed = true;
+ } else {
+ this.emit( 'change' );
+ }
+};
+
+/**
+ * Check if an action is one of the special actions.
+ *
+ * @param {OO.ui.ActionWidget} action Action to check
+ * @return {boolean} Action is special
+ */
+OO.ui.ActionSet.prototype.isSpecial = function ( action ) {
+ var flag;
+
+ for ( flag in this.special ) {
+ if ( action === this.special[ flag ] ) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Get action widgets based on the specified filter: ‘actions’, ‘flags’, ‘modes’, ‘visible’,
+ * or ‘disabled’.
+ *
+ * @param {Object} [filters] Filters to use, omit to get all actions
+ * @param {string|string[]} [filters.actions] Actions that action widgets must have
+ * @param {string|string[]} [filters.flags] Flags that action widgets must have (e.g., 'safe')
+ * @param {string|string[]} [filters.modes] Modes that action widgets must have
+ * @param {boolean} [filters.visible] Action widgets must be visible
+ * @param {boolean} [filters.disabled] Action widgets must be disabled
+ * @return {OO.ui.ActionWidget[]} Action widgets matching all criteria
+ */
+OO.ui.ActionSet.prototype.get = function ( filters ) {
+ var i, len, list, category, actions, index, match, matches;
+
+ if ( filters ) {
+ this.organize();
+
+ // Collect category candidates
+ matches = [];
+ for ( category in this.categorized ) {
+ list = filters[ category ];
+ if ( list ) {
+ if ( !Array.isArray( list ) ) {
+ list = [ list ];
+ }
+ for ( i = 0, len = list.length; i < len; i++ ) {
+ actions = this.categorized[ category ][ list[ i ] ];
+ if ( Array.isArray( actions ) ) {
+ matches.push.apply( matches, actions );
+ }
+ }
+ }
+ }
+ // Remove by boolean filters
+ for ( i = 0, len = matches.length; i < len; i++ ) {
+ match = matches[ i ];
+ if (
+ ( filters.visible !== undefined && match.isVisible() !== filters.visible ) ||
+ ( filters.disabled !== undefined && match.isDisabled() !== filters.disabled )
+ ) {
+ matches.splice( i, 1 );
+ len--;
+ i--;
+ }
+ }
+ // Remove duplicates
+ for ( i = 0, len = matches.length; i < len; i++ ) {
+ match = matches[ i ];
+ index = matches.lastIndexOf( match );
+ while ( index !== i ) {
+ matches.splice( index, 1 );
+ len--;
+ index = matches.lastIndexOf( match );
+ }
+ }
+ return matches;
+ }
+ return this.list.slice();
+};
+
+/**
+ * Get 'special' actions.
+ *
+ * Special actions are the first visible action widgets with special flags, such as 'safe' and 'primary'.
+ * Special flags can be configured in subclasses by changing the static #specialFlags property.
+ *
+ * @return {OO.ui.ActionWidget[]|null} 'Special' action widgets.
+ */
+OO.ui.ActionSet.prototype.getSpecial = function () {
+ this.organize();
+ return $.extend( {}, this.special );
+};
+
+/**
+ * Get 'other' actions.
+ *
+ * Other actions include all non-special visible action widgets.
+ *
+ * @return {OO.ui.ActionWidget[]} 'Other' action widgets
+ */
+OO.ui.ActionSet.prototype.getOthers = function () {
+ this.organize();
+ return this.others.slice();
+};
+
+/**
+ * Set the mode (e.g., ‘edit’ or ‘view’). Only {@link OO.ui.ActionWidget#modes actions} configured
+ * to be available in the specified mode will be made visible. All other actions will be hidden.
+ *
+ * @param {string} mode The mode. Only actions configured to be available in the specified
+ * mode will be made visible.
+ * @chainable
+ * @fires toggle
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.setMode = function ( mode ) {
+ var i, len, action;
+
+ this.changing = true;
+ for ( i = 0, len = this.list.length; i < len; i++ ) {
+ action = this.list[ i ];
+ action.toggle( action.hasMode( mode ) );
+ }
+
+ this.organized = false;
+ this.changing = false;
+ this.emit( 'change' );
+
+ return this;
+};
+
+/**
+ * Set the abilities of the specified actions.
+ *
+ * Action widgets that are configured with the specified actions will be enabled
+ * or disabled based on the boolean values specified in the `actions`
+ * parameter.
+ *
+ * @param {Object.<string,boolean>} actions A list keyed by action name with boolean
+ * values that indicate whether or not the action should be enabled.
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.setAbilities = function ( actions ) {
+ var i, len, action, item;
+
+ for ( i = 0, len = this.list.length; i < len; i++ ) {
+ item = this.list[ i ];
+ action = item.getAction();
+ if ( actions[ action ] !== undefined ) {
+ item.setDisabled( !actions[ action ] );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Executes a function once per action.
+ *
+ * When making changes to multiple actions, use this method instead of iterating over the actions
+ * manually to defer emitting a #change event until after all actions have been changed.
+ *
+ * @param {Object|null} actions Filters to use to determine which actions to iterate over; see #get
+ * @param {Function} callback Callback to run for each action; callback is invoked with three
+ * arguments: the action, the action's index, the list of actions being iterated over
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.forEach = function ( filter, callback ) {
+ this.changed = false;
+ this.changing = true;
+ this.get( filter ).forEach( callback );
+ this.changing = false;
+ if ( this.changed ) {
+ this.emit( 'change' );
+ }
+
+ return this;
+};
+
+/**
+ * Add action widgets to the action set.
+ *
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to add
+ * @chainable
+ * @fires add
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.add = function ( actions ) {
+ var i, len, action;
+
+ this.changing = true;
+ for ( i = 0, len = actions.length; i < len; i++ ) {
+ action = actions[ i ];
+ action.connect( this, {
+ click: [ 'emit', 'click', action ],
+ resize: [ 'emit', 'resize', action ],
+ toggle: [ 'onActionChange' ]
+ } );
+ this.list.push( action );
+ }
+ this.organized = false;
+ this.emit( 'add', actions );
+ this.changing = false;
+ this.emit( 'change' );
+
+ return this;
+};
+
+/**
+ * Remove action widgets from the set.
+ *
+ * To remove all actions, you may wish to use the #clear method instead.
+ *
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to remove
+ * @chainable
+ * @fires remove
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.remove = function ( actions ) {
+ var i, len, index, action;
+
+ this.changing = true;
+ for ( i = 0, len = actions.length; i < len; i++ ) {
+ action = actions[ i ];
+ index = this.list.indexOf( action );
+ if ( index !== -1 ) {
+ action.disconnect( this );
+ this.list.splice( index, 1 );
+ }
+ }
+ this.organized = false;
+ this.emit( 'remove', actions );
+ this.changing = false;
+ this.emit( 'change' );
+
+ return this;
+};
+
+/**
+ * Remove all action widets from the set.
+ *
+ * To remove only specified actions, use the {@link #method-remove remove} method instead.
+ *
+ * @chainable
+ * @fires remove
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.clear = function () {
+ var i, len, action,
+ removed = this.list.slice();
+
+ this.changing = true;
+ for ( i = 0, len = this.list.length; i < len; i++ ) {
+ action = this.list[ i ];
+ action.disconnect( this );
+ }
+
+ this.list = [];
+
+ this.organized = false;
+ this.emit( 'remove', removed );
+ this.changing = false;
+ this.emit( 'change' );
+
+ return this;
+};
+
+/**
+ * Organize actions.
+ *
+ * This is called whenever organized information is requested. It will only reorganize the actions
+ * if something has changed since the last time it ran.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.organize = function () {
+ var i, iLen, j, jLen, flag, action, category, list, item, special,
+ specialFlags = this.constructor.static.specialFlags;
+
+ if ( !this.organized ) {
+ this.categorized = {};
+ this.special = {};
+ this.others = [];
+ for ( i = 0, iLen = this.list.length; i < iLen; i++ ) {
+ action = this.list[ i ];
+ if ( action.isVisible() ) {
+ // Populate categories
+ for ( category in this.categories ) {
+ if ( !this.categorized[ category ] ) {
+ this.categorized[ category ] = {};
+ }
+ list = action[ this.categories[ category ] ]();
+ if ( !Array.isArray( list ) ) {
+ list = [ list ];
+ }
+ for ( j = 0, jLen = list.length; j < jLen; j++ ) {
+ item = list[ j ];
+ if ( !this.categorized[ category ][ item ] ) {
+ this.categorized[ category ][ item ] = [];
+ }
+ this.categorized[ category ][ item ].push( action );
+ }
+ }
+ // Populate special/others
+ special = false;
+ for ( j = 0, jLen = specialFlags.length; j < jLen; j++ ) {
+ flag = specialFlags[ j ];
+ if ( !this.special[ flag ] && action.hasFlag( flag ) ) {
+ this.special[ flag ] = action;
+ special = true;
+ break;
+ }
+ }
+ if ( !special ) {
+ this.others.push( action );
+ }
+ }
+ }
+ this.organized = true;
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/Dialog.js b/vendor/oojs/oojs-ui/src/Dialog.js
new file mode 100644
index 00000000..33226038
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Dialog.js
@@ -0,0 +1,323 @@
+/**
+ * The Dialog class serves as the base class for the other types of dialogs.
+ * Unless extended to include controls, the rendered dialog box is a simple window
+ * that users can close by hitting the ‘Esc’ key. Dialog windows are used with OO.ui.WindowManager,
+ * which opens, closes, and controls the presentation of the window. See the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * @example
+ * // A simple dialog window.
+ * function MyDialog( config ) {
+ * MyDialog.super.call( this, config );
+ * }
+ * OO.inheritClass( MyDialog, OO.ui.Dialog );
+ * MyDialog.prototype.initialize = function () {
+ * MyDialog.super.prototype.initialize.call( this );
+ * this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ * this.content.$element.append( '<p>A simple dialog window. Press \'Esc\' to close.</p>' );
+ * this.$body.append( this.content.$element );
+ * };
+ * MyDialog.prototype.getBodyHeight = function () {
+ * return this.content.$element.outerHeight( true );
+ * };
+ * var myDialog = new MyDialog( {
+ * size: 'medium'
+ * } );
+ * // Create and append a window manager, which opens and closes the window.
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ * windowManager.addWindows( [ myDialog ] );
+ * // Open the window!
+ * windowManager.openWindow( myDialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Dialogs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Window
+ * @mixins OO.ui.PendingElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Dialog = function OoUiDialog( config ) {
+ // Parent constructor
+ OO.ui.Dialog.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.PendingElement.call( this );
+
+ // Properties
+ this.actions = new OO.ui.ActionSet();
+ this.attachedActions = [];
+ this.currentAction = null;
+ this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
+
+ // Events
+ this.actions.connect( this, {
+ click: 'onActionClick',
+ resize: 'onActionResize',
+ change: 'onActionsChange'
+ } );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-dialog' )
+ .attr( 'role', 'dialog' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Dialog, OO.ui.Window );
+OO.mixinClass( OO.ui.Dialog, OO.ui.PendingElement );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of dialog.
+ *
+ * The dialog class must have a symbolic name in order to be registered with OO.Factory.
+ * Please see the [OOjs UI documentation on MediaWiki] [3] for more information.
+ *
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Dialog.static.name = '';
+
+/**
+ * The dialog title.
+ *
+ * The title can be specified as a plaintext string, a {@link OO.ui.LabelElement Label} node, or a function
+ * that will produce a Label node or string. The title can also be specified with data passed to the
+ * constructor (see #getSetupProcess). In this case, the static value will be overriden.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function}
+ */
+OO.ui.Dialog.static.title = '';
+
+/**
+ * An array of configured {@link OO.ui.ActionWidget action widgets}.
+ *
+ * Actions can also be specified with data passed to the constructor (see #getSetupProcess). In this case, the static
+ * value will be overriden.
+ *
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @static
+ * @inheritable
+ * @property {Object[]}
+ */
+OO.ui.Dialog.static.actions = [];
+
+/**
+ * Close the dialog when the 'Esc' key is pressed.
+ *
+ * @static
+ * @abstract
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Dialog.static.escapable = true;
+
+/* Methods */
+
+/**
+ * Handle frame document key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.Dialog.prototype.onDocumentKeyDown = function ( e ) {
+ if ( e.which === OO.ui.Keys.ESCAPE ) {
+ this.close();
+ e.preventDefault();
+ e.stopPropagation();
+ }
+};
+
+/**
+ * Handle action resized events.
+ *
+ * @private
+ * @param {OO.ui.ActionWidget} action Action that was resized
+ */
+OO.ui.Dialog.prototype.onActionResize = function () {
+ // Override in subclass
+};
+
+/**
+ * Handle action click events.
+ *
+ * @private
+ * @param {OO.ui.ActionWidget} action Action that was clicked
+ */
+OO.ui.Dialog.prototype.onActionClick = function ( action ) {
+ if ( !this.isPending() ) {
+ this.executeAction( action.getAction() );
+ }
+};
+
+/**
+ * Handle actions change event.
+ *
+ * @private
+ */
+OO.ui.Dialog.prototype.onActionsChange = function () {
+ this.detachActions();
+ if ( !this.isClosing() ) {
+ this.attachActions();
+ }
+};
+
+/**
+ * Get the set of actions used by the dialog.
+ *
+ * @return {OO.ui.ActionSet}
+ */
+OO.ui.Dialog.prototype.getActions = function () {
+ return this.actions;
+};
+
+/**
+ * Get a process for taking action.
+ *
+ * When you override this method, you can create a new OO.ui.Process and return it, or add additional
+ * accept steps to the process the parent method provides using the {@link OO.ui.Process#first 'first'}
+ * and {@link OO.ui.Process#next 'next'} methods of OO.ui.Process.
+ *
+ * @abstract
+ * @param {string} [action] Symbolic name of action
+ * @return {OO.ui.Process} Action process
+ */
+OO.ui.Dialog.prototype.getActionProcess = function ( action ) {
+ return new OO.ui.Process()
+ .next( function () {
+ if ( !action ) {
+ // An empty action always closes the dialog without data, which should always be
+ // safe and make no changes
+ this.close();
+ }
+ }, this );
+};
+
+/**
+ * @inheritdoc
+ *
+ * @param {Object} [data] Dialog opening data
+ * @param {jQuery|string|Function|null} [data.title] Dialog title, omit to use
+ * the {@link #static-title static title}
+ * @param {Object[]} [data.actions] List of configuration options for each
+ * {@link OO.ui.ActionWidget action widget}, omit to use {@link #static-actions static actions}.
+ */
+OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
+ data = data || {};
+
+ // Parent method
+ return OO.ui.Dialog.super.prototype.getSetupProcess.call( this, data )
+ .next( function () {
+ var config = this.constructor.static,
+ actions = data.actions !== undefined ? data.actions : config.actions;
+
+ this.title.setLabel(
+ data.title !== undefined ? data.title : this.constructor.static.title
+ );
+ this.actions.add( this.getActionWidgets( actions ) );
+
+ if ( this.constructor.static.escapable ) {
+ this.$document.on( 'keydown', this.onDocumentKeyDownHandler );
+ }
+ }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.Dialog.prototype.getTeardownProcess = function ( data ) {
+ // Parent method
+ return OO.ui.Dialog.super.prototype.getTeardownProcess.call( this, data )
+ .first( function () {
+ if ( this.constructor.static.escapable ) {
+ this.$document.off( 'keydown', this.onDocumentKeyDownHandler );
+ }
+
+ this.actions.clear();
+ this.currentAction = null;
+ }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.Dialog.prototype.initialize = function () {
+ // Parent method
+ OO.ui.Dialog.super.prototype.initialize.call( this );
+
+ // Properties
+ this.title = new OO.ui.LabelWidget();
+
+ // Initialization
+ this.$content.addClass( 'oo-ui-dialog-content' );
+ this.setPendingElement( this.$head );
+};
+
+/**
+ * Get action widgets from a list of configs
+ *
+ * @param {Object[]} actions Action widget configs
+ * @return {OO.ui.ActionWidget[]} Action widgets
+ */
+OO.ui.Dialog.prototype.getActionWidgets = function ( actions ) {
+ var i, len, widgets = [];
+ for ( i = 0, len = actions.length; i < len; i++ ) {
+ widgets.push(
+ new OO.ui.ActionWidget( actions[ i ] )
+ );
+ }
+ return widgets;
+};
+
+/**
+ * Attach action actions.
+ *
+ * @protected
+ */
+OO.ui.Dialog.prototype.attachActions = function () {
+ // Remember the list of potentially attached actions
+ this.attachedActions = this.actions.get();
+};
+
+/**
+ * Detach action actions.
+ *
+ * @protected
+ * @chainable
+ */
+OO.ui.Dialog.prototype.detachActions = function () {
+ var i, len;
+
+ // Detach all actions that may have been previously attached
+ for ( i = 0, len = this.attachedActions.length; i < len; i++ ) {
+ this.attachedActions[ i ].$element.detach();
+ }
+ this.attachedActions = [];
+};
+
+/**
+ * Execute an action.
+ *
+ * @param {string} action Symbolic name of action to execute
+ * @return {jQuery.Promise} Promise resolved when action completes, rejected if it fails
+ */
+OO.ui.Dialog.prototype.executeAction = function ( action ) {
+ this.pushPending();
+ this.currentAction = action;
+ return this.getActionProcess( action ).execute()
+ .always( this.popPending.bind( this ) );
+};
diff --git a/vendor/oojs/oojs-ui/src/Element.js b/vendor/oojs/oojs-ui/src/Element.js
new file mode 100644
index 00000000..127eb503
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Element.js
@@ -0,0 +1,748 @@
+/**
+ * Each Element represents a rendering in the DOM—a button or an icon, for example, or anything
+ * that is visible to a user. Unlike {@link OO.ui.Widget widgets}, plain elements usually do not have events
+ * connected to them and can't be interacted with.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string[]} [classes] The names of the CSS classes to apply to the element. CSS styles are added
+ * to the top level (e.g., the outermost div) of the element. See the [OOjs UI documentation on MediaWiki][2]
+ * for an example.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#cssExample
+ * @cfg {string} [id] The HTML id attribute used in the rendered tag.
+ * @cfg {string} [text] Text to insert
+ * @cfg {Array} [content] An array of content elements to append (after #text).
+ * Strings will be html-escaped; use an OO.ui.HtmlSnippet to append raw HTML.
+ * Instances of OO.ui.Element will have their $element appended.
+ * @cfg {jQuery} [$content] Content elements to append (after #text)
+ * @cfg {Mixed} [data] Custom data of any type or combination of types (e.g., string, number, array, object).
+ * Data can also be specified with the #setData method.
+ */
+OO.ui.Element = function OoUiElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$ = $;
+ this.visible = true;
+ this.data = config.data;
+ this.$element = config.$element ||
+ $( document.createElement( this.getTagName() ) );
+ this.elementGroup = null;
+ this.debouncedUpdateThemeClassesHandler = this.debouncedUpdateThemeClasses.bind( this );
+ this.updateThemeClassesPending = false;
+
+ // Initialization
+ if ( Array.isArray( config.classes ) ) {
+ this.$element.addClass( config.classes.join( ' ' ) );
+ }
+ if ( config.id ) {
+ this.$element.attr( 'id', config.id );
+ }
+ if ( config.text ) {
+ this.$element.text( config.text );
+ }
+ if ( config.content ) {
+ // The `content` property treats plain strings as text; use an
+ // HtmlSnippet to append HTML content. `OO.ui.Element`s get their
+ // appropriate $element appended.
+ this.$element.append( config.content.map( function ( v ) {
+ if ( typeof v === 'string' ) {
+ // Escape string so it is properly represented in HTML.
+ return document.createTextNode( v );
+ } else if ( v instanceof OO.ui.HtmlSnippet ) {
+ // Bypass escaping.
+ return v.toString();
+ } else if ( v instanceof OO.ui.Element ) {
+ return v.$element;
+ }
+ return v;
+ } ) );
+ }
+ if ( config.$content ) {
+ // The `$content` property treats plain strings as HTML.
+ this.$element.append( config.$content );
+ }
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Element );
+
+/* Static Properties */
+
+/**
+ * The name of the HTML tag used by the element.
+ *
+ * The static value may be ignored if the #getTagName method is overridden.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Element.static.tagName = 'div';
+
+/* Static Methods */
+
+/**
+ * Reconstitute a JavaScript object corresponding to a widget created
+ * by the PHP implementation.
+ *
+ * @param {string|HTMLElement|jQuery} idOrNode
+ * A DOM id (if a string) or node for the widget to infuse.
+ * @return {OO.ui.Element}
+ * The `OO.ui.Element` corresponding to this (infusable) document node.
+ * For `Tag` objects emitted on the HTML side (used occasionally for content)
+ * the value returned is a newly-created Element wrapping around the existing
+ * DOM node.
+ */
+OO.ui.Element.static.infuse = function ( idOrNode ) {
+ var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, true );
+ // Verify that the type matches up.
+ // FIXME: uncomment after T89721 is fixed (see T90929)
+ /*
+ if ( !( obj instanceof this['class'] ) ) {
+ throw new Error( 'Infusion type mismatch!' );
+ }
+ */
+ return obj;
+};
+
+/**
+ * Implementation helper for `infuse`; skips the type check and has an
+ * extra property so that only the top-level invocation touches the DOM.
+ * @private
+ * @param {string|HTMLElement|jQuery} idOrNode
+ * @param {boolean} top True only for top-level invocation.
+ * @return {OO.ui.Element}
+ */
+OO.ui.Element.static.unsafeInfuse = function ( idOrNode, top ) {
+ // look for a cached result of a previous infusion.
+ var id, $elem, data, cls, obj;
+ if ( typeof idOrNode === 'string' ) {
+ id = idOrNode;
+ $elem = $( document.getElementById( id ) );
+ } else {
+ $elem = $( idOrNode );
+ id = $elem.attr( 'id' );
+ }
+ data = $elem.data( 'ooui-infused' );
+ if ( data ) {
+ // cached!
+ if ( data === true ) {
+ throw new Error( 'Circular dependency! ' + id );
+ }
+ return data;
+ }
+ if ( !$elem.length ) {
+ throw new Error( 'Widget not found: ' + id );
+ }
+ data = $elem.attr( 'data-ooui' );
+ if ( !data ) {
+ throw new Error( 'No infusion data found: ' + id );
+ }
+ try {
+ data = $.parseJSON( data );
+ } catch ( _ ) {
+ data = null;
+ }
+ if ( !( data && data._ ) ) {
+ throw new Error( 'No valid infusion data found: ' + id );
+ }
+ if ( data._ === 'Tag' ) {
+ // Special case: this is a raw Tag; wrap existing node, don't rebuild.
+ return new OO.ui.Element( { $element: $elem } );
+ }
+ cls = OO.ui[data._];
+ if ( !cls ) {
+ throw new Error( 'Unknown widget type: ' + id );
+ }
+ $elem.data( 'ooui-infused', true ); // prevent loops
+ data.id = id; // implicit
+ data = OO.copy( data, null, function deserialize( value ) {
+ if ( OO.isPlainObject( value ) ) {
+ if ( value.tag ) {
+ return OO.ui.Element.static.unsafeInfuse( value.tag, false );
+ }
+ if ( value.html ) {
+ return new OO.ui.HtmlSnippet( value.html );
+ }
+ }
+ } );
+ // jscs:disable requireCapitalizedConstructors
+ obj = new cls( data ); // rebuild widget
+ // now replace old DOM with this new DOM.
+ if ( top ) {
+ $elem.replaceWith( obj.$element );
+ }
+ obj.$element.data( 'ooui-infused', obj );
+ // set the 'data-ooui' attribute so we can identify infused widgets
+ obj.$element.attr( 'data-ooui', '' );
+ return obj;
+};
+
+/**
+ * Get a jQuery function within a specific document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} context Context to bind the function to
+ * @param {jQuery} [$iframe] HTML iframe element that contains the document, omit if document is
+ * not in an iframe
+ * @return {Function} Bound jQuery function
+ */
+OO.ui.Element.static.getJQuery = function ( context, $iframe ) {
+ function wrapper( selector ) {
+ return $( selector, wrapper.context );
+ }
+
+ wrapper.context = this.getDocument( context );
+
+ if ( $iframe ) {
+ wrapper.$iframe = $iframe;
+ }
+
+ return wrapper;
+};
+
+/**
+ * Get the document of an element.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Object to get the document for
+ * @return {HTMLDocument|null} Document object
+ */
+OO.ui.Element.static.getDocument = function ( obj ) {
+ // jQuery - selections created "offscreen" won't have a context, so .context isn't reliable
+ return ( obj[ 0 ] && obj[ 0 ].ownerDocument ) ||
+ // Empty jQuery selections might have a context
+ obj.context ||
+ // HTMLElement
+ obj.ownerDocument ||
+ // Window
+ obj.document ||
+ // HTMLDocument
+ ( obj.nodeType === 9 && obj ) ||
+ null;
+};
+
+/**
+ * Get the window of an element or document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the window for
+ * @return {Window} Window object
+ */
+OO.ui.Element.static.getWindow = function ( obj ) {
+ var doc = this.getDocument( obj );
+ return doc.parentWindow || doc.defaultView;
+};
+
+/**
+ * Get the direction of an element or document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
+ * @return {string} Text direction, either 'ltr' or 'rtl'
+ */
+OO.ui.Element.static.getDir = function ( obj ) {
+ var isDoc, isWin;
+
+ if ( obj instanceof jQuery ) {
+ obj = obj[ 0 ];
+ }
+ isDoc = obj.nodeType === 9;
+ isWin = obj.document !== undefined;
+ if ( isDoc || isWin ) {
+ if ( isWin ) {
+ obj = obj.document;
+ }
+ obj = obj.body;
+ }
+ return $( obj ).css( 'direction' );
+};
+
+/**
+ * Get the offset between two frames.
+ *
+ * TODO: Make this function not use recursion.
+ *
+ * @static
+ * @param {Window} from Window of the child frame
+ * @param {Window} [to=window] Window of the parent frame
+ * @param {Object} [offset] Offset to start with, used internally
+ * @return {Object} Offset object, containing left and top properties
+ */
+OO.ui.Element.static.getFrameOffset = function ( from, to, offset ) {
+ var i, len, frames, frame, rect;
+
+ if ( !to ) {
+ to = window;
+ }
+ if ( !offset ) {
+ offset = { top: 0, left: 0 };
+ }
+ if ( from.parent === from ) {
+ return offset;
+ }
+
+ // Get iframe element
+ frames = from.parent.document.getElementsByTagName( 'iframe' );
+ for ( i = 0, len = frames.length; i < len; i++ ) {
+ if ( frames[ i ].contentWindow === from ) {
+ frame = frames[ i ];
+ break;
+ }
+ }
+
+ // Recursively accumulate offset values
+ if ( frame ) {
+ rect = frame.getBoundingClientRect();
+ offset.left += rect.left;
+ offset.top += rect.top;
+ if ( from !== to ) {
+ this.getFrameOffset( from.parent, offset );
+ }
+ }
+ return offset;
+};
+
+/**
+ * Get the offset between two elements.
+ *
+ * The two elements may be in a different frame, but in that case the frame $element is in must
+ * be contained in the frame $anchor is in.
+ *
+ * @static
+ * @param {jQuery} $element Element whose position to get
+ * @param {jQuery} $anchor Element to get $element's position relative to
+ * @return {Object} Translated position coordinates, containing top and left properties
+ */
+OO.ui.Element.static.getRelativePosition = function ( $element, $anchor ) {
+ var iframe, iframePos,
+ pos = $element.offset(),
+ anchorPos = $anchor.offset(),
+ elementDocument = this.getDocument( $element ),
+ anchorDocument = this.getDocument( $anchor );
+
+ // If $element isn't in the same document as $anchor, traverse up
+ while ( elementDocument !== anchorDocument ) {
+ iframe = elementDocument.defaultView.frameElement;
+ if ( !iframe ) {
+ throw new Error( '$element frame is not contained in $anchor frame' );
+ }
+ iframePos = $( iframe ).offset();
+ pos.left += iframePos.left;
+ pos.top += iframePos.top;
+ elementDocument = iframe.ownerDocument;
+ }
+ pos.left -= anchorPos.left;
+ pos.top -= anchorPos.top;
+ return pos;
+};
+
+/**
+ * Get element border sizes.
+ *
+ * @static
+ * @param {HTMLElement} el Element to measure
+ * @return {Object} Dimensions object with `top`, `left`, `bottom` and `right` properties
+ */
+OO.ui.Element.static.getBorders = function ( el ) {
+ var doc = el.ownerDocument,
+ win = doc.parentWindow || doc.defaultView,
+ style = win && win.getComputedStyle ?
+ win.getComputedStyle( el, null ) :
+ el.currentStyle,
+ $el = $( el ),
+ top = parseFloat( style ? style.borderTopWidth : $el.css( 'borderTopWidth' ) ) || 0,
+ left = parseFloat( style ? style.borderLeftWidth : $el.css( 'borderLeftWidth' ) ) || 0,
+ bottom = parseFloat( style ? style.borderBottomWidth : $el.css( 'borderBottomWidth' ) ) || 0,
+ right = parseFloat( style ? style.borderRightWidth : $el.css( 'borderRightWidth' ) ) || 0;
+
+ return {
+ top: top,
+ left: left,
+ bottom: bottom,
+ right: right
+ };
+};
+
+/**
+ * Get dimensions of an element or window.
+ *
+ * @static
+ * @param {HTMLElement|Window} el Element to measure
+ * @return {Object} Dimensions object with `borders`, `scroll`, `scrollbar` and `rect` properties
+ */
+OO.ui.Element.static.getDimensions = function ( el ) {
+ var $el, $win,
+ doc = el.ownerDocument || el.document,
+ win = doc.parentWindow || doc.defaultView;
+
+ if ( win === el || el === doc.documentElement ) {
+ $win = $( win );
+ return {
+ borders: { top: 0, left: 0, bottom: 0, right: 0 },
+ scroll: {
+ top: $win.scrollTop(),
+ left: $win.scrollLeft()
+ },
+ scrollbar: { right: 0, bottom: 0 },
+ rect: {
+ top: 0,
+ left: 0,
+ bottom: $win.innerHeight(),
+ right: $win.innerWidth()
+ }
+ };
+ } else {
+ $el = $( el );
+ return {
+ borders: this.getBorders( el ),
+ scroll: {
+ top: $el.scrollTop(),
+ left: $el.scrollLeft()
+ },
+ scrollbar: {
+ right: $el.innerWidth() - el.clientWidth,
+ bottom: $el.innerHeight() - el.clientHeight
+ },
+ rect: el.getBoundingClientRect()
+ };
+ }
+};
+
+/**
+ * Get scrollable object parent
+ *
+ * documentElement can't be used to get or set the scrollTop
+ * property on Blink. Changing and testing its value lets us
+ * use 'body' or 'documentElement' based on what is working.
+ *
+ * https://code.google.com/p/chromium/issues/detail?id=303131
+ *
+ * @static
+ * @param {HTMLElement} el Element to find scrollable parent for
+ * @return {HTMLElement} Scrollable parent
+ */
+OO.ui.Element.static.getRootScrollableElement = function ( el ) {
+ var scrollTop, body;
+
+ if ( OO.ui.scrollableElement === undefined ) {
+ body = el.ownerDocument.body;
+ scrollTop = body.scrollTop;
+ body.scrollTop = 1;
+
+ if ( body.scrollTop === 1 ) {
+ body.scrollTop = scrollTop;
+ OO.ui.scrollableElement = 'body';
+ } else {
+ OO.ui.scrollableElement = 'documentElement';
+ }
+ }
+
+ return el.ownerDocument[ OO.ui.scrollableElement ];
+};
+
+/**
+ * Get closest scrollable container.
+ *
+ * Traverses up until either a scrollable element or the root is reached, in which case the window
+ * will be returned.
+ *
+ * @static
+ * @param {HTMLElement} el Element to find scrollable container for
+ * @param {string} [dimension] Dimension of scrolling to look for; `x`, `y` or omit for either
+ * @return {HTMLElement} Closest scrollable container
+ */
+OO.ui.Element.static.getClosestScrollableContainer = function ( el, dimension ) {
+ var i, val,
+ props = [ 'overflow' ],
+ $parent = $( el ).parent();
+
+ if ( dimension === 'x' || dimension === 'y' ) {
+ props.push( 'overflow-' + dimension );
+ }
+
+ while ( $parent.length ) {
+ if ( $parent[ 0 ] === this.getRootScrollableElement( el ) ) {
+ return $parent[ 0 ];
+ }
+ i = props.length;
+ while ( i-- ) {
+ val = $parent.css( props[ i ] );
+ if ( val === 'auto' || val === 'scroll' ) {
+ return $parent[ 0 ];
+ }
+ }
+ $parent = $parent.parent();
+ }
+ return this.getDocument( el ).body;
+};
+
+/**
+ * Scroll element into view.
+ *
+ * @static
+ * @param {HTMLElement} el Element to scroll into view
+ * @param {Object} [config] Configuration options
+ * @param {string} [config.duration] jQuery animation duration value
+ * @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
+ * to scroll in both directions
+ * @param {Function} [config.complete] Function to call when scrolling completes
+ */
+OO.ui.Element.static.scrollIntoView = function ( el, config ) {
+ // Configuration initialization
+ config = config || {};
+
+ var rel, anim = {},
+ callback = typeof config.complete === 'function' && config.complete,
+ sc = this.getClosestScrollableContainer( el, config.direction ),
+ $sc = $( sc ),
+ eld = this.getDimensions( el ),
+ scd = this.getDimensions( sc ),
+ $win = $( this.getWindow( el ) );
+
+ // Compute the distances between the edges of el and the edges of the scroll viewport
+ if ( $sc.is( 'html, body' ) ) {
+ // If the scrollable container is the root, this is easy
+ rel = {
+ top: eld.rect.top,
+ bottom: $win.innerHeight() - eld.rect.bottom,
+ left: eld.rect.left,
+ right: $win.innerWidth() - eld.rect.right
+ };
+ } else {
+ // Otherwise, we have to subtract el's coordinates from sc's coordinates
+ rel = {
+ top: eld.rect.top - ( scd.rect.top + scd.borders.top ),
+ bottom: scd.rect.bottom - scd.borders.bottom - scd.scrollbar.bottom - eld.rect.bottom,
+ left: eld.rect.left - ( scd.rect.left + scd.borders.left ),
+ right: scd.rect.right - scd.borders.right - scd.scrollbar.right - eld.rect.right
+ };
+ }
+
+ if ( !config.direction || config.direction === 'y' ) {
+ if ( rel.top < 0 ) {
+ anim.scrollTop = scd.scroll.top + rel.top;
+ } else if ( rel.top > 0 && rel.bottom < 0 ) {
+ anim.scrollTop = scd.scroll.top + Math.min( rel.top, -rel.bottom );
+ }
+ }
+ if ( !config.direction || config.direction === 'x' ) {
+ if ( rel.left < 0 ) {
+ anim.scrollLeft = scd.scroll.left + rel.left;
+ } else if ( rel.left > 0 && rel.right < 0 ) {
+ anim.scrollLeft = scd.scroll.left + Math.min( rel.left, -rel.right );
+ }
+ }
+ if ( !$.isEmptyObject( anim ) ) {
+ $sc.stop( true ).animate( anim, config.duration || 'fast' );
+ if ( callback ) {
+ $sc.queue( function ( next ) {
+ callback();
+ next();
+ } );
+ }
+ } else {
+ if ( callback ) {
+ callback();
+ }
+ }
+};
+
+/**
+ * Force the browser to reconsider whether it really needs to render scrollbars inside the element
+ * and reserve space for them, because it probably doesn't.
+ *
+ * Workaround primarily for <https://code.google.com/p/chromium/issues/detail?id=387290>, but also
+ * similar bugs in other browsers. "Just" forcing a reflow is not sufficient in all cases, we need
+ * to first actually detach (or hide, but detaching is simpler) all children, *then* force a reflow,
+ * and then reattach (or show) them back.
+ *
+ * @static
+ * @param {HTMLElement} el Element to reconsider the scrollbars on
+ */
+OO.ui.Element.static.reconsiderScrollbars = function ( el ) {
+ var i, len, nodes = [];
+ // Detach all children
+ while ( el.firstChild ) {
+ nodes.push( el.firstChild );
+ el.removeChild( el.firstChild );
+ }
+ // Force reflow
+ void el.offsetHeight;
+ // Reattach all children
+ for ( i = 0, len = nodes.length; i < len; i++ ) {
+ el.appendChild( nodes[ i ] );
+ }
+};
+
+/* Methods */
+
+/**
+ * Toggle visibility of an element.
+ *
+ * @param {boolean} [show] Make element visible, omit to toggle visibility
+ * @fires visible
+ * @chainable
+ */
+OO.ui.Element.prototype.toggle = function ( show ) {
+ show = show === undefined ? !this.visible : !!show;
+
+ if ( show !== this.isVisible() ) {
+ this.visible = show;
+ this.$element.toggleClass( 'oo-ui-element-hidden', !this.visible );
+ this.emit( 'toggle', show );
+ }
+
+ return this;
+};
+
+/**
+ * Check if element is visible.
+ *
+ * @return {boolean} element is visible
+ */
+OO.ui.Element.prototype.isVisible = function () {
+ return this.visible;
+};
+
+/**
+ * Get element data.
+ *
+ * @return {Mixed} Element data
+ */
+OO.ui.Element.prototype.getData = function () {
+ return this.data;
+};
+
+/**
+ * Set element data.
+ *
+ * @param {Mixed} Element data
+ * @chainable
+ */
+OO.ui.Element.prototype.setData = function ( data ) {
+ this.data = data;
+ return this;
+};
+
+/**
+ * Check if element supports one or more methods.
+ *
+ * @param {string|string[]} methods Method or list of methods to check
+ * @return {boolean} All methods are supported
+ */
+OO.ui.Element.prototype.supports = function ( methods ) {
+ var i, len,
+ support = 0;
+
+ methods = Array.isArray( methods ) ? methods : [ methods ];
+ for ( i = 0, len = methods.length; i < len; i++ ) {
+ if ( $.isFunction( this[ methods[ i ] ] ) ) {
+ support++;
+ }
+ }
+
+ return methods.length === support;
+};
+
+/**
+ * Update the theme-provided classes.
+ *
+ * @localdoc This is called in element mixins and widget classes any time state changes.
+ * Updating is debounced, minimizing overhead of changing multiple attributes and
+ * guaranteeing that theme updates do not occur within an element's constructor
+ */
+OO.ui.Element.prototype.updateThemeClasses = function () {
+ if ( !this.updateThemeClassesPending ) {
+ this.updateThemeClassesPending = true;
+ setTimeout( this.debouncedUpdateThemeClassesHandler );
+ }
+};
+
+/**
+ * @private
+ */
+OO.ui.Element.prototype.debouncedUpdateThemeClasses = function () {
+ OO.ui.theme.updateElementClasses( this );
+ this.updateThemeClassesPending = false;
+};
+
+/**
+ * Get the HTML tag name.
+ *
+ * Override this method to base the result on instance information.
+ *
+ * @return {string} HTML tag name
+ */
+OO.ui.Element.prototype.getTagName = function () {
+ return this.constructor.static.tagName;
+};
+
+/**
+ * Check if the element is attached to the DOM
+ * @return {boolean} The element is attached to the DOM
+ */
+OO.ui.Element.prototype.isElementAttached = function () {
+ return $.contains( this.getElementDocument(), this.$element[ 0 ] );
+};
+
+/**
+ * Get the DOM document.
+ *
+ * @return {HTMLDocument} Document object
+ */
+OO.ui.Element.prototype.getElementDocument = function () {
+ // Don't cache this in other ways either because subclasses could can change this.$element
+ return OO.ui.Element.static.getDocument( this.$element );
+};
+
+/**
+ * Get the DOM window.
+ *
+ * @return {Window} Window object
+ */
+OO.ui.Element.prototype.getElementWindow = function () {
+ return OO.ui.Element.static.getWindow( this.$element );
+};
+
+/**
+ * Get closest scrollable container.
+ */
+OO.ui.Element.prototype.getClosestScrollableElementContainer = function () {
+ return OO.ui.Element.static.getClosestScrollableContainer( this.$element[ 0 ] );
+};
+
+/**
+ * Get group element is in.
+ *
+ * @return {OO.ui.GroupElement|null} Group element, null if none
+ */
+OO.ui.Element.prototype.getElementGroup = function () {
+ return this.elementGroup;
+};
+
+/**
+ * Set group element is in.
+ *
+ * @param {OO.ui.GroupElement|null} group Group element, null if none
+ * @chainable
+ */
+OO.ui.Element.prototype.setElementGroup = function ( group ) {
+ this.elementGroup = group;
+ return this;
+};
+
+/**
+ * Scroll element into view.
+ *
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
+ return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
+};
diff --git a/vendor/oojs/oojs-ui/src/Error.js b/vendor/oojs/oojs-ui/src/Error.js
new file mode 100644
index 00000000..af87b722
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Error.js
@@ -0,0 +1,91 @@
+/**
+ * Errors contain a required message (either a string or jQuery selection) that is used to describe what went wrong
+ * in a {@link OO.ui.Process process}. The error's #recoverable and #warning configurations are used to customize the
+ * appearance and functionality of the error interface.
+ *
+ * The basic error interface contains a formatted error message as well as two buttons: 'Dismiss' and 'Try again' (i.e., the error
+ * is 'recoverable' by default). If the error is not recoverable, the 'Try again' button will not be rendered and the widget
+ * that initiated the failed process will be disabled.
+ *
+ * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button, which will try the
+ * process again.
+ *
+ * For an example of error interfaces, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Processes_and_errors
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string|jQuery} message Description of error
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [recoverable=true] Error is recoverable.
+ * By default, errors are recoverable, and users can try the process again.
+ * @cfg {boolean} [warning=false] Error is a warning.
+ * If the error is a warning, the error interface will include a
+ * 'Dismiss' and a 'Continue' button. It is the responsibility of the developer to ensure that the warning
+ * is not triggered a second time if the user chooses to continue.
+ */
+OO.ui.Error = function OoUiError( message, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( message ) && config === undefined ) {
+ config = message;
+ message = config.message;
+ }
+
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.message = message instanceof jQuery ? message : String( message );
+ this.recoverable = config.recoverable === undefined || !!config.recoverable;
+ this.warning = !!config.warning;
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Error );
+
+/* Methods */
+
+/**
+ * Check if the error is recoverable.
+ *
+ * If the error is recoverable, users are able to try the process again.
+ *
+ * @return {boolean} Error is recoverable
+ */
+OO.ui.Error.prototype.isRecoverable = function () {
+ return this.recoverable;
+};
+
+/**
+ * Check if the error is a warning.
+ *
+ * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button.
+ *
+ * @return {boolean} Error is warning
+ */
+OO.ui.Error.prototype.isWarning = function () {
+ return this.warning;
+};
+
+/**
+ * Get error message as DOM nodes.
+ *
+ * @return {jQuery} Error message in DOM nodes
+ */
+OO.ui.Error.prototype.getMessage = function () {
+ return this.message instanceof jQuery ?
+ this.message.clone() :
+ $( '<div>' ).text( this.message ).contents();
+};
+
+/**
+ * Get the error message text.
+ *
+ * @return {string} Error message
+ */
+OO.ui.Error.prototype.getMessageText = function () {
+ return this.message instanceof jQuery ? this.message.text() : this.message;
+};
diff --git a/vendor/oojs/oojs-ui/src/HtmlSnippet.js b/vendor/oojs/oojs-ui/src/HtmlSnippet.js
new file mode 100644
index 00000000..b9c5c314
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/HtmlSnippet.js
@@ -0,0 +1,29 @@
+/**
+ * Wraps an HTML snippet for use with configuration values which default
+ * to strings. This bypasses the default html-escaping done to string
+ * values.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string} [content] HTML content
+ */
+OO.ui.HtmlSnippet = function OoUiHtmlSnippet( content ) {
+ // Properties
+ this.content = content;
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.HtmlSnippet );
+
+/* Methods */
+
+/**
+ * Render into HTML.
+ *
+ * @return {string} Unchanged HTML snippet.
+ */
+OO.ui.HtmlSnippet.prototype.toString = function () {
+ return this.content;
+};
diff --git a/vendor/oojs/oojs-ui/src/Layout.js b/vendor/oojs/oojs-ui/src/Layout.js
new file mode 100644
index 00000000..d0c368f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Layout.js
@@ -0,0 +1,33 @@
+/**
+ * Layouts are containers for elements and are used to arrange other widgets of arbitrary type in a way
+ * that is centrally controlled and can be updated dynamically. Layouts can be, and usually are, combined.
+ * See {@link OO.ui.FieldsetLayout FieldsetLayout}, {@link OO.ui.FieldLayout FieldLayout}, {@link OO.ui.FormLayout FormLayout},
+ * {@link OO.ui.PanelLayout PanelLayout}, {@link OO.ui.StackLayout StackLayout}, {@link OO.ui.PageLayout PageLayout},
+ * and {@link OO.ui.BookletLayout BookletLayout} for more information and examples.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Layout = function OoUiLayout( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.Layout.super.call( this, config );
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-layout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Layout, OO.ui.Element );
+OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
diff --git a/vendor/oojs/oojs-ui/src/Process.js b/vendor/oojs/oojs-ui/src/Process.js
new file mode 100644
index 00000000..649ffb99
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Process.js
@@ -0,0 +1,166 @@
+/**
+ * A Process is a list of steps that are called in sequence. The step can be a number, a jQuery promise,
+ * or a function:
+ *
+ * - **number**: the process will wait for the specified number of milliseconds before proceeding.
+ * - **promise**: the process will continue to the next step when the promise is successfully resolved
+ * or stop if the promise is rejected.
+ * - **function**: the process will execute the function. The process will stop if the function returns
+ * either a boolean `false` or a promise that is rejected; if the function returns a number, the process
+ * will wait for that number of milliseconds before proceeding.
+ *
+ * If the process fails, an {@link OO.ui.Error error} is generated. Depending on how the error is
+ * configured, users can dismiss the error and try the process again, or not. If a process is stopped,
+ * its remaining steps will not be performed.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {number|jQuery.Promise|Function} step Number of miliseconds to wait before proceeding, promise
+ * that must be resolved before proceeding, or a function to execute. See #createStep for more information. see #createStep for more information
+ * @param {Object} [context=null] Execution context of the function. The context is ignored if the step is
+ * a number or promise.
+ * @return {Object} Step object, with `callback` and `context` properties
+ */
+OO.ui.Process = function ( step, context ) {
+ // Properties
+ this.steps = [];
+
+ // Initialization
+ if ( step !== undefined ) {
+ this.next( step, context );
+ }
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Process );
+
+/* Methods */
+
+/**
+ * Start the process.
+ *
+ * @return {jQuery.Promise} Promise that is resolved when all steps have successfully completed.
+ * If any of the steps return a promise that is rejected or a boolean false, this promise is rejected
+ * and any remaining steps are not performed.
+ */
+OO.ui.Process.prototype.execute = function () {
+ var i, len, promise;
+
+ /**
+ * Continue execution.
+ *
+ * @ignore
+ * @param {Array} step A function and the context it should be called in
+ * @return {Function} Function that continues the process
+ */
+ function proceed( step ) {
+ return function () {
+ // Execute step in the correct context
+ var deferred,
+ result = step.callback.call( step.context );
+
+ if ( result === false ) {
+ // Use rejected promise for boolean false results
+ return $.Deferred().reject( [] ).promise();
+ }
+ if ( typeof result === 'number' ) {
+ if ( result < 0 ) {
+ throw new Error( 'Cannot go back in time: flux capacitor is out of service' );
+ }
+ // Use a delayed promise for numbers, expecting them to be in milliseconds
+ deferred = $.Deferred();
+ setTimeout( deferred.resolve, result );
+ return deferred.promise();
+ }
+ if ( result instanceof OO.ui.Error ) {
+ // Use rejected promise for error
+ return $.Deferred().reject( [ result ] ).promise();
+ }
+ if ( Array.isArray( result ) && result.length && result[ 0 ] instanceof OO.ui.Error ) {
+ // Use rejected promise for list of errors
+ return $.Deferred().reject( result ).promise();
+ }
+ // Duck-type the object to see if it can produce a promise
+ if ( result && $.isFunction( result.promise ) ) {
+ // Use a promise generated from the result
+ return result.promise();
+ }
+ // Use resolved promise for other results
+ return $.Deferred().resolve().promise();
+ };
+ }
+
+ if ( this.steps.length ) {
+ // Generate a chain reaction of promises
+ promise = proceed( this.steps[ 0 ] )();
+ for ( i = 1, len = this.steps.length; i < len; i++ ) {
+ promise = promise.then( proceed( this.steps[ i ] ) );
+ }
+ } else {
+ promise = $.Deferred().resolve().promise();
+ }
+
+ return promise;
+};
+
+/**
+ * Create a process step.
+ *
+ * @private
+ * @param {number|jQuery.Promise|Function} step
+ *
+ * - Number of milliseconds to wait before proceeding
+ * - Promise that must be resolved before proceeding
+ * - Function to execute
+ * - If the function returns a boolean false the process will stop
+ * - If the function returns a promise, the process will continue to the next
+ * step when the promise is resolved or stop if the promise is rejected
+ * - If the function returns a number, the process will wait for that number of
+ * milliseconds before proceeding
+ * @param {Object} [context=null] Execution context of the function. The context is
+ * ignored if the step is a number or promise.
+ * @return {Object} Step object, with `callback` and `context` properties
+ */
+OO.ui.Process.prototype.createStep = function ( step, context ) {
+ if ( typeof step === 'number' || $.isFunction( step.promise ) ) {
+ return {
+ callback: function () {
+ return step;
+ },
+ context: null
+ };
+ }
+ if ( $.isFunction( step ) ) {
+ return {
+ callback: step,
+ context: context
+ };
+ }
+ throw new Error( 'Cannot create process step: number, promise or function expected' );
+};
+
+/**
+ * Add step to the beginning of the process.
+ *
+ * @inheritdoc #createStep
+ * @return {OO.ui.Process} this
+ * @chainable
+ */
+OO.ui.Process.prototype.first = function ( step, context ) {
+ this.steps.unshift( this.createStep( step, context ) );
+ return this;
+};
+
+/**
+ * Add step to the end of the process.
+ *
+ * @inheritdoc #createStep
+ * @return {OO.ui.Process} this
+ * @chainable
+ */
+OO.ui.Process.prototype.next = function ( step, context ) {
+ this.steps.push( this.createStep( step, context ) );
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/Theme.js b/vendor/oojs/oojs-ui/src/Theme.js
new file mode 100644
index 00000000..92de72ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Theme.js
@@ -0,0 +1,48 @@
+/**
+ * Theme logic.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Theme = function OoUiTheme( config ) {
+ // Configuration initialization
+ config = config || {};
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Theme );
+
+/* Methods */
+
+/**
+ * Get a list of classes to be applied to a widget.
+ *
+ * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
+ * otherwise state transitions will not work properly.
+ *
+ * @param {OO.ui.Element} element Element for which to get classes
+ * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
+ */
+OO.ui.Theme.prototype.getElementClasses = function ( /* element */ ) {
+ return { on: [], off: [] };
+};
+
+/**
+ * Update CSS classes provided by the theme.
+ *
+ * For elements with theme logic hooks, this should be called any time there's a state change.
+ *
+ * @param {OO.ui.Element} element Element for which to update classes
+ * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
+ */
+OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
+ var classes = this.getElementClasses( element );
+
+ element.$element
+ .removeClass( classes.off.join( ' ' ) )
+ .addClass( classes.on.join( ' ' ) );
+};
diff --git a/vendor/oojs/oojs-ui/src/Tool.js b/vendor/oojs/oojs-ui/src/Tool.js
new file mode 100644
index 00000000..603c0639
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Tool.js
@@ -0,0 +1,279 @@
+/**
+ * Generic toolbar tool.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.FlaggedElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ * @cfg {string|Function} [title] Title text or a function that returns text
+ */
+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.super.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.IconElement.call( this, config );
+ OO.ui.FlaggedElement.call( this, config );
+ OO.ui.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.IconElement );
+OO.mixinClass( OO.ui.Tool, OO.ui.FlaggedElement );
+OO.mixinClass( OO.ui.Tool, OO.ui.TabIndexedElement );
+
+/* Events */
+
+/**
+ * @event select
+ */
+
+/* Static Properties */
+
+/**
+ * @static
+ * @inheritdoc
+ */
+OO.ui.Tool.static.tagName = 'span';
+
+/**
+ * Symbolic name of tool.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Tool.static.name = '';
+
+/**
+ * Tool group.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Tool.static.group = '';
+
+/**
+ * Tool title.
+ *
+ * Title is used as a tooltip when the tool is part of a bar tool group, or a label when the tool
+ * is part of a list or menu tool group. If a trigger is associated with an action by the same name
+ * as the tool, a description of its keyboard shortcut for the appropriate platform will be
+ * appended to the title if the tool is part of a bar tool group.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string|Function} Title text or a function that returns text
+ */
+OO.ui.Tool.static.title = '';
+
+/**
+ * Whether this tool should be displayed with both title and label when used in a bar tool group.
+ * 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;
+
+/**
+ * Tool can be automatically added to catch-all groups.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Tool.static.autoAddToCatchall = true;
+
+/**
+ * Tool can be automatically added to named groups.
+ *
+ * @static
+ * @property {boolean}
+ * @inheritable
+ */
+OO.ui.Tool.static.autoAddToGroup = true;
+
+/**
+ * Check if this tool is compatible with given data.
+ *
+ * @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 is an abstract method that must be overridden in a concrete subclass.
+ *
+ * @abstract
+ */
+OO.ui.Tool.prototype.onUpdateState = function () {
+ throw new Error(
+ 'OO.ui.Tool.onUpdateState not implemented in this subclass:' + this.constructor
+ );
+};
+
+/**
+ * Handle the tool being selected.
+ *
+ * This is an abstract method that must be overridden in a concrete subclass.
+ *
+ * @abstract
+ */
+OO.ui.Tool.prototype.onSelect = function () {
+ throw new Error(
+ 'OO.ui.Tool.onSelect not implemented in this subclass:' + this.constructor
+ );
+};
+
+/**
+ * Check if the button is active.
+ *
+ * @return {boolean} Button is active
+ */
+OO.ui.Tool.prototype.isActive = function () {
+ return this.active;
+};
+
+/**
+ * Make the button appear active or inactive.
+ *
+ * @param {boolean} state Make button appear active
+ */
+OO.ui.Tool.prototype.setActive = function ( state ) {
+ this.active = !!state;
+ if ( this.active ) {
+ this.$element.addClass( 'oo-ui-tool-active' );
+ } else {
+ this.$element.removeClass( 'oo-ui-tool-active' );
+ }
+};
+
+/**
+ * Get 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.
+ */
+OO.ui.Tool.prototype.destroy = function () {
+ this.toolbar.disconnect( this );
+ this.$element.remove();
+};
diff --git a/vendor/oojs/oojs-ui/src/ToolFactory.js b/vendor/oojs/oojs-ui/src/ToolFactory.js
new file mode 100644
index 00000000..aada2e9b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/ToolFactory.js
@@ -0,0 +1,120 @@
+/**
+ * Factory for tools.
+ *
+ * @class
+ * @extends OO.Factory
+ * @constructor
+ */
+OO.ui.ToolFactory = function OoUiToolFactory() {
+ // Parent constructor
+ OO.ui.ToolFactory.super.call( this );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToolFactory, OO.Factory );
+
+/* Methods */
+
+/**
+ * Get tools from the factory
+ *
+ * @param {Array} include Included tools
+ * @param {Array} exclude Excluded tools
+ * @param {Array} promote Promoted tools
+ * @param {Array} demote Demoted tools
+ * @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.
+ *
+ * Tools can be specified in the following ways:
+ *
+ * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
+ * - All tools in a group: `{ group: 'group-name' }`
+ * - All tools: `'*'`
+ *
+ * @private
+ * @param {Array|string} collection List of tools
+ * @param {Object} [used] Object with names that should be skipped as properties; extracted
+ * names will be added as properties
+ * @return {string[]} List of extracted names
+ */
+OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
+ var i, len, item, name, tool,
+ names = [];
+
+ if ( collection === '*' ) {
+ 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 if ( Array.isArray( collection ) ) {
+ for ( i = 0, len = collection.length; i < len; i++ ) {
+ item = collection[ i ];
+ // 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;
+};
diff --git a/vendor/oojs/oojs-ui/src/ToolGroup.js b/vendor/oojs/oojs-ui/src/ToolGroup.js
new file mode 100644
index 00000000..eab3cea4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/ToolGroup.js
@@ -0,0 +1,338 @@
+/**
+ * Collection of tools.
+ *
+ * Tools can be specified in the following ways:
+ *
+ * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
+ * - All tools in a group: `{ group: 'group-name' }`
+ * - All tools: `'*'`
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {Array|string} [include=[]] List of tools to include
+ * @cfg {Array|string} [exclude=[]] List of tools to exclude
+ * @cfg {Array|string} [promote=[]] List of tools to promote to the beginning
+ * @cfg {Array|string} [demote=[]] List of tools to demote to the end
+ */
+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.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.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.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.
+ *
+ * @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;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToolGroup.prototype.isDisabled = function () {
+ return this.autoDisabled || OO.ui.ToolGroup.super.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.super.prototype.updateDisabled.apply( this, arguments );
+};
+
+/**
+ * Handle mouse down and key down events.
+ *
+ * @param {jQuery.Event} e Mouse down or key down event
+ */
+OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
+ if (
+ !this.isDisabled() &&
+ ( e.which === 1 || 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.
+ *
+ * @param {Event} 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.
+ *
+ * @param {jQuery.Event} 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 === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ) {
+ this.pressed.onSelect();
+ this.pressed = null;
+ return false;
+ }
+
+ this.pressed = null;
+};
+
+/**
+ * Handle mouse over and focus events.
+ *
+ * @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.
+ *
+ * @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
+ *
+ * @param {string} name Symbolic name of tool
+ */
+OO.ui.ToolGroup.prototype.onToolFactoryRegister = function () {
+ this.populate();
+};
+
+/**
+ * Get the toolbar this group is in.
+ *
+ * @return {OO.ui.Toolbar} Toolbar of group
+ */
+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 tool group.
+ */
+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();
+};
diff --git a/vendor/oojs/oojs-ui/src/ToolGroupFactory.js b/vendor/oojs/oojs-ui/src/ToolGroupFactory.js
new file mode 100644
index 00000000..109b86e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/ToolGroupFactory.js
@@ -0,0 +1,38 @@
+/**
+ * Factory for tool groups.
+ *
+ * @class
+ * @extends OO.Factory
+ * @constructor
+ */
+OO.ui.ToolGroupFactory = function OoUiToolGroupFactory() {
+ // Parent constructor
+ OO.Factory.call( this );
+
+ var i, l,
+ 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
+ ];
+};
diff --git a/vendor/oojs/oojs-ui/src/Toolbar.js b/vendor/oojs/oojs-ui/src/Toolbar.js
new file mode 100644
index 00000000..f52fbffe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Toolbar.js
@@ -0,0 +1,481 @@
+/**
+ * Collection of tool groups.
+ *
+ * The following is a minimal example using several tools and tool groups.
+ *
+ * @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 PictureTool() {
+ * PictureTool.super.apply( this, arguments );
+ * }
+ * OO.inheritClass( PictureTool, 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).
+ * PictureTool.static.name = 'picture';
+ * PictureTool.static.icon = 'picture';
+ * PictureTool.static.title = 'Insert picture';
+ * // Defines the action that will happen when this tool is selected (clicked).
+ * PictureTool.prototype.onSelect = function () {
+ * $area.text( 'Picture tool clicked!' );
+ * // Never display this tool as "active" (selected).
+ * this.setActive( false );
+ * };
+ * // Make this tool available in our toolFactory and thus our toolbar
+ * toolFactory.register( PictureTool );
+ *
+ * // Register two more tools, nothing interesting here
+ * function SettingsTool() {
+ * SettingsTool.super.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 );
+ * };
+ * toolFactory.register( SettingsTool );
+ *
+ * // Register two more tools, nothing interesting here
+ * function StuffTool() {
+ * StuffTool.super.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 );
+ * };
+ * 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: [ 'picture', '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();
+ *
+ * The following example extends the previous one to illustrate 'menu' tool groups and the usage of
+ * '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 PictureTool() {
+ * PictureTool.super.apply( this, arguments );
+ * }
+ * OO.inheritClass( PictureTool, 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).
+ * PictureTool.static.name = 'picture';
+ * PictureTool.static.icon = 'picture';
+ * PictureTool.static.title = 'Insert picture';
+ * // Defines the action that will happen when this tool is selected (clicked).
+ * PictureTool.prototype.onSelect = function () {
+ * $area.text( 'Picture tool clicked!' );
+ * // Never display this tool as "active" (selected).
+ * this.setActive( false );
+ * };
+ * // The toolbar can be synchronized with the state of some external stuff, like a text
+ * // editor's editing area, highlighting the tools (e.g. a 'bold' tool would be shown as active
+ * // when the text cursor was inside bolded text). Here we simply disable this feature.
+ * PictureTool.prototype.onUpdateState = function () {
+ * };
+ * // Make this tool available in our toolFactory and thus our toolbar
+ * toolFactory.register( PictureTool );
+ *
+ * // Register two more tools, nothing interesting here
+ * function SettingsTool() {
+ * SettingsTool.super.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.super.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: [ 'picture', '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.GroupElement
+ *
+ * @constructor
+ * @param {OO.ui.ToolFactory} toolFactory Factory for creating tools
+ * @param {OO.ui.ToolGroupFactory} toolGroupFactory Factory for creating tool groups
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [actions] Add an actions section opposite to the tools
+ * @cfg {boolean} [shadow] Add a shadow below the toolbar
+ */
+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.super.call( this, config );
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+ OO.ui.GroupElement.call( this, config );
+
+ // Properties
+ this.toolFactory = toolFactory;
+ this.toolGroupFactory = toolGroupFactory;
+ this.groups = [];
+ this.tools = {};
+ this.$bar = $( '<div>' );
+ this.$actions = $( '<div>' );
+ this.initialized = false;
+ 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>' );
+ if ( config.shadow ) {
+ this.$bar.append( '<div class="oo-ui-toolbar-shadow"></div>' );
+ }
+ this.$element.addClass( 'oo-ui-toolbar' ).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.GroupElement );
+
+/* Methods */
+
+/**
+ * Get the tool factory.
+ *
+ * @return {OO.ui.ToolFactory} Tool factory
+ */
+OO.ui.Toolbar.prototype.getToolFactory = function () {
+ return this.toolFactory;
+};
+
+/**
+ * Get the tool group factory.
+ *
+ * @return {OO.Factory} Tool group factory
+ */
+OO.ui.Toolbar.prototype.getToolGroupFactory = function () {
+ return this.toolGroupFactory;
+};
+
+/**
+ * Handles mouse down events.
+ *
+ * @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.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 () {
+ this.initialized = true;
+ this.narrowThreshold = this.$group.width() + this.$actions.width();
+ $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
+ this.onWindowResize();
+};
+
+/**
+ * Setup toolbar.
+ *
+ * Tools can be specified in the following ways:
+ *
+ * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
+ * - All tools in a group: `{ group: 'group-name' }`
+ * - All tools: `'*'` - Using this will make the group a list with a "More" label by default
+ *
+ * @param {Object.<string,Array>} groups List of tool group configurations
+ * @param {Array|string} [groups.include] Tools to include
+ * @param {Array|string} [groups.exclude] Tools to exclude
+ * @param {Array|string} [groups.promote] Tools to promote to the beginning
+ * @param {Array|string} [groups.demote] Tools to demote to the end
+ */
+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 groups 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();
+};
+
+/**
+ * Destroys toolbar, removing event handlers and DOM elements.
+ *
+ * Call this 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 tool has not been used yet.
+ *
+ * @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.
+ *
+ * This is a stub that should be overridden to provide access to accelerator information.
+ *
+ * @param {string} name Symbolic name of tool
+ * @return {string|undefined} Tool accelerator label if available
+ */
+OO.ui.Toolbar.prototype.getToolAccelerator = function () {
+ return undefined;
+};
diff --git a/vendor/oojs/oojs-ui/src/Widget.js b/vendor/oojs/oojs-ui/src/Widget.js
new file mode 100644
index 00000000..4929e055
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Widget.js
@@ -0,0 +1,102 @@
+/**
+ * Widgets are compositions of one or more OOjs UI elements that users can both view
+ * and interact with. All widgets can be configured and modified via a standard API,
+ * and their state can change dynamically according to a model.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [disabled=false] Disable the widget. Disabled widgets cannot be used and their
+ * appearance reflects this state.
+ */
+OO.ui.Widget = function OoUiWidget( config ) {
+ // Initialize config
+ config = $.extend( { disabled: false }, config );
+
+ // Parent constructor
+ OO.ui.Widget.super.call( this, config );
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
+ // Properties
+ this.disabled = null;
+ this.wasDisabled = null;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-widget' );
+ this.setDisabled( !!config.disabled );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Widget, OO.ui.Element );
+OO.mixinClass( OO.ui.Widget, OO.EventEmitter );
+
+/* Events */
+
+/**
+ * @event disable
+ *
+ * A 'disable' event is emitted when a widget is disabled.
+ *
+ * @param {boolean} disabled Widget is disabled
+ */
+
+/**
+ * @event toggle
+ *
+ * A 'toggle' event is emitted when the visibility of the widget changes.
+ *
+ * @param {boolean} visible Widget is visible
+ */
+
+/* Methods */
+
+/**
+ * Check if the widget is disabled.
+ *
+ * @return {boolean} Widget is disabled
+ */
+OO.ui.Widget.prototype.isDisabled = function () {
+ return this.disabled;
+};
+
+/**
+ * Set the 'disabled' state of the widget.
+ *
+ * When a widget is disabled, it cannot be used and its appearance is updated to reflect this state.
+ *
+ * @param {boolean} disabled Disable widget
+ * @chainable
+ */
+OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
+ var isDisabled;
+
+ this.disabled = !!disabled;
+ isDisabled = this.isDisabled();
+ if ( isDisabled !== this.wasDisabled ) {
+ this.$element.toggleClass( 'oo-ui-widget-disabled', isDisabled );
+ this.$element.toggleClass( 'oo-ui-widget-enabled', !isDisabled );
+ this.$element.attr( 'aria-disabled', isDisabled.toString() );
+ this.emit( 'disable', isDisabled );
+ this.updateThemeClasses();
+ }
+ this.wasDisabled = isDisabled;
+
+ return this;
+};
+
+/**
+ * Update the disabled state, in case of changes in parent widget.
+ *
+ * @chainable
+ */
+OO.ui.Widget.prototype.updateDisabled = function () {
+ this.setDisabled( this.disabled );
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/Window.js b/vendor/oojs/oojs-ui/src/Window.js
new file mode 100644
index 00000000..c3c2e51d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/Window.js
@@ -0,0 +1,628 @@
+/**
+ * A window is a container for elements that are in a child frame. They are used with
+ * a window manager (OO.ui.WindowManager), which is used to open and close the window and control
+ * its presentation. The size of a window is specified using a symbolic name (e.g., ‘small’, ‘medium’,
+ * ‘large’), which is interpreted by the window manager. If the requested size is not recognized,
+ * the window manager will choose a sensible fallback.
+ *
+ * The lifecycle of a window has three primary stages (opening, opened, and closing) in which
+ * different processes are executed:
+ *
+ * **opening**: The opening stage begins when the window manager's {@link OO.ui.WindowManager#openWindow
+ * openWindow} or the window's {@link #open open} methods are used, and the window manager begins to open
+ * the window.
+ *
+ * - {@link #getSetupProcess} method is called and its result executed
+ * - {@link #getReadyProcess} method is called and its result executed
+ *
+ * **opened**: The window is now open
+ *
+ * **closing**: The closing stage begins when the window manager's
+ * {@link OO.ui.WindowManager#closeWindow closeWindow}
+ * or the window's {@link #close} methods are used, and the window manager begins to close the window.
+ *
+ * - {@link #getHoldProcess} method is called and its result executed
+ * - {@link #getTeardownProcess} method is called and its result executed. The window is now closed
+ *
+ * Each of the window's processes (setup, ready, hold, and teardown) can be extended in subclasses
+ * by overriding the window's #getSetupProcess, #getReadyProcess, #getHoldProcess and #getTeardownProcess
+ * methods. Note that each {@link OO.ui.Process process} is executed in series, so asynchronous
+ * processing can complete. Always assume window processes are executed asynchronously.
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [size] Symbolic name of the dialog size: `small`, `medium`, `large`, `larger` or
+ * `full`. If omitted, the value of the {@link #static-size static size} property will be used.
+ */
+OO.ui.Window = function OoUiWindow( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.Window.super.call( this, config );
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
+ // Properties
+ this.manager = null;
+ this.size = config.size || this.constructor.static.size;
+ this.$frame = $( '<div>' );
+ this.$overlay = $( '<div>' );
+ this.$content = $( '<div>' );
+
+ // Initialization
+ this.$overlay.addClass( 'oo-ui-window-overlay' );
+ this.$content
+ .addClass( 'oo-ui-window-content' )
+ .attr( 'tabindex', 0 );
+ this.$frame
+ .addClass( 'oo-ui-window-frame' )
+ .append( this.$content );
+
+ this.$element
+ .addClass( 'oo-ui-window' )
+ .append( this.$frame, this.$overlay );
+
+ // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+ // that reference properties not initialized at that time of parent class construction
+ // TODO: Find a better way to handle post-constructor setup
+ this.visible = false;
+ this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Window, OO.ui.Element );
+OO.mixinClass( OO.ui.Window, OO.EventEmitter );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the window size: `small`, `medium`, `large`, `larger` or `full`.
+ *
+ * The static size is used if no #size is configured during construction.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Window.static.size = 'medium';
+
+/* Methods */
+
+/**
+ * Handle mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.Window.prototype.onMouseDown = function ( e ) {
+ // Prevent clicking on the click-block from stealing focus
+ if ( e.target === this.$element[ 0 ] ) {
+ return false;
+ }
+};
+
+/**
+ * Check if the window has been initialized.
+ *
+ * Initialization occurs when a window is added to a manager.
+ *
+ * @return {boolean} Window has been initialized
+ */
+OO.ui.Window.prototype.isInitialized = function () {
+ return !!this.manager;
+};
+
+/**
+ * Check if the window is visible.
+ *
+ * @return {boolean} Window is visible
+ */
+OO.ui.Window.prototype.isVisible = function () {
+ return this.visible;
+};
+
+/**
+ * Check if the window is opening.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpening isOpening}
+ * method.
+ *
+ * @return {boolean} Window is opening
+ */
+OO.ui.Window.prototype.isOpening = function () {
+ return this.manager.isOpening( this );
+};
+
+/**
+ * Check if the window is closing.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isClosing isClosing} method.
+ *
+ * @return {boolean} Window is closing
+ */
+OO.ui.Window.prototype.isClosing = function () {
+ return this.manager.isClosing( this );
+};
+
+/**
+ * Check if the window is opened.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpened isOpened} method.
+ *
+ * @return {boolean} Window is opened
+ */
+OO.ui.Window.prototype.isOpened = function () {
+ return this.manager.isOpened( this );
+};
+
+/**
+ * Get the window manager.
+ *
+ * All windows must be attached to a window manager, which is used to open
+ * and close the window and control its presentation.
+ *
+ * @return {OO.ui.WindowManager} Manager of window
+ */
+OO.ui.Window.prototype.getManager = function () {
+ return this.manager;
+};
+
+/**
+ * Get the symbolic name of the window size (e.g., `small` or `medium`).
+ *
+ * @return {string} Symbolic name of the size: `small`, `medium`, `large`, `larger`, `full`
+ */
+OO.ui.Window.prototype.getSize = function () {
+ return this.size;
+};
+
+/**
+ * Disable transitions on window's frame for the duration of the callback function, then enable them
+ * back.
+ *
+ * @private
+ * @param {Function} callback Function to call while transitions are disabled
+ */
+OO.ui.Window.prototype.withoutSizeTransitions = function ( callback ) {
+ // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
+ // Disable transitions first, otherwise we'll get values from when the window was animating.
+ var oldTransition,
+ styleObj = this.$frame[ 0 ].style;
+ oldTransition = styleObj.transition || styleObj.OTransition || styleObj.MsTransition ||
+ styleObj.MozTransition || styleObj.WebkitTransition;
+ styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
+ styleObj.MozTransition = styleObj.WebkitTransition = 'none';
+ callback();
+ // Force reflow to make sure the style changes done inside callback really are not transitioned
+ this.$frame.height();
+ styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
+ styleObj.MozTransition = styleObj.WebkitTransition = oldTransition;
+};
+
+/**
+ * Get the height of the full window contents (i.e., the window head, body and foot together).
+ *
+ * What consistitutes the head, body, and foot varies depending on the window type.
+ * A {@link OO.ui.MessageDialog message dialog} displays a title and message in its body,
+ * and any actions in the foot. A {@link OO.ui.ProcessDialog process dialog} displays a title
+ * and special actions in the head, and dialog content in the body.
+ *
+ * To get just the height of the dialog body, use the #getBodyHeight method.
+ *
+ * @return {number} The height of the window contents (the dialog head, body and foot) in pixels
+ */
+OO.ui.Window.prototype.getContentHeight = function () {
+ var bodyHeight,
+ win = this,
+ bodyStyleObj = this.$body[ 0 ].style,
+ frameStyleObj = this.$frame[ 0 ].style;
+
+ // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
+ // Disable transitions first, otherwise we'll get values from when the window was animating.
+ this.withoutSizeTransitions( function () {
+ var oldHeight = frameStyleObj.height,
+ oldPosition = bodyStyleObj.position;
+ frameStyleObj.height = '1px';
+ // Force body to resize to new width
+ bodyStyleObj.position = 'relative';
+ bodyHeight = win.getBodyHeight();
+ frameStyleObj.height = oldHeight;
+ bodyStyleObj.position = oldPosition;
+ } );
+
+ return (
+ // Add buffer for border
+ ( this.$frame.outerHeight() - this.$frame.innerHeight() ) +
+ // Use combined heights of children
+ ( this.$head.outerHeight( true ) + bodyHeight + this.$foot.outerHeight( true ) )
+ );
+};
+
+/**
+ * Get the height of the window body.
+ *
+ * To get the height of the full window contents (the window body, head, and foot together),
+ * use #getContentHeight.
+ *
+ * When this function is called, the window will temporarily have been resized
+ * to height=1px, so .scrollHeight measurements can be taken accurately.
+ *
+ * @return {number} Height of the window body in pixels
+ */
+OO.ui.Window.prototype.getBodyHeight = function () {
+ return this.$body[ 0 ].scrollHeight;
+};
+
+/**
+ * Get the directionality of the frame (right-to-left or left-to-right).
+ *
+ * @return {string} Directionality: `'ltr'` or `'rtl'`
+ */
+OO.ui.Window.prototype.getDir = function () {
+ return this.dir;
+};
+
+/**
+ * Get the 'setup' process.
+ *
+ * The setup process is used to set up a window for use in a particular context,
+ * based on the `data` argument. This method is called during the opening phase of the window’s
+ * lifecycle.
+ *
+ * Override this method to add additional steps to the ‘setup’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * To add window content that persists between openings, you may wish to use the #initialize method
+ * instead.
+ *
+ * @abstract
+ * @param {Object} [data] Window opening data
+ * @return {OO.ui.Process} Setup process
+ */
+OO.ui.Window.prototype.getSetupProcess = function () {
+ return new OO.ui.Process();
+};
+
+/**
+ * Get the ‘ready’ process.
+ *
+ * The ready process is used to ready a window for use in a particular
+ * context, based on the `data` argument. This method is called during the opening phase of
+ * the window’s lifecycle, after the window has been {@link #getSetupProcess setup}.
+ *
+ * Override this method to add additional steps to the ‘ready’ process the parent method
+ * provides using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next}
+ * methods of OO.ui.Process.
+ *
+ * @abstract
+ * @param {Object} [data] Window opening data
+ * @return {OO.ui.Process} Ready process
+ */
+OO.ui.Window.prototype.getReadyProcess = function () {
+ return new OO.ui.Process();
+};
+
+/**
+ * Get the 'hold' process.
+ *
+ * The hold proccess is used to keep a window from being used in a particular context,
+ * based on the `data` argument. This method is called during the closing phase of the window’s
+ * lifecycle.
+ *
+ * Override this method to add additional steps to the 'hold' process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * @abstract
+ * @param {Object} [data] Window closing data
+ * @return {OO.ui.Process} Hold process
+ */
+OO.ui.Window.prototype.getHoldProcess = function () {
+ return new OO.ui.Process();
+};
+
+/**
+ * Get the ‘teardown’ process.
+ *
+ * The teardown process is used to teardown a window after use. During teardown,
+ * user interactions within the window are conveyed and the window is closed, based on the `data`
+ * argument. This method is called during the closing phase of the window’s lifecycle.
+ *
+ * Override this method to add additional steps to the ‘teardown’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * @abstract
+ * @param {Object} [data] Window closing data
+ * @return {OO.ui.Process} Teardown process
+ */
+OO.ui.Window.prototype.getTeardownProcess = function () {
+ return new OO.ui.Process();
+};
+
+/**
+ * Set the window manager.
+ *
+ * This will cause the window to initialize. Calling it more than once will cause an error.
+ *
+ * @param {OO.ui.WindowManager} manager Manager for this window
+ * @throws {Error} An error is thrown if the method is called more than once
+ * @chainable
+ */
+OO.ui.Window.prototype.setManager = function ( manager ) {
+ if ( this.manager ) {
+ throw new Error( 'Cannot set window manager, window already has a manager' );
+ }
+
+ this.manager = manager;
+ this.initialize();
+
+ return this;
+};
+
+/**
+ * Set the window size by symbolic name (e.g., 'small' or 'medium')
+ *
+ * @param {string} size Symbolic name of size: `small`, `medium`, `large`, `larger` or
+ * `full`
+ * @chainable
+ */
+OO.ui.Window.prototype.setSize = function ( size ) {
+ this.size = size;
+ this.updateSize();
+ return this;
+};
+
+/**
+ * Update the window size.
+ *
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ * @chainable
+ */
+OO.ui.Window.prototype.updateSize = function () {
+ if ( !this.manager ) {
+ throw new Error( 'Cannot update window size, must be attached to a manager' );
+ }
+
+ this.manager.updateWindowSize( this );
+
+ return this;
+};
+
+/**
+ * Set window dimensions. This method is called by the {@link OO.ui.WindowManager window manager}
+ * when the window is opening. In general, setDimensions should not be called directly.
+ *
+ * To set the size of the window, use the #setSize method.
+ *
+ * @param {Object} dim CSS dimension properties
+ * @param {string|number} [dim.width] Width
+ * @param {string|number} [dim.minWidth] Minimum width
+ * @param {string|number} [dim.maxWidth] Maximum width
+ * @param {string|number} [dim.width] Height, omit to set based on height of contents
+ * @param {string|number} [dim.minWidth] Minimum height
+ * @param {string|number} [dim.maxWidth] Maximum height
+ * @chainable
+ */
+OO.ui.Window.prototype.setDimensions = function ( dim ) {
+ var height,
+ win = this,
+ styleObj = this.$frame[ 0 ].style;
+
+ // Calculate the height we need to set using the correct width
+ if ( dim.height === undefined ) {
+ this.withoutSizeTransitions( function () {
+ var oldWidth = styleObj.width;
+ win.$frame.css( 'width', dim.width || '' );
+ height = win.getContentHeight();
+ styleObj.width = oldWidth;
+ } );
+ } else {
+ height = dim.height;
+ }
+
+ this.$frame.css( {
+ width: dim.width || '',
+ minWidth: dim.minWidth || '',
+ maxWidth: dim.maxWidth || '',
+ height: height || '',
+ minHeight: dim.minHeight || '',
+ maxHeight: dim.maxHeight || ''
+ } );
+
+ return this;
+};
+
+/**
+ * Initialize window contents.
+ *
+ * Before the window is opened for the first time, #initialize is called so that content that
+ * persists between openings can be added to the window.
+ *
+ * To set up a window with new content each time the window opens, use #getSetupProcess.
+ *
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ * @chainable
+ */
+OO.ui.Window.prototype.initialize = function () {
+ if ( !this.manager ) {
+ throw new Error( 'Cannot initialize window, must be attached to a manager' );
+ }
+
+ // Properties
+ this.$head = $( '<div>' );
+ this.$body = $( '<div>' );
+ this.$foot = $( '<div>' );
+ this.dir = OO.ui.Element.static.getDir( this.$content ) || 'ltr';
+ this.$document = $( this.getElementDocument() );
+
+ // Events
+ this.$element.on( 'mousedown', this.onMouseDown.bind( this ) );
+
+ // Initialization
+ this.$head.addClass( 'oo-ui-window-head' );
+ this.$body.addClass( 'oo-ui-window-body' );
+ this.$foot.addClass( 'oo-ui-window-foot' );
+ this.$content.append( this.$head, this.$body, this.$foot );
+
+ return this;
+};
+
+/**
+ * Open the window.
+ *
+ * This method is a wrapper around a call to the window manager’s {@link OO.ui.WindowManager#openWindow openWindow}
+ * method, which returns a promise resolved when the window is done opening.
+ *
+ * To customize the window each time it opens, use #getSetupProcess or #getReadyProcess.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved with a value when the window is opened, or rejected
+ * if the window fails to open. When the promise is resolved successfully, the first argument of the
+ * value is a new promise, which is resolved when the window begins closing.
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ */
+OO.ui.Window.prototype.open = function ( data ) {
+ if ( !this.manager ) {
+ throw new Error( 'Cannot open window, must be attached to a manager' );
+ }
+
+ return this.manager.openWindow( this, data );
+};
+
+/**
+ * Close the window.
+ *
+ * This method is a wrapper around a call to the window
+ * manager’s {@link OO.ui.WindowManager#closeWindow closeWindow} method,
+ * which returns a closing promise resolved when the window is done closing.
+ *
+ * The window's #getHoldProcess and #getTeardownProcess methods are called during the closing
+ * phase of the window’s lifecycle and can be used to specify closing behavior each time
+ * the window closes.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is closed
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ */
+OO.ui.Window.prototype.close = function ( data ) {
+ if ( !this.manager ) {
+ throw new Error( 'Cannot close window, must be attached to a manager' );
+ }
+
+ return this.manager.closeWindow( this, data );
+};
+
+/**
+ * Setup window.
+ *
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved when window is setup
+ */
+OO.ui.Window.prototype.setup = function ( data ) {
+ var win = this,
+ deferred = $.Deferred();
+
+ this.toggle( true );
+
+ this.getSetupProcess( data ).execute().done( function () {
+ // Force redraw by asking the browser to measure the elements' widths
+ win.$element.addClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
+ win.$content.addClass( 'oo-ui-window-content-setup' ).width();
+ deferred.resolve();
+ } );
+
+ return deferred.promise();
+};
+
+/**
+ * Ready window.
+ *
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved when window is ready
+ */
+OO.ui.Window.prototype.ready = function ( data ) {
+ var win = this,
+ deferred = $.Deferred();
+
+ this.$content.focus();
+ this.getReadyProcess( data ).execute().done( function () {
+ // Force redraw by asking the browser to measure the elements' widths
+ win.$element.addClass( 'oo-ui-window-ready' ).width();
+ win.$content.addClass( 'oo-ui-window-content-ready' ).width();
+ deferred.resolve();
+ } );
+
+ return deferred.promise();
+};
+
+/**
+ * Hold window.
+ *
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is held
+ */
+OO.ui.Window.prototype.hold = function ( data ) {
+ var win = this,
+ deferred = $.Deferred();
+
+ this.getHoldProcess( data ).execute().done( function () {
+ // Get the focused element within the window's content
+ var $focus = win.$content.find( OO.ui.Element.static.getDocument( win.$content ).activeElement );
+
+ // Blur the focused element
+ if ( $focus.length ) {
+ $focus[ 0 ].blur();
+ }
+
+ // Force redraw by asking the browser to measure the elements' widths
+ win.$element.removeClass( 'oo-ui-window-ready' ).width();
+ win.$content.removeClass( 'oo-ui-window-content-ready' ).width();
+ deferred.resolve();
+ } );
+
+ return deferred.promise();
+};
+
+/**
+ * Teardown window.
+ *
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is torn down
+ */
+OO.ui.Window.prototype.teardown = function ( data ) {
+ var win = this;
+
+ return this.getTeardownProcess( data ).execute()
+ .done( function () {
+ // Force redraw by asking the browser to measure the elements' widths
+ win.$element.removeClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
+ win.$content.removeClass( 'oo-ui-window-content-setup' ).width();
+ win.toggle( false );
+ } );
+};
diff --git a/vendor/oojs/oojs-ui/src/WindowManager.js b/vendor/oojs/oojs-ui/src/WindowManager.js
new file mode 100644
index 00000000..4731011a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/WindowManager.js
@@ -0,0 +1,675 @@
+/**
+ * Window managers are used to open and close {@link OO.ui.Window windows} and control their presentation.
+ * Managed windows are mutually exclusive. If a new window is opened while a current window is opening
+ * or is opened, the current window will be closed and any ongoing {@link OO.ui.Process process} will be cancelled. Windows
+ * themselves are persistent and—rather than being torn down when closed—can be repopulated with the
+ * pertinent data and reused.
+ *
+ * Over the lifecycle of a window, the window manager makes available three promises: `opening`,
+ * `opened`, and `closing`, which represent the primary stages of the cycle:
+ *
+ * **Opening**: the opening stage begins when the window manager’s #openWindow or a window’s
+ * {@link OO.ui.Window#open open} method is used, and the window manager begins to open the window.
+ *
+ * - an `opening` event is emitted with an `opening` promise
+ * - the #getSetupDelay method is called and the returned value is used to time a pause in execution before
+ * the window’s {@link OO.ui.Window#getSetupProcess getSetupProcess} method is called on the
+ * window and its result executed
+ * - a `setup` progress notification is emitted from the `opening` promise
+ * - the #getReadyDelay method is called the returned value is used to time a pause in execution before
+ * the window’s {@link OO.ui.Window#getReadyProcess getReadyProcess} method is called on the
+ * window and its result executed
+ * - a `ready` progress notification is emitted from the `opening` promise
+ * - the `opening` promise is resolved with an `opened` promise
+ *
+ * **Opened**: the window is now open.
+ *
+ * **Closing**: the closing stage begins when the window manager's #closeWindow or the
+ * window's {@link OO.ui.Window#close close} methods is used, and the window manager begins
+ * to close the window.
+ *
+ * - the `opened` promise is resolved with `closing` promise and a `closing` event is emitted
+ * - the #getHoldDelay method is called and the returned value is used to time a pause in execution before
+ * the window's {@link OO.ui.Window#getHoldProcess getHoldProces} method is called on the
+ * window and its result executed
+ * - a `hold` progress notification is emitted from the `closing` promise
+ * - the #getTeardownDelay() method is called and the returned value is used to time a pause in execution before
+ * the window's {@link OO.ui.Window#getTeardownProcess getTeardownProcess} method is called on the
+ * window and its result executed
+ * - a `teardown` progress notification is emitted from the `closing` promise
+ * - the `closing` promise is resolved. The window is now closed
+ *
+ * See the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.Factory} [factory] Window factory to use for automatic instantiation
+ * Note that window classes that are instantiated with a factory must have
+ * a {@link OO.ui.Dialog#static-name static name} property that specifies a symbolic name.
+ * @cfg {boolean} [modal=true] Prevent interaction outside the dialog
+ */
+OO.ui.WindowManager = function OoUiWindowManager( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.WindowManager.super.call( this, config );
+
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
+ // Properties
+ this.factory = config.factory;
+ this.modal = config.modal === undefined || !!config.modal;
+ this.windows = {};
+ this.opening = null;
+ this.opened = null;
+ this.closing = null;
+ this.preparingToOpen = null;
+ this.preparingToClose = null;
+ this.currentWindow = null;
+ this.globalEvents = false;
+ this.$ariaHidden = null;
+ this.onWindowResizeTimeout = null;
+ this.onWindowResizeHandler = this.onWindowResize.bind( this );
+ this.afterWindowResizeHandler = this.afterWindowResize.bind( this );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-windowManager' )
+ .toggleClass( 'oo-ui-windowManager-modal', this.modal );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.WindowManager, OO.ui.Element );
+OO.mixinClass( OO.ui.WindowManager, OO.EventEmitter );
+
+/* Events */
+
+/**
+ * An 'opening' event is emitted when the window begins to be opened.
+ *
+ * @event opening
+ * @param {OO.ui.Window} win Window that's being opened
+ * @param {jQuery.Promise} opening An `opening` promise resolved with a value when the window is opened successfully.
+ * When the `opening` promise is resolved, the first argument of the value is an 'opened' promise, the second argument
+ * is the opening data. The `opening` promise emits `setup` and `ready` notifications when those processes are complete.
+ * @param {Object} data Window opening data
+ */
+
+/**
+ * A 'closing' event is emitted when the window begins to be closed.
+ *
+ * @event closing
+ * @param {OO.ui.Window} win Window that's being closed
+ * @param {jQuery.Promise} closing A `closing` promise is resolved with a value when the window
+ * is closed successfully. The promise emits `hold` and `teardown` notifications when those
+ * processes are complete. When the `closing` promise is resolved, the first argument of its value
+ * is the closing data.
+ * @param {Object} data Window closing data
+ */
+
+/**
+ * A 'resize' event is emitted when a window is resized.
+ *
+ * @event resize
+ * @param {OO.ui.Window} win Window that was resized
+ */
+
+/* Static Properties */
+
+/**
+ * Map of the symbolic name of each window size and its CSS properties.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+OO.ui.WindowManager.static.sizes = {
+ small: {
+ width: 300
+ },
+ medium: {
+ width: 500
+ },
+ large: {
+ width: 700
+ },
+ larger: {
+ width: 900
+ },
+ full: {
+ // These can be non-numeric because they are never used in calculations
+ width: '100%',
+ height: '100%'
+ }
+};
+
+/**
+ * Symbolic name of the default window size.
+ *
+ * The default size is used if the window's requested size is not recognized.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.WindowManager.static.defaultSize = 'medium';
+
+/* Methods */
+
+/**
+ * Handle window resize events.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.WindowManager.prototype.onWindowResize = function () {
+ clearTimeout( this.onWindowResizeTimeout );
+ this.onWindowResizeTimeout = setTimeout( this.afterWindowResizeHandler, 200 );
+};
+
+/**
+ * Handle window resize events.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.WindowManager.prototype.afterWindowResize = function () {
+ if ( this.currentWindow ) {
+ this.updateWindowSize( this.currentWindow );
+ }
+};
+
+/**
+ * Check if window is opening.
+ *
+ * @return {boolean} Window is opening
+ */
+OO.ui.WindowManager.prototype.isOpening = function ( win ) {
+ return win === this.currentWindow && !!this.opening && this.opening.state() === 'pending';
+};
+
+/**
+ * Check if window is closing.
+ *
+ * @return {boolean} Window is closing
+ */
+OO.ui.WindowManager.prototype.isClosing = function ( win ) {
+ return win === this.currentWindow && !!this.closing && this.closing.state() === 'pending';
+};
+
+/**
+ * Check if window is opened.
+ *
+ * @return {boolean} Window is opened
+ */
+OO.ui.WindowManager.prototype.isOpened = function ( win ) {
+ return win === this.currentWindow && !!this.opened && this.opened.state() === 'pending';
+};
+
+/**
+ * Check if a window is being managed.
+ *
+ * @param {OO.ui.Window} win Window to check
+ * @return {boolean} Window is being managed
+ */
+OO.ui.WindowManager.prototype.hasWindow = function ( win ) {
+ var name;
+
+ for ( name in this.windows ) {
+ if ( this.windows[ name ] === win ) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Get the number of milliseconds to wait after opening begins before executing the ‘setup’ process.
+ *
+ * @param {OO.ui.Window} win Window being opened
+ * @param {Object} [data] Window opening data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getSetupDelay = function () {
+ return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after setup has finished before executing the ‘ready’ process.
+ *
+ * @param {OO.ui.Window} win Window being opened
+ * @param {Object} [data] Window opening data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getReadyDelay = function () {
+ return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after closing has begun before executing the 'hold' process.
+ *
+ * @param {OO.ui.Window} win Window being closed
+ * @param {Object} [data] Window closing data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getHoldDelay = function () {
+ return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after the ‘hold’ process has finished before
+ * executing the ‘teardown’ process.
+ *
+ * @param {OO.ui.Window} win Window being closed
+ * @param {Object} [data] Window closing data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getTeardownDelay = function () {
+ return this.modal ? 250 : 0;
+};
+
+/**
+ * Get a window by its symbolic name.
+ *
+ * If the window is not yet instantiated and its symbolic name is recognized by a factory, it will be
+ * instantiated and added to the window manager automatically. Please see the [OOjs UI documentation on MediaWiki][3]
+ * for more information about using factories.
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @param {string} name Symbolic name of the window
+ * @return {jQuery.Promise} Promise resolved with matching window, or rejected with an OO.ui.Error
+ * @throws {Error} An error is thrown if the symbolic name is not recognized by the factory.
+ * @throws {Error} An error is thrown if the named window is not recognized as a managed window.
+ */
+OO.ui.WindowManager.prototype.getWindow = function ( name ) {
+ var deferred = $.Deferred(),
+ win = this.windows[ name ];
+
+ if ( !( win instanceof OO.ui.Window ) ) {
+ if ( this.factory ) {
+ if ( !this.factory.lookup( name ) ) {
+ deferred.reject( new OO.ui.Error(
+ 'Cannot auto-instantiate window: symbolic name is unrecognized by the factory'
+ ) );
+ } else {
+ win = this.factory.create( name );
+ this.addWindows( [ win ] );
+ deferred.resolve( win );
+ }
+ } else {
+ deferred.reject( new OO.ui.Error(
+ 'Cannot get unmanaged window: symbolic name unrecognized as a managed window'
+ ) );
+ }
+ } else {
+ deferred.resolve( win );
+ }
+
+ return deferred.promise();
+};
+
+/**
+ * Get current window.
+ *
+ * @return {OO.ui.Window|null} Currently opening/opened/closing window
+ */
+OO.ui.WindowManager.prototype.getCurrentWindow = function () {
+ return this.currentWindow;
+};
+
+/**
+ * Open a window.
+ *
+ * @param {OO.ui.Window|string} win Window object or symbolic name of window to open
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} An `opening` promise resolved when the window is done opening.
+ * See {@link #event-opening 'opening' event} for more information about `opening` promises.
+ * @fires opening
+ */
+OO.ui.WindowManager.prototype.openWindow = function ( win, data ) {
+ var manager = this,
+ opening = $.Deferred();
+
+ // Argument handling
+ if ( typeof win === 'string' ) {
+ return this.getWindow( win ).then( function ( win ) {
+ return manager.openWindow( win, data );
+ } );
+ }
+
+ // Error handling
+ if ( !this.hasWindow( win ) ) {
+ opening.reject( new OO.ui.Error(
+ 'Cannot open window: window is not attached to manager'
+ ) );
+ } else if ( this.preparingToOpen || this.opening || this.opened ) {
+ opening.reject( new OO.ui.Error(
+ 'Cannot open window: another window is opening or open'
+ ) );
+ }
+
+ // Window opening
+ if ( opening.state() !== 'rejected' ) {
+ // If a window is currently closing, wait for it to complete
+ this.preparingToOpen = $.when( this.closing );
+ // Ensure handlers get called after preparingToOpen is set
+ this.preparingToOpen.done( function () {
+ if ( manager.modal ) {
+ manager.toggleGlobalEvents( true );
+ manager.toggleAriaIsolation( true );
+ }
+ manager.currentWindow = win;
+ manager.opening = opening;
+ manager.preparingToOpen = null;
+ manager.emit( 'opening', win, opening, data );
+ setTimeout( function () {
+ win.setup( data ).then( function () {
+ manager.updateWindowSize( win );
+ manager.opening.notify( { state: 'setup' } );
+ setTimeout( function () {
+ win.ready( data ).then( function () {
+ manager.opening.notify( { state: 'ready' } );
+ manager.opening = null;
+ manager.opened = $.Deferred();
+ opening.resolve( manager.opened.promise(), data );
+ } );
+ }, manager.getReadyDelay() );
+ } );
+ }, manager.getSetupDelay() );
+ } );
+ }
+
+ return opening.promise();
+};
+
+/**
+ * Close a window.
+ *
+ * @param {OO.ui.Window|string} win Window object or symbolic name of window to close
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} A `closing` promise resolved when the window is done closing.
+ * See {@link #event-closing 'closing' event} for more information about closing promises.
+ * @throws {Error} An error is thrown if the window is not managed by the window manager.
+ * @fires closing
+ */
+OO.ui.WindowManager.prototype.closeWindow = function ( win, data ) {
+ var manager = this,
+ closing = $.Deferred(),
+ opened;
+
+ // Argument handling
+ if ( typeof win === 'string' ) {
+ win = this.windows[ win ];
+ } else if ( !this.hasWindow( win ) ) {
+ win = null;
+ }
+
+ // Error handling
+ if ( !win ) {
+ closing.reject( new OO.ui.Error(
+ 'Cannot close window: window is not attached to manager'
+ ) );
+ } else if ( win !== this.currentWindow ) {
+ closing.reject( new OO.ui.Error(
+ 'Cannot close window: window already closed with different data'
+ ) );
+ } else if ( this.preparingToClose || this.closing ) {
+ closing.reject( new OO.ui.Error(
+ 'Cannot close window: window already closing with different data'
+ ) );
+ }
+
+ // Window closing
+ if ( closing.state() !== 'rejected' ) {
+ // If the window is currently opening, close it when it's done
+ this.preparingToClose = $.when( this.opening );
+ // Ensure handlers get called after preparingToClose is set
+ this.preparingToClose.done( function () {
+ manager.closing = closing;
+ manager.preparingToClose = null;
+ manager.emit( 'closing', win, closing, data );
+ opened = manager.opened;
+ manager.opened = null;
+ opened.resolve( closing.promise(), data );
+ setTimeout( function () {
+ win.hold( data ).then( function () {
+ closing.notify( { state: 'hold' } );
+ setTimeout( function () {
+ win.teardown( data ).then( function () {
+ closing.notify( { state: 'teardown' } );
+ if ( manager.modal ) {
+ manager.toggleGlobalEvents( false );
+ manager.toggleAriaIsolation( false );
+ }
+ manager.closing = null;
+ manager.currentWindow = null;
+ closing.resolve( data );
+ } );
+ }, manager.getTeardownDelay() );
+ } );
+ }, manager.getHoldDelay() );
+ } );
+ }
+
+ return closing.promise();
+};
+
+/**
+ * Add windows to the window manager.
+ *
+ * Windows can be added by reference, symbolic name, or explicitly defined symbolic names.
+ * See the [OOjs ui documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows An array of window objects specified
+ * by reference, symbolic name, or explicitly defined symbolic names.
+ * @throws {Error} An error is thrown if a window is added by symbolic name, but has neither an
+ * explicit nor a statically configured symbolic name.
+ */
+OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
+ var i, len, win, name, list;
+
+ if ( Array.isArray( windows ) ) {
+ // Convert to map of windows by looking up symbolic names from static configuration
+ list = {};
+ for ( i = 0, len = windows.length; i < len; i++ ) {
+ name = windows[ i ].constructor.static.name;
+ if ( typeof name !== 'string' ) {
+ throw new Error( 'Cannot add window' );
+ }
+ list[ name ] = windows[ i ];
+ }
+ } else if ( OO.isPlainObject( windows ) ) {
+ list = windows;
+ }
+
+ // Add windows
+ for ( name in list ) {
+ win = list[ name ];
+ this.windows[ name ] = win.toggle( false );
+ this.$element.append( win.$element );
+ win.setManager( this );
+ }
+};
+
+/**
+ * Remove the specified windows from the windows manager.
+ *
+ * Windows will be closed before they are removed. If you wish to remove all windows, you may wish to use
+ * the #clearWindows method instead. If you no longer need the window manager and want to ensure that it no
+ * longer listens to events, use the #destroy method.
+ *
+ * @param {string[]} names Symbolic names of windows to remove
+ * @return {jQuery.Promise} Promise resolved when window is closed and removed
+ * @throws {Error} An error is thrown if the named windows are not managed by the window manager.
+ */
+OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
+ var i, len, win, name, cleanupWindow,
+ manager = this,
+ promises = [],
+ cleanup = function ( name, win ) {
+ delete manager.windows[ name ];
+ win.$element.detach();
+ };
+
+ for ( i = 0, len = names.length; i < len; i++ ) {
+ name = names[ i ];
+ win = this.windows[ name ];
+ if ( !win ) {
+ throw new Error( 'Cannot remove window' );
+ }
+ cleanupWindow = cleanup.bind( null, name, win );
+ promises.push( this.closeWindow( name ).then( cleanupWindow, cleanupWindow ) );
+ }
+
+ return $.when.apply( $, promises );
+};
+
+/**
+ * Remove all windows from the window manager.
+ *
+ * Windows will be closed before they are removed. Note that the window manager, though not in use, will still
+ * listen to events. If the window manager will not be used again, you may wish to use the #destroy method instead.
+ * To remove just a subset of windows, use the #removeWindows method.
+ *
+ * @return {jQuery.Promise} Promise resolved when all windows are closed and removed
+ */
+OO.ui.WindowManager.prototype.clearWindows = function () {
+ return this.removeWindows( Object.keys( this.windows ) );
+};
+
+/**
+ * Set dialog size. In general, this method should not be called directly.
+ *
+ * Fullscreen mode will be used if the dialog is too wide to fit in the screen.
+ *
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.updateWindowSize = function ( win ) {
+ // Bypass for non-current, and thus invisible, windows
+ if ( win !== this.currentWindow ) {
+ return;
+ }
+
+ var viewport = OO.ui.Element.static.getDimensions( win.getElementWindow() ),
+ sizes = this.constructor.static.sizes,
+ size = win.getSize();
+
+ if ( !sizes[ size ] ) {
+ size = this.constructor.static.defaultSize;
+ }
+ if ( size !== 'full' && viewport.rect.right - viewport.rect.left < sizes[ size ].width ) {
+ size = 'full';
+ }
+
+ this.$element.toggleClass( 'oo-ui-windowManager-fullscreen', size === 'full' );
+ this.$element.toggleClass( 'oo-ui-windowManager-floating', size !== 'full' );
+ win.setDimensions( sizes[ size ] );
+
+ this.emit( 'resize', win );
+
+ return this;
+};
+
+/**
+ * Bind or unbind global events for scrolling.
+ *
+ * @private
+ * @param {boolean} [on] Bind global events
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
+ on = on === undefined ? !!this.globalEvents : !!on;
+
+ var scrollWidth, bodyMargin,
+ $body = $( this.getElementDocument().body ),
+ // We could have multiple window managers open so only modify
+ // the body css at the bottom of the stack
+ stackDepth = $body.data( 'windowManagerGlobalEvents' ) || 0 ;
+
+ if ( on ) {
+ if ( !this.globalEvents ) {
+ $( this.getElementWindow() ).on( {
+ // Start listening for top-level window dimension changes
+ 'orientationchange resize': this.onWindowResizeHandler
+ } );
+ if ( stackDepth === 0 ) {
+ scrollWidth = window.innerWidth - document.documentElement.clientWidth;
+ bodyMargin = parseFloat( $body.css( 'margin-right' ) ) || 0;
+ $body.css( {
+ overflow: 'hidden',
+ 'margin-right': bodyMargin + scrollWidth
+ } );
+ }
+ stackDepth++;
+ this.globalEvents = true;
+ }
+ } else if ( this.globalEvents ) {
+ $( this.getElementWindow() ).off( {
+ // Stop listening for top-level window dimension changes
+ 'orientationchange resize': this.onWindowResizeHandler
+ } );
+ stackDepth--;
+ if ( stackDepth === 0 ) {
+ $body.css( {
+ overflow: '',
+ 'margin-right': ''
+ } );
+ }
+ this.globalEvents = false;
+ }
+ $body.data( 'windowManagerGlobalEvents', stackDepth );
+
+ return this;
+};
+
+/**
+ * Toggle screen reader visibility of content other than the window manager.
+ *
+ * @private
+ * @param {boolean} [isolate] Make only the window manager visible to screen readers
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.toggleAriaIsolation = function ( isolate ) {
+ isolate = isolate === undefined ? !this.$ariaHidden : !!isolate;
+
+ if ( isolate ) {
+ if ( !this.$ariaHidden ) {
+ // Hide everything other than the window manager from screen readers
+ this.$ariaHidden = $( 'body' )
+ .children()
+ .not( this.$element.parentsUntil( 'body' ).last() )
+ .attr( 'aria-hidden', '' );
+ }
+ } else if ( this.$ariaHidden ) {
+ // Restore screen reader visibility
+ this.$ariaHidden.removeAttr( 'aria-hidden' );
+ this.$ariaHidden = null;
+ }
+
+ return this;
+};
+
+/**
+ * Destroy the window manager.
+ *
+ * Destroying the window manager ensures that it will no longer listen to events. If you would like to
+ * continue using the window manager, but wish to remove all windows from it, use the #clearWindows method
+ * instead.
+ */
+OO.ui.WindowManager.prototype.destroy = function () {
+ this.toggleGlobalEvents( false );
+ this.toggleAriaIsolation( false );
+ this.clearWindows();
+ this.$element.remove();
+};
diff --git a/vendor/oojs/oojs-ui/src/core.js b/vendor/oojs/oojs-ui/src/core.js
new file mode 100644
index 00000000..1331f5cc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/core.js
@@ -0,0 +1,286 @@
+/**
+ * Namespace for all classes, static methods and static properties.
+ *
+ * @class
+ * @singleton
+ */
+OO.ui = {};
+
+OO.ui.bind = $.proxy;
+
+/**
+ * @property {Object}
+ */
+OO.ui.Keys = {
+ UNDEFINED: 0,
+ BACKSPACE: 8,
+ DELETE: 46,
+ LEFT: 37,
+ RIGHT: 39,
+ UP: 38,
+ DOWN: 40,
+ ENTER: 13,
+ END: 35,
+ HOME: 36,
+ TAB: 9,
+ PAGEUP: 33,
+ PAGEDOWN: 34,
+ ESCAPE: 27,
+ SHIFT: 16,
+ SPACE: 32
+};
+
+/**
+ * Check if an element is focusable.
+ * Inspired from :focusable in jQueryUI v1.11.4 - 2015-04-14
+ *
+ * @param {jQuery} element Element to test
+ * @return {Boolean} [description]
+ */
+OO.ui.isFocusableElement = function ( $element ) {
+ var node = $element[0],
+ nodeName = node.nodeName.toLowerCase(),
+ // Check if the element have tabindex set
+ isInElementGroup = /^(input|select|textarea|button|object)$/.test( nodeName ),
+ // Check if the element is a link with href or if it has tabindex
+ isOtherElement = (
+ ( nodeName === 'a' && node.href ) ||
+ !isNaN( $element.attr( 'tabindex' ) )
+ ),
+ // Check if the element is visible
+ isVisible = (
+ // This is quicker than calling $element.is( ':visible' )
+ $.expr.filters.visible( node ) &&
+ // Check that all parents are visible
+ !$element.parents().addBack().filter( function () {
+ return $.css( this, 'visibility' ) === 'hidden';
+ } ).length
+ );
+
+ return (
+ ( isInElementGroup ? !node.disabled : isOtherElement ) &&
+ isVisible
+ );
+};
+
+/**
+ * Get the user's language and any fallback languages.
+ *
+ * These language codes are used to localize user interface elements in the user's language.
+ *
+ * In environments that provide a localization system, this function should be overridden to
+ * return the user's language(s). The default implementation returns English (en) only.
+ *
+ * @return {string[]} Language codes, in descending order of priority
+ */
+OO.ui.getUserLanguages = function () {
+ return [ 'en' ];
+};
+
+/**
+ * Get a value in an object keyed by language code.
+ *
+ * @param {Object.<string,Mixed>} obj Object keyed by language code
+ * @param {string|null} [lang] Language code, if omitted or null defaults to any user language
+ * @param {string} [fallback] Fallback code, used if no matching language can be found
+ * @return {Mixed} Local value
+ */
+OO.ui.getLocalValue = function ( obj, lang, fallback ) {
+ var i, len, langs;
+
+ // Requested language
+ if ( obj[ lang ] ) {
+ return obj[ lang ];
+ }
+ // Known user language
+ langs = OO.ui.getUserLanguages();
+ for ( i = 0, len = langs.length; i < len; i++ ) {
+ lang = langs[ i ];
+ if ( obj[ lang ] ) {
+ return obj[ lang ];
+ }
+ }
+ // Fallback language
+ if ( obj[ fallback ] ) {
+ return obj[ fallback ];
+ }
+ // First existing language
+ for ( lang in obj ) {
+ return obj[ lang ];
+ }
+
+ return undefined;
+};
+
+/**
+ * Check if a node is contained within another node
+ *
+ * Similar to jQuery#contains except a list of containers can be supplied
+ * and a boolean argument allows you to include the container in the match list
+ *
+ * @param {HTMLElement|HTMLElement[]} containers Container node(s) to search in
+ * @param {HTMLElement} contained Node to find
+ * @param {boolean} [matchContainers] Include the container(s) in the list of nodes to match, otherwise only match descendants
+ * @return {boolean} The node is in the list of target nodes
+ */
+OO.ui.contains = function ( containers, contained, matchContainers ) {
+ var i;
+ if ( !Array.isArray( containers ) ) {
+ containers = [ containers ];
+ }
+ for ( i = containers.length - 1; i >= 0; i-- ) {
+ if ( ( matchContainers && contained === containers[ i ] ) || $.contains( containers[ i ], contained ) ) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Return a function, that, as long as it continues to be invoked, will not
+ * be triggered. The function will be called after it stops being called for
+ * N milliseconds. If `immediate` is passed, trigger the function on the
+ * leading edge, instead of the trailing.
+ *
+ * Ported from: http://underscorejs.org/underscore.js
+ *
+ * @param {Function} func
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {Function}
+ */
+OO.ui.debounce = function ( func, wait, immediate ) {
+ var timeout;
+ return function () {
+ var context = this,
+ args = arguments,
+ later = function () {
+ timeout = null;
+ if ( !immediate ) {
+ func.apply( context, args );
+ }
+ };
+ if ( immediate && !timeout ) {
+ func.apply( context, args );
+ }
+ clearTimeout( timeout );
+ timeout = setTimeout( later, wait );
+ };
+};
+
+/**
+ * Reconstitute a JavaScript object corresponding to a widget created by
+ * the PHP implementation.
+ *
+ * This is an alias for `OO.ui.Element.static.infuse()`.
+ *
+ * @param {string|HTMLElement|jQuery} idOrNode
+ * A DOM id (if a string) or node for the widget to infuse.
+ * @return {OO.ui.Element}
+ * The `OO.ui.Element` corresponding to this (infusable) document node.
+ */
+OO.ui.infuse = function ( idOrNode ) {
+ return OO.ui.Element.static.infuse( idOrNode );
+};
+
+( function () {
+ /**
+ * Message store for the default implementation of OO.ui.msg
+ *
+ * Environments that provide a localization system should not use this, but should override
+ * OO.ui.msg altogether.
+ *
+ * @private
+ */
+ var messages = {
+ // Tool tip for a button that moves items in a list down one place
+ 'ooui-outline-control-move-down': 'Move item down',
+ // Tool tip for a button that moves items in a list up one place
+ 'ooui-outline-control-move-up': 'Move item up',
+ // Tool tip for a button that removes items from a list
+ 'ooui-outline-control-remove': 'Remove item',
+ // Label for the toolbar group that contains a list of all other available tools
+ 'ooui-toolbar-more': 'More',
+ // Label for the fake tool that expands the full list of tools in a toolbar group
+ 'ooui-toolgroup-expand': 'More',
+ // Label for the fake tool that collapses the full list of tools in a toolbar group
+ 'ooui-toolgroup-collapse': 'Fewer',
+ // Default label for the accept button of a confirmation dialog
+ 'ooui-dialog-message-accept': 'OK',
+ // Default label for the reject button of a confirmation dialog
+ 'ooui-dialog-message-reject': 'Cancel',
+ // Title for process dialog error description
+ 'ooui-dialog-process-error': 'Something went wrong',
+ // Label for process dialog dismiss error button, visible when describing errors
+ 'ooui-dialog-process-dismiss': 'Dismiss',
+ // Label for process dialog retry action button, visible when describing only recoverable errors
+ 'ooui-dialog-process-retry': 'Try again',
+ // Label for process dialog retry action button, visible when describing only warnings
+ 'ooui-dialog-process-continue': 'Continue'
+ };
+
+ /**
+ * Get a localized message.
+ *
+ * In environments that provide a localization system, this function should be overridden to
+ * return the message translated in the user's language. The default implementation always returns
+ * English messages.
+ *
+ * After the message key, message parameters may optionally be passed. In the default implementation,
+ * any occurrences of $1 are replaced with the first parameter, $2 with the second parameter, etc.
+ * Alternative implementations of OO.ui.msg may use any substitution system they like, as long as
+ * they support unnamed, ordered message parameters.
+ *
+ * @abstract
+ * @param {string} key Message key
+ * @param {Mixed...} [params] Message parameters
+ * @return {string} Translated message with parameters substituted
+ */
+ OO.ui.msg = function ( key ) {
+ var message = messages[ key ],
+ params = Array.prototype.slice.call( arguments, 1 );
+ if ( typeof message === 'string' ) {
+ // Perform $1 substitution
+ message = message.replace( /\$(\d+)/g, function ( unused, n ) {
+ var i = parseInt( n, 10 );
+ return params[ i - 1 ] !== undefined ? params[ i - 1 ] : '$' + n;
+ } );
+ } else {
+ // Return placeholder if message not found
+ message = '[' + key + ']';
+ }
+ return message;
+ };
+
+ /**
+ * Package a message and arguments for deferred resolution.
+ *
+ * Use this when you are statically specifying a message and the message may not yet be present.
+ *
+ * @param {string} key Message key
+ * @param {Mixed...} [params] Message parameters
+ * @return {Function} Function that returns the resolved message when executed
+ */
+ OO.ui.deferMsg = function () {
+ var args = arguments;
+ return function () {
+ return OO.ui.msg.apply( OO.ui, args );
+ };
+ };
+
+ /**
+ * Resolve a message.
+ *
+ * If the message is a function it will be executed, otherwise it will pass through directly.
+ *
+ * @param {Function|string} msg Deferred message, or message text
+ * @return {string} Resolved message
+ */
+ OO.ui.resolveMsg = function ( msg ) {
+ if ( $.isFunction( msg ) ) {
+ return msg();
+ }
+ return msg;
+ };
+
+} )();
diff --git a/vendor/oojs/oojs-ui/src/dialogs/MessageDialog.js b/vendor/oojs/oojs-ui/src/dialogs/MessageDialog.js
new file mode 100644
index 00000000..4ec12615
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/dialogs/MessageDialog.js
@@ -0,0 +1,325 @@
+/**
+ * MessageDialogs display a confirmation or alert message. By default, the rendered dialog box
+ * consists of a header that contains the dialog title, a body with the message, and a footer that
+ * contains any {@link OO.ui.ActionWidget action widgets}. The MessageDialog class is the only type
+ * of {@link OO.ui.Dialog dialog} that is usually instantiated directly.
+ *
+ * There are two basic types of message dialogs, confirmation and alert:
+ *
+ * - **confirmation**: the dialog title describes what a progressive action will do and the message provides
+ * more details about the consequences.
+ * - **alert**: the dialog title describes which event occurred and the message provides more information
+ * about why the event occurred.
+ *
+ * The MessageDialog class specifies two actions: ‘accept’, the primary
+ * action (e.g., ‘ok’) and ‘reject,’ the safe action (e.g., ‘cancel’). Both will close the window,
+ * passing along the selected action.
+ *
+ * For more information and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Example: Creating and opening a message dialog window.
+ * var messageDialog = new OO.ui.MessageDialog();
+ *
+ * // Create and append a window manager.
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ * windowManager.addWindows( [ messageDialog ] );
+ * // Open the window.
+ * windowManager.openWindow( messageDialog, {
+ * title: 'Basic message dialog',
+ * message: 'This is the message'
+ * } );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Message_Dialogs
+ *
+ * @class
+ * @extends OO.ui.Dialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MessageDialog = function OoUiMessageDialog( config ) {
+ // Parent constructor
+ OO.ui.MessageDialog.super.call( this, config );
+
+ // Properties
+ this.verticalActionLayout = null;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-messageDialog' );
+};
+
+/* Inheritance */
+
+OO.inheritClass( OO.ui.MessageDialog, OO.ui.Dialog );
+
+/* Static Properties */
+
+OO.ui.MessageDialog.static.name = 'message';
+
+OO.ui.MessageDialog.static.size = 'small';
+
+OO.ui.MessageDialog.static.verbose = false;
+
+/**
+ * Dialog title.
+ *
+ * The title of a confirmation dialog describes what a progressive action will do. The
+ * title of an alert dialog describes which event occurred.
+ *
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function|null}
+ */
+OO.ui.MessageDialog.static.title = null;
+
+/**
+ * The message displayed in the dialog body.
+ *
+ * A confirmation message describes the consequences of a progressive action. An alert
+ * message describes why an event occurred.
+ *
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function|null}
+ */
+OO.ui.MessageDialog.static.message = null;
+
+OO.ui.MessageDialog.static.actions = [
+ { action: 'accept', label: OO.ui.deferMsg( 'ooui-dialog-message-accept' ), flags: 'primary' },
+ { action: 'reject', label: OO.ui.deferMsg( 'ooui-dialog-message-reject' ), flags: 'safe' }
+];
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.setManager = function ( manager ) {
+ OO.ui.MessageDialog.super.prototype.setManager.call( this, manager );
+
+ // Events
+ this.manager.connect( this, {
+ resize: 'onResize'
+ } );
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.onActionResize = function ( action ) {
+ this.fitActions();
+ return OO.ui.MessageDialog.super.prototype.onActionResize.call( this, action );
+};
+
+/**
+ * Handle window resized events.
+ *
+ * @private
+ */
+OO.ui.MessageDialog.prototype.onResize = function () {
+ var dialog = this;
+ dialog.fitActions();
+ // Wait for CSS transition to finish and do it again :(
+ setTimeout( function () {
+ dialog.fitActions();
+ }, 300 );
+};
+
+/**
+ * Toggle action layout between vertical and horizontal.
+ *
+ *
+ * @private
+ * @param {boolean} [value] Layout actions vertically, omit to toggle
+ * @chainable
+ */
+OO.ui.MessageDialog.prototype.toggleVerticalActionLayout = function ( value ) {
+ value = value === undefined ? !this.verticalActionLayout : !!value;
+
+ if ( value !== this.verticalActionLayout ) {
+ this.verticalActionLayout = value;
+ this.$actions
+ .toggleClass( 'oo-ui-messageDialog-actions-vertical', value )
+ .toggleClass( 'oo-ui-messageDialog-actions-horizontal', !value );
+ }
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.getActionProcess = function ( action ) {
+ if ( action ) {
+ return new OO.ui.Process( function () {
+ this.close( { action: action } );
+ }, this );
+ }
+ return OO.ui.MessageDialog.super.prototype.getActionProcess.call( this, action );
+};
+
+/**
+ * @inheritdoc
+ *
+ * @param {Object} [data] Dialog opening data
+ * @param {jQuery|string|Function|null} [data.title] Description of the action being confirmed
+ * @param {jQuery|string|Function|null} [data.message] Description of the action's consequence
+ * @param {boolean} [data.verbose] Message is verbose and should be styled as a long message
+ * @param {Object[]} [data.actions] List of OO.ui.ActionOptionWidget configuration options for each
+ * action item
+ */
+OO.ui.MessageDialog.prototype.getSetupProcess = function ( data ) {
+ data = data || {};
+
+ // Parent method
+ return OO.ui.MessageDialog.super.prototype.getSetupProcess.call( this, data )
+ .next( function () {
+ this.title.setLabel(
+ data.title !== undefined ? data.title : this.constructor.static.title
+ );
+ this.message.setLabel(
+ data.message !== undefined ? data.message : this.constructor.static.message
+ );
+ this.message.$element.toggleClass(
+ 'oo-ui-messageDialog-message-verbose',
+ data.verbose !== undefined ? data.verbose : this.constructor.static.verbose
+ );
+ }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.getBodyHeight = function () {
+ var bodyHeight, oldOverflow,
+ $scrollable = this.container.$element;
+
+ oldOverflow = $scrollable[ 0 ].style.overflow;
+ $scrollable[ 0 ].style.overflow = 'hidden';
+
+ OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
+
+ bodyHeight = this.text.$element.outerHeight( true );
+ $scrollable[ 0 ].style.overflow = oldOverflow;
+
+ return bodyHeight;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.setDimensions = function ( dim ) {
+ var $scrollable = this.container.$element;
+ OO.ui.MessageDialog.super.prototype.setDimensions.call( this, dim );
+
+ // Twiddle the overflow property, otherwise an unnecessary scrollbar will be produced.
+ // Need to do it after transition completes (250ms), add 50ms just in case.
+ setTimeout( function () {
+ var oldOverflow = $scrollable[ 0 ].style.overflow;
+ $scrollable[ 0 ].style.overflow = 'hidden';
+
+ OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
+
+ $scrollable[ 0 ].style.overflow = oldOverflow;
+ }, 300 );
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.initialize = function () {
+ // Parent method
+ OO.ui.MessageDialog.super.prototype.initialize.call( this );
+
+ // Properties
+ this.$actions = $( '<div>' );
+ this.container = new OO.ui.PanelLayout( {
+ scrollable: true, classes: [ 'oo-ui-messageDialog-container' ]
+ } );
+ this.text = new OO.ui.PanelLayout( {
+ padded: true, expanded: false, classes: [ 'oo-ui-messageDialog-text' ]
+ } );
+ this.message = new OO.ui.LabelWidget( {
+ classes: [ 'oo-ui-messageDialog-message' ]
+ } );
+
+ // Initialization
+ this.title.$element.addClass( 'oo-ui-messageDialog-title' );
+ this.$content.addClass( 'oo-ui-messageDialog-content' );
+ this.container.$element.append( this.text.$element );
+ this.text.$element.append( this.title.$element, this.message.$element );
+ this.$body.append( this.container.$element );
+ this.$actions.addClass( 'oo-ui-messageDialog-actions' );
+ this.$foot.append( this.$actions );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.attachActions = function () {
+ var i, len, other, special, others;
+
+ // Parent method
+ OO.ui.MessageDialog.super.prototype.attachActions.call( this );
+
+ special = this.actions.getSpecial();
+ others = this.actions.getOthers();
+ if ( special.safe ) {
+ this.$actions.append( special.safe.$element );
+ special.safe.toggleFramed( false );
+ }
+ if ( others.length ) {
+ for ( i = 0, len = others.length; i < len; i++ ) {
+ other = others[ i ];
+ this.$actions.append( other.$element );
+ other.toggleFramed( false );
+ }
+ }
+ if ( special.primary ) {
+ this.$actions.append( special.primary.$element );
+ special.primary.toggleFramed( false );
+ }
+
+ if ( !this.isOpening() ) {
+ // If the dialog is currently opening, this will be called automatically soon.
+ // This also calls #fitActions.
+ this.updateSize();
+ }
+};
+
+/**
+ * Fit action actions into columns or rows.
+ *
+ * Columns will be used if all labels can fit without overflow, otherwise rows will be used.
+ *
+ * @private
+ */
+OO.ui.MessageDialog.prototype.fitActions = function () {
+ var i, len, action,
+ previous = this.verticalActionLayout,
+ actions = this.actions.get();
+
+ // Detect clipping
+ this.toggleVerticalActionLayout( false );
+ for ( i = 0, len = actions.length; i < len; i++ ) {
+ action = actions[ i ];
+ if ( action.$element.innerWidth() < action.$label.outerWidth( true ) ) {
+ this.toggleVerticalActionLayout( true );
+ break;
+ }
+ }
+
+ // Move the body out of the way of the foot
+ this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
+
+ if ( this.verticalActionLayout !== previous ) {
+ // We changed the layout, window height might need to be updated.
+ this.updateSize();
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/dialogs/ProcessDialog.js b/vendor/oojs/oojs-ui/src/dialogs/ProcessDialog.js
new file mode 100644
index 00000000..d8f7c137
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/dialogs/ProcessDialog.js
@@ -0,0 +1,296 @@
+/**
+ * ProcessDialog windows encapsulate a {@link OO.ui.Process process} and all of the code necessary
+ * to complete it. If the process terminates with an error, a customizable {@link OO.ui.Error error
+ * interface} alerts users to the trouble, permitting the user to dismiss the error and try again when
+ * relevant. The ProcessDialog class is always extended and customized with the actions and content
+ * required for each process.
+ *
+ * The process dialog box consists of a header that visually represents the ‘working’ state of long
+ * processes with an animation. The header contains the dialog title as well as
+ * two {@link OO.ui.ActionWidget action widgets}: a ‘safe’ action on the left (e.g., ‘Cancel’) and
+ * a ‘primary’ action on the right (e.g., ‘Done’).
+ *
+ * Like other windows, the process dialog is managed by a {@link OO.ui.WindowManager window manager}.
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information and examples.
+ *
+ * @example
+ * // Example: Creating and opening a process dialog window.
+ * function MyProcessDialog( config ) {
+ * MyProcessDialog.super.call( this, config );
+ * }
+ * OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
+ *
+ * MyProcessDialog.static.title = 'Process dialog';
+ * MyProcessDialog.static.actions = [
+ * { action: 'save', label: 'Done', flags: 'primary' },
+ * { label: 'Cancel', flags: 'safe' }
+ * ];
+ *
+ * MyProcessDialog.prototype.initialize = function () {
+ * MyProcessDialog.super.prototype.initialize.apply( this, arguments );
+ * this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ * this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action) on the right.</p>' );
+ * this.$body.append( this.content.$element );
+ * };
+ * MyProcessDialog.prototype.getActionProcess = function ( action ) {
+ * var dialog = this;
+ * if ( action ) {
+ * return new OO.ui.Process( function () {
+ * dialog.close( { action: action } );
+ * } );
+ * }
+ * return MyProcessDialog.super.prototype.getActionProcess.call( this, action );
+ * };
+ *
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ *
+ * var dialog = new MyProcessDialog();
+ * windowManager.addWindows( [ dialog ] );
+ * windowManager.openWindow( dialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Dialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ProcessDialog = function OoUiProcessDialog( config ) {
+ // Parent constructor
+ OO.ui.ProcessDialog.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-processDialog' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ProcessDialog, OO.ui.Dialog );
+
+/* Methods */
+
+/**
+ * Handle dismiss button click events.
+ *
+ * Hides errors.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.onDismissErrorButtonClick = function () {
+ this.hideErrors();
+};
+
+/**
+ * Handle retry button click events.
+ *
+ * Hides errors and then tries again.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.onRetryButtonClick = function () {
+ this.hideErrors();
+ this.executeAction( this.currentAction );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.onActionResize = function ( action ) {
+ if ( this.actions.isSpecial( action ) ) {
+ this.fitLabel();
+ }
+ return OO.ui.ProcessDialog.super.prototype.onActionResize.call( this, action );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.initialize = function () {
+ // Parent method
+ OO.ui.ProcessDialog.super.prototype.initialize.call( this );
+
+ // Properties
+ this.$navigation = $( '<div>' );
+ this.$location = $( '<div>' );
+ this.$safeActions = $( '<div>' );
+ this.$primaryActions = $( '<div>' );
+ this.$otherActions = $( '<div>' );
+ this.dismissButton = new OO.ui.ButtonWidget( {
+ label: OO.ui.msg( 'ooui-dialog-process-dismiss' )
+ } );
+ this.retryButton = new OO.ui.ButtonWidget();
+ this.$errors = $( '<div>' );
+ this.$errorsTitle = $( '<div>' );
+
+ // Events
+ this.dismissButton.connect( this, { click: 'onDismissErrorButtonClick' } );
+ this.retryButton.connect( this, { click: 'onRetryButtonClick' } );
+
+ // Initialization
+ this.title.$element.addClass( 'oo-ui-processDialog-title' );
+ this.$location
+ .append( this.title.$element )
+ .addClass( 'oo-ui-processDialog-location' );
+ this.$safeActions.addClass( 'oo-ui-processDialog-actions-safe' );
+ this.$primaryActions.addClass( 'oo-ui-processDialog-actions-primary' );
+ this.$otherActions.addClass( 'oo-ui-processDialog-actions-other' );
+ this.$errorsTitle
+ .addClass( 'oo-ui-processDialog-errors-title' )
+ .text( OO.ui.msg( 'ooui-dialog-process-error' ) );
+ this.$errors
+ .addClass( 'oo-ui-processDialog-errors oo-ui-element-hidden' )
+ .append( this.$errorsTitle, this.dismissButton.$element, this.retryButton.$element );
+ this.$content
+ .addClass( 'oo-ui-processDialog-content' )
+ .append( this.$errors );
+ this.$navigation
+ .addClass( 'oo-ui-processDialog-navigation' )
+ .append( this.$safeActions, this.$location, this.$primaryActions );
+ this.$head.append( this.$navigation );
+ this.$foot.append( this.$otherActions );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.getActionWidgets = function ( actions ) {
+ var i, len, widgets = [];
+ for ( i = 0, len = actions.length; i < len; i++ ) {
+ widgets.push(
+ new OO.ui.ActionWidget( $.extend( { framed: true }, actions[ i ] ) )
+ );
+ }
+ return widgets;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.attachActions = function () {
+ var i, len, other, special, others;
+
+ // Parent method
+ OO.ui.ProcessDialog.super.prototype.attachActions.call( this );
+
+ special = this.actions.getSpecial();
+ others = this.actions.getOthers();
+ if ( special.primary ) {
+ this.$primaryActions.append( special.primary.$element );
+ }
+ for ( i = 0, len = others.length; i < len; i++ ) {
+ other = others[ i ];
+ this.$otherActions.append( other.$element );
+ }
+ if ( special.safe ) {
+ this.$safeActions.append( special.safe.$element );
+ }
+
+ this.fitLabel();
+ this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.executeAction = function ( action ) {
+ var process = this;
+ return OO.ui.ProcessDialog.super.prototype.executeAction.call( this, action )
+ .fail( function ( errors ) {
+ process.showErrors( errors || [] );
+ } );
+};
+
+/**
+ * Fit label between actions.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ProcessDialog.prototype.fitLabel = function () {
+ var width = Math.max(
+ this.$safeActions.is( ':visible' ) ? this.$safeActions.width() : 0,
+ this.$primaryActions.is( ':visible' ) ? this.$primaryActions.width() : 0
+ );
+ this.$location.css( { paddingLeft: width, paddingRight: width } );
+
+ return this;
+};
+
+/**
+ * Handle errors that occurred during accept or reject processes.
+ *
+ * @private
+ * @param {OO.ui.Error[]|OO.ui.Error} errors Errors to be handled
+ */
+OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) {
+ var i, len, $item, actions,
+ items = [],
+ abilities = {},
+ recoverable = true,
+ warning = false;
+
+ if ( errors instanceof OO.ui.Error ) {
+ errors = [ errors ];
+ }
+
+ for ( i = 0, len = errors.length; i < len; i++ ) {
+ if ( !errors[ i ].isRecoverable() ) {
+ recoverable = false;
+ }
+ if ( errors[ i ].isWarning() ) {
+ warning = true;
+ }
+ $item = $( '<div>' )
+ .addClass( 'oo-ui-processDialog-error' )
+ .append( errors[ i ].getMessage() );
+ items.push( $item[ 0 ] );
+ }
+ this.$errorItems = $( items );
+ if ( recoverable ) {
+ abilities[this.currentAction] = true;
+ // Copy the flags from the first matching action
+ actions = this.actions.get( { actions: this.currentAction } );
+ if ( actions.length ) {
+ this.retryButton.clearFlags().setFlags( actions[0].getFlags() );
+ }
+ } else {
+ abilities[this.currentAction] = false;
+ this.actions.setAbilities( abilities );
+ }
+ if ( warning ) {
+ this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-continue' ) );
+ } else {
+ this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-retry' ) );
+ }
+ this.retryButton.toggle( recoverable );
+ this.$errorsTitle.after( this.$errorItems );
+ this.$errors.removeClass( 'oo-ui-element-hidden' ).scrollTop( 0 );
+};
+
+/**
+ * Hide errors.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.hideErrors = function () {
+ this.$errors.addClass( 'oo-ui-element-hidden' );
+ if ( this.$errorItems ) {
+ this.$errorItems.remove();
+ this.$errorItems = null;
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.getTeardownProcess = function ( data ) {
+ // Parent method
+ return OO.ui.ProcessDialog.super.prototype.getTeardownProcess.call( this, data )
+ .first( function () {
+ // Make sure to hide errors
+ this.hideErrors();
+ }, this );
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/ButtonElement.js b/vendor/oojs/oojs-ui/src/elements/ButtonElement.js
new file mode 100644
index 00000000..6d338ed7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/ButtonElement.js
@@ -0,0 +1,263 @@
+/**
+ * ButtonElement is often mixed into other classes to generate a button, which is a clickable
+ * interface element that can be configured with access keys for accessibility.
+ * See the [OOjs UI documentation on MediaWiki] [1] for examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$button] The button element created by the class.
+ * If this configuration is omitted, the button element will use a generated `<a>`.
+ * @cfg {boolean} [framed=true] Render the button with a frame
+ * @cfg {string} [accessKey] Button's access key
+ */
+OO.ui.ButtonElement = function OoUiButtonElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$button = null;
+ this.framed = null;
+ this.accessKey = null;
+ this.active = false;
+ this.onMouseUpHandler = this.onMouseUp.bind( this );
+ this.onMouseDownHandler = this.onMouseDown.bind( this );
+ this.onKeyDownHandler = this.onKeyDown.bind( this );
+ this.onKeyUpHandler = this.onKeyUp.bind( this );
+ this.onClickHandler = this.onClick.bind( this );
+ this.onKeyPressHandler = this.onKeyPress.bind( this );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-buttonElement' );
+ this.toggleFramed( config.framed === undefined || config.framed );
+ this.setAccessKey( config.accessKey );
+ this.setButtonElement( config.$button || $( '<a>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.ButtonElement );
+
+/* Static Properties */
+
+/**
+ * Cancel mouse down events.
+ *
+ * This property is usually set to `true` to prevent the focus from changing when the button is clicked.
+ * Classes such as {@link OO.ui.DraggableElement DraggableElement} and {@link OO.ui.ButtonOptionWidget ButtonOptionWidget}
+ * use a value of `false` so that dragging behavior is possible and mousedown events can be handled by a
+ * parent widget.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.ButtonElement.static.cancelButtonMouseDownEvents = true;
+
+/* Events */
+
+/**
+ * A 'click' event is emitted when the button element is clicked.
+ *
+ * @event click
+ */
+
+/* Methods */
+
+/**
+ * Set the button element.
+ *
+ * This method is used to retarget a button mixin so that its functionality applies to
+ * the specified button element instead of the one created by the class. If a button element
+ * is already set, the method will remove the mixin’s effect on that element.
+ *
+ * @param {jQuery} $button Element to use as button
+ */
+OO.ui.ButtonElement.prototype.setButtonElement = function ( $button ) {
+ if ( this.$button ) {
+ this.$button
+ .removeClass( 'oo-ui-buttonElement-button' )
+ .removeAttr( 'role accesskey' )
+ .off( {
+ mousedown: this.onMouseDownHandler,
+ keydown: this.onKeyDownHandler,
+ click: this.onClickHandler,
+ keypress: this.onKeyPressHandler
+ } );
+ }
+
+ this.$button = $button
+ .addClass( 'oo-ui-buttonElement-button' )
+ .attr( { role: 'button', accesskey: this.accessKey } )
+ .on( {
+ mousedown: this.onMouseDownHandler,
+ keydown: this.onKeyDownHandler,
+ click: this.onClickHandler,
+ keypress: this.onKeyPressHandler
+ } );
+};
+
+/**
+ * Handles mouse down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.ButtonElement.prototype.onMouseDown = function ( e ) {
+ if ( this.isDisabled() || e.which !== 1 ) {
+ return;
+ }
+ this.$element.addClass( 'oo-ui-buttonElement-pressed' );
+ // Run the mouseup handler no matter where the mouse is when the button is let go, so we can
+ // reliably remove the pressed class
+ this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
+ // Prevent change of focus unless specifically configured otherwise
+ if ( this.constructor.static.cancelButtonMouseDownEvents ) {
+ return false;
+ }
+};
+
+/**
+ * Handles mouse up events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse up event
+ */
+OO.ui.ButtonElement.prototype.onMouseUp = function ( e ) {
+ if ( this.isDisabled() || e.which !== 1 ) {
+ return;
+ }
+ this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
+ // Stop listening for mouseup, since we only needed this once
+ this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
+};
+
+/**
+ * Handles mouse click events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse click event
+ * @fires click
+ */
+OO.ui.ButtonElement.prototype.onClick = function ( e ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
+ if ( this.emit( 'click' ) ) {
+ return false;
+ }
+ }
+};
+
+/**
+ * Handles key down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.ButtonElement.prototype.onKeyDown = function ( e ) {
+ if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
+ return;
+ }
+ this.$element.addClass( 'oo-ui-buttonElement-pressed' );
+ // Run the keyup handler no matter where the key is when the button is let go, so we can
+ // reliably remove the pressed class
+ this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
+};
+
+/**
+ * Handles key up events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key up event
+ */
+OO.ui.ButtonElement.prototype.onKeyUp = function ( e ) {
+ if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
+ return;
+ }
+ this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
+ // Stop listening for keyup, since we only needed this once
+ this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
+};
+
+/**
+ * Handles key press events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key press event
+ * @fires click
+ */
+OO.ui.ButtonElement.prototype.onKeyPress = function ( e ) {
+ if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+ if ( this.emit( 'click' ) ) {
+ return false;
+ }
+ }
+};
+
+/**
+ * Check if button has a frame.
+ *
+ * @return {boolean} Button is framed
+ */
+OO.ui.ButtonElement.prototype.isFramed = function () {
+ return this.framed;
+};
+
+/**
+ * Render the button with or without a frame. Omit the `framed` parameter to toggle the button frame on and off.
+ *
+ * @param {boolean} [framed] Make button framed, omit to toggle
+ * @chainable
+ */
+OO.ui.ButtonElement.prototype.toggleFramed = function ( framed ) {
+ framed = framed === undefined ? !this.framed : !!framed;
+ if ( framed !== this.framed ) {
+ this.framed = framed;
+ this.$element
+ .toggleClass( 'oo-ui-buttonElement-frameless', !framed )
+ .toggleClass( 'oo-ui-buttonElement-framed', framed );
+ this.updateThemeClasses();
+ }
+
+ return this;
+};
+
+/**
+ * Set the button's access key.
+ *
+ * @param {string} accessKey Button's access key, use empty string to remove
+ * @chainable
+ */
+OO.ui.ButtonElement.prototype.setAccessKey = function ( accessKey ) {
+ accessKey = typeof accessKey === 'string' && accessKey.length ? accessKey : null;
+
+ if ( this.accessKey !== accessKey ) {
+ if ( this.$button ) {
+ if ( accessKey !== null ) {
+ this.$button.attr( 'accesskey', accessKey );
+ } else {
+ this.$button.removeAttr( 'accesskey' );
+ }
+ }
+ this.accessKey = accessKey;
+ }
+
+ return this;
+};
+
+/**
+ * Set the button to its 'active' state.
+ *
+ * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
+ * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
+ * for other button types.
+ *
+ * @param {boolean} [value] Make button active
+ * @chainable
+ */
+OO.ui.ButtonElement.prototype.setActive = function ( value ) {
+ this.$element.toggleClass( 'oo-ui-buttonElement-active', !!value );
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/ClippableElement.js b/vendor/oojs/oojs-ui/src/elements/ClippableElement.js
new file mode 100644
index 00000000..33b0b234
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/ClippableElement.js
@@ -0,0 +1,205 @@
+/**
+ * Element that can be automatically clipped to visible boundaries.
+ *
+ * Whenever the element's natural height changes, you have to call
+ * #clip to make sure it's still clipping correctly.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$clippable] Nodes to clip, assigned to #$clippable, omit to use #$element
+ */
+OO.ui.ClippableElement = function OoUiClippableElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$clippable = null;
+ this.clipping = false;
+ this.clippedHorizontally = false;
+ this.clippedVertically = false;
+ this.$clippableContainer = null;
+ this.$clippableScroller = null;
+ this.$clippableWindow = null;
+ this.idealWidth = null;
+ this.idealHeight = null;
+ this.onClippableContainerScrollHandler = this.clip.bind( this );
+ this.onClippableWindowResizeHandler = this.clip.bind( this );
+
+ // Initialization
+ this.setClippableElement( config.$clippable || this.$element );
+};
+
+/* Methods */
+
+/**
+ * Set clippable element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $clippable Element to make clippable
+ */
+OO.ui.ClippableElement.prototype.setClippableElement = function ( $clippable ) {
+ if ( this.$clippable ) {
+ this.$clippable.removeClass( 'oo-ui-clippableElement-clippable' );
+ this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
+ OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+ }
+
+ this.$clippable = $clippable.addClass( 'oo-ui-clippableElement-clippable' );
+ this.clip();
+};
+
+/**
+ * Toggle clipping.
+ *
+ * Do not turn clipping on until after the element is attached to the DOM and visible.
+ *
+ * @param {boolean} [clipping] Enable clipping, omit to toggle
+ * @chainable
+ */
+OO.ui.ClippableElement.prototype.toggleClipping = function ( clipping ) {
+ clipping = clipping === undefined ? !this.clipping : !!clipping;
+
+ if ( this.clipping !== clipping ) {
+ this.clipping = clipping;
+ if ( clipping ) {
+ this.$clippableContainer = $( this.getClosestScrollableElementContainer() );
+ // If the clippable container is the root, we have to listen to scroll events and check
+ // jQuery.scrollTop on the window because of browser inconsistencies
+ this.$clippableScroller = this.$clippableContainer.is( 'html, body' ) ?
+ $( OO.ui.Element.static.getWindow( this.$clippableContainer ) ) :
+ this.$clippableContainer;
+ this.$clippableScroller.on( 'scroll', this.onClippableContainerScrollHandler );
+ this.$clippableWindow = $( this.getElementWindow() )
+ .on( 'resize', this.onClippableWindowResizeHandler );
+ // Initial clip after visible
+ this.clip();
+ } else {
+ this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
+ OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+
+ this.$clippableContainer = null;
+ this.$clippableScroller.off( 'scroll', this.onClippableContainerScrollHandler );
+ this.$clippableScroller = null;
+ this.$clippableWindow.off( 'resize', this.onClippableWindowResizeHandler );
+ this.$clippableWindow = null;
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Check if the element will be clipped to fit the visible area of the nearest scrollable container.
+ *
+ * @return {boolean} Element will be clipped to the visible area
+ */
+OO.ui.ClippableElement.prototype.isClipping = function () {
+ return this.clipping;
+};
+
+/**
+ * Check if the bottom or right of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.ClippableElement.prototype.isClipped = function () {
+ return this.clippedHorizontally || this.clippedVertically;
+};
+
+/**
+ * Check if the right of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.ClippableElement.prototype.isClippedHorizontally = function () {
+ return this.clippedHorizontally;
+};
+
+/**
+ * Check if the bottom of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.ClippableElement.prototype.isClippedVertically = function () {
+ return this.clippedVertically;
+};
+
+/**
+ * Set the ideal size. These are the dimensions the element will have when it's not being clipped.
+ *
+ * @param {number|string} [width] Width as a number of pixels or CSS string with unit suffix
+ * @param {number|string} [height] Height as a number of pixels or CSS string with unit suffix
+ */
+OO.ui.ClippableElement.prototype.setIdealSize = function ( width, height ) {
+ this.idealWidth = width;
+ this.idealHeight = height;
+
+ if ( !this.clipping ) {
+ // Update dimensions
+ this.$clippable.css( { width: width, height: height } );
+ }
+ // While clipping, idealWidth and idealHeight are not considered
+};
+
+/**
+ * Clip element to visible boundaries and allow scrolling when needed. Call this method when
+ * the element's natural height changes.
+ *
+ * Element will be clipped the bottom or right of the element is within 10px of the edge of, or
+ * overlapped by, the visible area of the nearest scrollable container.
+ *
+ * @chainable
+ */
+OO.ui.ClippableElement.prototype.clip = function () {
+ if ( !this.clipping ) {
+ // this.$clippableContainer and this.$clippableWindow are null, so the below will fail
+ return this;
+ }
+
+ var buffer = 7, // Chosen by fair dice roll
+ cOffset = this.$clippable.offset(),
+ $container = this.$clippableContainer.is( 'html, body' ) ?
+ this.$clippableWindow : this.$clippableContainer,
+ ccOffset = $container.offset() || { top: 0, left: 0 },
+ ccHeight = $container.innerHeight() - buffer,
+ ccWidth = $container.innerWidth() - buffer,
+ cHeight = this.$clippable.outerHeight() + buffer,
+ cWidth = this.$clippable.outerWidth() + buffer,
+ scrollTop = this.$clippableScroller.scrollTop(),
+ scrollLeft = this.$clippableScroller.scrollLeft(),
+ desiredWidth = cOffset.left < 0 ?
+ cWidth + cOffset.left :
+ ( ccOffset.left + scrollLeft + ccWidth ) - cOffset.left,
+ desiredHeight = cOffset.top < 0 ?
+ cHeight + cOffset.top :
+ ( ccOffset.top + scrollTop + ccHeight ) - cOffset.top,
+ naturalWidth = this.$clippable.prop( 'scrollWidth' ),
+ naturalHeight = this.$clippable.prop( 'scrollHeight' ),
+ clipWidth = desiredWidth < naturalWidth,
+ clipHeight = desiredHeight < naturalHeight;
+
+ if ( clipWidth ) {
+ this.$clippable.css( { overflowX: 'scroll', width: desiredWidth } );
+ } else {
+ this.$clippable.css( { width: this.idealWidth || '', overflowX: '' } );
+ }
+ if ( clipHeight ) {
+ this.$clippable.css( { overflowY: 'scroll', height: desiredHeight } );
+ } else {
+ this.$clippable.css( { height: this.idealHeight || '', overflowY: '' } );
+ }
+
+ // If we stopped clipping in at least one of the dimensions
+ if ( !clipWidth || !clipHeight ) {
+ OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+ }
+
+ this.clippedHorizontally = clipWidth;
+ this.clippedVertically = clipHeight;
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/DraggableElement.js b/vendor/oojs/oojs-ui/src/elements/DraggableElement.js
new file mode 100644
index 00000000..9ae4d5bb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/DraggableElement.js
@@ -0,0 +1,142 @@
+/**
+ * DraggableElement is a mixin class used to create elements that can be clicked
+ * and dragged by a mouse to a new position within a group. This class must be used
+ * in conjunction with OO.ui.DraggableGroupElement, which provides a container for
+ * the draggable elements.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ */
+OO.ui.DraggableElement = function OoUiDraggableElement() {
+ // Properties
+ this.index = null;
+
+ // Initialize and events
+ this.$element
+ .attr( 'draggable', true )
+ .addClass( 'oo-ui-draggableElement' )
+ .on( {
+ dragstart: this.onDragStart.bind( this ),
+ dragover: this.onDragOver.bind( this ),
+ dragend: this.onDragEnd.bind( this ),
+ drop: this.onDrop.bind( this )
+ } );
+};
+
+OO.initClass( OO.ui.DraggableElement );
+
+/* Events */
+
+/**
+ * @event dragstart
+ *
+ * A dragstart event is emitted when the user clicks and begins dragging an item.
+ * @param {OO.ui.DraggableElement} item The item the user has clicked and is dragging with the mouse.
+ */
+
+/**
+ * @event dragend
+ * A dragend event is emitted when the user drags an item and releases the mouse,
+ * thus terminating the drag operation.
+ */
+
+/**
+ * @event drop
+ * A drop event is emitted when the user drags an item and then releases the mouse button
+ * over a valid target.
+ */
+
+/* Static Properties */
+
+/**
+ * @inheritdoc OO.ui.ButtonElement
+ */
+OO.ui.DraggableElement.static.cancelButtonMouseDownEvents = false;
+
+/* Methods */
+
+/**
+ * Respond to dragstart event.
+ *
+ * @private
+ * @param {jQuery.Event} event jQuery event
+ * @fires dragstart
+ */
+OO.ui.DraggableElement.prototype.onDragStart = function ( e ) {
+ var dataTransfer = e.originalEvent.dataTransfer;
+ // Define drop effect
+ dataTransfer.dropEffect = 'none';
+ dataTransfer.effectAllowed = 'move';
+ // We must set up a dataTransfer data property or Firefox seems to
+ // ignore the fact the element is draggable.
+ try {
+ dataTransfer.setData( 'application-x/OOjs-UI-draggable', this.getIndex() );
+ } catch ( err ) {
+ // The above is only for firefox. No need to set a catch clause
+ // if it fails, move on.
+ }
+ // Add dragging class
+ this.$element.addClass( 'oo-ui-draggableElement-dragging' );
+ // Emit event
+ this.emit( 'dragstart', this );
+ return true;
+};
+
+/**
+ * Respond to dragend event.
+ *
+ * @private
+ * @fires dragend
+ */
+OO.ui.DraggableElement.prototype.onDragEnd = function () {
+ this.$element.removeClass( 'oo-ui-draggableElement-dragging' );
+ this.emit( 'dragend' );
+};
+
+/**
+ * Handle drop event.
+ *
+ * @private
+ * @param {jQuery.Event} event jQuery event
+ * @fires drop
+ */
+OO.ui.DraggableElement.prototype.onDrop = function ( e ) {
+ e.preventDefault();
+ this.emit( 'drop', e );
+};
+
+/**
+ * In order for drag/drop to work, the dragover event must
+ * return false and stop propogation.
+ *
+ * @private
+ */
+OO.ui.DraggableElement.prototype.onDragOver = function ( e ) {
+ e.preventDefault();
+};
+
+/**
+ * Set item index.
+ * Store it in the DOM so we can access from the widget drag event
+ *
+ * @private
+ * @param {number} Item index
+ */
+OO.ui.DraggableElement.prototype.setIndex = function ( index ) {
+ if ( this.index !== index ) {
+ this.index = index;
+ this.$element.data( 'index', index );
+ }
+};
+
+/**
+ * Get item index
+ *
+ * @private
+ * @return {number} Item index
+ */
+OO.ui.DraggableElement.prototype.getIndex = function () {
+ return this.index;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/DraggableGroupElement.js b/vendor/oojs/oojs-ui/src/elements/DraggableGroupElement.js
new file mode 100644
index 00000000..134e2953
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/DraggableGroupElement.js
@@ -0,0 +1,261 @@
+/**
+ * DraggableGroupElement is a mixin class used to create a group element to
+ * contain draggable elements, which are items that can be clicked and dragged by a mouse.
+ * The class is used with OO.ui.DraggableElement.
+ *
+ * @abstract
+ * @class
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [orientation] Item orientation: 'horizontal' or 'vertical'. The orientation
+ * should match the layout of the items. Items displayed in a single row
+ * or in several rows should use horizontal orientation. The vertical orientation should only be
+ * used when the items are displayed in a single column. Defaults to 'vertical'
+ */
+OO.ui.DraggableGroupElement = function OoUiDraggableGroupElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.GroupElement.call( this, config );
+
+ // Properties
+ this.orientation = config.orientation || 'vertical';
+ this.dragItem = null;
+ this.itemDragOver = null;
+ this.itemKeys = {};
+ this.sideInsertion = '';
+
+ // Events
+ this.aggregate( {
+ dragstart: 'itemDragStart',
+ dragend: 'itemDragEnd',
+ drop: 'itemDrop'
+ } );
+ this.connect( this, {
+ itemDragStart: 'onItemDragStart',
+ itemDrop: 'onItemDrop',
+ itemDragEnd: 'onItemDragEnd'
+ } );
+ this.$element.on( {
+ dragover: $.proxy( this.onDragOver, this ),
+ dragleave: $.proxy( this.onDragLeave, this )
+ } );
+
+ // Initialize
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+ this.$placeholder = $( '<div>' )
+ .addClass( 'oo-ui-draggableGroupElement-placeholder' );
+ this.$element
+ .addClass( 'oo-ui-draggableGroupElement' )
+ .append( this.$status )
+ .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' )
+ .prepend( this.$placeholder );
+};
+
+/* Setup */
+OO.mixinClass( OO.ui.DraggableGroupElement, OO.ui.GroupElement );
+
+/* Events */
+
+/**
+ * A 'reorder' event is emitted when the order of items in the group changes.
+ *
+ * @event reorder
+ * @param {OO.ui.DraggableElement} item Reordered item
+ * @param {number} [newIndex] New index for the item
+ */
+
+/* Methods */
+
+/**
+ * Respond to item drag start event
+ *
+ * @private
+ * @param {OO.ui.DraggableElement} item Dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
+ var i, len;
+
+ // Map the index of each object
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ this.items[ i ].setIndex( i );
+ }
+
+ if ( this.orientation === 'horizontal' ) {
+ // Set the height of the indicator
+ this.$placeholder.css( {
+ height: item.$element.outerHeight(),
+ width: 2
+ } );
+ } else {
+ // Set the width of the indicator
+ this.$placeholder.css( {
+ height: 2,
+ width: item.$element.outerWidth()
+ } );
+ }
+ this.setDragItem( item );
+};
+
+/**
+ * Respond to item drag end event
+ *
+ * @private
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDragEnd = function () {
+ this.unsetDragItem();
+ return false;
+};
+
+/**
+ * Handle drop event and switch the order of the items accordingly
+ *
+ * @private
+ * @param {OO.ui.DraggableElement} item Dropped item
+ * @fires reorder
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
+ var toIndex = item.getIndex();
+ // Check if the dropped item is from the current group
+ // TODO: Figure out a way to configure a list of legally droppable
+ // elements even if they are not yet in the list
+ if ( this.getDragItem() ) {
+ // If the insertion point is 'after', the insertion index
+ // is shifted to the right (or to the left in RTL, hence 'after')
+ if ( this.sideInsertion === 'after' ) {
+ toIndex++;
+ }
+ // Emit change event
+ this.emit( 'reorder', this.getDragItem(), toIndex );
+ }
+ this.unsetDragItem();
+ // Return false to prevent propogation
+ return false;
+};
+
+/**
+ * Handle dragleave event.
+ *
+ * @private
+ */
+OO.ui.DraggableGroupElement.prototype.onDragLeave = function () {
+ // This means the item was dragged outside the widget
+ this.$placeholder
+ .css( 'left', 0 )
+ .addClass( 'oo-ui-element-hidden' );
+};
+
+/**
+ * Respond to dragover event
+ *
+ * @private
+ * @param {jQuery.Event} event Event details
+ */
+OO.ui.DraggableGroupElement.prototype.onDragOver = function ( e ) {
+ var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
+ itemSize, cssOutput, dragPosition, itemIndex, itemPosition,
+ clientX = e.originalEvent.clientX,
+ clientY = e.originalEvent.clientY;
+
+ // Get the OptionWidget item we are dragging over
+ dragOverObj = this.getElementDocument().elementFromPoint( clientX, clientY );
+ $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' );
+ if ( $optionWidget[ 0 ] ) {
+ itemOffset = $optionWidget.offset();
+ itemBoundingRect = $optionWidget[ 0 ].getBoundingClientRect();
+ itemPosition = $optionWidget.position();
+ itemIndex = $optionWidget.data( 'index' );
+ }
+
+ if (
+ itemOffset &&
+ this.isDragging() &&
+ itemIndex !== this.getDragItem().getIndex()
+ ) {
+ if ( this.orientation === 'horizontal' ) {
+ // Calculate where the mouse is relative to the item width
+ itemSize = itemBoundingRect.width;
+ itemMidpoint = itemBoundingRect.left + itemSize / 2;
+ dragPosition = clientX;
+ // Which side of the item we hover over will dictate
+ // where the placeholder will appear, on the left or
+ // on the right
+ cssOutput = {
+ left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
+ top: itemPosition.top
+ };
+ } else {
+ // Calculate where the mouse is relative to the item height
+ itemSize = itemBoundingRect.height;
+ itemMidpoint = itemBoundingRect.top + itemSize / 2;
+ dragPosition = clientY;
+ // Which side of the item we hover over will dictate
+ // where the placeholder will appear, on the top or
+ // on the bottom
+ cssOutput = {
+ top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
+ left: itemPosition.left
+ };
+ }
+ // Store whether we are before or after an item to rearrange
+ // For horizontal layout, we need to account for RTL, as this is flipped
+ if ( this.orientation === 'horizontal' && this.$element.css( 'direction' ) === 'rtl' ) {
+ this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before';
+ } else {
+ this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after';
+ }
+ // Add drop indicator between objects
+ this.$placeholder
+ .css( cssOutput )
+ .removeClass( 'oo-ui-element-hidden' );
+ } else {
+ // This means the item was dragged outside the widget
+ this.$placeholder
+ .css( 'left', 0 )
+ .addClass( 'oo-ui-element-hidden' );
+ }
+ // Prevent default
+ e.preventDefault();
+};
+
+/**
+ * Set a dragged item
+ *
+ * @param {OO.ui.DraggableElement} item Dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.setDragItem = function ( item ) {
+ this.dragItem = item;
+};
+
+/**
+ * Unset the current dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.unsetDragItem = function () {
+ this.dragItem = null;
+ this.itemDragOver = null;
+ this.$placeholder.addClass( 'oo-ui-element-hidden' );
+ this.sideInsertion = '';
+};
+
+/**
+ * Get the item that is currently being dragged.
+ *
+ * @return {OO.ui.DraggableElement|null} The currently dragged item, or `null` if no item is being dragged
+ */
+OO.ui.DraggableGroupElement.prototype.getDragItem = function () {
+ return this.dragItem;
+};
+
+/**
+ * Check if an item in the group is currently being dragged.
+ *
+ * @return {Boolean} Item is being dragged
+ */
+OO.ui.DraggableGroupElement.prototype.isDragging = function () {
+ return this.getDragItem() !== null;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/FlaggedElement.js b/vendor/oojs/oojs-ui/src/elements/FlaggedElement.js
new file mode 100644
index 00000000..7050f696
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/FlaggedElement.js
@@ -0,0 +1,209 @@
+/**
+ * The FlaggedElement class is an attribute mixin, meaning that it is used to add
+ * additional functionality to an element created by another class. The class provides
+ * a ‘flags’ property assigned the name (or an array of names) of styling flags,
+ * which are used to customize the look and feel of a widget to better describe its
+ * importance and functionality.
+ *
+ * The library currently contains the following styling flags for general use:
+ *
+ * - **progressive**: Progressive styling is applied to convey that the widget will move the user forward in a process.
+ * - **destructive**: Destructive styling is applied to convey that the widget will remove something.
+ * - **constructive**: Constructive styling is applied to convey that the widget will create something.
+ *
+ * The flags affect the appearance of the buttons:
+ *
+ * @example
+ * // FlaggedElement is mixed into ButtonWidget to provide styling flags
+ * var button1 = new OO.ui.ButtonWidget( {
+ * label: 'Constructive',
+ * flags: 'constructive'
+ * } );
+ * var button2 = new OO.ui.ButtonWidget( {
+ * label: 'Destructive',
+ * flags: 'destructive'
+ * } );
+ * var button3 = new OO.ui.ButtonWidget( {
+ * label: 'Progressive',
+ * flags: 'progressive'
+ * } );
+ * $( 'body' ).append( button1.$element, button2.$element, button3.$element );
+ *
+ * {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'constructive' or 'primary') to apply.
+ * Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ * @cfg {jQuery} [$flagged] The flagged element. By default,
+ * the flagged functionality is applied to the element created by the class ($element).
+ * If a different element is specified, the flagged functionality will be applied to it instead.
+ */
+OO.ui.FlaggedElement = function OoUiFlaggedElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.flags = {};
+ this.$flagged = null;
+
+ // Initialization
+ this.setFlags( config.flags );
+ this.setFlaggedElement( config.$flagged || this.$element );
+};
+
+/* Events */
+
+/**
+ * @event flag
+ * A flag event is emitted when the #clearFlags or #setFlags methods are used. The `changes`
+ * parameter contains the name of each modified flag and indicates whether it was
+ * added or removed.
+ *
+ * @param {Object.<string,boolean>} changes Object keyed by flag name. A Boolean `true` indicates
+ * that the flag was added, `false` that the flag was removed.
+ */
+
+/* Methods */
+
+/**
+ * Set the flagged element.
+ *
+ * This method is used to retarget a flagged mixin so that its functionality applies to the specified element.
+ * If an element is already set, the method will remove the mixin’s effect on that element.
+ *
+ * @param {jQuery} $flagged Element that should be flagged
+ */
+OO.ui.FlaggedElement.prototype.setFlaggedElement = function ( $flagged ) {
+ var classNames = Object.keys( this.flags ).map( function ( flag ) {
+ return 'oo-ui-flaggedElement-' + flag;
+ } ).join( ' ' );
+
+ if ( this.$flagged ) {
+ this.$flagged.removeClass( classNames );
+ }
+
+ this.$flagged = $flagged.addClass( classNames );
+};
+
+/**
+ * Check if the specified flag is set.
+ *
+ * @param {string} flag Name of flag
+ * @return {boolean} The flag is set
+ */
+OO.ui.FlaggedElement.prototype.hasFlag = function ( flag ) {
+ return flag in this.flags;
+};
+
+/**
+ * Get the names of all flags set.
+ *
+ * @return {string[]} Flag names
+ */
+OO.ui.FlaggedElement.prototype.getFlags = function () {
+ return Object.keys( this.flags );
+};
+
+/**
+ * Clear all flags.
+ *
+ * @chainable
+ * @fires flag
+ */
+OO.ui.FlaggedElement.prototype.clearFlags = function () {
+ var flag, className,
+ changes = {},
+ remove = [],
+ classPrefix = 'oo-ui-flaggedElement-';
+
+ for ( flag in this.flags ) {
+ className = classPrefix + flag;
+ changes[ flag ] = false;
+ delete this.flags[ flag ];
+ remove.push( className );
+ }
+
+ if ( this.$flagged ) {
+ this.$flagged.removeClass( remove.join( ' ' ) );
+ }
+
+ this.updateThemeClasses();
+ this.emit( 'flag', changes );
+
+ return this;
+};
+
+/**
+ * Add one or more flags.
+ *
+ * @param {string|string[]|Object.<string, boolean>} flags A flag name, an array of flag names,
+ * or an object keyed by flag name with a boolean value that indicates whether the flag should
+ * be added (`true`) or removed (`false`).
+ * @chainable
+ * @fires flag
+ */
+OO.ui.FlaggedElement.prototype.setFlags = function ( flags ) {
+ var i, len, flag, className,
+ changes = {},
+ add = [],
+ remove = [],
+ classPrefix = 'oo-ui-flaggedElement-';
+
+ if ( typeof flags === 'string' ) {
+ className = classPrefix + flags;
+ // Set
+ if ( !this.flags[ flags ] ) {
+ this.flags[ flags ] = true;
+ add.push( className );
+ }
+ } else if ( Array.isArray( flags ) ) {
+ for ( i = 0, len = flags.length; i < len; i++ ) {
+ flag = flags[ i ];
+ className = classPrefix + flag;
+ // Set
+ if ( !this.flags[ flag ] ) {
+ changes[ flag ] = true;
+ this.flags[ flag ] = true;
+ add.push( className );
+ }
+ }
+ } else if ( OO.isPlainObject( flags ) ) {
+ for ( flag in flags ) {
+ className = classPrefix + flag;
+ if ( flags[ flag ] ) {
+ // Set
+ if ( !this.flags[ flag ] ) {
+ changes[ flag ] = true;
+ this.flags[ flag ] = true;
+ add.push( className );
+ }
+ } else {
+ // Remove
+ if ( this.flags[ flag ] ) {
+ changes[ flag ] = false;
+ delete this.flags[ flag ];
+ remove.push( className );
+ }
+ }
+ }
+ }
+
+ if ( this.$flagged ) {
+ this.$flagged
+ .addClass( add.join( ' ' ) )
+ .removeClass( remove.join( ' ' ) );
+ }
+
+ this.updateThemeClasses();
+ this.emit( 'flag', changes );
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/GroupElement.js b/vendor/oojs/oojs-ui/src/elements/GroupElement.js
new file mode 100644
index 00000000..51cf5d25
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/GroupElement.js
@@ -0,0 +1,290 @@
+/**
+ * Any OOjs UI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
+ * {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
+ * items from the group is done through the interface the class provides.
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Groups
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$group] The container element created by the class. If this configuration
+ * is omitted, the group element will use a generated `<div>`.
+ */
+OO.ui.GroupElement = function OoUiGroupElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$group = null;
+ this.items = [];
+ this.aggregateItemEvents = {};
+
+ // Initialization
+ this.setGroupElement( config.$group || $( '<div>' ) );
+};
+
+/* Methods */
+
+/**
+ * Set the group element.
+ *
+ * If an element is already set, items will be moved to the new element.
+ *
+ * @param {jQuery} $group Element to use as group
+ */
+OO.ui.GroupElement.prototype.setGroupElement = function ( $group ) {
+ var i, len;
+
+ this.$group = $group;
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ this.$group.append( this.items[ i ].$element );
+ }
+};
+
+/**
+ * Check if a group contains no items.
+ *
+ * @return {boolean} Group is empty
+ */
+OO.ui.GroupElement.prototype.isEmpty = function () {
+ return !this.items.length;
+};
+
+/**
+ * Get all items in the group.
+ *
+ * The method returns an array of item references (e.g., [button1, button2, button3]) and is useful
+ * when synchronizing groups of items, or whenever the references are required (e.g., when removing items
+ * from a group).
+ *
+ * @return {OO.ui.Element[]} An array of items.
+ */
+OO.ui.GroupElement.prototype.getItems = function () {
+ return this.items.slice( 0 );
+};
+
+/**
+ * Get an item by its data.
+ *
+ * Only the first item with matching data will be returned. To return all matching items,
+ * use the #getItemsFromData method.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
+ */
+OO.ui.GroupElement.prototype.getItemFromData = function ( data ) {
+ var i, len, item,
+ hash = OO.getHash( data );
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if ( hash === OO.getHash( item.getData() ) ) {
+ return item;
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Get items by their data.
+ *
+ * All items with matching data will be returned. To return only the first match, use the #getItemFromData method instead.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element[]} Items with equivalent data
+ */
+OO.ui.GroupElement.prototype.getItemsFromData = function ( data ) {
+ var i, len, item,
+ hash = OO.getHash( data ),
+ items = [];
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if ( hash === OO.getHash( item.getData() ) ) {
+ items.push( item );
+ }
+ }
+
+ return items;
+};
+
+/**
+ * Aggregate the events emitted by the group.
+ *
+ * When events are aggregated, the group will listen to all contained items for the event,
+ * and then emit the event under a new name. The new event will contain an additional leading
+ * parameter containing the item that emitted the original event. Other arguments emitted from
+ * the original event are passed through.
+ *
+ * @param {Object.<string,string|null>} events An object keyed by the name of the event that should be
+ * aggregated (e.g., ‘click’) and the value of the new name to use (e.g., ‘groupClick’).
+ * A `null` value will remove aggregated events.
+
+ * @throws {Error} An error is thrown if aggregation already exists.
+ */
+OO.ui.GroupElement.prototype.aggregate = function ( events ) {
+ var i, len, item, add, remove, itemEvent, groupEvent;
+
+ for ( itemEvent in events ) {
+ groupEvent = events[ itemEvent ];
+
+ // Remove existing aggregated event
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+ // Don't allow duplicate aggregations
+ if ( groupEvent ) {
+ throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
+ }
+ // Remove event aggregation from existing items
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if ( item.connect && item.disconnect ) {
+ remove = {};
+ remove[ itemEvent ] = [ 'emit', groupEvent, item ];
+ item.disconnect( this, remove );
+ }
+ }
+ // Prevent future items from aggregating event
+ delete this.aggregateItemEvents[ itemEvent ];
+ }
+
+ // Add new aggregate event
+ if ( groupEvent ) {
+ // Make future items aggregate event
+ this.aggregateItemEvents[ itemEvent ] = groupEvent;
+ // Add event aggregation to existing items
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if ( item.connect && item.disconnect ) {
+ add = {};
+ add[ itemEvent ] = [ 'emit', groupEvent, item ];
+ item.connect( this, add );
+ }
+ }
+ }
+ }
+};
+
+/**
+ * Add items to the group.
+ *
+ * Items will be added to the end of the group array unless the optional `index` parameter specifies
+ * a different insertion point. Adding an existing item will move it to the end of the array or the point specified by the `index`.
+ *
+ * @param {OO.ui.Element[]} items An array of items to add to the group
+ * @param {number} [index] Index of the insertion point
+ * @chainable
+ */
+OO.ui.GroupElement.prototype.addItems = function ( items, index ) {
+ var i, len, item, event, events, currentIndex,
+ itemElements = [];
+
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ item = items[ i ];
+
+ // Check if item exists then remove it first, effectively "moving" it
+ currentIndex = $.inArray( item, this.items );
+ if ( currentIndex >= 0 ) {
+ this.removeItems( [ item ] );
+ // Adjust index to compensate for removal
+ if ( currentIndex < index ) {
+ index--;
+ }
+ }
+ // Add the item
+ if ( item.connect && item.disconnect && !$.isEmptyObject( this.aggregateItemEvents ) ) {
+ events = {};
+ for ( event in this.aggregateItemEvents ) {
+ events[ event ] = [ 'emit', this.aggregateItemEvents[ event ], item ];
+ }
+ item.connect( this, events );
+ }
+ item.setElementGroup( this );
+ itemElements.push( item.$element.get( 0 ) );
+ }
+
+ if ( index === undefined || index < 0 || index >= this.items.length ) {
+ this.$group.append( itemElements );
+ this.items.push.apply( this.items, items );
+ } else if ( index === 0 ) {
+ this.$group.prepend( itemElements );
+ this.items.unshift.apply( this.items, items );
+ } else {
+ this.items[ index ].$element.before( itemElements );
+ this.items.splice.apply( this.items, [ index, 0 ].concat( items ) );
+ }
+
+ return this;
+};
+
+/**
+ * Remove the specified items from a group.
+ *
+ * Removed items are detached (not removed) from the DOM so that they may be reused.
+ * To remove all items from a group, you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.Element[]} items An array of items to remove
+ * @chainable
+ */
+OO.ui.GroupElement.prototype.removeItems = function ( items ) {
+ var i, len, item, index, remove, itemEvent;
+
+ // Remove specific items
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ item = items[ i ];
+ index = $.inArray( item, this.items );
+ if ( index !== -1 ) {
+ if (
+ item.connect && item.disconnect &&
+ !$.isEmptyObject( this.aggregateItemEvents )
+ ) {
+ remove = {};
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+ remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
+ }
+ item.disconnect( this, remove );
+ }
+ item.setElementGroup( null );
+ this.items.splice( index, 1 );
+ item.$element.detach();
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Clear all items from the group.
+ *
+ * Cleared items are detached from the DOM, not removed, so that they may be reused.
+ * To remove only a subset of items from a group, use the #removeItems method.
+ *
+ * @chainable
+ */
+OO.ui.GroupElement.prototype.clearItems = function () {
+ var i, len, item, remove, itemEvent;
+
+ // Remove all items
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if (
+ item.connect && item.disconnect &&
+ !$.isEmptyObject( this.aggregateItemEvents )
+ ) {
+ remove = {};
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+ remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
+ }
+ item.disconnect( this, remove );
+ }
+ item.setElementGroup( null );
+ item.$element.detach();
+ }
+
+ this.items = [];
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/IconElement.js b/vendor/oojs/oojs-ui/src/elements/IconElement.js
new file mode 100644
index 00000000..e3cf2f2a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/IconElement.js
@@ -0,0 +1,187 @@
+/**
+ * IconElement is often mixed into other classes to generate an icon.
+ * Icons are graphics, about the size of normal text. They are used to aid the user
+ * in locating a control or to convey information in a space-efficient way. See the
+ * [OOjs UI documentation on MediaWiki] [1] for a list of icons
+ * included in the library.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$icon] The icon element created by the class. If this configuration is omitted,
+ * the icon element will use a generated `<span>`. To use a different HTML tag, or to specify that
+ * the icon element be set to an existing icon instead of the one generated by this class, set a
+ * value using a jQuery selection. For example:
+ *
+ * // Use a <div> tag instead of a <span>
+ * $icon: $("<div>")
+ * // Use an existing icon element instead of the one generated by the class
+ * $icon: this.$element
+ * // Use an icon element from a child widget
+ * $icon: this.childwidget.$element
+ * @cfg {Object|string} [icon=''] The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of
+ * symbolic names. A map is used for i18n purposes and contains a `default` icon
+ * name and additional names keyed by language code. The `default` name is used when no icon is keyed
+ * by the user's language.
+ *
+ * Example of an i18n map:
+ *
+ * { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
+ * See the [OOjs UI documentation on MediaWiki] [2] for a list of icons included in the library.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ * @cfg {string|Function} [iconTitle] A text string used as the icon title, or a function that returns title
+ * text. The icon title is displayed when users move the mouse over the icon.
+ */
+OO.ui.IconElement = function OoUiIconElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$icon = null;
+ this.icon = null;
+ this.iconTitle = null;
+
+ // Initialization
+ this.setIcon( config.icon || this.constructor.static.icon );
+ this.setIconTitle( config.iconTitle || this.constructor.static.iconTitle );
+ this.setIconElement( config.$icon || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.IconElement );
+
+/* Static Properties */
+
+/**
+ * The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of symbolic names. A map is used
+ * for i18n purposes and contains a `default` icon name and additional names keyed by
+ * language code. The `default` name is used when no icon is keyed by the user's language.
+ *
+ * Example of an i18n map:
+ *
+ * { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
+ *
+ * Note: the static property will be overridden if the #icon configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {Object|string}
+ */
+OO.ui.IconElement.static.icon = null;
+
+/**
+ * The icon title, displayed when users move the mouse over the icon. The value can be text, a
+ * function that returns title text, or `null` for no title.
+ *
+ * The static property will be overridden if the #iconTitle configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.IconElement.static.iconTitle = null;
+
+/* Methods */
+
+/**
+ * Set the icon element. This method is used to retarget an icon mixin so that its functionality
+ * applies to the specified icon element instead of the one created by the class. If an icon
+ * element is already set, the mixin’s effect on that element is removed. Generated CSS classes
+ * and mixin methods will no longer affect the element.
+ *
+ * @param {jQuery} $icon Element to use as icon
+ */
+OO.ui.IconElement.prototype.setIconElement = function ( $icon ) {
+ if ( this.$icon ) {
+ this.$icon
+ .removeClass( 'oo-ui-iconElement-icon oo-ui-icon-' + this.icon )
+ .removeAttr( 'title' );
+ }
+
+ this.$icon = $icon
+ .addClass( 'oo-ui-iconElement-icon' )
+ .toggleClass( 'oo-ui-icon-' + this.icon, !!this.icon );
+ if ( this.iconTitle !== null ) {
+ this.$icon.attr( 'title', this.iconTitle );
+ }
+};
+
+/**
+ * Set icon by symbolic name (e.g., ‘remove’ or ‘menu’). Use `null` to remove an icon.
+ * The icon parameter can also be set to a map of icon names. See the #icon config setting
+ * for an example.
+ *
+ * @param {Object|string|null} icon A symbolic icon name, a {@link #icon map of icon names} keyed
+ * by language code, or `null` to remove the icon.
+ * @chainable
+ */
+OO.ui.IconElement.prototype.setIcon = function ( icon ) {
+ icon = OO.isPlainObject( icon ) ? OO.ui.getLocalValue( icon, null, 'default' ) : icon;
+ icon = typeof icon === 'string' && icon.trim().length ? icon.trim() : null;
+
+ if ( this.icon !== icon ) {
+ if ( this.$icon ) {
+ if ( this.icon !== null ) {
+ this.$icon.removeClass( 'oo-ui-icon-' + this.icon );
+ }
+ if ( icon !== null ) {
+ this.$icon.addClass( 'oo-ui-icon-' + icon );
+ }
+ }
+ this.icon = icon;
+ }
+
+ this.$element.toggleClass( 'oo-ui-iconElement', !!this.icon );
+ this.updateThemeClasses();
+
+ return this;
+};
+
+/**
+ * Set the icon title. Use `null` to remove the title.
+ *
+ * @param {string|Function|null} iconTitle A text string used as the icon title,
+ * a function that returns title text, or `null` for no title.
+ * @chainable
+ */
+OO.ui.IconElement.prototype.setIconTitle = function ( iconTitle ) {
+ iconTitle = typeof iconTitle === 'function' ||
+ ( typeof iconTitle === 'string' && iconTitle.length ) ?
+ OO.ui.resolveMsg( iconTitle ) : null;
+
+ if ( this.iconTitle !== iconTitle ) {
+ this.iconTitle = iconTitle;
+ if ( this.$icon ) {
+ if ( this.iconTitle !== null ) {
+ this.$icon.attr( 'title', iconTitle );
+ } else {
+ this.$icon.removeAttr( 'title' );
+ }
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Get the symbolic name of the icon.
+ *
+ * @return {string} Icon name
+ */
+OO.ui.IconElement.prototype.getIcon = function () {
+ return this.icon;
+};
+
+/**
+ * Get the icon title. The title text is displayed when a user moves the mouse over the icon.
+ *
+ * @return {string} Icon title text
+ */
+OO.ui.IconElement.prototype.getIconTitle = function () {
+ return this.iconTitle;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/IndicatorElement.js b/vendor/oojs/oojs-ui/src/elements/IndicatorElement.js
new file mode 100644
index 00000000..5c6294d2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/IndicatorElement.js
@@ -0,0 +1,168 @@
+/**
+ * IndicatorElement is often mixed into other classes to generate an indicator.
+ * Indicators are small graphics that are generally used in two ways:
+ *
+ * - To draw attention to the status of an item. For example, an indicator might be
+ * used to show that an item in a list has errors that need to be resolved.
+ * - To clarify the function of a control that acts in an exceptional way (a button
+ * that opens a menu instead of performing an action directly, for example).
+ *
+ * For a list of indicators included in the library, please see the
+ * [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$indicator] The indicator element created by the class. If this
+ * configuration is omitted, the indicator element will use a generated `<span>`.
+ * @cfg {string} [indicator] Symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
+ * See the [OOjs UI documentation on MediaWiki][2] for a list of indicators included
+ * in the library.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ * @cfg {string|Function} [indicatorTitle] A text string used as the indicator title,
+ * or a function that returns title text. The indicator title is displayed when users move
+ * the mouse over the indicator.
+ */
+OO.ui.IndicatorElement = function OoUiIndicatorElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$indicator = null;
+ this.indicator = null;
+ this.indicatorTitle = null;
+
+ // Initialization
+ this.setIndicator( config.indicator || this.constructor.static.indicator );
+ this.setIndicatorTitle( config.indicatorTitle || this.constructor.static.indicatorTitle );
+ this.setIndicatorElement( config.$indicator || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.IndicatorElement );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
+ * The static property will be overridden if the #indicator configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|null}
+ */
+OO.ui.IndicatorElement.static.indicator = null;
+
+/**
+ * A text string used as the indicator title, a function that returns title text, or `null`
+ * for no title. The static property will be overridden if the #indicatorTitle configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.IndicatorElement.static.indicatorTitle = null;
+
+/* Methods */
+
+/**
+ * Set the indicator element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $indicator Element to use as indicator
+ */
+OO.ui.IndicatorElement.prototype.setIndicatorElement = function ( $indicator ) {
+ if ( this.$indicator ) {
+ this.$indicator
+ .removeClass( 'oo-ui-indicatorElement-indicator oo-ui-indicator-' + this.indicator )
+ .removeAttr( 'title' );
+ }
+
+ this.$indicator = $indicator
+ .addClass( 'oo-ui-indicatorElement-indicator' )
+ .toggleClass( 'oo-ui-indicator-' + this.indicator, !!this.indicator );
+ if ( this.indicatorTitle !== null ) {
+ this.$indicator.attr( 'title', this.indicatorTitle );
+ }
+};
+
+/**
+ * Set the indicator by its symbolic name: ‘alert’, ‘down’, ‘next’, ‘previous’, ‘required’, ‘up’. Use `null` to remove the indicator.
+ *
+ * @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
+ * @chainable
+ */
+OO.ui.IndicatorElement.prototype.setIndicator = function ( indicator ) {
+ indicator = typeof indicator === 'string' && indicator.length ? indicator.trim() : null;
+
+ if ( this.indicator !== indicator ) {
+ if ( this.$indicator ) {
+ if ( this.indicator !== null ) {
+ this.$indicator.removeClass( 'oo-ui-indicator-' + this.indicator );
+ }
+ if ( indicator !== null ) {
+ this.$indicator.addClass( 'oo-ui-indicator-' + indicator );
+ }
+ }
+ this.indicator = indicator;
+ }
+
+ this.$element.toggleClass( 'oo-ui-indicatorElement', !!this.indicator );
+ this.updateThemeClasses();
+
+ return this;
+};
+
+/**
+ * Set the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
+ *
+ * @param {string|Function|null} indicator Indicator title text, a function that returns text, or
+ * `null` for no indicator title
+ * @chainable
+ */
+OO.ui.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
+ indicatorTitle = typeof indicatorTitle === 'function' ||
+ ( typeof indicatorTitle === 'string' && indicatorTitle.length ) ?
+ OO.ui.resolveMsg( indicatorTitle ) : null;
+
+ if ( this.indicatorTitle !== indicatorTitle ) {
+ this.indicatorTitle = indicatorTitle;
+ if ( this.$indicator ) {
+ if ( this.indicatorTitle !== null ) {
+ this.$indicator.attr( 'title', indicatorTitle );
+ } else {
+ this.$indicator.removeAttr( 'title' );
+ }
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Get the symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
+ *
+ * @return {string} Symbolic name of indicator
+ */
+OO.ui.IndicatorElement.prototype.getIndicator = function () {
+ return this.indicator;
+};
+
+/**
+ * Get the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
+ *
+ * @return {string} Indicator title text
+ */
+OO.ui.IndicatorElement.prototype.getIndicatorTitle = function () {
+ return this.indicatorTitle;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/LabelElement.js b/vendor/oojs/oojs-ui/src/elements/LabelElement.js
new file mode 100644
index 00000000..674fa73a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/LabelElement.js
@@ -0,0 +1,152 @@
+/**
+ * LabelElement is often mixed into other classes to generate a label, which
+ * helps identify the function of an interface element.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$label] The label element created by the class. If this
+ * configuration is omitted, the label element will use a generated `<span>`.
+ * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] The label text. The label can be specified
+ * as a plaintext string, a jQuery selection of elements, or a function that will produce a string
+ * in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ * @cfg {boolean} [autoFitLabel=true] Fit the label to the width of the parent element.
+ * The label will be truncated to fit if necessary.
+ */
+OO.ui.LabelElement = function OoUiLabelElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$label = null;
+ this.label = null;
+ this.autoFitLabel = config.autoFitLabel === undefined || !!config.autoFitLabel;
+
+ // Initialization
+ this.setLabel( config.label || this.constructor.static.label );
+ this.setLabelElement( config.$label || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.LabelElement );
+
+/* Events */
+
+/**
+ * @event labelChange
+ * @param {string} value
+ */
+
+/* Static Properties */
+
+/**
+ * The label text. The label can be specified as a plaintext string, a function that will
+ * produce a string in the future, or `null` for no label. The static value will
+ * be overridden if a label is specified with the #label config option.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.LabelElement.static.label = null;
+
+/* Methods */
+
+/**
+ * Set the label element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $label Element to use as label
+ */
+OO.ui.LabelElement.prototype.setLabelElement = function ( $label ) {
+ if ( this.$label ) {
+ this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
+ }
+
+ this.$label = $label.addClass( 'oo-ui-labelElement-label' );
+ this.setLabelContent( this.label );
+};
+
+/**
+ * Set the label.
+ *
+ * An empty string will result in the label being hidden. A string containing only whitespace will
+ * be converted to a single `&nbsp;`.
+ *
+ * @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
+ * text; or null for no label
+ * @chainable
+ */
+OO.ui.LabelElement.prototype.setLabel = function ( label ) {
+ label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
+ label = ( ( typeof label === 'string' && label.length ) || label instanceof jQuery || label instanceof OO.ui.HtmlSnippet ) ? label : null;
+
+ this.$element.toggleClass( 'oo-ui-labelElement', !!label );
+
+ if ( this.label !== label ) {
+ if ( this.$label ) {
+ this.setLabelContent( label );
+ }
+ this.label = label;
+ this.emit( 'labelChange' );
+ }
+
+ return this;
+};
+
+/**
+ * Get the label.
+ *
+ * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
+ * text; or null for no label
+ */
+OO.ui.LabelElement.prototype.getLabel = function () {
+ return this.label;
+};
+
+/**
+ * Fit the label.
+ *
+ * @chainable
+ */
+OO.ui.LabelElement.prototype.fitLabel = function () {
+ if ( this.$label && this.$label.autoEllipsis && this.autoFitLabel ) {
+ this.$label.autoEllipsis( { hasSpan: false, tooltip: true } );
+ }
+
+ return this;
+};
+
+/**
+ * Set the content of the label.
+ *
+ * Do not call this method until after the label element has been set by #setLabelElement.
+ *
+ * @private
+ * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
+ * text; or null for no label
+ */
+OO.ui.LabelElement.prototype.setLabelContent = function ( label ) {
+ if ( typeof label === 'string' ) {
+ if ( label.match( /^\s*$/ ) ) {
+ // Convert whitespace only string to a single non-breaking space
+ this.$label.html( '&nbsp;' );
+ } else {
+ this.$label.text( label );
+ }
+ } else if ( label instanceof OO.ui.HtmlSnippet ) {
+ this.$label.html( label.toString() );
+ } else if ( label instanceof jQuery ) {
+ this.$label.empty().append( label );
+ } else {
+ this.$label.empty();
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/LookupElement.js b/vendor/oojs/oojs-ui/src/elements/LookupElement.js
new file mode 100644
index 00000000..b79f02a9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/LookupElement.js
@@ -0,0 +1,352 @@
+/**
+ * LookupElement is a mixin that creates a {@link OO.ui.TextInputMenuSelectWidget menu} of suggested values for
+ * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types
+ * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen
+ * from the lookup menu, that value becomes the value of the input field.
+ *
+ * Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is
+ * not the desired behavior, disable lookup menus with the #setLookupsDisabled method, then set the value, then
+ * re-enable lookups.
+ *
+ * See the [OOjs UI demos][1] for an example.
+ *
+ * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/index.html#widgets-apex-vector-ltr
+ *
+ * @class
+ * @abstract
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning
+ * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
+ * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
+ * By default, the lookup menu is not generated and displayed until the user begins to type.
+ */
+OO.ui.LookupElement = function OoUiLookupElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$overlay = config.$overlay || this.$element;
+ this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, {
+ widget: this,
+ input: this,
+ $container: config.$container
+ } );
+
+ this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;
+
+ this.lookupCache = {};
+ this.lookupQuery = null;
+ this.lookupRequest = null;
+ this.lookupsDisabled = false;
+ this.lookupInputFocused = false;
+
+ // Events
+ this.$input.on( {
+ focus: this.onLookupInputFocus.bind( this ),
+ blur: this.onLookupInputBlur.bind( this ),
+ mousedown: this.onLookupInputMouseDown.bind( this )
+ } );
+ this.connect( this, { change: 'onLookupInputChange' } );
+ this.lookupMenu.connect( this, {
+ toggle: 'onLookupMenuToggle',
+ choose: 'onLookupMenuItemChoose'
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-lookupElement' );
+ this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' );
+ this.$overlay.append( this.lookupMenu.$element );
+};
+
+/* Methods */
+
+/**
+ * Handle input focus event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input focus event
+ */
+OO.ui.LookupElement.prototype.onLookupInputFocus = function () {
+ this.lookupInputFocused = true;
+ this.populateLookupMenu();
+};
+
+/**
+ * Handle input blur event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input blur event
+ */
+OO.ui.LookupElement.prototype.onLookupInputBlur = function () {
+ this.closeLookupMenu();
+ this.lookupInputFocused = false;
+};
+
+/**
+ * Handle input mouse down event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input mouse down event
+ */
+OO.ui.LookupElement.prototype.onLookupInputMouseDown = function () {
+ // Only open the menu if the input was already focused.
+ // This way we allow the user to open the menu again after closing it with Esc
+ // by clicking in the input. Opening (and populating) the menu when initially
+ // clicking into the input is handled by the focus handler.
+ if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
+ this.populateLookupMenu();
+ }
+};
+
+/**
+ * Handle input change event.
+ *
+ * @protected
+ * @param {string} value New input value
+ */
+OO.ui.LookupElement.prototype.onLookupInputChange = function () {
+ if ( this.lookupInputFocused ) {
+ this.populateLookupMenu();
+ }
+};
+
+/**
+ * Handle the lookup menu being shown/hidden.
+ *
+ * @protected
+ * @param {boolean} visible Whether the lookup menu is now visible.
+ */
+OO.ui.LookupElement.prototype.onLookupMenuToggle = function ( visible ) {
+ if ( !visible ) {
+ // When the menu is hidden, abort any active request and clear the menu.
+ // This has to be done here in addition to closeLookupMenu(), because
+ // MenuSelectWidget will close itself when the user presses Esc.
+ this.abortLookupRequest();
+ this.lookupMenu.clearItems();
+ }
+};
+
+/**
+ * Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
+ *
+ * @protected
+ * @param {OO.ui.MenuOptionWidget} item Selected item
+ */
+OO.ui.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) {
+ this.setValue( item.getData() );
+};
+
+/**
+ * Get lookup menu.
+ *
+ * @private
+ * @return {OO.ui.TextInputMenuSelectWidget}
+ */
+OO.ui.LookupElement.prototype.getLookupMenu = function () {
+ return this.lookupMenu;
+};
+
+/**
+ * Disable or re-enable lookups.
+ *
+ * When lookups are disabled, calls to #populateLookupMenu will be ignored.
+ *
+ * @param {boolean} disabled Disable lookups
+ */
+OO.ui.LookupElement.prototype.setLookupsDisabled = function ( disabled ) {
+ this.lookupsDisabled = !!disabled;
+};
+
+/**
+ * Open the menu. If there are no entries in the menu, this does nothing.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.LookupElement.prototype.openLookupMenu = function () {
+ if ( !this.lookupMenu.isEmpty() ) {
+ this.lookupMenu.toggle( true );
+ }
+ return this;
+};
+
+/**
+ * Close the menu, empty it, and abort any pending request.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.LookupElement.prototype.closeLookupMenu = function () {
+ this.lookupMenu.toggle( false );
+ this.abortLookupRequest();
+ this.lookupMenu.clearItems();
+ return this;
+};
+
+/**
+ * Request menu items based on the input's current value, and when they arrive,
+ * populate the menu with these items and show the menu.
+ *
+ * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.LookupElement.prototype.populateLookupMenu = function () {
+ var widget = this,
+ value = this.getValue();
+
+ if ( this.lookupsDisabled ) {
+ return;
+ }
+
+ // If the input is empty, clear the menu, unless suggestions when empty are allowed.
+ if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
+ this.closeLookupMenu();
+ // Skip population if there is already a request pending for the current value
+ } else if ( value !== this.lookupQuery ) {
+ this.getLookupMenuItems()
+ .done( function ( items ) {
+ widget.lookupMenu.clearItems();
+ if ( items.length ) {
+ widget.lookupMenu
+ .addItems( items )
+ .toggle( true );
+ widget.initializeLookupMenuSelection();
+ } else {
+ widget.lookupMenu.toggle( false );
+ }
+ } )
+ .fail( function () {
+ widget.lookupMenu.clearItems();
+ } );
+ }
+
+ return this;
+};
+
+/**
+ * Highlight the first selectable item in the menu.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.LookupElement.prototype.initializeLookupMenuSelection = function () {
+ if ( !this.lookupMenu.getSelectedItem() ) {
+ this.lookupMenu.highlightItem( this.lookupMenu.getFirstSelectableItem() );
+ }
+};
+
+/**
+ * Get lookup menu items for the current query.
+ *
+ * @private
+ * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of
+ * the done event. If the request was aborted to make way for a subsequent request, this promise
+ * will not be rejected: it will remain pending forever.
+ */
+OO.ui.LookupElement.prototype.getLookupMenuItems = function () {
+ var widget = this,
+ value = this.getValue(),
+ deferred = $.Deferred(),
+ ourRequest;
+
+ this.abortLookupRequest();
+ if ( Object.prototype.hasOwnProperty.call( this.lookupCache, value ) ) {
+ deferred.resolve( this.getLookupMenuOptionsFromData( this.lookupCache[ value ] ) );
+ } else {
+ this.pushPending();
+ this.lookupQuery = value;
+ ourRequest = this.lookupRequest = this.getLookupRequest();
+ ourRequest
+ .always( function () {
+ // We need to pop pending even if this is an old request, otherwise
+ // the widget will remain pending forever.
+ // TODO: this assumes that an aborted request will fail or succeed soon after
+ // being aborted, or at least eventually. It would be nice if we could popPending()
+ // at abort time, but only if we knew that we hadn't already called popPending()
+ // for that request.
+ widget.popPending();
+ } )
+ .done( function ( response ) {
+ // If this is an old request (and aborting it somehow caused it to still succeed),
+ // ignore its success completely
+ if ( ourRequest === widget.lookupRequest ) {
+ widget.lookupQuery = null;
+ widget.lookupRequest = null;
+ widget.lookupCache[ value ] = widget.getLookupCacheDataFromResponse( response );
+ deferred.resolve( widget.getLookupMenuOptionsFromData( widget.lookupCache[ value ] ) );
+ }
+ } )
+ .fail( function () {
+ // If this is an old request (or a request failing because it's being aborted),
+ // ignore its failure completely
+ if ( ourRequest === widget.lookupRequest ) {
+ widget.lookupQuery = null;
+ widget.lookupRequest = null;
+ deferred.reject();
+ }
+ } );
+ }
+ return deferred.promise();
+};
+
+/**
+ * Abort the currently pending lookup request, if any.
+ *
+ * @private
+ */
+OO.ui.LookupElement.prototype.abortLookupRequest = function () {
+ var oldRequest = this.lookupRequest;
+ if ( oldRequest ) {
+ // First unset this.lookupRequest to the fail handler will notice
+ // that the request is no longer current
+ this.lookupRequest = null;
+ this.lookupQuery = null;
+ oldRequest.abort();
+ }
+};
+
+/**
+ * Get a new request object of the current lookup query value.
+ *
+ * @protected
+ * @abstract
+ * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
+ */
+OO.ui.LookupElement.prototype.getLookupRequest = function () {
+ // Stub, implemented in subclass
+ return null;
+};
+
+/**
+ * Pre-process data returned by the request from #getLookupRequest.
+ *
+ * The return value of this function will be cached, and any further queries for the given value
+ * will use the cache rather than doing API requests.
+ *
+ * @protected
+ * @abstract
+ * @param {Mixed} response Response from server
+ * @return {Mixed} Cached result data
+ */
+OO.ui.LookupElement.prototype.getLookupCacheDataFromResponse = function () {
+ // Stub, implemented in subclass
+ return [];
+};
+
+/**
+ * Get a list of menu option widgets from the (possibly cached) data returned by
+ * #getLookupCacheDataFromResponse.
+ *
+ * @protected
+ * @abstract
+ * @param {Mixed} data Cached result data, usually an array
+ * @return {OO.ui.MenuOptionWidget[]} Menu items
+ */
+OO.ui.LookupElement.prototype.getLookupMenuOptionsFromData = function () {
+ // Stub, implemented in subclass
+ return [];
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/PendingElement.js b/vendor/oojs/oojs-ui/src/elements/PendingElement.js
new file mode 100644
index 00000000..c5f71d54
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/PendingElement.js
@@ -0,0 +1,84 @@
+/**
+ * Element that can be marked as pending.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$pending] Element to mark as pending, defaults to this.$element
+ */
+OO.ui.PendingElement = function OoUiPendingElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.pending = 0;
+ this.$pending = null;
+
+ // Initialisation
+ this.setPendingElement( config.$pending || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.PendingElement );
+
+/* Methods */
+
+/**
+ * Set the pending element (and clean up any existing one).
+ *
+ * @param {jQuery} $pending The element to set to pending.
+ */
+OO.ui.PendingElement.prototype.setPendingElement = function ( $pending ) {
+ if ( this.$pending ) {
+ this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
+ }
+
+ this.$pending = $pending;
+ if ( this.pending > 0 ) {
+ this.$pending.addClass( 'oo-ui-pendingElement-pending' );
+ }
+};
+
+/**
+ * Check if input is pending.
+ *
+ * @return {boolean}
+ */
+OO.ui.PendingElement.prototype.isPending = function () {
+ return !!this.pending;
+};
+
+/**
+ * Increase the pending stack.
+ *
+ * @chainable
+ */
+OO.ui.PendingElement.prototype.pushPending = function () {
+ if ( this.pending === 0 ) {
+ this.$pending.addClass( 'oo-ui-pendingElement-pending' );
+ this.updateThemeClasses();
+ }
+ this.pending++;
+
+ return this;
+};
+
+/**
+ * Reduce the pending stack.
+ *
+ * Clamped at zero.
+ *
+ * @chainable
+ */
+OO.ui.PendingElement.prototype.popPending = function () {
+ if ( this.pending === 1 ) {
+ this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
+ this.updateThemeClasses();
+ }
+ this.pending = Math.max( 0, this.pending - 1 );
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/PopupElement.js b/vendor/oojs/oojs-ui/src/elements/PopupElement.js
new file mode 100644
index 00000000..099e94b7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/PopupElement.js
@@ -0,0 +1,36 @@
+/**
+ * PopupElement is mixed into other classes to generate a {@link OO.ui.PopupWidget popup widget}.
+ * A popup is a container for content. It is overlaid and positioned absolutely. By default, each
+ * popup has an anchor, which is an arrow-like protrusion that points toward the popup’s origin.
+ * See {@link OO.ui.PopupWidget PopupWidget} for an example.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [popup] Configuration to pass to popup
+ * @cfg {boolean} [popup.autoClose=true] Popup auto-closes when it loses focus
+ */
+OO.ui.PopupElement = function OoUiPopupElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.popup = new OO.ui.PopupWidget( $.extend(
+ { autoClose: true },
+ config.popup,
+ { $autoCloseIgnore: this.$element }
+ ) );
+};
+
+/* Methods */
+
+/**
+ * Get popup.
+ *
+ * @return {OO.ui.PopupWidget} Popup widget
+ */
+OO.ui.PopupElement.prototype.getPopup = function () {
+ return this.popup;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/TabIndexedElement.js b/vendor/oojs/oojs-ui/src/elements/TabIndexedElement.js
new file mode 100644
index 00000000..5c2151a0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/TabIndexedElement.js
@@ -0,0 +1,138 @@
+/**
+ * The TabIndexedElement class is an attribute mixin used to add additional functionality to an
+ * element created by another class. The mixin provides a ‘tabIndex’ property, which specifies the
+ * order in which users will navigate through the focusable elements via the "tab" key.
+ *
+ * @example
+ * // TabIndexedElement is mixed into the ButtonWidget class
+ * // to provide a tabIndex property.
+ * var button1 = new OO.ui.ButtonWidget( {
+ * label: 'fourth',
+ * tabIndex: 4
+ * } );
+ * var button2 = new OO.ui.ButtonWidget( {
+ * label: 'second',
+ * tabIndex: 2
+ * } );
+ * var button3 = new OO.ui.ButtonWidget( {
+ * label: 'third',
+ * tabIndex: 3
+ * } );
+ * var button4 = new OO.ui.ButtonWidget( {
+ * label: 'first',
+ * tabIndex: 1
+ * } );
+ * $( 'body' ).append( button1.$element, button2.$element, button3.$element, button4.$element );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$tabIndexed] The element that should use the tabindex functionality. By default,
+ * the functionality is applied to the element created by the class ($element). If a different element is specified, the tabindex
+ * functionality will be applied to it instead.
+ * @cfg {number|null} [tabIndex=0] Number that specifies the element’s position in the tab-navigation
+ * order (e.g., 1 for the first focusable element). Use 0 to use the default navigation order; use -1
+ * to remove the element from the tab-navigation flow.
+ */
+OO.ui.TabIndexedElement = function OoUiTabIndexedElement( config ) {
+ // Configuration initialization
+ config = $.extend( { tabIndex: 0 }, config );
+
+ // Properties
+ this.$tabIndexed = null;
+ this.tabIndex = null;
+
+ // Events
+ this.connect( this, { disable: 'onDisable' } );
+
+ // Initialization
+ this.setTabIndex( config.tabIndex );
+ this.setTabIndexedElement( config.$tabIndexed || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Set the element that should use the tabindex functionality.
+ *
+ * This method is used to retarget a tabindex mixin so that its functionality applies
+ * to the specified element. If an element is currently using the functionality, the mixin’s
+ * effect on that element is removed before the new element is set up.
+ *
+ * @param {jQuery} $tabIndexed Element that should use the tabindex functionality
+ * @chainable
+ */
+OO.ui.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed ) {
+ var tabIndex = this.tabIndex;
+ // Remove attributes from old $tabIndexed
+ this.setTabIndex( null );
+ // Force update of new $tabIndexed
+ this.$tabIndexed = $tabIndexed;
+ this.tabIndex = tabIndex;
+ return this.updateTabIndex();
+};
+
+/**
+ * Set the value of the tabindex.
+ *
+ * @param {number|null} tabIndex Tabindex value, or `null` for no tabindex
+ * @chainable
+ */
+OO.ui.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
+ tabIndex = typeof tabIndex === 'number' ? tabIndex : null;
+
+ if ( this.tabIndex !== tabIndex ) {
+ this.tabIndex = tabIndex;
+ this.updateTabIndex();
+ }
+
+ return this;
+};
+
+/**
+ * Update the `tabindex` attribute, in case of changes to tab index or
+ * disabled state.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.TabIndexedElement.prototype.updateTabIndex = function () {
+ if ( this.$tabIndexed ) {
+ if ( this.tabIndex !== null ) {
+ // Do not index over disabled elements
+ this.$tabIndexed.attr( {
+ tabindex: this.isDisabled() ? -1 : this.tabIndex,
+ // ChromeVox and NVDA do not seem to inherit this from parent elements
+ 'aria-disabled': this.isDisabled().toString()
+ } );
+ } else {
+ this.$tabIndexed.removeAttr( 'tabindex aria-disabled' );
+ }
+ }
+ return this;
+};
+
+/**
+ * Handle disable events.
+ *
+ * @private
+ * @param {boolean} disabled Element is disabled
+ */
+OO.ui.TabIndexedElement.prototype.onDisable = function () {
+ this.updateTabIndex();
+};
+
+/**
+ * Get the value of the tabindex.
+ *
+ * @return {number|null} Tabindex value
+ */
+OO.ui.TabIndexedElement.prototype.getTabIndex = function () {
+ return this.tabIndex;
+};
diff --git a/vendor/oojs/oojs-ui/src/elements/TitledElement.js b/vendor/oojs/oojs-ui/src/elements/TitledElement.js
new file mode 100644
index 00000000..905ec019
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/elements/TitledElement.js
@@ -0,0 +1,106 @@
+/**
+ * TitledElement is mixed into other classes to provide a `title` attribute.
+ * Titles are rendered by the browser and are made visible when the user moves
+ * the mouse over the element. Titles are not visible on touch devices.
+ *
+ * @example
+ * // TitledElement provides a 'title' attribute to the
+ * // ButtonWidget class
+ * var button = new OO.ui.ButtonWidget( {
+ * label: 'Button with Title',
+ * title: 'I am a button'
+ * } );
+ * $( 'body' ).append( button.$element );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$titled] The element to which the `title` attribute is applied.
+ * If this config is omitted, the title functionality is applied to $element, the
+ * element created by the class.
+ * @cfg {string|Function} [title] The 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.
+ */
+OO.ui.TitledElement = function OoUiTitledElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$titled = null;
+ this.title = null;
+
+ // Initialization
+ this.setTitle( config.title || this.constructor.static.title );
+ this.setTitledElement( config.$titled || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.TitledElement );
+
+/* Static Properties */
+
+/**
+ * The title text, a function that returns text, or `null` for no title. The value of the static property
+ * is overridden if the #title config option is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.TitledElement.static.title = null;
+
+/* Methods */
+
+/**
+ * Set the titled element.
+ *
+ * This method is used to retarget a titledElement mixin so that its functionality applies to the specified element.
+ * If an element is already set, the mixin’s effect on that element is removed before the new element is set up.
+ *
+ * @param {jQuery} $titled Element that should use the 'titled' functionality
+ */
+OO.ui.TitledElement.prototype.setTitledElement = function ( $titled ) {
+ if ( this.$titled ) {
+ this.$titled.removeAttr( 'title' );
+ }
+
+ this.$titled = $titled;
+ if ( this.title ) {
+ this.$titled.attr( 'title', this.title );
+ }
+};
+
+/**
+ * Set title.
+ *
+ * @param {string|Function|null} title Title text, a function that returns text, or `null` for no title
+ * @chainable
+ */
+OO.ui.TitledElement.prototype.setTitle = function ( title ) {
+ title = typeof title === 'string' ? OO.ui.resolveMsg( title ) : null;
+
+ if ( this.title !== title ) {
+ if ( this.$titled ) {
+ if ( title !== null ) {
+ this.$titled.attr( 'title', title );
+ } else {
+ this.$titled.removeAttr( 'title' );
+ }
+ }
+ this.title = title;
+ }
+
+ return this;
+};
+
+/**
+ * Get title.
+ *
+ * @return {string} Title string
+ */
+OO.ui.TitledElement.prototype.getTitle = function () {
+ return this.title;
+};
diff --git a/vendor/oojs/oojs-ui/src/intro.js.txt b/vendor/oojs/oojs-ui/src/intro.js.txt
new file mode 100644
index 00000000..31f545ca
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/intro.js.txt
@@ -0,0 +1,3 @@
+( function ( OO ) {
+
+'use strict';
diff --git a/vendor/oojs/oojs-ui/src/layouts/ActionFieldLayout.js b/vendor/oojs/oojs-ui/src/layouts/ActionFieldLayout.js
new file mode 100644
index 00000000..59640ed9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/ActionFieldLayout.js
@@ -0,0 +1,81 @@
+/**
+ * ActionFieldLayouts are used with OO.ui.FieldsetLayout. The layout consists of a field-widget, a button,
+ * and an optional label and/or help text. The field-widget (e.g., a {@link OO.ui.TextInputWidget TextInputWidget}),
+ * is required and is specified before any optional configuration settings.
+ *
+ * Labels can be aligned in one of four ways:
+ *
+ * - **left**: The label is placed before the field-widget and aligned with the left margin.
+ * A left-alignment is used for forms with many fields.
+ * - **right**: The label is placed before the field-widget and aligned to the right margin.
+ * A right-alignment is used for long but familiar forms which users tab through,
+ * verifying the current field with a quick glance at the label.
+ * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
+ * that users fill out from top to bottom.
+ * - **inline**: The label is placed after the field-widget and aligned to the left.
+ * An inline-alignment is best used with checkboxes or radio buttons.
+ *
+ * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout when help
+ * text is specified.
+ *
+ * @example
+ * // Example of an ActionFieldLayout
+ * var actionFieldLayout = new OO.ui.ActionFieldLayout(
+ * new OO.ui.TextInputWidget( {
+ * placeholder: 'Field widget'
+ * } ),
+ * new OO.ui.ButtonWidget( {
+ * label: 'Button'
+ * } ),
+ * {
+ * label: 'An ActionFieldLayout. This label is aligned top',
+ * align: 'top',
+ * help: 'This is help text'
+ * }
+ * );
+ *
+ * $( 'body' ).append( actionFieldLayout.$element );
+ *
+ *
+ * @class
+ * @extends OO.ui.FieldLayout
+ *
+ * @constructor
+ * @param {OO.ui.Widget} fieldWidget Field widget
+ * @param {OO.ui.ButtonWidget} buttonWidget Button widget
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' or 'inline'
+ * @cfg {string} [help] Help text. When help text is specified, a help icon will appear in the
+ * upper-right corner of the rendered field.
+ */
+OO.ui.ActionFieldLayout = function OoUiActionFieldLayout( fieldWidget, buttonWidget, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
+ config = fieldWidget;
+ fieldWidget = config.fieldWidget;
+ buttonWidget = config.buttonWidget;
+ }
+
+ // Configuration initialization
+ config = $.extend( { align: 'left' }, config );
+
+ // Parent constructor
+ OO.ui.ActionFieldLayout.super.call( this, fieldWidget, config );
+
+ // Properties
+ this.fieldWidget = fieldWidget;
+ this.buttonWidget = buttonWidget;
+ this.$button = $( '<div>' )
+ .addClass( 'oo-ui-actionFieldLayout-button' )
+ .append( this.buttonWidget.$element );
+ this.$input = $( '<div>' )
+ .addClass( 'oo-ui-actionFieldLayout-input' )
+ .append( this.fieldWidget.$element );
+ this.$field
+ .addClass( 'oo-ui-actionFieldLayout' )
+ .append( this.$input, this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
diff --git a/vendor/oojs/oojs-ui/src/layouts/BookletLayout.js b/vendor/oojs/oojs-ui/src/layouts/BookletLayout.js
new file mode 100644
index 00000000..eebf57d6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/BookletLayout.js
@@ -0,0 +1,542 @@
+/**
+ * BookletLayouts contain {@link OO.ui.PageLayout page layouts} as well as
+ * an {@link OO.ui.OutlineSelectWidget outline} that allows users to easily navigate
+ * through the pages and select which one to display. By default, only one page is
+ * displayed at a time and the outline is hidden. When a user navigates to a new page,
+ * the booklet layout automatically focuses on the first focusable element, unless the
+ * default setting is changed. Optionally, booklets can be configured to show
+ * {@link OO.ui.OutlineControlsWidget controls} for adding, moving, and removing items.
+ *
+ * @example
+ * // Example of a BookletLayout that contains two PageLayouts.
+ *
+ * function PageOneLayout( name, config ) {
+ * PageOneLayout.super.call( this, name, config );
+ * this.$element.append( '<p>First page</p><p>(This booklet has an outline, displayed on the left)</p>' );
+ * }
+ * OO.inheritClass( PageOneLayout, OO.ui.PageLayout );
+ * PageOneLayout.prototype.setupOutlineItem = function () {
+ * this.outlineItem.setLabel( 'Page One' );
+ * };
+ *
+ * function PageTwoLayout( name, config ) {
+ * PageTwoLayout.super.call( this, name, config );
+ * this.$element.append( '<p>Second page</p>' );
+ * }
+ * OO.inheritClass( PageTwoLayout, OO.ui.PageLayout );
+ * PageTwoLayout.prototype.setupOutlineItem = function () {
+ * this.outlineItem.setLabel( 'Page Two' );
+ * };
+ *
+ * var page1 = new PageOneLayout( 'one' ),
+ * page2 = new PageTwoLayout( 'two' );
+ *
+ * var booklet = new OO.ui.BookletLayout( {
+ * outlined: true
+ * } );
+ *
+ * booklet.addPages ( [ page1, page2 ] );
+ * $( 'body' ).append( booklet.$element );
+ *
+ * @class
+ * @extends OO.ui.MenuLayout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all pages, one after another
+ * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new page is displayed.
+ * @cfg {boolean} [outlined=false] Show the outline. The outline is used to navigate through the pages of the booklet.
+ * @cfg {boolean} [editable=false] Show controls for adding, removing and reordering pages
+ */
+OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.BookletLayout.super.call( this, config );
+
+ // Properties
+ this.currentPageName = null;
+ this.pages = {};
+ this.ignoreFocus = false;
+ this.stackLayout = new OO.ui.StackLayout( { continuous: !!config.continuous } );
+ this.$content.append( this.stackLayout.$element );
+ this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
+ this.outlineVisible = false;
+ this.outlined = !!config.outlined;
+ if ( this.outlined ) {
+ this.editable = !!config.editable;
+ this.outlineControlsWidget = null;
+ this.outlineSelectWidget = new OO.ui.OutlineSelectWidget();
+ this.outlinePanel = new OO.ui.PanelLayout( { scrollable: true } );
+ this.$menu.append( this.outlinePanel.$element );
+ this.outlineVisible = true;
+ if ( this.editable ) {
+ this.outlineControlsWidget = new OO.ui.OutlineControlsWidget(
+ this.outlineSelectWidget
+ );
+ }
+ }
+ this.toggleMenu( this.outlined );
+
+ // Events
+ this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
+ if ( this.outlined ) {
+ this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
+ }
+ if ( this.autoFocus ) {
+ // Event 'focus' does not bubble, but 'focusin' does
+ this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
+ }
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-bookletLayout' );
+ this.stackLayout.$element.addClass( 'oo-ui-bookletLayout-stackLayout' );
+ if ( this.outlined ) {
+ this.outlinePanel.$element
+ .addClass( 'oo-ui-bookletLayout-outlinePanel' )
+ .append( this.outlineSelectWidget.$element );
+ if ( this.editable ) {
+ this.outlinePanel.$element
+ .addClass( 'oo-ui-bookletLayout-outlinePanel-editable' )
+ .append( this.outlineControlsWidget.$element );
+ }
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.BookletLayout, OO.ui.MenuLayout );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when a page is {@link #setPage set} to be displayed by the booklet layout.
+ * @event set
+ * @param {OO.ui.PageLayout} page Current page
+ */
+
+/**
+ * An 'add' event is emitted when pages are {@link #addPages added} to the booklet layout.
+ *
+ * @event add
+ * @param {OO.ui.PageLayout[]} page Added pages
+ * @param {number} index Index pages were added at
+ */
+
+/**
+ * A 'remove' event is emitted when pages are {@link #clearPages cleared} or
+ * {@link #removePages removed} from the booklet.
+ *
+ * @event remove
+ * @param {OO.ui.PageLayout[]} pages Removed pages
+ */
+
+/* Methods */
+
+/**
+ * Handle stack layout focus.
+ *
+ * @private
+ * @param {jQuery.Event} e Focusin event
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
+ var name, $target;
+
+ // Find the page that an element was focused within
+ $target = $( e.target ).closest( '.oo-ui-pageLayout' );
+ for ( name in this.pages ) {
+ // Check for page match, exclude current page to find only page changes
+ if ( this.pages[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentPageName ) {
+ this.setPage( name );
+ break;
+ }
+ }
+};
+
+/**
+ * Handle stack layout set events.
+ *
+ * @private
+ * @param {OO.ui.PanelLayout|null} page The page panel that is now the current panel
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
+ var layout = this;
+ if ( page ) {
+ page.scrollElementIntoView( { complete: function () {
+ if ( layout.autoFocus ) {
+ layout.focus();
+ }
+ } } );
+ }
+};
+
+/**
+ * Focus the first input in the current page.
+ *
+ * If no page is selected, the first selectable page will be selected.
+ * If the focus is already in an element on the current page, nothing will happen.
+ * @param {number} [itemIndex] A specific item to focus on
+ */
+OO.ui.BookletLayout.prototype.focus = function ( itemIndex ) {
+ var $input, page,
+ items = this.stackLayout.getItems();
+
+ if ( itemIndex !== undefined && items[ itemIndex ] ) {
+ page = items[ itemIndex ];
+ } else {
+ page = this.stackLayout.getCurrentItem();
+ }
+
+ if ( !page && this.outlined ) {
+ this.selectFirstSelectablePage();
+ page = this.stackLayout.getCurrentItem();
+ }
+ if ( !page ) {
+ return;
+ }
+ // Only change the focus if is not already in the current page
+ if ( !page.$element.find( ':focus' ).length ) {
+ $input = page.$element.find( ':input:first' );
+ if ( $input.length ) {
+ $input[ 0 ].focus();
+ }
+ }
+};
+
+/**
+ * Find the first focusable input in the booklet layout and focus
+ * on it.
+ */
+OO.ui.BookletLayout.prototype.focusFirstFocusable = function () {
+ var i, len,
+ found = false,
+ items = this.stackLayout.getItems(),
+ checkAndFocus = function () {
+ if ( OO.ui.isFocusableElement( $( this ) ) ) {
+ $( this ).focus();
+ found = true;
+ return false;
+ }
+ };
+
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ if ( found ) {
+ break;
+ }
+ // Find all potentially focusable elements in the item
+ // and check if they are focusable
+ items[i].$element
+ .find( 'input, select, textarea, button, object' )
+ /* jshint loopfunc:true */
+ .each( checkAndFocus );
+ }
+};
+
+/**
+ * Handle outline widget select events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+OO.ui.BookletLayout.prototype.onOutlineSelectWidgetSelect = function ( item ) {
+ if ( item ) {
+ this.setPage( item.getData() );
+ }
+};
+
+/**
+ * Check if booklet has an outline.
+ *
+ * @return {boolean} Booklet has an outline
+ */
+OO.ui.BookletLayout.prototype.isOutlined = function () {
+ return this.outlined;
+};
+
+/**
+ * Check if booklet has editing controls.
+ *
+ * @return {boolean} Booklet is editable
+ */
+OO.ui.BookletLayout.prototype.isEditable = function () {
+ return this.editable;
+};
+
+/**
+ * Check if booklet has a visible outline.
+ *
+ * @return {boolean} Outline is visible
+ */
+OO.ui.BookletLayout.prototype.isOutlineVisible = function () {
+ return this.outlined && this.outlineVisible;
+};
+
+/**
+ * Hide or show the outline.
+ *
+ * @param {boolean} [show] Show outline, omit to invert current state
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.toggleOutline = function ( show ) {
+ if ( this.outlined ) {
+ show = show === undefined ? !this.outlineVisible : !!show;
+ this.outlineVisible = show;
+ this.toggleMenu( show );
+ }
+
+ return this;
+};
+
+/**
+ * Get the page closest to the specified page.
+ *
+ * @param {OO.ui.PageLayout} page Page to use as a reference point
+ * @return {OO.ui.PageLayout|null} Page closest to the specified page
+ */
+OO.ui.BookletLayout.prototype.getClosestPage = function ( page ) {
+ var next, prev, level,
+ pages = this.stackLayout.getItems(),
+ index = $.inArray( page, pages );
+
+ if ( index !== -1 ) {
+ next = pages[ index + 1 ];
+ prev = pages[ index - 1 ];
+ // Prefer adjacent pages at the same level
+ if ( this.outlined ) {
+ level = this.outlineSelectWidget.getItemFromData( page.getName() ).getLevel();
+ if (
+ prev &&
+ level === this.outlineSelectWidget.getItemFromData( prev.getName() ).getLevel()
+ ) {
+ return prev;
+ }
+ if (
+ next &&
+ level === this.outlineSelectWidget.getItemFromData( next.getName() ).getLevel()
+ ) {
+ return next;
+ }
+ }
+ }
+ return prev || next || null;
+};
+
+/**
+ * Get the outline widget.
+ *
+ * If the booklet is not outlined, the method will return `null`.
+ *
+ * @return {OO.ui.OutlineSelectWidget|null} Outline widget, or null if the booklet is not outlined
+ */
+OO.ui.BookletLayout.prototype.getOutline = function () {
+ return this.outlineSelectWidget;
+};
+
+/**
+ * Get the outline controls widget.
+ *
+ * If the outline is not editable, the method will return `null`.
+ *
+ * @return {OO.ui.OutlineControlsWidget|null} The outline controls widget.
+ */
+OO.ui.BookletLayout.prototype.getOutlineControls = function () {
+ return this.outlineControlsWidget;
+};
+
+/**
+ * Get a page by its symbolic name.
+ *
+ * @param {string} name Symbolic name of page
+ * @return {OO.ui.PageLayout|undefined} Page, if found
+ */
+OO.ui.BookletLayout.prototype.getPage = function ( name ) {
+ return this.pages[ name ];
+};
+
+/**
+ * Get the current page.
+ *
+ * @return {OO.ui.PageLayout|undefined} Current page, if found
+ */
+OO.ui.BookletLayout.prototype.getCurrentPage = function () {
+ var name = this.getCurrentPageName();
+ return name ? this.getPage( name ) : undefined;
+};
+
+/**
+ * Get the symbolic name of the current page.
+ *
+ * @return {string|null} Symbolic name of the current page
+ */
+OO.ui.BookletLayout.prototype.getCurrentPageName = function () {
+ return this.currentPageName;
+};
+
+/**
+ * Add pages to the booklet layout
+ *
+ * When pages are added with the same names as existing pages, the existing pages will be
+ * automatically removed before the new pages are added.
+ *
+ * @param {OO.ui.PageLayout[]} pages Pages to add
+ * @param {number} index Index of the insertion point
+ * @fires add
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.addPages = function ( pages, index ) {
+ var i, len, name, page, item, currentIndex,
+ stackLayoutPages = this.stackLayout.getItems(),
+ remove = [],
+ items = [];
+
+ // Remove pages with same names
+ for ( i = 0, len = pages.length; i < len; i++ ) {
+ page = pages[ i ];
+ name = page.getName();
+
+ if ( Object.prototype.hasOwnProperty.call( this.pages, name ) ) {
+ // Correct the insertion index
+ currentIndex = $.inArray( this.pages[ name ], stackLayoutPages );
+ if ( currentIndex !== -1 && currentIndex + 1 < index ) {
+ index--;
+ }
+ remove.push( this.pages[ name ] );
+ }
+ }
+ if ( remove.length ) {
+ this.removePages( remove );
+ }
+
+ // Add new pages
+ for ( i = 0, len = pages.length; i < len; i++ ) {
+ page = pages[ i ];
+ name = page.getName();
+ this.pages[ page.getName() ] = page;
+ if ( this.outlined ) {
+ item = new OO.ui.OutlineOptionWidget( { data: name } );
+ page.setOutlineItem( item );
+ items.push( item );
+ }
+ }
+
+ if ( this.outlined && items.length ) {
+ this.outlineSelectWidget.addItems( items, index );
+ this.selectFirstSelectablePage();
+ }
+ this.stackLayout.addItems( pages, index );
+ this.emit( 'add', pages, index );
+
+ return this;
+};
+
+/**
+ * Remove the specified pages from the booklet layout.
+ *
+ * To remove all pages from the booklet, you may wish to use the #clearPages method instead.
+ *
+ * @param {OO.ui.PageLayout[]} pages An array of pages to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.removePages = function ( pages ) {
+ var i, len, name, page,
+ items = [];
+
+ for ( i = 0, len = pages.length; i < len; i++ ) {
+ page = pages[ i ];
+ name = page.getName();
+ delete this.pages[ name ];
+ if ( this.outlined ) {
+ items.push( this.outlineSelectWidget.getItemFromData( name ) );
+ page.setOutlineItem( null );
+ }
+ }
+ if ( this.outlined && items.length ) {
+ this.outlineSelectWidget.removeItems( items );
+ this.selectFirstSelectablePage();
+ }
+ this.stackLayout.removeItems( pages );
+ this.emit( 'remove', pages );
+
+ return this;
+};
+
+/**
+ * Clear all pages from the booklet layout.
+ *
+ * To remove only a subset of pages from the booklet, use the #removePages method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.clearPages = function () {
+ var i, len,
+ pages = this.stackLayout.getItems();
+
+ this.pages = {};
+ this.currentPageName = null;
+ if ( this.outlined ) {
+ this.outlineSelectWidget.clearItems();
+ for ( i = 0, len = pages.length; i < len; i++ ) {
+ pages[ i ].setOutlineItem( null );
+ }
+ }
+ this.stackLayout.clearItems();
+
+ this.emit( 'remove', pages );
+
+ return this;
+};
+
+/**
+ * Set the current page by symbolic name.
+ *
+ * @fires set
+ * @param {string} name Symbolic name of page
+ */
+OO.ui.BookletLayout.prototype.setPage = function ( name ) {
+ var selectedItem,
+ $focused,
+ page = this.pages[ name ];
+
+ if ( name !== this.currentPageName ) {
+ if ( this.outlined ) {
+ selectedItem = this.outlineSelectWidget.getSelectedItem();
+ if ( selectedItem && selectedItem.getData() !== name ) {
+ this.outlineSelectWidget.selectItemByData( name );
+ }
+ }
+ if ( page ) {
+ if ( this.currentPageName && this.pages[ this.currentPageName ] ) {
+ this.pages[ this.currentPageName ].setActive( false );
+ // Blur anything focused if the next page doesn't have anything focusable - this
+ // is not needed if the next page has something focusable because once it is focused
+ // this blur happens automatically
+ if ( this.autoFocus && !page.$element.find( ':input' ).length ) {
+ $focused = this.pages[ this.currentPageName ].$element.find( ':focus' );
+ if ( $focused.length ) {
+ $focused[ 0 ].blur();
+ }
+ }
+ }
+ this.currentPageName = name;
+ this.stackLayout.setItem( page );
+ page.setActive( true );
+ this.emit( 'set', page );
+ }
+ }
+};
+
+/**
+ * Select the first selectable page.
+ *
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () {
+ if ( !this.outlineSelectWidget.getSelectedItem() ) {
+ this.outlineSelectWidget.selectItem( this.outlineSelectWidget.getFirstSelectableItem() );
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/CardLayout.js b/vendor/oojs/oojs-ui/src/layouts/CardLayout.js
new file mode 100644
index 00000000..353aedc9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/CardLayout.js
@@ -0,0 +1,138 @@
+/**
+ * CardLayouts are used within {@link OO.ui.IndexLayout index layouts} to create cards that users can select and display
+ * from the index's optional {@link OO.ui.TabSelectWidget tab} navigation. Cards are usually not instantiated directly,
+ * rather extended to include the required content and functionality.
+ *
+ * Each card must have a unique symbolic name, which is passed to the constructor. In addition, the card's tab
+ * item is customized (with a label) using the #setupTabItem method. See
+ * {@link OO.ui.IndexLayout IndexLayout} for an example.
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ *
+ * @constructor
+ * @param {string} name Unique symbolic name of card
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.CardLayout = function OoUiCardLayout( name, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( name ) && config === undefined ) {
+ config = name;
+ name = config.name;
+ }
+
+ // Configuration initialization
+ config = $.extend( { scrollable: true }, config );
+
+ // Parent constructor
+ OO.ui.CardLayout.super.call( this, config );
+
+ // Properties
+ this.name = name;
+ this.tabItem = null;
+ this.active = false;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-cardLayout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CardLayout, OO.ui.PanelLayout );
+
+/* Events */
+
+/**
+ * An 'active' event is emitted when the card becomes active. Cards become active when they are
+ * shown in a index layout that is configured to display only one card at a time.
+ *
+ * @event active
+ * @param {boolean} active Card is active
+ */
+
+/* Methods */
+
+/**
+ * Get the symbolic name of the card.
+ *
+ * @return {string} Symbolic name of card
+ */
+OO.ui.CardLayout.prototype.getName = function () {
+ return this.name;
+};
+
+/**
+ * Check if card is active.
+ *
+ * Cards become active when they are shown in a {@link OO.ui.IndexLayout index layout} that is configured to display
+ * only one card at a time. Additional CSS is applied to the card's tab item to reflect the active state.
+ *
+ * @return {boolean} Card is active
+ */
+OO.ui.CardLayout.prototype.isActive = function () {
+ return this.active;
+};
+
+/**
+ * Get tab item.
+ *
+ * The tab item allows users to access the card from the index's tab
+ * navigation. The tab item itself can be customized (with a label, level, etc.) using the #setupTabItem method.
+ *
+ * @return {OO.ui.TabOptionWidget|null} Tab option widget
+ */
+OO.ui.CardLayout.prototype.getTabItem = function () {
+ return this.tabItem;
+};
+
+/**
+ * Set or unset the tab item.
+ *
+ * Specify a {@link OO.ui.TabOptionWidget tab option} to set it,
+ * or `null` to clear the tab item. To customize the tab item itself (e.g., to set a label or tab
+ * level), use #setupTabItem instead of this method.
+ *
+ * @param {OO.ui.TabOptionWidget|null} tabItem Tab option widget, null to clear
+ * @chainable
+ */
+OO.ui.CardLayout.prototype.setTabItem = function ( tabItem ) {
+ this.tabItem = tabItem || null;
+ if ( tabItem ) {
+ this.setupTabItem();
+ }
+ return this;
+};
+
+/**
+ * Set up the tab item.
+ *
+ * Use this method to customize the tab item (e.g., to add a label or tab level). To set or unset
+ * the tab item itself (with a {@link OO.ui.TabOptionWidget tab option} or `null`), use
+ * the #setTabItem method instead.
+ *
+ * @param {OO.ui.TabOptionWidget} tabItem Tab option widget to set up
+ * @chainable
+ */
+OO.ui.CardLayout.prototype.setupTabItem = function () {
+ return this;
+};
+
+/**
+ * Set the card to its 'active' state.
+ *
+ * Cards become active when they are shown in a index layout that is configured to display only one card at a time. Additional
+ * CSS is applied to the tab item to reflect the card's active state. Outside of the index
+ * context, setting the active state on a card does nothing.
+ *
+ * @param {boolean} value Card is active
+ * @fires active
+ */
+OO.ui.CardLayout.prototype.setActive = function ( active ) {
+ active = !!active;
+
+ if ( active !== this.active ) {
+ this.active = active;
+ this.$element.toggleClass( 'oo-ui-cardLayout-active', this.active );
+ this.emit( 'active', this.active );
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/FieldLayout.js b/vendor/oojs/oojs-ui/src/layouts/FieldLayout.js
new file mode 100644
index 00000000..86385741
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/FieldLayout.js
@@ -0,0 +1,160 @@
+/**
+ * FieldLayouts are used with OO.ui.FieldsetLayout. Each FieldLayout requires a field-widget,
+ * which is a widget that is specified by reference before any optional configuration settings.
+ *
+ * Field layouts can be configured with help text and/or labels. Labels are aligned in one of four ways:
+ *
+ * - **left**: The label is placed before the field-widget and aligned with the left margin.
+ * A left-alignment is used for forms with many fields.
+ * - **right**: The label is placed before the field-widget and aligned to the right margin.
+ * A right-alignment is used for long but familiar forms which users tab through,
+ * verifying the current field with a quick glance at the label.
+ * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
+ * that users fill out from top to bottom.
+ * - **inline**: The label is placed after the field-widget and aligned to the left.
+ * An inline-alignment is best used with checkboxes or radio buttons.
+ *
+ * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for examples and more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.LabelElement
+ *
+ * @constructor
+ * @param {OO.ui.Widget} fieldWidget Field widget
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' or 'inline'
+ * @cfg {string} [help] Help text. When help text is specified, a help icon will appear
+ * in the upper-right corner of the rendered field.
+ */
+OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
+ config = fieldWidget;
+ fieldWidget = config.fieldWidget;
+ }
+
+ var hasInputWidget = fieldWidget instanceof OO.ui.InputWidget;
+
+ // Configuration initialization
+ config = $.extend( { align: 'left' }, config );
+
+ // Parent constructor
+ OO.ui.FieldLayout.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.LabelElement.call( this, config );
+
+ // Properties
+ this.fieldWidget = fieldWidget;
+ this.$field = $( '<div>' );
+ this.$body = $( '<' + ( hasInputWidget ? 'label' : 'div' ) + '>' );
+ this.align = null;
+ if ( config.help ) {
+ this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
+ classes: [ 'oo-ui-fieldLayout-help' ],
+ framed: false,
+ icon: 'info'
+ } );
+
+ this.popupButtonWidget.getPopup().$body.append(
+ $( '<div>' )
+ .text( config.help )
+ .addClass( 'oo-ui-fieldLayout-help-content' )
+ );
+ this.$help = this.popupButtonWidget.$element;
+ } else {
+ this.$help = $( [] );
+ }
+
+ // Events
+ if ( hasInputWidget ) {
+ this.$label.on( 'click', this.onLabelClick.bind( this ) );
+ }
+ this.fieldWidget.connect( this, { disable: 'onFieldDisable' } );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-fieldLayout' )
+ .append( this.$help, this.$body );
+ this.$body.addClass( 'oo-ui-fieldLayout-body' );
+ this.$field
+ .addClass( 'oo-ui-fieldLayout-field' )
+ .toggleClass( 'oo-ui-fieldLayout-disable', this.fieldWidget.isDisabled() )
+ .append( this.fieldWidget.$element );
+
+ this.setAlignment( config.align );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FieldLayout, OO.ui.LabelElement );
+
+/* Methods */
+
+/**
+ * Handle field disable events.
+ *
+ * @private
+ * @param {boolean} value Field is disabled
+ */
+OO.ui.FieldLayout.prototype.onFieldDisable = function ( value ) {
+ this.$element.toggleClass( 'oo-ui-fieldLayout-disabled', value );
+};
+
+/**
+ * Handle label mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.FieldLayout.prototype.onLabelClick = function () {
+ this.fieldWidget.simulateLabelClick();
+ return false;
+};
+
+/**
+ * Get the widget contained by the field.
+ *
+ * @return {OO.ui.Widget} Field widget
+ */
+OO.ui.FieldLayout.prototype.getField = function () {
+ return this.fieldWidget;
+};
+
+/**
+ * Set the field alignment mode.
+ *
+ * @private
+ * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
+ * @chainable
+ */
+OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
+ if ( value !== this.align ) {
+ // Default to 'left'
+ if ( [ 'left', 'right', 'top', 'inline' ].indexOf( value ) === -1 ) {
+ value = 'left';
+ }
+ // Reorder elements
+ if ( value === 'inline' ) {
+ this.$body.append( this.$field, this.$label );
+ } else {
+ this.$body.append( this.$label, this.$field );
+ }
+ // Set classes. The following classes can be used here:
+ // * oo-ui-fieldLayout-align-left
+ // * oo-ui-fieldLayout-align-right
+ // * oo-ui-fieldLayout-align-top
+ // * oo-ui-fieldLayout-align-inline
+ if ( this.align ) {
+ this.$element.removeClass( 'oo-ui-fieldLayout-align-' + this.align );
+ }
+ this.$element.addClass( 'oo-ui-fieldLayout-align-' + value );
+ this.align = value;
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/FieldsetLayout.js b/vendor/oojs/oojs-ui/src/layouts/FieldsetLayout.js
new file mode 100644
index 00000000..1a21e8e1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/FieldsetLayout.js
@@ -0,0 +1,86 @@
+/**
+ * FieldsetLayouts are composed of one or more {@link OO.ui.FieldLayout FieldLayouts},
+ * which each contain an individual widget and, optionally, a label. Each Fieldset can be
+ * configured with a label as well. For more information and examples,
+ * please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Example of a fieldset layout
+ * var input1 = new OO.ui.TextInputWidget( {
+ * placeholder: 'A text input field'
+ * } );
+ *
+ * var input2 = new OO.ui.TextInputWidget( {
+ * placeholder: 'A text input field'
+ * } );
+ *
+ * var fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'Example of a fieldset layout'
+ * } );
+ *
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( input1, {
+ * label: 'Field One'
+ * } ),
+ * new OO.ui.FieldLayout( input2, {
+ * label: 'Field Two'
+ * } )
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.FieldLayout[]} [items] An array of fields to add to the fieldset. See OO.ui.FieldLayout for more information about fields.
+ */
+OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.FieldsetLayout.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.GroupElement.call( this, config );
+
+ if ( config.help ) {
+ this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
+ classes: [ 'oo-ui-fieldsetLayout-help' ],
+ framed: false,
+ icon: 'info'
+ } );
+
+ this.popupButtonWidget.getPopup().$body.append(
+ $( '<div>' )
+ .text( config.help )
+ .addClass( 'oo-ui-fieldsetLayout-help-content' )
+ );
+ this.$help = this.popupButtonWidget.$element;
+ } else {
+ this.$help = $( [] );
+ }
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-fieldsetLayout' )
+ .prepend( this.$help, this.$icon, this.$label, this.$group );
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FieldsetLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.IconElement );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.GroupElement );
diff --git a/vendor/oojs/oojs-ui/src/layouts/FormLayout.js b/vendor/oojs/oojs-ui/src/layouts/FormLayout.js
new file mode 100644
index 00000000..a131c169
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/FormLayout.js
@@ -0,0 +1,119 @@
+/**
+ * FormLayouts are used to wrap {@link OO.ui.FieldsetLayout FieldsetLayouts} when you intend to use browser-based
+ * form submission for the fields instead of handling them in JavaScript. Form layouts can be configured with an
+ * HTML form action, an encoding type, and a method using the #action, #enctype, and #method configs, respectively.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * Only widgets from the {@link OO.ui.InputWidget InputWidget} family support form submission. It
+ * includes standard form elements like {@link OO.ui.CheckboxInputWidget checkboxes}, {@link
+ * OO.ui.RadioInputWidget radio buttons} and {@link OO.ui.TextInputWidget text fields}, as well as
+ * some fancier controls. Some controls have both regular and InputWidget variants, for example
+ * OO.ui.DropdownWidget and OO.ui.DropdownInputWidget – only the latter support form submission and
+ * often have simplified APIs to match the capabilities of HTML forms.
+ * See the [OOjs UI Inputs documentation on MediaWiki] [2] for more information about InputWidgets.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Forms
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @example
+ * // Example of a form layout that wraps a fieldset layout
+ * var input1 = new OO.ui.TextInputWidget( {
+ * placeholder: 'Username'
+ * } );
+ * var input2 = new OO.ui.TextInputWidget( {
+ * placeholder: 'Password',
+ * type: 'password'
+ * } );
+ * var submit = new OO.ui.ButtonInputWidget( {
+ * label: 'Submit'
+ * } );
+ *
+ * var fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'A form layout'
+ * } );
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( input1, {
+ * label: 'Username',
+ * align: 'top'
+ * } ),
+ * new OO.ui.FieldLayout( input2, {
+ * label: 'Password',
+ * align: 'top'
+ * } ),
+ * new OO.ui.FieldLayout( submit )
+ * ] );
+ * var form = new OO.ui.FormLayout( {
+ * items: [ fieldset ],
+ * action: '/api/formhandler',
+ * method: 'get'
+ * } )
+ * $( 'body' ).append( form.$element );
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [method] HTML form `method` attribute
+ * @cfg {string} [action] HTML form `action` attribute
+ * @cfg {string} [enctype] HTML form `enctype` attribute
+ * @cfg {OO.ui.FieldsetLayout[]} [items] Fieldset layouts to add to the form layout.
+ */
+OO.ui.FormLayout = function OoUiFormLayout( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.FormLayout.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+ // Events
+ this.$element.on( 'submit', this.onFormSubmit.bind( this ) );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-formLayout' )
+ .attr( {
+ method: config.method,
+ action: config.action,
+ enctype: config.enctype
+ } );
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FormLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FormLayout, OO.ui.GroupElement );
+
+/* Events */
+
+/**
+ * A 'submit' event is emitted when the form is submitted.
+ *
+ * @event submit
+ */
+
+/* Static Properties */
+
+OO.ui.FormLayout.static.tagName = 'form';
+
+/* Methods */
+
+/**
+ * Handle form submit events.
+ *
+ * @private
+ * @param {jQuery.Event} e Submit event
+ * @fires submit
+ */
+OO.ui.FormLayout.prototype.onFormSubmit = function () {
+ if ( this.emit( 'submit' ) ) {
+ return false;
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/IndexLayout.js b/vendor/oojs/oojs-ui/src/layouts/IndexLayout.js
new file mode 100644
index 00000000..4cda00a9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/IndexLayout.js
@@ -0,0 +1,452 @@
+/**
+ * IndexLayouts contain {@link OO.ui.CardLayout card layouts} as well as
+ * {@link OO.ui.TabSelectWidget tabs} that allow users to easily navigate through the cards and
+ * select which one to display. By default, only one card is displayed at a time. When a user
+ * navigates to a new card, the index layout automatically focuses on the first focusable element,
+ * unless the default setting is changed.
+ *
+ * TODO: This class is similar to BookletLayout, we may want to refactor to reduce duplication
+ *
+ * @example
+ * // Example of a IndexLayout that contains two CardLayouts.
+ *
+ * function CardOneLayout( name, config ) {
+ * CardOneLayout.super.call( this, name, config );
+ * this.$element.append( '<p>First card</p>' );
+ * }
+ * OO.inheritClass( CardOneLayout, OO.ui.CardLayout );
+ * CardOneLayout.prototype.setupTabItem = function () {
+ * this.tabItem.setLabel( 'Card One' );
+ * };
+ *
+ * function CardTwoLayout( name, config ) {
+ * CardTwoLayout.super.call( this, name, config );
+ * this.$element.append( '<p>Second card</p>' );
+ * }
+ * OO.inheritClass( CardTwoLayout, OO.ui.CardLayout );
+ * CardTwoLayout.prototype.setupTabItem = function () {
+ * this.tabItem.setLabel( 'Card Two' );
+ * };
+ *
+ * var card1 = new CardOneLayout( 'one' ),
+ * card2 = new CardTwoLayout( 'two' );
+ *
+ * var index = new OO.ui.IndexLayout();
+ *
+ * index.addCards ( [ card1, card2 ] );
+ * $( 'body' ).append( index.$element );
+ *
+ * @class
+ * @extends OO.ui.MenuLayout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all cards, one after another
+ * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new card is displayed.
+ */
+OO.ui.IndexLayout = function OoUiIndexLayout( config ) {
+ // Configuration initialization
+ config = $.extend( {}, config, { menuPosition: 'top' } );
+
+ // Parent constructor
+ OO.ui.IndexLayout.super.call( this, config );
+
+ // Properties
+ this.currentCardName = null;
+ this.cards = {};
+ this.ignoreFocus = false;
+ this.stackLayout = new OO.ui.StackLayout( { continuous: !!config.continuous } );
+ this.$content.append( this.stackLayout.$element );
+ this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
+
+ this.tabSelectWidget = new OO.ui.TabSelectWidget();
+ this.tabPanel = new OO.ui.PanelLayout();
+ this.$menu.append( this.tabPanel.$element );
+
+ this.toggleMenu( true );
+
+ // Events
+ this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
+ this.tabSelectWidget.connect( this, { select: 'onTabSelectWidgetSelect' } );
+ if ( this.autoFocus ) {
+ // Event 'focus' does not bubble, but 'focusin' does
+ this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
+ }
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-indexLayout' );
+ this.stackLayout.$element.addClass( 'oo-ui-indexLayout-stackLayout' );
+ this.tabPanel.$element
+ .addClass( 'oo-ui-indexLayout-tabPanel' )
+ .append( this.tabSelectWidget.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndexLayout, OO.ui.MenuLayout );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when a card is {@link #setCard set} to be displayed by the index layout.
+ * @event set
+ * @param {OO.ui.CardLayout} card Current card
+ */
+
+/**
+ * An 'add' event is emitted when cards are {@link #addCards added} to the index layout.
+ *
+ * @event add
+ * @param {OO.ui.CardLayout[]} card Added cards
+ * @param {number} index Index cards were added at
+ */
+
+/**
+ * A 'remove' event is emitted when cards are {@link #clearCards cleared} or
+ * {@link #removeCards removed} from the index.
+ *
+ * @event remove
+ * @param {OO.ui.CardLayout[]} cards Removed cards
+ */
+
+/* Methods */
+
+/**
+ * Handle stack layout focus.
+ *
+ * @private
+ * @param {jQuery.Event} e Focusin event
+ */
+OO.ui.IndexLayout.prototype.onStackLayoutFocus = function ( e ) {
+ var name, $target;
+
+ // Find the card that an element was focused within
+ $target = $( e.target ).closest( '.oo-ui-cardLayout' );
+ for ( name in this.cards ) {
+ // Check for card match, exclude current card to find only card changes
+ if ( this.cards[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentCardName ) {
+ this.setCard( name );
+ break;
+ }
+ }
+};
+
+/**
+ * Handle stack layout set events.
+ *
+ * @private
+ * @param {OO.ui.PanelLayout|null} card The card panel that is now the current panel
+ */
+OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( card ) {
+ var layout = this;
+ if ( card ) {
+ card.scrollElementIntoView( { complete: function () {
+ if ( layout.autoFocus ) {
+ layout.focus();
+ }
+ } } );
+ }
+};
+
+/**
+ * Focus the first input in the current card.
+ *
+ * If no card is selected, the first selectable card will be selected.
+ * If the focus is already in an element on the current card, nothing will happen.
+ * @param {number} [itemIndex] A specific item to focus on
+ */
+OO.ui.IndexLayout.prototype.focus = function ( itemIndex ) {
+ var $input, card,
+ items = this.stackLayout.getItems();
+
+ if ( itemIndex !== undefined && items[ itemIndex ] ) {
+ card = items[ itemIndex ];
+ } else {
+ card = this.stackLayout.getCurrentItem();
+ }
+
+ if ( !card ) {
+ this.selectFirstSelectableCard();
+ card = this.stackLayout.getCurrentItem();
+ }
+ if ( !card ) {
+ return;
+ }
+ // Only change the focus if is not already in the current card
+ if ( !card.$element.find( ':focus' ).length ) {
+ $input = card.$element.find( ':input:first' );
+ if ( $input.length ) {
+ $input[ 0 ].focus();
+ }
+ }
+};
+
+/**
+ * Find the first focusable input in the index layout and focus
+ * on it.
+ */
+OO.ui.IndexLayout.prototype.focusFirstFocusable = function () {
+ var i, len,
+ found = false,
+ items = this.stackLayout.getItems(),
+ checkAndFocus = function () {
+ if ( OO.ui.isFocusableElement( $( this ) ) ) {
+ $( this ).focus();
+ found = true;
+ return false;
+ }
+ };
+
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ if ( found ) {
+ break;
+ }
+ // Find all potentially focusable elements in the item
+ // and check if they are focusable
+ items[i].$element
+ .find( 'input, select, textarea, button, object' )
+ .each( checkAndFocus );
+ }
+};
+
+/**
+ * Handle tab widget select events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+OO.ui.IndexLayout.prototype.onTabSelectWidgetSelect = function ( item ) {
+ if ( item ) {
+ this.setCard( item.getData() );
+ }
+};
+
+/**
+ * Get the card closest to the specified card.
+ *
+ * @param {OO.ui.CardLayout} card Card to use as a reference point
+ * @return {OO.ui.CardLayout|null} Card closest to the specified card
+ */
+OO.ui.IndexLayout.prototype.getClosestCard = function ( card ) {
+ var next, prev, level,
+ cards = this.stackLayout.getItems(),
+ index = $.inArray( card, cards );
+
+ if ( index !== -1 ) {
+ next = cards[ index + 1 ];
+ prev = cards[ index - 1 ];
+ // Prefer adjacent cards at the same level
+ level = this.tabSelectWidget.getItemFromData( card.getName() ).getLevel();
+ if (
+ prev &&
+ level === this.tabSelectWidget.getItemFromData( prev.getName() ).getLevel()
+ ) {
+ return prev;
+ }
+ if (
+ next &&
+ level === this.tabSelectWidget.getItemFromData( next.getName() ).getLevel()
+ ) {
+ return next;
+ }
+ }
+ return prev || next || null;
+};
+
+/**
+ * Get the tabs widget.
+ *
+ * @return {OO.ui.TabSelectWidget} Tabs widget
+ */
+OO.ui.IndexLayout.prototype.getTabs = function () {
+ return this.tabSelectWidget;
+};
+
+/**
+ * Get a card by its symbolic name.
+ *
+ * @param {string} name Symbolic name of card
+ * @return {OO.ui.CardLayout|undefined} Card, if found
+ */
+OO.ui.IndexLayout.prototype.getCard = function ( name ) {
+ return this.cards[ name ];
+};
+
+/**
+ * Get the current card.
+ *
+ * @return {OO.ui.CardLayout|undefined} Current card, if found
+ */
+OO.ui.IndexLayout.prototype.getCurrentCard = function () {
+ var name = this.getCurrentCardName();
+ return name ? this.getCard( name ) : undefined;
+};
+
+/**
+ * Get the symbolic name of the current card.
+ *
+ * @return {string|null} Symbolic name of the current card
+ */
+OO.ui.IndexLayout.prototype.getCurrentCardName = function () {
+ return this.currentCardName;
+};
+
+/**
+ * Add cards to the index layout
+ *
+ * When cards are added with the same names as existing cards, the existing cards will be
+ * automatically removed before the new cards are added.
+ *
+ * @param {OO.ui.CardLayout[]} cards Cards to add
+ * @param {number} index Index of the insertion point
+ * @fires add
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.addCards = function ( cards, index ) {
+ var i, len, name, card, item, currentIndex,
+ stackLayoutCards = this.stackLayout.getItems(),
+ remove = [],
+ items = [];
+
+ // Remove cards with same names
+ for ( i = 0, len = cards.length; i < len; i++ ) {
+ card = cards[ i ];
+ name = card.getName();
+
+ if ( Object.prototype.hasOwnProperty.call( this.cards, name ) ) {
+ // Correct the insertion index
+ currentIndex = $.inArray( this.cards[ name ], stackLayoutCards );
+ if ( currentIndex !== -1 && currentIndex + 1 < index ) {
+ index--;
+ }
+ remove.push( this.cards[ name ] );
+ }
+ }
+ if ( remove.length ) {
+ this.removeCards( remove );
+ }
+
+ // Add new cards
+ for ( i = 0, len = cards.length; i < len; i++ ) {
+ card = cards[ i ];
+ name = card.getName();
+ this.cards[ card.getName() ] = card;
+ item = new OO.ui.TabOptionWidget( { data: name } );
+ card.setTabItem( item );
+ items.push( item );
+ }
+
+ if ( items.length ) {
+ this.tabSelectWidget.addItems( items, index );
+ this.selectFirstSelectableCard();
+ }
+ this.stackLayout.addItems( cards, index );
+ this.emit( 'add', cards, index );
+
+ return this;
+};
+
+/**
+ * Remove the specified cards from the index layout.
+ *
+ * To remove all cards from the index, you may wish to use the #clearCards method instead.
+ *
+ * @param {OO.ui.CardLayout[]} cards An array of cards to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.removeCards = function ( cards ) {
+ var i, len, name, card,
+ items = [];
+
+ for ( i = 0, len = cards.length; i < len; i++ ) {
+ card = cards[ i ];
+ name = card.getName();
+ delete this.cards[ name ];
+ items.push( this.tabSelectWidget.getItemFromData( name ) );
+ card.setTabItem( null );
+ }
+ if ( items.length ) {
+ this.tabSelectWidget.removeItems( items );
+ this.selectFirstSelectableCard();
+ }
+ this.stackLayout.removeItems( cards );
+ this.emit( 'remove', cards );
+
+ return this;
+};
+
+/**
+ * Clear all cards from the index layout.
+ *
+ * To remove only a subset of cards from the index, use the #removeCards method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.clearCards = function () {
+ var i, len,
+ cards = this.stackLayout.getItems();
+
+ this.cards = {};
+ this.currentCardName = null;
+ this.tabSelectWidget.clearItems();
+ for ( i = 0, len = cards.length; i < len; i++ ) {
+ cards[ i ].setTabItem( null );
+ }
+ this.stackLayout.clearItems();
+
+ this.emit( 'remove', cards );
+
+ return this;
+};
+
+/**
+ * Set the current card by symbolic name.
+ *
+ * @fires set
+ * @param {string} name Symbolic name of card
+ */
+OO.ui.IndexLayout.prototype.setCard = function ( name ) {
+ var selectedItem,
+ $focused,
+ card = this.cards[ name ];
+
+ if ( name !== this.currentCardName ) {
+ selectedItem = this.tabSelectWidget.getSelectedItem();
+ if ( selectedItem && selectedItem.getData() !== name ) {
+ this.tabSelectWidget.selectItemByData( name );
+ }
+ if ( card ) {
+ if ( this.currentCardName && this.cards[ this.currentCardName ] ) {
+ this.cards[ this.currentCardName ].setActive( false );
+ // Blur anything focused if the next card doesn't have anything focusable - this
+ // is not needed if the next card has something focusable because once it is focused
+ // this blur happens automatically
+ if ( this.autoFocus && !card.$element.find( ':input' ).length ) {
+ $focused = this.cards[ this.currentCardName ].$element.find( ':focus' );
+ if ( $focused.length ) {
+ $focused[ 0 ].blur();
+ }
+ }
+ }
+ this.currentCardName = name;
+ this.stackLayout.setItem( card );
+ card.setActive( true );
+ this.emit( 'set', card );
+ }
+ }
+};
+
+/**
+ * Select the first selectable card.
+ *
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () {
+ if ( !this.tabSelectWidget.getSelectedItem() ) {
+ this.tabSelectWidget.selectItem( this.tabSelectWidget.getFirstSelectableItem() );
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/MenuLayout.js b/vendor/oojs/oojs-ui/src/layouts/MenuLayout.js
new file mode 100644
index 00000000..53937cc7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/MenuLayout.js
@@ -0,0 +1,156 @@
+/**
+ * MenuLayouts combine a menu and a content {@link OO.ui.PanelLayout panel}. The menu is positioned relative to the content (after, before, top, or bottom)
+ * and its size is customized with the #menuSize config. The content area will fill all remaining space.
+ *
+ * @example
+ * var menuLayout = new OO.ui.MenuLayout( {
+ * position: 'top'
+ * } ),
+ * menuPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
+ * contentPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
+ * select = new OO.ui.SelectWidget( {
+ * items: [
+ * new OO.ui.OptionWidget( {
+ * data: 'before',
+ * label: 'Before',
+ * } ),
+ * new OO.ui.OptionWidget( {
+ * data: 'after',
+ * label: 'After',
+ * } ),
+ * new OO.ui.OptionWidget( {
+ * data: 'top',
+ * label: 'Top',
+ * } ),
+ * new OO.ui.OptionWidget( {
+ * data: 'bottom',
+ * label: 'Bottom',
+ * } )
+ * ]
+ * } ).on( 'select', function ( item ) {
+ * menuLayout.setMenuPosition( item.getData() );
+ * } );
+ *
+ * menuLayout.$menu.append(
+ * menuPanel.$element.append( '<b>Menu panel</b>', select.$element )
+ * );
+ * menuLayout.$content.append(
+ * contentPanel.$element.append( '<b>Content panel</b>', '<p>Note that the menu is positioned relative to the content panel: top, bottom, after, before.</p>')
+ * );
+ * $( 'body' ).append( menuLayout.$element );
+ *
+ * If menu size needs to be overridden, it can be accomplished using CSS similar to the snippet
+ * below. MenuLayout's CSS will override the appropriate values with 'auto' or '0' to display the
+ * menu correctly. If `menuPosition` is known beforehand, CSS rules corresponding to other positions
+ * may be omitted.
+ *
+ * .oo-ui-menuLayout-menu {
+ * height: 200px;
+ * width: 200px;
+ * }
+ * .oo-ui-menuLayout-content {
+ * top: 200px;
+ * left: 200px;
+ * right: 200px;
+ * bottom: 200px;
+ * }
+ *
+ * @class
+ * @extends OO.ui.Layout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [showMenu=true] Show menu
+ * @cfg {string} [menuPosition='before'] Position of menu: `top`, `after`, `bottom` or `before`
+ */
+OO.ui.MenuLayout = function OoUiMenuLayout( config ) {
+ // Configuration initialization
+ config = $.extend( {
+ showMenu: true,
+ menuPosition: 'before'
+ }, config );
+
+ // Parent constructor
+ OO.ui.MenuLayout.super.call( this, config );
+
+ /**
+ * Menu DOM node
+ *
+ * @property {jQuery}
+ */
+ this.$menu = $( '<div>' );
+ /**
+ * Content DOM node
+ *
+ * @property {jQuery}
+ */
+ this.$content = $( '<div>' );
+
+ // Initialization
+ this.$menu
+ .addClass( 'oo-ui-menuLayout-menu' );
+ this.$content.addClass( 'oo-ui-menuLayout-content' );
+ this.$element
+ .addClass( 'oo-ui-menuLayout' )
+ .append( this.$content, this.$menu );
+ this.setMenuPosition( config.menuPosition );
+ this.toggleMenu( config.showMenu );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuLayout, OO.ui.Layout );
+
+/* Methods */
+
+/**
+ * Toggle menu.
+ *
+ * @param {boolean} showMenu Show menu, omit to toggle
+ * @chainable
+ */
+OO.ui.MenuLayout.prototype.toggleMenu = function ( showMenu ) {
+ showMenu = showMenu === undefined ? !this.showMenu : !!showMenu;
+
+ if ( this.showMenu !== showMenu ) {
+ this.showMenu = showMenu;
+ this.$element
+ .toggleClass( 'oo-ui-menuLayout-showMenu', this.showMenu )
+ .toggleClass( 'oo-ui-menuLayout-hideMenu', !this.showMenu );
+ }
+
+ return this;
+};
+
+/**
+ * Check if menu is visible
+ *
+ * @return {boolean} Menu is visible
+ */
+OO.ui.MenuLayout.prototype.isMenuVisible = function () {
+ return this.showMenu;
+};
+
+/**
+ * Set menu position.
+ *
+ * @param {string} position Position of menu, either `top`, `after`, `bottom` or `before`
+ * @throws {Error} If position value is not supported
+ * @chainable
+ */
+OO.ui.MenuLayout.prototype.setMenuPosition = function ( position ) {
+ this.$element.removeClass( 'oo-ui-menuLayout-' + this.menuPosition );
+ this.menuPosition = position;
+ this.$element.addClass( 'oo-ui-menuLayout-' + position );
+
+ return this;
+};
+
+/**
+ * Get menu position.
+ *
+ * @return {string} Menu position
+ */
+OO.ui.MenuLayout.prototype.getMenuPosition = function () {
+ return this.menuPosition;
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/PageLayout.js b/vendor/oojs/oojs-ui/src/layouts/PageLayout.js
new file mode 100644
index 00000000..4753b7db
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/PageLayout.js
@@ -0,0 +1,138 @@
+/**
+ * PageLayouts are used within {@link OO.ui.BookletLayout booklet layouts} to create pages that users can select and display
+ * from the booklet's optional {@link OO.ui.OutlineSelectWidget outline} navigation. Pages are usually not instantiated directly,
+ * rather extended to include the required content and functionality.
+ *
+ * Each page must have a unique symbolic name, which is passed to the constructor. In addition, the page's outline
+ * item is customized (with a label, outline level, etc.) using the #setupOutlineItem method. See
+ * {@link OO.ui.BookletLayout BookletLayout} for an example.
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ *
+ * @constructor
+ * @param {string} name Unique symbolic name of page
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( name ) && config === undefined ) {
+ config = name;
+ name = config.name;
+ }
+
+ // Configuration initialization
+ config = $.extend( { scrollable: true }, config );
+
+ // Parent constructor
+ OO.ui.PageLayout.super.call( this, config );
+
+ // Properties
+ this.name = name;
+ this.outlineItem = null;
+ this.active = false;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-pageLayout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PageLayout, OO.ui.PanelLayout );
+
+/* Events */
+
+/**
+ * An 'active' event is emitted when the page becomes active. Pages become active when they are
+ * shown in a booklet layout that is configured to display only one page at a time.
+ *
+ * @event active
+ * @param {boolean} active Page is active
+ */
+
+/* Methods */
+
+/**
+ * Get the symbolic name of the page.
+ *
+ * @return {string} Symbolic name of page
+ */
+OO.ui.PageLayout.prototype.getName = function () {
+ return this.name;
+};
+
+/**
+ * Check if page is active.
+ *
+ * Pages become active when they are shown in a {@link OO.ui.BookletLayout booklet layout} that is configured to display
+ * only one page at a time. Additional CSS is applied to the page's outline item to reflect the active state.
+ *
+ * @return {boolean} Page is active
+ */
+OO.ui.PageLayout.prototype.isActive = function () {
+ return this.active;
+};
+
+/**
+ * Get outline item.
+ *
+ * The outline item allows users to access the page from the booklet's outline
+ * navigation. The outline item itself can be customized (with a label, level, etc.) using the #setupOutlineItem method.
+ *
+ * @return {OO.ui.OutlineOptionWidget|null} Outline option widget
+ */
+OO.ui.PageLayout.prototype.getOutlineItem = function () {
+ return this.outlineItem;
+};
+
+/**
+ * Set or unset the outline item.
+ *
+ * Specify an {@link OO.ui.OutlineOptionWidget outline option} to set it,
+ * or `null` to clear the outline item. To customize the outline item itself (e.g., to set a label or outline
+ * level), use #setupOutlineItem instead of this method.
+ *
+ * @param {OO.ui.OutlineOptionWidget|null} outlineItem Outline option widget, null to clear
+ * @chainable
+ */
+OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
+ this.outlineItem = outlineItem || null;
+ if ( outlineItem ) {
+ this.setupOutlineItem();
+ }
+ return this;
+};
+
+/**
+ * Set up the outline item.
+ *
+ * Use this method to customize the outline item (e.g., to add a label or outline level). To set or unset
+ * the outline item itself (with an {@link OO.ui.OutlineOptionWidget outline option} or `null`), use
+ * the #setOutlineItem method instead.
+ *
+ * @param {OO.ui.OutlineOptionWidget} outlineItem Outline option widget to set up
+ * @chainable
+ */
+OO.ui.PageLayout.prototype.setupOutlineItem = function () {
+ return this;
+};
+
+/**
+ * Set the page to its 'active' state.
+ *
+ * Pages become active when they are shown in a booklet layout that is configured to display only one page at a time. Additional
+ * CSS is applied to the outline item to reflect the page's active state. Outside of the booklet
+ * context, setting the active state on a page does nothing.
+ *
+ * @param {boolean} value Page is active
+ * @fires active
+ */
+OO.ui.PageLayout.prototype.setActive = function ( active ) {
+ active = !!active;
+
+ if ( active !== this.active ) {
+ this.active = active;
+ this.$element.toggleClass( 'oo-ui-pageLayout-active', active );
+ this.emit( 'active', this.active );
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/layouts/PanelLayout.js b/vendor/oojs/oojs-ui/src/layouts/PanelLayout.js
new file mode 100644
index 00000000..9183f597
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/PanelLayout.js
@@ -0,0 +1,55 @@
+/**
+ * PanelLayouts expand to cover the entire area of their parent. They can be configured with scrolling, padding,
+ * and a frame, and are often used together with {@link OO.ui.StackLayout StackLayouts}.
+ *
+ * @example
+ * // Example of a panel layout
+ * var panel = new OO.ui.PanelLayout( {
+ * expanded: false,
+ * framed: true,
+ * padded: true,
+ * $content: $( '<p>A panel layout with padding and a frame.</p>' )
+ * } );
+ * $( 'body' ).append( panel.$element );
+ *
+ * @class
+ * @extends OO.ui.Layout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [scrollable=false] Allow vertical scrolling
+ * @cfg {boolean} [padded=false] Add padding between the content and the edges of the panel.
+ * @cfg {boolean} [expanded=true] Expand the panel to fill the entire parent element.
+ * @cfg {boolean} [framed=false] Render the panel with a frame to visually separate it from outside content.
+ */
+OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
+ // Configuration initialization
+ config = $.extend( {
+ scrollable: false,
+ padded: false,
+ expanded: true,
+ framed: false
+ }, config );
+
+ // Parent constructor
+ OO.ui.PanelLayout.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-panelLayout' );
+ if ( config.scrollable ) {
+ this.$element.addClass( 'oo-ui-panelLayout-scrollable' );
+ }
+ if ( config.padded ) {
+ this.$element.addClass( 'oo-ui-panelLayout-padded' );
+ }
+ if ( config.expanded ) {
+ this.$element.addClass( 'oo-ui-panelLayout-expanded' );
+ }
+ if ( config.framed ) {
+ this.$element.addClass( 'oo-ui-panelLayout-framed' );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PanelLayout, OO.ui.Layout );
diff --git a/vendor/oojs/oojs-ui/src/layouts/StackLayout.js b/vendor/oojs/oojs-ui/src/layouts/StackLayout.js
new file mode 100644
index 00000000..58f35d31
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/layouts/StackLayout.js
@@ -0,0 +1,214 @@
+/**
+ * StackLayouts contain a series of {@link OO.ui.PanelLayout panel layouts}. By default, only one panel is displayed
+ * at a time, though the stack layout can also be configured to show all contained panels, one after another,
+ * by setting the #continuous option to 'true'.
+ *
+ * @example
+ * // A stack layout with two panels, configured to be displayed continously
+ * var myStack = new OO.ui.StackLayout( {
+ * items: [
+ * new OO.ui.PanelLayout( {
+ * $content: $( '<p>Panel One</p>' ),
+ * padded: true,
+ * framed: true
+ * } ),
+ * new OO.ui.PanelLayout( {
+ * $content: $( '<p>Panel Two</p>' ),
+ * padded: true,
+ * framed: true
+ * } )
+ * ],
+ * continuous: true
+ * } );
+ * $( 'body' ).append( myStack.$element );
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all panels, one after another. By default, only one panel is displayed at a time.
+ * @cfg {OO.ui.Layout[]} [items] Panel layouts to add to the stack layout.
+ */
+OO.ui.StackLayout = function OoUiStackLayout( config ) {
+ // Configuration initialization
+ config = $.extend( { scrollable: true }, config );
+
+ // Parent constructor
+ OO.ui.StackLayout.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+ // Properties
+ this.currentItem = null;
+ this.continuous = !!config.continuous;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-stackLayout' );
+ if ( this.continuous ) {
+ this.$element.addClass( 'oo-ui-stackLayout-continuous' );
+ }
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.StackLayout, OO.ui.PanelLayout );
+OO.mixinClass( OO.ui.StackLayout, OO.ui.GroupElement );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when panels are {@link #addItems added}, {@link #removeItems removed},
+ * {@link #clearItems cleared} or {@link #setItem displayed}.
+ *
+ * @event set
+ * @param {OO.ui.Layout|null} item Current panel or `null` if no panel is shown
+ */
+
+/* Methods */
+
+/**
+ * Get the current panel.
+ *
+ * @return {OO.ui.Layout|null}
+ */
+OO.ui.StackLayout.prototype.getCurrentItem = function () {
+ return this.currentItem;
+};
+
+/**
+ * Unset the current item.
+ *
+ * @private
+ * @param {OO.ui.StackLayout} layout
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.unsetCurrentItem = function () {
+ var prevItem = this.currentItem;
+ if ( prevItem === null ) {
+ return;
+ }
+
+ this.currentItem = null;
+ this.emit( 'set', null );
+};
+
+/**
+ * Add panel layouts to the stack layout.
+ *
+ * Panels will be added to the end of the stack layout array unless the optional index parameter specifies a different
+ * insertion point. Adding a panel that is already in the stack will move it to the end of the array or the point specified
+ * by the index.
+ *
+ * @param {OO.ui.Layout[]} items Panels to add
+ * @param {number} [index] Index of the insertion point
+ * @chainable
+ */
+OO.ui.StackLayout.prototype.addItems = function ( items, index ) {
+ // Update the visibility
+ this.updateHiddenState( items, this.currentItem );
+
+ // Mixin method
+ OO.ui.GroupElement.prototype.addItems.call( this, items, index );
+
+ if ( !this.currentItem && items.length ) {
+ this.setItem( items[ 0 ] );
+ }
+
+ return this;
+};
+
+/**
+ * Remove the specified panels from the stack layout.
+ *
+ * Removed panels are detached from the DOM, not removed, so that they may be reused. To remove all panels,
+ * you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.Layout[]} items Panels to remove
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.removeItems = function ( items ) {
+ // Mixin method
+ OO.ui.GroupElement.prototype.removeItems.call( this, items );
+
+ if ( $.inArray( this.currentItem, items ) !== -1 ) {
+ if ( this.items.length ) {
+ this.setItem( this.items[ 0 ] );
+ } else {
+ this.unsetCurrentItem();
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Clear all panels from the stack layout.
+ *
+ * Cleared panels are detached from the DOM, not removed, so that they may be reused. To remove only
+ * a subset of panels, use the #removeItems method.
+ *
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.clearItems = function () {
+ this.unsetCurrentItem();
+ OO.ui.GroupElement.prototype.clearItems.call( this );
+
+ return this;
+};
+
+/**
+ * Show the specified panel.
+ *
+ * If another panel is currently displayed, it will be hidden.
+ *
+ * @param {OO.ui.Layout} item Panel to show
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.setItem = function ( item ) {
+ if ( item !== this.currentItem ) {
+ this.updateHiddenState( this.items, item );
+
+ if ( $.inArray( item, this.items ) !== -1 ) {
+ this.currentItem = item;
+ this.emit( 'set', item );
+ } else {
+ this.unsetCurrentItem();
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Update the visibility of all items in case of non-continuous view.
+ *
+ * Ensure all items are hidden except for the selected one.
+ * This method does nothing when the stack is continuous.
+ *
+ * @private
+ * @param {OO.ui.Layout[]} items Item list iterate over
+ * @param {OO.ui.Layout} [selectedItem] Selected item to show
+ */
+OO.ui.StackLayout.prototype.updateHiddenState = function ( items, selectedItem ) {
+ var i, len;
+
+ if ( !this.continuous ) {
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ if ( !selectedItem || selectedItem !== items[ i ] ) {
+ items[ i ].$element.addClass( 'oo-ui-element-hidden' );
+ }
+ }
+ if ( selectedItem ) {
+ selectedItem.$element.removeClass( 'oo-ui-element-hidden' );
+ }
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/outro.js.txt b/vendor/oojs/oojs-ui/src/outro.js.txt
new file mode 100644
index 00000000..4cc1e390
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/outro.js.txt
@@ -0,0 +1 @@
+}( OO ) );
diff --git a/vendor/oojs/oojs-ui/src/styles/Dialog.less b/vendor/oojs/oojs-ui/src/styles/Dialog.less
new file mode 100644
index 00000000..391cefda
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Dialog.less
@@ -0,0 +1,35 @@
+@import 'common';
+
+.oo-ui-dialog {
+ &-content {
+ > .oo-ui-window {
+ &-head,
+ &-body,
+ &-foot {
+ position: absolute;
+ left: 0;
+ right: 0;
+ overflow: hidden;
+ .oo-ui-box-sizing(border-box);
+ }
+
+ &-head {
+ z-index: 1;
+ top: 0;
+ }
+
+ &-body {
+ z-index: 2;
+ top: 0;
+ bottom: 0;
+ }
+
+ &-foot {
+ z-index: 1;
+ bottom: 0;
+ }
+ }
+ }
+
+ .theme-oo-ui-dialog();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Element.less b/vendor/oojs/oojs-ui/src/styles/Element.less
new file mode 100644
index 00000000..f8f3389f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Element.less
@@ -0,0 +1,9 @@
+@import 'common';
+
+.oo-ui-element {
+ &-hidden {
+ display: none !important;
+ }
+
+ .theme-oo-ui-element();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Layout.less b/vendor/oojs/oojs-ui/src/styles/Layout.less
new file mode 100644
index 00000000..d1ed2f7e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Layout.less
@@ -0,0 +1,5 @@
+@import 'common';
+
+.oo-ui-layout {
+ .theme-oo-ui-layout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Tool.less b/vendor/oojs/oojs-ui/src/styles/Tool.less
new file mode 100644
index 00000000..86cf6c38
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Tool.less
@@ -0,0 +1,5 @@
+@import 'common';
+
+.oo-ui-tool {
+ .theme-oo-ui-tool();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/ToolGroup.less b/vendor/oojs/oojs-ui/src/styles/ToolGroup.less
new file mode 100644
index 00000000..4ba55ea3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/ToolGroup.less
@@ -0,0 +1,21 @@
+@import 'common';
+
+.oo-ui-toolGroup {
+ display: inline-block;
+ vertical-align: middle;
+
+ &-empty {
+ display: none;
+ }
+
+ .oo-ui-tool-link {
+ text-decoration: none;
+
+ .oo-ui-iconElement-icon {
+ background-position: center center;
+ background-repeat: no-repeat;
+ }
+ }
+
+ .theme-oo-ui-toolGroup();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Toolbar.less b/vendor/oojs/oojs-ui/src/styles/Toolbar.less
new file mode 100644
index 00000000..42eeeaa5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Toolbar.less
@@ -0,0 +1,52 @@
+@import 'common';
+
+.oo-ui-toolbar {
+ clear: both;
+
+ &-bar {
+ line-height: 1em;
+ position: relative;
+ }
+
+ &-actions {
+ float: right;
+
+ .oo-ui-toolbar {
+ display: inline-block;
+ }
+ }
+
+ &-tools {
+ display: inline;
+ white-space: nowrap;
+
+ .oo-ui-toolbar-narrow & {
+ white-space: normal;
+ }
+
+ // Tools like PopupToolGroup can have a lot of content, which should be wrapped normally
+ .oo-ui-tool {
+ white-space: normal;
+ }
+ }
+
+ &-tools,
+ &-actions,
+ &-shadow {
+ .oo-ui-unselectable();
+ }
+
+ &-actions .oo-ui-popupWidget {
+ .oo-ui-selectable();
+ }
+
+ &-shadow {
+ background-position: left top;
+ background-repeat: repeat-x;
+ position: absolute;
+ width: 100%;
+ pointer-events: none;
+ }
+
+ .theme-oo-ui-toolbar();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Widget.less b/vendor/oojs/oojs-ui/src/styles/Widget.less
new file mode 100644
index 00000000..107bc424
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Widget.less
@@ -0,0 +1,5 @@
+@import 'common';
+
+.oo-ui-widget {
+ .theme-oo-ui-widget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/Window.less b/vendor/oojs/oojs-ui/src/styles/Window.less
new file mode 100644
index 00000000..34064684
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/Window.less
@@ -0,0 +1,32 @@
+@import 'common';
+
+.oo-ui-window {
+ &-frame {
+ .oo-ui-box-sizing(border-box);
+ }
+
+ // Content div takes focus when opened, so hide outline
+ &-content:focus {
+ outline: none;
+ }
+
+ &-head,
+ &-foot {
+ .oo-ui-unselectable();
+ }
+
+ &-body {
+ margin: 0;
+ padding: 0;
+ background: none;
+ }
+
+ &-overlay {
+ position: absolute;
+ top: 0;
+ /* @noflip */
+ left: 0;
+ }
+
+ .theme-oo-ui-window();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/WindowManager.less b/vendor/oojs/oojs-ui/src/styles/WindowManager.less
new file mode 100644
index 00000000..bd1e2ab1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/WindowManager.less
@@ -0,0 +1,39 @@
+@import 'common';
+
+.oo-ui-windowManager {
+ &-modal > .oo-ui-dialog {
+ position: fixed;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+
+ &.oo-ui-window-active {
+ width: auto;
+ height: auto;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 1em;
+ }
+
+ &.oo-ui-window-setup > .oo-ui-window-frame {
+ position: absolute;
+ right: 0;
+ left: 0;
+ margin: auto;
+ overflow: hidden;
+ max-width: 100%;
+ max-height: 100%;
+ }
+ }
+
+ &-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ bottom: 0;
+ }
+
+ .theme-oo-ui-windowManager();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/common.less b/vendor/oojs/oojs-ui/src/styles/common.less
new file mode 100644
index 00000000..9663580f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/common.less
@@ -0,0 +1,102 @@
+@import 'theme';
+
+// Variables
+
+// @oo-ui-default-image-ext set during build
+// @oo-ui-distribution set during build
+@oo-ui-default-image-path: 'images';
+
+// Mixins
+
+.oo-ui-background-image( @url ) {
+ background-image: e('/* @embed */') url(~'@{url}');
+}
+
+.oo-ui-background-image-svg2( @svg, @fallback ) when ( @oo-ui-distribution = mixed ) {
+ background-image: url(@fallback);
+ background-image: -webkit-linear-gradient(transparent, transparent), e('/* @embed */') url(@svg);
+ background-image: linear-gradient(transparent, transparent), e('/* @embed */') url(@svg);
+ background-image: -o-linear-gradient(transparent, transparent), url(@fallback);
+}
+.oo-ui-background-image-svg2( @svg, @fallback ) when ( @oo-ui-distribution = vector ) {
+ .oo-ui-background-image(@svg);
+}
+.oo-ui-background-image-svg2( @svg, @fallback ) when ( @oo-ui-distribution = raster ) {
+ .oo-ui-background-image(@fallback);
+}
+
+.oo-ui-background-image-svg( @url-without-extension ) {
+ @svg: '@{url-without-extension}.svg';
+ @fallback: '@{url-without-extension}.@{oo-ui-default-image-ext}';
+ .oo-ui-background-image-svg2(@svg, @fallback);
+}
+
+.oo-ui-force-webkit-gpu() {
+ -webkit-transform: translate3d(0, 0, 0);
+}
+
+.oo-ui-animation( @value1, @value2: X, ... ) {
+ @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+ -webkit-animation: @value;
+ -moz-animation: @value;
+ -ms-animation: @value;
+ -o-animation: @value;
+ animation: @value;
+}
+
+.oo-ui-transition( @value1, @value2: X, ... ) {
+ @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+ -webkit-transition: @value;
+ -moz-transition: @value;
+ -ms-transition: @value;
+ -o-transition: @value;
+ transition: @value;
+}
+
+.oo-ui-transform( @string ) {
+ -webkit-transform: @string;
+ -moz-transform: @string;
+ -ms-transform: @string;
+ -o-transform: @string;
+ transform: @string;
+}
+
+.oo-ui-box-sizing( @type: border-box ) {
+ -webkit-box-sizing: @type;
+ -moz-box-sizing: @type;
+ box-sizing: @type;
+}
+
+.oo-ui-vertical-gradient( @start: #EEE, @stop: #FFF ) {
+ background: mix(@start, @stop, 50%);
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='%d', endColorstr='%d')", @start, @stop));
+ background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, @start), color-stop(100%, @stop));
+ background-image: -webkit-linear-gradient(top, @start 0%, @stop 100%);
+ background-image: -moz-linear-gradient(top, @start 0%, @stop 100%);
+ background-image: -ms-linear-gradient(top, @start 0%, @stop 100%);
+ background-image: -o-linear-gradient(top, @start 0%, @stop 100%);
+ background-image: linear-gradient(top, @start 0%, @stop 100%);
+}
+
+.oo-ui-unselectable() {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.oo-ui-selectable() {
+ -webkit-touch-callout: default;
+ -webkit-user-select: all;
+ -moz-user-select: all;
+ -ms-user-select: all;
+ user-select: all;
+}
+
+.oo-ui-inline-spacing( @spacing, @cancelled-spacing: 0 ) {
+ margin-right: @spacing;
+ &:last-child {
+ margin-right: @cancelled-spacing;
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/core.less b/vendor/oojs/oojs-ui/src/styles/core.less
new file mode 100644
index 00000000..fc1c91f8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/core.less
@@ -0,0 +1,114 @@
+//
+// Base styles.
+//
+// Themes should include this file after defining their variables and mixins.
+//
+
+@import 'common';
+
+/* @noflip */
+.oo-ui-rtl {
+ direction: rtl;
+}
+
+/* @noflip */
+.oo-ui-ltr {
+ direction: ltr;
+}
+
+@import 'Element.less';
+@import 'elements/ButtonElement.less';
+@import 'elements/ClippableElement.less';
+@import 'elements/FlaggedElement.less';
+@import 'elements/DraggableElement.less';
+@import 'elements/DraggableGroupElement.less';
+@import 'elements/GroupElement.less';
+@import 'elements/IconElement.less';
+@import 'elements/IndicatorElement.less';
+@import 'elements/LabelElement.less';
+@import 'elements/LookupElement.less';
+@import 'elements/PopupElement.less';
+@import 'elements/TabIndexedElement.less';
+@import 'elements/TitledElement.less';
+
+@import 'Layout.less';
+@import 'layouts/BookletLayout.less';
+@import 'layouts/IndexLayout.less';
+@import 'layouts/FieldLayout.less';
+@import 'layouts/ActionFieldLayout.less';
+@import 'layouts/FieldsetLayout.less';
+@import 'layouts/FormLayout.less';
+@import 'layouts/MenuLayout.less';
+@import 'layouts/PanelLayout.less';
+@import 'layouts/CardLayout.less';
+@import 'layouts/PageLayout.less';
+@import 'layouts/StackLayout.less';
+
+@import 'Tool.less';
+@import 'tools/PopupTool.less';
+@import 'tools/ToolGroupTool.less';
+
+@import 'ToolGroup.less';
+@import 'toolgroups/BarToolGroup.less';
+@import 'toolgroups/PopupToolGroup.less';
+@import 'toolgroups/ListToolGroup.less';
+@import 'toolgroups/MenuToolGroup.less';
+
+@import 'Toolbar.less';
+
+@import 'Widget.less';
+@import 'widgets/SelectWidget.less';
+@import 'widgets/OptionWidget.less';
+@import 'widgets/DecoratedOptionWidget.less';
+@import 'widgets/ButtonSelectWidget.less';
+@import 'widgets/RadioSelectWidget.less';
+@import 'widgets/ButtonOptionWidget.less';
+@import 'widgets/RadioOptionWidget.less';
+
+@import 'widgets/LabelWidget.less';
+@import 'widgets/IconWidget.less';
+@import 'widgets/IndicatorWidget.less';
+
+@import 'widgets/ButtonWidget.less';
+@import 'widgets/ButtonGroupWidget.less';
+
+@import 'widgets/ToggleWidget.less';
+@import 'widgets/ToggleButtonWidget.less';
+@import 'widgets/ToggleSwitchWidget.less';
+
+@import 'widgets/ProgressBarWidget.less';
+
+@import 'widgets/ActionWidget.less';
+
+@import 'widgets/PopupWidget.less';
+@import 'widgets/PopupButtonWidget.less';
+
+@import 'widgets/InputWidget.less';
+@import 'widgets/ButtonInputWidget.less';
+@import 'widgets/CheckboxInputWidget.less';
+@import 'widgets/DropdownInputWidget.less';
+@import 'widgets/RadioInputWidget.less';
+@import 'widgets/TextInputWidget.less';
+
+@import 'widgets/MenuSelectWidget.less';
+@import 'widgets/MenuOptionWidget.less';
+@import 'widgets/MenuSectionOptionWidget.less';
+@import 'widgets/TextInputMenuSelectWidget.less';
+@import 'widgets/DropdownWidget.less';
+
+@import 'widgets/OutlineSelectWidget.less';
+@import 'widgets/OutlineOptionWidget.less';
+@import 'widgets/OutlineControlsWidget.less';
+
+@import 'widgets/TabSelectWidget.less';
+@import 'widgets/TabOptionWidget.less';
+
+@import 'widgets/ComboBoxWidget.less';
+@import 'widgets/SearchWidget.less';
+
+@import 'Window.less';
+@import 'Dialog.less';
+@import 'dialogs/MessageDialog.less';
+@import 'dialogs/ProcessDialog.less';
+
+@import 'WindowManager.less';
diff --git a/vendor/oojs/oojs-ui/src/styles/dialogs/MessageDialog.less b/vendor/oojs/oojs-ui/src/styles/dialogs/MessageDialog.less
new file mode 100644
index 00000000..e50029f9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/dialogs/MessageDialog.less
@@ -0,0 +1,45 @@
+@import '../common';
+
+.oo-ui-messageDialog {
+ &-actions {
+ &-horizontal {
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+
+ .oo-ui-actionWidget {
+ display: table-cell;
+ width: 1%;
+ }
+ }
+
+ &-vertical {
+ display: block;
+
+ .oo-ui-actionWidget {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .oo-ui-actionWidget {
+ position: relative;
+ text-align: center;
+
+ .oo-ui-buttonElement-button {
+ display: block;
+ }
+
+ .oo-ui-labelElement-label {
+ position: relative;
+ top: auto;
+ bottom: auto;
+ display: inline;
+ white-space: nowrap;
+ }
+ }
+ }
+
+ .theme-oo-ui-messageDialog();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/dialogs/ProcessDialog.less b/vendor/oojs/oojs-ui/src/styles/dialogs/ProcessDialog.less
new file mode 100644
index 00000000..379588ae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/dialogs/ProcessDialog.less
@@ -0,0 +1,52 @@
+@import '../common';
+
+.oo-ui-processDialog {
+ &-location {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ &-title {
+ display: inline;
+ padding: 0;
+ }
+
+ &-actions {
+ &-safe,
+ &-primary,
+ &-other {
+ .oo-ui-actionWidget {
+ white-space: nowrap;
+ }
+ }
+
+ &-safe,
+ &-primary {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ }
+
+ &-safe {
+ left: 0;
+ }
+
+ &-primary {
+ right: 0;
+ }
+ }
+
+ &-errors {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 2;
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+
+ .theme-oo-ui-processDialog();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/ButtonElement.less b/vendor/oojs/oojs-ui/src/styles/elements/ButtonElement.less
new file mode 100644
index 00000000..248772e5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/ButtonElement.less
@@ -0,0 +1,62 @@
+@import '../common';
+
+.oo-ui-buttonElement {
+ > .oo-ui-buttonElement-button {
+ cursor: pointer;
+ display: inline-block;
+ vertical-align: middle;
+ font: inherit;
+ white-space: nowrap;
+ .oo-ui-unselectable();
+
+ > .oo-ui-iconElement-icon,
+ > .oo-ui-indicatorElement-indicator {
+ display: none;
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+ cursor: default;
+ }
+
+ &.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ display: inline-block;
+ vertical-align: middle;
+ background-position: center center;
+ background-repeat: no-repeat;
+ }
+
+ &-frameless {
+ display: inline-block;
+ position: relative;
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ }
+
+ &-framed {
+ > .oo-ui-buttonElement-button {
+ display: inline-block;
+ vertical-align: top;
+ text-align: center;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ &.oo-ui-widget-disabled {
+ > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ cursor: default;
+ }
+ }
+ }
+
+ .theme-oo-ui-buttonElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/ClippableElement.less b/vendor/oojs/oojs-ui/src/styles/elements/ClippableElement.less
new file mode 100644
index 00000000..2a661e93
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/ClippableElement.less
@@ -0,0 +1,7 @@
+@import '../common';
+
+.oo-ui-clippableElement-clippable {
+ .oo-ui-box-sizing(border-box);
+
+ .theme-oo-ui-clippableElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/DraggableElement.less b/vendor/oojs/oojs-ui/src/styles/elements/DraggableElement.less
new file mode 100644
index 00000000..3f382766
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/DraggableElement.less
@@ -0,0 +1,22 @@
+@import '../common';
+
+.oo-ui-draggableElement {
+ cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+
+ &-dragging {
+ cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+ background: rgba( 0, 0, 0, 0.2 );
+ opacity: 0.4;
+ }
+
+ /*
+ * HACK: In order to style horizontally, we must override
+ * OO.ui.OptionWidget's display rule that is currently set
+ * to be 'block'
+ */
+ .oo-ui-draggableGroupElement-horizontal &.oo-ui-optionWidget {
+ display: inline-block;
+ }
+
+ .theme-oo-ui-draggableElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/DraggableGroupElement.less b/vendor/oojs/oojs-ui/src/styles/elements/DraggableGroupElement.less
new file mode 100644
index 00000000..07e08594
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/DraggableGroupElement.less
@@ -0,0 +1,11 @@
+@import '../common';
+
+.oo-ui-draggableGroupElement {
+ &-placeholder {
+ position: absolute;
+ display: block;
+ background: rgba( 0, 0, 0, 0.4 );
+ }
+
+ .theme-oo-ui-draggableGroupElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/FlaggedElement.less b/vendor/oojs/oojs-ui/src/styles/elements/FlaggedElement.less
new file mode 100644
index 00000000..6e9202e9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/FlaggedElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-flaggedElement {
+ .theme-oo-ui-flaggedElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/GroupElement.less b/vendor/oojs/oojs-ui/src/styles/elements/GroupElement.less
new file mode 100644
index 00000000..159d32cc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/GroupElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-groupElement {
+ .theme-oo-ui-groupElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/IconElement.less b/vendor/oojs/oojs-ui/src/styles/elements/IconElement.less
new file mode 100644
index 00000000..5d90fda6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/IconElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-iconElement {
+ .theme-oo-ui-iconElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/IndicatorElement.less b/vendor/oojs/oojs-ui/src/styles/elements/IndicatorElement.less
new file mode 100644
index 00000000..d400803c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/IndicatorElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-indicatorElement {
+ .theme-oo-ui-indicatorElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/LabelElement.less b/vendor/oojs/oojs-ui/src/styles/elements/LabelElement.less
new file mode 100644
index 00000000..c47ecdc4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/LabelElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-labelElement {
+ .theme-oo-ui-labelElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/LookupElement.less b/vendor/oojs/oojs-ui/src/styles/elements/LookupElement.less
new file mode 100644
index 00000000..0ab29f72
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/LookupElement.less
@@ -0,0 +1,10 @@
+@import '../common';
+
+.oo-ui-lookupElement {
+ > .oo-ui-menuSelectWidget {
+ z-index: 1;
+ width: 100%;
+ }
+
+ .theme-oo-ui-lookupElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/PopupElement.less b/vendor/oojs/oojs-ui/src/styles/elements/PopupElement.less
new file mode 100644
index 00000000..12b80daf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/PopupElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-popupElement {
+ .theme-oo-ui-popupElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/TabIndexedElement.less b/vendor/oojs/oojs-ui/src/styles/elements/TabIndexedElement.less
new file mode 100644
index 00000000..f894d369
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/TabIndexedElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-tabIndexedElement {
+ .theme-oo-ui-tabIndexedElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/elements/TitledElement.less b/vendor/oojs/oojs-ui/src/styles/elements/TitledElement.less
new file mode 100644
index 00000000..f8a15b68
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/elements/TitledElement.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-titledElement {
+ .theme-oo-ui-titledElement();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/images/grab.cur b/vendor/oojs/oojs-ui/src/styles/images/grab.cur
new file mode 100644
index 00000000..fba3ddc8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/images/grab.cur
Binary files differ
diff --git a/vendor/oojs/oojs-ui/src/styles/images/grabbing.cur b/vendor/oojs/oojs-ui/src/styles/images/grabbing.cur
new file mode 100644
index 00000000..41aaa62a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/images/grabbing.cur
Binary files differ
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/ActionFieldLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/ActionFieldLayout.less
new file mode 100644
index 00000000..2d4c2a66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/ActionFieldLayout.less
@@ -0,0 +1,26 @@
+@import '../common';
+
+.oo-ui-actionFieldLayout {
+ &-field {
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+ }
+
+ &-input,
+ &-button {
+ display: table-cell;
+ vertical-align: middle;
+ }
+
+ &-input {
+ padding-right: 1em;
+ }
+
+ &-button {
+ width: 1%;
+ white-space: nowrap;
+ }
+
+ .theme-oo-ui-actionFieldLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/BookletLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/BookletLayout.less
new file mode 100644
index 00000000..b31cb6d7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/BookletLayout.less
@@ -0,0 +1,43 @@
+@import '../common';
+
+.oo-ui-bookletLayout {
+ &-stackLayout {
+ &.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
+ overflow-y: hidden;
+ }
+
+ > .oo-ui-panelLayout {
+ width: 100%;
+
+ .oo-ui-box-sizing(border-box);
+
+ &-scrollable {
+ overflow-y: auto;
+ }
+
+ &-padded {
+ padding: 2em;
+ }
+ }
+ }
+
+ &-outlinePanel {
+ &-editable > .oo-ui-outlineSelectWidget {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 3em;
+ overflow-y: auto;
+ }
+
+ > .oo-ui-outlineControlsWidget {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+ }
+
+ .theme-oo-ui-bookletLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/CardLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/CardLayout.less
new file mode 100644
index 00000000..4fdb20b2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/CardLayout.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-cardLayout {
+ .theme-oo-ui-cardLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/FieldLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/FieldLayout.less
new file mode 100644
index 00000000..799f9f4b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/FieldLayout.less
@@ -0,0 +1,59 @@
+@import '../common';
+
+.oo-ui-fieldLayout {
+ display: block;
+
+ &:before,
+ &:after {
+ content: " ";
+ display: table;
+ }
+
+ &:after {
+ clear: both;
+ }
+
+ &.oo-ui-fieldLayout-align-left,
+ &.oo-ui-fieldLayout-align-right {
+ > .oo-ui-fieldLayout-body {
+ > .oo-ui-labelElement-label,
+ > .oo-ui-fieldLayout-field {
+ display: block;
+ float: left;
+ }
+ }
+ }
+
+ &.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ text-align: right;
+ }
+
+ &.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
+ display: table;
+
+ > .oo-ui-labelElement-label,
+ > .oo-ui-fieldLayout-field {
+ display: table-cell;
+ vertical-align: middle;
+ }
+ }
+
+ &.oo-ui-labelElement.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ display: inline-block;
+ }
+
+ > .oo-ui-fieldLayout-help {
+ float: right;
+
+ > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+ z-index: 1;
+ }
+
+ .oo-ui-fieldLayout-help-content {
+ padding: 0.5em 0.75em;
+ line-height: 1.5em;
+ }
+ }
+
+ .theme-oo-ui-fieldLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/FieldsetLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/FieldsetLayout.less
new file mode 100644
index 00000000..726b9310
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/FieldsetLayout.less
@@ -0,0 +1,31 @@
+@import '../common';
+
+.oo-ui-fieldsetLayout {
+ position: relative;
+
+ &.oo-ui-iconElement > .oo-ui-iconElement-icon {
+ display: block;
+ position: absolute;
+ background-position: center center;
+ background-repeat: no-repeat;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-labelElement-label {
+ display: inline-block;
+ }
+
+ > .oo-ui-fieldsetLayout-help {
+ float: right;
+
+ > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+ z-index: 1;
+ }
+
+ .oo-ui-fieldsetLayout-help-content {
+ padding: 0.5em 0.75em;
+ line-height: 1.5em;
+ }
+ }
+
+ .theme-oo-ui-fieldsetLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/FormLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/FormLayout.less
new file mode 100644
index 00000000..7354e4bd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/FormLayout.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-formLayout {
+ .theme-oo-ui-formLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/IndexLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/IndexLayout.less
new file mode 100644
index 00000000..63f3f146
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/IndexLayout.less
@@ -0,0 +1,13 @@
+@import '../common';
+
+.oo-ui-indexLayout {
+ > .oo-ui-menuLayout-menu {
+ height: 3em;
+ }
+
+ > .oo-ui-menuLayout-content {
+ top: 3em;
+ }
+
+ .theme-oo-ui-indexLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/MenuLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/MenuLayout.less
new file mode 100644
index 00000000..c5080e2b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/MenuLayout.less
@@ -0,0 +1,108 @@
+@import '../common';
+
+.oo-ui-menuLayout {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ &-menu,
+ &-content {
+ position: absolute;
+ .oo-ui-transition(all ease-in-out 200ms);
+ }
+
+ // These are overridden with 'auto' or '0' later
+ &-menu {
+ height: 18em;
+ width: 18em;
+ }
+
+ // These are overridden with 'auto' or '0' later
+ &-content {
+ top: 18em;
+ left: 18em;
+ right: 18em;
+ bottom: 18em;
+ }
+
+ &.oo-ui-menuLayout-hideMenu {
+ .oo-ui-menuLayout-menu {
+ width: 0 !important;
+ height: 0 !important;
+ overflow: hidden;
+ }
+
+ .oo-ui-menuLayout-content {
+ top: 0 !important;
+ left: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ }
+ }
+
+ &.oo-ui-menuLayout-showMenu {
+ &.oo-ui-menuLayout-top {
+ .oo-ui-menuLayout-menu {
+ width: auto !important;
+ left: 0;
+ top: 0;
+ right: 0;
+ }
+
+ .oo-ui-menuLayout-content {
+ right: 0 !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ }
+ }
+
+ &.oo-ui-menuLayout-after {
+ .oo-ui-menuLayout-menu {
+ height: auto !important;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ .oo-ui-menuLayout-content {
+ bottom: 0 !important;
+ left: 0 !important;
+ top: 0 !important;
+ }
+ }
+
+ &.oo-ui-menuLayout-bottom {
+ .oo-ui-menuLayout-menu {
+ width: auto !important;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ .oo-ui-menuLayout-content {
+ left: 0 !important;
+ top: 0 !important;
+ right: 0 !important;
+ }
+ }
+
+ &.oo-ui-menuLayout-before {
+ .oo-ui-menuLayout-menu {
+ height: auto !important;
+ bottom: 0;
+ left: 0;
+ top: 0;
+ }
+
+ .oo-ui-menuLayout-content {
+ top: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ }
+ }
+ }
+
+ .theme-oo-ui-menuLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/PageLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/PageLayout.less
new file mode 100644
index 00000000..91aa4571
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/PageLayout.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-pageLayout {
+ .theme-oo-ui-pageLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/PanelLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/PanelLayout.less
new file mode 100644
index 00000000..ed488ed7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/PanelLayout.less
@@ -0,0 +1,19 @@
+@import '../common';
+
+.oo-ui-panelLayout {
+ position: relative;
+
+ &-scrollable {
+ overflow-y: auto;
+ }
+
+ &-expanded {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ .theme-oo-ui-panelLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/layouts/StackLayout.less b/vendor/oojs/oojs-ui/src/styles/layouts/StackLayout.less
new file mode 100644
index 00000000..6dd4fa79
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/layouts/StackLayout.less
@@ -0,0 +1,10 @@
+@import '../common';
+
+.oo-ui-stackLayout {
+ &-continuous > .oo-ui-panelLayout {
+ display: block;
+ position: relative;
+ }
+
+ .theme-oo-ui-stackLayout();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/theme.less b/vendor/oojs/oojs-ui/src/styles/theme.less
new file mode 100644
index 00000000..2cd4c0e3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/theme.less
@@ -0,0 +1,91 @@
+//
+// Blank theme mixins.
+//
+// Base styles invoke these mixins at the end of their definitions. Override these mixins to add
+// additional rules to the base styles.
+//
+
+.theme-oo-ui-layout () {}
+.theme-oo-ui-element () {}
+.theme-oo-ui-widget () {}
+.theme-oo-ui-window () {}
+.theme-oo-ui-dialog () {}
+.theme-oo-ui-windowManager () {}
+
+.theme-oo-ui-buttonElement () {}
+.theme-oo-ui-clippableElement () {}
+.theme-oo-ui-draggableElement () {}
+.theme-oo-ui-flaggedElement () {}
+.theme-oo-ui-groupElement () {}
+.theme-oo-ui-draggableGroupElement () {}
+.theme-oo-ui-iconElement () {}
+.theme-oo-ui-indicatorElement () {}
+.theme-oo-ui-labelElement () {}
+.theme-oo-ui-lookupElement () {}
+.theme-oo-ui-popupElement () {}
+.theme-oo-ui-tabIndexedElement () {}
+.theme-oo-ui-titledElement () {}
+
+.theme-oo-ui-tool () {}
+.theme-oo-ui-toolbar () {}
+.theme-oo-ui-toolGroup () {}
+.theme-oo-ui-messageDialog () {}
+.theme-oo-ui-processDialog () {}
+
+.theme-oo-ui-bookletLayout () {}
+.theme-oo-ui-indexLayout () {}
+.theme-oo-ui-fieldLayout () {}
+.theme-oo-ui-actionFieldLayout () {}
+.theme-oo-ui-fieldsetLayout () {}
+.theme-oo-ui-formLayout () {}
+.theme-oo-ui-menuLayout () {}
+.theme-oo-ui-panelLayout () {}
+.theme-oo-ui-cardLayout () {}
+.theme-oo-ui-pageLayout () {}
+.theme-oo-ui-stackLayout () {}
+
+.theme-oo-ui-barToolGroup () {}
+.theme-oo-ui-popupToolGroup () {}
+.theme-oo-ui-listToolGroup () {}
+.theme-oo-ui-menuToolGroup () {}
+
+.theme-oo-ui-popupTool () {}
+.theme-oo-ui-toolGroupTool () {}
+
+.theme-oo-ui-outlineControlsWidget () {}
+.theme-oo-ui-toggleWidget () {}
+
+.theme-oo-ui-buttonGroupWidget () {}
+.theme-oo-ui-buttonWidget () {}
+.theme-oo-ui-actionWidget () {}
+.theme-oo-ui-popupButtonWidget () {}
+.theme-oo-ui-toggleButtonWidget () {}
+.theme-oo-ui-iconWidget () {}
+.theme-oo-ui-indicatorWidget () {}
+.theme-oo-ui-dropdownWidget () {}
+.theme-oo-ui-inputWidget () {}
+.theme-oo-ui-buttonInputWidget () {}
+.theme-oo-ui-checkboxInputWidget () {}
+.theme-oo-ui-dropdownInputWidget () {}
+.theme-oo-ui-radioInputWidget () {}
+.theme-oo-ui-textInputWidget () {}
+.theme-oo-ui-comboBoxWidget () {}
+.theme-oo-ui-labelWidget () {}
+.theme-oo-ui-optionWidget () {}
+.theme-oo-ui-decoratedOptionWidget () {}
+.theme-oo-ui-buttonOptionWidget () {}
+.theme-oo-ui-radioOptionWidget () {}
+.theme-oo-ui-menuOptionWidget () {}
+.theme-oo-ui-menuSectionOptionWidget () {}
+.theme-oo-ui-outlineOptionWidget () {}
+.theme-oo-ui-tabOptionWidget () {}
+.theme-oo-ui-popupWidget () {}
+.theme-oo-ui-searchWidget () {}
+.theme-oo-ui-selectWidget () {}
+.theme-oo-ui-buttonSelectWidget () {}
+.theme-oo-ui-radioSelectWidget () {}
+.theme-oo-ui-menuSelectWidget () {}
+.theme-oo-ui-textInputMenuSelectWidget () {}
+.theme-oo-ui-outlineSelectWidget () {}
+.theme-oo-ui-tabSelectWidget () {}
+.theme-oo-ui-toggleSwitchWidget () {}
diff --git a/vendor/oojs/oojs-ui/src/styles/toolgroups/BarToolGroup.less b/vendor/oojs/oojs-ui/src/styles/toolgroups/BarToolGroup.less
new file mode 100644
index 00000000..44c3bb8c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/toolgroups/BarToolGroup.less
@@ -0,0 +1,51 @@
+@import '../common';
+
+.oo-ui-barToolGroup {
+ > .oo-ui-iconElement-icon,
+ > .oo-ui-labelElement-label {
+ display: none;
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ > .oo-ui-tool-link {
+ cursor: pointer;
+ }
+ }
+
+ > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ display: inline-block;
+ position: relative;
+ vertical-align: top;
+
+ > .oo-ui-tool-link {
+ display: block;
+
+ .oo-ui-tool-accel {
+ display: none;
+ }
+ }
+
+ &.oo-ui-iconElement > .oo-ui-tool-link {
+ .oo-ui-iconElement-icon {
+ display: inline-block;
+ vertical-align: top;
+ }
+
+ .oo-ui-tool-title {
+ display: none;
+ }
+ }
+
+ &.oo-ui-iconElement.oo-ui-tool-with-label > .oo-ui-tool-link {
+ .oo-ui-tool-title {
+ display: inline;
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-tool-link {
+ cursor: default;
+ }
+ }
+
+ .theme-oo-ui-barToolGroup();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/toolgroups/ListToolGroup.less b/vendor/oojs/oojs-ui/src/styles/toolgroups/ListToolGroup.less
new file mode 100644
index 00000000..f3c745a3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/toolgroups/ListToolGroup.less
@@ -0,0 +1,21 @@
+@import '../common';
+
+.oo-ui-listToolGroup {
+ .oo-ui-tool {
+ display: block;
+
+ .oo-ui-box-sizing(border-box);
+
+ &-link {
+ cursor: pointer;
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link {
+ cursor: default;
+ }
+ }
+ }
+
+ .theme-oo-ui-listToolGroup();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/toolgroups/MenuToolGroup.less b/vendor/oojs/oojs-ui/src/styles/toolgroups/MenuToolGroup.less
new file mode 100644
index 00000000..cfbbe0b6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/toolgroups/MenuToolGroup.less
@@ -0,0 +1,19 @@
+@import '../common';
+
+.oo-ui-menuToolGroup {
+ .oo-ui-tool {
+ display: block;
+
+ &-link {
+ cursor: pointer;
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link {
+ cursor: default;
+ }
+ }
+ }
+
+ .theme-oo-ui-menuToolGroup();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/toolgroups/PopupToolGroup.less b/vendor/oojs/oojs-ui/src/styles/toolgroups/PopupToolGroup.less
new file mode 100644
index 00000000..1b54bea5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/toolgroups/PopupToolGroup.less
@@ -0,0 +1,73 @@
+@import '../common';
+
+.oo-ui-popupToolGroup {
+ position: relative;
+
+ &-handle {
+ display: block;
+ cursor: pointer;
+
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ position: absolute;
+ background-position: center center;
+ background-repeat: no-repeat;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-popupToolGroup-handle {
+ cursor: default;
+ }
+ }
+
+ .oo-ui-toolGroup-tools {
+ display: none;
+ position: absolute;
+ z-index: 4;
+
+ .oo-ui-iconElement-icon {
+ background-repeat: no-repeat;
+ background-position: center center;
+ }
+ }
+
+ &-active.oo-ui-widget-enabled {
+ > .oo-ui-toolGroup-tools {
+ display: block;
+ }
+ }
+
+ &-left > .oo-ui-toolGroup-tools {
+ left: 0;
+ }
+
+ &-right > .oo-ui-toolGroup-tools {
+ right: 0;
+ }
+
+ .oo-ui-tool-link {
+ display: table;
+ width: 100%;
+ vertical-align: middle;
+ white-space: nowrap;
+
+ .oo-ui-iconElement-icon,
+ .oo-ui-tool-accel,
+ .oo-ui-tool-title {
+ display: table-cell;
+ vertical-align: middle;
+ }
+
+ .oo-ui-tool-accel {
+ text-align: right;
+ }
+
+ .oo-ui-tool-accel:not(:empty) {
+ // Push away from tool's title
+ padding-left: 3em;
+ }
+ }
+
+ .theme-oo-ui-popupToolGroup();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/tools/PopupTool.less b/vendor/oojs/oojs-ui/src/styles/tools/PopupTool.less
new file mode 100644
index 00000000..df90302b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/tools/PopupTool.less
@@ -0,0 +1,12 @@
+@import '../common';
+
+.oo-ui-popupTool {
+ .oo-ui-popupWidget {
+ &-popup,
+ &-anchor {
+ z-index: 4;
+ }
+ }
+
+ .theme-oo-ui-popupTool();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/tools/ToolGroupTool.less b/vendor/oojs/oojs-ui/src/styles/tools/ToolGroupTool.less
new file mode 100644
index 00000000..753d4e8a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/tools/ToolGroupTool.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-toolGroupTool {
+ .theme-oo-ui-toolGroupTool();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ActionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ActionWidget.less
new file mode 100644
index 00000000..39ed296f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ActionWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-actionWidget {
+ .theme-oo-ui-actionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ButtonGroupWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonGroupWidget.less
new file mode 100644
index 00000000..eee220d4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonGroupWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-buttonGroupWidget {
+ .theme-oo-ui-buttonGroupWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ButtonInputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonInputWidget.less
new file mode 100644
index 00000000..5de860eb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonInputWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-buttonInputWidget {
+ display: inline-block;
+ vertical-align: middle;
+
+ .theme-oo-ui-buttonInputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ButtonOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonOptionWidget.less
new file mode 100644
index 00000000..fe214090
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonOptionWidget.less
@@ -0,0 +1,18 @@
+@import '../common';
+
+.oo-ui-buttonOptionWidget {
+ display: inline-block;
+
+ .oo-ui-buttonElement-button {
+ position: relative;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon,
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ position: static;
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ .theme-oo-ui-buttonOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ButtonSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonSelectWidget.less
new file mode 100644
index 00000000..d0430db5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonSelectWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-buttonSelectWidget {
+ display: inline-block;
+ white-space: nowrap;
+
+ .theme-oo-ui-buttonSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ButtonWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonWidget.less
new file mode 100644
index 00000000..c33149f2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ButtonWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-buttonWidget {
+ display: inline-block;
+ vertical-align: middle;
+
+ .theme-oo-ui-buttonWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/CheckboxInputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/CheckboxInputWidget.less
new file mode 100644
index 00000000..608c9a6b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/CheckboxInputWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-checkboxInputWidget {
+ .theme-oo-ui-checkboxInputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ComboBoxWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ComboBoxWidget.less
new file mode 100644
index 00000000..1b393c5b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ComboBoxWidget.less
@@ -0,0 +1,13 @@
+@import '../common';
+
+.oo-ui-comboBoxWidget {
+ display: inline-block;
+ position: relative;
+
+ > .oo-ui-menuSelectWidget {
+ z-index: 1;
+ width: 100%;
+ }
+
+ .theme-oo-ui-comboBoxWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/DecoratedOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/DecoratedOptionWidget.less
new file mode 100644
index 00000000..a31d0670
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/DecoratedOptionWidget.less
@@ -0,0 +1,12 @@
+@import '../common';
+
+.oo-ui-decoratedOptionWidget {
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ position: absolute;
+ background-repeat: no-repeat;
+ background-position: center center;
+ }
+
+ .theme-oo-ui-decoratedOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/DropdownInputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/DropdownInputWidget.less
new file mode 100644
index 00000000..8b7bb7cf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/DropdownInputWidget.less
@@ -0,0 +1,17 @@
+@import '../common';
+
+.oo-ui-dropdownInputWidget {
+ position: relative;
+ // Necessary for proper alignment when used with display: inline-block
+ vertical-align: middle;
+ .oo-ui-box-sizing(border-box);
+
+ select {
+ display: inline-block;
+ width: 100%;
+ resize: none;
+ .oo-ui-box-sizing(border-box);
+ }
+
+ .theme-oo-ui-dropdownInputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/DropdownWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/DropdownWidget.less
new file mode 100644
index 00000000..d934cd4b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/DropdownWidget.less
@@ -0,0 +1,33 @@
+@import '../common';
+
+.oo-ui-dropdownWidget {
+ display: inline-block;
+ position: relative;
+
+ &-handle {
+ width: 100%;
+ display: inline-block;
+ cursor: pointer;
+
+ .oo-ui-unselectable();
+ .oo-ui-box-sizing(border-box);
+
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ position: absolute;
+ background-position: center center;
+ background-repeat: no-repeat;
+ }
+ }
+
+ > .oo-ui-menuSelectWidget {
+ z-index: 1;
+ width: 100%;
+ }
+
+ &.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
+ cursor: default;
+ }
+
+ .theme-oo-ui-dropdownWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/IconWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/IconWidget.less
new file mode 100644
index 00000000..03f6ab79
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/IconWidget.less
@@ -0,0 +1,10 @@
+@import '../common';
+
+.oo-ui-iconWidget {
+ display: inline-block;
+ vertical-align: middle;
+ background-position: center center;
+ background-repeat: no-repeat;
+
+ .theme-oo-ui-iconWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/IndicatorWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/IndicatorWidget.less
new file mode 100644
index 00000000..c1b00458
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/IndicatorWidget.less
@@ -0,0 +1,10 @@
+@import '../common';
+
+.oo-ui-indicatorWidget {
+ display: inline-block;
+ vertical-align: middle;
+ background-position: center center;
+ background-repeat: no-repeat;
+
+ .theme-oo-ui-indicatorWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/InputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/InputWidget.less
new file mode 100644
index 00000000..93628830
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/InputWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-inputWidget {
+ .theme-oo-ui-inputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/LabelWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/LabelWidget.less
new file mode 100644
index 00000000..cddd1a0a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/LabelWidget.less
@@ -0,0 +1,7 @@
+@import '../common';
+
+.oo-ui-labelWidget {
+ display: inline-block;
+
+ .theme-oo-ui-labelWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/MenuOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/MenuOptionWidget.less
new file mode 100644
index 00000000..f7f9f7a4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/MenuOptionWidget.less
@@ -0,0 +1,21 @@
+@import '../common';
+
+.oo-ui-menuOptionWidget {
+ position: relative;
+
+ .oo-ui-iconElement-icon {
+ display: none;
+ }
+
+ &.oo-ui-optionWidget {
+ &-selected {
+ background-color: transparent;
+
+ .oo-ui-iconElement-icon {
+ display: block;
+ }
+ }
+ }
+
+ .theme-oo-ui-menuOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/MenuSectionOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/MenuSectionOptionWidget.less
new file mode 100644
index 00000000..f670f7f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/MenuSectionOptionWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-menuSectionOptionWidget {
+ cursor: default;
+
+ .theme-oo-ui-menuSectionOptionWidget();
+}
+
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/MenuSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/MenuSelectWidget.less
new file mode 100644
index 00000000..0585469f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/MenuSelectWidget.less
@@ -0,0 +1,15 @@
+@import '../common';
+
+.oo-ui-menuSelectWidget {
+ position: absolute;
+
+ input {
+ position: absolute;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ opacity: 0;
+ }
+
+ .theme-oo-ui-menuSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/OptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/OptionWidget.less
new file mode 100644
index 00000000..8543fc74
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/OptionWidget.less
@@ -0,0 +1,20 @@
+@import '../common';
+
+.oo-ui-optionWidget {
+ position: relative;
+ display: block;
+ cursor: pointer;
+
+ &.oo-ui-widget-disabled {
+ cursor: default;
+ }
+
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ display: block;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+
+ .theme-oo-ui-optionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/OutlineControlsWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineControlsWidget.less
new file mode 100644
index 00000000..97d4e1d7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineControlsWidget.less
@@ -0,0 +1,32 @@
+@import '../common';
+
+.oo-ui-outlineControlsWidget {
+ &-items,
+ &-movers {
+ float: left;
+ .oo-ui-box-sizing(border-box);
+ }
+
+ > .oo-ui-iconElement-icon {
+ float: left;
+ background-position: right center;
+ background-repeat: no-repeat;
+ }
+
+ &-items {
+ float: left;
+
+ .oo-ui-buttonWidget {
+ float: left;
+ }
+ }
+ &-movers {
+ float: right;
+
+ .oo-ui-buttonWidget {
+ float: right;
+ }
+ }
+
+ .theme-oo-ui-outlineControlsWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/OutlineOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineOptionWidget.less
new file mode 100644
index 00000000..6558c652
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineOptionWidget.less
@@ -0,0 +1,9 @@
+@import '../common';
+
+.oo-ui-outlineOptionWidget {
+ position: relative;
+ cursor: pointer;
+ .oo-ui-unselectable();
+
+ .theme-oo-ui-outlineOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/OutlineSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineSelectWidget.less
new file mode 100644
index 00000000..65fc621b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/OutlineSelectWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-outlineSelectWidget {
+ .theme-oo-ui-outlineSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/PopupButtonWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/PopupButtonWidget.less
new file mode 100644
index 00000000..805239ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/PopupButtonWidget.less
@@ -0,0 +1,12 @@
+@import '../common';
+
+.oo-ui-popupButtonWidget {
+ position: relative;
+
+ .oo-ui-popupWidget {
+ position: absolute;
+ cursor: auto;
+ }
+
+ .theme-oo-ui-popupButtonWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/PopupWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/PopupWidget.less
new file mode 100644
index 00000000..d8fdd40c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/PopupWidget.less
@@ -0,0 +1,49 @@
+@import '../common';
+
+.oo-ui-popupWidget {
+ position: absolute;
+ /* @noflip */
+ left: 0;
+
+ &-popup {
+ position: relative;
+ overflow: hidden;
+ z-index: 1;
+ }
+
+ &-anchor {
+ display: none;
+ z-index: 1;
+ }
+
+ &-anchored {
+ .oo-ui-popupWidget-anchor {
+ display: block;
+ position: absolute;
+ top: 0;
+ /* @noflip */
+ left: 0;
+ background-repeat: no-repeat;
+ }
+ }
+
+ &-head {
+ .oo-ui-unselectable();
+
+ .oo-ui-buttonWidget {
+ float: right;
+ }
+
+ .oo-ui-labelElement-label {
+ float: left;
+ cursor: default;
+ }
+ }
+
+ &-body {
+ clear: both;
+ overflow: hidden;
+ }
+
+ .theme-oo-ui-popupWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ProgressBarWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ProgressBarWidget.less
new file mode 100644
index 00000000..82993fc1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ProgressBarWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-progressBarWidget {
+ .theme-oo-ui-progressBarWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/RadioInputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/RadioInputWidget.less
new file mode 100644
index 00000000..7b75f871
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/RadioInputWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-radioInputWidget {
+ .theme-oo-ui-radioInputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/RadioOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/RadioOptionWidget.less
new file mode 100644
index 00000000..9a83997a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/RadioOptionWidget.less
@@ -0,0 +1,13 @@
+@import '../common';
+
+.oo-ui-radioOptionWidget {
+ cursor: default;
+
+ .oo-ui-radioInputWidget,
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ .theme-oo-ui-radioOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/RadioSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/RadioSelectWidget.less
new file mode 100644
index 00000000..f6900cb6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/RadioSelectWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-radioSelectWidget {
+ .theme-oo-ui-radioSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/SearchWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/SearchWidget.less
new file mode 100644
index 00000000..920beacf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/SearchWidget.less
@@ -0,0 +1,25 @@
+@import '../common';
+
+.oo-ui-searchWidget {
+ &-query {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+
+ .oo-ui-textInputWidget {
+ width: 100%;
+ }
+ }
+
+ &-results {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+
+ .theme-oo-ui-searchWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/SelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/SelectWidget.less
new file mode 100644
index 00000000..2ad0b943
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/SelectWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-selectWidget {
+ .theme-oo-ui-selectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/TabOptionWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/TabOptionWidget.less
new file mode 100644
index 00000000..0b83154c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/TabOptionWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-tabOptionWidget {
+ display: inline-block;
+ vertical-align: bottom;
+
+ .theme-oo-ui-tabOptionWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/TabSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/TabSelectWidget.less
new file mode 100644
index 00000000..b3a57c98
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/TabSelectWidget.less
@@ -0,0 +1,9 @@
+@import '../common';
+
+.oo-ui-tabSelectWidget {
+ text-align: left;
+ white-space: nowrap;
+ overflow: hidden;
+
+ .theme-oo-ui-tabSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/TextInputMenuSelectWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/TextInputMenuSelectWidget.less
new file mode 100644
index 00000000..6edc474b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/TextInputMenuSelectWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-textInputMenuSelectWidget {
+ .theme-oo-ui-textInputMenuSelectWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/TextInputWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/TextInputWidget.less
new file mode 100644
index 00000000..dd0cdf46
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/TextInputWidget.less
@@ -0,0 +1,71 @@
+@import '../common';
+
+.oo-ui-textInputWidget {
+ position: relative;
+ // Necessary for proper alignment when used with display: inline-block
+ vertical-align: middle;
+ .oo-ui-box-sizing(border-box);
+
+ input,
+ textarea {
+ display: inline-block;
+ width: 100%;
+ resize: none;
+ .oo-ui-box-sizing(border-box);
+ }
+
+ > .oo-ui-iconElement-icon,
+ > .oo-ui-indicatorElement-indicator,
+ > .oo-ui-labelElement-label {
+ display: none;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-iconElement-icon,
+ &.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
+ display: block;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ background-repeat: no-repeat;
+
+ .oo-ui-unselectable();
+ }
+
+ &.oo-ui-widget-enabled {
+ > .oo-ui-iconElement-icon,
+ > .oo-ui-indicatorElement-indicator {
+ cursor: pointer;
+ }
+ }
+
+ &.oo-ui-labelElement > .oo-ui-labelElement-label {
+ display: block;
+ }
+
+ > .oo-ui-iconElement-icon {
+ left: 0;
+ }
+
+ > .oo-ui-indicatorElement-indicator {
+ right: 0;
+ }
+
+ > .oo-ui-labelElement-label {
+ position: absolute;
+ top: 0;
+ }
+
+ &-labelPosition-after {
+ > .oo-ui-labelElement-label {
+ right: 0
+ }
+ }
+
+ &-labelPosition-before {
+ > .oo-ui-labelElement-label {
+ left: 0
+ }
+ }
+
+ .theme-oo-ui-textInputWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ToggleButtonWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleButtonWidget.less
new file mode 100644
index 00000000..5441e4e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleButtonWidget.less
@@ -0,0 +1,8 @@
+@import '../common';
+
+.oo-ui-toggleButtonWidget {
+ display: inline-block;
+ vertical-align: middle;
+
+ .theme-oo-ui-toggleButtonWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ToggleSwitchWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleSwitchWidget.less
new file mode 100644
index 00000000..f030cf66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleSwitchWidget.less
@@ -0,0 +1,41 @@
+@import '../common';
+
+.oo-ui-toggleSwitchWidget {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ overflow: hidden;
+ cursor: pointer;
+
+ .oo-ui-box-sizing(border-box);
+ .oo-ui-transform(translateZ(0px));
+
+ &.oo-ui-widget-disabled {
+ cursor: default;
+ }
+
+ &-grip {
+ position: absolute;
+ display: block;
+
+ .oo-ui-box-sizing(border-box);
+ }
+
+ .oo-ui-toggleSwitchWidget-glow {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ left: 0;
+
+ .oo-ui-unselectable();
+ }
+
+ .oo-ui-toggleWidget-off & {
+ &-glow {
+ display: none;
+ }
+ }
+
+ .theme-oo-ui-toggleSwitchWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/styles/widgets/ToggleWidget.less b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleWidget.less
new file mode 100644
index 00000000..f51e45b5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/styles/widgets/ToggleWidget.less
@@ -0,0 +1,5 @@
+@import '../common';
+
+.oo-ui-toggleWidget {
+ .theme-oo-ui-toggleWidget();
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/ApexTheme.js b/vendor/oojs/oojs-ui/src/themes/apex/ApexTheme.js
new file mode 100644
index 00000000..157dfb64
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/ApexTheme.js
@@ -0,0 +1,18 @@
+/**
+ * @class
+ * @extends OO.ui.Theme
+ *
+ * @constructor
+ */
+OO.ui.ApexTheme = function OoUiApexTheme() {
+ // Parent constructor
+ OO.ui.ApexTheme.super.call( this );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ApexTheme, OO.ui.Theme );
+
+/* Instantiation */
+
+OO.ui.theme = new OO.ui.ApexTheme();
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/common.less b/vendor/oojs/oojs-ui/src/themes/apex/common.less
new file mode 100644
index 00000000..74dd3a3b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/common.less
@@ -0,0 +1,27 @@
+// Base variables and mixins
+@import '../../styles/common';
+
+// Theme variables
+
+@progressive: #087ecc;
+@constructive: #76ab36;
+@destructive: #d45353;
+
+@progressive-gradient-start: #eaf4fa;
+@progressive-gradient-end: #b0d9ee;
+@progressive-border: #a6cee1;
+@progressive-border-selected: #9dc2d4;
+
+@constructive-gradient-start: #f0fbe1;
+@constructive-gradient-end: #c3e59a;
+@constructive-border: #b8d892;
+@constructive-border-selected: #adcb89;
+
+@oo-ui-default-image-path: 'themes/apex/images';
+
+@icon-size: unit(24 / 16 / 0.8, em);
+@indicator-size: unit(12 / 16 / 0.8, em);
+
+// Theme mixins
+
+// (add mixins here)
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/core.less b/vendor/oojs/oojs-ui/src/themes/apex/core.less
new file mode 100644
index 00000000..1c83a946
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/core.less
@@ -0,0 +1,12 @@
+// Base and theme variables and mixins
+@import 'common';
+
+// Theme rules
+@import 'elements';
+@import 'layouts';
+@import 'tools';
+@import 'widgets';
+@import 'windows';
+
+// Base rules
+@import '../../styles/core';
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/elements.less b/vendor/oojs/oojs-ui/src/themes/apex/elements.less
new file mode 100644
index 00000000..bfd11228
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/elements.less
@@ -0,0 +1,247 @@
+@import 'common';
+
+.theme-oo-ui-element () {}
+
+.theme-oo-ui-buttonElement () {
+ > .oo-ui-buttonElement-button {
+ color: #333;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button {
+ > .oo-ui-iconElement-icon {
+ margin-left: 0;
+ }
+ }
+
+ &.oo-ui-indicatorElement > .oo-ui-buttonElement-button {
+ > .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ height: @indicator-size;
+ margin: @indicator-size / 2;
+ }
+ }
+
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ margin-left: @indicator-size / 2;
+ }
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ width: @icon-size;
+ height: @icon-size;
+ }
+
+ &-frameless {
+ > .oo-ui-buttonElement-button {
+ > .oo-ui-iconElement-icon {
+ /* Don't animate opacities for now, causes wiggling in Chrome (bug 63020) */
+ /*.oo-ui-transition(opacity 200ms);*/
+ }
+
+ &:hover,
+ &:focus {
+ outline: none;
+
+ > .oo-ui-iconElement-icon {
+ opacity: 1;
+ }
+ > .oo-ui-labelElement-label {
+ color: #000;
+ }
+ }
+
+ > .oo-ui-labelElement-label {
+ color: #333;
+ }
+ }
+
+ &.oo-ui-labelElement {
+ > .oo-ui-buttonElement-button {
+ > .oo-ui-labelElement-label {
+ margin-left: 0.25em;
+ }
+ }
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @progressive;
+ }
+
+ &-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @constructive;
+ }
+
+ &-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @destructive;
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+ > .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ > .oo-ui-labelElement-label {
+ color: #ccc;
+ }
+ }
+ }
+
+ &-framed {
+ > .oo-ui-buttonElement-button {
+ margin: 0.1em 0;
+ padding: 0.2em 0.8em;
+ border-radius: 0.3em;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
+ border: 1px #c9c9c9 solid;
+ .oo-ui-transition(border-color 100ms ease-in-out);
+ .oo-ui-vertical-gradient(#fff, #ddd);
+
+ &:hover,
+ &:focus {
+ border-color: #aaa;
+ outline: none;
+ }
+ }
+
+ // Support <input/> from ButtonInputWidget
+ > input.oo-ui-buttonElement-button,
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ line-height: @icon-size;
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
+ color: black;
+ border-color: #c9c9c9;
+ .oo-ui-vertical-gradient(#ddd, #fff);
+ }
+
+ &.oo-ui-iconElement {
+ > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ margin-left: -0.5em;
+ margin-right: -0.5em;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ margin-right: 0.3em;
+ }
+ }
+
+ &.oo-ui-indicatorElement {
+ > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ /* -0.5 - 0.475 */
+ margin-left: -0.005em;
+ margin-right: -0.005em;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+ &.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ margin-left: @indicator-size / 2;
+ margin-right: -0.275em;
+ }
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ > .oo-ui-buttonElement-button {
+ border: 1px solid @progressive-border;
+ .oo-ui-vertical-gradient(@progressive-gradient-start, @progressive-gradient-end);
+
+ &:hover,
+ &:focus {
+ border-color: @progressive-border-selected;
+ }
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ border: 1px solid @progressive-border;
+ .oo-ui-vertical-gradient(@progressive-gradient-end, @progressive-gradient-start);
+ }
+ }
+
+ &-constructive {
+ > .oo-ui-buttonElement-button {
+ border: 1px solid @constructive-border;
+ .oo-ui-vertical-gradient(@constructive-gradient-start, @constructive-gradient-end);
+
+ &:hover,
+ &:focus {
+ border-color: @constructive-border-selected;
+ }
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ border: 1px solid @constructive-border;
+ .oo-ui-vertical-gradient(@constructive-gradient-end, @constructive-gradient-start);
+ }
+ }
+
+ &-destructive > .oo-ui-buttonElement-button {
+ color: @destructive;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ opacity: 0.5;
+ // Opacity causes 1px measurement errors in Chrome, so force GPU rendering
+ .oo-ui-force-webkit-gpu();
+ box-shadow: none;
+ color: #333;
+ background: #eee;
+ border-color: #ccc;
+
+ &:hover,
+ &:focus {
+ border-color: #ccc;
+ box-shadow: none;
+ }
+ }
+ }
+ }
+}
+
+.theme-oo-ui-clippableElement () {}
+
+.theme-oo-ui-flaggedElement () {}
+
+.theme-oo-ui-draggableElement () {}
+
+.theme-oo-ui-groupElement () {}
+
+.theme-oo-ui-draggableGroupElement () {}
+
+.theme-oo-ui-iconElement () {
+ .oo-ui-iconElement-icon,
+ &.oo-ui-iconElement-icon {
+ opacity: 0.8;
+ background-size: contain;
+ background-position: center center;
+ }
+}
+
+.theme-oo-ui-indicatorElement () {
+ .oo-ui-indicatorElement-indicator,
+ &.oo-ui-indicatorElement-indicator {
+ opacity: 0.8;
+ background-size: contain;
+ background-position: center center;
+ }
+}
+
+.theme-oo-ui-labelElement () {}
+
+.theme-oo-ui-lookupElement () {}
+
+.theme-oo-ui-popupElement () {}
+
+.theme-oo-ui-tabIndexedElement () {}
+
+.theme-oo-ui-titledElement () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-advanced.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-advanced.json
new file mode 100644
index 00000000..8fdc5051
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-advanced.json
@@ -0,0 +1,75 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "alignCentre": { "file": "images/icons/align-center.svg" },
+ "alignLeft": { "file": "images/icons/align-float-left.svg" },
+ "alignRight": { "file": "images/icons/align-float-right.svg" },
+ "find": { "file": {
+ "ltr": "images/icons/find-ltr.svg",
+ "rtl": "images/icons/find-rtl.svg"
+ } },
+ "insert": { "file": "images/icons/insert.svg" },
+ "layout": { "file": {
+ "ltr": "images/icons/layout-ltr.svg",
+ "rtl": "images/icons/layout-rtl.svg"
+ } },
+ "newline": { "file": {
+ "ltr": "images/icons/newline-ltr.svg",
+ "rtl": "images/icons/newline-rtl.svg"
+ } },
+ "redirect": { "file": {
+ "ltr": "images/icons/redirect-ltr.svg",
+ "rtl": "images/icons/redirect-rtl.svg"
+ } },
+ "noWikiText": { "file": {
+ "ltr": "images/icons/noWikiText-ltr.svg",
+ "rtl": "images/icons/noWikiText-rtl.svg"
+ } },
+ "outline": { "file": {
+ "ltr": "images/icons/outline-ltr.svg",
+ "rtl": "images/icons/outline-rtl.svg"
+ } },
+ "puzzle": { "file": {
+ "ltr": "images/icons/puzzle-ltr.svg",
+ "rtl": "images/icons/puzzle-rtl.svg"
+ } },
+ "quotes": { "file": {
+ "ltr": "images/icons/quotes-ltr.svg",
+ "rtl": "images/icons/quotes-rtl.svg"
+ } },
+ "quotesAdd": { "file": {
+ "ltr": "images/icons/quotesAdd-ltr.svg",
+ "rtl": "images/icons/quotesAdd-rtl.svg"
+ } },
+ "redirect": { "file": {
+ "ltr": "images/icons/redirect-ltr.svg",
+ "rtl": "images/icons/redirect-rtl.svg"
+ } },
+ "searchCaseSensitive": { "file": "images/icons/case-sensitive.svg" },
+ "searchRegularExpression": { "file": "images/icons/regular-expression.svg" },
+ "specialCharacter": { "file": "images/icons/specialCharacter.svg" },
+ "table": { "file": "images/icons/table.svg" },
+ "tableAddColumnAfter": { "file": {
+ "ltr": "images/icons/table-insert-column-rtl.svg",
+ "rtl": "images/icons/table-insert-column-ltr.svg"
+ } },
+ "tableAddColumnBefore": { "file": {
+ "ltr": "images/icons/table-insert-column-ltr.svg",
+ "rtl": "images/icons/table-insert-column-rtl.svg"
+ } },
+ "tableAddRowAfter": { "file": "images/icons/table-insert-row-after.svg" },
+ "tableAddRowBefore": { "file": "images/icons/table-insert-row-before.svg" },
+ "tableCaption": { "file": "images/icons/table-caption.svg" },
+ "tableMergeCells": { "file": "images/icons/table-merge-cells.svg" },
+ "templateAdd": { "file": {
+ "ltr": "images/icons/templateAdd-ltr.svg",
+ "rtl": "images/icons/templateAdd-rtl.svg"
+ } },
+ "translation": { "file": {
+ "ltr": "images/icons/translation-ltr.svg",
+ "rtl": "images/icons/translation-rtl.svg"
+ } },
+ "wikiText": { "file": "images/icons/wikiText.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-core.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-core.json
new file mode 100644
index 00000000..95e8358e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-core.json
@@ -0,0 +1,24 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "edit": { "file": {
+ "ltr": "images/icons/edit-ltr.svg",
+ "rtl": "images/icons/edit-rtl.svg"
+ } },
+ "editLock": { "file": {
+ "ltr": "images/icons/editLock-ltr.svg",
+ "rtl": "images/icons/editLock-rtl.svg"
+ } },
+ "editUndo": { "file": {
+ "ltr": "images/icons/editUndo-ltr.svg",
+ "rtl": "images/icons/editUndo-rtl.svg"
+ } },
+ "link": { "file": "images/icons/link.svg" },
+ "linkExternal": { "file": {
+ "ltr": "images/icons/external-link-ltr.svg",
+ "rtl": "images/icons/external-link-rtl.svg"
+ } },
+ "linkSecure": { "file": "images/icons/secure-link.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-list.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-list.json
new file mode 100644
index 00000000..490f8faf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-list.json
@@ -0,0 +1,22 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "indent": { "file": {
+ "ltr": "images/icons/indent-ltr.svg",
+ "rtl": "images/icons/indent-rtl.svg"
+ } },
+ "listBullet": { "file": {
+ "ltr": "images/icons/listBullet-ltr.svg",
+ "rtl": "images/icons/listBullet-rtl.svg"
+ } },
+ "listNumbered": { "file": {
+ "ltr": "images/icons/listNumbered-ltr.svg",
+ "rtl": "images/icons/listNumbered-rtl.svg"
+ } },
+ "outdent": { "file": {
+ "ltr": "images/icons/outdent-ltr.svg",
+ "rtl": "images/icons/outdent-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-styling.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-styling.json
new file mode 100644
index 00000000..65fbc218
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-editing-styling.json
@@ -0,0 +1,72 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "bigger": { "file": {
+ "ltr": "images/icons/bigger-ltr.svg",
+ "rtl": "images/icons/bigger-rtl.svg"
+ } },
+ "smaller": { "file": {
+ "ltr": "images/icons/smaller-ltr.svg",
+ "rtl": "images/icons/smaller-rtl.svg"
+ } },
+ "subscript": { "file": {
+ "ltr": "images/icons/subscript-ltr.svg",
+ "rtl": "images/icons/subscript-rtl.svg"
+ } },
+ "superscript": { "file": {
+ "ltr": "images/icons/superscript-ltr.svg",
+ "rtl": "images/icons/superscript-rtl.svg"
+ } },
+ "bold": { "file": {
+ "default": "images/icons/bold-a.svg",
+ "lang": {
+ "ar": "images/icons/bold-arab-ain.svg",
+ "be": "images/icons/bold-cyrl-te.svg",
+ "cs,en,he,ml,pl": "images/icons/bold-b.svg",
+ "da,de,hu,ksh,nn,no,sv": "images/icons/bold-f.svg",
+ "es,gl,pt": "images/icons/bold-n.svg",
+ "eu,fi": "images/icons/bold-l.svg",
+ "fa": "images/icons/bold-arab-dad.svg",
+ "fr,it": "images/icons/bold-g.svg",
+ "hy": "images/icons/bold-armn-to.svg",
+ "ka": "images/icons/bold-geor-man.svg",
+ "ky,ru": "images/icons/bold-cyrl-zhe.svg",
+ "nl": "images/icons/bold-v.svg",
+ "os": "images/icons/bold-cyrl-be.svg"
+ }
+ } },
+ "italic": { "file": {
+ "default": "images/icons/italic-a.svg",
+ "lang": {
+ "ar": "images/icons/italic-arab-meem.svg",
+ "cs,en,fr,he,ml,pl,pt": "images/icons/italic-i.svg",
+ "be,da,de,fi,ky,nn,no,os,sv,ru": "images/icons/italic-k.svg",
+ "es,gl,it,nl": "images/icons/italic-c.svg",
+ "eu": "images/icons/italic-e.svg",
+ "fa": "images/icons/italic-arab-keheh-jeem.svg",
+ "hu": "images/icons/italic-d.svg",
+ "hy": "images/icons/italic-armn-sha.svg",
+ "ksh": "images/icons/italic-s.svg",
+ "ka": "images/icons/italic-geor-kan.svg"
+ }
+ } },
+ "strikethrough": { "file": {
+ "default": "images/icons/strikethrough-a.svg",
+ "lang": {
+ "en": "images/icons/strikethrough-s.svg",
+ "fi": "images/icons/strikethrough-y.svg"
+ }
+ } },
+ "underline": { "file": {
+ "default": "images/icons/underline-a.svg",
+ "lang": {
+ "en": "images/icons/underline-u.svg"
+ }
+ } },
+ "textLanguage": { "file": "images/icons/language.svg" },
+ "textDirLTR": { "file": "images/icons/text-dir-lefttoright.svg" },
+ "textDirRTL": { "file": "images/icons/text-dir-righttoleft.svg" },
+ "textStyle": { "file": "images/icons/text-style.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-moderation.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-moderation.json
new file mode 100644
index 00000000..f904cc26
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-moderation.json
@@ -0,0 +1,33 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "block": { "file": "images/icons/block.svg" },
+ "blockUndo": { "file": {
+ "ltr": "images/icons/blockUndo-ltr.svg",
+ "rtl": "images/icons/blockUndo-rtl.svg"
+ } },
+ "flag": { "file": {
+ "ltr": "images/icons/flag-ltr.svg",
+ "rtl": "images/icons/flag-rtl.svg"
+ } },
+ "flagUndo": { "file": {
+ "ltr": "images/icons/flagUndo-ltr.svg",
+ "rtl": "images/icons/flagUndo-rtl.svg"
+ } },
+ "lock": { "file": "images/icons/lock.svg" },
+ "star": { "file": "images/icons/star.svg" },
+ "trash": { "file": "images/icons/trash.svg" },
+ "trashUndo": { "file": {
+ "ltr": "images/icons/trashUndo-ltr.svg",
+ "rtl": "images/icons/trashUndo-rtl.svg"
+ } },
+ "unLock": { "file": {
+ "ltr": "images/icons/unLock-ltr.svg",
+ "rtl": "images/icons/unLock-rtl.svg"
+ } },
+ "unStar": { "file": "images/icons/unStar.svg" }
+
+
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons-movement.json b/vendor/oojs/oojs-ui/src/themes/apex/icons-movement.json
new file mode 100644
index 00000000..9aa1b809
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons-movement.json
@@ -0,0 +1,27 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "arrowNext": { "file": {
+ "ltr": "images/icons/arrow-ltr.svg",
+ "rtl": "images/icons/arrow-rtl.svg"
+ } },
+ "arrowLast": { "file": {
+ "ltr": "images/icons/arrow-rtl.svg",
+ "rtl": "images/icons/arrow-ltr.svg"
+ } },
+ "caretNext": { "file": {
+ "ltr": "images/icons/caret-rtl.svg",
+ "rtl": "images/icons/caret-ltr.svg"
+ } },
+ "caretLast": { "file": {
+ "ltr": "images/icons/caret-ltr.svg",
+ "rtl": "images/icons/caret-rtl.svg"
+ } },
+ "caretDown": { "file": "images/icons/caretDown.svg" },
+ "caretUp": { "file": "images/icons/caretUp.svg" },
+ "downTriangle": { "file": "images/icons/downTriangle.svg" },
+ "move": { "file": "images/icons/move.svg" },
+ "upTriangle": { "file": "images/icons/upTriangle.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/icons.json b/vendor/oojs/oojs-ui/src/themes/apex/icons.json
new file mode 100644
index 00000000..9372363f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/icons.json
@@ -0,0 +1,50 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "circle": { "file": "images/icons/circle.svg" },
+ "add": { "file": "images/icons/add.svg" },
+ "advanced": { "file": "images/icons/advanced.svg" },
+ "cancel": { "file": "images/icons/cancel.svg" },
+ "alert": { "file": "images/icons/alert.svg" },
+ "check": { "file": "images/icons/check.svg" },
+ "close": { "file": "images/icons/close.svg" },
+ "code": { "file": "images/icons/code.svg" },
+ "collapse": { "file": "images/icons/collapse.svg" },
+ "comment": { "file": "images/icons/comment.svg" },
+ "ellipsis": { "file": "images/icons/ellipsis.svg" },
+ "expand": { "file": "images/icons/expand.svg" },
+ "help": { "file": {
+ "ltr": "images/icons/help-ltr.svg",
+ "rtl": "images/icons/help-rtl.svg",
+ "lang": {
+ "he,yi": "images/icons/help-ltr.svg"
+ }
+ } },
+ "history": { "file": "images/icons/history.svg" },
+ "info": { "file": "images/icons/info.svg" },
+ "menu": { "file": "images/icons/menu.svg" },
+ "next": { "file": {
+ "ltr": "images/icons/move-ltr.svg",
+ "rtl": "images/icons/move-rtl.svg"
+ } },
+ "picture": { "file": "images/icons/picture.svg" },
+ "previous": { "file": {
+ "ltr": "images/icons/move-rtl.svg",
+ "rtl": "images/icons/move-ltr.svg"
+ } },
+ "redo": { "file": {
+ "ltr": "images/icons/arched-arrow-ltr.svg",
+ "rtl": "images/icons/arched-arrow-rtl.svg"
+ } },
+ "remove": { "file": "images/icons/remove.svg" },
+ "search": { "file": "images/icons/search.svg" },
+ "settings": { "file": "images/icons/settings.svg" },
+ "tag": { "file": "images/icons/tag.svg" },
+ "undo": { "file": {
+ "ltr": "images/icons/arched-arrow-rtl.svg",
+ "rtl": "images/icons/arched-arrow-ltr.svg"
+ } },
+ "window": { "file": "images/icons/window.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/add.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/add.svg
new file mode 100644
index 00000000..29e5dba8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/add.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="add">
+ <path id="plus" d="M13 8h-2v3h-3v2h3v3h2v-3h3v-2h-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/advanced.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/advanced.svg
new file mode 100644
index 00000000..201b4d73
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/advanced.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="settings">
+ <path id="gear" d="M20.869 13.476c.079-.482.131-.972.131-1.476s-.052-.994-.131-1.476l-2.463-.259c-.149-.556-.367-1.082-.648-1.57l1.558-1.924c-.576-.806-1.281-1.511-2.087-2.087l-1.924 1.558c-.488-.281-1.015-.499-1.57-.648l-.259-2.463c-.482-.079-.972-.131-1.476-.131s-.994.052-1.476.131l-.259 2.463c-.555.149-1.081.367-1.57.648l-1.924-1.557c-.805.576-1.51 1.281-2.086 2.086l1.558 1.924c-.281.488-.499 1.015-.648 1.57l-2.463.259c-.08.482-.132.972-.132 1.476s.052.994.131 1.476l2.463.259c.149.556.367 1.082.648 1.57l-1.558 1.924c.576.806 1.281 1.511 2.087 2.087l1.924-1.558c.488.281 1.015.499 1.57.648l.259 2.463c.482.079.972.131 1.476.131s.994-.052 1.476-.131l.259-2.463c.556-.149 1.082-.367 1.57-.648l1.924 1.558c.806-.576 1.511-1.281 2.087-2.087l-1.558-1.924c.281-.488.499-1.015.648-1.57l2.463-.259zm-8.869 2.522c-2.209 0-3.998-1.789-3.998-3.998s1.789-3.998 3.998-3.998 3.998 1.789 3.998 3.998-1.789 3.998-3.998 3.998z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/alert.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/alert.svg
new file mode 100644
index 00000000..f0c65224
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/alert.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="alert">
+ <path id="point" d="M11 16h2v2h-2z"/>
+ <path id="stroke" d="M13.516 10h-3l.484 5h2z"/>
+ <path id="triangle" d="M12.017 5.974l7.519 13.026h-15.04l7.521-13.026m0-2.474c-.544 0-1.088.357-1.5 1.071l-7.985 13.831c-.825 1.429-.15 2.598 1.5 2.598h15.968c1.65 0 2.325-1.169 1.5-2.599l-7.983-13.829c-.413-.715-.956-1.072-1.5-1.072z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-center.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-center.svg
new file mode 100644
index 00000000..887c2f66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-center.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-center">
+ <path d="M9 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1h-6c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zM3.5 18h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 6h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-left.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-left.svg
new file mode 100644
index 00000000..ce9761e2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-left.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-float-left">
+ <path d="M4 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1h-6c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zM13.5 9h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM13.5 12h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM13.5 15h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 6h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 18h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-right.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-right.svg
new file mode 100644
index 00000000..557692ae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/align-float-right.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-float-right">
+ <path d="M20 9h-6c-.554 0-1 .446-1 1v5c0 .554.446 1 1 1h6c.554 0 1-.446 1-1v-5c0-.554-.446-1-1-1zM10.5 9h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM10.5 12h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM10.5 15h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM20.5 6h-17c-.277 0-.5.223-.5.5s.223.5.5.5h17c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM20.5 18h-17c-.277 0-.5.223-.5.5s.223.5.5.5h17c.277 0 .5-.223.5-.5s-.223-.5-.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-ltr.svg
new file mode 100644
index 00000000..8a670ef2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="arched-arrow-ltr">
+ <path id="arrow" d="M19.925 14.937l-2.391-6.901-1.48 2.329c-.964-.845-2.699-1.85-5.513-1.823-4.887.046-6.524 4.244-6.524 4.244s2.753-2.639 6.925-1.949c1.729.286 3.007 1.206 3.675 1.791l-1.474 2.319 6.782-.01z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-rtl.svg
new file mode 100644
index 00000000..01fc216b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arched-arrow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="arched-arrow-rtl">
+ <path id="arrow" d="M13.401 8.542c-2.814-.027-4.549.978-5.513 1.823l-1.48-2.329-2.391 6.901 6.782.009-1.474-2.319c.668-.584 1.945-1.504 3.675-1.791 4.172-.69 6.925 1.949 6.925 1.949s-1.637-4.197-6.524-4.243z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-ltr.svg
new file mode 100644
index 00000000..b07621e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M16 12h-10c-1.7 0-3 1.3-3 3h13v3l5-4.5-5-4.5v3z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-rtl.svg
new file mode 100644
index 00000000..a0189283
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/arrow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M8 12h10c1.7 0 3 1.3 3 3h-13v3l-5-4.5 5-4.5v3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-ltr.svg
new file mode 100644
index 00000000..94ec6704
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.666 6h-1.372l-4.48 12h1.705l1.494-4h3.999l1.508 4h1.666l-4.52-12zm-2.28 7l1.617-4.333 1.634 4.333h-3.251z" id="a"/>
+ <g id="up">
+ <path id="arrow" d="M15.5 9h7l-3.5-6z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-rtl.svg
new file mode 100644
index 00000000..b2a6c139
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bigger-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
+ <g id="up">
+ <path id="arrow" d="M1.5 9h7L5 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/block.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/block.svg
new file mode 100644
index 00000000..0ddd1d47
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/block.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9h-10v-2h10v2z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-ltr.svg
new file mode 100644
index 00000000..3d9cfd7d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g66">
+ <path d="M17 11v2h-2l3.6 3.6c.9-1.3 1.4-2.9 1.4-4.6 0-4.4-3.6-8-8-8-1.7 0-3.3.5-4.6 1.4l5.6 5.6h4zm-13-7l-1 1 2.4 2.4c-.9 1.3-1.4 2.9-1.4 4.6 0 4.4 3.6 8 8 8 1.7 0 3.3-.5 4.6-1.4l2.4 2.4 1-1-16-16zm3 9v-2h2l2 2h-4z" id="path68"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-rtl.svg
new file mode 100644
index 00000000..8f807596
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/blockUndo-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g66">
+ <path d="M7 11v2h2l-3.6 3.6c-.9-1.3-1.4-2.9-1.4-4.6 0-4.4 3.6-8 8-8 1.7 0 3.3.5 4.6 1.4l-5.6 5.6h-4zm13-7l1 1-2.4 2.4c.9 1.3 1.4 2.9 1.4 4.6 0 4.4-3.6 8-8 8-1.7 0-3.3-.5-4.6-1.4l-2.4 2.4-1-1 16-16zm-3 9v-2h-2l-2 2h4z" id="path68"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-a.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-a.svg
new file mode 100644
index 00000000..4b828779
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-a.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-a">
+ <path d="M16 18h3l-5-12h-3l-5 12h3l1.25-3h4.5l1.25 3zm-4.917-5l1.417-3.4 1.417 3.4h-2.834z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-ain.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-ain.svg
new file mode 100644
index 00000000..f96cebce
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-ain.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-arab-ain">
+ <path id="arab-ain" d="M9.337 13.616c0 1.349 1.386 2.101 4.159 2.258l2.187-.029.318.044c-.03.127-.251.345-.665.652l-.089.066c-1.236.929-2.423 1.393-3.56 1.393-1.143 0-2.046-.33-2.711-.99-.65-.66-.975-1.559-.975-2.698.005-1.354.566-2.573 1.684-3.658v-.044l-.606-.55c-.148-.181-.222-.391-.222-.63 0-.489.239-1.109.717-1.862.65-1.046 1.303-1.566 1.958-1.561.886.005 1.618.42 2.194 1.246.325.479-.03.552-1.064.22-.842-.327-1.527-.051-2.054.828l.015.073 1.123.865.052.007c1.404-.498 2.418-.74 3.043-.726-.059.117-.14.362-.244.733-.103.357-.204.684-.303.982l-.126.374-.384.051c-1.743.239-2.992.716-3.745 1.429-.463.464-.697.973-.702 1.525"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-dad.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-dad.svg
new file mode 100644
index 00000000..f04c6aad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-arab-dad.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-arab-dad">
+ <path id="arab-dad" d="M16.411 8.232l-1.676-.665.694-1.567 1.688.64-.707 1.592m.775 3.078c-.509-.286-1-.427-1.476-.423-.471 0-.982.205-1.532.616l-.506.379.006.025c1.084.066 1.934.099 2.551.099h.313c.567-.021.992-.064 1.276-.131-.067-.17-.275-.359-.625-.566h-.006m-6.803 3.296c-.017-.904-.329-1.87-.938-2.898l1.294-1.729.119.149c.267.336.504.924.713 1.766l.063.05c.496-.008.942-.17 1.338-.485v-.006l1.732-1.53c.679-.601 1.282-.902 1.807-.902.383.004.848.195 1.394.572.55.377.884.696 1 .958.063.149.094.386.094.709 0 .696-.11 1.229-.331 1.598-.192.311-.473.555-.844.734-.438.207-1.549.311-3.333.311-.8 0-1.795-.021-2.983-.062l-.144.429c-.254.672-.463 1.113-.625 1.324-.725.937-1.786 1.405-3.183 1.405-1.705-.008-2.557-.922-2.557-2.742.004-.941.279-1.814.825-2.618.15-.216.298-.367.444-.454.225-.133.288-.091.188.124-.396.862-.596 1.548-.6 2.058.008 1.177.752 1.768 2.232 1.772 1.038-.004 1.803-.182 2.295-.535"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-armn-to.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-armn-to.svg
new file mode 100644
index 00000000..4dbec6d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-armn-to.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-armn-to">
+ <path id="armn-to" d="M13.86 16.257c.124 0 .254-.026.39-.078.135-.058.257-.15.367-.274.114-.13.205-.302.273-.516.073-.213.11-.48.11-.797V13h-1.14c-.14 0-.284.026-.43.078-.14.047-.27.133-.383.258-.11.125-.2.294-.274.508-.067.213-.1.487-.1.82 0 .34.035.47.108.695.08.218.175.395.29.53.12.136.247.232.383.29.14.05.276.077.406.077m-2.97-7.84c-.37.082-.695.247-.976.45-.28.198-.505.47-.672.813-.16.343-.242.78-.242 1.312V18H6v-7.188c0-.776.15-1.455.453-2.04.302-.587.714-1.077 1.234-1.467.52-.39 1.13-.685 1.83-.883.697-.198 1.44-.297 2.225-.297.526 0 1.04.044 1.54.133.504.088.98.22 1.43.398.447.172.858.388 1.233.65.375.26.698.564.97.913.275.348.49.738.64 1.17.15.433.226 1.094.226 1.61h1.353v2.04H17.78v1.6c0 .58-.103 1.092-.31 1.54-.21.442-.49.815-.845 1.117-.35.302-.834.53-1.297.687-.464.15-.953.226-1.47.226-.51 0-.996-.078-1.46-.234-.464-.156-.87-.39-1.22-.703-.348-.313-.626-.703-.835-1.172-.203-.473-.304-1.028-.304-1.663s.105-1.182.32-1.64c.213-.46.497-.685.85-.977.355-.297.76-.513 1.22-.648.458-.14.935-.21 1.43-.21h1.132c-.01-.49-.04-1.043-.242-1.36-.198-.323-.453-.58-.766-.766-.312-.193-.598-.332-.984-.426-.374-.09-.577-.094-1.1-.094-.52 0-.64.02-1.01.102z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-b.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-b.svg
new file mode 100644
index 00000000..4f648203
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-b.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-b">
+ <path id="b" d="M7 18h6c2 0 4-1 4-3 0-1.064.011-1.975-1.989-3 2-.975 1.989-1.935 1.989-3 0-2-2-3-4-3h-6v12zm7-8c0 1.001 0 1-2 1h-2v-3h2c2 0 2 0 2 1v1zm-2 6h-2v-3h2c2 0 2 0 2 1v1s0 1-2 1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-be.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-be.svg
new file mode 100644
index 00000000..279466d4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-be.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-be">
+ <path id="cyrl-be" d="M7 6h9v2h-6v3h2.649c.893 0 1.633.109 2.22.327.588.218 1.088.622 1.502 1.211.419.589.629 1.187.629 1.978 0 .813-.21 1.398-.629 1.977-.419.578-.898.974-1.437 1.187-.533.213-1.295.319-2.286.319h-5.649m4.767-2c.751 0 1.279-.049 1.584-.12.305-.076.569-.246.792-.508.229-.262.343-.473.343-.855 0-.557-.199-.868-.596-1.119-.392-.256-1.064-.398-2.016-.398h-1.873v3"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-te.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-te.svg
new file mode 100644
index 00000000..fdeeb6c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-te.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-te">
+ <path id="te" d="M11 18v-10h-4v-2h11v2h-4v10"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-zhe.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-zhe.svg
new file mode 100644
index 00000000..5996c813
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-cyrl-zhe.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-zhe">
+ <path id="cyrl-zhe" d="M13 6v5.154c.328-.033.537-.181.705-.447.168-.266.401-.873.698-1.821.39-1.241.789-2.033 1.197-2.374.403-.336 1.075-.504 2.014-.504l.386-.008v1.78l-.386-.008c-.399 0-.691.062-.878.187-.186.119-.337.304-.452.553-.115.249-.286.762-.512 1.537-.12.412-.25.756-.392 1.033-.137.276-.383.537-.738.78.439.157.8.466 1.084.927.288.455.603 1.103.944 1.943l1.33 3.268h-2.314l-1.17-3.081-.113-.252-.239-.561c-.248-.569-.452-.932-.612-1.089-.16-.157-.317-.236-.552-.236v5.22h-2v-5.22c-.226 0-.382.076-.546.228-.164.152-.368.518-.612 1.098l-.246.561-.113.252-1.17 3.081h-2.314l1.33-3.268c.328-.808.636-1.447.924-1.919.293-.477.663-.794 1.11-.951-.355-.244-.603-.501-.745-.772-.137-.276-.268-.623-.392-1.041-.222-.759-.39-1.266-.505-1.52-.111-.255-.261-.444-.452-.569-.186-.125-.492-.187-.917-.187l-.352.008v-1.78l.386.008c.953 0 1.631.171 2.034.512.399.347.791 1.136 1.177 2.366.301.954.534 1.564.698 1.829.168.26.377.406.705.439v-5.154"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-f.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-f.svg
new file mode 100644
index 00000000..357d2e5d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-f.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-f">
+ <path id="f" d="M16 8v-2h-8v12h3v-5h4v-2h-4v-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-g.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-g.svg
new file mode 100644
index 00000000..e032542e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-g.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-g">
+ <path id="g" d="M12 14v-2h5v4.203c-.497.475-1.22.894-2.166 1.259-.941.359-1.896.538-2.864.538-1.23 0-2.303-.253-3.217-.76-.915-.512-1.602-1.24-2.062-2.185-.46-.95-.69-1.982-.69-3.095 0-1.208.257-2.282.77-3.222.513-.939 1.265-1.66 2.255-2.161.754-.385 1.693-.578 2.816-.578 1.46 0 2.6.303 3.418.91.824.602 1.353 1.435 1.589 2.501l-2.359.435c-.166-.57-.479-1.018-.939-1.346-.455-.332-1.024-.499-1.709-.499-1.038 0-1.864.325-2.479.974-.61.649-.915 1.612-.915 2.889 0 1.377.31 2.412.931 3.103.62.686 1.433 1.029 2.439 1.029.497 0 .995-.095 1.492-.285.503-.195 1.332-.571 1.691-.845v-.867"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-geor-man.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-geor-man.svg
new file mode 100644
index 00000000..b211bf7a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-geor-man.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-geor-man">
+ <path id="geor-man" d="M13.832 14.061c0-1.715-.394-2.573-1.182-2.573-.868 0-1.302.779-1.302 2.338-.01 1.624.421 2.436 1.295 2.436.793 0 1.189-.734 1.189-2.201m2.168 0c0 2.626-1.116 3.939-3.349 3.939-2.434 0-3.651-1.386-3.651-4.159 0-2.738 1.217-4.106 3.651-4.106.841 0 1.182.63 1.182.63v-1.579c0-.789-.449-1.184-1.347-1.184-.572 0-.858.374-.858 1.123h-2.341c.005-1.817 1.064-2.725 3.176-2.725 2.368 0 3.548.946 3.538 2.839"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-l.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-l.svg
new file mode 100644
index 00000000..16797938
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-l.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-l">
+ <path id="l" d="M8 18v-12h3v10h5v2"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-n.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-n.svg
new file mode 100644
index 00000000..73ad019a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-n.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-n">
+ <path id="n" d="M7 18v-12h3l4 8v-8h3v12h-3l-4-8v8h-3"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-v.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-v.svg
new file mode 100644
index 00000000..146943a5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/bold-v.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-v">
+ <path id="v" d="M10.5 18l-4.5-12h3l3 8 3-8h3l-4.5 12"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/cancel.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/cancel.svg
new file mode 100644
index 00000000..bfc1b44b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/cancel.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="cancel">
+ <path id="circle-with-strike" d="M11.999 5.022c-3.853 0-6.977 3.124-6.977 6.978 0 3.853 3.124 6.978 6.977 6.978 3.854 0 6.979-3.125 6.979-6.978 0-3.854-3.125-6.978-6.979-6.978zm-5.113 6.978c0-1.092.572-3.25.93-2.929l7.113 7.113c.488.525-1.837.931-2.93.931-2.825-.001-5.113-2.291-5.113-5.115zm9.298 2.929l-7.114-7.113c-.445-.483 1.837-.931 2.929-.931 2.827 0 5.115 2.289 5.115 5.114 0 1.093-.364 3.543-.93 2.93z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-ltr.svg
new file mode 100644
index 00000000..f31ec095
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 13.1l8.9 8.9c.8-.8.8-2 0-2.8l-6.1-6.1 6-6.1c.8-.8.8-2 0-2.8l-8.8 8.9z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-rtl.svg
new file mode 100644
index 00000000..02b4e387
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caret-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16.5 13.1l-8.9 8.9c-.8-.8-.8-2 0-2.8l6.1-6.1-6-6.1c-.8-.8-.8-2 0-2.8l8.8 8.9z" id="path108"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretDown.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretDown.svg
new file mode 100644
index 00000000..a04ca572
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretDown.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 16l8.9-8.9c-.8-.8-2-.8-2.8 0l-6.1 6.1-6.1-6c-.8-.8-2-.8-2.8 0l8.9 8.8z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretUp.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretUp.svg
new file mode 100644
index 00000000..d0e0c283
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/caretUp.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 6.5l8.9 8.9c-.8.8-2 .8-2.8 0l-6.1-6.1-6.1 6c-.8.8-2 .8-2.8 0l8.9-8.8z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/case-sensitive.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/case-sensitive.svg
new file mode 100644
index 00000000..824790c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/case-sensitive.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="regular-expression">
+ <path id="upper-case" d="M 7.53125,7 4,17 l 2.0625,0 0.71875,-2.40625 3.625,0 L 11.125,17 13.1875,17 9.65625,7 7.53125,7 z M 8.59375,8.53125 9.9375,13 7.25,13 8.59375,8.53125 z" />
+ <path id="lower-case" d="m 18.548697,17 -0.183254,-1.035072 -0.05451,0 c -0.349771,0.440361 -0.710892,0.746796 -1.083366,0.919307 -0.367941,0.167972 -0.849436,0.251959 -1.444489,0.251959 -0.564328,0 -0.954665,-0.20883 -1.377109,-0.626492 -0.417903,-0.417659 -0.626854,-1.012371 -0.626853,-1.784137 -1e-6,-0.80808 0.281628,-1.402791 0.844889,-1.784137 0.567801,-0.385878 1.193222,-0.607062 2.208372,-0.640111 l 1.321843,-0.04086 0,-0.333674 c 0,-0.771759 -0.395195,-1.15764 -1.185571,-1.157647 -0.608688,7e-6 -1.324118,0.183867 -2.146293,0.551584 L 14.134181,9.9184512 c 0.876685,-0.4585114 1.848761,-0.6877705 2.916233,-0.6877783 1.022038,7.8e-6 1.586855,0.2224573 2.131951,0.6673492 C 19.727448,10.342928 20,11.019356 20,11.927309 l 0,5.073215 -1.451303,0 m -0.394476,-3.527417 -0.804008,0.02724 c -0.604145,0.01816 -1.053844,0.127119 -1.349098,0.326866 -0.29526,0.199753 -0.442889,0.503919 -0.442886,0.912498 -3e-6,0.585634 0.336136,0.878451 1.008417,0.878449 0.481492,2e-6 0.865326,-0.138462 1.151503,-0.415391 0.29071,-0.276925 0.436067,-0.644648 0.436072,-1.103169 l 0,-0.626491" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/check.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/check.svg
new file mode 100644
index 00000000..03e36607
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/check.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="check">
+ <path d="M7.105 13.473l1.422-1.423 1.901 1.902 4.81-6.952 1.657 1.148-6.26 8.852z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/circle.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/circle.svg
new file mode 100644
index 00000000..436259e5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/circle.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="6"></circle></svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/close.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/close.svg
new file mode 100644
index 00000000..1345e867
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/close.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="close">
+ <path id="x" d="M18.717 6.697l-1.414-1.414-5.303 5.303-5.303-5.303-1.414 1.414 5.303 5.303-5.303 5.303 1.414 1.414 5.303-5.303 5.303 5.303 1.414-1.414-5.303-5.303z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/code.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/code.svg
new file mode 100644
index 00000000..32f140d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/code.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
+ <g id="code">
+ <path id="left-bracket" d="M4 12v-1h1c1 0 1 0 1-1v-2.386c0-.514.024-.896.073-1.142.054-.252.139-.463.257-.633.204-.279.473-.475.808-.584.335-.115.872-.255 1.835-.255h1.027v1h-.752c-.457 0-.77.191-.936.408-.167.215-.312.445-.312 1.068v1.857c0 .729-.041 1.18-.244 1.493-.2.307-.562.529-1.09.667.535.155.9.385 1.096.688.199.303.238.757.238 1.484v1.862c0 .619.145.848.312 1.062.166.22.479.407.936.407l.752.004v1h-1.027c-.963 0-1.5-.133-1.835-.248-.335-.109-.604-.307-.808-.591-.118-.165-.203-.374-.257-.625-.049-.253-.073-.636-.073-1.149v-2.387c0-1 0-1-1-1h-1z"/>
+ <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" width="24" height="24" xlink:href="#left-bracket"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/collapse.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/collapse.svg
new file mode 100644
index 00000000..55aa8f8f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/collapse.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="collapse">
+ <path id="arrow" d="M6.697 15.714l5.303-5.302 5.303 5.302 1.414-1.414-6.717-6.717-6.717 6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/comment.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/comment.svg
new file mode 100644
index 00000000..0ae7e63f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/comment.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="comment">
+ <path id="speech-bubble" d="M15 6h-6c-1.657 0-3 1.344-3 3v4c0 1.656 1.343 3 3 3v3l3-3h3c1.657 0 3-1.344 3-3v-4c0-1.656-1.343-3-3-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/downTriangle.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/downTriangle.svg
new file mode 100644
index 00000000..7bc1c228
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/downTriangle.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 18l8-10h-16z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-ltr.svg
new file mode 100644
index 00000000..3972e070
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_3">
+ <path d="M17 2l-12 12-1 5 5-1 12-12c0-2-2-4-4-4zm-9.8 13.5c-.3-.3-.7-.6-1-.8 2.3-2.3 11.3-11.4 11.3-11.4.4.1.7.3 1 .7l-11.3 11.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-rtl.svg
new file mode 100644
index 00000000..978b2fd1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/edit-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_3">
+ <path d="M8 2l12 12 1 5-5-1-12-12c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8-2.3-2.3-11.3-11.4-11.3-11.4-.4.1-.7.3-1 .7l11.3 11.5z" id="path173"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-ltr.svg
new file mode 100644
index 00000000..7e376824
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_2">
+ <g id="g184">
+ <path d="M21 4v-1s0-3-3-3-3 3-3 3v1h-1v6h8v-6zm-1.5 0h-3v-1s0-1.5 1.5-1.5c1.48.06 1.5 1.5 1.5 1.5zm-6.5 5.6l-6.8 6.9c-.3-.3-.7-.6-1-.8 1.4-1.4 5-5 7.8-7.9v-1.8l-9 9-1 5 5-1 8-8h-3z" id="path186"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-rtl.svg
new file mode 100644
index 00000000..0b4751d2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editLock-rtl.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_2">
+ <g id="g184">
+ <path d="M4 4v-1s0-3 3-3 3 3 3 3v1h1v6h-8v-6zm1.5 0h3v-1s0-1.5-1.5-1.5c-1.48.06-1.5 1.5-1.5 1.5zm6.5 5.6l6.8 6.9c.3-.3.7-.6 1-.8-1.4-1.4-5-5-7.8-7.9v-1.8l9 9 1 5-5-1-8-8h3z" id="path186"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-ltr.svg
new file mode 100644
index 00000000..f346874e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g196">
+ <g id="g198">
+ <path d="M14.9 2.8c.9 0 1.8.2 2.7.6.9.4 1.6.9 1.9 1.6-2.8.1-5 1.1-6.6 3.1l1.3 2-6.7-.3.5-6.8 1.7 2c1.8-1.5 3.5-2.2 5.2-2.2z" id="path200"/>
+ </g>
+ </g>
+ <g id="g204">
+ <path d="M15.2 11.1l-2.6-.1-5.4 5.5c-.3-.3-.7-.6-1-.8.9-.9 2.8-2.8 4.7-4.8h-1.8l-4.1 4.1-1 5 5-1 7.8-7.8-1.6-.1zm5.4-5.1c-1.7 0-3.2.5-4.4 1.4l-.9.9.8 1.3.9 1.4 4-4c0-.3-.1-.7-.2-1h-.2z" id="path206"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-rtl.svg
new file mode 100644
index 00000000..5b59d452
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/editUndo-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g196">
+ <g id="g198">
+ <path d="M10.1 2.8c-.9 0-1.8.2-2.7.6-.9.4-1.6.9-1.9 1.6 2.8.1 5 1.1 6.6 3.1l-1.3 2 6.7-.3-.5-6.8-1.7 2c-1.8-1.5-3.5-2.2-5.2-2.2z" id="path200"/>
+ </g>
+ </g>
+ <g id="g204">
+ <path d="M9.8 11.1l2.6-.1 5.4 5.5c.3-.3.7-.6 1-.8-.9-.9-2.8-2.8-4.7-4.8h1.8l4.1 4.1 1 5-5-1-7.8-7.8 1.6-.1zm-5.4-5.1c1.7 0 3.2.5 4.4 1.4l.9.9-.8 1.3-.9 1.4-4-4c0-.3.1-.7.2-1h.2z" id="path206"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/ellipsis.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/ellipsis.svg
new file mode 100644
index 00000000..dd36a30d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/ellipsis.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <g>
+ <path d="M8 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ <g>
+ <path d="M14 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ <g>
+ <path d="M20 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/expand.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/expand.svg
new file mode 100644
index 00000000..7666b41d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/expand.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="expand">
+ <path id="arrow" d="M17.303 8.283l-5.303 5.303-5.303-5.303-1.414 1.414 6.717 6.717 6.717-6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-ltr.svg
new file mode 100644
index 00000000..827bc1b1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="external">
+ <path id="box" d="M2 2h3v1h-2v6h6v-2h1v3h-8z"/>
+ <path id="arrow" d="M6.211 2h3.789v3.789l-1.421-1.421-2.132 2.132-.947-.947 2.132-2.132z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-rtl.svg
new file mode 100644
index 00000000..c375ca0f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/external-link-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="external">
+ <path id="box" d="M7 3h2v6h-6v-2h-1v3h8v-8h-3z"/>
+ <path id="arrow" d="M2 5.789l1.421-1.421 2.132 2.132.947-.947-2.132-2.132 1.421-1.421h-3.789z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-ltr.svg
new file mode 100644
index 00000000..f8578cf8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="find">
+ <path id="magnifying-glass" d="m 13.65625,11 c -1.921,0 -3.5,1.54775 -3.5,3.46875 0,1.92 1.579,3.5 3.5,3.5 0.749,0 1.432,-0.25225 2,-0.65625 l 0.09375,0.15625 2.375,2.375 c 0.19,0.189 0.53425,0.15325 0.78125,-0.09375 0.247,-0.247 0.314,-0.59125 0.125,-0.78125 l -2.375,-2.375 L 16.46875,16.5 C 16.87175,15.934 17.125,15.21775 17.125,14.46875 17.124,12.54875 15.57525,11 13.65625,11 z m 0,1.65625 c 1.011306,0 1.8125,0.801194 1.8125,1.8125 0,1.011306 -0.801194,1.84375 -1.8125,1.84375 -1.011306,0 -1.84375,-0.832444 -1.84375,-1.84375 0,-1.011306 0.832444,-1.8125 1.84375,-1.8125 z" />
+ <path id="text" d="M 6,5 6,7 16,7 16,5 6,5 z m 0,3 0,2 11,0 0,-2 -11,0 z m 0,3 0,2 3.53125,0 c 0.2825289,-0.797203 0.786096,-1.486208 1.4375,-2 L 6,11 z m 0,3 0,2 3.53125,0 C 9.3537004,15.520243 9.25,15.010236 9.25,14.46875 9.25,14.309811 9.2962033,14.154621 9.3125,14 L 6,14 z" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-rtl.svg
new file mode 100644
index 00000000..2a1e9c6f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/find-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="find">
+ <path id="magnifying-glass" d="m 11.343828,11.000025 c 1.921,0 3.5,1.54775 3.5,3.46875 0,1.92 -1.579,3.5 -3.5,3.5 -0.749,0 -1.432,-0.25225 -2,-0.65625 l -0.09375,0.15625 -2.375,2.375 c -0.19,0.189 -0.53425,0.15325 -0.78125,-0.09375 -0.247,-0.247 -0.314,-0.59125 -0.125,-0.78125 l 2.375,-2.375 0.1875,-0.09375 c -0.403,-0.566 -0.65625,-1.28225 -0.65625,-2.03125 10e-4,-1.92 1.54975,-3.46875 3.46875,-3.46875 z m 0,1.65625 c -1.011306,0 -1.8125,0.801194 -1.8125,1.8125 0,1.011306 0.801194,1.84375 1.8125,1.84375 1.011306,0 1.84375,-0.832444 1.84375,-1.84375 0,-1.011306 -0.832444,-1.8125 -1.84375,-1.8125 z" />
+ <path id="text" d="M 19,5 19,7 9,7 9,5 z m 0,3 0,2 -11,0 0,-2 z m 0,3 0,2 -3.53125,0 c -0.282529,-0.797203 -0.786096,-1.486208 -1.4375,-2 z m 0,3 0,2 -3.53125,0 C 15.6463,15.520243 15.75,15.010236 15.75,14.46875 15.75,14.309811 15.703797,14.154621 15.6875,14 z" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-ltr.svg
new file mode 100644
index 00000000..6e81d2bc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M14 6.5v-1.5c-1.4-1.5-5.2-1.2-6 0v-1h-1v15h1v-7c.8-.8 3.4-.9 5-.5v1.5c1.2 1.5 4.3 1.2 5 0v-7c-.7.7-2.7.9-4 .5z" id="path216"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-rtl.svg
new file mode 100644
index 00000000..4b743aac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flag-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11 6.5v-1.5c1.4-1.5 5.2-1.2 6 0v-1h1v15h-1v-7c-.8-.8-3.4-.9-5-.5v1.5c-1.2 1.5-4.3 1.2-5 0v-7c.7.7 2.7.9 4 .5z" id="path216"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-ltr.svg
new file mode 100644
index 00000000..49cdb7a2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-ltr.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g2990">
+ <g id="Layer_1">
+ <path id="path227" d="M14 6.5v-1.5c-1.4-1.5-5.2-1.2-6 0v-1h-1v15h1v-7c.8-.8 3.4-.9 5-.5v1.5c1.2 1.5 4.3 1.2 5 0v-7c-.7.7-2.7.9-4 .5z"/>
+ </g>
+ <g id="Layer_2">
+ <g id="g230">
+ <path id="path232" d="M17.997 1.989l.99.99-15.98 15.98-.99-.99z"/>
+ </g>
+ <g id="g234">
+ <path id="path236" d="M16.999 1.016l.99.99-15.98 15.98-.99-.99z" fill="#fff"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-rtl.svg
new file mode 100644
index 00000000..e470de42
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/flagUndo-rtl.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g2990">
+ <g id="Layer_1">
+ <path id="path227" d="M11 6.5v-1.5c1.4-1.5 5.2-1.2 6 0v-1h1v15h-1v-7c-.8-.8-3.4-.9-5-.5v1.5c-1.2 1.5-4.3 1.2-5 0v-7c.7.7 2.7.9 4 .5z"/>
+ </g>
+ <g id="Layer_2">
+ <g id="g230">
+ <path id="path232" d="M7.003 1.989l-.99.99 15.98 15.98.99-.99z"/>
+ </g>
+ <g id="g234">
+ <path id="path236" d="M8.001 1.016l-.99.99 15.98 15.98.99-.99z" fill="#fff"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-ltr.svg
new file mode 100644
index 00000000..bb2545c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-ltr.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="help">
+ <path id="circle" d="M12.001 2.085c-5.478 0-9.916 4.438-9.916 9.916 0 5.476 4.438 9.914 9.916 9.914 5.476 0 9.914-4.438 9.914-9.914 0-5.478-4.438-9.916-9.914-9.916zm.001 18c-4.465 0-8.084-3.619-8.084-8.083 0-4.465 3.619-8.084 8.084-8.084 4.464 0 8.083 3.619 8.083 8.084 0 4.464-3.619 8.083-8.083 8.083z"/>
+ <g id="question-mark">
+ <path id="top" d="M11.766 6.688c-2.5 0-3.219 2.188-3.219 2.188l1.411.854s.298-.791.901-1.229c.516-.375 1.625-.625 2.219.125.701.885-.17 1.587-1.078 2.719-.953 1.186-1 3.655-1 3.655h1.969s.135-2.318 1.041-3.381c.603-.707 1.443-1.338 1.443-2.494s-1.187-2.437-3.687-2.437z"/>
+ <path id="bottom" d="M11 16h2v2h-2z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-rtl.svg
new file mode 100644
index 00000000..99c7f842
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/help-rtl.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="help">
+ <path id="circle" d="M11.999 2.085c5.478 0 9.916 4.438 9.916 9.916 0 5.476-4.438 9.914-9.916 9.914-5.476 0-9.914-4.438-9.914-9.914 0-5.478 4.438-9.916 9.914-9.916zm-.001 18c4.465 0 8.084-3.619 8.084-8.083 0-4.465-3.619-8.084-8.084-8.084-4.464 0-8.083 3.619-8.083 8.084 0 4.464 3.619 8.083 8.083 8.083z"/>
+ <g id="question-mark">
+ <path id="top" d="M12.234 6.688c2.5 0 3.219 2.188 3.219 2.188l-1.411.854s-.298-.791-.901-1.229c-.516-.375-1.625-.625-2.219.125-.701.885.17 1.587 1.078 2.719.953 1.186 1 3.655 1 3.655h-1.969s-.135-2.318-1.041-3.381c-.603-.707-1.443-1.338-1.443-2.494 0-1.156 1.187-2.437 3.687-2.437z"/>
+ <path id="bottom" d="M13 16h-2v2h2z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/history.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/history.svg
new file mode 100644
index 00000000..35f15afe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/history.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="history">
+ <path id="clock-hands" d="M17.26 15.076s-2.385-1.935-4.005-3.062c.72-2.397 1.702-6.559 1.702-6.559s-4.35 5.363-4.877 6.699c-.463 1.168 1.459 2.209 2.346 1.678 1.9.551 4.834 1.244 4.834 1.244z"/>
+ <path id="arrow" d="M12.086 2.085c-5.478 0-9.916 4.438-9.916 9.916 0 1.783.476 3.454 1.301 4.898l-2.223 2.04h5.688v-5.219l-2.066 1.896c-.55-1.088-.866-2.312-.866-3.615 0-4.465 3.619-8.084 8.084-8.084 4.464 0 8.083 3.619 8.083 8.084 0 4.464-3.619 8.083-8.083 8.083-1.145 0-2.228-.247-3.213-.678l-.833 1.634c1.235.557 2.602.874 4.045.874 5.476 0 9.914-4.438 9.914-9.914-.001-5.477-4.439-9.915-9.915-9.915z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-ltr.svg
new file mode 100644
index 00000000..e95d40d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-ltr.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="indent-list-ltr">
+ <path id="arrow" d="M5 15.079l4.794-3.527-4.704-3.599-.01 2.047h-2.08v3h2z"/>
+ <path id="bottom_line" d="M20 17h-16c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h16c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ <path id="middle_line" d="M20 10h-7c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h7c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ <path id="top_line" d="M20 3h-16c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h16c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-rtl.svg
new file mode 100644
index 00000000..cca3ad31
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/indent-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="indent-list-rtl">
+ <path id="arrow" d="M19 15.079l-4.794-3.527 4.704-3.599.01 2.047h2.08v3h-2z"/>
+ <path id="bottom_line" d="M4 17h16c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-16c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ <path id="middle_line" d="M4 10h7c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-7c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ <path id="top_line_5_" d="M4 3h16c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-16c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/info.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/info.svg
new file mode 100644
index 00000000..9c0d1cbc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/info.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0, 0, 24, 24">
+ <g id="info">
+ <path id="circled-i" d="M11.499 17c-3.036 0-5.499-2.464-5.499-5.5 0-3.037 2.462-5.5 5.499-5.5 3.037 0 5.501 2.462 5.501 5.5 0 3.036-2.464 5.5-5.501 5.5zm.002-12c-3.591 0-6.501 2.91-6.501 6.5s2.91 6.5 6.501 6.5c3.588 0 6.499-2.911 6.499-6.5s-2.911-6.5-6.499-6.5zM12 10v4h1v1h-3v-1h1v-3h-1v-1zM11 8h1v1h-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/insert.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/insert.svg
new file mode 100644
index 00000000..0833f84f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/insert.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="insert">
+ <path d="M13 5h-2v6h-6v2h6v6h2v-6h6v-2h-6z" id="plus"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-a.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-a.svg
new file mode 100644
index 00000000..a0e66bff
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-a.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-a">
+ <path id="a" d="M14.667 6h-1.372l-7 12h1.705l2.333-4h4l.667 4h1.667l-2-12zm-3.75 7l2.527-4.333.723 4.333h-3.25z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-keheh-jeem.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-keheh-jeem.svg
new file mode 100644
index 00000000..d4bff1be
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-keheh-jeem.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-arab-keheh-jeem">
+ <path id="arab-keheh-jeem" d="M18.125 5.844c-1.695.555-3.297 1.162-4.594 1.938-.49.299-.774.712-.875 1.125-.064.263-.035.572.063.781.189.405.539.574.844.813l.094-.125.531.625c.14.164.343.513.469.938.137.463.08.725 0 1.125h-3.438c-.338 0-.592.007-.766-.02-.339-.053-.256-.208-.234-.34.332-.127.564-.173.938-.141.29-.494.593-.885.906-1.313-.98.037-1.878.015-2.688-.094-.346-.047-.698-.186-1.094-.156-.357.026-.768.239-1.031.719-.246.448-.434.839-.656 1.281l.75-.469c.23-.142.484-.227.719-.219.157.005.275.054.406.094-.231.205-.509.402-.719.563-.301.26-.702.688-.906 1-.403.615-.694 1.084-.875 1.781-.179.689.004 1.339.469 1.75.426.376.846.519 1.281.563.65.065 1.205.093 2-.188.657-.231 1.021-.553 1.5-.969-.883.11-1.817.089-2.531.031-.871-.07-1.268-.384-1.469-.594-.271-.283-.307-.64-.156-1.219.036-.141.097-.323.25-.531.168-.228.364-.435.594-.656.451-.436 1.011-.737 1.461-.938-.045.206-.107.443-.055.688.049.229.248.379.438.469.259.122.506.155.688.156 1.421.011 2.862 0 4.281 0 .247 0 .452-.163.594-.375.139-.208.249-.481.344-.844.131-.499.094-1.062-.094-1.625-.182-.543-.418-1.009-.719-1.406-.335-.443-.674-.829-1-1.219 1.257-.815 2.716-1.239 3.969-1.688.121-.452.224-.926.313-1.313zm-9.469 8.438c-.262.394-.584.691-.875 1 .375.286.748.556 1.094.813.335-.303.626-.674.875-.969-.39-.268-.771-.588-1.094-.844z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-meem.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-meem.svg
new file mode 100644
index 00000000..bfbc9bf5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-arab-meem.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-arab-meem">
+ <path id="arab-meem" d="M16 9.729l-.93 2.19h-4.663c-.479 0-.857.122-1.135.367l-.061.11c-.184 2.016-.502 3.558-.955 4.627-.272.641-.633 1.252-1.082 1.833-.177.226-.219.186-.126-.119l.142-.504.17-.669.234-.87.002-.009.202-1.045.258-1.411.353-1.906c.191-.312.424-.638.699-.98.276-.342.589-.706.94-1.09.129-.092.697-.18 1.705-.266 1.05-.086 1.638-.183 1.765-.293l.065-.128c.007-.11-.011-.241-.054-.394-.043-.153-.12-.327-.231-.522-.22-.428-.438-.641-.654-.641-.294 0-.915.269-1.864.806-.359.208-.376.125-.051-.247 1.558-1.71 2.708-2.566 3.45-2.566.383 0 .671.131.863.394.135.195.25.599.344 1.21l.203 1.2c.106.586.242.895.409.925"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-armn-sha.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-armn-sha.svg
new file mode 100644
index 00000000..63de0f6c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-armn-sha.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-armn-sha">
+ <path id="armn-sha" d="M11.564 7.678c-.268-.13-.578-.22-.93-.268-.35-.047-.75-.07-1.197-.07h-1.11L8.586 6h1.724c.558 0 1.042.032 1.45.095.416.063.794.173 1.136.33l4.483 2.033-.324 1.67-2.624-1.165c-.126-.058-.27-.103-.433-.134-.164-.038-.356-.057-.576-.057-.583 0-1.137.095-1.663.284-.524.19-1 .46-1.425.812-.42.35-.777.78-1.072 1.283-.294.504-.504 1.074-.63 1.71-.242 1.255-.152 2.21.268 2.868.426.652 1.19.978 2.294.978.55 0 1.045-.08 1.48-.237.437-.156.815-.377 1.136-.66.326-.29.59-.633.796-1.033.21-.4.362-.84.457-1.323l.11-.56h1.6l-.12.59c-.13.674-.356 1.288-.676 1.845-.32.55-.725 1.026-1.214 1.425-.488.394-1.053.7-1.694.922-.642.215-1.343.323-2.105.323-.767 0-1.434-.113-2-.34-.568-.225-1.025-.553-1.372-.984-.347-.436-.573-.97-.678-1.607-.105-.637-.078-1.364.08-2.184.125-.66.346-1.273.66-1.835.316-.567.697-1.066 1.144-1.496.445-.436.944-.794 1.496-1.072.55-.284 1.13-.475 1.733-.575l-.466-.23"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-c.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-c.svg
new file mode 100644
index 00000000..b468deac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-c.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-c">
+ <path id="c" d="M15.008 13.718l1.481.214c-.468 1.34-1.15 2.354-2.046 3.04-.896.686-1.901 1.029-3.015 1.029-1.359 0-2.438-.43-3.237-1.29-.794-.86-1.191-2.092-1.191-3.697 0-2.09.606-3.818 1.817-5.185 1.079-1.219 2.42-1.828 4.023-1.828 1.186 0 2.145.33 2.878.989.738.66 1.165 1.546 1.282 2.66l-1.397.135c-.148-.839-.453-1.464-.916-1.876-.458-.417-1.051-.625-1.779-.625-1.369 0-2.476.631-3.321 1.892-.733 1.087-1.099 2.377-1.099 3.871 0 1.193.282 2.103.847 2.731.565.628 1.3.942 2.206.942.774 0 1.473-.261 2.099-.784.626-.522 1.081-1.261 1.366-2.216"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-d.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-d.svg
new file mode 100644
index 00000000..92a834d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-d.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-d">
+ <path id="d" d="M7 18l2.462-12h3.557c.853 0 1.505.063 1.955.188.644.169 1.194.472 1.65.909.456.431.799.971 1.03 1.621.231.649.346 1.378.346 2.186 0 .966-.145 1.847-.435 2.644-.284.791-.66 1.49-1.127 2.095-.461.6-.947 1.072-1.456 1.416-.504.338-1.102.589-1.794.753-.526.126-1.172.188-1.939.188h-4.249m1.859-1.359h1.867c.842 0 1.591-.079 2.245-.237.408-.098.756-.243 1.046-.434.381-.246.727-.57 1.038-.974.408-.535.732-1.143.974-1.825.247-.688.37-1.468.37-2.341 0-.971-.166-1.716-.499-2.235-.333-.524-.756-.87-1.271-1.04-.381-.126-.974-.188-1.778-.188h-1.85l-1.907 9.274"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-e.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-e.svg
new file mode 100644
index 00000000..66a5ef5d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-e.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-e">
+ <path id="e" d="M7 18l2.474-12h8.526l-.282 1.367h-6.947l-.75 3.633h6.09l-.282 1.367h-6.09l-.877 4.274h7.438l-.282 1.359h-9.018"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-geor-kan.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-geor-kan.svg
new file mode 100644
index 00000000..3398904d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-geor-kan.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-geor-kan">
+ <path id="geor-kan" d="M15.057 14.663c-.441 2.225-1.834 3.337-4.178 3.337-1.919 0-2.879-.787-2.879-2.36 0-.298.036-.624.108-.977.083-.431.245-.836.488-1.217l1.241.605-.207.613c-.055.259-.083.497-.083.712 0 .972.521 1.458 1.564 1.458 1.307 0 2.101-.723 2.383-2.17l.058-.331c.044-.221.066-.425.066-.613 0-.928-.546-1.391-1.638-1.391h-1.117l.248-1.259h1.117c1.202-.005 1.908-.552 2.118-1.64.039-.182.058-.356.058-.522 0-1.143-.899-1.714-2.697-1.714l.232-1.193c2.708 0 4.062.875 4.062 2.625 0 .248-.028.516-.083.803-.204 1.093-1.051 1.825-2.54 2.195l-.033.166c1.23.199 1.845.823 1.845 1.872 0 .21-.025.433-.074.671l-.058.331"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-i.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-i.svg
new file mode 100644
index 00000000..93bec5a6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-i.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-i">
+ <path id="i" d="M12.5 17.999l.249-.994h-1.5l2.509-10.037h1.5l.242-.967h-5l-.242.967h1.5l-2.509 10.037h-1.5l-.249.994z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-k.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-k.svg
new file mode 100644
index 00000000..d4831549
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-k.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-k">
+ <path id="k" d="M12.018 10.652l4.982-4.652h-2l-5.309 5.234 1.309-5.234h-1.5l-3 12h1.5l1.173-4.693 1.54-1.438c.287 4.131 3.287 6.131 3.287 6.131h2s-4-2-3.982-7.348z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-s.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-s.svg
new file mode 100644
index 00000000..4f6364cb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/italic-s.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-s">
+ <path id="s" d="M16.474 6.589l-.302 1.526c-.522-.279-1.041-.488-1.557-.628-.511-.145-1.007-.217-1.487-.217-.935 0-1.679.204-2.231.612-.553.408-.829.95-.829 1.627 0 .372.101.658.302.86.207.196.733.408 1.58.635l.937.232c1.059.274 1.795.622 2.208 1.046.413.418.62 1.007.62 1.766 0 1.167-.46 2.117-1.379 2.851-.914.733-2.12 1.1-3.618 1.1-.615 0-1.232-.062-1.852-.186-.62-.119-1.242-.302-1.867-.55l.318-1.611c.573.356 1.147.625 1.72.806.578.181 1.154.271 1.728.271.976 0 1.759-.217 2.347-.651.589-.434.883-.999.883-1.697 0-.465-.119-.816-.356-1.054-.232-.243-.736-.462-1.511-.658l-.937-.24c-1.069-.279-1.8-.599-2.192-.961-.387-.367-.581-.878-.581-1.534 0-1.152.442-2.094 1.325-2.828.888-.739 2.043-1.108 3.463-1.108.553 0 1.1.049 1.642.147.542.098 1.085.245 1.627.442"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/language.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/language.svg
new file mode 100644
index 00000000..081e49a1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/language.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="language">
+ <path id="japanese" d="M17.533 9.81l.271-.59 1.041.407-.18.363c.661.271 1.101.468 1.312.589.331.211.618.514.86.905.211.393.316.846.316 1.358 0 .786-.302 1.479-.905 2.083-.604.634-1.66 1.057-3.169 1.268-.121-.361-.258-.679-.408-.95.965-.151 1.645-.333 2.037-.545.454-.21.785-.481.998-.813.21-.303.314-.663.314-1.087 0-.482-.136-.905-.407-1.269-.331-.331-.8-.589-1.402-.77-.333.634-.649 1.117-.951 1.449-.242.332-.694.906-1.358 1.721.09.393.181.709.272.951l-1.042.362-.091-.498c-.423.361-.801.617-1.133.77-.361.15-.664.226-.905.226-.303 0-.574-.136-.814-.407-.243-.301-.362-.68-.362-1.132 0-.604.136-1.147.407-1.63.241-.453.603-.89 1.086-1.313.272-.241.725-.528 1.359-.86 0-.271.03-.799.09-1.585-.514.03-.921.045-1.222.045-.393 0-.711-.015-.951-.045l-.046-1.041c.725.091 1.494.135 2.31.135 0-.149.075-.738.227-1.766l1.177.183c-.151.542-.256 1.041-.316 1.493.242-.029.543-.075.906-.136.362-.061.573-.091.634-.091s.648-.15 1.766-.453l.046 1.041c-.967.243-2.145.439-3.532.591-.062.663-.092 1.086-.092 1.266.663-.151 1.284-.225 1.857-.225zm-2.672 3.893c-.061-.481-.136-1.252-.227-2.31-.573.424-1.041.86-1.403 1.313-.303.423-.452.875-.452 1.358 0 .241.044.438.136.588.09.092.195.137.316.137.363.001.907-.361 1.63-1.086zm.771-2.763c0 .483.029 1.088.09 1.811.604-.905 1.057-1.599 1.359-2.082-.574.06-1.058.151-1.449.271z"/>
+ <path id="english" d="M9.497 15.981h1.851l-3.084-8.949h-1.85l-3.081 8.949h1.85l.557-1.981h3.209l.548 1.981zm-3.489-3.377l1.331-3.782 1.344 3.782h-2.675z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-ltr.svg
new file mode 100644
index 00000000..47e71b39
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="layout-ltr">
+ <path id="text" d="M5 19v-14h6v8h8v6h-14z"/>
+ <path id="float" d="M13 5v6h6v-6h-6zm5 5h-4v-4h4v4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-rtl.svg
new file mode 100644
index 00000000..fe9ee617
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/layout-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="layout-rtl">
+ <path id="text" d="M5 19v-6h8v-8h6v14h-14z"/>
+ <path id="float" d="M5 5v6h6v-6h-6zm1 1h4v4h-4v-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/link.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/link.svg
new file mode 100644
index 00000000..dbae3414
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/link.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="link">
+ <path id="right" d="M19.188 12.001c0 1.1-.891 2.015-1.988 2.015l-4.195-.015c.538 1.088.963 1.999 1.997 1.999h3c1.656 0 2.998-2.343 2.998-4s-1.342-4-2.998-4h-3c-1.034 0-1.459.911-1.998 1.999l4.195-.015c1.098 0 1.989.917 1.989 2.017z"/>
+ <path id="center" d="M8 12c0 .535.42 1 .938 1h6.109c.518 0 .938-.465.938-1 0-.534-.42-1-.938-1h-6.109c-.518 0-.938.466-.938 1z"/>
+ <path id="left" d="M4.816 11.999c0-1.1.891-2.015 1.988-2.015l4.196.015c-.539-1.088-.964-1.999-1.998-1.999h-3c-1.656 0-2.998 2.343-2.998 4s1.342 4 2.998 4h3c1.034 0 1.459-.911 1.998-1.999l-4.195.015c-1.098 0-1.989-.917-1.989-2.017z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-ltr.svg
new file mode 100644
index 00000000..5a43f5ce
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bullet-list-ltr">
+ <path id="bottom_dot" d="M5 10h-1c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h1c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ <path id="middle_dot" d="M5 17h-1c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h1c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ <path id="top_dot" d="M5 3h-1c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h1c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ <path id="bottom_line" d="M20 17h-11c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h11c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ <path id="middle_line" d="M20 10h-11c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h11c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ <path id="top_line" d="M20 3h-11c-.552 0-1 .447-1 1v1c0 .553.448 1 1 1h11c.552 0 1-.447 1-1v-1c0-.553-.448-1-1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-rtl.svg
new file mode 100644
index 00000000..fb6e9569
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listBullet-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bullet-list-rtl">
+ <path id="bottom_dot_1_" d="M19 10h1c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-1c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ <path id="middle_dot_1_" d="M19 17h1c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-1c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ <path id="top_dot_1_" d="M19 3h1c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-1c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ <path id="bottom_line_7_" d="M4 17h11c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-11c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ <path id="middle_line_7_" d="M4 10h11c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-11c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ <path id="top_line_7_" d="M4 3h11c.552 0 1 .447 1 1v1c0 .553-.448 1-1 1h-11c-.552 0-1-.447-1-1v-1c0-.553.448-1 1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-ltr.svg
new file mode 100644
index 00000000..e929dae0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="number-list-ltr">
+ <path id="bottom_dot" d="M3 16v1h1.993l.03 1h-1.023v1h1v1h-2v1h2.023l.977-1.002v-1l-.955-.531.955-.5v-.969l-1.007-.998z"/>
+ <path id="middle_dot" d="M3 9v1h2.117l-2.117 2.187v1.811l3-.062v-.936h-2.118l2.118-2.188v-1.032l-.668-.78z"/>
+ <path id="top_dot" d="M4.993 2h-.648l-1.327 1.391.031.609h1.025l-.068 2h-1.006v1h3v-1h-1.037z"/>
+ <path id="bottom_line" d="M20.002 17h-11.002c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h11.002c.551 0 .998-.447.998-1v-1c0-.553-.447-1-.998-1z"/>
+ <path id="middle_line" d="M20.002 10h-11.002c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h11.002c.551 0 .998-.447.998-1v-1c0-.553-.447-1-.998-1z"/>
+ <path id="top_line" d="M20.002 3h-11.002c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h11.002c.551 0 .998-.447.998-1v-1c0-.553-.447-1-.998-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-rtl.svg
new file mode 100644
index 00000000..bbfa92f4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/listNumbered-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="number-list-rtl">
+ <path id="bottom_dot" d="M18 16v1h1.993l.03 1h-1.023v1h1v1h-2v1h2.023l.977-1.002v-1l-.956-.531.956-.5v-.969l-1.007-.998z"/>
+ <path id="middle_dot" d="M18 9v1h2.116l-2.116 2.187v1.811l3-.062v-.936h-2.118l2.118-2.188v-1.032l-.669-.78z"/>
+ <path id="top_dot" d="M19.993 2h-.648l-1.328 1.391.031.609h1.026l-.069 2h-1.005v1h3v-1h-1.038z"/>
+ <path id="bottom_line" d="M3.999 17h11.002c.552 0 .999.447.999 1v1c0 .553-.447 1-.999 1h-11.002c-.552 0-.999-.447-.999-1v-1c0-.553.447-1 .999-1z"/>
+ <path id="middle_line" d="M3.999 10h11.002c.552 0 .999.447.999 1v1c0 .553-.447 1-.999 1h-11.002c-.552 0-.999-.447-.999-1v-1c0-.553.447-1 .999-1z"/>
+ <path id="top_line" d="M3.999 3h11.002c.552 0 .999.447.999 1v1c0 .553-.447 1-.999 1h-11.002c-.552 0-.999-.447-.999-1v-1c0-.553.447-1 .999-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/lock.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/lock.svg
new file mode 100644
index 00000000..76328f37
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/lock.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="lock">
+ <path d="M12 6c-2.21 0-4 1.79-4 4v1H7v7h10v-7h-1v-1c0-2.21-1.79-4-4-4zm0 2c1.105 0 2 .895 2 2v1h-4v-1c0-1.105.895-2 2-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/menu.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/menu.svg
new file mode 100644
index 00000000..50ac8a39
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/menu.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="menu">
+ <path id="lines" d="M6 15h12c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-12c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1zm-1-4v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1h-12c-.553 0-1 .447-1 1zm0-5v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1h-12c-.553 0-1 .447-1 1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-ltr.svg
new file mode 100644
index 00000000..51e6611a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="move-ltr">
+ <path id="arrow" d="M8.935 7.181l5.302 5.302-5.302 5.303 1.414 1.414 6.716-6.717-6.716-6.716z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-rtl.svg
new file mode 100644
index 00000000..bcee09d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="move-rtl">
+ <path id="arrow" d="M15.065 17.786l-5.302-5.303 5.302-5.302-1.414-1.414-6.716 6.716 6.716 6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move.svg
new file mode 100644
index 00000000..9063bd48
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/move.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M20 11l-4-3v2h-3v-3h2l-3-4-3 4h2v3h-3v-2l-4 3 4 3v-2h3v3h-2l3 4 3-4h-2v-3h3v2z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-ltr.svg
new file mode 100644
index 00000000..dad5f51c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="line_return">
+ <path d="M17.8 5.7c-.5 0-.9.2-1.2.5s-.5.7-.5 1.2v4.3h-5.1v-4l-6 5.5 6 5.5v-4h8v-9h-1.2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-rtl.svg
new file mode 100644
index 00000000..fd758cc6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/newline-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="line_return">
+ <path d="M6.2 5.7c.5 0 .9.2 1.2.5.3.3.5.7.5 1.2v4.3H13v-4l6 5.5-6 5.5v-4H5v-9h1.2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-ltr.svg
new file mode 100644
index 00000000..601428e2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M16 14l2 2v-11h-4v2h2zm0 2l-7-7-2-2-1-1-1-1-3-3-1 1 2 2h-1v14h4v-2h-2v-10h1l2 2v10h4v-2h-2v-6l6 6h-1v2h3l4 4 1-1-4-4zm-5-9v-2h-4l2 2zm8-2v2h2v10h-2l2 2h2v-14z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-rtl.svg
new file mode 100644
index 00000000..31785a3c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/noWikiText-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g484">
+ <path d="M8 14l-2 2v-11h4v2h-2zm0 2l7-7 2-2 1-1 1-1 3-3 1 1-2 2h1v14h-4v-2h2v-10h-1l-2 2v10h-4v-2h2v-6l-6 6h1v2h-3l-4 4-1-1 4-4zm5-9v-2h4l-2 2zm-8-2v2h-2v10h2l-2 2h-2v-14z" id="path486"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-ltr.svg
new file mode 100644
index 00000000..344b7617
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-ltr.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outdent-list-ltr">
+ <path id="arrow" d="M8 13h2v-3h-2.052l-.031-2.06-4.712 3.585 4.795 3.554z"/>
+ <path id="bottom_line" d="M20 17h-16c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h16c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ <path id="middle_line" d="M20 10h-7c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h7c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ <path id="top_line" d="M20 3h-16c-.553 0-1 .447-1 1v1c0 .553.447 1 1 1h16c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-rtl.svg
new file mode 100644
index 00000000..31e92c51
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outdent-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outdent-list-rtl">
+ <path id="arrow" d="M16 13h-2v-3h2.052l.031-2.06 4.712 3.585-4.795 3.554z"/>
+ <path id="bottom_line" d="M4 17h16c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-16c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ <path id="middle_line" d="M4 10h7c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-7c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ <path id="top_line" d="M4 3h16c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1h-16c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-ltr.svg
new file mode 100644
index 00000000..9c0ea598
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outline-ltr">
+ <path id="text" d="M5 13h14v6h-14v-6z"/>
+ <path id="float" d="M5 5v6h6v-6h-6zm5 5h-4v-4h4v4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-rtl.svg
new file mode 100644
index 00000000..2a3428e9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/outline-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outline-rtl">
+ <path id="text" d="M19 19h-14v-6h14v6z"/>
+ <path id="float" d="M13 5v6h6v-6h-6zm1 1h4v4h-4v-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/picture.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/picture.svg
new file mode 100644
index 00000000..7400bca9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/picture.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="picture">
+ <path id="frame" d="M18 4h-12c-2-.007-3 .993-3 2.993l.014 9.007c-.014 2 .986 2.988 2.986 3h12c2-.012 2.994-1 3-3.006v-9.001c-.006-2-1-3-3-2.993zm1 13h-14v-11h14v11z"/>
+ <path id="mountains" d="M6 13.5l3.5-3.5 2.328 2.312-1.312 1.094.875 1.032 4.109-3.438 2.5 2v3h-12z"/>
+ <path id="sky" d="M6 12l3.516-4.156 3.046 3.172 2.938-2.016 2.5 2v-4h-12z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-ltr.svg
new file mode 100644
index 00000000..97b77bb4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M18 9.9c-.7 0-1.4.3-1.8.9v-4.8h-4c.2-.4.4-.8.4-1.2 0-1.2-1-2.2-2.2-2.2-1.3-.1-2.3.9-2.3 2.2 0 .4.2.8.4 1.2h-4.4v3.6l.6-.1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5c-.2 0-.4 0-.6-.1v3.6h4.9c-.5.4-.9 1-.9 1.8 0 1.2 1 2.2 2.3 2.2 1.2 0 2.2-1 2.2-2.2 0-.7-.3-1.4-.9-1.8h4.5v-4.5c.4.5 1 .9 1.8.9 1.2 0 2.2-1 2.2-2.2 0-1.3-1-2.3-2.2-2.3z" id="path542"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-rtl.svg
new file mode 100644
index 00000000..0ad5f375
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/puzzle-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6.3 9.9c.7 0 1.4.3 1.8.9v-4.8h4c-.2-.4-.4-.8-.4-1.2 0-1.2 1-2.2 2.2-2.2 1.3-.1 2.3.9 2.3 2.2 0 .4-.2.8-.4 1.2h4.4v3.6l-.6-.1c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5c.2 0 .4 0 .6-.1v3.6h-4.9c.5.4.9 1 .9 1.8 0 1.2-1 2.2-2.3 2.2-1.2 0-2.2-1-2.2-2.2 0-.7.3-1.4.9-1.8h-4.5v-4.5c-.4.5-1 .9-1.8.9-1.2 0-2.2-1-2.2-2.2 0-1.3 1-2.3 2.2-2.3z" id="path542"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-ltr.svg
new file mode 100644
index 00000000..dc1c06f0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M19.9 8.7c.3-.1.6-.3.8-.6s.3-.7.3-1.1v-1c-1.3.2-1.9.2-3.3.8-.9.5-1.6 1.1-2.2 1.8s-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4s.1-.9.8-1.8c.6-.7 1.3-1.2 2.1-1.5zm-14.4-.1c-.6.7-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4s.1-.9.8-1.8c.6-.7 1.3-1.2 2.1-1.5.3-.1.6-.3.8-.6s.3-.7.3-1.1v-1c-1.3.2-1.9.2-3.3.8-.8.5-1.6 1.1-2.2 1.8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-rtl.svg
new file mode 100644
index 00000000..3a8b7012
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotes-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g552">
+ <path d="M4.1 8.7c-.3-.1-.6-.3-.8-.6-.2-.3-.3-.7-.3-1.1v-1c1.3.2 1.9.2 3.3.8.9.5 1.6 1.1 2.2 1.8.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4s-.1-.9-.8-1.8c-.6-.7-1.3-1.2-2.1-1.5zm14.4-.1c.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4s-.1-.9-.8-1.8c-.6-.7-1.3-1.2-2.1-1.5-.3-.1-.6-.3-.8-.6-.2-.3-.3-.7-.3-1.1v-1c1.3.2 1.9.2 3.3.8.8.5 1.6 1.1 2.2 1.8z" id="path554"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-ltr.svg
new file mode 100644
index 00000000..24fca8f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M3.5 8.6c-.6.7-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4s.1-.9.8-1.8c.6-.7 1.3-1.2 2.1-1.5.3-.1.6-.3.8-.6.2-.3.3-.7.3-1.1v-1c-1.3.2-1.9.2-3.3.8-.8.5-1.6 1.1-2.2 1.8zm15.5-3.6v-4h-2v4h-4v2h4v4h2v-4h4v-2zm-4 7s.1-.9.8-1.8l.2-.2v-2h-1.9l-.6.6c-.6.7-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-rtl.svg
new file mode 100644
index 00000000..736f2a6d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/quotesAdd-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M20.5 8.6c.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4s-.1-.9-.8-1.8c-.6-.7-1.3-1.2-2.1-1.5-.3-.1-.6-.3-.8-.6-.2-.3-.3-.7-.3-1.1v-1c1.3.2 1.9.2 3.3.8.8.5 1.6 1.1 2.2 1.8zm-15.5-3.6v-4h2v4h4v2h-4v4h-2v-4h-4v-2zm4 7s-.1-.9-.8-1.8l-.2-.2v-2h1.9l.6.6c.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-ltr.svg
new file mode 100644
index 00000000..884d40df
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="create_redirect">
+ <g>
+ <path d="M17.7 2.4c-.3-.3-.7-.4-1.2-.4h-12.1v16.2c0 .5.1.8.4 1.1s.7.7 1.2.7h10.2c-.6-.2-1.2-.5-1.9-1-.4-.3-.8-.6-1.2-1l-.5-.6h-6.2v-1.4h5.4s-.4-1.5-.4-2h-5v-1h9v1c.4.1 1.1.1 1.5.1.4 0 .7 0 1.1-.1v-10.5c.1-.5-.1-.9-.3-1.1zm-5.2 1.6h3v4.5h-3v-4.5zm-6.1 0h4v1.6h-4v-1.6zm0 3h4v1.5h-4v-1.5zm0 3h9v1.5h-9v-1.5zm12.7 3.1l4.9 3.8-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-rtl.svg
new file mode 100644
index 00000000..a07e8364
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/redirect-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="create_redirect">
+ <g id="g3264">
+ <path d="M6.3 2.4c.3-.3.7-.4 1.2-.4h12.1v16.2c0 .5-.1.8-.4 1.1-.3.3-.7.7-1.2.7h-10.2c.6-.2 1.2-.5 1.9-1 .4-.3.8-.6 1.2-1l.5-.6h6.2v-1.4h-5.4s.4-1.5.4-2h5v-1h-9v1c-.4.1-1.1.1-1.5.1-.4 0-.7 0-1.1-.1v-10.5c-.1-.5.1-.9.3-1.1zm5.2 1.6h-3v4.5h3v-4.5zm6.1 0h-4v1.6h4v-1.6zm0 3h-4v1.5h4v-1.5zm0 3h-9v1.5h9v-1.5z" id="path3266"/>
+ <path d="M4.9 13.1l-4.9 3.8 4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3" id="path3268"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/regular-expression.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/regular-expression.svg
new file mode 100644
index 00000000..7b672618
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/regular-expression.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="regular-expression">
+ <path id="left-bracket" d="m 3,12.044797 c -5e-7,-0.989171 0.150394,-1.914889 0.451184,-2.7771612 C 3.7558785,8.4053812 4.1933899,7.6495032 4.7637193,7 L 6.2286026,7 C 5.6778034,7.7204251 5.261777,8.511764 4.9805221,9.3740188 4.6992623,10.236291 4.5586337,11.122815 4.5586357,12.033598 c -2e-6,0.914522 0.1425798,1.799179 0.427746,2.653974 C 5.2754491,15.538635 5.6856161,16.309444 6.2168835,17 L 4.7637193,17 C 4.1894835,16.365435 3.7519721,15.624488 3.451184,14.777158 3.150394,13.929828 3,13.019042 3,12.044797" />
+ <path id="dot" d="m 10,16 c 0,0.552285 -0.4477153,1 -1,1 -0.5522847,0 -1,-0.447715 -1,-1 0,-0.552285 0.4477153,-1 1,-1 0.5522847,0 1,0.447715 1,1 z" />
+ <path id="star" d="m 14.250652,7.0127142 -0.240235,2.15625 2.185547,-0.609375 0.193359,1.4765618 -1.992187,0.140625 1.306641,1.740234 -1.330079,0.708985 -0.914062,-1.833985 -0.802734,1.822266 -1.382813,-0.697266 1.294922,-1.740234 -1.980469,-0.152343 0.228516,-1.4648438 2.138672,0.609375 -0.240235,-2.15625 1.535157,0" />
+ <path id="right-bracket" d="m 21,12.044797 c -3e-6,0.981711 -0.152351,1.896229 -0.457043,2.743558 C 20.241767,15.635686 19.806209,16.3729 19.235883,17 l -1.453164,0 c 0.527356,-0.686824 0.93557,-1.455766 1.224642,-2.306829 0.289069,-0.854795 0.433604,-1.741318 0.433606,-2.659573 -2e-6,-0.910783 -0.140631,-1.797307 -0.421886,-2.6595792 C 18.737821,8.511764 18.321795,7.7204251 17.771,7 l 1.464883,0 c 0.574232,0.653236 1.011744,1.4128466 1.312536,2.2788341 0.300785,0.8622719 0.45118,1.7842569 0.451183,2.7659629" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/remove.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/remove.svg
new file mode 100644
index 00000000..6ad79174
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/remove.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="remove">
+ <path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/search.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/search.svg
new file mode 100644
index 00000000..208d44b3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/search.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="search">
+ <path id="search" d="M16.021 15.96l-2.374-2.375-.169-.099c.403-.566.643-1.26.643-2.009-.001-1.92-1.558-3.477-3.477-3.477-1.921 0-3.478 1.557-3.478 3.478 0 1.92 1.557 3.477 3.478 3.477.749 0 1.442-.239 2.01-.643l.098.169 2.375 2.374c.19.189.543.143.79-.104s.293-.601.104-.791zm-5.377-2.27c-1.221 0-2.213-.991-2.213-2.213 0-1.221.992-2.213 2.213-2.213 1.222 0 2.213.992 2.213 2.213-.001 1.222-.992 2.213-2.213 2.213z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/secure-link.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/secure-link.svg
new file mode 100644
index 00000000..a9c7d276
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/secure-link.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="secure">
+ <path id="lock" d="M8 5h.019v-.997c.001-.057.004-1.409-.832-2.255-.434-.438-.998-.66-1.679-.66s-1.245.222-1.678.659c-.837.847-.833 2.199-.832 2.251v1.002h.002c-.553 0-1 .447-1 1v3c0 .553.447 1 1 1h5c.553 0 1-.447 1-1v-3c0-.553-.447-1-1-1zm-4.002 0v-1.007c0-.01.005-.999.543-1.543.482-.485 1.449-.487 1.932-.002.544.546.546 1.536.546 1.55v1.002h-3.021z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/settings.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/settings.svg
new file mode 100644
index 00000000..9fa0a4b3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/settings.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0, 0, 24, 24">
+ <g id="settings">
+ <path id="gear" d="M3 4h3v2h-3zM12 4h9v2h-9zM8 3h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 11h9v2h-9zM18 11h3v2h-3zM14 10h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 18h6v2h-6zM15 18h6v2h-6zM11 17h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-ltr.svg
new file mode 100644
index 00000000..e8b427b1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+ <g id="down">
+ <path id="arrow" d="M22 3l-3.5 6L15 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-rtl.svg
new file mode 100644
index 00000000..e5e95196
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/smaller-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+ <g id="down">
+ <path id="arrow" d="M9 3L5.5 9 2 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/specialCharacter.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/specialCharacter.svg
new file mode 100644
index 00000000..4d601281
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/specialCharacter.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="special-character">
+ <path id="omega" d="M12 6.708c-.794 0-1.368.103-1.894.31-.525.207-.944.496-1.255.867-.311.366-.531.808-.66 1.327-.128.513-.192 1.08-.192 1.699 0 .513.058 1 .174 1.46.122.46.311.87.568 1.23.629.863 1.155 1.139 2.011 1.363l.247 3.035h-5v-3h.605l.531 1.354.394.053.605.044.751.035.456.009h.66l-.092-.894c-.629-.094-.811-.268-1.336-.522-.525-.26-.98-.59-1.365-.991-.379-.401-.675-.867-.889-1.398-.214-.537-.321-1.13-.321-1.779 0-.82.131-1.537.394-2.15.269-.619.656-1.133 1.163-1.54.507-.407 1.133-.711 1.878-.912.745-.206 1.6-.31 2.565-.31.959 0 1.811.103 2.556.31.751.201 1.38.504 1.887.912.507.407.892.92 1.154 1.54.269.614.403 1.33.403 2.15 0 .649-.107 1.242-.321 1.779-.214.531-.513.997-.898 1.398-.379.401-.831.732-1.356.991-.525.254-.707.428-1.336.522l-.092.894h.66l.447-.009.751-.035.605-.044.403-.053.531-1.354h.605v3h-5l.247-3.035c1.066-.11 1.337-.696 2.002-1.363.263-.36.452-.77.568-1.23.122-.46.183-.947.183-1.46 0-.619-.064-1.186-.192-1.699-.128-.519-.348-.962-.66-1.327-.311-.372-.73-.661-1.255-.867-.525-.206-1.1-.31-1.894-.31"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/star.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/star.svg
new file mode 100644
index 00000000..ea8c26c6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/star.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5 1.9-3.6m0-3.4l-2.8 5.6-6.2.9 4.5 4.4-1.1 6.1 5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9-2.7-5.5z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-a.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-a.svg
new file mode 100644
index 00000000..480189f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-a.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-a">
+ <path id="strikethrough" d="M6 11h12v1h-12v-1z"/>
+ <path id="a" d="M12.666 6h-1.372l-4.48 12h1.705l1.494-4h3.999l1.508 4h1.666l-4.52-12zm-2.28 7l1.617-4.333 1.634 4.333h-3.251z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-s.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-s.svg
new file mode 100644
index 00000000..d57b652f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-s.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-s">
+ <path id="strikethrough" d="M6 12h12v1h-12v-1z"/>
+ <path id="s" d="M12.094 6c-1.133 0-2.076.287-2.75.9-.67.613-1 1.49-1 2.52 0 .889.221 1.602.719 2.13.498.528 1.279.91 2.312 1.14l.812.182v-.03c.656.147 1.128.375 1.375.63.252.256.375.607.375 1.11 0 .573-.172.97-.531 1.26-.358.291-.894.45-1.625.45-.477 0-.969-.074-1.469-.24-.502-.166-1.031-.417-1.562-.75l-.375-.238v2.158l.156.062c.58.237 1.143.417 1.688.54.549.121 1.07.18 1.562.18 1.286 0 2.297-.293 3-.9.709-.605 1.062-1.486 1.062-2.608 0-.943-.256-1.726-.781-2.312-.521-.592-1.305-1-2.344-1.229l-.812-.181c-.716-.148-1.204-.352-1.406-.539-.205-.203-.312-.485-.312-.935 0-.533.162-.899.5-1.17.342-.271.836-.42 1.531-.42.395 0 .818.052 1.25.181.433.127.908.333 1.406.6l.375.18v-2.041s-1.188-.383-1.688-.479c-.499-.098-.984-.151-1.468-.151z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-y.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-y.svg
new file mode 100644
index 00000000..8409dc15
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/strikethrough-y.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-y">
+ <path id="strikethrough" d="M6 11h12v1h-12v-1z"/>
+ <path id="a" d="M7 6h1.724l3.288 4.935 3.264-4.935h1.724l-4.194 6.285v5.715h-1.612v-5.715l-4.194-6.285"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-ltr.svg
new file mode 100644
index 00000000..b7507daf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-ltr.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+ <path d="M18 13l-1 1v3l1 1h-1l-.527-.46L16 18h-1l1-1v-3l-1-1h1l.485.497L17 13z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-rtl.svg
new file mode 100644
index 00000000..9fe5325f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/subscript-rtl.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+ <path d="M8 13l1 1v3l-1 1h1l.527-.46L10 18h1l-1-1v-3l1-1h-1l-.485.497L9 13z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-ltr.svg
new file mode 100644
index 00000000..39f30a76
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-ltr.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+ <path d="M18 7l-1 1v3l1 1h-1l-.527-.46L16 12h-1l1-1V8l-1-1h1l.485.497L17 7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-rtl.svg
new file mode 100644
index 00000000..eabab21c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/superscript-rtl.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+ <path d="M8 7l1 1v3l-1 1h1l.527-.46L10 12h1l-1-1V8l1-1h-1l-.485.497L9 7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-caption.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-caption.svg
new file mode 100644
index 00000000..15bb06a6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-caption.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-caption">
+ <path id="caption" d="M6 6h12v3H6z"/>
+ <path id="table" d="M4 10v7h16v-7H4zm1 1h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2zM5 14h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-ltr.svg
new file mode 100644
index 00000000..798ee4a1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-column-ltr">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,5 2,0 0,14 -2,0 z"
+ id="column" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-rtl.svg
new file mode 100644
index 00000000..dfa33a08
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-column-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-column-rtl">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 17,5 2,0 0,14 -2,0 z"
+ id="column" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-after.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-after.svg
new file mode 100644
index 00000000..91d06644
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-after.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-row-after">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,17 14,0 0,2 -14,0 z"
+ id="row" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-before.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-before.svg
new file mode 100644
index 00000000..4b71f2a8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-insert-row-before.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-row-before">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,5 14,0 0,2 -14,0 z"
+ id="row" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-merge-cells.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-merge-cells.svg
new file mode 100644
index 00000000..6a8b77d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table-merge-cells.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-merge-cells">
+ <g id="merge-cell-left">
+ <path id="cell-border" d="m 4,7 0,9 7,0 0,-3 -1,0.834 L 10,15 5,15 5,8 10,8 10,9.167 11,10 11,7 z" />
+ <path id="arrow" d="m 8,9 0,2 -2,0 0,1 2,0 0,2 3,-2.5 z" />
+ </g>
+ <use id="merge-cell-right" xlink:href="#merge-cell-left" transform="matrix(-1,0,0,1,24,0)" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table.svg
new file mode 100644
index 00000000..1ba8c440
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/table.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+
+ <g id="table-insert">
+ <path id="table" d="M4 5v13h16v-13zm2 2h5v4h-5zm7 0h5v4h-5zm-7 5h5v4h-5zm7 0h5v4h-5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/tag.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/tag.svg
new file mode 100644
index 00000000..534824c8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/tag.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="tag">
+ <path d="M18.748 11.717c.389.389.389 1.025 0 1.414l-4.949 4.95c-.389.389-1.025.389-1.414 0l-6.01-6.01c-.389-.389-.707-1.157-.707-1.707l-.001-4.364c0-.55.45-1 1-1h4.364c.55 0 1.318.318 1.707.707l6.01 6.01zm-10.644-4.261c-.579.576-.578 1.514-.001 2.093.578.577 1.516.577 2.095.001.576-.578.576-1.517 0-2.095-.581-.576-1.518-.577-2.094.001z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-ltr.svg
new file mode 100644
index 00000000..6b594b29
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M24 6h-4v-4h-2v4h-4v2h4v4h2v-4h4z"/>
+ </g>
+ <path d="M19 13v7h-16c-1.1 0-2-.9-2-2v-11h12v-1h-13v12c0 1.7 1.3 3 3 3h17v-8h-1z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-rtl.svg
new file mode 100644
index 00000000..36b25a3e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/templateAdd-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g690">
+ <path d="M0 6h4v-4h2v4h4v2h-4v4h-2v-4h-4z" id="path692"/>
+ </g>
+ <path d="M5 13v7h16c1.1 0 2-.9 2-2v-11h-12v-1h13v12c0 1.7-1.3 3-3 3h-17v-8h1z" id="path694"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-lefttoright.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-lefttoright.svg
new file mode 100644
index 00000000..62526a03
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-lefttoright.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-dir-ltr">
+ <path d="M7 7h-2v-1h2l.469.5.531-.5h2v1h-2v10h2v1h-2l-.5-.531-.5.531h-2v-1h2zM13.976 16v-2h-2.976v-4h2.976v-1.956l6.024 3.978z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-righttoleft.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-righttoleft.svg
new file mode 100644
index 00000000..913bbfd6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-dir-righttoleft.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-dir-rtl">
+ <path d="M17 17h2v1h-2l-.469-.5-.531.5h-2v-1h2v-10h-2v-1h2l.5.531.5-.531h2v1h-2zM10.024 8v2h2.976v4h-2.976v1.956l-6.024-3.978z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-style.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-style.svg
new file mode 100644
index 00000000..0198c355
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/text-style.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-style">
+ <path id="a" d="M15.296 18h2.789l-1.14-12h-2.789l-8.156 12h2.789l2.039-3h4.183l.285 3zm-3.109-5l2.311-3.4.323 3.4h-2.634z"/>
+ <path id="underline" d="M6 19h12v1h-12v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-ltr.svg
new file mode 100644
index 00000000..7740e43e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11.1 13.1c-1.8-2.1-2.7-4.3-3-5.1h4.7l.7-2h-5.5v-3h-2v3h-5v2h5c-.2.9-1.3 4.8-5.1 7.6l1.2 1.6c2.7-2 4.3-4.5 5.1-6.4.7 1.3 1.7 3 3.2 4.5l.7-2.2zm1.4 6.9l1.3-4h5.3l1.3 4h2.2l-4.6-14h-3l-4.7 14h2.2zm4-12l2 6h-4l2-6z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-rtl.svg
new file mode 100644
index 00000000..c78e6222
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/translation-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.4 13.1c1.8-2.1 2.7-4.3 3-5.1h-4.7l-.7-2h5.5v-3h2v3h5v2h-5c.2.9 1.3 4.8 5.1 7.6l-1.2 1.6c-2.7-2-4.3-4.5-5.1-6.4-.7 1.3-1.7 3-3.2 4.5l-.7-2.2zm-1.4 6.9l-1.3-4h-5.3l-1.3 4h-2.2l4.6-14h3l4.7 14h-2.2zm-4-12l-2 6h4l-2-6z" id="path704"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trash.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trash.svg
new file mode 100644
index 00000000..f5914312
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trash.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2h-12zm1 1h10l-1 11h-8z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-ltr.svg
new file mode 100644
index 00000000..0731f056
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20.5 20.5l-15.5-15.5-1 1 3 3 1 11h8l.2-1.8 3.3 3.3zm-3.5-11.5h-6l5.5 5.5zm1-1c0-1.1-.9-2-2-2h-2l-1-1h-2l-1 1h-2l2 2h8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-rtl.svg
new file mode 100644
index 00000000..2a92cbef
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/trashUndo-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g714">
+ <path d="M4 20.5l15.5-15.5 1 1-3 3-1 11h-8l-.2-1.8-3.3 3.3zm3.5-11.5h6l-5.5 5.5zm-1-1c0-1.1.9-2 2-2h2l1-1h2l1 1h2l-2 2h-8z" id="path716"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-ltr.svg
new file mode 100644
index 00000000..66c024a9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 9v-2s0-5-4.5-5-4.5 5-4.5 5h2s0-3 2.5-3 2.5 3 2.5 3v2h-3v7c0 1.7 1.3 3 3 3h10v-10z" id="path726"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-rtl.svg
new file mode 100644
index 00000000..07cecbfe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unLock-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11 9v-2s0-5 4.5-5 4.5 5 4.5 5h-2s0-3-2.5-3-2.5 3-2.5 3v2h3v7c0 1.7-1.3 3-3 3h-10v-10z" id="path726"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unStar.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unStar.svg
new file mode 100644
index 00000000..724d1901
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/unStar.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-a.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-a.svg
new file mode 100644
index 00000000..dd6dde36
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-a.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="underline-a">
+ <path id="a" d="M14.424 16h2.076l-3.463-10h-2.077l-3.46 10h2.077l.627-2h3.604l.616 2zm-3.921-3.623l1.496-4.379 1.511 4.379h-3z"/>
+ <path id="underline" d="M7 17h10v1h-10v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-u.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-u.svg
new file mode 100644
index 00000000..fbd7c147
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/underline-u.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="underline-u">
+ <path id="u" d="M8 6h2v5.959c-.104 1.707.695 2.002 2 2.041 1.777.062 2.002-.879 2-2.041v-5.959h2v6.123c0 1.279-.338 2.245-1.016 2.898-.672.651-1.666.979-2.98.979-1.32 0-2.319-.326-2.996-.979-.672-.653-1.008-1.619-1.008-2.898v-6.123"/>
+ <path id="underline" d="M7 17h10v1h-10v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/upTriangle.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/upTriangle.svg
new file mode 100644
index 00000000..9e5e72f6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/upTriangle.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 8l8 10h-16z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/wikiText.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/wikiText.svg
new file mode 100644
index 00000000..eebd9b1a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/wikiText.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M23 5h-4v2h2v10h-2v2h4z"/>
+ </g>
+ <g>
+ <path d="M18 5h-4v2h2v10h-2v2h4z"/>
+ </g>
+ <g>
+ <path d="M2 5h4v2h-2v10h2v2h-4z"/>
+ </g>
+ <g>
+ <path d="M7 5h4v2h-2v10h2v2h-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/icons/window.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/window.svg
new file mode 100644
index 00000000..cd3b76c2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/icons/window.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="window">
+ <path id="title" d="M7 10h10v1h-10z"/>
+ <path id="frame" d="M16 19h-8c-2.206 0-4-1.794-4-4v-6c0-2.206 1.794-4 4-4h8c2.206 0 4 1.794 4 4v6c0 2.206-1.794 4-4 4zm-8-12c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2h8c1.103 0 2-.897 2-2v-6c0-1.103-.897-2-2-2h-8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/alert.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/alert.svg
new file mode 100644
index 00000000..d9dc6a87
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/alert.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="alert">
+ <path d="M6 12c-3.314 0-6-2.686-6-6s2.686-6 6-6 6 2.686 6 6-2.686 6-6 6zm-1-5h2v-5h-2zm0 3h2v-2h-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-down.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-down.svg
new file mode 100644
index 00000000..bfa8ef0b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-down.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="down">
+ <path id="arrow" d="M2 3l3.5 6 3.5-6z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-ltr.svg
new file mode 100644
index 00000000..aeca27a9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="ltr">
+ <path id="arrow" d="M3 9v-7l6 3.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-rtl.svg
new file mode 100644
index 00000000..eba00995
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="rtl">
+ <path id="arrow" d="M3 5.5l6 3.5v-7z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-up.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-up.svg
new file mode 100644
index 00000000..4b01bb02
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/arrow-up.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="up">
+ <path id="arrow" d="M5.5 2l-3.5 6h7z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/required.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/required.svg
new file mode 100644
index 00000000..969fa2d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/required.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="required">
+ <path d="M5 1h2v10h-2zM9.83 2.634l1 1.732-8.66 5-1-1.732zM1.17 4.366l1-1.732 8.66 5-1 1.732z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-ltr.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-ltr.svg
new file mode 100644
index 00000000..266349ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="search">
+ <path id="path3051" d="M10.369 9.474l-2.374-2.375-.169-.099c.403-.566.643-1.26.643-2.009-.001-1.92-1.558-3.477-3.477-3.477-1.921 0-3.478 1.557-3.478 3.478 0 1.92 1.557 3.477 3.478 3.477.749 0 1.442-.239 2.01-.643l.098.169 2.375 2.374c.19.189.543.143.79-.104s.293-.601.104-.791zm-5.377-2.27c-1.221 0-2.213-.991-2.213-2.213 0-1.221.992-2.213 2.213-2.213 1.222 0 2.213.992 2.213 2.213-.001 1.222-.992 2.213-2.213 2.213z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-rtl.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-rtl.svg
new file mode 100644
index 00000000..5368fd7c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/indicators/search-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="search">
+ <path id="path3051" d="M1.631 9.474l2.374-2.375.169-.099c-.403-.566-.643-1.26-.643-2.009.001-1.92 1.558-3.477 3.477-3.477 1.921 0 3.478 1.557 3.478 3.478 0 1.92-1.557 3.477-3.478 3.477-.749 0-1.442-.239-2.01-.643l-.098.169-2.375 2.374c-.19.189-.543.143-.79-.104s-.293-.601-.104-.791zm5.377-2.27c1.221 0 2.213-.991 2.213-2.213 0-1.221-.992-2.213-2.213-2.213-1.222 0-2.213.992-2.213 2.213.001 1.222.992 2.213 2.213 2.213z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/textures/pending.gif b/vendor/oojs/oojs-ui/src/themes/apex/images/textures/pending.gif
new file mode 100644
index 00000000..1194eed2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/textures/pending.gif
Binary files differ
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/textures/transparency.svg b/vendor/oojs/oojs-ui/src/themes/apex/images/textures/transparency.svg
new file mode 100644
index 00000000..63a0b57c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/textures/transparency.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="16" height="16" viewBox="0, 0, 16, 16">
+ <g id="transparency">
+ <path d="M0,0 L8,0 L8,8 L0,8 z" fill="#CCCCCC"/>
+ <path d="M8,8 L16,8 L16,16 L8,16 z" fill="#CCCCCC"/>
+ <path d="M8,0 L16,0 L16,8 L8,8 z" fill="#FFFFFF"/>
+ <path d="M0,8 L8,8 L8,16 L0,16 z" fill="#FFFFFF"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/images/toolbar-shadow.png b/vendor/oojs/oojs-ui/src/themes/apex/images/toolbar-shadow.png
new file mode 100644
index 00000000..97e8d13d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/images/toolbar-shadow.png
Binary files differ
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/indicators.json b/vendor/oojs/oojs-ui/src/themes/apex/indicators.json
new file mode 100644
index 00000000..0a9d1d26
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/indicators.json
@@ -0,0 +1,22 @@
+{
+ "prefix": "oo-ui-indicator",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "alert": { "file": "images/indicators/alert.svg" },
+ "up": { "file": "images/indicators/arrow-up.svg" },
+ "down": { "file": "images/indicators/arrow-down.svg" },
+ "next": { "file": {
+ "ltr": "images/indicators/arrow-ltr.svg",
+ "rtl": "images/indicators/arrow-rtl.svg"
+ } },
+ "previous": { "file": {
+ "ltr": "images/indicators/arrow-rtl.svg",
+ "rtl": "images/indicators/arrow-ltr.svg"
+ } },
+ "required": { "file": "images/indicators/required.svg" },
+ "search": { "file": {
+ "ltr": "images/indicators/search-ltr.svg",
+ "rtl": "images/indicators/search-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/layouts.less b/vendor/oojs/oojs-ui/src/themes/apex/layouts.less
new file mode 100644
index 00000000..b86aa010
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/layouts.less
@@ -0,0 +1,135 @@
+@import 'common';
+
+.theme-oo-ui-layout () {}
+
+.theme-oo-ui-bookletLayout () {
+ &-stackLayout {
+ > .oo-ui-panelLayout {
+ padding: 1.5em;
+ }
+ }
+
+ &-outlinePanel {
+ border-right: 1px solid #ddd;
+
+ > .oo-ui-outlineControlsWidget {
+ box-shadow: 0 0 0.25em rgba(0,0,0,0.25);
+ }
+ }
+}
+
+.theme-oo-ui-indexLayout () {
+ &-stackLayout {
+ > .oo-ui-panelLayout {
+ padding: 1.5em;
+ }
+ }
+}
+
+.theme-oo-ui-fieldLayout () {
+ margin-bottom: 1em;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &.oo-ui-fieldLayout-align-left,
+ &.oo-ui-fieldLayout-align-right {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding-top: 0.5em;
+ margin-right: 5%;
+ width: 35%;
+ }
+
+ > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+ width: 60%;
+ }
+ }
+
+ &.oo-ui-fieldLayout-align-inline {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding: 0.5em;
+ }
+
+ > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+ padding: 0.5em 0;
+ }
+ }
+
+ &.oo-ui-fieldLayout-align-top {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding: 0.5em 0;
+ }
+ }
+
+ > .oo-ui-popupButtonWidget {
+ .oo-ui-inline-spacing(0);
+ margin-top: 0.25em;
+ }
+
+ &-disabled .oo-ui-labelElement-label {
+ color: #ccc;
+ }
+}
+
+.theme-oo-ui-actionFieldLayout () {}
+
+.theme-oo-ui-fieldsetLayout () {
+ margin: 0;
+ padding: 0;
+ border: none;
+
+ + .oo-ui-fieldsetLayout,
+ + .oo-ui-formLayout {
+ margin-top: 2em;
+ }
+
+ > .oo-ui-labelElement-label {
+ font-size: 1.1em;
+ margin-bottom: 0.5em;
+ padding: 0.25em 0;
+ font-weight: bold;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-labelElement-label {
+ padding-left: 2em;
+ line-height: 1.8em;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-iconElement-icon {
+ left: 0;
+ top: 0.25em;
+ width: @icon-size;
+ height: @icon-size;
+ }
+
+ > .oo-ui-popupButtonWidget {
+ .oo-ui-inline-spacing(0);
+ }
+}
+
+.theme-oo-ui-formLayout () {
+ + .oo-ui-fieldsetLayout,
+ + .oo-ui-formLayout {
+ margin-top: 2em;
+ }
+}
+
+.theme-oo-ui-menuLayout () {}
+
+.theme-oo-ui-panelLayout () {
+ &-padded {
+ padding: 1.25em;
+ }
+
+ &-framed {
+ border-radius: 0.5em;
+ box-shadow: 0 0.25em 1em rgba(0, 0, 0, 0.25);
+ }
+}
+
+.theme-oo-ui-cardLayout () {}
+
+.theme-oo-ui-pageLayout () {}
+
+.theme-oo-ui-stackLayout () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/textures.json b/vendor/oojs/oojs-ui/src/themes/apex/textures.json
new file mode 100644
index 00000000..e90730ab
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/textures.json
@@ -0,0 +1,8 @@
+{
+ "prefix": "oo-ui-texture",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "pending": { "file": "images/textures/pending.gif" },
+ "transparency": { "file": "images/textures/transparency.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/tools.less b/vendor/oojs/oojs-ui/src/themes/apex/tools.less
new file mode 100644
index 00000000..913bb516
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/tools.less
@@ -0,0 +1,422 @@
+@import 'common';
+
+.theme-oo-ui-toolbar () {
+ &-bar {
+ border-bottom: 1px solid #ccc;
+ .oo-ui-vertical-gradient(#fff, #F1F7FB);
+
+ .oo-ui-toolbar-bar {
+ border: none;
+ background: none;
+ }
+ }
+
+ &-actions {
+ > .oo-ui-buttonElement {
+ margin-top: 0.4em;
+ margin-bottom: 0.4em;
+ }
+
+ > .oo-ui-buttonElement:last-child {
+ margin-right: 0.5em;
+ }
+ }
+
+ &-shadow {
+ .oo-ui-background-image('@{oo-ui-default-image-path}/toolbar-shadow.png');
+ bottom: -9px;
+ height: 9px;
+ opacity: 0.5;
+ .oo-ui-transition(opacity 500ms ease-in-out);
+ }
+}
+
+.theme-oo-ui-tool () {}
+
+.theme-oo-ui-popupTool () {
+ .oo-ui-popupWidget {
+ /* @noflip */
+ margin-left: 1.25em;
+ }
+}
+
+.theme-oo-ui-toolGroupTool () {
+ > .oo-ui-popupToolGroup {
+ border: 0;
+ border-radius: 0;
+ margin: 0;
+ }
+
+ // Like .oo-ui-tool in barToolGroup
+ &:first-child > .oo-ui-popupToolGroup {
+ border-top-left-radius: 0.3125em;
+ border-bottom-left-radius: 0.3125em;
+ }
+
+ &:last-child > .oo-ui-popupToolGroup {
+ border-top-right-radius: 0.3125em;
+ border-bottom-right-radius: 0.3125em;
+ }
+
+ // Like .oo-ui-tool-link in barToolGroup
+ > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
+ height: 1.875em;
+ padding: 0.3125em;
+
+ .oo-ui-iconElement-icon {
+ height: 1.875em;
+ width: 1.875em;
+ }
+ }
+
+ > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle {
+ .oo-ui-labelElement-label {
+ line-height: 2.1em; // 0.5em less
+ }
+ }
+}
+
+.theme-oo-ui-toolGroup () {
+ margin: 0.375em;
+ border-radius: 0.3125em;
+ border: 1px solid transparent;
+ .oo-ui-transition(border-color 300ms ease-in-out);
+
+ .oo-ui-toolbar-narrow & {
+ + .oo-ui-toolGroup {
+ margin-left: 0;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ border-color: rgba(0,0,0,0.1);
+ }
+
+ .oo-ui-tool-link .oo-ui-tool-title {
+ color: #000;
+ }
+ }
+}
+
+.theme-oo-ui-barToolGroup () {
+ > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ margin: -1px 0 -1px -1px;
+ border: 1px solid transparent;
+
+ &:first-child {
+ border-top-left-radius: 0.3125em;
+ border-bottom-left-radius: 0.3125em;
+ }
+
+ &:last-child {
+ margin-right: -1px;
+ border-top-right-radius: 0.3125em;
+ border-bottom-right-radius: 0.3125em;
+ }
+
+ > .oo-ui-tool-link {
+ height: 1.875em;
+ padding: 0.3125em;
+
+ .oo-ui-iconElement-icon {
+ height: 1.875em;
+ width: 1.875em;
+ }
+
+ .oo-ui-tool-title {
+ line-height: 2.1em; // 0.5em less
+ }
+ }
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ &.oo-ui-widget-enabled:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+
+ &.oo-ui-tool-active {
+ &.oo-ui-widget-enabled {
+ border-color: rgba(0,0,0,0.2);
+ box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+ .oo-ui-vertical-gradient(#F1F7FB, #fff);
+ }
+
+ &.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+ border-left-color: rgba(0,0,0,0.1);
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-tool-link {
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 1;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ > .oo-ui-tool-link {
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+}
+
+.theme-oo-ui-popupToolGroup () {
+ height: 2.5em;
+ min-width: 2.5em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 1.875em;
+ }
+
+ &.oo-ui-iconElement {
+ min-width: 3.125em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 2.5em;
+ }
+ }
+
+ &.oo-ui-indicatorElement.oo-ui-iconElement {
+ min-width: 4.375em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 3.75em;
+ }
+ }
+
+ &.oo-ui-labelElement {
+ .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ line-height: 2.6em;
+ margin: 0 1em;
+
+ .oo-ui-toolbar-narrow & {
+ margin: 0 0.5em;
+ }
+ }
+
+ &.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ margin-left: 3em;
+
+ .oo-ui-toolbar-narrow & {
+ margin-left: 2.5em;
+ }
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ margin-right: 2.25em;
+
+ .oo-ui-toolbar-narrow & {
+ margin-right: 1.75em;
+ }
+ }
+ }
+
+ &-handle {
+ .oo-ui-indicatorElement-indicator {
+ width: 0.9375em;
+ height: 0.9375em;
+ margin: 0.78125em;
+ top: 0;
+ right: 0;
+
+ .oo-ui-toolbar-narrow & {
+ right: -0.3125em;
+ }
+ }
+ .oo-ui-iconElement-icon {
+ width: 1.875em;
+ height: 1.875em;
+ margin: 0.3125em;
+ top: 0;
+ left: 0.3125em;
+
+ .oo-ui-toolbar-narrow & {
+ left: 0;
+ }
+ }
+ }
+
+ &-header {
+ line-height: 2.6em;
+ margin: 0 0.6em;
+ font-weight: bold;
+ }
+
+ &-active.oo-ui-widget-enabled {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+
+ .oo-ui-vertical-gradient(#F1F7FB, #fff);
+ }
+
+ .oo-ui-toolGroup-tools {
+ top: 2.5em;
+ margin: 0 -1px;
+ border: 1px solid #ccc;
+ background-color: white;
+ box-shadow: 0 0.3125em 1.25em rgba(0,0,0,0.25);
+ }
+
+ .oo-ui-tool-link {
+ padding: 0.3125em 0 0.3125em 0.3125em;
+
+ .oo-ui-iconElement-icon {
+ height: 1.875em;
+ width: 1.875em;
+ min-width: 1.875em;
+ }
+
+ .oo-ui-tool-title {
+ padding-left: 0.5em;
+ }
+
+ .oo-ui-tool-accel,
+ .oo-ui-tool-title {
+ line-height: 2em;
+ }
+
+ .oo-ui-tool-accel {
+ color: #888;
+ }
+ }
+}
+
+.theme-oo-ui-listToolGroup () {
+ .oo-ui-toolGroup-tools {
+ padding: 0.3125em;
+ }
+
+ &.oo-ui-popupToolGroup-active {
+ border-color: rgba(0,0,0,0.2);
+ }
+
+ .oo-ui-tool {
+ border: 1px solid transparent;
+ margin: -1px 0;
+ padding: 0 0.625em 0 0;
+
+ &-active {
+ &.oo-ui-widget-enabled {
+ border-color: rgba(0,0,0,0.1);
+ box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+ .oo-ui-vertical-gradient(#F1F7FB, #fff);
+
+ + .oo-ui-tool-active.oo-ui-widget-enabled {
+ border-top-color: rgba(0,0,0,0.1);
+ }
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+ &:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 1;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link {
+ .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-tool-accel {
+ color: #ddd;
+ }
+
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+}
+
+.theme-oo-ui-menuToolGroup () {
+ border-color: rgba(0,0,0,0.1);
+
+ .oo-ui-popupToolGroup-handle {
+ min-width: 10em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 8.125em;
+ }
+ }
+
+ .oo-ui-toolGroup-tools {
+ padding: 0.3125em 0 0.3125em 0;
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+ }
+
+ &.oo-ui-popupToolGroup-active {
+ border-color: rgba(0,0,0,0.25);
+ }
+
+ .oo-ui-tool {
+ padding: 0 1.25em 0 0.3125em;
+
+ &-link {
+ .oo-ui-iconElement-icon {
+ background-image: none;
+ }
+ }
+
+ &-active .oo-ui-tool-link .oo-ui-iconElement-icon {
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/check');
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ background-color: #e1f3ff;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+ border-color: rgba(0,0,0,0.05);
+
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/widgets.less b/vendor/oojs/oojs-ui/src/themes/apex/widgets.less
new file mode 100644
index 00000000..f415b6dd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/widgets.less
@@ -0,0 +1,797 @@
+@import 'common';
+
+.theme-oo-ui-widget () {}
+
+.theme-oo-ui-outlineControlsWidget () {
+ height: 3em;
+ background-color: #fff;
+
+ &-items,
+ &-movers {
+ height: 2em;
+ margin: 0.5em 0.5em 0.5em 0;
+ padding: 0;
+ }
+
+ > .oo-ui-iconElement-icon {
+ width: 1.5em;
+ height: 2em;
+ margin: 0.5em 0 0.5em 0.5em;
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-toggleWidget () {}
+
+.theme-oo-ui-buttonGroupWidget () {
+ display: inline-block;
+ white-space: nowrap;
+ border-radius: 0.3em;
+
+ .oo-ui-inline-spacing(0.5em);
+ .oo-ui-buttonElement {
+ .oo-ui-inline-spacing(0);
+ }
+
+ .oo-ui-buttonElement-framed {
+ .oo-ui-buttonElement-button {
+ border-radius: 0;
+ margin-left: -1px;
+ }
+
+ &:first-child .oo-ui-buttonElement-button {
+ border-bottom-left-radius: 0.3em;
+ border-top-left-radius: 0.3em;
+ margin-left: 0;
+ }
+
+ &:last-child .oo-ui-buttonElement-button {
+ border-bottom-right-radius: 0.3em;
+ border-top-right-radius: 0.3em;
+ }
+ }
+}
+
+.theme-oo-ui-buttonWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-actionWidget () {
+ &.oo-ui-pendingElement-pending {
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+}
+
+.theme-oo-ui-popupButtonWidget () {
+ &.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
+ // Compensate for icon being inset
+ /* @noflip */
+ left: 1em;
+ }
+
+ &.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
+ // Compensate for icon being inset
+ /* @noflip */
+ left: 1.25em;
+ }
+}
+
+.theme-oo-ui-toggleButtonWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-iconWidget () {
+ line-height: 2.5em;
+ height: @icon-size;
+ width: @icon-size;
+
+ &.oo-ui-widget-disabled {
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-indicatorWidget () {
+ line-height: 2.5em;
+ height: @indicator-size;
+ width: @indicator-size;
+ margin: @indicator-size / 2;
+
+ &.oo-ui-widget-disabled {
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-dropdownWidget () {
+ margin: 0.25em 0;
+ width: 100%;
+ max-width: 50em;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ &-handle {
+ height: 2.5em;
+ border: 1px solid rgba(0,0,0,0.1);
+ border-radius: 0.25em;
+
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ right: 0;
+ }
+
+ .oo-ui-iconElement-icon {
+ left: 0.25em;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: 2.5em;
+ margin: 0 0.5em;
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ top: 0;
+ width: @indicator-size;
+ height: @indicator-size;
+ margin: 0.775em;
+ }
+
+ .oo-ui-iconElement-icon {
+ top: 0;
+ width: @icon-size;
+ height: @icon-size;
+ margin: 0.3em;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-dropdownWidget-handle {
+ color: #ccc;
+ text-shadow: 0 1px 1px #fff;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ }
+
+ &.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+ margin-left: 3em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+ margin-right: 2em;
+ }
+}
+
+.theme-oo-ui-inputWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-buttonInputWidget () {}
+
+.theme-oo-ui-checkboxInputWidget () {}
+
+.theme-oo-ui-dropdownInputWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ select {
+ height: 2.5em;
+ padding: 0.5em;
+ font-size: inherit;
+ font-family: inherit;
+ border: 1px solid rgba(0,0,0,0.1);
+ border-radius: 0.25em;
+ }
+
+ &.oo-ui-widget-enabled {
+ select:hover,
+ select:focus {
+ border-color: rgba(0,0,0,0.2);
+ outline: none;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ select {
+ color: #ccc;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ }
+}
+
+.theme-oo-ui-radioInputWidget () {}
+
+.theme-oo-ui-textInputWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ input,
+ textarea {
+ padding: 0.5em;
+ font-size: inherit;
+ font-family: inherit;
+ background-color: #fff;
+ color: black;
+ border: 1px solid #ccc;
+ box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #ddd;
+ border-radius: 0.25em;
+ .oo-ui-transition(border-color 200ms, box-shadow 200ms);
+ }
+
+ &-decorated {
+ input,
+ textarea {
+ padding-left: 2em;
+ }
+ }
+
+ &-icon {
+ width: 2em;
+ }
+
+ &.oo-ui-widget-enabled {
+ input:focus,
+ textarea:focus {
+ outline: none;
+ border-color: #a7dcff;
+ box-shadow: 0 0 0.3em #a7dcff, 0 0 0 white;
+ }
+
+ input[readonly],
+ textarea[readonly] {
+ color: #777;
+ }
+
+ &.oo-ui-flaggedElement-invalid {
+ input,
+ textarea {
+ background-color: #fdd;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ input,
+ textarea {
+ color: #ccc;
+ text-shadow: 0 1px 1px #fff;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ .oo-ui-labelElement-label {
+ color: #ddd;
+ text-shadow: 0 1px 1px #fff;
+ }
+ }
+
+ &.oo-ui-pendingElement-pending {
+ input,
+ textarea {
+ background-color: transparent;
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+ }
+
+ &.oo-ui-iconElement {
+ input,
+ textarea {
+ padding-left: 2em;
+ }
+
+ .oo-ui-iconElement-icon {
+ width: @icon-size;
+ margin-left: 0.1em;
+ }
+ }
+
+ &.oo-ui-indicatorElement {
+ input,
+ textarea {
+ padding-right: 1.5em;
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ margin-right: 0.775em;
+ }
+ }
+
+ > .oo-ui-labelElement-label {
+ padding: 0.4em;
+ line-height: 1.5em;
+ color: #888;
+ }
+
+ &-labelPosition-after {
+ &.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+ margin-right: 1.6em;
+ }
+ }
+
+ &-labelPosition-before {
+ &.oo-ui-iconElement > .oo-ui-labelElement-label {
+ margin-left: 2.1em;
+ }
+ }
+}
+
+.theme-oo-ui-comboBoxWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ &-handle {
+ border: 1px solid rgba(0,0,0,0.1);
+ border-radius: 0.25em;
+
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+ }
+
+ &.oo-ui-widget-disabled,
+ &-empty {
+ .oo-ui-textInputWidget.oo-ui-indicatorElement {
+ .oo-ui-indicatorElement-indicator {
+ cursor: default;
+ opacity: 0.2;
+ }
+ }
+ }
+
+ > .oo-ui-selectWidget {
+ margin-top: -3px
+ }
+}
+
+.theme-oo-ui-labelWidget () {
+ padding: 0.5em 0;
+}
+
+.theme-oo-ui-optionWidget () {
+ padding: 0.25em 0.5em;
+ border: none;
+
+ &-highlighted {
+ background-color: #e1f3ff;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: 1.5em;
+ }
+
+ .oo-ui-selectWidget-depressed &-selected {
+ background-color: #a7dcff;
+ }
+
+ .oo-ui-selectWidget-pressed &-pressed,
+ .oo-ui-selectWidget-pressed &-pressed&-highlighted,
+ .oo-ui-selectWidget-pressed &-pressed&-highlighted&-selected {
+ background-color: #a7dcff;
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+ }
+}
+
+.theme-oo-ui-decoratedOptionWidget () {
+ padding: 0.5em 2em 0.5em 3em;
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon,
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ top: 0;
+ height: 100%;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ width: @icon-size;
+ left: 0.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ right: 0.5em;
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ }
+}
+
+.theme-oo-ui-buttonOptionWidget () {
+ padding: 0;
+ background-color: transparent;
+
+ .oo-ui-buttonElement-button {
+ height: @icon-size;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ margin-top: 0;
+ }
+
+ &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-pressed,
+ &.oo-ui-optionWidget-highlighted {
+ background-color: transparent;
+ }
+}
+
+.theme-oo-ui-radioOptionWidget () {
+ padding: 0;
+ background-color: transparent;
+
+ &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-pressed,
+ &.oo-ui-optionWidget-highlighted {
+ background-color: transparent;
+ }
+
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ padding-left: 0.5em;
+ }
+
+ .oo-ui-radioInputWidget {
+ margin-right: 0;
+ }
+}
+
+.theme-oo-ui-menuOptionWidget () {
+ &.oo-ui-optionWidget {
+ &-selected {
+ background-color: transparent;
+ }
+ &-highlighted,
+ &-highlighted.oo-ui-optionWidget-selected {
+ background-color: #e1f3ff;
+ }
+ }
+}
+
+.theme-oo-ui-menuSectionOptionWidget () {
+ padding: 0.33em 0.75em;
+ color: #888;
+}
+
+.theme-oo-ui-outlineOptionWidget () {
+ font-size: 1.1em;
+ padding: 0.75em;
+
+ &.oo-ui-indicatorElement .oo-ui-labelElement-label {
+ padding-right: 1.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ opacity: 0.5;
+ }
+
+ &-level-0 {
+ padding-left: 3.5em;
+
+ .oo-ui-iconElement-icon {
+ left: 1em;
+ }
+ }
+ &-level-1 {
+ padding-left: 5em;
+
+ .oo-ui-iconElement-icon {
+ left: 2.5em;
+ }
+ }
+
+ &-level-2 {
+ padding-left: 6.5em;
+
+ .oo-ui-iconElement-icon {
+ left: 4em;
+ }
+ }
+
+ .oo-ui-selectWidget-depressed &.oo-ui-optionWidget-selected {
+ background-color: #a7dcff;
+ text-shadow: 0 1px 1px rgba(255,255,255,0.5);
+ }
+
+ &.oo-ui-flaggedElement-important {
+ font-weight: bold;
+ }
+
+ &.oo-ui-flaggedElement-placeholder {
+ font-style: italic;
+ }
+
+ &.oo-ui-flaggedElement-empty {
+ .oo-ui-iconElement-icon {
+ opacity: 0.5;
+ }
+ .oo-ui-labelElement-label {
+ color: #777;
+ }
+ }
+}
+
+.theme-oo-ui-tabOptionWidget () {
+ padding: 0.5em 1em;
+ margin: 0.5em 0 0 0.75em;
+ border: 1px solid transparent;
+ border-bottom: none;
+ border-top-left-radius: 0.5em;
+ border-top-right-radius: 0.5em;
+
+ &.oo-ui-indicatorElement .oo-ui-labelElement-label {
+ padding-right: 1.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ opacity: 0.5;
+ }
+
+ .oo-ui-selectWidget-pressed &.oo-ui-optionWidget-pressed {
+ background-color: transparent;
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: #ddd;
+ }
+
+ &:active {
+ background-color: #fff;
+ border-color: #ddd;
+ }
+ }
+
+ .oo-ui-selectWidget-pressed &.oo-ui-optionWidget-selected,
+ .oo-ui-selectWidget-depressed &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-selected:hover {
+ background-color: #fff;
+ border-color: #ddd;
+ }
+}
+
+.theme-oo-ui-popupWidget () {
+ &-popup {
+ border: 1px solid #ccc;
+ border-radius: 0.25em;
+ background-color: #fff;
+ box-shadow: 0 0.15em 0.5em 0 rgba(0, 0, 0, 0.2);
+ }
+
+ @anchor-size: 6px;
+
+ &-anchored {
+ .oo-ui-popupWidget-popup {
+ margin-top: @anchor-size;
+ }
+
+ .oo-ui-popupWidget-anchor:before,
+ .oo-ui-popupWidget-anchor:after {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-color: transparent;
+ border-top: 0;
+ }
+
+ .oo-ui-popupWidget-anchor:before {
+ bottom: -@anchor-size - 1px;
+ left: -@anchor-size;
+ border-bottom-color: #aaa;
+ border-width: @anchor-size + 1px;
+ }
+
+ .oo-ui-popupWidget-anchor:after {
+ bottom: -@anchor-size - 1px;
+ left: -@anchor-size + 1px;
+ border-bottom-color: #fff;
+ border-width: @anchor-size;
+ }
+ }
+
+ &-transitioning .oo-ui-popupWidget-popup {
+ .oo-ui-transition(
+ width 100ms ease-in-out, height 100ms ease-in-out, left 100ms ease-in-out
+ );
+ }
+
+ &-head {
+ height: 2.5em;
+
+ .oo-ui-buttonWidget {
+ margin: 0.25em;
+ }
+
+ .oo-ui-labelElement-label {
+ margin: 0.75em 1em;
+ }
+ }
+
+ &-body-padded {
+ padding: 0 1em;
+ }
+}
+
+.theme-oo-ui-searchWidget () {
+ &-query {
+ height: 4em;
+ padding: 0 1em;
+ box-shadow: 0 0 0.5em rgba(0,0,0,0.2);
+
+ .oo-ui-textInputWidget {
+ margin: 0.75em 0;
+ }
+ }
+
+ &-results {
+ top: 4em;
+ padding: 1em;
+ line-height: 0;
+ }
+}
+
+.theme-oo-ui-selectWidget () {}
+
+.theme-oo-ui-buttonSelectWidget () {
+ border-radius: 0.3em;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ .oo-ui-buttonOptionWidget {
+ .oo-ui-buttonElement-button {
+ border-radius: 0;
+ margin-left: -1px;
+ }
+
+ &:first-child .oo-ui-buttonElement-button {
+ border-bottom-left-radius: 0.3em;
+ border-top-left-radius: 0.3em;
+ margin-left: 0;
+ }
+
+ &:last-child .oo-ui-buttonElement-button {
+ border-bottom-right-radius: 0.3em;
+ border-top-right-radius: 0.3em;
+ }
+ }
+}
+
+.theme-oo-ui-radioSelectWidget () {
+ padding: 0.75em 0 0.5em 0;
+}
+
+.theme-oo-ui-menuSelectWidget () {
+ background: #fff;
+ margin-top: -1px;
+ border: 1px solid #ccc;
+ border-radius: 0 0 0.25em 0.25em;
+ box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
+}
+
+.theme-oo-ui-textInputMenuSelectWidget () {}
+
+.theme-oo-ui-outlineSelectWidget () {}
+
+.theme-oo-ui-tabSelectWidget () {
+ background-color: #eee;
+ box-shadow: inset 0 -0.015em 0.1em rgba(0, 0, 0, 0.1);
+}
+
+.theme-oo-ui-toggleSwitchWidget () {
+ @travelDistance: 2em;
+ height: 2em;
+ width: @travelDistance + 2em;
+ border-radius: 1em;
+ box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #ddd;
+ border: 1px solid #ccc;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ .oo-ui-vertical-gradient(#ddd, #fff);
+
+ &.oo-ui-widget-disabled {
+ opacity: 0.5;
+ }
+
+ &-grip {
+ top: 0.25em;
+ left: 0.25em;
+ width: 1.5em;
+ height: 1.5em;
+ margin-top: -1px;
+ border-radius: 1em;
+ box-shadow: 0 0.1em 0.25em rgba(0, 0, 0, 0.1);
+ border: 1px #c9c9c9 solid;
+
+ .oo-ui-transition(left 200ms ease-in-out, margin-left 200ms ease-in-out);
+ .oo-ui-vertical-gradient(#fff, #ddd);
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover,
+ &:hover .oo-ui-toggleSwitchWidget-grip {
+ border-color: #aaa;
+ }
+ }
+
+ .oo-ui-toggleSwitchWidget-glow {
+ border-radius: 1em;
+ box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
+
+ .oo-ui-transition(opacity 200ms ease-in-out);
+ .oo-ui-vertical-gradient(#b0d9ee, #eaf4fa);
+ }
+
+ .oo-ui-toggleWidget-on & {
+ &-glow {
+ opacity: 1;
+ }
+ &-grip {
+ left: @travelDistance + 0.25em;
+ margin-left: -2px;
+ }
+ }
+
+ .oo-ui-toggleWidget-off & {
+ &-glow {
+ display: block;
+ opacity: 0;
+ }
+ &-grip {
+ left: 0.25em;
+ margin-left: 0;
+ }
+ }
+}
+
+.theme-oo-ui-progressBarWidget () {
+ max-width: 50em;
+ border: 1px solid #ccc;
+ border-radius: 0.25em;
+ overflow: hidden;
+
+ &-bar {
+ height: 1em;
+ border-right: 1px solid #ccc;
+ .oo-ui-transition(width 200ms, margin-left 200ms);
+ .oo-ui-vertical-gradient(@progressive-gradient-start, @progressive-gradient-end);
+ }
+ &-indeterminate {
+ .oo-ui-progressBarWidget-bar {
+ .oo-ui-animation(oo-ui-progressBarWidget-slide 2s infinite linear);
+ width: 40%;
+ margin-left: -10%;
+ border-left: 1px solid @progressive-border;
+ }
+ }
+ &.oo-ui-widget-disabled {
+ opacity: 0.6;
+ }
+}
+
+.oo-ui-progressBarWidget-slide-frames () {
+ from { margin-left: -40%; }
+ to { margin-left: 100%; }
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-moz-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-ms-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-o-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
diff --git a/vendor/oojs/oojs-ui/src/themes/apex/windows.less b/vendor/oojs/oojs-ui/src/themes/apex/windows.less
new file mode 100644
index 00000000..0048c7ff
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/apex/windows.less
@@ -0,0 +1,307 @@
+@import 'common';
+
+.theme-oo-ui-window () {
+ background-color: transparent;
+ background-image: none;
+}
+
+.theme-oo-ui-dialog () {
+ &-content > .oo-ui-window-body {
+ box-shadow: 0 0 0.66em rgba(0,0,0,0.25);
+ }
+}
+
+.theme-oo-ui-messageDialog () {
+ &-content {
+ .oo-ui-window-body {
+ box-shadow: 0 0 0.33em rgba(0,0,0,0.33);
+ }
+ }
+
+ &-title,
+ &-message {
+ display: block;
+ text-align: center;
+ padding-top: 0.5em;
+ }
+
+ &-title {
+ font-size: 1.5em;
+ line-height: 1em;
+ color: #000;
+ }
+
+ &-message {
+ font-size: 0.9em;
+ line-height: 1.25em;
+ color: #666;
+
+ &-verbose {
+ font-size: 1.1em;
+ line-height: 1.5em;
+ text-align: left;
+ }
+ }
+
+ &-actions {
+ &-horizontal {
+ .oo-ui-actionWidget {
+ border-right: 1px solid #e5e5e5;
+
+ &:last-child {
+ border-right-width: 0;
+ }
+ }
+ }
+
+ &-vertical {
+ .oo-ui-actionWidget {
+ border-bottom: 1px solid #e5e5e5;
+
+ &:last-child {
+ border-bottom-width: 0;
+ }
+ }
+ }
+
+ .oo-ui-actionWidget {
+ height: 3.4em;
+
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ text-align: center;
+ line-height: 3.4em;
+ padding: 0 2em;
+ }
+
+ &:hover {
+ background-color: rgba(0,0,0,0.05);
+ }
+
+ &:active {
+ background-color: rgba(0,0,0,0.1);
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ &:hover {
+ background-color: rgba(8,126,204,0.05);
+ }
+
+ &:active {
+ background-color: rgba(8,126,204,0.1);
+ }
+
+ .oo-ui-labelElement-label {
+ font-weight: bold;
+ }
+ }
+
+ &-constructive {
+ &:hover {
+ background-color: rgba(118,171,54,0.05);
+ }
+
+ &:active {
+ background-color: rgba(118,171,54,0.1);
+ }
+ }
+
+ &-destructive {
+ &:hover {
+ background-color: rgba(212,83,83,0.05);
+ }
+
+ &:active {
+ background-color: rgba(212,83,83,0.1);
+ }
+ }
+ }
+ }
+ }
+}
+
+.theme-oo-ui-processDialog () {
+ &-content {
+ .oo-ui-window-head {
+ height: 3.4em;
+
+ &.oo-ui-pendingElement-pending {
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+ }
+
+ .oo-ui-window-body {
+ top: 3.4em;
+ box-shadow: 0 0 0.33em rgba(0,0,0,0.33);
+ }
+ }
+
+ &-navigation {
+ position: relative;
+ height: 3.4em;
+ padding: 0 1em;
+ }
+
+ &-location {
+ padding: 0.75em 0;
+ height: @icon-size;
+ cursor: default;
+ text-align: center;
+ }
+
+ &-title {
+ font-weight: bold;
+ line-height: @icon-size;
+ }
+
+ &-actions {
+ &-safe,
+ &-primary,
+ &-other {
+ .oo-ui-actionWidget {
+ .oo-ui-buttonElement-button {
+ min-width: @icon-size;
+ min-height: @icon-size;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: @icon-size;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ margin-top: -0.125em;
+ }
+
+ &.oo-ui-buttonElement-framed {
+ margin: 0.75em 0 0.75em 0.75em;
+ .oo-ui-buttonElement-button {
+ padding: 0 1em;
+ vertical-align: middle;
+ }
+ }
+ }
+ }
+
+ &-safe,
+ &-primary {
+ .oo-ui-actionWidget {
+ &:hover {
+ background-color: rgba(0,0,0,0.05);
+ }
+
+ &:active {
+ background-color: rgba(0,0,0,0.1);
+ }
+
+ &.oo-ui-buttonElement-framed {
+ margin: 0.75em;
+ .oo-ui-buttonElement-button {
+ /* Adjust for border so text aligns with title */
+ margin: -1px;
+ }
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ &:hover {
+ background-color: rgba(8,126,204,0.05);
+ }
+
+ &:active {
+ background-color: rgba(8,126,204,0.1);
+ }
+
+ .oo-ui-labelElement-label {
+ font-weight: bold;
+ }
+ }
+
+ &-constructive {
+ &:hover {
+ background-color: rgba(118,171,54,0.05);
+ }
+
+ &:active {
+ background-color: rgba(118,171,54,0.1);
+ }
+ }
+
+ &-destructive {
+ &:hover {
+ background-color: rgba(212,83,83,0.05);
+ }
+
+ &:active {
+ background-color: rgba(212,83,83,0.1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ > .oo-ui-window-frame {
+ min-height: 5em;
+ }
+
+ &-errors {
+ background-color: rgba(255,255,255,0.9);
+ padding: 3em 3em 1.5em 3em;
+ text-align: center;
+
+ .oo-ui-buttonWidget {
+ margin: 2em 1em 2em 1em;
+ }
+
+ &-title {
+ font-size: 1.5em;
+ color: #000;
+ margin-bottom: 2em;
+ }
+ }
+
+ &-error {
+ text-align: left;
+ margin: 1em;
+ padding: 1em;
+ border: 1px solid #ff9e9e;
+ background-color: #fff7f7;
+ border-radius: 0.25em;
+ }
+}
+
+.theme-oo-ui-windowManager () {
+ &-modal > .oo-ui-dialog {
+ background-color: rgba(255,255,255,0.5);
+ opacity: 0;
+
+ .oo-ui-transition(opacity 250ms ease-in-out);
+
+ > .oo-ui-window-frame {
+ top: 1em;
+ bottom: 1em;
+ background-color: #fff;
+
+ opacity: 0;
+ .oo-ui-transform(scale(0.5));
+ .oo-ui-transition(all 250ms ease-in-out);
+ }
+
+ &.oo-ui-window-ready {
+ /* Fade window overlay */
+ opacity: 1;
+
+ > .oo-ui-window-frame {
+ /* Fade frame */
+ opacity: 1;
+ .oo-ui-transform(scale(1));
+ }
+ }
+ }
+
+ &-modal&-floating > .oo-ui-dialog > .oo-ui-window-frame {
+ border: 1px solid #ccc;
+ border-radius: 0.5em;
+ box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.3);
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/BlankTheme.js b/vendor/oojs/oojs-ui/src/themes/blank/BlankTheme.js
new file mode 100644
index 00000000..208b6a9c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/BlankTheme.js
@@ -0,0 +1,32 @@
+/**
+ * @class
+ * @extends {OO.ui.Theme}
+ *
+ * @constructor
+ */
+OO.ui.BlankTheme = function OoUiBlankTheme() {
+ // Parent constructor
+ OO.ui.BlankTheme.super.call( this );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.BlankTheme, OO.ui.Theme );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.BlankTheme.prototype.getElementClasses = function ( element ) {
+ // Parent method
+ var classes = OO.ui.BlankTheme.super.prototype.getElementClasses.call( this, element );
+
+ // Add classes to classes.on or classes.off
+
+ return classes;
+};
+
+/* Instantiation */
+
+OO.ui.theme = new OO.ui.BlankTheme();
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/common.less b/vendor/oojs/oojs-ui/src/themes/blank/common.less
new file mode 100644
index 00000000..f9ec869a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/common.less
@@ -0,0 +1,10 @@
+// Base variables and mixins
+@import '../../styles/common';
+
+// Theme variables
+
+@oo-ui-default-image-path: 'themes/blank/images';
+
+// Theme mixins
+
+// (add mixins here)
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/core.less b/vendor/oojs/oojs-ui/src/themes/blank/core.less
new file mode 100644
index 00000000..1c83a946
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/core.less
@@ -0,0 +1,12 @@
+// Base and theme variables and mixins
+@import 'common';
+
+// Theme rules
+@import 'elements';
+@import 'layouts';
+@import 'tools';
+@import 'widgets';
+@import 'windows';
+
+// Base rules
+@import '../../styles/core';
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/elements.less b/vendor/oojs/oojs-ui/src/themes/blank/elements.less
new file mode 100644
index 00000000..89b96648
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/elements.less
@@ -0,0 +1,29 @@
+@import 'common';
+
+.theme-oo-ui-element () {}
+
+.theme-oo-ui-buttonElement () {}
+
+.theme-oo-ui-clippableElement () {}
+
+.theme-oo-ui-draggableElement () {}
+
+.theme-oo-ui-flaggedElement () {}
+
+.theme-oo-ui-groupElement () {}
+
+.theme-oo-ui-draggableGroupElement () {}
+
+.theme-oo-ui-iconElement () {}
+
+.theme-oo-ui-indicatorElement () {}
+
+.theme-oo-ui-labelElement () {}
+
+.theme-oo-ui-lookupElement () {}
+
+.theme-oo-ui-popupElement () {}
+
+.theme-oo-ui-tabIndexedElement () {}
+
+.theme-oo-ui-titledElement () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/images/icons/README.txt b/vendor/oojs/oojs-ui/src/themes/blank/images/icons/README.txt
new file mode 100644
index 00000000..129fbed2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/images/icons/README.txt
@@ -0,0 +1 @@
+Remove this file and add icon images here \ No newline at end of file
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/images/indicators/README.txt b/vendor/oojs/oojs-ui/src/themes/blank/images/indicators/README.txt
new file mode 100644
index 00000000..9ff30ec2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/images/indicators/README.txt
@@ -0,0 +1 @@
+Remove this file and add indicator images here \ No newline at end of file
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/images/textures/README.txt b/vendor/oojs/oojs-ui/src/themes/blank/images/textures/README.txt
new file mode 100644
index 00000000..6ca283de
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/images/textures/README.txt
@@ -0,0 +1 @@
+Remove this file and add texture images here \ No newline at end of file
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/layouts.less b/vendor/oojs/oojs-ui/src/themes/blank/layouts.less
new file mode 100644
index 00000000..93f677c7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/layouts.less
@@ -0,0 +1,26 @@
+@import 'common';
+
+.theme-oo-ui-layout () {}
+
+.theme-oo-ui-bookletLayout () {}
+
+.theme-oo-ui-indexLayout () {}
+
+.theme-oo-ui-fieldLayout () {}
+
+.theme-oo-ui-actionFieldLayout () {}
+
+.theme-oo-ui-fieldsetLayout () {}
+
+.theme-oo-ui-formLayout () {}
+
+.theme-oo-ui-menuLayout () {}
+
+.theme-oo-ui-panelLayout () {}
+
+.theme-oo-ui-cardLayout () {}
+
+.theme-oo-ui-pageLayout () {}
+
+.theme-oo-ui-stackLayout () {}
+
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/tools.less b/vendor/oojs/oojs-ui/src/themes/blank/tools.less
new file mode 100644
index 00000000..923b0ad6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/tools.less
@@ -0,0 +1,20 @@
+@import 'common';
+
+.theme-oo-ui-toolbar () {}
+
+.theme-oo-ui-tool () {}
+
+.theme-oo-ui-popupTool () {}
+
+.theme-oo-ui-toolGroupTool () {}
+
+.theme-oo-ui-toolGroup () {}
+
+.theme-oo-ui-barToolGroup () {}
+
+.theme-oo-ui-popupToolGroup () {}
+
+.theme-oo-ui-listToolGroup () {}
+
+.theme-oo-ui-menuToolGroup () {}
+
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/widgets.less b/vendor/oojs/oojs-ui/src/themes/blank/widgets.less
new file mode 100644
index 00000000..68c64cd8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/widgets.less
@@ -0,0 +1,77 @@
+@import 'common';
+
+.theme-oo-ui-widget () {}
+
+.theme-oo-ui-outlineControlsWidget () {}
+
+.theme-oo-ui-toggleWidget () {}
+
+.theme-oo-ui-buttonGroupWidget () {}
+
+.theme-oo-ui-buttonWidget () {}
+
+.theme-oo-ui-actionWidget () {}
+
+.theme-oo-ui-popupButtonWidget () {}
+
+.theme-oo-ui-toggleButtonWidget () {}
+
+.theme-oo-ui-iconWidget () {}
+
+.theme-oo-ui-indicatorWidget () {}
+
+.theme-oo-ui-dropdownWidget () {}
+
+.theme-oo-ui-inputWidget () {}
+
+.theme-oo-ui-buttonInputWidget () {}
+
+.theme-oo-ui-checkboxInputWidget () {}
+
+.theme-oo-ui-dropdownInputWidget () {}
+
+.theme-oo-ui-radioInputWidget () {}
+
+.theme-oo-ui-textInputWidget () {}
+
+.theme-oo-ui-comboBoxWidget () {}
+
+.theme-oo-ui-labelWidget () {}
+
+.theme-oo-ui-optionWidget () {}
+
+.theme-oo-ui-decoratedOptionWidget () {}
+
+.theme-oo-ui-buttonOptionWidget () {}
+
+.theme-oo-ui-radioOptionWidget () {}
+
+.theme-oo-ui-menuOptionWidget () {}
+
+.theme-oo-ui-menuSectionOptionWidget () {}
+
+.theme-oo-ui-outlineOptionWidget () {}
+
+.theme-oo-ui-tabOptionWidget () {}
+
+.theme-oo-ui-popupWidget () {}
+
+.theme-oo-ui-searchWidget () {}
+
+.theme-oo-ui-selectWidget () {}
+
+.theme-oo-ui-buttonSelectWidget () {}
+
+.theme-oo-ui-radioSelectWidget () {}
+
+.theme-oo-ui-menuSelectWidget () {}
+
+.theme-oo-ui-textInputMenuSelectWidget () {}
+
+.theme-oo-ui-outlineSelectWidget () {}
+
+.theme-oo-ui-tabSelectWidget () {}
+
+.theme-oo-ui-toggleSwitchWidget () {}
+
+.theme-oo-ui-progressBarWidget () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/blank/windows.less b/vendor/oojs/oojs-ui/src/themes/blank/windows.less
new file mode 100644
index 00000000..35c0b40a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/blank/windows.less
@@ -0,0 +1,11 @@
+@import 'common';
+
+.theme-oo-ui-window () {}
+
+.theme-oo-ui-dialog () {}
+
+.theme-oo-ui-messageDialog () {}
+
+.theme-oo-ui-processDialog () {}
+
+.theme-oo-ui-windowManager () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/MediaWikiTheme.js b/vendor/oojs/oojs-ui/src/themes/mediawiki/MediaWikiTheme.js
new file mode 100644
index 00000000..be2371bd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/MediaWikiTheme.js
@@ -0,0 +1,56 @@
+/**
+ * @class
+ * @extends OO.ui.Theme
+ *
+ * @constructor
+ */
+OO.ui.MediaWikiTheme = function OoUiMediaWikiTheme() {
+ // Parent constructor
+ OO.ui.MediaWikiTheme.super.call( this );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MediaWikiTheme, OO.ui.Theme );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
+ // Parent method
+ var variant,
+ variants = {
+ warning: false,
+ invert: false,
+ progressive: false,
+ constructive: false,
+ destructive: false
+ },
+ // Parent method
+ classes = OO.ui.MediaWikiTheme.super.prototype.getElementClasses.call( this, element ),
+ isFramed;
+
+ if ( element.supports( [ 'hasFlag' ] ) ) {
+ isFramed = element.supports( [ 'isFramed' ] ) && element.isFramed();
+ if ( isFramed && ( element.isDisabled() || element.hasFlag( 'primary' ) ) ) {
+ variants.invert = true;
+ } else {
+ variants.progressive = element.hasFlag( 'progressive' );
+ variants.constructive = element.hasFlag( 'constructive' );
+ variants.destructive = element.hasFlag( 'destructive' );
+ variants.warning = element.hasFlag( 'warning' );
+ }
+ }
+
+ for ( variant in variants ) {
+ classes[ variants[ variant ] ? 'on' : 'off' ].push( 'oo-ui-image-' + variant );
+ }
+
+ return classes;
+};
+
+/* Instantiation */
+
+OO.ui.theme = new OO.ui.MediaWikiTheme();
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/common.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/common.less
new file mode 100644
index 00000000..ab63c022
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/common.less
@@ -0,0 +1,69 @@
+// Base variables and mixins
+@import '../../styles/common';
+
+// Theme variables
+
+@active: #999;
+@background: #ffffff;
+
+@select: #d8e6fe;
+
+@progressive: #347bff;
+@progressive-hover: #2962CC;
+@progressive-selected: #1F4999;
+@progressive-fade: rgba(52,123,255,0.1);
+
+@constructive: #00af89;
+@constructive-hover: #008064;
+@constructive-selected: #005946;
+@constructive-fade: rgba(0,171,137,0.1);
+
+@destructive: #d11d13;
+@destructive-hover: #8C130D;
+@destructive-selected: #73100A;
+@destructive-fade: rgba(209,29,19,0.1);
+
+@text: #555555;
+@pressed-text: #444444;
+@pressed-color: #d0d0d0; // Used for borders and backgrounds
+@disabled-text: #cccccc;
+@disabled-framed-text: #ffffff;
+@disabled-background: #dddddd;
+
+@neutral-button-border: 1px solid #cdcdcd;
+
+@oo-ui-default-image-path: 'themes/mediawiki/images';
+
+@input-border-color: #777;
+@input-active-color: #ddd;
+@input-disabled-color: #eee;
+@input-hover-border-bottom-width: 3px;
+@input-focus-border-width: 2px;
+@input-size: 1.6em;
+@border-radius: 2px;
+
+@icon-size: unit(24 / 16 / 0.8, em);
+@indicator-size: unit(12 / 16 / 0.8, em);
+
+// Theme animation variables
+@quick-ease: 0.1s ease-in-out;
+@medium-ease-out-back: 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+@medium-ease-out-sine: 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+
+// Theme mixins
+
+// Workaround for Safari 8 bug. Combining a selector like `input[type="checkbox"]:checked + span`
+// with transition on background-size, background-color, and a single background-image using SVG
+// causes the selector to sometimes not be applied. (T89309)
+//
+// * Syntax mimics the core mixin .oo-ui-background-image-svg().
+// * No-op in distributions other than 'vector'.
+// * Using -webkit- prefix to limit this stupidity from impacting other browsers. Alas, some
+// non-Safari ones also parse the -webkit- prefix (Chrome, Opera).
+// * We take the payload size hit of the unnecessary /* @embed */ hint across the board. It should
+// be mostly mitigated by using gzip compression.
+// * Upstream bug report: https://bugs.webkit.org/show_bug.cgi?id=141789
+.oo-ui-background-image-safari( @url-without-extension ) when ( @oo-ui-distribution = vector ) {
+ @svg: '@{url-without-extension}.svg';
+ background-image: -webkit-linear-gradient(transparent, transparent), e('/* @embed */') url(@svg);
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/core.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/core.less
new file mode 100644
index 00000000..1c83a946
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/core.less
@@ -0,0 +1,12 @@
+// Base and theme variables and mixins
+@import 'common';
+
+// Theme rules
+@import 'elements';
+@import 'layouts';
+@import 'tools';
+@import 'widgets';
+@import 'windows';
+
+// Base rules
+@import '../../styles/core';
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/elements.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/elements.less
new file mode 100644
index 00000000..535c0251
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/elements.less
@@ -0,0 +1,319 @@
+@import 'common';
+
+.theme-oo-ui-element () {}
+
+.theme-oo-ui-buttonElement () {
+ > .oo-ui-buttonElement-button {
+ font-weight: bold;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button {
+ > .oo-ui-iconElement-icon {
+ margin-left: 0;
+ }
+ }
+
+ &.oo-ui-indicatorElement > .oo-ui-buttonElement-button {
+ > .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ height: @indicator-size;
+ margin: @indicator-size / 2;
+ }
+ }
+
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ margin-left: @indicator-size / 2;
+ }
+ &.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ width: @icon-size;
+ height: @icon-size;
+ }
+
+ &-frameless {
+ > .oo-ui-buttonElement-button {
+ &:focus {
+ box-shadow: inset 0 0 0 1px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.2);
+ outline: none;
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ margin-right: 0em;
+ }
+ }
+
+ &.oo-ui-labelElement {
+ > .oo-ui-buttonElement-button {
+ > .oo-ui-labelElement-label {
+ margin-left: 0.25em;
+ margin-right: 0.25em;
+ }
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @text;
+ }
+
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @pressed-text;
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ .mediawiki-frameless-button-colored(@progressive, @progressive-selected);
+ }
+
+ &-constructive {
+ .mediawiki-frameless-button-colored(@constructive, @constructive-selected);
+ }
+
+ &-destructive {
+ .mediawiki-frameless-button-colored(@destructive, @destructive-selected);
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+ color: @disabled-text;
+
+ > .oo-ui-iconElement-icon,
+ > .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ }
+ }
+
+ &-framed {
+ > .oo-ui-buttonElement-button {
+ margin: 0.1em 0;
+ padding: 0.2em 0.8em;
+ border-radius: @border-radius;
+
+ &:hover,
+ &:focus {
+ outline: none;
+ }
+
+ .oo-ui-transition(
+ background @quick-ease,
+ color @quick-ease,
+ box-shadow @quick-ease
+ );
+ }
+
+ // Support <input/> from ButtonInputWidget
+ > input.oo-ui-buttonElement-button,
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ line-height: @icon-size;
+ }
+
+ &.oo-ui-iconElement {
+ > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ margin-left: -0.5em;
+ margin-right: -0.5em;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+ margin-right: 0.3em;
+ }
+ }
+
+ &.oo-ui-indicatorElement {
+ > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ /* -0.5 - 0.475 */
+ margin-left: -0.005em;
+ margin-right: -0.005em;
+ }
+
+ &.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+ &.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+ margin-left: @indicator-size / 2;
+ margin-right: -0.275em;
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+ color: @disabled-framed-text;
+ background: @disabled-background;
+ border: 1px solid @disabled-background;
+ }
+
+ &.oo-ui-widget-enabled {
+ > .oo-ui-buttonElement-button {
+ color: @text;
+ background-color: @background;
+ border: @neutral-button-border;
+
+ &:hover {
+ background-color: darken(@background,8%);
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px rgba(0,0,0,0.2);
+ }
+ }
+
+ & > .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ background-color: darken(@background,15%);
+ border-color: darken(@background,15%);
+ box-shadow: none;
+ }
+
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+ background-color: @active;
+ color: #fff;
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ .mediawiki-framed-button-colored(@progressive, @progressive-fade, @progressive-selected);
+ }
+
+ &-constructive {
+ .mediawiki-framed-button-colored(@constructive, @constructive-fade, @constructive-selected);
+ }
+
+ &-destructive {
+ .mediawiki-framed-button-colored(@destructive, @destructive-fade, @destructive-selected);
+ }
+ }
+ &.oo-ui-flaggedElement-primary.oo-ui-flaggedElement {
+ &-progressive {
+ .mediawiki-framed-primary-button-colored(@progressive, @progressive-hover, @progressive-selected);
+ }
+
+ &-constructive {
+ .mediawiki-framed-primary-button-colored(@constructive, @constructive-hover, @constructive-selected);
+ }
+
+ &-destructive {
+ .mediawiki-framed-primary-button-colored(@destructive, @destructive-hover, @destructive-selected);
+ }
+ }
+ }
+ }
+}
+
+.mediawiki-frameless-button-colored( @neutral, @pressed ) {
+ > .oo-ui-buttonElement-button {
+ &:hover,
+ &:focus {
+ > .oo-ui-labelElement-label {
+ color: @neutral;
+ }
+ }
+
+ > .oo-ui-labelElement-label {
+ color: #777777;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ & > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+ color: @pressed;
+ box-shadow: none;
+ }
+ }
+}
+
+.mediawiki-framed-button-colored( @neutral, @hover, @pressed ) {
+ > .oo-ui-buttonElement-button {
+ color: @neutral;
+
+ &:hover {
+ background-color: @hover;
+ border-color: fade(@pressed,50%);
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px @pressed;
+ border-color: @pressed;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ color: @pressed;
+ border-color: @pressed;
+ box-shadow: none;
+ }
+
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+ background-color: @active;
+ color: #fff;
+ }
+ }
+}
+
+.mediawiki-framed-primary-button-colored( @neutral, @hover, @pressed ) {
+ > .oo-ui-buttonElement-button {
+ color: @background;
+ background-color: @neutral;
+ border-color: @neutral;
+
+ &:hover {
+ background: @hover;
+ border-color: @hover;
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px @background;
+ border-color: @neutral;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ .oo-ui-buttonElement-button:active,
+ &.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+ color: @background;
+ background-color: @pressed;
+ border-color: @pressed;
+ box-shadow: none;
+ }
+
+ &.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+ background-color: @active;
+ color: #fff;
+ }
+ }
+}
+
+.theme-oo-ui-clippableElement () {}
+
+.theme-oo-ui-flaggedElement () {}
+
+.theme-oo-ui-draggableElement () {}
+
+.theme-oo-ui-groupElement () {}
+
+.theme-oo-ui-draggableGroupElement () {}
+
+.theme-oo-ui-iconElement () {
+ .oo-ui-iconElement-icon,
+ &.oo-ui-iconElement-icon {
+ background-size: contain;
+ background-position: center center;
+ }
+}
+
+.theme-oo-ui-indicatorElement () {
+ .oo-ui-indicatorElement-indicator,
+ &.oo-ui-indicatorElement-indicator {
+ background-size: contain;
+ background-position: center center;
+ }
+}
+
+.theme-oo-ui-labelElement () {}
+
+.theme-oo-ui-lookupElement () {}
+
+.theme-oo-ui-popupElement () {}
+
+.theme-oo-ui-tabIndexedElement () {}
+
+.theme-oo-ui-titledElement () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-alerts.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-alerts.json
new file mode 100644
index 00000000..5fbf34d0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-alerts.json
@@ -0,0 +1,33 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "bell": { "file": "images/icons/bell.svg" },
+ "bellOn": { "file": {
+ "ltr": "images/icons/bellOn-ltr.svg",
+ "rtl": "images/icons/bellOn-rtl.svg"
+ } },
+ "eye": { "file": "images/icons/eye.svg" },
+ "eyeClosed": { "file": "images/icons/eyeClosed.svg" },
+ "message": { "file": {
+ "ltr": "images/icons/message-ltr.svg",
+ "rtl": "images/icons/message-rtl.svg"
+ } },
+ "signature": { "file": {
+ "ltr": "images/icons/signature-ltr.svg",
+ "rtl": "images/icons/signature-rtl.svg"
+ } },
+ "speechBubble": { "file": {
+ "ltr": "images/icons/speechBubble-ltr.svg",
+ "rtl": "images/icons/speechBubble-rtl.svg"
+ } },
+ "speechBubbleAdd": { "file": {
+ "ltr": "images/icons/speechBubbleAdd-ltr.svg",
+ "rtl": "images/icons/speechBubbleAdd-rtl.svg"
+ } },
+ "speechBubbles": { "file": {
+ "ltr": "images/icons/speechBubbles-ltr.svg",
+ "rtl": "images/icons/speechBubbles-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-content.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-content.json
new file mode 100644
index 00000000..29681c0e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-content.json
@@ -0,0 +1,50 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "article": { "file": {
+ "ltr": "images/icons/article-ltr.svg",
+ "rtl": "images/icons/article-rtl.svg"
+ } },
+ "articleCheck": { "file": {
+ "ltr": "images/icons/articleCheck-ltr.svg",
+ "rtl": "images/icons/articleCheck-rtl.svg"
+ } },
+ "articleSearch": { "file": {
+ "ltr": "images/icons/articleSearch-ltr.svg",
+ "rtl": "images/icons/articleSearch-rtl.svg"
+ } },
+ "book": { "file": {
+ "ltr": "images/icons/book-ltr.svg",
+ "rtl": "images/icons/book-rtl.svg"
+ } },
+ "citeArticle": { "file": {
+ "ltr": "images/icons/citeArticle-ltr.svg",
+ "rtl": "images/icons/citeArticle-rtl.svg"
+ } },
+ "die": { "file": {
+ "ltr": "images/icons/die-ltr.svg",
+ "rtl": "images/icons/die-rtl.svg"
+ } },
+ "download": { "file": {
+ "ltr": "images/icons/download-ltr.svg",
+ "rtl": "images/icons/download-rtl.svg"
+ } },
+ "folderPlaceholder": { "file": {
+ "ltr": "images/icons/folderPlaceholder-ltr.svg",
+ "rtl": "images/icons/folderPlaceholder-rtl.svg"
+ } },
+ "journal": { "file": {
+ "ltr": "images/icons/journal-ltr.svg",
+ "rtl": "images/icons/journal-rtl.svg"
+ } },
+ "newspaper": { "file": {
+ "ltr": "images/icons/newspaper-ltr.svg",
+ "rtl": "images/icons/newspaper-rtl.svg"
+ } },
+ "upload": { "file": {
+ "ltr": "images/icons/upload-ltr.svg",
+ "rtl": "images/icons/upload-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-advanced.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-advanced.json
new file mode 100644
index 00000000..8fdc5051
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-advanced.json
@@ -0,0 +1,75 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "alignCentre": { "file": "images/icons/align-center.svg" },
+ "alignLeft": { "file": "images/icons/align-float-left.svg" },
+ "alignRight": { "file": "images/icons/align-float-right.svg" },
+ "find": { "file": {
+ "ltr": "images/icons/find-ltr.svg",
+ "rtl": "images/icons/find-rtl.svg"
+ } },
+ "insert": { "file": "images/icons/insert.svg" },
+ "layout": { "file": {
+ "ltr": "images/icons/layout-ltr.svg",
+ "rtl": "images/icons/layout-rtl.svg"
+ } },
+ "newline": { "file": {
+ "ltr": "images/icons/newline-ltr.svg",
+ "rtl": "images/icons/newline-rtl.svg"
+ } },
+ "redirect": { "file": {
+ "ltr": "images/icons/redirect-ltr.svg",
+ "rtl": "images/icons/redirect-rtl.svg"
+ } },
+ "noWikiText": { "file": {
+ "ltr": "images/icons/noWikiText-ltr.svg",
+ "rtl": "images/icons/noWikiText-rtl.svg"
+ } },
+ "outline": { "file": {
+ "ltr": "images/icons/outline-ltr.svg",
+ "rtl": "images/icons/outline-rtl.svg"
+ } },
+ "puzzle": { "file": {
+ "ltr": "images/icons/puzzle-ltr.svg",
+ "rtl": "images/icons/puzzle-rtl.svg"
+ } },
+ "quotes": { "file": {
+ "ltr": "images/icons/quotes-ltr.svg",
+ "rtl": "images/icons/quotes-rtl.svg"
+ } },
+ "quotesAdd": { "file": {
+ "ltr": "images/icons/quotesAdd-ltr.svg",
+ "rtl": "images/icons/quotesAdd-rtl.svg"
+ } },
+ "redirect": { "file": {
+ "ltr": "images/icons/redirect-ltr.svg",
+ "rtl": "images/icons/redirect-rtl.svg"
+ } },
+ "searchCaseSensitive": { "file": "images/icons/case-sensitive.svg" },
+ "searchRegularExpression": { "file": "images/icons/regular-expression.svg" },
+ "specialCharacter": { "file": "images/icons/specialCharacter.svg" },
+ "table": { "file": "images/icons/table.svg" },
+ "tableAddColumnAfter": { "file": {
+ "ltr": "images/icons/table-insert-column-rtl.svg",
+ "rtl": "images/icons/table-insert-column-ltr.svg"
+ } },
+ "tableAddColumnBefore": { "file": {
+ "ltr": "images/icons/table-insert-column-ltr.svg",
+ "rtl": "images/icons/table-insert-column-rtl.svg"
+ } },
+ "tableAddRowAfter": { "file": "images/icons/table-insert-row-after.svg" },
+ "tableAddRowBefore": { "file": "images/icons/table-insert-row-before.svg" },
+ "tableCaption": { "file": "images/icons/table-caption.svg" },
+ "tableMergeCells": { "file": "images/icons/table-merge-cells.svg" },
+ "templateAdd": { "file": {
+ "ltr": "images/icons/templateAdd-ltr.svg",
+ "rtl": "images/icons/templateAdd-rtl.svg"
+ } },
+ "translation": { "file": {
+ "ltr": "images/icons/translation-ltr.svg",
+ "rtl": "images/icons/translation-rtl.svg"
+ } },
+ "wikiText": { "file": "images/icons/wikiText.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-core.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-core.json
new file mode 100644
index 00000000..3bacb605
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-core.json
@@ -0,0 +1,45 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "variants": {
+ "invert": {
+ "color": "#FFFFFF",
+ "global": true
+ },
+ "progressive": {
+ "color": "#347BFF"
+ },
+ "constructive": {
+ "color": "#00AF89"
+ },
+ "destructive": {
+ "color": "#D11D13"
+ },
+ "warning": {
+ "color": "#FF5D00"
+ }
+ },
+ "images": {
+ "edit": { "file": {
+ "ltr": "images/icons/edit-ltr.svg",
+ "rtl": "images/icons/edit-rtl.svg"
+ }, "variants": [ "progressive" ] },
+ "editLock": { "file": {
+ "ltr": "images/icons/editLock-ltr.svg",
+ "rtl": "images/icons/editLock-rtl.svg"
+ } },
+ "editUndo": { "file": {
+ "ltr": "images/icons/editUndo-ltr.svg",
+ "rtl": "images/icons/editUndo-rtl.svg"
+ } },
+ "link": { "file": {
+ "ltr": "images/icons/link-ltr.svg",
+ "rtl": "images/icons/link-rtl.svg"
+ } },
+ "linkExternal": { "file": {
+ "ltr": "images/icons/external-link-ltr.svg",
+ "rtl": "images/icons/external-link-rtl.svg"
+ } },
+ "linkSecure": { "file": "images/icons/secure-link.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-list.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-list.json
new file mode 100644
index 00000000..490f8faf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-list.json
@@ -0,0 +1,22 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "indent": { "file": {
+ "ltr": "images/icons/indent-ltr.svg",
+ "rtl": "images/icons/indent-rtl.svg"
+ } },
+ "listBullet": { "file": {
+ "ltr": "images/icons/listBullet-ltr.svg",
+ "rtl": "images/icons/listBullet-rtl.svg"
+ } },
+ "listNumbered": { "file": {
+ "ltr": "images/icons/listNumbered-ltr.svg",
+ "rtl": "images/icons/listNumbered-rtl.svg"
+ } },
+ "outdent": { "file": {
+ "ltr": "images/icons/outdent-ltr.svg",
+ "rtl": "images/icons/outdent-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-styling.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-styling.json
new file mode 100644
index 00000000..65fbc218
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-editing-styling.json
@@ -0,0 +1,72 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "bigger": { "file": {
+ "ltr": "images/icons/bigger-ltr.svg",
+ "rtl": "images/icons/bigger-rtl.svg"
+ } },
+ "smaller": { "file": {
+ "ltr": "images/icons/smaller-ltr.svg",
+ "rtl": "images/icons/smaller-rtl.svg"
+ } },
+ "subscript": { "file": {
+ "ltr": "images/icons/subscript-ltr.svg",
+ "rtl": "images/icons/subscript-rtl.svg"
+ } },
+ "superscript": { "file": {
+ "ltr": "images/icons/superscript-ltr.svg",
+ "rtl": "images/icons/superscript-rtl.svg"
+ } },
+ "bold": { "file": {
+ "default": "images/icons/bold-a.svg",
+ "lang": {
+ "ar": "images/icons/bold-arab-ain.svg",
+ "be": "images/icons/bold-cyrl-te.svg",
+ "cs,en,he,ml,pl": "images/icons/bold-b.svg",
+ "da,de,hu,ksh,nn,no,sv": "images/icons/bold-f.svg",
+ "es,gl,pt": "images/icons/bold-n.svg",
+ "eu,fi": "images/icons/bold-l.svg",
+ "fa": "images/icons/bold-arab-dad.svg",
+ "fr,it": "images/icons/bold-g.svg",
+ "hy": "images/icons/bold-armn-to.svg",
+ "ka": "images/icons/bold-geor-man.svg",
+ "ky,ru": "images/icons/bold-cyrl-zhe.svg",
+ "nl": "images/icons/bold-v.svg",
+ "os": "images/icons/bold-cyrl-be.svg"
+ }
+ } },
+ "italic": { "file": {
+ "default": "images/icons/italic-a.svg",
+ "lang": {
+ "ar": "images/icons/italic-arab-meem.svg",
+ "cs,en,fr,he,ml,pl,pt": "images/icons/italic-i.svg",
+ "be,da,de,fi,ky,nn,no,os,sv,ru": "images/icons/italic-k.svg",
+ "es,gl,it,nl": "images/icons/italic-c.svg",
+ "eu": "images/icons/italic-e.svg",
+ "fa": "images/icons/italic-arab-keheh-jeem.svg",
+ "hu": "images/icons/italic-d.svg",
+ "hy": "images/icons/italic-armn-sha.svg",
+ "ksh": "images/icons/italic-s.svg",
+ "ka": "images/icons/italic-geor-kan.svg"
+ }
+ } },
+ "strikethrough": { "file": {
+ "default": "images/icons/strikethrough-a.svg",
+ "lang": {
+ "en": "images/icons/strikethrough-s.svg",
+ "fi": "images/icons/strikethrough-y.svg"
+ }
+ } },
+ "underline": { "file": {
+ "default": "images/icons/underline-a.svg",
+ "lang": {
+ "en": "images/icons/underline-u.svg"
+ }
+ } },
+ "textLanguage": { "file": "images/icons/language.svg" },
+ "textDirLTR": { "file": "images/icons/text-dir-lefttoright.svg" },
+ "textDirRTL": { "file": "images/icons/text-dir-righttoleft.svg" },
+ "textStyle": { "file": "images/icons/text-style.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-interactions.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-interactions.json
new file mode 100644
index 00000000..497a3014
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-interactions.json
@@ -0,0 +1,52 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "beta": { "file": "images/icons/beta.svg" },
+ "betaLaunch": { "file": "images/icons/betaLaunch.svg" },
+ "bookmark": { "file": {
+ "ltr": "images/icons/bookmark-ltr.svg",
+ "rtl": "images/icons/bookmark-rtl.svg"
+ } },
+ "browser": { "file": {
+ "ltr": "images/icons/browser-ltr.svg",
+ "rtl": "images/icons/browser-rtl.svg"
+ } },
+ "clear": { "file": "images/icons/clear.svg" },
+ "clock": { "file": "images/icons/clock.svg" },
+ "funnel": { "file": {
+ "ltr": "images/icons/funnel-ltr.svg",
+ "rtl": "images/icons/funnel-rtl.svg"
+ } },
+ "heart": { "file": "images/icons/heart.svg" },
+ "key": { "file": {
+ "ltr": "images/icons/key-ltr.svg",
+ "rtl": "images/icons/key-rtl.svg"
+ } },
+ "keyboard": { "file": {
+ "ltr": "images/icons/keyboard-ltr.svg",
+ "rtl": "images/icons/keyboard-rtl.svg"
+ } },
+ "logOut": { "file": {
+ "ltr": "images/icons/logOut-ltr.svg",
+ "rtl": "images/icons/logOut-rtl.svg"
+ } },
+ "newWindow": { "file": {
+ "ltr": "images/icons/newWindow-ltr.svg",
+ "rtl": "images/icons/newWindow-rtl.svg"
+ } },
+ "printer": { "file": {
+ "ltr": "images/icons/printer-ltr.svg",
+ "rtl": "images/icons/printer-rtl.svg"
+ } },
+ "ribbonPrize": { "file": "images/icons/ribbonPrize.svg" },
+ "sun": { "file": {
+ "ltr": "images/icons/sun-ltr.svg",
+ "rtl": "images/icons/sun-rtl.svg"
+ } },
+ "watchlist": { "file": {
+ "ltr": "images/icons/watchlist-ltr.svg",
+ "rtl": "images/icons/watchlist-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-layout.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-layout.json
new file mode 100644
index 00000000..ae6b09d7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-layout.json
@@ -0,0 +1,43 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "variants": {
+ "invert": {
+ "color": "#FFFFFF",
+ "global": true
+ },
+ "progressive": {
+ "color": "#347BFF"
+ },
+ "constructive": {
+ "color": "#00AF89"
+ },
+ "destructive": {
+ "color": "#D11D13"
+ },
+ "warning": {
+ "color": "#FF5D00"
+ }
+ },
+ "images": {
+ "stripeFlow": { "file": {
+ "ltr": "images/icons/stripeFlow-ltr.svg",
+ "rtl": "images/icons/stripeFlow-rtl.svg"
+ } },
+ "stripeSideMenu": { "file": "images/icons/stripeSideMenu.svg" },
+ "stripeSummary": { "file": {
+ "ltr": "images/icons/stripeSummary-ltr.svg",
+ "rtl": "images/icons/stripeSummary-rtl.svg"
+ } },
+ "stripeToC": { "file": {
+ "ltr": "images/icons/stripeToC-ltr.svg",
+ "rtl": "images/icons/stripeToC-rtl.svg"
+ }, "variants": [ "progressive" ] },
+ "viewCompact": { "file": "images/icons/viewCompact.svg" },
+ "viewDetails": { "file": {
+ "ltr": "images/icons/viewDetails-ltr.svg",
+ "rtl": "images/icons/viewDetails-rtl.svg"
+ } },
+ "visionSimulator": { "file": "images/icons/visionSimulator.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-location.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-location.json
new file mode 100644
index 00000000..9eec19ba
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-location.json
@@ -0,0 +1,19 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "map": { "file": {
+ "ltr": "images/icons/map-ltr.svg",
+ "rtl": "images/icons/map-rtl.svg"
+ } },
+ "mapPin": { "file": "images/icons/mapPin.svg" },
+ "mapPinAdd": { "file": {
+ "ltr": "images/icons/mapPinAdd-ltr.svg",
+ "rtl": "images/icons/mapPinAdd-rtl.svg"
+ } },
+ "wikitrail": { "file": {
+ "ltr": "images/icons/wikitrail-ltr.svg",
+ "rtl": "images/icons/wikitrail-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-media.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-media.json
new file mode 100644
index 00000000..960079b4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-media.json
@@ -0,0 +1,27 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "image": { "file": {
+ "ltr": "images/icons/image-ltr.svg",
+ "rtl": "images/icons/image-rtl.svg"
+ } },
+ "imageAdd": { "file": {
+ "ltr": "images/icons/imageAdd-ltr.svg",
+ "rtl": "images/icons/imageAdd-rtl.svg"
+ } },
+ "imageLock": { "file": {
+ "ltr": "images/icons/imageLock-ltr.svg",
+ "rtl": "images/icons/imageLock-rtl.svg"
+ } },
+ "photoGallery": { "file": {
+ "ltr": "images/icons/photoGallery-ltr.svg",
+ "rtl": "images/icons/photoGallery-rtl.svg"
+ } },
+ "play": { "file": {
+ "ltr": "images/icons/play-ltr.svg",
+ "rtl": "images/icons/play-rtl.svg"
+ } },
+ "stop": { "file": "images/icons/stop.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-moderation.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-moderation.json
new file mode 100644
index 00000000..1f12f2ae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-moderation.json
@@ -0,0 +1,52 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "variants": {
+ "invert": {
+ "color": "#FFFFFF",
+ "global": true
+ },
+ "progressive": {
+ "color": "#347BFF"
+ },
+ "constructive": {
+ "color": "#00AF89"
+ },
+ "destructive": {
+ "color": "#D11D13"
+ },
+ "warning": {
+ "color": "#FF5D00"
+ }
+ },
+ "images": {
+ "block": { "file": "images/icons/block.svg", "variants": [ "destructive" ] },
+ "blockUndo": { "file": {
+ "ltr": "images/icons/blockUndo-ltr.svg",
+ "rtl": "images/icons/blockUndo-rtl.svg"
+ } },
+ "flag": { "file": {
+ "ltr": "images/icons/flag-ltr.svg",
+ "rtl": "images/icons/flag-rtl.svg"
+ } },
+ "flagUndo": { "file": {
+ "ltr": "images/icons/flagUndo-ltr.svg",
+ "rtl": "images/icons/flagUndo-rtl.svg"
+ } },
+ "lock": { "file": {
+ "ltr": "images/icons/lock-ltr.svg",
+ "rtl": "images/icons/lock-rtl.svg"
+ }, "variants": [ "destructive" ] },
+ "star": { "file": "images/icons/star.svg" },
+ "trash": { "file": "images/icons/trash.svg" },
+ "trashUndo": { "file": {
+ "ltr": "images/icons/trashUndo-ltr.svg",
+ "rtl": "images/icons/trashUndo-rtl.svg"
+ } },
+ "unLock": { "file": {
+ "ltr": "images/icons/unLock-ltr.svg",
+ "rtl": "images/icons/unLock-rtl.svg"
+ }, "variants": [ "destructive" ] },
+ "unStar": { "file": "images/icons/unStar.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-movement.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-movement.json
new file mode 100644
index 00000000..9aa1b809
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-movement.json
@@ -0,0 +1,27 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "arrowNext": { "file": {
+ "ltr": "images/icons/arrow-ltr.svg",
+ "rtl": "images/icons/arrow-rtl.svg"
+ } },
+ "arrowLast": { "file": {
+ "ltr": "images/icons/arrow-rtl.svg",
+ "rtl": "images/icons/arrow-ltr.svg"
+ } },
+ "caretNext": { "file": {
+ "ltr": "images/icons/caret-rtl.svg",
+ "rtl": "images/icons/caret-ltr.svg"
+ } },
+ "caretLast": { "file": {
+ "ltr": "images/icons/caret-ltr.svg",
+ "rtl": "images/icons/caret-rtl.svg"
+ } },
+ "caretDown": { "file": "images/icons/caretDown.svg" },
+ "caretUp": { "file": "images/icons/caretUp.svg" },
+ "downTriangle": { "file": "images/icons/downTriangle.svg" },
+ "move": { "file": "images/icons/move.svg" },
+ "upTriangle": { "file": "images/icons/upTriangle.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-user.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-user.json
new file mode 100644
index 00000000..2bda5753
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-user.json
@@ -0,0 +1,19 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "userActive": { "file": {
+ "ltr": "images/icons/userActive-ltr.svg",
+ "rtl": "images/icons/userActive-rtl.svg"
+ } },
+ "userAvatar": { "file": "images/icons/userAvatar.svg" },
+ "userInactive": { "file": {
+ "ltr": "images/icons/userInactive-ltr.svg",
+ "rtl": "images/icons/userInactive-rtl.svg"
+ } },
+ "userTalk": { "file": {
+ "ltr": "images/icons/userTalk-ltr.svg",
+ "rtl": "images/icons/userTalk-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-wikimedia.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-wikimedia.json
new file mode 100644
index 00000000..fd13c95d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons-wikimedia.json
@@ -0,0 +1,9 @@
+{
+ "prefix": "oo-ui-icon",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "logoCC": { "file": "images/icons/logo-cc.svg" },
+ "logoWikimediaCommons": { "file": "images/icons/logo-wikimediaCommons.svg" },
+ "logoWikipedia": { "file": "images/icons/logo-wikipedia.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/icons.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons.json
new file mode 100644
index 00000000..d385eb11
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/icons.json
@@ -0,0 +1,75 @@
+{
+ "selectorWithoutVariant": ".oo-ui-icon-{name}",
+ "selectorWithVariant": ".oo-ui-image-{variant} .oo-ui-icon-{name}, .oo-ui-image-{variant}.oo-ui-icon-{name}",
+ "intro": "@import '../../../../src/styles/common';",
+ "variants": {
+ "invert": {
+ "color": "#FFFFFF",
+ "global": true
+ },
+ "progressive": {
+ "color": "#347BFF"
+ },
+ "constructive": {
+ "color": "#00AF89"
+ },
+ "destructive": {
+ "color": "#D11D13"
+ },
+ "warning": {
+ "color": "#FF5D00"
+ }
+ },
+ "images": {
+ "add": { "file": "images/icons/add.svg", "variants": [ "constructive" ] },
+ "advanced": { "file": "images/icons/advanced.svg" },
+ "alert": { "file": "images/icons/alert.svg", "variants": [ "warning" ] },
+ "cancel": { "file": "images/icons/cancel.svg" },
+ "check": { "file": "images/icons/check.svg", "variants": [ "constructive", "progressive" ] },
+ "circle": { "file": "images/icons/circle.svg", "variants": [ "constructive" ] },
+ "close": { "file": {
+ "ltr": "images/icons/close-ltr.svg",
+ "rtl": "images/icons/close-rtl.svg"
+ } },
+ "code": { "file": "images/icons/code.svg" },
+ "collapse": { "file": "images/icons/collapse.svg" },
+ "comment": { "file": "images/icons/comment.svg" },
+ "ellipsis": { "file": "images/icons/ellipsis.svg" },
+ "expand": { "file": "images/icons/expand.svg" },
+ "help": { "file": {
+ "ltr": "images/icons/help-ltr.svg",
+ "rtl": "images/icons/help-rtl.svg",
+ "lang": {
+ "he,yi": "images/icons/help-ltr.svg"
+ }
+ } },
+ "history": { "file": "images/icons/history.svg" },
+ "info": { "file": "images/icons/info.svg" },
+ "menu": { "file": "images/icons/menu.svg" },
+ "next": { "file": {
+ "ltr": "images/icons/move-ltr.svg",
+ "rtl": "images/icons/move-rtl.svg"
+ } },
+ "picture": { "file": "images/icons/picture.svg" },
+ "previous": { "file": {
+ "ltr": "images/icons/move-rtl.svg",
+ "rtl": "images/icons/move-ltr.svg"
+ } },
+ "redo": { "file": {
+ "ltr": "images/icons/arched-arrow-ltr.svg",
+ "rtl": "images/icons/arched-arrow-rtl.svg"
+ } },
+ "remove": { "file": "images/icons/remove.svg", "variants": [ "destructive" ] },
+ "search": { "file": {
+ "ltr": "images/icons/search-ltr.svg",
+ "rtl": "images/icons/search-rtl.svg"
+ } },
+ "settings": { "file": "images/icons/settings.svg" },
+ "tag": { "file": "images/icons/tag.svg", "variants": [ "destructive", "warning", "constructive", "progressive" ] },
+ "undo": { "file": {
+ "ltr": "images/icons/arched-arrow-rtl.svg",
+ "rtl": "images/icons/arched-arrow-ltr.svg"
+ } },
+ "window": { "file": "images/icons/window.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/add.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/add.svg
new file mode 100644
index 00000000..29e5dba8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/add.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="add">
+ <path id="plus" d="M13 8h-2v3h-3v2h3v3h2v-3h3v-2h-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/advanced.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/advanced.svg
new file mode 100644
index 00000000..b4629bf9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/advanced.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M20 14.5v-2.9l-1.8-.3c-.1-.4-.3-.8-.6-1.4l1.1-1.5-2.1-2.1-1.5 1.1c-.5-.3-1-.5-1.4-.6l-.2-1.8h-2.9l-.3 1.8c-.5.1-.9.3-1.4.6l-1.5-1.1-2.1 2.1 1 1.5c-.3.5-.4.9-.6 1.4l-1.7.2v2.9l1.8.3c.1.5.3.9.6 1.4l-1 1.5 2.1 2.1 1.5-1c.4.2.9.4 1.4.6l.3 1.8h3l.3-1.8c.5-.1.9-.3 1.4-.6l1.5 1.1 2.1-2.1-1.1-1.5c.3-.5.5-1 .6-1.4l1.5-.3zm-8 1.5c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/alert.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/alert.svg
new file mode 100644
index 00000000..f0c65224
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/alert.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="alert">
+ <path id="point" d="M11 16h2v2h-2z"/>
+ <path id="stroke" d="M13.516 10h-3l.484 5h2z"/>
+ <path id="triangle" d="M12.017 5.974l7.519 13.026h-15.04l7.521-13.026m0-2.474c-.544 0-1.088.357-1.5 1.071l-7.985 13.831c-.825 1.429-.15 2.598 1.5 2.598h15.968c1.65 0 2.325-1.169 1.5-2.599l-7.983-13.829c-.413-.715-.956-1.072-1.5-1.072z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-center.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-center.svg
new file mode 100644
index 00000000..887c2f66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-center.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-center">
+ <path d="M9 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1h-6c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zM3.5 18h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 6h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-left.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-left.svg
new file mode 100644
index 00000000..ce9761e2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-left.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-float-left">
+ <path d="M4 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1h-6c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zM13.5 9h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM13.5 12h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM13.5 15h7c.277 0 .5.223.5.5s-.223.5-.5.5h-7c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 6h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zM3.5 18h17c.277 0 .5.223.5.5s-.223.5-.5.5h-17c-.277 0-.5-.223-.5-.5s.223-.5.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-right.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-right.svg
new file mode 100644
index 00000000..557692ae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/align-float-right.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="align-float-right">
+ <path d="M20 9h-6c-.554 0-1 .446-1 1v5c0 .554.446 1 1 1h6c.554 0 1-.446 1-1v-5c0-.554-.446-1-1-1zM10.5 9h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM10.5 12h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM10.5 15h-7c-.277 0-.5.223-.5.5s.223.5.5.5h7c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM20.5 6h-17c-.277 0-.5.223-.5.5s.223.5.5.5h17c.277 0 .5-.223.5-.5s-.223-.5-.5-.5zM20.5 18h-17c-.277 0-.5.223-.5.5s.223.5.5.5h17c.277 0 .5-.223.5-.5s-.223-.5-.5-.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-ltr.svg
new file mode 100644
index 00000000..049f21e2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<path d="M-472.8,494.7l6.3,5.7l-6.3,5.7v-3.8h-1.3c-3.2,0-6.3,1.3-7.6,3.8c0-4.7,2.8-7.6,7.9-7.6h0.9V494.7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-rtl.svg
new file mode 100644
index 00000000..20875f34
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arched-arrow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<path d="M-476.3,494.7l-6.3,5.7l6.3,5.7v-3.8h1.3c3.2,0,6.3,1.3,7.6,3.8c0-4.7-2.8-7.6-7.9-7.6h-0.9V494.7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-ltr.svg
new file mode 100644
index 00000000..b07621e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M16 12h-10c-1.7 0-3 1.3-3 3h13v3l5-4.5-5-4.5v3z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-rtl.svg
new file mode 100644
index 00000000..a0189283
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/arrow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M8 12h10c1.7 0 3 1.3 3 3h-13v3l-5-4.5 5-4.5v3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-ltr.svg
new file mode 100644
index 00000000..b719946d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M12 10h4v-5h-4v5zm-5 2h9v-1h-9v1zm0 2h9v-1h-9v1zm0 2h9v-1h-9v1zm4-9h-4v1h4v-1zm0 2h-4v1h4v-1zm0-4h-4v1h4v-1zm-6-2h13v16h-10c-1.7 0-3-1.3-3-3v-13z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-rtl.svg
new file mode 100644
index 00000000..f14dfbda
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/article-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g16">
+ <path d="M11 10h-4v-5h4v5zm5 2h-9v-1h9v1zm0 2h-9v-1h9v1zm0 2h-9v-1h9v1zm-4-9h4v1h-4v-1zm0 2h4v1h-4v-1zm0-4h4v1h-4v-1zm6-2h-13v16h10c1.7 0 3-1.3 3-3v-13z" id="path18"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-ltr.svg
new file mode 100644
index 00000000..77119710
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-ltr.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <g>
+ <path d="M21 11l-6 7-4-4-1 1 5 5 7-8z"/>
+ </g>
+ <path d="M17 14v-11h-13v13c0 1.7 1.3 3 3 3h5l-3-3h-3v-1h2.6l1-1h-3.6v-1h9v1h-2l1 1h2l1-1zm-11-9h4v1h-4v-1zm0 2h4v1h-4v-1zm0 2h4v1h-4v-1zm9 3h-9v-1h9v1zm-4-2v-5h4v5h-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-rtl.svg
new file mode 100644
index 00000000..771b3ffb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleCheck-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g28">
+ <g id="g30">
+ <path d="M5 11l6 7 4-4 1 1-5 5-7-8z" id="path32"/>
+ </g>
+ <path d="M9 14v-11h13v13c0 1.7-1.3 3-3 3h-5l3-3h3v-1h-2.6l-1-1h3.6v-1h-9v1h2l-1 1h-2l-1-1zm11-9h-4v1h4v-1zm0 2h-4v1h4v-1zm0 2h-4v1h4v-1zm-9 3h9v-1h-9v1zm4-2v-5h-4v5h4z" id="path34"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-ltr.svg
new file mode 100644
index 00000000..e54c0c4a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M19.1 18.5c.6-.7.9-1.5.9-2.5 0-2.2-1.8-4-4-4s-4 1.8-4 4 1.8 4 4 4c.7 0 1.3-.1 1.8-.4l2.7 2.7 1.1-1.1-2.5-2.7zm-3.1-.3c-1.2 0-2.2-1-2.2-2.3 0-1.2 1-2.2 2.2-2.2 1.2 0 2.3 1 2.3 2.2-.1 1.3-1.1 2.3-2.3 2.3zm-4.2-5.2c.3-.4.6-.7 1-1h-5.8v-1h9s1.2 0 2 .6v-8.6h-13v13c0 1.7 1.3 3 3 3h3.8c-.6-.8-1-1.9-1-3h-3.8v-1h3.9l.3-1h-4.2v-1h4.8zm.2-8h4v5h-4v-5zm-5 0h4v1h-4v-1zm0 2h4v1h-4v-1zm0 2h4v1h-4v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-rtl.svg
new file mode 100644
index 00000000..31134f1c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/articleSearch-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g44">
+ <path d="M7.5 18.5c-.6-.7-.9-1.5-.9-2.5 0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4c-.7 0-1.3-.1-1.8-.4l-2.7 2.7-1.1-1.1 2.5-2.7zm3.1-.3c1.2 0 2.2-1 2.2-2.3 0-1.2-1-2.2-2.2-2.2-1.2 0-2.3 1-2.3 2.2.1 1.3 1.1 2.3 2.3 2.3zm4.2-5.2c-.3-.4-.6-.7-1-1h5.8v-1h-9s-1.2 0-2 .6v-8.6h13v13c0 1.7-1.3 3-3 3h-3.8c.6-.8 1-1.9 1-3h3.8v-1h-3.9l-.3-1h4.2v-1h-4.8zm-.2-8h-4v5h4v-5zm5 0h-4v1h4v-1zm0 2h-4v1h4v-1zm0 2h-4v1h4v-1z" id="path46"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bell.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bell.svg
new file mode 100644
index 00000000..df08800f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bell.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M17.5 14v-5c0-3-2.3-5-5.5-5s-5.5 2-5.5 5v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zm-5.5 6h-3c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-ltr.svg
new file mode 100644
index 00000000..f419e79f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M17.8 14.7l1.7-4.7c1-2.8-.5-5.5-3.5-6.6s-5.9 0-6.9 2.8l-1.7 4.7c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zm-5.8 5.1l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-rtl.svg
new file mode 100644
index 00000000..e4c3a3fa
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bellOn-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6.209 14.7l-1.7-4.7c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z" id="path56"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/beta.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/beta.svg
new file mode 100644
index 00000000..51a5c782
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/beta.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 12l-3-2-1 4-1-4-3 2 2-3-4-1 4-1-2-3 3 2 1-4 1 4 3-2-2 3 4 1-4 1 2 3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/betaLaunch.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/betaLaunch.svg
new file mode 100644
index 00000000..a693b59b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/betaLaunch.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M15.3 14.7c.8-3.8-.6-10.7-3.3-10.7-2.7 0-4.2 6.7-3.4 10.5l-1.6 3.5h2.7l.3 1h4c.2-.3.1-.5.3-1h2.7l-1.7-3.3zm-3.3-4.7c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm2 10c0 1.1-2 2-2 2s-2-.9-2-2"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-ltr.svg
new file mode 100644
index 00000000..94ec6704
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.666 6h-1.372l-4.48 12h1.705l1.494-4h3.999l1.508 4h1.666l-4.52-12zm-2.28 7l1.617-4.333 1.634 4.333h-3.251z" id="a"/>
+ <g id="up">
+ <path id="arrow" d="M15.5 9h7l-3.5-6z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-rtl.svg
new file mode 100644
index 00000000..b2a6c139
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bigger-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
+ <g id="up">
+ <path id="arrow" d="M1.5 9h7L5 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/block.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/block.svg
new file mode 100644
index 00000000..0ddd1d47
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/block.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9h-10v-2h10v2z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-ltr.svg
new file mode 100644
index 00000000..3d9cfd7d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g66">
+ <path d="M17 11v2h-2l3.6 3.6c.9-1.3 1.4-2.9 1.4-4.6 0-4.4-3.6-8-8-8-1.7 0-3.3.5-4.6 1.4l5.6 5.6h4zm-13-7l-1 1 2.4 2.4c-.9 1.3-1.4 2.9-1.4 4.6 0 4.4 3.6 8 8 8 1.7 0 3.3-.5 4.6-1.4l2.4 2.4 1-1-16-16zm3 9v-2h2l2 2h-4z" id="path68"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-rtl.svg
new file mode 100644
index 00000000..8f807596
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/blockUndo-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g66">
+ <path d="M7 11v2h2l-3.6 3.6c-.9-1.3-1.4-2.9-1.4-4.6 0-4.4 3.6-8 8-8 1.7 0 3.3.5 4.6 1.4l-5.6 5.6h-4zm13-7l1 1-2.4 2.4c.9 1.3 1.4 2.9 1.4 4.6 0 4.4-3.6 8-8 8-1.7 0-3.3-.5-4.6-1.4l-2.4 2.4-1-1 16-16zm-3 9v-2h-2l-2 2h4z" id="path68"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-a.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-a.svg
new file mode 100644
index 00000000..4b828779
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-a.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-a">
+ <path d="M16 18h3l-5-12h-3l-5 12h3l1.25-3h4.5l1.25 3zm-4.917-5l1.417-3.4 1.417 3.4h-2.834z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-ain.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-ain.svg
new file mode 100644
index 00000000..f96cebce
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-ain.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-arab-ain">
+ <path id="arab-ain" d="M9.337 13.616c0 1.349 1.386 2.101 4.159 2.258l2.187-.029.318.044c-.03.127-.251.345-.665.652l-.089.066c-1.236.929-2.423 1.393-3.56 1.393-1.143 0-2.046-.33-2.711-.99-.65-.66-.975-1.559-.975-2.698.005-1.354.566-2.573 1.684-3.658v-.044l-.606-.55c-.148-.181-.222-.391-.222-.63 0-.489.239-1.109.717-1.862.65-1.046 1.303-1.566 1.958-1.561.886.005 1.618.42 2.194 1.246.325.479-.03.552-1.064.22-.842-.327-1.527-.051-2.054.828l.015.073 1.123.865.052.007c1.404-.498 2.418-.74 3.043-.726-.059.117-.14.362-.244.733-.103.357-.204.684-.303.982l-.126.374-.384.051c-1.743.239-2.992.716-3.745 1.429-.463.464-.697.973-.702 1.525"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-dad.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-dad.svg
new file mode 100644
index 00000000..f04c6aad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-arab-dad.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-arab-dad">
+ <path id="arab-dad" d="M16.411 8.232l-1.676-.665.694-1.567 1.688.64-.707 1.592m.775 3.078c-.509-.286-1-.427-1.476-.423-.471 0-.982.205-1.532.616l-.506.379.006.025c1.084.066 1.934.099 2.551.099h.313c.567-.021.992-.064 1.276-.131-.067-.17-.275-.359-.625-.566h-.006m-6.803 3.296c-.017-.904-.329-1.87-.938-2.898l1.294-1.729.119.149c.267.336.504.924.713 1.766l.063.05c.496-.008.942-.17 1.338-.485v-.006l1.732-1.53c.679-.601 1.282-.902 1.807-.902.383.004.848.195 1.394.572.55.377.884.696 1 .958.063.149.094.386.094.709 0 .696-.11 1.229-.331 1.598-.192.311-.473.555-.844.734-.438.207-1.549.311-3.333.311-.8 0-1.795-.021-2.983-.062l-.144.429c-.254.672-.463 1.113-.625 1.324-.725.937-1.786 1.405-3.183 1.405-1.705-.008-2.557-.922-2.557-2.742.004-.941.279-1.814.825-2.618.15-.216.298-.367.444-.454.225-.133.288-.091.188.124-.396.862-.596 1.548-.6 2.058.008 1.177.752 1.768 2.232 1.772 1.038-.004 1.803-.182 2.295-.535"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-armn-to.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-armn-to.svg
new file mode 100644
index 00000000..4dbec6d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-armn-to.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-armn-to">
+ <path id="armn-to" d="M13.86 16.257c.124 0 .254-.026.39-.078.135-.058.257-.15.367-.274.114-.13.205-.302.273-.516.073-.213.11-.48.11-.797V13h-1.14c-.14 0-.284.026-.43.078-.14.047-.27.133-.383.258-.11.125-.2.294-.274.508-.067.213-.1.487-.1.82 0 .34.035.47.108.695.08.218.175.395.29.53.12.136.247.232.383.29.14.05.276.077.406.077m-2.97-7.84c-.37.082-.695.247-.976.45-.28.198-.505.47-.672.813-.16.343-.242.78-.242 1.312V18H6v-7.188c0-.776.15-1.455.453-2.04.302-.587.714-1.077 1.234-1.467.52-.39 1.13-.685 1.83-.883.697-.198 1.44-.297 2.225-.297.526 0 1.04.044 1.54.133.504.088.98.22 1.43.398.447.172.858.388 1.233.65.375.26.698.564.97.913.275.348.49.738.64 1.17.15.433.226 1.094.226 1.61h1.353v2.04H17.78v1.6c0 .58-.103 1.092-.31 1.54-.21.442-.49.815-.845 1.117-.35.302-.834.53-1.297.687-.464.15-.953.226-1.47.226-.51 0-.996-.078-1.46-.234-.464-.156-.87-.39-1.22-.703-.348-.313-.626-.703-.835-1.172-.203-.473-.304-1.028-.304-1.663s.105-1.182.32-1.64c.213-.46.497-.685.85-.977.355-.297.76-.513 1.22-.648.458-.14.935-.21 1.43-.21h1.132c-.01-.49-.04-1.043-.242-1.36-.198-.323-.453-.58-.766-.766-.312-.193-.598-.332-.984-.426-.374-.09-.577-.094-1.1-.094-.52 0-.64.02-1.01.102z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-b.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-b.svg
new file mode 100644
index 00000000..4f648203
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-b.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-b">
+ <path id="b" d="M7 18h6c2 0 4-1 4-3 0-1.064.011-1.975-1.989-3 2-.975 1.989-1.935 1.989-3 0-2-2-3-4-3h-6v12zm7-8c0 1.001 0 1-2 1h-2v-3h2c2 0 2 0 2 1v1zm-2 6h-2v-3h2c2 0 2 0 2 1v1s0 1-2 1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-be.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-be.svg
new file mode 100644
index 00000000..279466d4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-be.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-be">
+ <path id="cyrl-be" d="M7 6h9v2h-6v3h2.649c.893 0 1.633.109 2.22.327.588.218 1.088.622 1.502 1.211.419.589.629 1.187.629 1.978 0 .813-.21 1.398-.629 1.977-.419.578-.898.974-1.437 1.187-.533.213-1.295.319-2.286.319h-5.649m4.767-2c.751 0 1.279-.049 1.584-.12.305-.076.569-.246.792-.508.229-.262.343-.473.343-.855 0-.557-.199-.868-.596-1.119-.392-.256-1.064-.398-2.016-.398h-1.873v3"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-te.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-te.svg
new file mode 100644
index 00000000..fdeeb6c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-te.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-te">
+ <path id="te" d="M11 18v-10h-4v-2h11v2h-4v10"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-zhe.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-zhe.svg
new file mode 100644
index 00000000..5996c813
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-cyrl-zhe.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-cyrl-zhe">
+ <path id="cyrl-zhe" d="M13 6v5.154c.328-.033.537-.181.705-.447.168-.266.401-.873.698-1.821.39-1.241.789-2.033 1.197-2.374.403-.336 1.075-.504 2.014-.504l.386-.008v1.78l-.386-.008c-.399 0-.691.062-.878.187-.186.119-.337.304-.452.553-.115.249-.286.762-.512 1.537-.12.412-.25.756-.392 1.033-.137.276-.383.537-.738.78.439.157.8.466 1.084.927.288.455.603 1.103.944 1.943l1.33 3.268h-2.314l-1.17-3.081-.113-.252-.239-.561c-.248-.569-.452-.932-.612-1.089-.16-.157-.317-.236-.552-.236v5.22h-2v-5.22c-.226 0-.382.076-.546.228-.164.152-.368.518-.612 1.098l-.246.561-.113.252-1.17 3.081h-2.314l1.33-3.268c.328-.808.636-1.447.924-1.919.293-.477.663-.794 1.11-.951-.355-.244-.603-.501-.745-.772-.137-.276-.268-.623-.392-1.041-.222-.759-.39-1.266-.505-1.52-.111-.255-.261-.444-.452-.569-.186-.125-.492-.187-.917-.187l-.352.008v-1.78l.386.008c.953 0 1.631.171 2.034.512.399.347.791 1.136 1.177 2.366.301.954.534 1.564.698 1.829.168.26.377.406.705.439v-5.154"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-f.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-f.svg
new file mode 100644
index 00000000..357d2e5d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-f.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-f">
+ <path id="f" d="M16 8v-2h-8v12h3v-5h4v-2h-4v-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-g.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-g.svg
new file mode 100644
index 00000000..e032542e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-g.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-g">
+ <path id="g" d="M12 14v-2h5v4.203c-.497.475-1.22.894-2.166 1.259-.941.359-1.896.538-2.864.538-1.23 0-2.303-.253-3.217-.76-.915-.512-1.602-1.24-2.062-2.185-.46-.95-.69-1.982-.69-3.095 0-1.208.257-2.282.77-3.222.513-.939 1.265-1.66 2.255-2.161.754-.385 1.693-.578 2.816-.578 1.46 0 2.6.303 3.418.91.824.602 1.353 1.435 1.589 2.501l-2.359.435c-.166-.57-.479-1.018-.939-1.346-.455-.332-1.024-.499-1.709-.499-1.038 0-1.864.325-2.479.974-.61.649-.915 1.612-.915 2.889 0 1.377.31 2.412.931 3.103.62.686 1.433 1.029 2.439 1.029.497 0 .995-.095 1.492-.285.503-.195 1.332-.571 1.691-.845v-.867"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-geor-man.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-geor-man.svg
new file mode 100644
index 00000000..b211bf7a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-geor-man.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-geor-man">
+ <path id="geor-man" d="M13.832 14.061c0-1.715-.394-2.573-1.182-2.573-.868 0-1.302.779-1.302 2.338-.01 1.624.421 2.436 1.295 2.436.793 0 1.189-.734 1.189-2.201m2.168 0c0 2.626-1.116 3.939-3.349 3.939-2.434 0-3.651-1.386-3.651-4.159 0-2.738 1.217-4.106 3.651-4.106.841 0 1.182.63 1.182.63v-1.579c0-.789-.449-1.184-1.347-1.184-.572 0-.858.374-.858 1.123h-2.341c.005-1.817 1.064-2.725 3.176-2.725 2.368 0 3.548.946 3.538 2.839"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-l.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-l.svg
new file mode 100644
index 00000000..16797938
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-l.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-l">
+ <path id="l" d="M8 18v-12h3v10h5v2"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-n.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-n.svg
new file mode 100644
index 00000000..73ad019a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-n.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-n">
+ <path id="n" d="M7 18v-12h3l4 8v-8h3v12h-3l-4-8v8h-3"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-v.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-v.svg
new file mode 100644
index 00000000..146943a5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bold-v.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="bold-v">
+ <path id="v" d="M10.5 18l-4.5-12h3l3 8 3-8h3l-4.5 12"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-ltr.svg
new file mode 100644
index 00000000..7a058ed3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M15 7c-1.7 0-3 1.3-3 3 0-1.7-1.3-3-3-3h-6v13h6c1.7 0 3 1 3 2 0-1 1.3-2 3-2h6v-13h-6zm5 12h-5c-1.7 0-2 .4-2 .4v-8.9c0-1.4 1.1-2.5 2.5-2.5h4.5v11z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-rtl.svg
new file mode 100644
index 00000000..6ae47ec5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/book-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M9 7c1.7 0 3 1.3 3 3 0-1.7 1.3-3 3-3h6v13h-6c-1.7 0-3 1-3 2 0-1-1.3-2-3-2h-6v-13h6zm-5 12h5c1.7 0 2 .4 2 .4v-8.9c0-1.4-1.1-2.5-2.5-2.5h-4.5v11z" id="path78"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-ltr.svg
new file mode 100644
index 00000000..d803d6be
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M15 5h-7c-1.1 0-2 .9-2 2v3h3v11l4-3 4 3v-14c0-1.1-.9-2-2-2zm-6 4h-2v-2c0-.6.4-1 1-1h1v3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-rtl.svg
new file mode 100644
index 00000000..744d0f4e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/bookmark-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M8 5h7c1.1 0 2 .9 2 2v3h-3v11l-4-3-4 3v-14c0-1.1.9-2 2-2zm6 4h2v-2c0-.6-.4-1-1-1h-1v3z" id="path88"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-ltr.svg
new file mode 100644
index 00000000..7bd04250
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M3 6v11c0 1.7 1.3 3 3 3h15v-14h-18zm2.5 1c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5.7-1.5 1.5-1.5zm14.5 12h-14c-1.1 0-2-.9-2-2v-6h16v8z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-rtl.svg
new file mode 100644
index 00000000..84b18dae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/browser-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M21 6v11c0 1.7-1.3 3-3 3h-15v-14h18zm-2.5 1c-.8 0-1.5.7-1.5 1.5s.7 1.5 1.5 1.5 1.5-.7 1.5-1.5-.7-1.5-1.5-1.5zm-14.5 12h14c1.1 0 2-.9 2-2v-6h-16v8z" id="path98"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/cancel.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/cancel.svg
new file mode 100644
index 00000000..bfc1b44b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/cancel.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="cancel">
+ <path id="circle-with-strike" d="M11.999 5.022c-3.853 0-6.977 3.124-6.977 6.978 0 3.853 3.124 6.978 6.977 6.978 3.854 0 6.979-3.125 6.979-6.978 0-3.854-3.125-6.978-6.979-6.978zm-5.113 6.978c0-1.092.572-3.25.93-2.929l7.113 7.113c.488.525-1.837.931-2.93.931-2.825-.001-5.113-2.291-5.113-5.115zm9.298 2.929l-7.114-7.113c-.445-.483 1.837-.931 2.929-.931 2.827 0 5.115 2.289 5.115 5.114 0 1.093-.364 3.543-.93 2.93z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-ltr.svg
new file mode 100644
index 00000000..f31ec095
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 13.1l8.9 8.9c.8-.8.8-2 0-2.8l-6.1-6.1 6-6.1c.8-.8.8-2 0-2.8l-8.8 8.9z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-rtl.svg
new file mode 100644
index 00000000..02b4e387
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caret-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16.5 13.1l-8.9 8.9c-.8-.8-.8-2 0-2.8l6.1-6.1-6-6.1c-.8-.8-.8-2 0-2.8l8.8 8.9z" id="path108"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretDown.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretDown.svg
new file mode 100644
index 00000000..a04ca572
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretDown.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 16l8.9-8.9c-.8-.8-2-.8-2.8 0l-6.1 6.1-6.1-6c-.8-.8-2-.8-2.8 0l8.9 8.8z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretUp.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretUp.svg
new file mode 100644
index 00000000..d0e0c283
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/caretUp.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 6.5l8.9 8.9c-.8.8-2 .8-2.8 0l-6.1-6.1-6.1 6c-.8.8-2 .8-2.8 0l8.9-8.8z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/case-sensitive.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/case-sensitive.svg
new file mode 100644
index 00000000..824790c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/case-sensitive.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="regular-expression">
+ <path id="upper-case" d="M 7.53125,7 4,17 l 2.0625,0 0.71875,-2.40625 3.625,0 L 11.125,17 13.1875,17 9.65625,7 7.53125,7 z M 8.59375,8.53125 9.9375,13 7.25,13 8.59375,8.53125 z" />
+ <path id="lower-case" d="m 18.548697,17 -0.183254,-1.035072 -0.05451,0 c -0.349771,0.440361 -0.710892,0.746796 -1.083366,0.919307 -0.367941,0.167972 -0.849436,0.251959 -1.444489,0.251959 -0.564328,0 -0.954665,-0.20883 -1.377109,-0.626492 -0.417903,-0.417659 -0.626854,-1.012371 -0.626853,-1.784137 -1e-6,-0.80808 0.281628,-1.402791 0.844889,-1.784137 0.567801,-0.385878 1.193222,-0.607062 2.208372,-0.640111 l 1.321843,-0.04086 0,-0.333674 c 0,-0.771759 -0.395195,-1.15764 -1.185571,-1.157647 -0.608688,7e-6 -1.324118,0.183867 -2.146293,0.551584 L 14.134181,9.9184512 c 0.876685,-0.4585114 1.848761,-0.6877705 2.916233,-0.6877783 1.022038,7.8e-6 1.586855,0.2224573 2.131951,0.6673492 C 19.727448,10.342928 20,11.019356 20,11.927309 l 0,5.073215 -1.451303,0 m -0.394476,-3.527417 -0.804008,0.02724 c -0.604145,0.01816 -1.053844,0.127119 -1.349098,0.326866 -0.29526,0.199753 -0.442889,0.503919 -0.442886,0.912498 -3e-6,0.585634 0.336136,0.878451 1.008417,0.878449 0.481492,2e-6 0.865326,-0.138462 1.151503,-0.415391 0.29071,-0.276925 0.436067,-0.644648 0.436072,-1.103169 l 0,-0.626491" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/check.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/check.svg
new file mode 100644
index 00000000..cf7858ba
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/check.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="check">
+ <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/circle.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/circle.svg
new file mode 100644
index 00000000..436259e5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/circle.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="6"></circle></svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-ltr.svg
new file mode 100644
index 00000000..28ba0cb5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M7 12h9v-1h-9v1zm0 2h9v-1h-9v1zm0 2h9v-1h-9v1zm4-9h-4v1h4v-1zm0 2h-4v1h4v-1zm0-4h-4v1h4v-1zm5-2h2v16h-10c-1.7 0-3-1.3-3-3v-13h8v7l1.5-2 1.5 2v-7z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-rtl.svg
new file mode 100644
index 00000000..7625307c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/citeArticle-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g128">
+ <path d="M16 12h-9v-1h9v1zm0 2h-9v-1h9v1zm0 2h-9v-1h9v1zm-4-9h4v1h-4v-1zm0 2h4v1h-4v-1zm0-4h4v1h-4v-1zm-5-2h-2v16h10c1.7 0 3-1.3 3-3v-13h-8v7l-1.5-2-1.5 2v-7z" id="path130"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clear.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clear.svg
new file mode 100644
index 00000000..55a26c97
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clear.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="clear">
+ <path id="circle-with-cross" d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 11l-1 1-3-3-3 3-1-1 3-3-3-3 1-1 3 3 3-3 1 1-3 3 3 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clock.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clock.svg
new file mode 100644
index 00000000..1cf72670
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/clock.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 12l-4-3v-6h2v5l1.7 1.2c1.3.9 1 1.9.3 2.8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-ltr.svg
new file mode 100644
index 00000000..4f0f64ec
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M18.4 8.1c.8-.8.8-2 0-2.8l-6.4 6.5-5.6-5.6-1.4 1.4 5.6 5.6-5 5c-.8.8-.8 2 0 2.8l6.4-6.4 5.6 5.6 1.4-1.4-5.6-5.6 5-5.1z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-rtl.svg
new file mode 100644
index 00000000..d9829d0e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/close-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M5.6 8.1c-.8-.8-.8-2 0-2.8l6.4 6.5 5.6-5.6 1.4 1.4-5.6 5.6 5 5c.8.8.8 2 0 2.8l-6.4-6.4-5.6 5.6-1.4-1.4 5.6-5.6-5-5.1z" id="path140"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/code.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/code.svg
new file mode 100644
index 00000000..32f140d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/code.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
+ <g id="code">
+ <path id="left-bracket" d="M4 12v-1h1c1 0 1 0 1-1v-2.386c0-.514.024-.896.073-1.142.054-.252.139-.463.257-.633.204-.279.473-.475.808-.584.335-.115.872-.255 1.835-.255h1.027v1h-.752c-.457 0-.77.191-.936.408-.167.215-.312.445-.312 1.068v1.857c0 .729-.041 1.18-.244 1.493-.2.307-.562.529-1.09.667.535.155.9.385 1.096.688.199.303.238.757.238 1.484v1.862c0 .619.145.848.312 1.062.166.22.479.407.936.407l.752.004v1h-1.027c-.963 0-1.5-.133-1.835-.248-.335-.109-.604-.307-.808-.591-.118-.165-.203-.374-.257-.625-.049-.253-.073-.636-.073-1.149v-2.387c0-1 0-1-1-1h-1z"/>
+ <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" width="24" height="24" xlink:href="#left-bracket"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/collapse.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/collapse.svg
new file mode 100644
index 00000000..55aa8f8f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/collapse.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="collapse">
+ <path id="arrow" d="M6.697 15.714l5.303-5.302 5.303 5.302 1.414-1.414-6.717-6.717-6.717 6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/comment.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/comment.svg
new file mode 100644
index 00000000..0ae7e63f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/comment.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="comment">
+ <path id="speech-bubble" d="M15 6h-6c-1.657 0-3 1.344-3 3v4c0 1.656 1.343 3 3 3v3l3-3h3c1.657 0 3-1.344 3-3v-4c0-1.656-1.343-3-3-3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-ltr.svg
new file mode 100644
index 00000000..eb4c360d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16 5h-12v12c0 1.6 1.3 3 3 3h12v-12c0-1.7-1.4-3-3-3zm-8.5 12c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm0-6c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm0-6c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-rtl.svg
new file mode 100644
index 00000000..e929fdb1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/die-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 5h12v12c0 1.6-1.3 3-3 3h-12v-12c0-1.7 1.4-3 3-3zm8.5 12c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm0-6c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm0-6c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5z" id="path150"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/downTriangle.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/downTriangle.svg
new file mode 100644
index 00000000..7bc1c228
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/downTriangle.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 18l8-10h-16z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-ltr.svg
new file mode 100644
index 00000000..d0d5bb5b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M16 11h-3v-7c-1.7 0-3 1.3-3 3v4h-3l4.5 5 4.5-5zm1 2v5h-10c-.6 0-1-.4-1-1v-4h-2v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-rtl.svg
new file mode 100644
index 00000000..9abb2ae4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/download-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g160">
+ <path d="M7 11h3v-7c1.7 0 3 1.3 3 3v4h3l-4.5 5-4.5-5zm-1 2v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3h-12v-7h2z" id="path162"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-ltr.svg
new file mode 100644
index 00000000..3972e070
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_3">
+ <path d="M17 2l-12 12-1 5 5-1 12-12c0-2-2-4-4-4zm-9.8 13.5c-.3-.3-.7-.6-1-.8 2.3-2.3 11.3-11.4 11.3-11.4.4.1.7.3 1 .7l-11.3 11.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-rtl.svg
new file mode 100644
index 00000000..978b2fd1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/edit-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_3">
+ <path d="M8 2l12 12 1 5-5-1-12-12c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8-2.3-2.3-11.3-11.4-11.3-11.4-.4.1-.7.3-1 .7l11.3 11.5z" id="path173"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-ltr.svg
new file mode 100644
index 00000000..7e376824
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_2">
+ <g id="g184">
+ <path d="M21 4v-1s0-3-3-3-3 3-3 3v1h-1v6h8v-6zm-1.5 0h-3v-1s0-1.5 1.5-1.5c1.48.06 1.5 1.5 1.5 1.5zm-6.5 5.6l-6.8 6.9c-.3-.3-.7-.6-1-.8 1.4-1.4 5-5 7.8-7.9v-1.8l-9 9-1 5 5-1 8-8h-3z" id="path186"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-rtl.svg
new file mode 100644
index 00000000..0b4751d2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editLock-rtl.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_2">
+ <g id="g184">
+ <path d="M4 4v-1s0-3 3-3 3 3 3 3v1h1v6h-8v-6zm1.5 0h3v-1s0-1.5-1.5-1.5c-1.48.06-1.5 1.5-1.5 1.5zm6.5 5.6l6.8 6.9c.3-.3.7-.6 1-.8-1.4-1.4-5-5-7.8-7.9v-1.8l9 9 1 5-5-1-8-8h3z" id="path186"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-ltr.svg
new file mode 100644
index 00000000..f346874e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g196">
+ <g id="g198">
+ <path d="M14.9 2.8c.9 0 1.8.2 2.7.6.9.4 1.6.9 1.9 1.6-2.8.1-5 1.1-6.6 3.1l1.3 2-6.7-.3.5-6.8 1.7 2c1.8-1.5 3.5-2.2 5.2-2.2z" id="path200"/>
+ </g>
+ </g>
+ <g id="g204">
+ <path d="M15.2 11.1l-2.6-.1-5.4 5.5c-.3-.3-.7-.6-1-.8.9-.9 2.8-2.8 4.7-4.8h-1.8l-4.1 4.1-1 5 5-1 7.8-7.8-1.6-.1zm5.4-5.1c-1.7 0-3.2.5-4.4 1.4l-.9.9.8 1.3.9 1.4 4-4c0-.3-.1-.7-.2-1h-.2z" id="path206"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-rtl.svg
new file mode 100644
index 00000000..5b59d452
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/editUndo-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g196">
+ <g id="g198">
+ <path d="M10.1 2.8c-.9 0-1.8.2-2.7.6-.9.4-1.6.9-1.9 1.6 2.8.1 5 1.1 6.6 3.1l-1.3 2 6.7-.3-.5-6.8-1.7 2c-1.8-1.5-3.5-2.2-5.2-2.2z" id="path200"/>
+ </g>
+ </g>
+ <g id="g204">
+ <path d="M9.8 11.1l2.6-.1 5.4 5.5c.3-.3.7-.6 1-.8-.9-.9-2.8-2.8-4.7-4.8h1.8l4.1 4.1 1 5-5-1-7.8-7.8 1.6-.1zm-5.4-5.1c1.7 0 3.2.5 4.4 1.4l.9.9-.8 1.3-.9 1.4-4-4c0-.3.1-.7.2-1h.2z" id="path206"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ellipsis.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ellipsis.svg
new file mode 100644
index 00000000..dd36a30d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ellipsis.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <g>
+ <path d="M8 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ <g>
+ <path d="M14 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ <g>
+ <path d="M20 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/expand.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/expand.svg
new file mode 100644
index 00000000..7666b41d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/expand.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="expand">
+ <path id="arrow" d="M17.303 8.283l-5.303 5.303-5.303-5.303-1.414 1.414 6.717 6.717 6.717-6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-ltr.svg
new file mode 100644
index 00000000..827bc1b1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="external">
+ <path id="box" d="M2 2h3v1h-2v6h6v-2h1v3h-8z"/>
+ <path id="arrow" d="M6.211 2h3.789v3.789l-1.421-1.421-2.132 2.132-.947-.947 2.132-2.132z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-rtl.svg
new file mode 100644
index 00000000..c375ca0f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/external-link-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="external">
+ <path id="box" d="M7 3h2v6h-6v-2h-1v3h8v-8h-3z"/>
+ <path id="arrow" d="M2 5.789l1.421-1.421 2.132 2.132.947-.947-2.132-2.132 1.421-1.421h-3.789z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eye.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eye.svg
new file mode 100644
index 00000000..fa3bc3c1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eye.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z" id="path6"/>
+ <circle cx="12" cy="14" r="2" id="circle8"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eyeClosed.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eyeClosed.svg
new file mode 100644
index 00000000..fa1167df
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/eyeClosed.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M19.4 12.7c.7-.8 1.2-1.7 1.4-2.7h-1.6c-.9 2.5-3.9 4.4-7.7 4.6h-.1c-3.7-.2-6.8-2.1-7.7-4.6h-1.5c.2 1 .8 1.9 1.4 2.7l-2 2 .7.7 2-2c.8.6 1.7 1.2 2.7 1.7l-1 2.8.9.3 1-2.8c1 .3 2 .6 3.1.6v3h1v-3c1.1-.1 2.2-.3 3.1-.6l1 2.8.9-.3-1-2.8c1-.4 1.9-1 2.6-1.7l2 2 .7-.7-1.9-2z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-ltr.svg
new file mode 100644
index 00000000..f8578cf8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="find">
+ <path id="magnifying-glass" d="m 13.65625,11 c -1.921,0 -3.5,1.54775 -3.5,3.46875 0,1.92 1.579,3.5 3.5,3.5 0.749,0 1.432,-0.25225 2,-0.65625 l 0.09375,0.15625 2.375,2.375 c 0.19,0.189 0.53425,0.15325 0.78125,-0.09375 0.247,-0.247 0.314,-0.59125 0.125,-0.78125 l -2.375,-2.375 L 16.46875,16.5 C 16.87175,15.934 17.125,15.21775 17.125,14.46875 17.124,12.54875 15.57525,11 13.65625,11 z m 0,1.65625 c 1.011306,0 1.8125,0.801194 1.8125,1.8125 0,1.011306 -0.801194,1.84375 -1.8125,1.84375 -1.011306,0 -1.84375,-0.832444 -1.84375,-1.84375 0,-1.011306 0.832444,-1.8125 1.84375,-1.8125 z" />
+ <path id="text" d="M 6,5 6,7 16,7 16,5 6,5 z m 0,3 0,2 11,0 0,-2 -11,0 z m 0,3 0,2 3.53125,0 c 0.2825289,-0.797203 0.786096,-1.486208 1.4375,-2 L 6,11 z m 0,3 0,2 3.53125,0 C 9.3537004,15.520243 9.25,15.010236 9.25,14.46875 9.25,14.309811 9.2962033,14.154621 9.3125,14 L 6,14 z" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-rtl.svg
new file mode 100644
index 00000000..2a1e9c6f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/find-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="find">
+ <path id="magnifying-glass" d="m 11.343828,11.000025 c 1.921,0 3.5,1.54775 3.5,3.46875 0,1.92 -1.579,3.5 -3.5,3.5 -0.749,0 -1.432,-0.25225 -2,-0.65625 l -0.09375,0.15625 -2.375,2.375 c -0.19,0.189 -0.53425,0.15325 -0.78125,-0.09375 -0.247,-0.247 -0.314,-0.59125 -0.125,-0.78125 l 2.375,-2.375 0.1875,-0.09375 c -0.403,-0.566 -0.65625,-1.28225 -0.65625,-2.03125 10e-4,-1.92 1.54975,-3.46875 3.46875,-3.46875 z m 0,1.65625 c -1.011306,0 -1.8125,0.801194 -1.8125,1.8125 0,1.011306 0.801194,1.84375 1.8125,1.84375 1.011306,0 1.84375,-0.832444 1.84375,-1.84375 0,-1.011306 -0.832444,-1.8125 -1.84375,-1.8125 z" />
+ <path id="text" d="M 19,5 19,7 9,7 9,5 z m 0,3 0,2 -11,0 0,-2 z m 0,3 0,2 -3.53125,0 c -0.282529,-0.797203 -0.786096,-1.486208 -1.4375,-2 z m 0,3 0,2 -3.53125,0 C 15.6463,15.520243 15.75,15.010236 15.75,14.46875 15.75,14.309811 15.703797,14.154621 15.6875,14 z" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-ltr.svg
new file mode 100644
index 00000000..6e81d2bc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M14 6.5v-1.5c-1.4-1.5-5.2-1.2-6 0v-1h-1v15h1v-7c.8-.8 3.4-.9 5-.5v1.5c1.2 1.5 4.3 1.2 5 0v-7c-.7.7-2.7.9-4 .5z" id="path216"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-rtl.svg
new file mode 100644
index 00000000..4b743aac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flag-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11 6.5v-1.5c1.4-1.5 5.2-1.2 6 0v-1h1v15h-1v-7c-.8-.8-3.4-.9-5-.5v1.5c-1.2 1.5-4.3 1.2-5 0v-7c.7.7 2.7.9 4 .5z" id="path216"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-ltr.svg
new file mode 100644
index 00000000..49cdb7a2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-ltr.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g2990">
+ <g id="Layer_1">
+ <path id="path227" d="M14 6.5v-1.5c-1.4-1.5-5.2-1.2-6 0v-1h-1v15h1v-7c.8-.8 3.4-.9 5-.5v1.5c1.2 1.5 4.3 1.2 5 0v-7c-.7.7-2.7.9-4 .5z"/>
+ </g>
+ <g id="Layer_2">
+ <g id="g230">
+ <path id="path232" d="M17.997 1.989l.99.99-15.98 15.98-.99-.99z"/>
+ </g>
+ <g id="g234">
+ <path id="path236" d="M16.999 1.016l.99.99-15.98 15.98-.99-.99z" fill="#fff"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-rtl.svg
new file mode 100644
index 00000000..e470de42
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/flagUndo-rtl.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g2990">
+ <g id="Layer_1">
+ <path id="path227" d="M11 6.5v-1.5c1.4-1.5 5.2-1.2 6 0v-1h1v15h-1v-7c-.8-.8-3.4-.9-5-.5v1.5c-1.2 1.5-4.3 1.2-5 0v-7c.7.7 2.7.9 4 .5z"/>
+ </g>
+ <g id="Layer_2">
+ <g id="g230">
+ <path id="path232" d="M7.003 1.989l-.99.99 15.98 15.98.99-.99z"/>
+ </g>
+ <g id="g234">
+ <path id="path236" d="M8.001 1.016l-.99.99 15.98 15.98.99-.99z" fill="#fff"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-ltr.svg
new file mode 100644
index 00000000..63e0b1aa
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M2 5v15h20v-15h-20zm15 11h-9c-.6 0-1-.4-1-1v-6h3l2 1h5v6z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-rtl.svg
new file mode 100644
index 00000000..25bec742
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/folderPlaceholder-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M22 5v15h-20v-15h20zm-15 11h9c.6 0 1-.4 1-1v-6h-3l-2 1h-5v6z" id="path246"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-ltr.svg
new file mode 100644
index 00000000..191584eb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M11 13l-6-7h15l-6 7v7c-1.7 0-3-1.3-3-3v-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-rtl.svg
new file mode 100644
index 00000000..45f2f642
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/funnel-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g256">
+ <path d="M14 13l6-7h-15l6 7v7c1.7 0 3-1.3 3-3v-4z" id="path258"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/heart.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/heart.svg
new file mode 100644
index 00000000..6433201a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/heart.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M15 7c-2 0-3 2-3 2s-1-2-3-2c-2.5 0-4 2-4 4 0 4 5 5 7 8 2-3 7-4 7-8 0-2-1.5-4-4-4z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-ltr.svg
new file mode 100644
index 00000000..bb2545c5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-ltr.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="help">
+ <path id="circle" d="M12.001 2.085c-5.478 0-9.916 4.438-9.916 9.916 0 5.476 4.438 9.914 9.916 9.914 5.476 0 9.914-4.438 9.914-9.914 0-5.478-4.438-9.916-9.914-9.916zm.001 18c-4.465 0-8.084-3.619-8.084-8.083 0-4.465 3.619-8.084 8.084-8.084 4.464 0 8.083 3.619 8.083 8.084 0 4.464-3.619 8.083-8.083 8.083z"/>
+ <g id="question-mark">
+ <path id="top" d="M11.766 6.688c-2.5 0-3.219 2.188-3.219 2.188l1.411.854s.298-.791.901-1.229c.516-.375 1.625-.625 2.219.125.701.885-.17 1.587-1.078 2.719-.953 1.186-1 3.655-1 3.655h1.969s.135-2.318 1.041-3.381c.603-.707 1.443-1.338 1.443-2.494s-1.187-2.437-3.687-2.437z"/>
+ <path id="bottom" d="M11 16h2v2h-2z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-rtl.svg
new file mode 100644
index 00000000..99c7f842
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/help-rtl.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="help">
+ <path id="circle" d="M11.999 2.085c5.478 0 9.916 4.438 9.916 9.916 0 5.476-4.438 9.914-9.916 9.914-5.476 0-9.914-4.438-9.914-9.914 0-5.478 4.438-9.916 9.914-9.916zm-.001 18c4.465 0 8.084-3.619 8.084-8.083 0-4.465-3.619-8.084-8.084-8.084-4.464 0-8.083 3.619-8.083 8.084 0 4.464 3.619 8.083 8.083 8.083z"/>
+ <g id="question-mark">
+ <path id="top" d="M12.234 6.688c2.5 0 3.219 2.188 3.219 2.188l-1.411.854s-.298-.791-.901-1.229c-.516-.375-1.625-.625-2.219.125-.701.885.17 1.587 1.078 2.719.953 1.186 1 3.655 1 3.655h-1.969s-.135-2.318-1.041-3.381c-.603-.707-1.443-1.338-1.443-2.494 0-1.156 1.187-2.437 3.687-2.437z"/>
+ <path id="bottom" d="M13 16h-2v2h2z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/history.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/history.svg
new file mode 100644
index 00000000..35f15afe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/history.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="history">
+ <path id="clock-hands" d="M17.26 15.076s-2.385-1.935-4.005-3.062c.72-2.397 1.702-6.559 1.702-6.559s-4.35 5.363-4.877 6.699c-.463 1.168 1.459 2.209 2.346 1.678 1.9.551 4.834 1.244 4.834 1.244z"/>
+ <path id="arrow" d="M12.086 2.085c-5.478 0-9.916 4.438-9.916 9.916 0 1.783.476 3.454 1.301 4.898l-2.223 2.04h5.688v-5.219l-2.066 1.896c-.55-1.088-.866-2.312-.866-3.615 0-4.465 3.619-8.084 8.084-8.084 4.464 0 8.083 3.619 8.083 8.084 0 4.464-3.619 8.083-8.083 8.083-1.145 0-2.228-.247-3.213-.678l-.833 1.634c1.235.557 2.602.874 4.045.874 5.476 0 9.914-4.438 9.914-9.914-.001-5.477-4.439-9.915-9.915-9.915z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-ltr.svg
new file mode 100644
index 00000000..bebe0a9e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20 18l-4-4-2 2-4-4-2 1-4 5h16zm2-13v15h-20v-15h20z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-rtl.svg
new file mode 100644
index 00000000..88e0e3c0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/image-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g278">
+ <path d="M4 18l4-4 2 2 4-4 2 1 4 5h-16zm-2-13v15h20v-15h-20z" id="path280"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-ltr.svg
new file mode 100644
index 00000000..300e4b15
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M17 12v-4h-4v-3h-13v15h20v-8h-3zm-15 6l4-5 2-1 4 4 2-2 4 4h-16z"/>
+ <g>
+ <path d="M24 5h-4v-4h-2v4h-4v2h4v4h2v-4h4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-rtl.svg
new file mode 100644
index 00000000..70e32486
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageAdd-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 12v-4h4v-3h13v15h-20v-8h3zm15 6l-4-5-2-1-4 4-2-2-4 4h16z" id="path290"/>
+ <g id="g292">
+ <path d="M0 5h4v-4h2v4h4v2h-4v4h-2v-4h-4z" id="path294"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-ltr.svg
new file mode 100644
index 00000000..8cec9f5a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M19.5 4h-3v-1s0-1.5 1.5-1.5c1.5.06 1.5 1.5 1.5 1.5zm1.5 0v-1s0-3-3-3-3 3-3 3v1h-1v6h8v-6zm-8 7v-6h-11v15h20v-9zm-9 7l4-5 2-1 4 4 2-2 4 4z" id="path304"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-rtl.svg
new file mode 100644
index 00000000..6bb78f71
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/imageLock-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M4.5 4h3v-1s0-1.5-1.5-1.5c-1.5.06-1.5 1.5-1.5 1.5zm-1.5 0v-1s0-3 3-3 3 3 3 3v1h1v6h-8v-6zm8 7v-6h11v15h-20v-9zm9 7l-4-5-2-1-4 4-2-2-4 4z" id="path304"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-ltr.svg
new file mode 100644
index 00000000..ada33959
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M10 8h9v2h-9v-2zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8h-18v-2h18v2zm0 14h-18v-2h18v2zm-18-12v8l5-4-5-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-rtl.svg
new file mode 100644
index 00000000..9afedbbd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/indent-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g314">
+ <path d="M14 8h-9v2h9v-2zm0 3h-9v2h9v-2zm0 3h-6v2h6v-2zm-11-8h18v-2h-18v2zm0 14h18v-2h-18v2zm18-12v8l-5-4 5-4z" id="path316"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/info.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/info.svg
new file mode 100644
index 00000000..4bdefd46
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/info.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="info">
+ <path id="circled-i" d="M11.499 17c-3.036 0-5.499-2.464-5.499-5.5 0-3.037 2.462-5.5 5.499-5.5 3.037 0 5.501 2.462 5.501 5.5 0 3.036-2.464 5.5-5.501 5.5zm.002-12c-3.591 0-6.501 2.91-6.501 6.5s2.91 6.5 6.501 6.5c3.588 0 6.499-2.911 6.499-6.5s-2.911-6.5-6.499-6.5zM12 10v4h1v1h-3v-1h1v-3h-1v-1zM11 8h1v1h-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/insert.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/insert.svg
new file mode 100644
index 00000000..0833f84f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/insert.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="insert">
+ <path d="M13 5h-2v6h-6v2h6v6h2v-6h6v-2h-6z" id="plus"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-a.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-a.svg
new file mode 100644
index 00000000..a0e66bff
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-a.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-a">
+ <path id="a" d="M14.667 6h-1.372l-7 12h1.705l2.333-4h4l.667 4h1.667l-2-12zm-3.75 7l2.527-4.333.723 4.333h-3.25z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-keheh-jeem.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-keheh-jeem.svg
new file mode 100644
index 00000000..d4bff1be
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-keheh-jeem.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-arab-keheh-jeem">
+ <path id="arab-keheh-jeem" d="M18.125 5.844c-1.695.555-3.297 1.162-4.594 1.938-.49.299-.774.712-.875 1.125-.064.263-.035.572.063.781.189.405.539.574.844.813l.094-.125.531.625c.14.164.343.513.469.938.137.463.08.725 0 1.125h-3.438c-.338 0-.592.007-.766-.02-.339-.053-.256-.208-.234-.34.332-.127.564-.173.938-.141.29-.494.593-.885.906-1.313-.98.037-1.878.015-2.688-.094-.346-.047-.698-.186-1.094-.156-.357.026-.768.239-1.031.719-.246.448-.434.839-.656 1.281l.75-.469c.23-.142.484-.227.719-.219.157.005.275.054.406.094-.231.205-.509.402-.719.563-.301.26-.702.688-.906 1-.403.615-.694 1.084-.875 1.781-.179.689.004 1.339.469 1.75.426.376.846.519 1.281.563.65.065 1.205.093 2-.188.657-.231 1.021-.553 1.5-.969-.883.11-1.817.089-2.531.031-.871-.07-1.268-.384-1.469-.594-.271-.283-.307-.64-.156-1.219.036-.141.097-.323.25-.531.168-.228.364-.435.594-.656.451-.436 1.011-.737 1.461-.938-.045.206-.107.443-.055.688.049.229.248.379.438.469.259.122.506.155.688.156 1.421.011 2.862 0 4.281 0 .247 0 .452-.163.594-.375.139-.208.249-.481.344-.844.131-.499.094-1.062-.094-1.625-.182-.543-.418-1.009-.719-1.406-.335-.443-.674-.829-1-1.219 1.257-.815 2.716-1.239 3.969-1.688.121-.452.224-.926.313-1.313zm-9.469 8.438c-.262.394-.584.691-.875 1 .375.286.748.556 1.094.813.335-.303.626-.674.875-.969-.39-.268-.771-.588-1.094-.844z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-meem.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-meem.svg
new file mode 100644
index 00000000..bfbc9bf5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-arab-meem.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-arab-meem">
+ <path id="arab-meem" d="M16 9.729l-.93 2.19h-4.663c-.479 0-.857.122-1.135.367l-.061.11c-.184 2.016-.502 3.558-.955 4.627-.272.641-.633 1.252-1.082 1.833-.177.226-.219.186-.126-.119l.142-.504.17-.669.234-.87.002-.009.202-1.045.258-1.411.353-1.906c.191-.312.424-.638.699-.98.276-.342.589-.706.94-1.09.129-.092.697-.18 1.705-.266 1.05-.086 1.638-.183 1.765-.293l.065-.128c.007-.11-.011-.241-.054-.394-.043-.153-.12-.327-.231-.522-.22-.428-.438-.641-.654-.641-.294 0-.915.269-1.864.806-.359.208-.376.125-.051-.247 1.558-1.71 2.708-2.566 3.45-2.566.383 0 .671.131.863.394.135.195.25.599.344 1.21l.203 1.2c.106.586.242.895.409.925"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-armn-sha.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-armn-sha.svg
new file mode 100644
index 00000000..63de0f6c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-armn-sha.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-armn-sha">
+ <path id="armn-sha" d="M11.564 7.678c-.268-.13-.578-.22-.93-.268-.35-.047-.75-.07-1.197-.07h-1.11L8.586 6h1.724c.558 0 1.042.032 1.45.095.416.063.794.173 1.136.33l4.483 2.033-.324 1.67-2.624-1.165c-.126-.058-.27-.103-.433-.134-.164-.038-.356-.057-.576-.057-.583 0-1.137.095-1.663.284-.524.19-1 .46-1.425.812-.42.35-.777.78-1.072 1.283-.294.504-.504 1.074-.63 1.71-.242 1.255-.152 2.21.268 2.868.426.652 1.19.978 2.294.978.55 0 1.045-.08 1.48-.237.437-.156.815-.377 1.136-.66.326-.29.59-.633.796-1.033.21-.4.362-.84.457-1.323l.11-.56h1.6l-.12.59c-.13.674-.356 1.288-.676 1.845-.32.55-.725 1.026-1.214 1.425-.488.394-1.053.7-1.694.922-.642.215-1.343.323-2.105.323-.767 0-1.434-.113-2-.34-.568-.225-1.025-.553-1.372-.984-.347-.436-.573-.97-.678-1.607-.105-.637-.078-1.364.08-2.184.125-.66.346-1.273.66-1.835.316-.567.697-1.066 1.144-1.496.445-.436.944-.794 1.496-1.072.55-.284 1.13-.475 1.733-.575l-.466-.23"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-c.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-c.svg
new file mode 100644
index 00000000..b468deac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-c.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-c">
+ <path id="c" d="M15.008 13.718l1.481.214c-.468 1.34-1.15 2.354-2.046 3.04-.896.686-1.901 1.029-3.015 1.029-1.359 0-2.438-.43-3.237-1.29-.794-.86-1.191-2.092-1.191-3.697 0-2.09.606-3.818 1.817-5.185 1.079-1.219 2.42-1.828 4.023-1.828 1.186 0 2.145.33 2.878.989.738.66 1.165 1.546 1.282 2.66l-1.397.135c-.148-.839-.453-1.464-.916-1.876-.458-.417-1.051-.625-1.779-.625-1.369 0-2.476.631-3.321 1.892-.733 1.087-1.099 2.377-1.099 3.871 0 1.193.282 2.103.847 2.731.565.628 1.3.942 2.206.942.774 0 1.473-.261 2.099-.784.626-.522 1.081-1.261 1.366-2.216"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-d.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-d.svg
new file mode 100644
index 00000000..92a834d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-d.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-d">
+ <path id="d" d="M7 18l2.462-12h3.557c.853 0 1.505.063 1.955.188.644.169 1.194.472 1.65.909.456.431.799.971 1.03 1.621.231.649.346 1.378.346 2.186 0 .966-.145 1.847-.435 2.644-.284.791-.66 1.49-1.127 2.095-.461.6-.947 1.072-1.456 1.416-.504.338-1.102.589-1.794.753-.526.126-1.172.188-1.939.188h-4.249m1.859-1.359h1.867c.842 0 1.591-.079 2.245-.237.408-.098.756-.243 1.046-.434.381-.246.727-.57 1.038-.974.408-.535.732-1.143.974-1.825.247-.688.37-1.468.37-2.341 0-.971-.166-1.716-.499-2.235-.333-.524-.756-.87-1.271-1.04-.381-.126-.974-.188-1.778-.188h-1.85l-1.907 9.274"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-e.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-e.svg
new file mode 100644
index 00000000..66a5ef5d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-e.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-e">
+ <path id="e" d="M7 18l2.474-12h8.526l-.282 1.367h-6.947l-.75 3.633h6.09l-.282 1.367h-6.09l-.877 4.274h7.438l-.282 1.359h-9.018"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-geor-kan.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-geor-kan.svg
new file mode 100644
index 00000000..3398904d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-geor-kan.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-geor-kan">
+ <path id="geor-kan" d="M15.057 14.663c-.441 2.225-1.834 3.337-4.178 3.337-1.919 0-2.879-.787-2.879-2.36 0-.298.036-.624.108-.977.083-.431.245-.836.488-1.217l1.241.605-.207.613c-.055.259-.083.497-.083.712 0 .972.521 1.458 1.564 1.458 1.307 0 2.101-.723 2.383-2.17l.058-.331c.044-.221.066-.425.066-.613 0-.928-.546-1.391-1.638-1.391h-1.117l.248-1.259h1.117c1.202-.005 1.908-.552 2.118-1.64.039-.182.058-.356.058-.522 0-1.143-.899-1.714-2.697-1.714l.232-1.193c2.708 0 4.062.875 4.062 2.625 0 .248-.028.516-.083.803-.204 1.093-1.051 1.825-2.54 2.195l-.033.166c1.23.199 1.845.823 1.845 1.872 0 .21-.025.433-.074.671l-.058.331"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-i.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-i.svg
new file mode 100644
index 00000000..93bec5a6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-i.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-i">
+ <path id="i" d="M12.5 17.999l.249-.994h-1.5l2.509-10.037h1.5l.242-.967h-5l-.242.967h1.5l-2.509 10.037h-1.5l-.249.994z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-k.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-k.svg
new file mode 100644
index 00000000..d4831549
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-k.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-k">
+ <path id="k" d="M12.018 10.652l4.982-4.652h-2l-5.309 5.234 1.309-5.234h-1.5l-3 12h1.5l1.173-4.693 1.54-1.438c.287 4.131 3.287 6.131 3.287 6.131h2s-4-2-3.982-7.348z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-s.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-s.svg
new file mode 100644
index 00000000..4f6364cb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/italic-s.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="italic-s">
+ <path id="s" d="M16.474 6.589l-.302 1.526c-.522-.279-1.041-.488-1.557-.628-.511-.145-1.007-.217-1.487-.217-.935 0-1.679.204-2.231.612-.553.408-.829.95-.829 1.627 0 .372.101.658.302.86.207.196.733.408 1.58.635l.937.232c1.059.274 1.795.622 2.208 1.046.413.418.62 1.007.62 1.766 0 1.167-.46 2.117-1.379 2.851-.914.733-2.12 1.1-3.618 1.1-.615 0-1.232-.062-1.852-.186-.62-.119-1.242-.302-1.867-.55l.318-1.611c.573.356 1.147.625 1.72.806.578.181 1.154.271 1.728.271.976 0 1.759-.217 2.347-.651.589-.434.883-.999.883-1.697 0-.465-.119-.816-.356-1.054-.232-.243-.736-.462-1.511-.658l-.937-.24c-1.069-.279-1.8-.599-2.192-.961-.387-.367-.581-.878-.581-1.534 0-1.152.442-2.094 1.325-2.828.888-.739 2.043-1.108 3.463-1.108.553 0 1.1.049 1.642.147.542.098 1.085.245 1.627.442"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-ltr.svg
new file mode 100644
index 00000000..c7e16033
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16 9v-1h-6v1h6zm-2 2v-1h-4v1h4zm-8-6h1v16h-1v-16zm2 0h10v13c0 1.7-1.3 3-3 3h-7v-16z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-rtl.svg
new file mode 100644
index 00000000..2d16be37
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/journal-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M8 9v-1h6v1h-6zm2 2v-1h4v1h-4zm8-6h-1v16h1v-16zm-2 0h-10v13c0 1.7 1.3 3 3 3h7v-16z" id="path326"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-ltr.svg
new file mode 100644
index 00000000..8dfb89ae
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M14.5 4c-3 0-5.5 2.5-5.5 5.5 0 1 .3 1.9.7 2.8l-5.7 5.7v2h4v-2h2v-2h2l1.2-1.2c.4.1.9.2 1.3.2 3 0 5.5-2.5 5.5-5.5s-2.5-5.5-5.5-5.5zm1.5 5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-rtl.svg
new file mode 100644
index 00000000..06392874
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/key-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M9.5 4c3 0 5.5 2.5 5.5 5.5 0 1-.3 1.9-.7 2.8l5.7 5.7v2h-4v-2h-2v-2h-2l-1.2-1.2c-.4.1-.9.2-1.3.2-3 0-5.5-2.5-5.5-5.5s2.5-5.5 5.5-5.5zm-1.5 5c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5z" id="path336"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-ltr.svg
new file mode 100644
index 00000000..ea5055c8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M3 7v9c0 1.7 1.3 3 3 3h15v-12h-18zm8 2h2v2h-2v-2zm0 3h2v2h-2v-2zm-3-3h2v2h-2v-2zm0 3h2v2h-2v-2zm-1 5h-1c-.6 0-1-.4-1-1v-1h2v2zm0-3h-2v-2h2v2zm0-3h-2v-2h2v2zm9 6h-8v-2h8v2zm0-3h-2v-2h2v2zm0-3h-2v-2h2v2zm3 6h-2v-2h2v2zm0-3h-2v-2h2v2zm0-3h-2v-2h2v2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-rtl.svg
new file mode 100644
index 00000000..b35d108d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/keyboard-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g346">
+ <path d="M21 7v9c0 1.7-1.3 3-3 3h-15v-12h18zm-8 2h-2v2h2v-2zm0 3h-2v2h2v-2zm3-3h-2v2h2v-2zm0 3h-2v2h2v-2zm1 5h1c.6 0 1-.4 1-1v-1h-2v2zm0-3h2v-2h-2v2zm0-3h2v-2h-2v2zm-9 6h8v-2h-8v2zm0-3h2v-2h-2v2zm0-3h2v-2h-2v2zm-3 6h2v-2h-2v2zm0-3h2v-2h-2v2zm0-3h2v-2h-2v2z" id="path348"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/language.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/language.svg
new file mode 100644
index 00000000..081e49a1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/language.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="language">
+ <path id="japanese" d="M17.533 9.81l.271-.59 1.041.407-.18.363c.661.271 1.101.468 1.312.589.331.211.618.514.86.905.211.393.316.846.316 1.358 0 .786-.302 1.479-.905 2.083-.604.634-1.66 1.057-3.169 1.268-.121-.361-.258-.679-.408-.95.965-.151 1.645-.333 2.037-.545.454-.21.785-.481.998-.813.21-.303.314-.663.314-1.087 0-.482-.136-.905-.407-1.269-.331-.331-.8-.589-1.402-.77-.333.634-.649 1.117-.951 1.449-.242.332-.694.906-1.358 1.721.09.393.181.709.272.951l-1.042.362-.091-.498c-.423.361-.801.617-1.133.77-.361.15-.664.226-.905.226-.303 0-.574-.136-.814-.407-.243-.301-.362-.68-.362-1.132 0-.604.136-1.147.407-1.63.241-.453.603-.89 1.086-1.313.272-.241.725-.528 1.359-.86 0-.271.03-.799.09-1.585-.514.03-.921.045-1.222.045-.393 0-.711-.015-.951-.045l-.046-1.041c.725.091 1.494.135 2.31.135 0-.149.075-.738.227-1.766l1.177.183c-.151.542-.256 1.041-.316 1.493.242-.029.543-.075.906-.136.362-.061.573-.091.634-.091s.648-.15 1.766-.453l.046 1.041c-.967.243-2.145.439-3.532.591-.062.663-.092 1.086-.092 1.266.663-.151 1.284-.225 1.857-.225zm-2.672 3.893c-.061-.481-.136-1.252-.227-2.31-.573.424-1.041.86-1.403 1.313-.303.423-.452.875-.452 1.358 0 .241.044.438.136.588.09.092.195.137.316.137.363.001.907-.361 1.63-1.086zm.771-2.763c0 .483.029 1.088.09 1.811.604-.905 1.057-1.599 1.359-2.082-.574.06-1.058.151-1.449.271z"/>
+ <path id="english" d="M9.497 15.981h1.851l-3.084-8.949h-1.85l-3.081 8.949h1.85l.557-1.981h3.209l.548 1.981zm-3.489-3.377l1.331-3.782 1.344 3.782h-2.675z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-ltr.svg
new file mode 100644
index 00000000..47e71b39
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="layout-ltr">
+ <path id="text" d="M5 19v-14h6v8h8v6h-14z"/>
+ <path id="float" d="M13 5v6h6v-6h-6zm5 5h-4v-4h4v4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-rtl.svg
new file mode 100644
index 00000000..fe9ee617
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/layout-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="layout-rtl">
+ <path id="text" d="M5 19v-6h8v-8h6v14h-14z"/>
+ <path id="float" d="M5 5v6h6v-6h-6zm1 1h4v4h-4v-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-ltr.svg
new file mode 100644
index 00000000..841ba7d4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-ltr.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<g>
+ <path d="M-471.2,493.6c-2.1,0-3.6,1.9-5.1,3.3c0.2,0,0.5-0.1,0.8-0.1c0.5,0,1,0.1,1.5,0.3c0.8-0.8,1.6-1.7,2.8-1.7
+ c0.6,0,1.3,0.3,1.8,0.7c1,1,1,2.6,0,3.6l-2.6,2.6c-0.4,0.4-1.2,0.7-1.8,0.7c-1.4,0-2.1-0.9-2.6-2l-1.3,1.3c0.8,1.5,2,2.6,3.8,2.6
+ c1.2,0,2.3-0.5,3-1.3l2.6-2.6c0.9-0.9,1.5-2,1.5-3.3C-467,495.5-469,493.6-471.2,493.6z M-475.5,505.7l-0.9,0.9
+ c-0.4,0.4-1.2,0.7-1.8,0.7c-0.6,0-1.3-0.3-1.8-0.7c-1-1-1-2.7,0-3.6l2.6-2.6c0.4-0.4,1.2-0.7,1.8-0.7c1.4,0,2.1,1,2.6,2l1.3-1.3
+ c-0.8-1.5-2-2.6-3.8-2.6c-1.2,0-2.3,0.5-3,1.3l-2.6,2.6c-1.7,1.7-1.7,4.4,0,6c1.6,1.6,4.4,1.7,5.9,0l1.9-1.9
+ c-0.3,0.1-0.6,0.1-0.9,0.1C-474.7,505.9-475.1,505.9-475.5,505.7z"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-rtl.svg
new file mode 100644
index 00000000..d4c2fd66
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/link-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g358">
+ <path d="M9.025 3.6c2.1 0 3.6 1.9 5.1 3.3-.2 0-.5-.1-.8-.1-.5 0-1 .1-1.5.3-.8-.8-1.6-1.7-2.8-1.7-.6 0-1.3.3-1.8.7-1 1-1 2.6 0 3.6l2.6 2.6c.4.4 1.2.7 1.8.7 1.4 0 2.1-.9 2.6-2l1.3 1.3c-.8 1.5-2 2.6-3.8 2.6-1.2 0-2.3-.5-3-1.3l-2.6-2.6c-.9-.9-1.5-2-1.5-3.3.2-2.2 2.2-4.1 4.4-4.1zm4.3 12.1l.9.9c.4.4 1.2.7 1.8.7.6 0 1.3-.3 1.8-.7 1-1 1-2.7 0-3.6l-2.6-2.6c-.4-.4-1.2-.7-1.8-.7-1.4 0-2.1 1-2.6 2l-1.3-1.3c.8-1.5 2-2.6 3.8-2.6 1.2 0 2.3.5 3 1.3l2.6 2.6c1.7 1.7 1.7 4.4 0 6-1.6 1.6-4.4 1.7-5.9 0l-1.9-1.9c.3.1.6.1.9.1.5 0 .9 0 1.3-.2z" id="path360"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-ltr.svg
new file mode 100644
index 00000000..09a4ff5d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M21 7h-12v-2h12v2zm-14-1c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7h-12v-2h12v2zm-14-1c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7h-12v-2h12v2zm-14-1c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-rtl.svg
new file mode 100644
index 00000000..67b9dfee
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listBullet-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M3 7h12v-2h-12v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zm-14 7h12v-2h-12v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zm-14 7h12v-2h-12v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2z" id="path370"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-ltr.svg
new file mode 100644
index 00000000..87e8854e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M21 7h-13v-2h13v2zm0 6h-13v-2h13v2zm0 6h-13v-2h13v2zm-17-15h2v4h-1v-3h-1zm-1 6v-1h3v3h-2v1h2v1h-3v-3h2v-1zm3 10h-3v-1h2v-1h-1v-1h1v-1h-2v-1h3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-rtl.svg
new file mode 100644
index 00000000..831a5fb9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/listNumbered-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M3 7h13v-2h-13zm0 6h13v-2h-13zm0 6h13v-2h-13zm15-15h2v4h-1v-3h-1zm0 6v-1h3v3h-2v1h2v1h-3v-3h2v-1zm3 10h-3v-1h2v-1h-1v-1h1v-1h-2v-1h3z" id="path380"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-ltr.svg
new file mode 100644
index 00000000..59454922
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g390">
+ <path d="M15 8s0-3-2.5-3-2.5 3-2.5 3v1h5zm2 0v1h2v10h-10c-1.7 0-3-1.3-3-3v-7h2v-1s0-5 4.5-5 4.5 5 4.5 5z" id="path392"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-rtl.svg
new file mode 100644
index 00000000..0591f661
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/lock-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g390">
+ <path d="M10 8s0-3 2.5-3 2.5 3 2.5 3v1h-5zm-2 0v1h-2v10h10c1.7 0 3-1.3 3-3v-7h-2v-1s0-5-4.5-5-4.5 5-4.5 5z" id="path392"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-ltr.svg
new file mode 100644
index 00000000..4af765ca
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M15 14v3l5-4.5-5-4.5v3h-7c0 1.7 1.3 3 3 3h4zm-1-9h-10v15h10v-2h-8v-11h8v-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-rtl.svg
new file mode 100644
index 00000000..f72c04ad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logOut-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g402">
+ <path d="M9 14v3l-5-4.5 5-4.5v3h7c0 1.7-1.3 3-3 3h-4zm1-9h10v15h-10v-2h8v-11h-8v-2z" id="path404"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-cc.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-cc.svg
new file mode 100644
index 00000000..124b2101
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-cc.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M12 6c-3.9 0-7 3.1-7 7s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm-1.7-4.6c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5zm4 0c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikimediaCommons.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikimediaCommons.svg
new file mode 100644
index 00000000..079e1773
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikimediaCommons.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M15.4 7.8c-2-.9-2.3-2.5-2.4-2.8.1.1 2 1 2 1l-3-5-3 5 2-1s0 .8.6 2.1c.8 1.5 2.2 2.2 2.2 2.2s1.6.7 2.2 1.3l-.7.7-.5-.5-.4 1.8 1.8-.4-.5-.5.7-.7c.9 1 1.5 2.3 1.6 3.8h-1v-.8l-1.5 1 1.5 1v-.8h1c-.1 1.5-.6 2.8-1.6 3.8l-.7-.7.5-.5-1.8-.4.4 1.8.5-.5.7.7c-1 .9-2.3 1.5-3.8 1.6v-1h.8l-1-1.5-1 1.5h.8v1c-1.5-.1-2.8-.6-3.8-1.6l.7-.7.5.5.4-1.8-1.8.4.5.5-.7.7c-.9-1-1.5-2.3-1.6-3.8h1v.8l1.5-1-1.5-1v.8h-1c.1-1.5.6-2.8 1.6-3.8l.7.7-.5.5 1.8.4-.4-1.8-.5.5-.7-.7-1.5-1.4c-1.5 1.4-2.5 3.5-2.5 5.8 0 4.4 3.6 8 8 8s8-3.6 8-8c0-3.2-1.9-5.9-4.6-7.2z"/>
+ <circle cx="12" cy="15" r="3"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikipedia.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikipedia.svg
new file mode 100644
index 00000000..6672d9dc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/logo-wikipedia.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M22.3 6.3c0 .2 0 .3-.1.3-.7.1-1.2.5-1.6 1.1-.1.2-.2.4-.3.7l-4.6 10.1c-.1.2-.2.3-.2.3s-.1.1-.2.1c-.2 0-.4-.1-.5-.4l-2.6-5.5-2.8 5.5c-.1.3-.3.4-.5.4s-.4-.1-.5-.4l-4.3-10.1c-.3-.8-.6-1.2-.8-1.4-.2-.2-.5-.3-1-.4-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h4.3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.6.1-1 .2-1.1.4-.1.2 0 .6.3 1.2l3.6 8.2h.1l2.2-4.4-1.7-3.6c-.3-.7-.6-1.2-.8-1.4s-.5-.3-.9-.4c-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h3.6c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.4.1-.6.2-.6.4s.1.6.4 1.2l1 1.9 1-1.9c.3-.6.5-.9.5-1.1 0-.2 0-.3-.1-.4-.1-.1-.3-.1-.5-.1l-.1-.3c0-.2 0-.3.1-.3h3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.5.1-.8.2-1.1.5-.3.3-.6.7-.8 1.3l-1.3 2.8 2.5 5.2h.1l3.7-8.1c.3-.5.3-.9.2-1.2-.1-.3-.5-.4-1.1-.5-.1-.1-.1-.2-.1-.3s0-.3.1-.3h3.7c-.2.1-.2.2-.2.3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-ltr.svg
new file mode 100644
index 00000000..0fc47737
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M15 6l-6-2-6 2v15l6-2 6 2 6-2v-15l-6 2zm-6.3 12.1l-4.7 1.5v-12.9l5-1.7v12.9l-.3.2zm11.3.2l-5 1.7v-12.9l.3-.1 4.7-1.6v12.9z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-rtl.svg
new file mode 100644
index 00000000..b33f1e39
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/map-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M9 6l6-2 6 2v15l-6-2-6 2-6-2v-15l6 2zm6.3 12.1l4.7 1.5v-12.9l-5-1.7v12.9l.3.2zm-11.3.2l5 1.7v-12.9l-.3-.1-4.7-1.6v12.9z" id="path424"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPin.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPin.svg
new file mode 100644
index 00000000..f422c84f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPin.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M19 12c0-3.9-3.1-7-7-7s-7 3.1-7 7c0 1.4.4 2.6 1.1 3.7l5.9 7.3 5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7zm-7 4c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z" id="path4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-ltr.svg
new file mode 100644
index 00000000..9a54eb6a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-ltr.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g434">
+ <g id="g436">
+ <path d="M24 4h-4v-4h-2v4h-4v2h4v4h2v-4h4z" id="path438"/>
+ </g>
+ </g>
+ <path d="M18 11h-1v-3.9l-.1-.1h-3.9v-1.9c-.3-.1-.7-.1-1-.1-3.9 0-7 3.1-7 7 0 1.4.4 2.6 1.1 3.7l5.9 7.3 5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7 0-.3 0-.7-.1-1h-.9zm-6 5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z" id="path440"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-rtl.svg
new file mode 100644
index 00000000..d3e152e0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/mapPinAdd-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g434">
+ <g id="g436">
+ <path d="M0 4h4v-4h2v4h4v2h-4v4h-2v-4h-4z" id="path438"/>
+ </g>
+ </g>
+ <path d="M6 11h1v-3.9l.1-.1h3.9v-1.9c.3-.1.7-.1 1-.1 3.9 0 7 3.1 7 7 0 1.4-.4 2.6-1.1 3.7l-5.9 7.3-5.9-7.3c-.7-1.1-1.1-2.3-1.1-3.7 0-.3 0-.7.1-1h.9zm6 5c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4z" id="path440"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/menu.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/menu.svg
new file mode 100644
index 00000000..89fd9789
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/menu.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<g id="menu">
+ <path id="lines" d="M-481,505h12c0.6,0,1,0.4,1,1v1c0,0.6-0.4,1-1,1h-12c-0.6,0-1-0.4-1-1v-1C-482,505.4-481.6,505-481,505z
+ M-482,501v1c0,0.6,0.4,1,1,1h12c0.6,0,1-0.4,1-1v-1c0-0.6-0.4-1-1-1h-12C-481.6,500-482,500.4-482,501z M-482,496v1
+ c0,0.6,0.4,1,1,1h12c0.6,0,1-0.4,1-1v-1c0-0.6-0.4-1-1-1h-12C-481.6,495-482,495.4-482,496z"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-ltr.svg
new file mode 100644
index 00000000..3f308ff7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M21 9c0-1.7-1.3-3-3-3h-15v3l9 4 9-4zm-18 2v6c0 1.7 1.3 3 3 3h15v-9l-9 4-9-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-rtl.svg
new file mode 100644
index 00000000..fa61aa18
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/message-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g450">
+ <path d="M3 9c0-1.7 1.3-3 3-3h15v3l-9 4-9-4zm18 2v6c0 1.7-1.3 3-3 3h-15v-9l9 4 9-4z" id="path452"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-ltr.svg
new file mode 100644
index 00000000..51e6611a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="move-ltr">
+ <path id="arrow" d="M8.935 7.181l5.302 5.302-5.302 5.303 1.414 1.414 6.716-6.717-6.716-6.716z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-rtl.svg
new file mode 100644
index 00000000..bcee09d9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="move-rtl">
+ <path id="arrow" d="M15.065 17.786l-5.302-5.303 5.302-5.302-1.414-1.414-6.716 6.716 6.716 6.717z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move.svg
new file mode 100644
index 00000000..9063bd48
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/move.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M20 11l-4-3v2h-3v-3h2l-3-4-3 4h2v3h-3v-2l-4 3 4 3v-2h3v3h-2l3 4 3-4h-2v-3h3v2z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-ltr.svg
new file mode 100644
index 00000000..b8ea833e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M12 5l2.5 2.5-3.5 3.5c-1.2 1.2-1.2 2.8 0 4l5.5-5.5 2.5 2.5v-7h-7zm5 12h-9c-.6 0-1-.4-1-1v-9h3l-2-2h-3v11c0 1.7 1.3 3 3 3h11v-3l-2-2v3z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-rtl.svg
new file mode 100644
index 00000000..58a9eeb2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newWindow-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g462">
+ <path d="M12 5l-2.5 2.5 3.5 3.5c1.2 1.2 1.2 2.8 0 4l-5.5-5.5-2.5 2.5v-7h7zm-5 12h9c.6 0 1-.4 1-1v-9h-3l2-2h3v11c0 1.7-1.3 3-3 3h-11v-3l2-2v3z" id="path464"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-ltr.svg
new file mode 100644
index 00000000..dad5f51c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="line_return">
+ <path d="M17.8 5.7c-.5 0-.9.2-1.2.5s-.5.7-.5 1.2v4.3h-5.1v-4l-6 5.5 6 5.5v-4h8v-9h-1.2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-rtl.svg
new file mode 100644
index 00000000..fd758cc6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newline-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="line_return">
+ <path d="M6.2 5.7c.5 0 .9.2 1.2.5.3.3.5.7.5 1.2v4.3H13v-4l6 5.5-6 5.5v-4H5v-9h1.2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-ltr.svg
new file mode 100644
index 00000000..46471a33
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6 7v12c-.6 0-1-.4-1-1v-9h-1v9c0 1.1.9 2 2 2h15v-13h-15zm9 11h-7v-1h7v1zm0-2h-7v-1h7v1zm0-2h-7v-1h7v1zm4 4h-3v-5h3v5zm0-7h-11v-2h11v2z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-rtl.svg
new file mode 100644
index 00000000..7564dff0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/newspaper-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M19 7v12c.6 0 1-.4 1-1v-9h1v9c0 1.1-.9 2-2 2h-15v-13h15zm-9 11h7v-1h-7v1zm0-2h7v-1h-7v1zm0-2h7v-1h-7v1zm-4 4h3v-5h-3v5zm0-7h11v-2h-11v2z" id="path474"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-ltr.svg
new file mode 100644
index 00000000..601428e2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M16 14l2 2v-11h-4v2h2zm0 2l-7-7-2-2-1-1-1-1-3-3-1 1 2 2h-1v14h4v-2h-2v-10h1l2 2v10h4v-2h-2v-6l6 6h-1v2h3l4 4 1-1-4-4zm-5-9v-2h-4l2 2zm8-2v2h2v10h-2l2 2h2v-14z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-rtl.svg
new file mode 100644
index 00000000..31785a3c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/noWikiText-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g484">
+ <path d="M8 14l-2 2v-11h4v2h-2zm0 2l7-7 2-2 1-1 1-1 3-3 1 1-2 2h1v14h-4v-2h2v-10h-1l-2 2v10h-4v-2h2v-6l-6 6h1v2h-3l-4 4-1-1 4-4zm5-9v-2h4l-2 2zm-8-2v2h-2v10h2l-2 2h-2v-14z" id="path486"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-ltr.svg
new file mode 100644
index 00000000..4264ff08
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M10 8h9v2h-9v-2zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8h-18v-2h18v2zm0 14h-18v-2h18v2zm-18-8l5 4v-8l-5 4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-rtl.svg
new file mode 100644
index 00000000..2479343e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outdent-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g496">
+ <path d="M14 8h-9v2h9v-2zm0 3h-9v2h9v-2zm0 3h-6v2h6v-2zm-11-8h18v-2h-18v2zm0 14h18v-2h-18v2zm18-8l-5 4v-8l5 4z" id="path498"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-ltr.svg
new file mode 100644
index 00000000..9c0ea598
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outline-ltr">
+ <path id="text" d="M5 13h14v6h-14v-6z"/>
+ <path id="float" d="M5 5v6h6v-6h-6zm5 5h-4v-4h4v4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-rtl.svg
new file mode 100644
index 00000000..2a3428e9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/outline-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="outline-rtl">
+ <path id="text" d="M19 19h-14v-6h14v6z"/>
+ <path id="float" d="M13 5v6h6v-6h-6zm1 1h4v4h-4v-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-ltr.svg
new file mode 100644
index 00000000..92fc07ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M2 3h18v2h-16v12h-2v-14zm13 13l-4-4-4 5h13l-3-3-2 2zm-10-10h17v13h-17v-13z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-rtl.svg
new file mode 100644
index 00000000..d989d3d4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/photoGallery-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g508">
+ <path d="M22 3h-18v2h16v12h2v-14zm-13 13l4-4 4 5h-13l3-3 2 2zm10-10h-17v13h17v-13z" id="path510"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/picture.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/picture.svg
new file mode 100644
index 00000000..7400bca9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/picture.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="picture">
+ <path id="frame" d="M18 4h-12c-2-.007-3 .993-3 2.993l.014 9.007c-.014 2 .986 2.988 2.986 3h12c2-.012 2.994-1 3-3.006v-9.001c-.006-2-1-3-3-2.993zm1 13h-14v-11h14v11z"/>
+ <path id="mountains" d="M6 13.5l3.5-3.5 2.328 2.312-1.312 1.094.875 1.032 4.109-3.438 2.5 2v3h-12z"/>
+ <path id="sky" d="M6 12l3.516-4.156 3.046 3.172 2.938-2.016 2.5 2v-4h-12z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-ltr.svg
new file mode 100644
index 00000000..5ce95636
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-2 12v-8l6 4-6 4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-rtl.svg
new file mode 100644
index 00000000..591a5d3a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/play-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g520">
+ <path d="M12 5c4.4 0 8 3.6 8 8s-3.6 8-8 8-8-3.6-8-8 3.6-8 8-8zm2 12v-8l-6 4 6 4z" id="path522"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-ltr.svg
new file mode 100644
index 00000000..baae35e9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M18 8h-1v-4h-10v4h-4v6c0 1.7 1.3 3 3 3h1v3h10v-3h4v-6c0-1.7-1.3-3-3-3zm-10-3h8v3h-8v-3zm8 14h-8v-6h8v6z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-rtl.svg
new file mode 100644
index 00000000..8294afd5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/printer-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6 8h1v-4h10v4h4v6c0 1.7-1.3 3-3 3h-1v3h-10v-3h-4v-6c0-1.7 1.3-3 3-3zm10-3h-8v3h8v-3zm-8 14h8v-6h-8v6z" id="path532"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-ltr.svg
new file mode 100644
index 00000000..97b77bb4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M18 9.9c-.7 0-1.4.3-1.8.9v-4.8h-4c.2-.4.4-.8.4-1.2 0-1.2-1-2.2-2.2-2.2-1.3-.1-2.3.9-2.3 2.2 0 .4.2.8.4 1.2h-4.4v3.6l.6-.1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5c-.2 0-.4 0-.6-.1v3.6h4.9c-.5.4-.9 1-.9 1.8 0 1.2 1 2.2 2.3 2.2 1.2 0 2.2-1 2.2-2.2 0-.7-.3-1.4-.9-1.8h4.5v-4.5c.4.5 1 .9 1.8.9 1.2 0 2.2-1 2.2-2.2 0-1.3-1-2.3-2.2-2.3z" id="path542"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-rtl.svg
new file mode 100644
index 00000000..0ad5f375
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/puzzle-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6.3 9.9c.7 0 1.4.3 1.8.9v-4.8h4c-.2-.4-.4-.8-.4-1.2 0-1.2 1-2.2 2.2-2.2 1.3-.1 2.3.9 2.3 2.2 0 .4-.2.8-.4 1.2h4.4v3.6l-.6-.1c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5c.2 0 .4 0 .6-.1v3.6h-4.9c.5.4.9 1 .9 1.8 0 1.2-1 2.2-2.3 2.2-1.2 0-2.2-1-2.2-2.2 0-.7.3-1.4.9-1.8h-4.5v-4.5c-.4.5-1 .9-1.8.9-1.2 0-2.2-1-2.2-2.2 0-1.3 1-2.3 2.2-2.3z" id="path542"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-ltr.svg
new file mode 100644
index 00000000..b3b923e5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<g>
+ <path d="M-468.9,498.1c0.2-0.1,0.5-0.2,0.6-0.5s0.2-0.5,0.2-0.9V496c-1,0.2-1.5,0.2-2.6,0.6c-0.7,0.4-1.2,0.9-1.7,1.4
+ c-0.5,0.5-1.9,2.6-1.9,5.8v3.1h4.7c0.9,0,1.6-0.7,1.6-1.6v-4.7h-3.1c0,0,0.1-0.7,0.6-1.4C-470,498.7-469.5,498.3-468.9,498.1z
+ M-480.1,498c-0.5,0.5-1.9,2.9-1.9,6v2.9h4.7c0.9,0,1.6-0.7,1.6-1.6v-4.7h-3.1c0,0,0.1-0.7,0.6-1.4c0.5-0.5,1-0.9,1.6-1.2
+ c0.2-0.1,0.5-0.2,0.6-0.5s0.2-0.5,0.2-0.9V496c-1,0.2-1.5,0.2-2.6,0.6C-479,497-479.6,497.5-480.1,498z"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-rtl.svg
new file mode 100644
index 00000000..b40a8ac9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotes-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-487 489 24 24" enable-background="new -487 489 24 24" xml:space="preserve">
+<g>
+ <path d="M-479.5,499.3c0.5,0.7,0.6,1.4,0.6,1.4h-3.1v4.7c0,0.9,0.7,1.6,1.6,1.6h4.7v-3.1c0-3.1-1.5-5.2-1.9-5.8
+ c-0.5-0.5-1-1-1.7-1.4c-1.1-0.5-1.6-0.5-2.6-0.6v0.8c0,0.3,0.1,0.6,0.2,0.9s0.4,0.4,0.6,0.5C-480.5,498.3-480,498.7-479.5,499.3z
+ M-471.7,496.6c-1.1-0.5-1.6-0.5-2.6-0.6v0.8c0,0.3,0.1,0.6,0.2,0.9s0.4,0.4,0.6,0.5c0.6,0.2,1.2,0.6,1.6,1.2
+ c0.5,0.7,0.6,1.4,0.6,1.4h-3.1v4.7c0,0.9,0.7,1.6,1.6,1.6h4.7V504c0-3.1-1.5-5.4-1.9-6C-470.4,497.5-471,497-471.7,496.6z"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-ltr.svg
new file mode 100644
index 00000000..24fca8f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M3.5 8.6c-.6.7-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4s.1-.9.8-1.8c.6-.7 1.3-1.2 2.1-1.5.3-.1.6-.3.8-.6.2-.3.3-.7.3-1.1v-1c-1.3.2-1.9.2-3.3.8-.8.5-1.6 1.1-2.2 1.8zm15.5-3.6v-4h-2v4h-4v2h4v4h2v-4h4v-2zm-4 7s.1-.9.8-1.8l.2-.2v-2h-1.9l-.6.6c-.6.7-2.5 3.4-2.5 7.4v4h6c1.1 0 2-.9 2-2v-6h-4z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-rtl.svg
new file mode 100644
index 00000000..736f2a6d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/quotesAdd-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <path d="M20.5 8.6c.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4s-.1-.9-.8-1.8c-.6-.7-1.3-1.2-2.1-1.5-.3-.1-.6-.3-.8-.6-.2-.3-.3-.7-.3-1.1v-1c1.3.2 1.9.2 3.3.8.8.5 1.6 1.1 2.2 1.8zm-15.5-3.6v-4h2v4h4v2h-4v4h-2v-4h-4v-2zm4 7s-.1-.9-.8-1.8l-.2-.2v-2h1.9l.6.6c.6.7 2.5 3.4 2.5 7.4v4h-6c-1.1 0-2-.9-2-2v-6h4z" id="path6"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-ltr.svg
new file mode 100644
index 00000000..884d40df
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="create_redirect">
+ <g>
+ <path d="M17.7 2.4c-.3-.3-.7-.4-1.2-.4h-12.1v16.2c0 .5.1.8.4 1.1s.7.7 1.2.7h10.2c-.6-.2-1.2-.5-1.9-1-.4-.3-.8-.6-1.2-1l-.5-.6h-6.2v-1.4h5.4s-.4-1.5-.4-2h-5v-1h9v1c.4.1 1.1.1 1.5.1.4 0 .7 0 1.1-.1v-10.5c.1-.5-.1-.9-.3-1.1zm-5.2 1.6h3v4.5h-3v-4.5zm-6.1 0h4v1.6h-4v-1.6zm0 3h4v1.5h-4v-1.5zm0 3h9v1.5h-9v-1.5zm12.7 3.1l4.9 3.8-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-rtl.svg
new file mode 100644
index 00000000..a07e8364
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/redirect-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="create_redirect">
+ <g id="g3264">
+ <path d="M6.3 2.4c.3-.3.7-.4 1.2-.4h12.1v16.2c0 .5-.1.8-.4 1.1-.3.3-.7.7-1.2.7h-10.2c.6-.2 1.2-.5 1.9-1 .4-.3.8-.6 1.2-1l.5-.6h6.2v-1.4h-5.4s.4-1.5.4-2h5v-1h-9v1c-.4.1-1.1.1-1.5.1-.4 0-.7 0-1.1-.1v-10.5c-.1-.5.1-.9.3-1.1zm5.2 1.6h-3v4.5h3v-4.5zm6.1 0h-4v1.6h4v-1.6zm0 3h-4v1.5h4v-1.5zm0 3h-9v1.5h9v-1.5z" id="path3266"/>
+ <path d="M4.9 13.1l-4.9 3.8 4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3" id="path3268"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/regular-expression.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/regular-expression.svg
new file mode 100644
index 00000000..7b672618
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/regular-expression.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="regular-expression">
+ <path id="left-bracket" d="m 3,12.044797 c -5e-7,-0.989171 0.150394,-1.914889 0.451184,-2.7771612 C 3.7558785,8.4053812 4.1933899,7.6495032 4.7637193,7 L 6.2286026,7 C 5.6778034,7.7204251 5.261777,8.511764 4.9805221,9.3740188 4.6992623,10.236291 4.5586337,11.122815 4.5586357,12.033598 c -2e-6,0.914522 0.1425798,1.799179 0.427746,2.653974 C 5.2754491,15.538635 5.6856161,16.309444 6.2168835,17 L 4.7637193,17 C 4.1894835,16.365435 3.7519721,15.624488 3.451184,14.777158 3.150394,13.929828 3,13.019042 3,12.044797" />
+ <path id="dot" d="m 10,16 c 0,0.552285 -0.4477153,1 -1,1 -0.5522847,0 -1,-0.447715 -1,-1 0,-0.552285 0.4477153,-1 1,-1 0.5522847,0 1,0.447715 1,1 z" />
+ <path id="star" d="m 14.250652,7.0127142 -0.240235,2.15625 2.185547,-0.609375 0.193359,1.4765618 -1.992187,0.140625 1.306641,1.740234 -1.330079,0.708985 -0.914062,-1.833985 -0.802734,1.822266 -1.382813,-0.697266 1.294922,-1.740234 -1.980469,-0.152343 0.228516,-1.4648438 2.138672,0.609375 -0.240235,-2.15625 1.535157,0" />
+ <path id="right-bracket" d="m 21,12.044797 c -3e-6,0.981711 -0.152351,1.896229 -0.457043,2.743558 C 20.241767,15.635686 19.806209,16.3729 19.235883,17 l -1.453164,0 c 0.527356,-0.686824 0.93557,-1.455766 1.224642,-2.306829 0.289069,-0.854795 0.433604,-1.741318 0.433606,-2.659573 -2e-6,-0.910783 -0.140631,-1.797307 -0.421886,-2.6595792 C 18.737821,8.511764 18.321795,7.7204251 17.771,7 l 1.464883,0 c 0.574232,0.653236 1.011744,1.4128466 1.312536,2.2788341 0.300785,0.8622719 0.45118,1.7842569 0.451183,2.7659629" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/remove.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/remove.svg
new file mode 100644
index 00000000..6ad79174
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/remove.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="remove">
+ <path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ribbonPrize.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ribbonPrize.svg
new file mode 100644
index 00000000..6e4979f8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/ribbonPrize.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="Layer_1">
+ <g>
+ <circle cx="11.5" cy="8.5" r="2.5"/>
+ <path d="M16.3 8.7l.7-.7-.8-.8.4-.8-1.1-.5.1-.9-1.2-.2-.1-.9-1.2.2-.4-.8-1.1.5-.6-.8-.8.8-.9-.4-.5 1.1-.9-.2-.2 1.2-.9.2.2 1.2-.9.4.5 1.1-.6.6.8.8-.4.8 1.1.5-.1.9 1.2.2.1.9 1.2-.2.4.8 1.1-.5.6.8.8-.8.8.4.5-1.1.9.1.2-1.2.9-.1-.2-1.2.8-.4-.4-1zm-4.8 3.3c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5zm.5 3l-.7-.7-1.1.6-.4-.7-.8.3v8.5l2.5-3 2.5 3v-8.5l-1-.5z"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-ltr.svg
new file mode 100644
index 00000000..cdcbc30d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="search">
+ <path id="magnifying-glass" d="M 10.5,4 C 6.9101491,4 4,6.9101491 4,10.5 c 0,3.589851 2.9101491,6.5 6.5,6.5 1.02211,0 1.983324,-0.235899 2.84375,-0.65625 L 16,19 c 1.4,1.4 2.5,1.5 4,0 L 15.5625,14.5625 C 16.462737,13.447115 17,12.044969 17,10.5 17,6.9101491 14.089851,4 10.5,4 z m 0,2 C 12.985281,6 15,8.0147186 15,10.5 15,12.985281 12.985281,15 10.5,15 8.0147186,15 6,12.985281 6,10.5 6,8.0147186 8.0147186,6 10.5,6 z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-rtl.svg
new file mode 100644
index 00000000..c6753493
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/search-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="search">
+ <path id="magnifying-glass" d="m 13.5,4 c 3.589851,0 6.5,2.9101491 6.5,6.5 0,3.589851 -2.910149,6.5 -6.5,6.5 -1.02211,0 -1.983324,-0.235899 -2.84375,-0.65625 L 8,19 C 6.6,20.4 5.5,20.5 4,19 L 8.4375,14.5625 C 7.537263,13.447115 7,12.044969 7,10.5 7,6.9101491 9.910149,4 13.5,4 z m 0,2 C 11.014719,6 9,8.0147186 9,10.5 9,12.985281 11.014719,15 13.5,15 15.985281,15 18,12.985281 18,10.5 18,8.0147186 15.985281,6 13.5,6 z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/secure-link.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/secure-link.svg
new file mode 100644
index 00000000..a9c7d276
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/secure-link.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="secure">
+ <path id="lock" d="M8 5h.019v-.997c.001-.057.004-1.409-.832-2.255-.434-.438-.998-.66-1.679-.66s-1.245.222-1.678.659c-.837.847-.833 2.199-.832 2.251v1.002h.002c-.553 0-1 .447-1 1v3c0 .553.447 1 1 1h5c.553 0 1-.447 1-1v-3c0-.553-.447-1-1-1zm-4.002 0v-1.007c0-.01.005-.999.543-1.543.482-.485 1.449-.487 1.932-.002.544.546.546 1.536.546 1.55v1.002h-3.021z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/settings.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/settings.svg
new file mode 100644
index 00000000..bcd665ee
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/settings.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="settings">
+ <path id="gear" d="M3 4h3v2h-3zM12 4h9v2h-9zM8 3h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 11h9v2h-9zM18 11h3v2h-3zM14 10h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 18h6v2h-6zM15 18h6v2h-6zM11 17h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-ltr.svg
new file mode 100644
index 00000000..0d495049
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M0 20h24v1h-24v-1zm6-8l-1-1-2 2-2-2-1 1 2 2-2 2 1 1 2-2 2 2 1-1-2-2zm15.6 3.7c-.9-.5-1.9-.5-2.7 0-1.5.9-3.1.4-3.1.4-.4-.2-.8-.4-1.1-.6 2.2-.6 4.4-1.8 6-3.9 1.1-1.2 2.5-3.9.4-6-.7-.7-1.6-1.1-2.7-1-1.4.1-2.8.9-3.9 2.1-.9 1.1-3.1 4.5-2.3 7.5 0 .1 0 .2.1.3-2.3.3-4.2.2-4.4.1v1.5c.7.1 2.7.2 5.1-.2.5.7 1.3 1.2 2.3 1.6.1 0 2.4.8 4.5-.6.5-.3.9-.1 1.1 0 .4.2.7.6.7 1h1.4c0-.8-.6-1.7-1.4-2.2zm-8-1.7c-.5-2.2 1.1-5.1 2-6.2.8-.9 1.8-1.5 2.8-1.6h.1c.6 0 1.1.2 1.5.6 1.6 1.6-.4 3.9-.5 4-1.5 2-3.7 3-5.8 3.5l-.1-.3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-rtl.svg
new file mode 100644
index 00000000..6c0ae5e0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/signature-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M24 20h-24v1h24v-1zm-6-8l1-1 2 2 2-2 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2zm-15.6 3.7c.9-.5 1.9-.5 2.7 0 1.5.9 3.1.4 3.1.4.4-.2.8-.4 1.1-.6-2.2-.6-4.4-1.8-6-3.9-1.1-1.2-2.5-3.9-.4-6 .7-.7 1.6-1.1 2.7-1 1.4.1 2.8.9 3.9 2.1.9 1.1 3.1 4.5 2.3 7.5 0 .1 0 .2-.1.3 2.3.3 4.2.2 4.4.1v1.5c-.7.1-2.7.2-5.1-.2-.5.7-1.3 1.2-2.3 1.6-.1 0-2.4.8-4.5-.6-.5-.3-.9-.1-1.1 0-.4.2-.7.6-.7 1h-1.4c0-.8.6-1.7 1.4-2.2zm8-1.7c.5-2.2-1.1-5.1-2-6.2-.8-.9-1.8-1.5-2.8-1.6h-.1c-.6 0-1.1.2-1.5.6-1.6 1.6.4 3.9.5 4 1.5 2 3.7 3 5.8 3.5l.1-.3z" id="path576"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-ltr.svg
new file mode 100644
index 00000000..e8b427b1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+ <g id="down">
+ <path id="arrow" d="M22 3l-3.5 6L15 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-rtl.svg
new file mode 100644
index 00000000..e5e95196
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/smaller-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+ <g id="down">
+ <path id="arrow" d="M9 3L5.5 9 2 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/specialCharacter.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/specialCharacter.svg
new file mode 100644
index 00000000..4d601281
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/specialCharacter.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="special-character">
+ <path id="omega" d="M12 6.708c-.794 0-1.368.103-1.894.31-.525.207-.944.496-1.255.867-.311.366-.531.808-.66 1.327-.128.513-.192 1.08-.192 1.699 0 .513.058 1 .174 1.46.122.46.311.87.568 1.23.629.863 1.155 1.139 2.011 1.363l.247 3.035h-5v-3h.605l.531 1.354.394.053.605.044.751.035.456.009h.66l-.092-.894c-.629-.094-.811-.268-1.336-.522-.525-.26-.98-.59-1.365-.991-.379-.401-.675-.867-.889-1.398-.214-.537-.321-1.13-.321-1.779 0-.82.131-1.537.394-2.15.269-.619.656-1.133 1.163-1.54.507-.407 1.133-.711 1.878-.912.745-.206 1.6-.31 2.565-.31.959 0 1.811.103 2.556.31.751.201 1.38.504 1.887.912.507.407.892.92 1.154 1.54.269.614.403 1.33.403 2.15 0 .649-.107 1.242-.321 1.779-.214.531-.513.997-.898 1.398-.379.401-.831.732-1.356.991-.525.254-.707.428-1.336.522l-.092.894h.66l.447-.009.751-.035.605-.044.403-.053.531-1.354h.605v3h-5l.247-3.035c1.066-.11 1.337-.696 2.002-1.363.263-.36.452-.77.568-1.23.122-.46.183-.947.183-1.46 0-.619-.064-1.186-.192-1.699-.128-.519-.348-.962-.66-1.327-.311-.372-.73-.661-1.255-.867-.525-.206-1.1-.31-1.894-.31"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-ltr.svg
new file mode 100644
index 00000000..f3fb8b31
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M19 20h-17l3-3v-11h17v11c0 1.7-1.3 3-3 3z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-rtl.svg
new file mode 100644
index 00000000..fd9b7bd9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubble-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g586">
+ <path d="M5 20h17l-3-3v-11h-17v11c0 1.7 1.3 3 3 3z" id="path588"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-ltr.svg
new file mode 100644
index 00000000..333c1e86
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M19 20h-17l3-3v-11h17v11c0 1.7-1.3 3-3 3z"/>
+ </g>
+ <path fill="#fff" d="M13 9h1v7h-1zm-3 3h7v1h-7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-rtl.svg
new file mode 100644
index 00000000..4e6313f1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbleAdd-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g598">
+ <path d="M5 20h17l-3-3v-11h-17v11c0 1.7 1.3 3 3 3z" id="path600"/>
+ </g>
+ <path d="M11 9h-1v7h1zm3 3h-7v1h7z" id="path602" fill="#fff"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-ltr.svg
new file mode 100644
index 00000000..c4b4a2f7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20 9v9l2 2h-14v-11h12zm-17-5h12v4h-8v7h-6l2-2v-9z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-rtl.svg
new file mode 100644
index 00000000..c452fbbd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/speechBubbles-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g612">
+ <path d="M3 9v9l-2 2h14v-11h-12zm17-5h-12v4h8v7h6l-2-2v-9z" id="path614"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/star.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/star.svg
new file mode 100644
index 00000000..ea8c26c6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/star.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5 1.9-3.6m0-3.4l-2.8 5.6-6.2.9 4.5 4.4-1.1 6.1 5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9-2.7-5.5z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stop.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stop.svg
new file mode 100644
index 00000000..7bd06331
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stop.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 6c3.9 0 7 3.1 7 7s-3.1 7-7 7-7-3.1-7-7 3.1-7 7-7m0-1c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-3 5h6v6h-6z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-a.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-a.svg
new file mode 100644
index 00000000..480189f5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-a.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-a">
+ <path id="strikethrough" d="M6 11h12v1h-12v-1z"/>
+ <path id="a" d="M12.666 6h-1.372l-4.48 12h1.705l1.494-4h3.999l1.508 4h1.666l-4.52-12zm-2.28 7l1.617-4.333 1.634 4.333h-3.251z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-s.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-s.svg
new file mode 100644
index 00000000..d57b652f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-s.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-s">
+ <path id="strikethrough" d="M6 12h12v1h-12v-1z"/>
+ <path id="s" d="M12.094 6c-1.133 0-2.076.287-2.75.9-.67.613-1 1.49-1 2.52 0 .889.221 1.602.719 2.13.498.528 1.279.91 2.312 1.14l.812.182v-.03c.656.147 1.128.375 1.375.63.252.256.375.607.375 1.11 0 .573-.172.97-.531 1.26-.358.291-.894.45-1.625.45-.477 0-.969-.074-1.469-.24-.502-.166-1.031-.417-1.562-.75l-.375-.238v2.158l.156.062c.58.237 1.143.417 1.688.54.549.121 1.07.18 1.562.18 1.286 0 2.297-.293 3-.9.709-.605 1.062-1.486 1.062-2.608 0-.943-.256-1.726-.781-2.312-.521-.592-1.305-1-2.344-1.229l-.812-.181c-.716-.148-1.204-.352-1.406-.539-.205-.203-.312-.485-.312-.935 0-.533.162-.899.5-1.17.342-.271.836-.42 1.531-.42.395 0 .818.052 1.25.181.433.127.908.333 1.406.6l.375.18v-2.041s-1.188-.383-1.688-.479c-.499-.098-.984-.151-1.468-.151z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-y.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-y.svg
new file mode 100644
index 00000000..8409dc15
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/strikethrough-y.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="strikethrough-y">
+ <path id="strikethrough" d="M6 11h12v1h-12v-1z"/>
+ <path id="a" d="M7 6h1.724l3.288 4.935 3.264-4.935h1.724l-4.194 6.285v5.715h-1.612v-5.715l-4.194-6.285"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-ltr.svg
new file mode 100644
index 00000000..acacc362
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M4 9h12v2h-12v-2zm0 3h8v2h-8v-2zm0-7h16v3h-16v-3zm16 14h-16v-3h16v3z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-rtl.svg
new file mode 100644
index 00000000..c38a283f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeFlow-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M20 9h-12v2h12v-2zm0 3h-8v2h8v-2zm0-7h-16v3h16v-3zm-16 14h16v-3h-16v3z" id="path624"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSideMenu.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSideMenu.svg
new file mode 100644
index 00000000..47e70d74
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSideMenu.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20 19h-16v-2h16v2z"/>
+ </g>
+ <g>
+ <path d="M20 15h-16v-2h16v2z"/>
+ </g>
+ <g>
+ <path d="M20 11h-16v-2h16v2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-ltr.svg
new file mode 100644
index 00000000..7f8822bc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20 11h-16v-2h16v2zm-16 1h8v2h-8v-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-rtl.svg
new file mode 100644
index 00000000..fcb10bad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeSummary-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g654">
+ <path d="M4 11h16v-2h-16v2zm16 1h-8v2h8v-2z" id="path656"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-ltr.svg
new file mode 100644
index 00000000..76c80d20
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M17 13h-13v-3h13v3zm-5 6h-8v-3h8v3zm-8-12v-3h16v3h-16z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-rtl.svg
new file mode 100644
index 00000000..308c2e62
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/stripeToC-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g666">
+ <path d="M7 13h13v-3h-13v3zm5 6h8v-3h-8v3zm8-12v-3h-16v3h16z" id="path668"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-ltr.svg
new file mode 100644
index 00000000..b7507daf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-ltr.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+ <path d="M18 13l-1 1v3l1 1h-1l-.527-.46L16 18h-1l1-1v-3l-1-1h1l.485.497L17 13z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-rtl.svg
new file mode 100644
index 00000000..9fe5325f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/subscript-rtl.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+ <path d="M8 13l1 1v3l-1 1h1l.527-.46L10 18h1l-1-1v-3l1-1h-1l-.485.497L9 13z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-ltr.svg
new file mode 100644
index 00000000..f1b7caf3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-ltr.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M18.1 5.1c0 .3-.1.6-.3.9l-1.4 1.4-.9-.8 2.2-2.2c.3.1.4.4.4.7zm-.5 5.3h3.2c0 .3-.1.6-.4.9s-.5.4-.8.4h-2v-1.3zm-6.2-5v-3.2c.3 0 .6.1.9.4s.4.5.4.8v2h-1.3zm6.4 11.7c-.3 0-.6-.1-.8-.3l-1.4-1.4.8-.8 2.2 2.2c-.2.2-.5.3-.8.3zm-11.6-12.2c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2h-3.2c0-.3.1-.6.4-.9s.5-.3.8-.3zm1.8 5.6l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8s.1-.6.3-.8z"/>
+ <circle cx="12" cy="11" r="4"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-rtl.svg
new file mode 100644
index 00000000..a625fb90
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/sun-rtl.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M5.9 5.1c0 .3.1.6.3.9l1.4 1.4.9-.8-2.2-2.2c-.3.1-.4.4-.4.7zm.5 5.3h-3.2c0 .3.1.6.4.9.3.3.5.4.8.4h2v-1.3zm6.2-5v-3.2c-.3 0-.6.1-.9.4-.3.3-.4.5-.4.8v2h1.3zm-6.4 11.7c.3 0 .6-.1.8-.3l1.4-1.4-.8-.8-2.2 2.2c.2.2.5.3.8.3zm11.6-12.2c-.3 0-.6.1-.8.3l-1.4 1.4.8.9 2.2-2.3c-.2-.2-.5-.3-.8-.3zm-5.2 11.7h-1.2v3.2c.3 0 .6-.1.9-.4.3-.3.4-.5.4-.8l-.1-2zm7-6.2h-2v1.2h3.2c0-.3-.1-.6-.4-.9-.3-.3-.5-.3-.8-.3zm-1.8 5.6l-1.4-1.4-.8.8 2.2 2.2c.2-.2.3-.5.3-.8 0-.3-.1-.6-.3-.8z" id="path678"/>
+ <circle cx="12" cy="11" r="4" id="circle680" transform="matrix(-1 0 0 1 24 0)"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-ltr.svg
new file mode 100644
index 00000000..39f30a76
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-ltr.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+ <path d="M18 7l-1 1v3l1 1h-1l-.527-.46L16 12h-1l1-1V8l-1-1h1l.485.497L17 7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-rtl.svg
new file mode 100644
index 00000000..eabab21c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/superscript-rtl.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+ <path d="M8 7l1 1v3l-1 1h1l.527-.46L10 12h1l-1-1V8l1-1h-1l-.485.497L9 7z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-caption.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-caption.svg
new file mode 100644
index 00000000..15bb06a6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-caption.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-caption">
+ <path id="caption" d="M6 6h12v3H6z"/>
+ <path id="table" d="M4 10v7h16v-7H4zm1 1h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2zM5 14h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-ltr.svg
new file mode 100644
index 00000000..798ee4a1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-ltr.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-column-ltr">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,5 2,0 0,14 -2,0 z"
+ id="column" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-rtl.svg
new file mode 100644
index 00000000..dfa33a08
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-column-rtl.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-column-rtl">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 17,5 2,0 0,14 -2,0 z"
+ id="column" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-after.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-after.svg
new file mode 100644
index 00000000..91d06644
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-after.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-row-after">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,17 14,0 0,2 -14,0 z"
+ id="row" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-before.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-before.svg
new file mode 100644
index 00000000..4b71f2a8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-insert-row-before.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-insert-row-before">
+ <path
+ d="m 13,9 -2,0 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 z"
+ id="plus" />
+ <path
+ d="m 5,5 14,0 0,2 -14,0 z"
+ id="row" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-merge-cells.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-merge-cells.svg
new file mode 100644
index 00000000..6a8b77d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table-merge-cells.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
+ <g id="table-merge-cells">
+ <g id="merge-cell-left">
+ <path id="cell-border" d="m 4,7 0,9 7,0 0,-3 -1,0.834 L 10,15 5,15 5,8 10,8 10,9.167 11,10 11,7 z" />
+ <path id="arrow" d="m 8,9 0,2 -2,0 0,1 2,0 0,2 3,-2.5 z" />
+ </g>
+ <use id="merge-cell-right" xlink:href="#merge-cell-left" transform="matrix(-1,0,0,1,24,0)" />
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table.svg
new file mode 100644
index 00000000..1ba8c440
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/table.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+
+ <g id="table-insert">
+ <path id="table" d="M4 5v13h16v-13zm2 2h5v4h-5zm7 0h5v4h-5zm-7 5h5v4h-5zm7 0h5v4h-5z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/tag.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/tag.svg
new file mode 100644
index 00000000..534824c8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/tag.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="tag">
+ <path d="M18.748 11.717c.389.389.389 1.025 0 1.414l-4.949 4.95c-.389.389-1.025.389-1.414 0l-6.01-6.01c-.389-.389-.707-1.157-.707-1.707l-.001-4.364c0-.55.45-1 1-1h4.364c.55 0 1.318.318 1.707.707l6.01 6.01zm-10.644-4.261c-.579.576-.578 1.514-.001 2.093.578.577 1.516.577 2.095.001.576-.578.576-1.517 0-2.095-.581-.576-1.518-.577-2.094.001z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-ltr.svg
new file mode 100644
index 00000000..6b594b29
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-ltr.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M24 6h-4v-4h-2v4h-4v2h4v4h2v-4h4z"/>
+ </g>
+ <path d="M19 13v7h-16c-1.1 0-2-.9-2-2v-11h12v-1h-13v12c0 1.7 1.3 3 3 3h17v-8h-1z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-rtl.svg
new file mode 100644
index 00000000..36b25a3e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/templateAdd-rtl.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g690">
+ <path d="M0 6h4v-4h2v4h4v2h-4v4h-2v-4h-4z" id="path692"/>
+ </g>
+ <path d="M5 13v7h16c1.1 0 2-.9 2-2v-11h-12v-1h13v12c0 1.7-1.3 3-3 3h-17v-8h1z" id="path694"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-lefttoright.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-lefttoright.svg
new file mode 100644
index 00000000..62526a03
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-lefttoright.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-dir-ltr">
+ <path d="M7 7h-2v-1h2l.469.5.531-.5h2v1h-2v10h2v1h-2l-.5-.531-.5.531h-2v-1h2zM13.976 16v-2h-2.976v-4h2.976v-1.956l6.024 3.978z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-righttoleft.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-righttoleft.svg
new file mode 100644
index 00000000..913bbfd6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-dir-righttoleft.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-dir-rtl">
+ <path d="M17 17h2v1h-2l-.469-.5-.531.5h-2v-1h2v-10h-2v-1h2l.5.531.5-.531h2v1h-2zM10.024 8v2h2.976v4h-2.976v1.956l-6.024-3.978z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-style.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-style.svg
new file mode 100644
index 00000000..0198c355
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/text-style.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="text-style">
+ <path id="a" d="M15.296 18h2.789l-1.14-12h-2.789l-8.156 12h2.789l2.039-3h4.183l.285 3zm-3.109-5l2.311-3.4.323 3.4h-2.634z"/>
+ <path id="underline" d="M6 19h12v1h-12v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-ltr.svg
new file mode 100644
index 00000000..7740e43e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11.1 13.1c-1.8-2.1-2.7-4.3-3-5.1h4.7l.7-2h-5.5v-3h-2v3h-5v2h5c-.2.9-1.3 4.8-5.1 7.6l1.2 1.6c2.7-2 4.3-4.5 5.1-6.4.7 1.3 1.7 3 3.2 4.5l.7-2.2zm1.4 6.9l1.3-4h5.3l1.3 4h2.2l-4.6-14h-3l-4.7 14h2.2zm4-12l2 6h-4l2-6z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-rtl.svg
new file mode 100644
index 00000000..c78e6222
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/translation-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12.4 13.1c1.8-2.1 2.7-4.3 3-5.1h-4.7l-.7-2h5.5v-3h2v3h5v2h-5c.2.9 1.3 4.8 5.1 7.6l-1.2 1.6c-2.7-2-4.3-4.5-5.1-6.4-.7 1.3-1.7 3-3.2 4.5l-.7-2.2zm-1.4 6.9l-1.3-4h-5.3l-1.3 4h-2.2l4.6-14h3l4.7 14h-2.2zm-4-12l-2 6h4l-2-6z" id="path704"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trash.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trash.svg
new file mode 100644
index 00000000..f5914312
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trash.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2h-12zm1 1h10l-1 11h-8z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-ltr.svg
new file mode 100644
index 00000000..0731f056
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M20.5 20.5l-15.5-15.5-1 1 3 3 1 11h8l.2-1.8 3.3 3.3zm-3.5-11.5h-6l5.5 5.5zm1-1c0-1.1-.9-2-2-2h-2l-1-1h-2l-1 1h-2l2 2h8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-rtl.svg
new file mode 100644
index 00000000..2a92cbef
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/trashUndo-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g714">
+ <path d="M4 20.5l15.5-15.5 1 1-3 3-1 11h-8l-.2-1.8-3.3 3.3zm3.5-11.5h6l-5.5 5.5zm-1-1c0-1.1.9-2 2-2h2l1-1h2l1 1h2l-2 2h-8z" id="path716"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-ltr.svg
new file mode 100644
index 00000000..66c024a9
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 9v-2s0-5-4.5-5-4.5 5-4.5 5h2s0-3 2.5-3 2.5 3 2.5 3v2h-3v7c0 1.7 1.3 3 3 3h10v-10z" id="path726"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-rtl.svg
new file mode 100644
index 00000000..07cecbfe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unLock-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M11 9v-2s0-5 4.5-5 4.5 5 4.5 5h-2s0-3-2.5-3-2.5 3-2.5 3v2h3v7c0 1.7-1.3 3-3 3h-10v-10z" id="path726"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unStar.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unStar.svg
new file mode 100644
index 00000000..724d1901
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/unStar.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-a.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-a.svg
new file mode 100644
index 00000000..dd6dde36
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-a.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="underline-a">
+ <path id="a" d="M14.424 16h2.076l-3.463-10h-2.077l-3.46 10h2.077l.627-2h3.604l.616 2zm-3.921-3.623l1.496-4.379 1.511 4.379h-3z"/>
+ <path id="underline" d="M7 17h10v1h-10v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-u.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-u.svg
new file mode 100644
index 00000000..fbd7c147
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/underline-u.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="underline-u">
+ <path id="u" d="M8 6h2v5.959c-.104 1.707.695 2.002 2 2.041 1.777.062 2.002-.879 2-2.041v-5.959h2v6.123c0 1.279-.338 2.245-1.016 2.898-.672.651-1.666.979-2.98.979-1.32 0-2.319-.326-2.996-.979-.672-.653-1.008-1.619-1.008-2.898v-6.123"/>
+ <path id="underline" d="M7 17h10v1h-10v-1z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upTriangle.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upTriangle.svg
new file mode 100644
index 00000000..9e5e72f6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upTriangle.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 8l8 10h-16z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-ltr.svg
new file mode 100644
index 00000000..18879e32
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M10 13c0 1.7 1.3 3 3 3v-7h3l-4.5-5-4.5 5h3v4zm7 0v5h-10c-.6 0-1-.4-1-1v-4h-2v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-rtl.svg
new file mode 100644
index 00000000..7a3535ba
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/upload-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g736">
+ <path d="M13 13c0 1.7-1.3 3-3 3v-7h-3l4.5-5 4.5 5h-3v4zm-7 0v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3h-12v-7h2z" id="path738"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-ltr.svg
new file mode 100644
index 00000000..5dcc3179
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16 5h-12v12c0 1.7 1.3 3 3 3h12v-12c0-1.7-1.3-3-3-3zm-2 4c.7 0 1.2.6 1.2 1.2s-.6 1.2-1.2 1.2-1.2-.6-1.2-1.2.5-1.2 1.2-1.2zm-5 0c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2.5-1.3 1.2-1.3zm7 5.4c0 .2-.1.3-.3.5-.7.6-1.6 1-2.6 1.3s-2.1.2-3.1 0-2-.9-2.7-1.5c-.1-.1-.2-.3-.2-.4s.1-.3.2-.4c.1-.1.3-.2.4-.2.2 0 .3.1.4.2.5.5 1.2.9 2.1 1.1s1.7.2 2.6 0 1.6-.5 2.1-1c.1-.1.3-.2.4-.2s.3.1.5.2.2.2.2.4z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-rtl.svg
new file mode 100644
index 00000000..a5e4dc95
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userActive-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 5h12v12c0 1.7-1.3 3-3 3h-12v-12c0-1.7 1.3-3 3-3zm2 4c-.7 0-1.2.6-1.2 1.2s.6 1.2 1.2 1.2 1.2-.6 1.2-1.2-.5-1.2-1.2-1.2zm5 0c-.7 0-1.2.6-1.2 1.2s.5 1.3 1.2 1.3 1.2-.6 1.2-1.2-.5-1.3-1.2-1.3zm-7 5.4c0 .2.1.3.3.5.7.6 1.6 1 2.6 1.3 1 .3 2.1.2 3.1 0s2-.9 2.7-1.5c.1-.1.2-.3.2-.4 0-.1-.1-.3-.2-.4-.1-.1-.3-.2-.4-.2-.2 0-.3.1-.4.2-.5.5-1.2.9-2.1 1.1-.9.2-1.7.2-2.6 0-.9-.2-1.6-.5-2.1-1-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.5.2s-.2.2-.2.4z" id="path748"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userAvatar.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userAvatar.svg
new file mode 100644
index 00000000..e9687fa6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userAvatar.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g4">
+ <g id="g6">
+ <path d="M11.5 13c1.7 0 3.5-2 3.5-5 0-.1 0-4-3.5-4s-3.5 3.9-3.5 4c0 3 1.8 5 3.5 5zm3.5-1c-.4.7-1.7 2-3.5 2s-3.2-1.3-3.5-2h-2c-1.1 0-2 .9-2 2v6h15v-6c0-1.1-.9-2-2-2h-2z" id="path8"/>
+ </g>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-ltr.svg
new file mode 100644
index 00000000..bb5b0968
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M16 5h-12v12c0 1.7 1.3 3 3 3h12v-12c0-1.7-1.3-3-3-3zm-9.3 5.4c-.5-.4-.7-.8-.7-1.4.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6zm8.4 4.3c0 .2-.1.3-.3.4-1 .6-2.2.9-3.5.9-1.2 0-2.3-.3-3.3-1-.2-.1-.2-.2-.3-.4s0-.3.1-.5.2-.2.4-.3.3 0 .5.1c.8.5 1.7.8 2.8.8s2-.2 2.8-.7c.1-.1.3-.1.5-.1s.3.1.4.3l-.1.5zm1.2-4.3c-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6-.7-.8-.7-1.4c.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-rtl.svg
new file mode 100644
index 00000000..4a9fd0d7
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userInactive-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M7 5h12v12c0 1.7-1.3 3-3 3h-12v-12c0-1.7 1.3-3 3-3zm9.3 5.4c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9s-1.9-.3-2.5-.9c0 .6.2 1 .7 1.4.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6zm-8.4 4.3c0 .2.1.3.3.4 1 .6 2.2.9 3.5.9 1.2 0 2.3-.3 3.3-1 .2-.1.2-.2.3-.4.1-.2 0-.3-.1-.5s-.2-.2-.4-.3c-.2-.1-.3 0-.5.1-.8.5-1.7.8-2.8.8-1.1 0-2-.2-2.8-.7-.1-.1-.3-.1-.5-.1s-.3.1-.4.3l.1.5zm-1.2-4.3c.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9s-1.9-.3-2.5-.9c0 .6.2 1 .7 1.4z" id="path758"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-ltr.svg
new file mode 100644
index 00000000..f516539c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-ltr.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3v-11h-17zm11.2 2.5c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2.6-1.3 1.2-1.3zm-5.4 0c.7 0 1.2.6 1.2 1.2s-.6 1.3-1.2 1.3-1.2-.6-1.2-1.2.5-1.3 1.2-1.3zm2.7 8.5c-5.1 0-6-5-6-5s2 1 6 1l6-1s-1 5-6 5z" id="path6"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-rtl.svg
new file mode 100644
index 00000000..8963fafc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/userTalk-rtl.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M19 6v11l3 3h-17c-1.7 0-3-1.3-3-3v-11h17zm-11.2 2.5c-.7 0-1.2.6-1.2 1.2s.5 1.3 1.2 1.3 1.2-.6 1.2-1.2-.6-1.3-1.2-1.3zm5.4 0c-.7 0-1.2.6-1.2 1.2s.6 1.3 1.2 1.3 1.2-.6 1.2-1.2-.5-1.3-1.2-1.3zm-2.7 8.5c5.1 0 6-5 6-5s-2 1-6 1l-6-1s1 5 6 5z" id="path770"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewCompact.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewCompact.svg
new file mode 100644
index 00000000..d96a2e3f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewCompact.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="viewCompact">
+ <circle cx="6" cy="6" r="2"/>
+ <circle cx="12" cy="6" r="2"/>
+ <circle cx="18" cy="6" r="2"/>
+ <circle cx="6" cy="12" r="2"/>
+ <circle cx="12" cy="12" r="2"/>
+ <circle cx="18" cy="12" r="2"/>
+ <circle cx="6" cy="18" r="2"/>
+ <circle cx="12" cy="18" r="2"/>
+ <circle cx="18" cy="18" r="2"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-ltr.svg
new file mode 100644
index 00000000..4f5f9b3d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-ltr.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="viewDetails">
+ <circle cx="5.5" cy="8.5" r="2.5"/>
+ <path d="M10 6h12v1H10zM10 8h9v1h-9zM10 10h4v1h-4z"/>
+ <circle cx="5.5" cy="16.5" r="2.5"/>
+ <path d="M10 14h12v1H10zM10 16h9v1h-9zM10 18h4v1h-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-rtl.svg
new file mode 100644
index 00000000..f43b05f1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/viewDetails-rtl.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="viewDetails">
+ <circle cx="18.5" cy="8.5" r="2.5"/>
+ <path d="M14 6H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
+ <circle cx="18.5" cy="16.5" r="2.5"/>
+ <path d="M14 14H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/visionSimulator.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/visionSimulator.svg
new file mode 100644
index 00000000..ae6ba27a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/visionSimulator.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M0 10v8h2.3c.3.6 1 1 1.7 1h4c1.5 0 2.7-.8 3-2h2c.3 1.2 1.5 2 3 2h4c.7 0 1.4 0 1.7-1H24v-8zm10 6c0 1-.4 2-2 2H4c-.6 0-1-.4-1-1v-3c0-.6.4-1 1-1h5c.6 0 1 .4 1 1zm11 1c0 .6-.4 1-1 1h-4c-1.6 0-2-1-2-2v-2c0-.6.4-1 1-1h5c.6 0 1 .4 1 1z"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-ltr.svg
new file mode 100644
index 00000000..79c7d5ca
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M13 14h5v1h-5v-1zm0 3h5v-1h-5v1zm0 1h5v1h-5v-1zm-1-5v3l-5 3 1-6-4-3 6-1 2-5s1.9 5 2 5l6 1-4 3h-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-rtl.svg
new file mode 100644
index 00000000..6bbc2fa2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/watchlist-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g780">
+ <path d="M11 14h-5v1h5v-1zm0 3h-5v-1h5v1zm0 1h-5v1h5v-1zm1-5v3l5 3-1-6 4-3-6-1-2-5s-1.9 5-2 5l-6 1 4 3h4z" id="path782"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikiText.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikiText.svg
new file mode 100644
index 00000000..eebd9b1a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikiText.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M23 5h-4v2h2v10h-2v2h4z"/>
+ </g>
+ <g>
+ <path d="M18 5h-4v2h2v10h-2v2h4z"/>
+ </g>
+ <g>
+ <path d="M2 5h4v2h-2v10h2v2h-4z"/>
+ </g>
+ <g>
+ <path d="M7 5h4v2h-2v10h2v2h-4z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-ltr.svg
new file mode 100644
index 00000000..c606becd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M15 9l.7-1.8c.9.4 1.8.7 2.4.9l-.6 1.7v.2l-2.5-1zm-4.3-1.9l.8-1.8c1.2.5 2.6 1.1 3 1.4l-.8 1.8-3-1.4zm-5.9-1c-.8 0-1.4.2-2 .6l-1.1-1.7c.9-.6 1.9-.9 3.1-.9v2zm-4.3.7l1.8.8c-.3.7-.3 1.3-.1 1.8l-1.9.7c-.3-1.2-.3-2.3.2-3.3zm4.2 5.4l-1.3 1.5c-1-1-1.7-1.6-2-2l1.5-1.3c.7.8 1.3 1.4 1.8 1.8zm7.3 4.3c0 1.9-1.6 3.5-3.5 3.5s-3.5-1.6-3.5-3.5 1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5z"/>
+ </g>
+ <path d="M24 8l-1-1-1.5 1.5-1.5-1.5-1 1 1.5 1.5-1.5 1.5 1 1 1.5-1.5 1.5 1.5 1-1-1.5-1.5z"/>
+ <circle cx="8" cy="5" r="2"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-rtl.svg
new file mode 100644
index 00000000..f304f6ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/wikitrail-rtl.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="g792">
+ <path d="M9.095 9l-.7-1.8c-.9.4-1.8.7-2.4.9l.6 1.7v.2l2.5-1zm4.3-1.9l-.8-1.8c-1.2.5-2.6 1.1-3 1.4l.8 1.8 3-1.4zm5.9-1c.8 0 1.4.2 2 .6l1.1-1.7c-.9-.6-1.9-.9-3.1-.9v2zm4.3.7l-1.8.8c.3.7.3 1.3.1 1.8l1.9.7c.3-1.2.3-2.3-.2-3.3zm-4.2 5.4l1.3 1.5c1-1 1.7-1.6 2-2l-1.5-1.3c-.7.8-1.3 1.4-1.8 1.8zm-7.3 4.3c0 1.9 1.6 3.5 3.5 3.5s3.5-1.6 3.5-3.5-1.6-3.5-3.5-3.5-3.5 1.6-3.5 3.5z" id="path794"/>
+ </g>
+ <path d="M.095 8l1-1 1.5 1.5 1.5-1.5 1 1-1.5 1.5 1.5 1.5-1 1-1.5-1.5-1.5 1.5-1-1 1.5-1.5z" id="path796"/>
+ <circle cx="8" cy="5" r="2" id="circle798" transform="matrix(-1 0 0 1 24.095 0)"/>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/window.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/window.svg
new file mode 100644
index 00000000..cd3b76c2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/icons/window.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <g id="window">
+ <path id="title" d="M7 10h10v1h-10z"/>
+ <path id="frame" d="M16 19h-8c-2.206 0-4-1.794-4-4v-6c0-2.206 1.794-4 4-4h8c2.206 0 4 1.794 4 4v6c0 2.206-1.794 4-4 4zm-8-12c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2h8c1.103 0 2-.897 2-2v-6c0-1.103-.897-2-2-2h-8z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/alert.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/alert.svg
new file mode 100644
index 00000000..d9dc6a87
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/alert.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="alert">
+ <path d="M6 12c-3.314 0-6-2.686-6-6s2.686-6 6-6 6 2.686 6 6-2.686 6-6 6zm-1-5h2v-5h-2zm0 3h2v-2h-2z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-down.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-down.svg
new file mode 100644
index 00000000..17380577
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-down.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
+<g id="down">
+ <path id="arrow" d="M883.3,341H116.7L500,724.3l0,0l0,0L883.3,341"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-ltr.svg
new file mode 100644
index 00000000..fb366e64
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-ltr.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-493 495 12 12" enable-background="new -493 495 12 12" xml:space="preserve">
+<g id="ltr">
+ <path id="arrow" d="M-489,496v10l5-5h0h0L-489,496"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-rtl.svg
new file mode 100644
index 00000000..62b6bb50
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-rtl.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-493 495 12 12" enable-background="new -493 495 12 12" xml:space="preserve">
+<g id="rtl">
+ <path id="arrow" d="M-485,506v-10l-5,5h0h0L-485,506"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-up.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-up.svg
new file mode 100644
index 00000000..20e734fb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/arrow-up.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="-493 495 12 12" enable-background="new -493 495 12 12" xml:space="preserve">
+<g id="up">
+ <path id="arrow" d="M-492,503h10l-5-5v0v0L-492,503"/>
+</g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/required.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/required.svg
new file mode 100644
index 00000000..969fa2d8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/required.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="required">
+ <path d="M5 1h2v10h-2zM9.83 2.634l1 1.732-8.66 5-1-1.732zM1.17 4.366l1-1.732 8.66 5-1 1.732z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-ltr.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-ltr.svg
new file mode 100644
index 00000000..266349ed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-ltr.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="search">
+ <path id="path3051" d="M10.369 9.474l-2.374-2.375-.169-.099c.403-.566.643-1.26.643-2.009-.001-1.92-1.558-3.477-3.477-3.477-1.921 0-3.478 1.557-3.478 3.478 0 1.92 1.557 3.477 3.478 3.477.749 0 1.442-.239 2.01-.643l.098.169 2.375 2.374c.19.189.543.143.79-.104s.293-.601.104-.791zm-5.377-2.27c-1.221 0-2.213-.991-2.213-2.213 0-1.221.992-2.213 2.213-2.213 1.222 0 2.213.992 2.213 2.213-.001 1.222-.992 2.213-2.213 2.213z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-rtl.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-rtl.svg
new file mode 100644
index 00000000..5368fd7c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/indicators/search-rtl.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <g id="search">
+ <path id="path3051" d="M1.631 9.474l2.374-2.375.169-.099c-.403-.566-.643-1.26-.643-2.009.001-1.92 1.558-3.477 3.477-3.477 1.921 0 3.478 1.557 3.478 3.478 0 1.92-1.557 3.477-3.478 3.477-.749 0-1.442-.239-2.01-.643l-.098.169-2.375 2.374c-.19.189-.543.143-.79-.104s-.293-.601-.104-.791zm5.377-2.27c1.221 0 2.213-.991 2.213-2.213 0-1.221-.992-2.213-2.213-2.213-1.222 0-2.213.992-2.213 2.213.001 1.222.992 2.213 2.213 2.213z"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/pending.gif b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/pending.gif
new file mode 100644
index 00000000..1194eed2
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/pending.gif
Binary files differ
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/transparency.svg b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/transparency.svg
new file mode 100644
index 00000000..63a0b57c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/images/textures/transparency.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="16" height="16" viewBox="0, 0, 16, 16">
+ <g id="transparency">
+ <path d="M0,0 L8,0 L8,8 L0,8 z" fill="#CCCCCC"/>
+ <path d="M8,8 L16,8 L16,16 L8,16 z" fill="#CCCCCC"/>
+ <path d="M8,0 L16,0 L16,8 L8,8 z" fill="#FFFFFF"/>
+ <path d="M0,8 L8,8 L8,16 L0,16 z" fill="#FFFFFF"/>
+ </g>
+</svg>
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/indicators.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/indicators.json
new file mode 100644
index 00000000..d83e57e6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/indicators.json
@@ -0,0 +1,29 @@
+{
+ "selectorWithoutVariant": ".oo-ui-indicator-{name}",
+ "selectorWithVariant": ".oo-ui-image-{variant} .oo-ui-indicator-{name}, .oo-ui-image-{variant}.oo-ui-indicator-{name}",
+ "intro": "@import '../../../../src/styles/common';",
+ "variants": {
+ "invert": {
+ "color": "#FFFFFF",
+ "global": true
+ }
+ },
+ "images": {
+ "alert": { "file": "images/indicators/alert.svg" },
+ "up": { "file": "images/indicators/arrow-up.svg" },
+ "down": { "file": "images/indicators/arrow-down.svg" },
+ "next": { "file": {
+ "ltr": "images/indicators/arrow-ltr.svg",
+ "rtl": "images/indicators/arrow-rtl.svg"
+ } },
+ "previous": { "file": {
+ "ltr": "images/indicators/arrow-rtl.svg",
+ "rtl": "images/indicators/arrow-ltr.svg"
+ } },
+ "required": { "file": "images/indicators/required.svg" },
+ "search": { "file": {
+ "ltr": "images/indicators/search-ltr.svg",
+ "rtl": "images/indicators/search-rtl.svg"
+ } }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/layouts.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/layouts.less
new file mode 100644
index 00000000..c4956f33
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/layouts.less
@@ -0,0 +1,135 @@
+@import 'common';
+
+.theme-oo-ui-layout () {}
+
+.theme-oo-ui-bookletLayout () {
+ &-stackLayout {
+ > .oo-ui-panelLayout {
+ padding: 1.5em;
+ }
+ }
+
+ &-outlinePanel {
+ border-right: 1px solid #ddd;
+
+ > .oo-ui-outlineControlsWidget {
+ box-shadow: 0 0 0.25em rgba(0,0,0,0.25);
+ }
+ }
+}
+
+.theme-oo-ui-indexLayout () {
+ &-stackLayout {
+ > .oo-ui-panelLayout {
+ padding: 1.5em;
+ }
+ }
+}
+
+.theme-oo-ui-fieldLayout () {
+ margin-bottom: 1em;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &.oo-ui-fieldLayout-align-left,
+ &.oo-ui-fieldLayout-align-right {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding-top: 0.5em;
+ margin-right: 5%;
+ width: 35%;
+ }
+
+ > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+ width: 60%;
+ }
+ }
+
+ &.oo-ui-fieldLayout-align-inline {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding: 0.5em;
+ padding-left: 1em;
+ }
+
+ > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+ padding: 0.5em 0;
+ }
+ }
+
+ &.oo-ui-fieldLayout-align-top {
+ &.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+ padding: 0.5em 0;
+ }
+ }
+
+ > .oo-ui-popupButtonWidget {
+ .oo-ui-inline-spacing(0);
+ margin-top: 0.25em;
+ }
+
+ &-disabled .oo-ui-labelElement-label {
+ color: #ccc;
+ }
+}
+
+.theme-oo-ui-actionFieldLayout () {}
+
+.theme-oo-ui-fieldsetLayout () {
+ margin: 0;
+ padding: 0;
+ border: none;
+
+ + .oo-ui-fieldsetLayout,
+ + .oo-ui-formLayout {
+ margin-top: 2em;
+ }
+
+ > .oo-ui-labelElement-label {
+ font-size: 1.1em;
+ margin-bottom: 0.5em;
+ padding: 0.25em 0;
+ font-weight: bold;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-labelElement-label {
+ padding-left: 2em;
+ line-height: 1.8em;
+ }
+
+ &.oo-ui-iconElement > .oo-ui-iconElement-icon {
+ left: 0;
+ top: 0.25em;
+ width: @icon-size;
+ height: @icon-size;
+ }
+
+ > .oo-ui-popupButtonWidget {
+ .oo-ui-inline-spacing(0);
+ }
+}
+
+.theme-oo-ui-formLayout () {
+ + .oo-ui-fieldsetLayout,
+ + .oo-ui-formLayout {
+ margin-top: 2em;
+ }
+}
+
+.theme-oo-ui-menuLayout () {}
+
+.theme-oo-ui-panelLayout () {
+ &-padded {
+ padding: 1.25em;
+ }
+
+ &-framed {
+ border: 1px solid #aaa;
+ border-radius: 0.2em;
+ box-shadow: inset 0 -0.2em 0 0 rgba(0,0,0,0.2);
+ }
+}
+
+.theme-oo-ui-pageLayout () {}
+
+.theme-oo-ui-stackLayout () {}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/textures.json b/vendor/oojs/oojs-ui/src/themes/mediawiki/textures.json
new file mode 100644
index 00000000..e90730ab
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/textures.json
@@ -0,0 +1,8 @@
+{
+ "prefix": "oo-ui-texture",
+ "intro": "@import '../../../../src/styles/common';",
+ "images": {
+ "pending": { "file": "images/textures/pending.gif" },
+ "transparency": { "file": "images/textures/transparency.svg" }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/tools.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/tools.less
new file mode 100644
index 00000000..fa5d6ddc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/tools.less
@@ -0,0 +1,422 @@
+@import 'common';
+
+@oo-ui-toolbar-normal: #fff;
+@oo-ui-toolbar-normal-hover: #eee;
+@oo-ui-toolbar-active: #e5e5e5;
+@oo-ui-toolbar-active-hover: #eee;
+@oo-ui-toolbar-pressed: #e7e7e7;
+@oo-ui-toolbar-bar-text: #555;
+@oo-ui-toolbar-dropdown-text: #000;
+
+.theme-oo-ui-toolbar () {
+ &-bar {
+ border-bottom: 1px solid #ccc;
+ background-color: @oo-ui-toolbar-normal;
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1);
+ font-weight: 500;
+ color: @oo-ui-toolbar-bar-text;
+
+ .oo-ui-toolbar-bar {
+ border: none;
+ background: none;
+ box-shadow: none;
+ }
+ }
+
+ &-actions {
+ > .oo-ui-buttonElement {
+ margin-top: 0.25em;
+ margin-bottom: 0.25em;
+ }
+
+ > .oo-ui-toolbar,
+ > .oo-ui-buttonElement:last-child {
+ margin-right: 0.5em;
+ }
+ }
+}
+
+.theme-oo-ui-tool () {}
+
+.theme-oo-ui-popupTool () {
+ .oo-ui-popupWidget {
+ /* @noflip */
+ margin-left: 1.25em;
+ }
+}
+
+.theme-oo-ui-toolGroupTool () {
+ > .oo-ui-popupToolGroup {
+ border: 0;
+ border-radius: 0;
+ margin: 0;
+ }
+
+ > .oo-ui-toolGroup {
+ border-right: none;
+ }
+
+ > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
+ height: 2.5em;
+ padding: 0.3125em;
+
+ .oo-ui-iconElement-icon {
+ height: 2.5em;
+ width: 1.875em;
+ }
+ }
+
+ > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle {
+ .oo-ui-labelElement-label {
+ line-height: 2.1em;
+ }
+ }
+}
+
+.theme-oo-ui-toolGroup () {
+ border-radius: 0px;
+ border-right: 1px solid #ddd;
+
+ .oo-ui-toolbar-narrow & {
+ + .oo-ui-toolGroup {
+ margin-left: 0;
+ }
+ }
+
+ .oo-ui-toolGroup {
+ .oo-ui-widget-enabled {
+ border-right: none !important;
+ }
+ }
+}
+
+.theme-oo-ui-barToolGroup () {
+ > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ > .oo-ui-tool-link {
+ height: 1.875em;
+ padding: 0.625em;
+
+ .oo-ui-iconElement-icon {
+ height: 1.875em;
+ width: 1.875em;
+ }
+
+ .oo-ui-tool-title {
+ line-height: 2.1em; // 0.5em less
+ padding: 0em 0.4em;
+ }
+ }
+ }
+
+ &.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ &.oo-ui-widget-enabled:hover {
+ border-color: rgba(0,0,0,0.2);
+ background-color: @oo-ui-toolbar-normal-hover;
+ }
+
+ > a.oo-ui-tool-link .oo-ui-tool-title {
+ color: @oo-ui-toolbar-bar-text;
+ }
+
+ &.oo-ui-tool-active {
+ &.oo-ui-widget-enabled {
+ border-color: rgba(0,0,0,0.2);
+ box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+ background-color: @oo-ui-toolbar-active;
+ }
+
+ &.oo-ui-widget-enabled:hover {
+ background-color: @oo-ui-toolbar-active-hover;
+ }
+
+ &.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+ border-left-color: rgba(0,0,0,0.1);
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-tool-link {
+ .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ > .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 0.7;
+ }
+
+ &:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 0.9;
+ }
+ }
+
+ &.oo-ui-widget-enabled:active {
+ background-color: @oo-ui-toolbar-pressed;
+ }
+ }
+
+ &.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool {
+ > a.oo-ui-tool-link {
+ .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+}
+
+.theme-oo-ui-popupToolGroup () {
+ height: 3.125em;
+ min-width: 2em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 1.875em;
+ }
+
+ &.oo-ui-iconElement {
+ min-width: 3.125em;
+ .oo-ui-toolbar-narrow & {
+ min-width: 2.5em;
+ }
+ }
+
+ &.oo-ui-indicatorElement.oo-ui-iconElement {
+ min-width: 4.375em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 3.75em;
+ }
+ }
+
+ &.oo-ui-labelElement {
+ .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ line-height: 2.6em;
+ margin: 0 1em;
+
+ .oo-ui-toolbar-narrow & {
+ margin: 0 0.5em;
+ }
+ }
+
+ &.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ margin-left: 3em;
+
+ .oo-ui-toolbar-narrow & {
+ margin-left: 2.5em;
+ }
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+ margin-right: 2em;
+
+ .oo-ui-toolbar-narrow & {
+ margin-right: 1.75em;
+ }
+ }
+ }
+
+ &.oo-ui-widget-enabled &-handle:hover {
+ background-color: @oo-ui-toolbar-normal-hover;
+ }
+
+ &.oo-ui-widget-enabled &-handle:active {
+ background-color: @oo-ui-toolbar-active;
+ }
+
+ &-handle {
+ padding: 0.3125em;
+ height: 2.5em;
+
+ .oo-ui-indicatorElement-indicator {
+ width: 0.9375em;
+ height: 1.625em;
+ margin: 0.78125em 0.5em;
+ top: 0;
+ right: 0;
+ opacity: 0.3;
+
+ .oo-ui-toolbar-narrow & {
+ right: -0.3125em;
+ }
+ }
+
+ .oo-ui-iconElement-icon {
+ width: 1.875em;
+ height: 2.6em;
+ margin: 0.25em;
+ top: 0;
+ left: 0.3125em;
+ opacity: 0.7;
+
+ .oo-ui-toolbar-narrow & {
+ left: 0;
+ }
+ }
+ }
+
+ &-header {
+ line-height: 2.6em;
+ margin: 0 0.6em;
+ font-weight: bold;
+ }
+
+ &-active.oo-ui-widget-enabled {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+ background-color: @oo-ui-toolbar-normal-hover;
+ }
+
+ .oo-ui-toolGroup-tools {
+ top: 3.125em;
+ margin: 0 -1px;
+ border: 1px solid #ccc;
+ background-color: @oo-ui-toolbar-normal;
+ box-shadow: 0px 2px 3px rgba(0,0,0,0.2);
+ min-width: 16em;
+ }
+
+ .oo-ui-tool-link {
+ padding: 0.4em 0.625em;
+ box-sizing: border-box;
+
+ .oo-ui-iconElement-icon {
+ height: 2.5em;
+ width: 1.875em;
+ min-width: 1.875em;
+ }
+
+ .oo-ui-tool-title {
+ padding-left: 0.5em;
+ color: @oo-ui-toolbar-dropdown-text;
+ }
+
+ .oo-ui-tool-accel,
+ .oo-ui-tool-title {
+ line-height: 2em;
+ }
+
+ .oo-ui-tool-accel {
+ color: #888;
+ }
+ }
+}
+
+.theme-oo-ui-listToolGroup () {
+ &.oo-ui-popupToolGroup-active {
+ border-color: rgba(0,0,0,0.2);
+ }
+
+ .oo-ui-tool {
+ &.oo-ui-widget-enabled {
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ background-color: @oo-ui-toolbar-normal-hover;
+ }
+
+ &:active {
+ background-color: @oo-ui-toolbar-pressed;
+ }
+
+ &:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 0.9;
+ }
+ }
+
+ &-active {
+ &.oo-ui-widget-enabled {
+ border-color: rgba(0,0,0,0.1);
+ box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+ background-color: @oo-ui-toolbar-active;
+
+ + .oo-ui-tool-active.oo-ui-widget-enabled {
+ border-top-color: rgba(0,0,0,0.1);
+ }
+
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ background-color: @oo-ui-toolbar-active-hover;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link {
+ .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-tool-accel {
+ color: #ddd;
+ }
+
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+}
+
+.theme-oo-ui-menuToolGroup () {
+ .oo-ui-popupToolGroup-handle {
+ min-width: 10em;
+
+ .oo-ui-toolbar-narrow & {
+ min-width: 8.125em;
+ }
+ }
+
+ .oo-ui-tool {
+ &-link {
+ .oo-ui-iconElement-icon {
+ background-image: none;
+ }
+ }
+
+ &-active .oo-ui-tool-link .oo-ui-iconElement-icon {
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/check');
+ }
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ background-color: @oo-ui-toolbar-normal-hover;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-tool-link .oo-ui-tool-title {
+ color: #ccc;
+ }
+
+ .oo-ui-tool-link .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+
+ .oo-ui-indicatorElement-indicator,
+ .oo-ui-iconElement-icon {
+ opacity: 0.2;
+ }
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/widgets.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/widgets.less
new file mode 100644
index 00000000..2f072878
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/widgets.less
@@ -0,0 +1,978 @@
+@import 'common';
+
+.theme-oo-ui-widget () {}
+
+.theme-oo-ui-outlineControlsWidget () {
+ height: 3em;
+ background-color: #fff;
+
+ &-items,
+ &-movers {
+ height: 2em;
+ margin: 0.5em 0.5em 0.5em 0;
+ padding: 0;
+ }
+
+ > .oo-ui-iconElement-icon {
+ width: 1.5em;
+ height: 2em;
+ margin: 0.5em 0 0.5em 0.5em;
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-toggleWidget () {}
+
+.theme-oo-ui-buttonGroupWidget () {
+ display: inline-block;
+ white-space: nowrap;
+ border-radius: @border-radius;
+
+ .oo-ui-inline-spacing(0.5em);
+ .oo-ui-buttonElement {
+ .oo-ui-inline-spacing(0);
+ }
+
+ .oo-ui-buttonElement-framed {
+ .oo-ui-buttonElement-button {
+ border-radius: 0;
+ margin-left: -1px;
+ }
+
+ &:first-child .oo-ui-buttonElement-button {
+ border-bottom-left-radius: @border-radius;
+ border-top-left-radius: @border-radius;
+ margin-left: 0;
+ }
+
+ &:last-child .oo-ui-buttonElement-button {
+ border-bottom-right-radius: @border-radius;
+ border-top-right-radius: @border-radius;
+ }
+ }
+}
+
+.theme-oo-ui-buttonWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-actionWidget () {
+ &.oo-ui-pendingElement-pending {
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+}
+
+.theme-oo-ui-popupButtonWidget () {
+ &.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
+ // Compensate for icon being inset
+ /* @noflip */
+ left: 1em;
+ }
+
+ &.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
+ // Compensate for icon being inset
+ /* @noflip */
+ left: 1.75em;
+ }
+}
+
+.theme-oo-ui-toggleButtonWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-iconWidget () {
+ line-height: 2.5em;
+ height: @icon-size;
+ width: @icon-size;
+
+ &.oo-ui-widget-disabled {
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-indicatorWidget () {
+ line-height: 2.5em;
+ height: @indicator-size;
+ width: @indicator-size;
+ margin: @indicator-size / 2;
+
+ &.oo-ui-widget-disabled {
+ opacity: 0.2;
+ }
+}
+
+.theme-oo-ui-dropdownWidget () {
+ margin: 0.25em 0;
+ width: 100%;
+ max-width: 50em;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ &-handle {
+ height: 2.5em;
+ border: 1px solid #ccc;
+ border-radius: 0.1em;
+
+ .oo-ui-indicatorElement-indicator {
+ right: 0;
+ }
+
+ .oo-ui-iconElement-icon {
+ left: 0.25em;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: 2.5em;
+ margin: 0 1em;
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ top: 0;
+ width: @indicator-size;
+ height: @indicator-size;
+ margin: 0.775em;
+ }
+
+ .oo-ui-iconElement-icon {
+ top: 0;
+ width: @icon-size;
+ height: @icon-size;
+ margin: 0.3em;
+ }
+ }
+
+ &:hover .oo-ui-dropdownWidget-handle {
+ border-color: #aaa;
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-dropdownWidget-handle {
+ color: #ccc;
+ text-shadow: 0 1px 1px #fff;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ }
+
+ &.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+ margin-left: 3em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+ margin-right: 2em;
+ }
+
+ .oo-ui-selectWidget {
+ border-top-color: #fff;
+ }
+}
+
+.theme-oo-ui-inputWidget () {
+ .oo-ui-inline-spacing(0.5em);
+}
+
+.theme-oo-ui-buttonInputWidget () {}
+
+.theme-oo-ui-checkboxInputWidget () {
+ position: relative;
+ line-height: @input-size;
+ // Prevent the fake span from jumping to the next line of text
+ white-space: nowrap;
+
+ * {
+ font: inherit;
+ vertical-align: middle;
+ }
+
+ // This input element is visually replaced by the the span that follows
+ input[type="checkbox"] {
+ // Use opacity so that VoiceOver software can still identify it
+ opacity: 0;
+ // Render "on top of" the span, so that it's still clickable
+ z-index: 1;
+ position: relative;
+
+ // Having margin would offset the input from where the span is absolutely positioned,
+ // making only the overlap region receive events
+ margin: 0;
+
+ // Ensure the invisible input takes up the required width
+ width: @input-size;
+ height: @input-size;
+
+ // Needed for Firefox mobile (See bug 71750 to workaround default Firefox stylesheet)
+ max-width: none;
+
+ & + span {
+ cursor: pointer;
+ .oo-ui-transition(background-size @medium-ease-out-back);
+ .oo-ui-box-sizing( border-box );
+ position: absolute;
+ left: 0;
+ border-radius: @border-radius;
+ width: @input-size;
+ height: @input-size;
+ background-color: white;
+ border: 1px solid @input-border-color;
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/check-constructive');
+ .oo-ui-background-image-safari('@{oo-ui-default-image-path}/icons/check-constructive');
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-origin: border-box;
+ background-size: 0 0;
+ }
+
+ &:checked + span {
+ background-size: 100% 100%;
+ }
+
+ &:active + span {
+ background-color: @input-active-color;
+ border-color: @input-active-color;
+ }
+
+ &:focus + span {
+ border-width: @input-focus-border-width;
+ }
+
+ &:focus:hover + span,
+ &:hover + span {
+ border-bottom-width: @input-hover-border-bottom-width;
+ }
+
+ &:disabled + span {
+ cursor: default;
+ background-color: @input-disabled-color;
+ border-color: @input-disabled-color;
+ }
+
+ &:disabled:checked + span {
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/check-invert');
+ .oo-ui-background-image-safari('@{oo-ui-default-image-path}/icons/check-invert');
+ }
+ }
+}
+
+.theme-oo-ui-dropdownInputWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ select {
+ height: 2.5em;
+ padding: 0.5em;
+ font-size: inherit;
+ font-family: inherit;
+ .oo-ui-box-sizing(border-box);
+ border: 1px solid #ccc;
+ }
+
+ &.oo-ui-widget-enabled {
+ select:hover,
+ select:focus {
+ border-color: #aaa;
+ outline: none;
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ select {
+ color: #ccc;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ }
+}
+
+.theme-oo-ui-radioInputWidget () {
+ position: relative;
+ line-height: @input-size;
+ // Prevent the fake span from jumping to the next line of text
+ white-space: nowrap;
+
+ * {
+ font: inherit;
+ vertical-align: middle;
+ }
+
+ // This input element is visually replaced by the the span that follows
+ input[type="radio"] {
+ // Use opacity so that VoiceOver software can still identify it
+ opacity: 0;
+ // Render "on top of" the span, so that it's still clickable
+ z-index: 1;
+ position: relative;
+
+ // Having margin would offset the input from where the span is absolutely positioned,
+ // making only the overlap region receive events
+ margin: 0;
+
+ // Ensure the invisible input takes up the required width
+ width: @input-size;
+ height: @input-size;
+
+ // Needed for Firefox mobile (See bug 71750 to workaround default Firefox stylesheet)
+ max-width: none;
+
+ & + span {
+ cursor: pointer;
+ .oo-ui-transition(background-size @medium-ease-out-back);
+ .oo-ui-box-sizing(border-box);
+ position: absolute;
+ left: 0;
+ border-radius: 100%;
+ width: @input-size;
+ height: @input-size;
+ background: white;
+ border: 1px solid @input-border-color;
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/circle-constructive');
+ .oo-ui-background-image-safari('@{oo-ui-default-image-path}/icons/circle-constructive');
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-origin: border-box;
+ background-size: 0 0;
+ }
+
+ &:checked + span {
+ background-size: 100% 100%;
+ }
+
+ &:active + span {
+ background-color: @input-active-color;
+ border-color: @input-active-color;
+ }
+
+ &:focus + span {
+ border-width: @input-focus-border-width;
+ }
+
+ &:focus:hover + span,
+ &:hover + span {
+ border-bottom-width: @input-hover-border-bottom-width;
+ }
+
+ &:disabled + span {
+ cursor: default;
+ background-color: @input-disabled-color;
+ border-color: @input-disabled-color;
+ }
+
+ &:disabled:checked + span {
+ .oo-ui-background-image-svg('@{oo-ui-default-image-path}/icons/circle-invert');
+ .oo-ui-background-image-safari('@{oo-ui-default-image-path}/icons/circle-invert');
+ }
+ }
+}
+
+.theme-oo-ui-textInputWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ input,
+ textarea {
+ padding: 0.5em;
+ margin: 0;
+ font-size: inherit;
+ font-family: inherit;
+ background-color: #fff;
+ color: black;
+ border: solid 1px #ccc;
+ box-shadow: inset 0 0 0 0 @progressive;
+ border-radius: 0.1em;
+ .oo-ui-transition(box-shadow @quick-ease);
+ .oo-ui-box-sizing(border-box);
+ }
+
+ &-decorated {
+ input,
+ textarea {
+ padding-left: 2em;
+ }
+ }
+
+ &-icon {
+ width: 2em;
+ }
+
+ &.oo-ui-widget-enabled {
+ input,
+ textarea {
+ .oo-ui-transition(
+ border @medium-ease-out-sine,
+ box-shadow @medium-ease-out-sine
+ );
+ }
+
+ input:focus,
+ textarea:focus {
+ outline: none;
+ border-color: @progressive;
+ box-shadow: inset 0 0 0 0.1em @progressive;
+ }
+
+ input[readonly],
+ textarea[readonly] {
+ color: #777;
+ text-shadow: 0 1px 1px #fff;
+
+ &:focus {
+ border-color: #ccc;
+ box-shadow: inset 0 0 0 0.1em #ccc;
+ }
+ }
+
+ &.oo-ui-flaggedElement-invalid {
+ input,
+ textarea {
+ border-color: red;
+ box-shadow: inset 0 0 0 0 red;
+ }
+
+ input:focus,
+ textarea:focus {
+ border-color: red;
+ box-shadow: inset 0 0 0 0.1em red;
+ }
+ }
+ }
+
+ &.oo-ui-widget-disabled {
+ input,
+ textarea {
+ color: #ccc;
+ text-shadow: 0 1px 1px #fff;
+ border-color: #ddd;
+ background-color: #f3f3f3;
+ }
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ .oo-ui-labelElement-label {
+ color: #ddd;
+ text-shadow: 0 1px 1px #fff;
+ }
+ }
+
+ &.oo-ui-pendingElement-pending {
+ input,
+ textarea {
+ background-color: transparent;
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+ }
+
+ &.oo-ui-iconElement {
+ input,
+ textarea {
+ padding-left: 2.75em;
+ }
+
+ .oo-ui-iconElement-icon {
+ left: 0.4em;
+ width: @icon-size;
+ margin-left: 0.1em;
+ height: 100%;
+ background-position: right center;
+ }
+ }
+
+ &.oo-ui-indicatorElement {
+ input,
+ textarea {
+ padding-right: @icon-size;
+ }
+
+ .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ margin: 0 0.775em;
+ height: 100%;
+ }
+ }
+
+ > .oo-ui-labelElement-label {
+ padding: 0.4em;
+ line-height: 1.5em;
+ color: #888;
+ }
+
+ &-labelPosition-after {
+ &.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+ margin-right: 2em;
+ }
+ }
+
+ &-labelPosition-before {
+ &.oo-ui-iconElement > .oo-ui-labelElement-label {
+ margin-left: 2.5em;
+ }
+ }
+}
+
+.theme-oo-ui-comboBoxWidget () {
+ width: 100%;
+ max-width: 50em;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ .oo-ui-textInputWidget {
+ input,
+ textarea {
+ height: 2.35em;
+ }
+ }
+}
+
+.theme-oo-ui-labelWidget () {}
+
+.theme-oo-ui-optionWidget () {
+ padding: 0.25em 0.5em;
+ border: none;
+
+ &-highlighted {
+ background-color: #eee;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: 1.5em;
+ }
+
+ .oo-ui-selectWidget-depressed &-selected,
+ .oo-ui-selectWidget-pressed &-pressed,
+ .oo-ui-selectWidget-pressed &-pressed&-highlighted,
+ .oo-ui-selectWidget-pressed &-pressed&-highlighted&-selected {
+ background-color: @pressed-color;
+ }
+
+ &.oo-ui-widget-disabled {
+ color: #ccc;
+ }
+}
+
+.theme-oo-ui-decoratedOptionWidget () {
+ padding: 0.5em 2em 0.5em 3em;
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon,
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ top: 0;
+ height: 100%;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ width: @icon-size;
+ left: 0.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ width: @indicator-size;
+ right: 0.5em;
+ }
+
+ &.oo-ui-widget-disabled {
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ opacity: 0.2;
+ }
+ }
+}
+
+.theme-oo-ui-buttonOptionWidget () {
+ padding: 0;
+ background-color: transparent;
+
+ .oo-ui-buttonElement-button {
+ height: @icon-size;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ margin-top: 0;
+ }
+
+ &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-pressed,
+ &.oo-ui-optionWidget-highlighted {
+ background-color: transparent;
+ }
+
+ // Override DecoratedOptionWidget styles
+ &.oo-ui-widget-disabled {
+ .oo-ui-iconElement-icon,
+ .oo-ui-indicatorElement-indicator {
+ opacity: 1;
+ }
+ }
+}
+
+.theme-oo-ui-radioOptionWidget () {
+ padding: 0.25em 0;
+ background-color: transparent;
+
+ &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-pressed,
+ &.oo-ui-optionWidget-highlighted {
+ background-color: transparent;
+ }
+
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ padding: 0.25em;
+ padding-left: 1em;
+ }
+
+ .oo-ui-radioInputWidget {
+ margin-right: 0;
+ }
+}
+
+.theme-oo-ui-menuOptionWidget () {
+ padding: 0.5em 1em;
+
+ &.oo-ui-optionWidget {
+ &-selected {
+ background-color: @select;
+ color: rgba(0,0,0,0.8);
+
+ .oo-ui-iconElement-icon {
+ display: none;
+ }
+ }
+ &-highlighted {
+ background-color: #eee;
+ color: black;
+ }
+ &-selected&-highlighted {
+ background-color: @select;
+ }
+ }
+}
+
+.theme-oo-ui-menuSectionOptionWidget () {
+ padding: 0.33em 0.75em;
+ color: #888;
+}
+
+.theme-oo-ui-outlineOptionWidget () {
+ font-size: 1.1em;
+ padding: 0.75em;
+
+ &.oo-ui-indicatorElement .oo-ui-labelElement-label {
+ padding-right: 1.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ opacity: 0.5;
+ }
+
+ &-level-0 {
+ padding-left: 3.5em;
+
+ .oo-ui-iconElement-icon {
+ left: 1em;
+ }
+ }
+ &-level-1 {
+ padding-left: 5em;
+
+ .oo-ui-iconElement-icon {
+ left: 2.5em;
+ }
+ }
+
+ &-level-2 {
+ padding-left: 6.5em;
+
+ .oo-ui-iconElement-icon {
+ left: 4em;
+ }
+ }
+
+ .oo-ui-selectWidget-depressed &.oo-ui-optionWidget-selected {
+ background-color: @pressed-color;
+ text-shadow: 0 1px 1px #fff;
+ }
+
+ &.oo-ui-flaggedElement-important {
+ font-weight: bold;
+ }
+
+ &.oo-ui-flaggedElement-placeholder {
+ font-style: italic;
+ }
+
+ &.oo-ui-flaggedElement-empty {
+ .oo-ui-iconElement-icon {
+ opacity: 0.5;
+ }
+ .oo-ui-labelElement-label {
+ color: #777;
+ }
+ }
+}
+
+.theme-oo-ui-tabOptionWidget () {
+ padding: 0.35em 1em;
+ margin: 0.5em 0 0 0.75em;
+ border: 1px solid transparent;
+ border-bottom: none;
+ border-top-left-radius: @border-radius;
+ border-top-right-radius: @border-radius;
+ color: #666;
+ font-weight: bold;
+
+ &.oo-ui-widget-enabled {
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+
+ &:active {
+ background-color: rgba(255, 255, 255, 0.8);
+ }
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-labelElement-label {
+ padding-right: 1.5em;
+ }
+
+ &.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+ opacity: 0.5;
+ }
+
+ .oo-ui-selectWidget-pressed &.oo-ui-optionWidget-selected,
+ .oo-ui-selectWidget-depressed &.oo-ui-optionWidget-selected,
+ &.oo-ui-optionWidget-selected:hover {
+ background-color: #fff;
+ color: #333;
+ }
+}
+
+.theme-oo-ui-popupWidget () {
+ &-popup {
+ border: 1px solid #aaa;
+ border-radius: 0.2em;
+ background-color: #fff;
+ box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2);
+ }
+
+ @anchor-size: 9px;
+
+ &-anchored {
+ .oo-ui-popupWidget-popup {
+ margin-top: @anchor-size;
+ }
+
+ .oo-ui-popupWidget-anchor:before,
+ .oo-ui-popupWidget-anchor:after {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-color: transparent;
+ border-top: 0;
+ }
+
+ .oo-ui-popupWidget-anchor:before {
+ bottom: -@anchor-size - 1px;
+ left: -@anchor-size;
+ border-bottom-color: #888;
+ border-width: @anchor-size + 1px;
+ }
+
+ .oo-ui-popupWidget-anchor:after {
+ bottom: -@anchor-size - 1px;
+ left: -@anchor-size + 1px;
+ border-bottom-color: #fff;
+ border-width: @anchor-size;
+ }
+ }
+
+ &-transitioning .oo-ui-popupWidget-popup {
+ .oo-ui-transition(width @quick-ease, height @quick-ease, left @quick-ease);
+ }
+
+ &-head {
+ height: 2.5em;
+
+ .oo-ui-buttonWidget {
+ margin: 0.25em;
+ }
+
+ .oo-ui-labelElement-label {
+ margin: 0.75em 1em;
+ }
+ }
+
+ &-body-padded {
+ padding: 0 1em;
+ }
+}
+
+.theme-oo-ui-searchWidget () {
+ &-query {
+ height: 4em;
+ padding: 0 1em;
+ border-bottom: 1px solid #ccc;
+
+ .oo-ui-textInputWidget {
+ margin: 0.75em 0;
+ }
+ }
+
+ &-results {
+ top: 4em;
+ padding: 1em;
+ line-height: 0;
+ }
+}
+
+.theme-oo-ui-selectWidget () {}
+
+.theme-oo-ui-buttonSelectWidget () {
+ border-radius: @border-radius;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ .oo-ui-buttonOptionWidget {
+ .oo-ui-buttonElement-button {
+ border-radius: 0;
+ margin-left: -1px;
+ }
+
+ &:first-child .oo-ui-buttonElement-button {
+ border-bottom-left-radius: @border-radius;
+ border-top-left-radius: @border-radius;
+ margin-left: 0;
+ }
+
+ &:last-child .oo-ui-buttonElement-button {
+ border-bottom-right-radius: @border-radius;
+ border-top-right-radius: @border-radius;
+ }
+ }
+}
+
+.theme-oo-ui-radioSelectWidget () {}
+
+.theme-oo-ui-menuSelectWidget () {
+ background: #fff;
+ margin-top: -1px;
+ border: 1px solid #aaa;
+ border-radius: 0 0 0.2em 0.2em;
+ padding-bottom: 0.25em;
+ box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2);
+}
+
+.theme-oo-ui-textInputMenuSelectWidget () {}
+
+.theme-oo-ui-outlineSelectWidget () {}
+
+.theme-oo-ui-tabSelectWidget () {
+ background-color: #ddd;
+}
+
+.theme-oo-ui-toggleSwitchWidget () {
+ @travelDistance: 2em;
+ height: 2em;
+ width: @travelDistance + 2em;
+ border-radius: 1em;
+ border: 1px #ddd solid;
+
+ .oo-ui-inline-spacing(0.5em);
+
+ &-grip {
+ top: 0.25em;
+ left: 0.25em;
+ width: 1.5em;
+ height: 1.5em;
+ margin-top: -1px;
+ border-radius: 1em;
+ border: 1px #ddd solid;
+ background-color: #f7f7f7;
+
+ .oo-ui-transition(left @quick-ease, margin-left @quick-ease);
+ }
+
+ &-glow {
+ border-radius: 1em;
+ background-color: #f7f7f7;
+
+ .oo-ui-transition(background-color @quick-ease);
+ }
+
+ &.oo-ui-toggleWidget-on {
+ .oo-ui-toggleSwitchWidget-grip {
+ left: @travelDistance + 0.25em;
+ margin-left: -2px;
+ }
+ }
+
+ &.oo-ui-toggleWidget-off {
+ .oo-ui-toggleSwitchWidget-glow {
+ display: block;
+ }
+ .oo-ui-toggleSwitchWidget-grip {
+ left: 0.25em;
+ margin-left: 0;
+ }
+ }
+
+ &.oo-ui-widget-enabled {
+ border: 1px #ccc solid;
+
+ &:hover {
+ border-color: #aaa;
+ }
+
+ .oo-ui-toggleSwitchWidget-grip {
+ background-color: #fff;
+ border-color: #aaa;
+ }
+
+ &.oo-ui-toggleWidget-on {
+ .oo-ui-toggleSwitchWidget-glow {
+ background-color: @pressed-color;
+ }
+ }
+
+ &.oo-ui-toggleWidget-off {
+ .oo-ui-toggleSwitchWidget-glow {
+ background-color: #fff;
+ }
+ }
+ }
+}
+
+.theme-oo-ui-progressBarWidget () {
+ max-width: 50em;
+ border: 1px solid #ccc;
+ border-radius: 0.1em;
+ overflow: hidden;
+
+ &-bar {
+ height: 1em;
+ background: #ddd;
+ .oo-ui-transition(width 200ms, margin-left 200ms);
+ }
+ &-indeterminate {
+ .oo-ui-progressBarWidget-bar {
+ .oo-ui-animation(oo-ui-progressBarWidget-slide 2s infinite linear);
+ width: 40%;
+ margin-left: -10%;
+ border-left-width: 1px;
+ }
+ }
+ &.oo-ui-widget-disabled {
+ opacity: 0.6;
+ }
+}
+
+.oo-ui-progressBarWidget-slide-frames () {
+ from { margin-left: -40%; }
+ to { margin-left: 100%; }
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-moz-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-ms-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@-o-keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
+@keyframes oo-ui-progressBarWidget-slide { .oo-ui-progressBarWidget-slide-frames }
diff --git a/vendor/oojs/oojs-ui/src/themes/mediawiki/windows.less b/vendor/oojs/oojs-ui/src/themes/mediawiki/windows.less
new file mode 100644
index 00000000..423233ce
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/themes/mediawiki/windows.less
@@ -0,0 +1,300 @@
+@import 'common';
+
+.theme-oo-ui-window () {
+ background: transparent;
+}
+
+.theme-oo-ui-dialog () {
+ &-content > .oo-ui-window-body {
+ outline: 1px solid #aaa;
+ }
+}
+
+.theme-oo-ui-messageDialog () {
+ &-title,
+ &-message {
+ display: block;
+ text-align: center;
+ padding-top: 0.5em;
+ }
+
+ &-title {
+ font-size: 1.5em;
+ line-height: 1em;
+ color: #000;
+ }
+
+ &-message {
+ font-size: 0.9em;
+ line-height: 1.25em;
+ color: #666;
+
+ &-verbose {
+ font-size: 1.1em;
+ line-height: 1.5em;
+ text-align: left;
+ }
+ }
+
+ &-actions {
+ &-horizontal {
+ .oo-ui-actionWidget {
+ border-right: 1px solid #e5e5e5;
+
+ &:last-child {
+ border-right-width: 0;
+ }
+ }
+ }
+
+ &-vertical {
+ .oo-ui-actionWidget {
+ border-bottom: 1px solid #e5e5e5;
+
+ &:last-child {
+ border-bottom-width: 0;
+ }
+ }
+ }
+
+ .oo-ui-actionWidget {
+ height: 3.4em;
+
+ &.oo-ui-labelElement .oo-ui-labelElement-label {
+ text-align: center;
+ line-height: 3.4em;
+ padding: 0 2em;
+ }
+
+ &:hover {
+ background-color: rgba(0,0,0,0.05);
+ }
+
+ &:active {
+ background-color: rgba(0,0,0,0.1);
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ &:hover {
+ background-color: rgba(8,126,204,0.05);
+ }
+
+ &:active {
+ background-color: rgba(8,126,204,0.1);
+ }
+
+ .oo-ui-labelElement-label {
+ font-weight: bold;
+ }
+ }
+
+ &-constructive {
+ &:hover {
+ background-color: rgba(118,171,54,0.05);
+ }
+
+ &:active {
+ background-color: rgba(118,171,54,0.1);
+ }
+ }
+
+ &-destructive {
+ &:hover {
+ background-color: rgba(212,83,83,0.05);
+ }
+
+ &:active {
+ background-color: rgba(212,83,83,0.1);
+ }
+ }
+ }
+ }
+ }
+}
+
+.theme-oo-ui-processDialog () {
+ &-content {
+ .oo-ui-window-head {
+ height: 3.4em;
+
+ &.oo-ui-pendingElement-pending {
+ .oo-ui-background-image('@{oo-ui-default-image-path}/textures/pending.gif');
+ }
+ }
+
+ .oo-ui-window-body {
+ top: 3.4em;
+ outline: 1px solid rgba(0,0,0,0.2);
+ }
+ }
+
+ &-navigation {
+ position: relative;
+ height: 3.4em;
+ padding: 0 1em;
+ }
+
+ &-location {
+ padding: 0.75em 0;
+ height: @icon-size;
+ cursor: default;
+ text-align: center;
+ }
+
+ &-title {
+ font-weight: bold;
+ line-height: @icon-size;
+ }
+
+ &-actions {
+ &-safe,
+ &-primary,
+ &-other {
+ .oo-ui-actionWidget {
+ .oo-ui-buttonElement-button {
+ min-width: @icon-size;
+ min-height: @icon-size;
+ }
+
+ .oo-ui-labelElement-label {
+ line-height: @icon-size;
+ }
+
+ &.oo-ui-iconElement .oo-ui-iconElement-icon {
+ margin-top: -0.125em;
+ }
+
+ &.oo-ui-buttonElement-framed {
+ margin: 0.75em 0 0.75em 0.75em;
+ .oo-ui-buttonElement-button {
+ padding: 0 1em;
+ vertical-align: middle;
+ }
+ }
+ }
+ }
+
+ &-safe,
+ &-primary {
+ .oo-ui-actionWidget {
+ &:hover {
+ background-color: rgba(0,0,0,0.05);
+ }
+
+ &:active {
+ background-color: rgba(0,0,0,0.1);
+ }
+
+ &.oo-ui-buttonElement-framed {
+ margin: 0.75em;
+ .oo-ui-buttonElement-button {
+ /* Adjust for border so text aligns with title */
+ margin: -1px;
+ }
+ }
+
+ &.oo-ui-flaggedElement {
+ &-progressive {
+ &:hover {
+ background-color: rgba(8,126,204,0.05);
+ }
+
+ &:active {
+ background-color: rgba(8,126,204,0.1);
+ }
+
+ .oo-ui-labelElement-label {
+ font-weight: bold;
+ }
+ }
+
+ &-constructive {
+ &:hover {
+ background-color: rgba(118,171,54,0.05);
+ }
+
+ &:active {
+ background-color: rgba(118,171,54,0.1);
+ }
+ }
+
+ &-destructive {
+ &:hover {
+ background-color: rgba(212,83,83,0.05);
+ }
+
+ &:active {
+ background-color: rgba(212,83,83,0.1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ > .oo-ui-window-frame {
+ min-height: 5em;
+ }
+
+ &-errors {
+ background-color: rgba(255,255,255,0.9);
+ padding: 3em 3em 1.5em 3em;
+ text-align: center;
+
+ .oo-ui-buttonWidget {
+ margin: 2em 1em 2em 1em;
+ }
+
+ &-title {
+ font-size: 1.5em;
+ color: #000;
+ margin-bottom: 2em;
+ }
+ }
+
+ &-error {
+ text-align: left;
+ margin: 1em;
+ padding: 1em;
+ border: 1px solid #ff9e9e;
+ background-color: #fff7f7;
+ border-radius: 0.25em;
+ }
+}
+
+.theme-oo-ui-windowManager () {
+ &-modal > .oo-ui-dialog {
+ background-color: rgba(255,255,255,0.5);
+ opacity: 0;
+
+ .oo-ui-transition(opacity 250ms ease-in-out);
+
+ > .oo-ui-window-frame {
+ top: 1em;
+ bottom: 1em;
+ background-color: #fff;
+
+ opacity: 0;
+ .oo-ui-transform(scale(0.5));
+ .oo-ui-transition(all 250ms ease-in-out);
+ }
+
+ &.oo-ui-window-ready {
+ /* Fade window overlay */
+ opacity: 1;
+
+ > .oo-ui-window-frame {
+ /* Fade frame */
+ opacity: 1;
+ .oo-ui-transform(scale(1));
+ }
+ }
+ }
+
+ &-modal&-floating > .oo-ui-dialog > .oo-ui-window-frame {
+ border: 1px solid #aaa;
+ border-radius: 0.2em;
+ box-shadow: inset 0 -0.2em 0 0 rgba(0,0,0,0.2);
+ }
+}
diff --git a/vendor/oojs/oojs-ui/src/toolgroups/BarToolGroup.js b/vendor/oojs/oojs-ui/src/toolgroups/BarToolGroup.js
new file mode 100644
index 00000000..038894d6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/toolgroups/BarToolGroup.js
@@ -0,0 +1,35 @@
+/**
+ * Horizontal bar layout of tools as icon buttons.
+ *
+ * @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.super.call( this, toolbar, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-barToolGroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.BarToolGroup, OO.ui.ToolGroup );
+
+/* Static Properties */
+
+OO.ui.BarToolGroup.static.titleTooltips = true;
+
+OO.ui.BarToolGroup.static.accelTooltips = true;
+
+OO.ui.BarToolGroup.static.name = 'bar';
diff --git a/vendor/oojs/oojs-ui/src/toolgroups/ListToolGroup.js b/vendor/oojs/oojs-ui/src/toolgroups/ListToolGroup.js
new file mode 100644
index 00000000..e78f507e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/toolgroups/ListToolGroup.js
@@ -0,0 +1,133 @@
+/**
+ * Drop down list layout of tools as labeled icon buttons.
+ *
+ * This layout allows some tools to be collapsible, controlled by a "More" / "Fewer" option at the
+ * bottom of the main list. These are not automatically positioned at the bottom of the list; you
+ * may want to use the 'promote' and 'demote' configuration options to achieve this.
+ *
+ * @class
+ * @extends OO.ui.PopupToolGroup
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {Array} [allowCollapse] List of tools that can be collapsed. Remaining tools will be always
+ * shown.
+ * @cfg {Array} [forceExpand] List of tools that *may not* be collapsed. All remaining tools will be
+ * allowed to be collapsed.
+ * @cfg {boolean} [expanded=false] Whether the collapsible tools are expanded by default
+ */
+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.super.call( this, toolbar, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-listToolGroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ListToolGroup, OO.ui.PopupToolGroup );
+
+/* Static Properties */
+
+OO.ui.ListToolGroup.static.name = 'list';
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ListToolGroup.prototype.populate = function () {
+ var i, len, allowCollapse = [];
+
+ OO.ui.ListToolGroup.super.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();
+};
+
+OO.ui.ListToolGroup.prototype.getExpandCollapseTool = function () {
+ if ( this.expandCollapseTool === undefined ) {
+ var ExpandCollapseTool = function () {
+ ExpandCollapseTool.super.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 === 1 || 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.super.super.prototype.onMouseKeyUp.call( this, e );
+ } else {
+ return OO.ui.ListToolGroup.super.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 );
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/toolgroups/MenuToolGroup.js b/vendor/oojs/oojs-ui/src/toolgroups/MenuToolGroup.js
new file mode 100644
index 00000000..aef69a93
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/toolgroups/MenuToolGroup.js
@@ -0,0 +1,58 @@
+/**
+ * Drop down menu layout of tools as selectable menu items.
+ *
+ * @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.super.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 */
+
+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.
+ */
+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( ', ' ) || ' ' );
+};
diff --git a/vendor/oojs/oojs-ui/src/toolgroups/PopupToolGroup.js b/vendor/oojs/oojs-ui/src/toolgroups/PopupToolGroup.js
new file mode 100644
index 00000000..0fbdf4fc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/toolgroups/PopupToolGroup.js
@@ -0,0 +1,187 @@
+/**
+ * Popup list of tools with an icon and optional label.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.ToolGroup
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.TitledElement
+ * @mixins OO.ui.ClippableElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [header] Text to display at the top of the pop-up
+ */
+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 = config || {};
+
+ // Parent constructor
+ OO.ui.PopupToolGroup.super.call( this, toolbar, config );
+
+ // Properties
+ this.active = false;
+ this.dragging = false;
+ this.onBlurHandler = this.onBlur.bind( this );
+ this.$handle = $( '<span>' );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.TitledElement.call( this, config );
+ OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
+ OO.ui.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' )
+ .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.IconElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.TitledElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.ClippableElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.PopupToolGroup.prototype.setDisabled = function () {
+ // Parent method
+ OO.ui.PopupToolGroup.super.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.
+ *
+ * @param {jQuery.Event} 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 === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ) {
+ this.setActive( false );
+ }
+ return OO.ui.PopupToolGroup.super.prototype.onMouseKeyUp.call( this, e );
+};
+
+/**
+ * Handle mouse up and key up events.
+ *
+ * @param {jQuery.Event} e Mouse up or key up event
+ */
+OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
+ if (
+ !this.isDisabled() &&
+ ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ) {
+ return false;
+ }
+};
+
+/**
+ * Handle mouse down and key down events.
+ *
+ * @param {jQuery.Event} e Mouse down or key down event
+ */
+OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
+ if (
+ !this.isDisabled() &&
+ ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ) {
+ this.setActive( !this.active );
+ return false;
+ }
+};
+
+/**
+ * Switch into active mode.
+ *
+ * When active, mouseup events anywhere in the document will trigger deactivation.
+ */
+OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
+ 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 );
+
+ // 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 );
+ }
+ } 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 );
+ }
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/tools/PopupTool.js b/vendor/oojs/oojs-ui/src/tools/PopupTool.js
new file mode 100644
index 00000000..98f93d75
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/tools/PopupTool.js
@@ -0,0 +1,59 @@
+/**
+ * Tool that shows a popup when selected.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Tool
+ * @mixins OO.ui.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.super.call( this, toolGroup, config );
+
+ // Mixin constructors
+ OO.ui.PopupElement.call( this, config );
+
+ // Initialization
+ 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.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 );
+};
diff --git a/vendor/oojs/oojs-ui/src/tools/ToolGroupTool.js b/vendor/oojs/oojs-ui/src/tools/ToolGroupTool.js
new file mode 100644
index 00000000..b8f70a3a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/tools/ToolGroupTool.js
@@ -0,0 +1,96 @@
+/**
+ * Tool that has a tool group inside. This is a bad workaround for the lack of proper hierarchical
+ * menus in toolbars (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.super.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 */
+
+/**
+ * Tool group configuration. See OO.ui.Toolbar#setup for the accepted values.
+ *
+ * @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 OO.ui.ToolGroup from the configuration.
+ *
+ * @param {Object.<string,Array>} group Tool group configuration. See OO.ui.Toolbar#setup for the
+ * accepted values.
+ * @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 );
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ActionWidget.js b/vendor/oojs/oojs-ui/src/widgets/ActionWidget.js
new file mode 100644
index 00000000..789f04f1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ActionWidget.js
@@ -0,0 +1,170 @@
+/**
+ * An ActionWidget is a {@link OO.ui.ButtonWidget button widget} that executes an action.
+ * Action widgets are used with OO.ui.ActionSet, which manages the behavior and availability
+ * of the actions.
+ *
+ * Both actions and action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @class
+ * @extends OO.ui.ButtonWidget
+ * @mixins OO.ui.PendingElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [action] Symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
+ * @cfg {string[]} [modes] Symbolic names of the modes (e.g., ‘edit’ or ‘read’) in which the action
+ * should be made available. See the action set's {@link OO.ui.ActionSet#setMode setMode} method
+ * for more information about setting modes.
+ * @cfg {boolean} [framed=false] Render the action button with a frame
+ */
+OO.ui.ActionWidget = function OoUiActionWidget( config ) {
+ // Configuration initialization
+ config = $.extend( { framed: false }, config );
+
+ // Parent constructor
+ OO.ui.ActionWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.PendingElement.call( this, config );
+
+ // Properties
+ this.action = config.action || '';
+ this.modes = config.modes || [];
+ this.width = 0;
+ this.height = 0;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-actionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ActionWidget, OO.ui.ButtonWidget );
+OO.mixinClass( OO.ui.ActionWidget, OO.ui.PendingElement );
+
+/* Events */
+
+/**
+ * A resize event is emitted when the size of the widget changes.
+ *
+ * @event resize
+ */
+
+/* Methods */
+
+/**
+ * Check if the action is configured to be available in the specified `mode`.
+ *
+ * @param {string} mode Name of mode
+ * @return {boolean} The action is configured with the mode
+ */
+OO.ui.ActionWidget.prototype.hasMode = function ( mode ) {
+ return this.modes.indexOf( mode ) !== -1;
+};
+
+/**
+ * Get the symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
+ *
+ * @return {string}
+ */
+OO.ui.ActionWidget.prototype.getAction = function () {
+ return this.action;
+};
+
+/**
+ * Get the symbolic name of the mode or modes for which the action is configured to be available.
+ *
+ * The current mode is set with the action set's {@link OO.ui.ActionSet#setMode setMode} method.
+ * Only actions that are configured to be avaiable in the current mode will be visible. All other actions
+ * are hidden.
+ *
+ * @return {string[]}
+ */
+OO.ui.ActionWidget.prototype.getModes = function () {
+ return this.modes.slice();
+};
+
+/**
+ * Emit a resize event if the size has changed.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ActionWidget.prototype.propagateResize = function () {
+ var width, height;
+
+ if ( this.isElementAttached() ) {
+ width = this.$element.width();
+ height = this.$element.height();
+
+ if ( width !== this.width || height !== this.height ) {
+ this.width = width;
+ this.height = height;
+ this.emit( 'resize' );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setIcon = function () {
+ // Mixin method
+ OO.ui.IconElement.prototype.setIcon.apply( this, arguments );
+ this.propagateResize();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setLabel = function () {
+ // Mixin method
+ OO.ui.LabelElement.prototype.setLabel.apply( this, arguments );
+ this.propagateResize();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setFlags = function () {
+ // Mixin method
+ OO.ui.FlaggedElement.prototype.setFlags.apply( this, arguments );
+ this.propagateResize();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.clearFlags = function () {
+ // Mixin method
+ OO.ui.FlaggedElement.prototype.clearFlags.apply( this, arguments );
+ this.propagateResize();
+
+ return this;
+};
+
+/**
+ * Toggle the visibility of the action button.
+ *
+ * @param {boolean} [show] Show button, omit to toggle visibility
+ * @chainable
+ */
+OO.ui.ActionWidget.prototype.toggle = function () {
+ // Parent method
+ OO.ui.ActionWidget.super.prototype.toggle.apply( this, arguments );
+ this.propagateResize();
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ButtonGroupWidget.js b/vendor/oojs/oojs-ui/src/widgets/ButtonGroupWidget.js
new file mode 100644
index 00000000..f1388ab8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ButtonGroupWidget.js
@@ -0,0 +1,53 @@
+/**
+ * A ButtonGroupWidget groups related buttons and is used together with OO.ui.ButtonWidget and
+ * its subclasses. Each button in a group is addressed by a unique reference. Buttons can be added,
+ * removed, and cleared from the group.
+ *
+ * @example
+ * // Example: A ButtonGroupWidget with two buttons
+ * var button1 = new OO.ui.PopupButtonWidget( {
+ * label: 'Select a category',
+ * icon: 'menu',
+ * popup: {
+ * $content: $( '<p>List of categories...</p>' ),
+ * padded: true,
+ * align: 'left'
+ * }
+ * } );
+ * var button2 = new OO.ui.ButtonWidget( {
+ * label: 'Add item'
+ * });
+ * var buttonGroup = new OO.ui.ButtonGroupWidget( {
+ * items: [button1, button2]
+ * } );
+ * $( 'body' ).append( buttonGroup.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.ButtonWidget[]} [items] Buttons to add
+ */
+OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ButtonGroupWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-buttonGroupWidget' );
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonGroupWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.GroupElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/ButtonInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/ButtonInputWidget.js
new file mode 100644
index 00000000..1d4d97fe
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ButtonInputWidget.js
@@ -0,0 +1,121 @@
+/**
+ * ButtonInputWidget is used to submit HTML forms and is intended to be used within
+ * a OO.ui.FormLayout. If you do not need the button to work with HTML forms, you probably
+ * want to use OO.ui.ButtonWidget instead. Button input widgets can be rendered as either an
+ * HTML `<button/>` (the default) or an HTML `<input/>` tags. See the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * @example
+ * // A ButtonInputWidget rendered as an HTML button, the default.
+ * var button = new OO.ui.ButtonInputWidget( {
+ * label: 'Input button',
+ * icon: 'check',
+ * value: 'check'
+ * } );
+ * $( 'body' ).append( button.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs#Button_inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ * @mixins OO.ui.ButtonElement
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [type='button'] The value of the HTML `'type'` attribute: 'button', 'submit' or 'reset'.
+ * @cfg {boolean} [useInputTag=false] Use an `<input/>` tag instead of a `<button/>` tag, the default.
+ * Widgets configured to be an `<input/>` do not support {@link #icon icons} and {@link #indicator indicators},
+ * non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
+ * be set to `true` when there’s need to support IE6 in a form with multiple buttons.
+ */
+OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
+ // Configuration initialization
+ config = $.extend( { type: 'button', useInputTag: false }, config );
+
+ // Properties (must be set before parent constructor, which calls #setValue)
+ this.useInputTag = config.useInputTag;
+
+ // Parent constructor
+ OO.ui.ButtonInputWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ButtonElement.call( this, $.extend( {}, config, { $button: this.$input } ) );
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
+
+ // Initialization
+ if ( !config.useInputTag ) {
+ this.$input.append( this.$icon, this.$label, this.$indicator );
+ }
+ this.$element.addClass( 'oo-ui-buttonInputWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.ButtonElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.TitledElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @private
+ */
+OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
+ var html = '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + config.type + '">';
+ return $( html );
+};
+
+/**
+ * Set label value.
+ *
+ * If #useInputTag is `true`, the label is set as the `value` of the `<input/>` tag.
+ *
+ * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
+ * text, or `null` for no label
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
+ OO.ui.LabelElement.prototype.setLabel.call( this, label );
+
+ if ( this.useInputTag ) {
+ if ( typeof label === 'function' ) {
+ label = OO.ui.resolveMsg( label );
+ }
+ if ( label instanceof jQuery ) {
+ label = label.text();
+ }
+ if ( !label ) {
+ label = '';
+ }
+ this.$input.val( label );
+ }
+
+ return this;
+};
+
+/**
+ * Set the value of the input.
+ *
+ * This method is disabled for button inputs configured as {@link #useInputTag <input/> tags}, as
+ * they do not support {@link #value values}.
+ *
+ * @param {string} value New value
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
+ if ( !this.useInputTag ) {
+ OO.ui.ButtonInputWidget.super.prototype.setValue.call( this, value );
+ }
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ButtonOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/ButtonOptionWidget.js
new file mode 100644
index 00000000..7758c949
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ButtonOptionWidget.js
@@ -0,0 +1,60 @@
+/**
+ * ButtonOptionWidget is a special type of {@link OO.ui.ButtonElement button element} that
+ * can be selected and configured with data. The class is
+ * used with OO.ui.ButtonSelectWidget to create a selection of button options. Please see the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ * @mixins OO.ui.ButtonElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( config ) {
+ // Configuration initialization
+ config = $.extend( { tabIndex: -1 }, config );
+
+ // Parent constructor
+ OO.ui.ButtonOptionWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ButtonElement.call( this, config );
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-buttonOptionWidget' );
+ this.$button.append( this.$element.contents() );
+ this.$element.append( this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonOptionWidget, OO.ui.DecoratedOptionWidget );
+OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.ButtonElement );
+OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.TabIndexedElement );
+
+/* Static Properties */
+
+// Allow button mouse down events to pass through so they can be handled by the parent select widget
+OO.ui.ButtonOptionWidget.static.cancelButtonMouseDownEvents = false;
+
+OO.ui.ButtonOptionWidget.static.highlightable = false;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonOptionWidget.prototype.setSelected = function ( state ) {
+ OO.ui.ButtonOptionWidget.super.prototype.setSelected.call( this, state );
+
+ if ( this.constructor.static.selectable ) {
+ this.setActive( state );
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ButtonSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/ButtonSelectWidget.js
new file mode 100644
index 00000000..18177761
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ButtonSelectWidget.js
@@ -0,0 +1,62 @@
+/**
+ * ButtonSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains
+ * button options and is used together with
+ * OO.ui.ButtonOptionWidget. The ButtonSelectWidget provides an interface for
+ * highlighting, choosing, and selecting mutually exclusive options. Please see
+ * the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * @example
+ * // Example: A ButtonSelectWidget that contains three ButtonOptionWidgets
+ * var option1 = new OO.ui.ButtonOptionWidget( {
+ * data: 1,
+ * label: 'Option 1',
+ * title: 'Button option 1'
+ * } );
+ *
+ * var option2 = new OO.ui.ButtonOptionWidget( {
+ * data: 2,
+ * label: 'Option 2',
+ * title: 'Button option 2'
+ * } );
+ *
+ * var option3 = new OO.ui.ButtonOptionWidget( {
+ * data: 3,
+ * label: 'Option 3',
+ * title: 'Button option 3'
+ * } );
+ *
+ * var buttonSelect=new OO.ui.ButtonSelectWidget( {
+ * items: [ option1, option2, option3 ]
+ * } );
+ * $( 'body' ).append( buttonSelect.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ButtonSelectWidget = function OoUiButtonSelectWidget( config ) {
+ // Parent constructor
+ OO.ui.ButtonSelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, config );
+
+ // Events
+ this.$element.on( {
+ focus: this.bindKeyDownListener.bind( this ),
+ blur: this.unbindKeyDownListener.bind( this )
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-buttonSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.ButtonSelectWidget, OO.ui.TabIndexedElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/ButtonWidget.js b/vendor/oojs/oojs-ui/src/widgets/ButtonWidget.js
new file mode 100644
index 00000000..474fafed
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ButtonWidget.js
@@ -0,0 +1,215 @@
+/**
+ * ButtonWidget is a generic widget for buttons. A wide variety of looks,
+ * feels, and functionality can be customized via the class’s configuration options
+ * and methods. Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches
+ *
+ * @example
+ * // A button widget
+ * var button = new OO.ui.ButtonWidget( {
+ * label: 'Button with Icon',
+ * icon: 'remove',
+ * iconTitle: 'Remove'
+ * } );
+ * $( 'body' ).append( button.$element );
+ *
+ * NOTE: HTML form buttons should use the OO.ui.ButtonInputWidget class.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.ButtonElement
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.TitledElement
+ * @mixins OO.ui.FlaggedElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [href] Hyperlink to visit when the button is clicked.
+ * @cfg {string} [target] The frame or window in which to open the hyperlink.
+ * @cfg {boolean} [noFollow] Search engine traversal hint (default: true)
+ */
+OO.ui.ButtonWidget = function OoUiButtonWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ButtonWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ButtonElement.call( this, config );
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
+ OO.ui.FlaggedElement.call( this, config );
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
+
+ // Properties
+ this.href = null;
+ this.target = null;
+ this.noFollow = false;
+
+ // Events
+ this.connect( this, { disable: 'onDisable' } );
+
+ // Initialization
+ this.$button.append( this.$icon, this.$label, this.$indicator );
+ this.$element
+ .addClass( 'oo-ui-buttonWidget' )
+ .append( this.$button );
+ this.setHref( config.href );
+ this.setTarget( config.target );
+ this.setNoFollow( config.noFollow );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.ButtonElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.TitledElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.FlaggedElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonWidget.prototype.onMouseDown = function ( e ) {
+ if ( !this.isDisabled() ) {
+ // Remove the tab-index while the button is down to prevent the button from stealing focus
+ this.$button.removeAttr( 'tabindex' );
+ }
+
+ return OO.ui.ButtonElement.prototype.onMouseDown.call( this, e );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonWidget.prototype.onMouseUp = function ( e ) {
+ if ( !this.isDisabled() ) {
+ // Restore the tab-index after the button is up to restore the button's accessibility
+ this.$button.attr( 'tabindex', this.tabIndex );
+ }
+
+ return OO.ui.ButtonElement.prototype.onMouseUp.call( this, e );
+};
+
+/**
+ * Get hyperlink location.
+ *
+ * @return {string} Hyperlink location
+ */
+OO.ui.ButtonWidget.prototype.getHref = function () {
+ return this.href;
+};
+
+/**
+ * Get hyperlink target.
+ *
+ * @return {string} Hyperlink target
+ */
+OO.ui.ButtonWidget.prototype.getTarget = function () {
+ return this.target;
+};
+
+/**
+ * Get search engine traversal hint.
+ *
+ * @return {boolean} Whether search engines should avoid traversing this hyperlink
+ */
+OO.ui.ButtonWidget.prototype.getNoFollow = function () {
+ return this.noFollow;
+};
+
+/**
+ * Set hyperlink location.
+ *
+ * @param {string|null} href Hyperlink location, null to remove
+ */
+OO.ui.ButtonWidget.prototype.setHref = function ( href ) {
+ href = typeof href === 'string' ? href : null;
+
+ if ( href !== this.href ) {
+ this.href = href;
+ this.updateHref();
+ }
+
+ return this;
+};
+
+/**
+ * Update the `href` attribute, in case of changes to href or
+ * disabled state.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ButtonWidget.prototype.updateHref = function () {
+ if ( this.href !== null && !this.isDisabled() ) {
+ this.$button.attr( 'href', this.href );
+ } else {
+ this.$button.removeAttr( 'href' );
+ }
+
+ return this;
+};
+
+/**
+ * Handle disable events.
+ *
+ * @private
+ * @param {boolean} disabled Element is disabled
+ */
+OO.ui.ButtonWidget.prototype.onDisable = function () {
+ this.updateHref();
+};
+
+/**
+ * Set hyperlink target.
+ *
+ * @param {string|null} target Hyperlink target, null to remove
+ */
+OO.ui.ButtonWidget.prototype.setTarget = function ( target ) {
+ target = typeof target === 'string' ? target : null;
+
+ if ( target !== this.target ) {
+ this.target = target;
+ if ( target !== null ) {
+ this.$button.attr( 'target', target );
+ } else {
+ this.$button.removeAttr( 'target' );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Set search engine traversal hint.
+ *
+ * @param {boolean} noFollow True if search engines should avoid traversing this hyperlink
+ */
+OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) {
+ noFollow = typeof noFollow === 'boolean' ? noFollow : true;
+
+ if ( noFollow !== this.noFollow ) {
+ this.noFollow = noFollow;
+ if ( noFollow ) {
+ this.$button.attr( 'rel', 'nofollow' );
+ } else {
+ this.$button.removeAttr( 'rel' );
+ }
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/CheckboxInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/CheckboxInputWidget.js
new file mode 100644
index 00000000..1ac93bc5
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/CheckboxInputWidget.js
@@ -0,0 +1,110 @@
+/**
+ * CheckboxInputWidgets, like HTML checkboxes, can be selected and/or configured with a value.
+ * Note that these {@link OO.ui.InputWidget input widgets} are best laid out
+ * in {@link OO.ui.FieldLayout field layouts} that use the {@link OO.ui.FieldLayout#align inline}
+ * alignment. For more information, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ * @example
+ * // An example of selected, unselected, and disabled checkbox inputs
+ * var checkbox1=new OO.ui.CheckboxInputWidget( {
+ * value: 'a',
+ * selected: true
+ * } );
+ * var checkbox2=new OO.ui.CheckboxInputWidget( {
+ * value: 'b'
+ * } );
+ * var checkbox3=new OO.ui.CheckboxInputWidget( {
+ * value:'c',
+ * disabled: true
+ * } );
+ * // Create a fieldset layout with fields for each checkbox.
+ * var fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'Checkboxes'
+ * } );
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( checkbox1, { label: 'Selected checkbox', align: 'inline' } ),
+ * new OO.ui.FieldLayout( checkbox2, { label: 'Unselected checkbox', align: 'inline' } ),
+ * new OO.ui.FieldLayout( checkbox3, { label: 'Disabled checkbox', align: 'inline' } ),
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [selected=false] Select the checkbox initially. By default, the checkbox is not selected.
+ */
+OO.ui.CheckboxInputWidget = function OoUiCheckboxInputWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.CheckboxInputWidget.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-checkboxInputWidget' );
+ this.setSelected( config.selected !== undefined ? config.selected : false );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @private
+ */
+OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
+ return $( '<input type="checkbox" />' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
+ var widget = this;
+ if ( !this.isDisabled() ) {
+ // Allow the stack to clear so the value will be updated
+ setTimeout( function () {
+ widget.setSelected( widget.$input.prop( 'checked' ) );
+ } );
+ }
+};
+
+/**
+ * Set selection state of this checkbox.
+ *
+ * @param {boolean} state `true` for selected
+ * @chainable
+ */
+OO.ui.CheckboxInputWidget.prototype.setSelected = function ( state ) {
+ state = !!state;
+ if ( this.selected !== state ) {
+ this.selected = state;
+ this.$input.prop( 'checked', this.selected );
+ this.emit( 'change', this.selected );
+ }
+ return this;
+};
+
+/**
+ * Check if this checkbox is selected.
+ *
+ * @return {boolean} Checkbox is selected
+ */
+OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
+ // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
+ // it, and we won't know unless they're kind enough to trigger a 'change' event.
+ var selected = this.$input.prop( 'checked' );
+ if ( this.selected !== selected ) {
+ this.setSelected( selected );
+ }
+ return this.selected;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ComboBoxWidget.js b/vendor/oojs/oojs-ui/src/widgets/ComboBoxWidget.js
new file mode 100644
index 00000000..346d17ee
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ComboBoxWidget.js
@@ -0,0 +1,230 @@
+/**
+ * ComboBoxWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
+ * can be entered manually) and a {@link OO.ui.MenuSelectWidget menu of options} (from which
+ * a value can be chosen instead). Users can choose options from the combo box in one of two ways:
+ *
+ * - by typing a value in the text input field. If the value exactly matches the value of a menu
+ * option, that option will appear to be selected.
+ * - by choosing a value from the menu. The value of the chosen option will then appear in the text
+ * input field.
+ *
+ * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Example: A ComboBoxWidget.
+ * var comboBox = new OO.ui.ComboBoxWidget( {
+ * label: 'ComboBoxWidget',
+ * input: { value: 'Option One' },
+ * menu: {
+ * items: [
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'Option 1',
+ * label: 'Option One'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'Option 2',
+ * label: 'Option Two'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'Option 3',
+ * label: 'Option Three'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'Option 4',
+ * label: 'Option Four'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'Option 5',
+ * label: 'Option Five'
+ * } )
+ * ]
+ * }
+ * } );
+ * $( 'body' ).append( comboBox.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [menu] Configuration options to pass to the {@link OO.ui.MenuSelectWidget menu select widget}.
+ * @cfg {Object} [input] Configuration options to pass to the {@link OO.ui.TextInputWidget text input widget}.
+ * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
+ * the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
+ * containing `<div>` and has a larger area. By default, the menu uses relative positioning.
+ */
+OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ComboBoxWidget.super.call( this, config );
+
+ // Properties (must be set before TabIndexedElement constructor call)
+ this.$indicator = this.$( '<span>' );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$indicator } ) );
+
+ // Properties
+ this.$overlay = config.$overlay || this.$element;
+ this.input = new OO.ui.TextInputWidget( $.extend(
+ {
+ indicator: 'down',
+ $indicator: this.$indicator,
+ disabled: this.isDisabled()
+ },
+ config.input
+ ) );
+ this.input.$input.eq( 0 ).attr( {
+ role: 'combobox',
+ 'aria-autocomplete': 'list'
+ } );
+ this.menu = new OO.ui.TextInputMenuSelectWidget( this.input, $.extend(
+ {
+ widget: this,
+ input: this.input,
+ disabled: this.isDisabled()
+ },
+ config.menu
+ ) );
+
+ // Events
+ this.$indicator.on( {
+ click: this.onClick.bind( this ),
+ keypress: this.onKeyPress.bind( this )
+ } );
+ this.input.connect( this, {
+ change: 'onInputChange',
+ enter: 'onInputEnter'
+ } );
+ this.menu.connect( this, {
+ choose: 'onMenuChoose',
+ add: 'onMenuItemsChange',
+ remove: 'onMenuItemsChange'
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-comboBoxWidget' ).append( this.input.$element );
+ this.$overlay.append( this.menu.$element );
+ this.onMenuItemsChange();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ComboBoxWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.ComboBoxWidget, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Get the combobox's menu.
+ * @return {OO.ui.TextInputMenuSelectWidget} Menu widget
+ */
+OO.ui.ComboBoxWidget.prototype.getMenu = function () {
+ return this.menu;
+};
+
+/**
+ * Handle input change events.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.ComboBoxWidget.prototype.onInputChange = function ( value ) {
+ var match = this.menu.getItemFromData( value );
+
+ this.menu.selectItem( match );
+ if ( this.menu.getHighlightedItem() ) {
+ this.menu.highlightItem( match );
+ }
+
+ if ( !this.isDisabled() ) {
+ this.menu.toggle( true );
+ }
+};
+
+/**
+ * Handle mouse click events.
+ *
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.ComboBoxWidget.prototype.onClick = function ( e ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
+ this.menu.toggle();
+ this.input.$input[ 0 ].focus();
+ }
+ return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.ComboBoxWidget.prototype.onKeyPress = function ( e ) {
+ if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+ this.menu.toggle();
+ this.input.$input[ 0 ].focus();
+ return false;
+ }
+};
+
+/**
+ * Handle input enter events.
+ *
+ * @private
+ */
+OO.ui.ComboBoxWidget.prototype.onInputEnter = function () {
+ if ( !this.isDisabled() ) {
+ this.menu.toggle( false );
+ }
+};
+
+/**
+ * Handle menu choose events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+OO.ui.ComboBoxWidget.prototype.onMenuChoose = function ( item ) {
+ this.input.setValue( item.getData() );
+};
+
+/**
+ * Handle menu item change events.
+ *
+ * @private
+ */
+OO.ui.ComboBoxWidget.prototype.onMenuItemsChange = function () {
+ var match = this.menu.getItemFromData( this.input.getValue() );
+ this.menu.selectItem( match );
+ if ( this.menu.getHighlightedItem() ) {
+ this.menu.highlightItem( match );
+ }
+ this.$element.toggleClass( 'oo-ui-comboBoxWidget-empty', this.menu.isEmpty() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
+ // Parent method
+ OO.ui.ComboBoxWidget.super.prototype.setDisabled.call( this, disabled );
+
+ if ( this.input ) {
+ this.input.setDisabled( this.isDisabled() );
+ }
+ if ( this.menu ) {
+ this.menu.setDisabled( this.isDisabled() );
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/DecoratedOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/DecoratedOptionWidget.js
new file mode 100644
index 00000000..e39ebcad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/DecoratedOptionWidget.js
@@ -0,0 +1,55 @@
+/**
+ * DecoratedOptionWidgets are {@link OO.ui.OptionWidget options} that can be configured
+ * with an {@link OO.ui.IconElement icon} and/or {@link OO.ui.IndicatorElement indicator}.
+ * This class is used with OO.ui.SelectWidget to create a selection of mutually exclusive
+ * options. For more information about options and selects, please see the
+ * [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Decorated options in a select widget
+ * var select = new OO.ui.SelectWidget( {
+ * items: [
+ * new OO.ui.DecoratedOptionWidget( {
+ * data: 'a',
+ * label: 'Option with icon',
+ * icon: 'help'
+ * } ),
+ * new OO.ui.DecoratedOptionWidget( {
+ * data: 'b',
+ * label: 'Option with indicator',
+ * indicator: 'next'
+ * } )
+ * ]
+ * } );
+ * $( 'body' ).append( select.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
+ // Parent constructor
+ OO.ui.DecoratedOptionWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-decoratedOptionWidget' )
+ .prepend( this.$icon )
+ .append( this.$indicator );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DecoratedOptionWidget, OO.ui.OptionWidget );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.IndicatorElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/DropdownInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/DropdownInputWidget.js
new file mode 100644
index 00000000..d4a5ce2a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/DropdownInputWidget.js
@@ -0,0 +1,137 @@
+/**
+ * DropdownInputWidget is a {@link OO.ui.DropdownWidget DropdownWidget} intended to be used
+ * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
+ * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * more information about input widgets.
+ *
+ * @example
+ * // Example: A DropdownInputWidget with three options
+ * var dropDown = new OO.ui.DropdownInputWidget( {
+ * label: 'Dropdown menu: Select a menu option',
+ * options: [
+ * { data: 'a', label: 'First' } ,
+ * { data: 'b', label: 'Second'} ,
+ * { data: 'c', label: 'Third' }
+ * ]
+ * } );
+ * $( 'body' ).append( dropDown.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
+ */
+OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties (must be done before parent constructor which calls #setDisabled)
+ this.dropdownWidget = new OO.ui.DropdownWidget();
+
+ // Parent constructor
+ OO.ui.DropdownInputWidget.super.call( this, config );
+
+ // Events
+ this.dropdownWidget.getMenu().connect( this, { select: 'onMenuSelect' } );
+
+ // Initialization
+ this.setOptions( config.options || [] );
+ this.$element
+ .addClass( 'oo-ui-dropdownInputWidget' )
+ .append( this.dropdownWidget.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DropdownInputWidget, OO.ui.InputWidget );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @private
+ */
+OO.ui.DropdownInputWidget.prototype.getInputElement = function () {
+ return $( '<input type="hidden">' );
+};
+
+/**
+ * Handles menu select events.
+ *
+ * @private
+ * @param {OO.ui.MenuOptionWidget} item Selected menu item
+ */
+OO.ui.DropdownInputWidget.prototype.onMenuSelect = function ( item ) {
+ this.setValue( item.getData() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) {
+ this.dropdownWidget.getMenu().selectItemByData( value );
+ OO.ui.DropdownInputWidget.super.prototype.setValue.call( this, value );
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.setDisabled = function ( state ) {
+ this.dropdownWidget.setDisabled( state );
+ OO.ui.DropdownInputWidget.super.prototype.setDisabled.call( this, state );
+ return this;
+};
+
+/**
+ * Set the options available for this input.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @chainable
+ */
+OO.ui.DropdownInputWidget.prototype.setOptions = function ( options ) {
+ var value = this.getValue();
+
+ // Rebuild the dropdown menu
+ this.dropdownWidget.getMenu()
+ .clearItems()
+ .addItems( options.map( function ( opt ) {
+ return new OO.ui.MenuOptionWidget( {
+ data: opt.data,
+ label: opt.label !== undefined ? opt.label : opt.data
+ } );
+ } ) );
+
+ // Restore the previous value, or reset to something sensible
+ if ( this.dropdownWidget.getMenu().getItemFromData( value ) ) {
+ // Previous value is still available, ensure consistency with the dropdown
+ this.setValue( value );
+ } else {
+ // No longer valid, reset
+ if ( options.length ) {
+ this.setValue( options[ 0 ].data );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.focus = function () {
+ this.dropdownWidget.getMenu().toggle( true );
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.blur = function () {
+ this.dropdownWidget.getMenu().toggle( false );
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/DropdownWidget.js b/vendor/oojs/oojs-ui/src/widgets/DropdownWidget.js
new file mode 100644
index 00000000..f6c592e8
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/DropdownWidget.js
@@ -0,0 +1,149 @@
+/**
+ * DropdownWidgets are not menus themselves, rather they contain a menu of options created with
+ * OO.ui.MenuOptionWidget. The DropdownWidget takes care of opening and displaying the menu so that
+ * users can interact with it.
+ *
+ * @example
+ * // Example: A DropdownWidget with a menu that contains three options
+ * var dropDown = new OO.ui.DropdownWidget( {
+ * label: 'Dropdown menu: Select a menu option',
+ * menu: {
+ * items: [
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'a',
+ * label: 'First'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'b',
+ * label: 'Second'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'c',
+ * label: 'Third'
+ * } )
+ * ]
+ * }
+ * } );
+ *
+ * $( 'body' ).append( dropDown.$element );
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.TitledElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [menu] Configuration options to pass to menu widget
+ */
+OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
+ // Configuration initialization
+ config = $.extend( { indicator: 'down' }, config );
+
+ // Parent constructor
+ OO.ui.DropdownWidget.super.call( this, config );
+
+ // Properties (must be set before TabIndexedElement constructor call)
+ this.$handle = this.$( '<span>' );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
+
+ // Properties
+ this.menu = new OO.ui.MenuSelectWidget( $.extend( { widget: this }, config.menu ) );
+
+ // Events
+ this.$handle.on( {
+ click: this.onClick.bind( this ),
+ keypress: this.onKeyPress.bind( this )
+ } );
+ this.menu.connect( this, { select: 'onMenuSelect' } );
+
+ // Initialization
+ this.$handle
+ .addClass( 'oo-ui-dropdownWidget-handle' )
+ .append( this.$icon, this.$label, this.$indicator );
+ this.$element
+ .addClass( 'oo-ui-dropdownWidget' )
+ .append( this.$handle, this.menu.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DropdownWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.TitledElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Get the menu.
+ *
+ * @return {OO.ui.MenuSelectWidget} Menu of widget
+ */
+OO.ui.DropdownWidget.prototype.getMenu = function () {
+ return this.menu;
+};
+
+/**
+ * Handles menu select events.
+ *
+ * @private
+ * @param {OO.ui.MenuOptionWidget} item Selected menu item
+ */
+OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
+ var selectedLabel;
+
+ if ( !item ) {
+ return;
+ }
+
+ selectedLabel = item.getLabel();
+
+ // If the label is a DOM element, clone it, because setLabel will append() it
+ if ( selectedLabel instanceof jQuery ) {
+ selectedLabel = selectedLabel.clone();
+ }
+
+ this.setLabel( selectedLabel );
+};
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
+ this.menu.toggle();
+ }
+ return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
+ if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+ this.menu.toggle();
+ return false;
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/GroupWidget.js b/vendor/oojs/oojs-ui/src/widgets/GroupWidget.js
new file mode 100644
index 00000000..7d9be905
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/GroupWidget.js
@@ -0,0 +1,48 @@
+/**
+ * Mixin for OO.ui.Widget subclasses to provide OO.ui.GroupElement.
+ *
+ * Use together with OO.ui.ItemWidget to make disabled state inheritable.
+ *
+ * @private
+ * @abstract
+ * @class
+ * @extends OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.GroupWidget = function OoUiGroupWidget( config ) {
+ // Parent constructor
+ OO.ui.GroupWidget.super.call( this, config );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.GroupWidget, OO.ui.GroupElement );
+
+/* Methods */
+
+/**
+ * Set the disabled state of the widget.
+ *
+ * This will also update the disabled state of child widgets.
+ *
+ * @param {boolean} disabled Disable widget
+ * @chainable
+ */
+OO.ui.GroupWidget.prototype.setDisabled = function ( disabled ) {
+ var i, len;
+
+ // Parent method
+ // Note: Calling #setDisabled this way assumes this is mixed into an OO.ui.Widget
+ OO.ui.Widget.prototype.setDisabled.call( this, disabled );
+
+ // During construction, #setDisabled is called before the OO.ui.GroupElement constructor
+ if ( this.items ) {
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ this.items[ i ].updateDisabled();
+ }
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/IconWidget.js b/vendor/oojs/oojs-ui/src/widgets/IconWidget.js
new file mode 100644
index 00000000..66ea3871
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/IconWidget.js
@@ -0,0 +1,54 @@
+/**
+ * IconWidget is a generic widget for {@link OO.ui.IconElement icons}. In general, IconWidgets should be used with OO.ui.LabelWidget,
+ * which creates a label that identifies the icon’s function. See the [OOjs UI documentation on MediaWiki] [1]
+ * for a list of icons included in the library.
+ *
+ * @example
+ * // An icon widget with a label
+ * var myIcon = new OO.ui.IconWidget( {
+ * icon: 'help',
+ * iconTitle: 'Help'
+ * } );
+ * // Create a label.
+ * var iconLabel = new OO.ui.LabelWidget( {
+ * label: 'Help'
+ * } );
+ * $( 'body' ).append( myIcon.$element, iconLabel.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.TitledElement
+ * @mixins OO.ui.FlaggedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IconWidget = function OoUiIconWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.IconWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+ OO.ui.FlaggedElement.call( this, $.extend( {}, config, { $flagged: this.$element } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-iconWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.TitledElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.FlaggedElement );
+
+/* Static Properties */
+
+OO.ui.IconWidget.static.tagName = 'span';
diff --git a/vendor/oojs/oojs-ui/src/widgets/IndicatorWidget.js b/vendor/oojs/oojs-ui/src/widgets/IndicatorWidget.js
new file mode 100644
index 00000000..e6ecddf0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/IndicatorWidget.js
@@ -0,0 +1,52 @@
+/**
+ * IndicatorWidgets create indicators, which are small graphics that are generally used to draw
+ * attention to the status of an item or to clarify the function of a control. For a list of
+ * indicators included in the library, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Example of an indicator widget
+ * var indicator1 = new OO.ui.IndicatorWidget( {
+ * indicator: 'alert'
+ * } );
+ *
+ * // Create a fieldset layout to add a label
+ * var fieldset = new OO.ui.FieldsetLayout();
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( indicator1, { label: 'An alert indicator:' } )
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.IndicatorWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-indicatorWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IndicatorWidget.static.tagName = 'span';
diff --git a/vendor/oojs/oojs-ui/src/widgets/InputWidget.js b/vendor/oojs/oojs-ui/src/widgets/InputWidget.js
new file mode 100644
index 00000000..de790bac
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/InputWidget.js
@@ -0,0 +1,207 @@
+/**
+ * InputWidget is the base class for all input widgets, which
+ * include {@link OO.ui.TextInputWidget text inputs}, {@link OO.ui.CheckboxInputWidget checkbox inputs},
+ * {@link OO.ui.RadioInputWidget radio inputs}, and {@link OO.ui.ButtonInputWidget button inputs}.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.FlaggedElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [name=''] The value of the input’s HTML `name` attribute.
+ * @cfg {string} [value=''] The value of the input.
+ * @cfg {Function} [inputFilter] The name of an input filter function. Input filters modify the value of an input
+ * before it is accepted.
+ */
+OO.ui.InputWidget = function OoUiInputWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.InputWidget.super.call( this, config );
+
+ // Properties
+ this.$input = this.getInputElement( config );
+ this.value = '';
+ this.inputFilter = config.inputFilter;
+
+ // Mixin constructors
+ OO.ui.FlaggedElement.call( this, config );
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$input } ) );
+
+ // Events
+ this.$input.on( 'keydown mouseup cut paste change input select', this.onEdit.bind( this ) );
+
+ // Initialization
+ this.$input
+ .attr( 'name', config.name )
+ .prop( 'disabled', this.isDisabled() );
+ this.$element.addClass( 'oo-ui-inputWidget' ).append( this.$input, $( '<span>' ) );
+ this.setValue( config.value );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.InputWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.FlaggedElement );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.TabIndexedElement );
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the value of the input changes.
+ *
+ * @param {string} value
+ */
+
+/* Methods */
+
+/**
+ * Get input element.
+ *
+ * Subclasses of OO.ui.InputWidget use the `config` parameter to produce different elements in
+ * different circumstances. The element must have a `value` property (like form elements).
+ *
+ * @private
+ * @param {Object} config Configuration options
+ * @return {jQuery} Input element
+ */
+OO.ui.InputWidget.prototype.getInputElement = function () {
+ return $( '<input>' );
+};
+
+/**
+ * Handle potentially value-changing events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down, mouse up, cut, paste, change, input, or select event
+ */
+OO.ui.InputWidget.prototype.onEdit = function () {
+ var widget = this;
+ if ( !this.isDisabled() ) {
+ // Allow the stack to clear so the value will be updated
+ setTimeout( function () {
+ widget.setValue( widget.$input.val() );
+ } );
+ }
+};
+
+/**
+ * Get the value of the input.
+ *
+ * @return {string} Input value
+ */
+OO.ui.InputWidget.prototype.getValue = function () {
+ // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
+ // it, and we won't know unless they're kind enough to trigger a 'change' event.
+ var value = this.$input.val();
+ if ( this.value !== value ) {
+ this.setValue( value );
+ }
+ return this.value;
+};
+
+/**
+ * Set the direction of the input, either RTL (right-to-left) or LTR (left-to-right).
+ *
+ * @param {boolean} isRTL
+ * Direction is right-to-left
+ */
+OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
+ this.$input.prop( 'dir', isRTL ? 'rtl' : 'ltr' );
+};
+
+/**
+ * Set the value of the input.
+ *
+ * @param {string} value New value
+ * @fires change
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.setValue = function ( value ) {
+ value = this.cleanUpValue( value );
+ // Update the DOM if it has changed. Note that with cleanUpValue, it
+ // is possible for the DOM value to change without this.value changing.
+ if ( this.$input.val() !== value ) {
+ this.$input.val( value );
+ }
+ if ( this.value !== value ) {
+ this.value = value;
+ this.emit( 'change', this.value );
+ }
+ return this;
+};
+
+/**
+ * Clean up incoming value.
+ *
+ * Ensures value is a string, and converts undefined and null to empty string.
+ *
+ * @private
+ * @param {string} value Original value
+ * @return {string} Cleaned up value
+ */
+OO.ui.InputWidget.prototype.cleanUpValue = function ( value ) {
+ if ( value === undefined || value === null ) {
+ return '';
+ } else if ( this.inputFilter ) {
+ return this.inputFilter( String( value ) );
+ } else {
+ return String( value );
+ }
+};
+
+/**
+ * Simulate the behavior of clicking on a label bound to this input. This method is only called by
+ * {@link OO.ui.LabelWidget LabelWidget} and {@link OO.ui.FieldLayout FieldLayout}. It should not be
+ * called directly.
+ */
+OO.ui.InputWidget.prototype.simulateLabelClick = function () {
+ if ( !this.isDisabled() ) {
+ if ( this.$input.is( ':checkbox, :radio' ) ) {
+ this.$input.click();
+ }
+ if ( this.$input.is( ':input' ) ) {
+ this.$input[ 0 ].focus();
+ }
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.prototype.setDisabled = function ( state ) {
+ OO.ui.InputWidget.super.prototype.setDisabled.call( this, state );
+ if ( this.$input ) {
+ this.$input.prop( 'disabled', this.isDisabled() );
+ }
+ return this;
+};
+
+/**
+ * Focus the input.
+ *
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.focus = function () {
+ this.$input[ 0 ].focus();
+ return this;
+};
+
+/**
+ * Blur the input.
+ *
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.blur = function () {
+ this.$input[ 0 ].blur();
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ItemWidget.js b/vendor/oojs/oojs-ui/src/widgets/ItemWidget.js
new file mode 100644
index 00000000..292514f4
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ItemWidget.js
@@ -0,0 +1,48 @@
+/**
+ * Mixin for widgets used as items in widgets that inherit OO.ui.GroupWidget.
+ *
+ * Item widgets have a reference to a OO.ui.GroupWidget while they are attached to the group. This
+ * allows bidirectional communication.
+ *
+ * Use together with OO.ui.GroupWidget to make disabled state inheritable.
+ *
+ * @private
+ * @abstract
+ * @class
+ *
+ * @constructor
+ */
+OO.ui.ItemWidget = function OoUiItemWidget() {
+ //
+};
+
+/* Methods */
+
+/**
+ * Check if widget is disabled.
+ *
+ * Checks parent if present, making disabled state inheritable.
+ *
+ * @return {boolean} Widget is disabled
+ */
+OO.ui.ItemWidget.prototype.isDisabled = function () {
+ return this.disabled ||
+ ( this.elementGroup instanceof OO.ui.Widget && this.elementGroup.isDisabled() );
+};
+
+/**
+ * Set group element is in.
+ *
+ * @param {OO.ui.GroupElement|null} group Group element, null if none
+ * @chainable
+ */
+OO.ui.ItemWidget.prototype.setElementGroup = function ( group ) {
+ // Parent method
+ // Note: Calling #setElementGroup this way assumes this is mixed into an OO.ui.Element
+ OO.ui.Element.prototype.setElementGroup.call( this, group );
+
+ // Initialize item disabled states
+ this.updateDisabled();
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/LabelWidget.js b/vendor/oojs/oojs-ui/src/widgets/LabelWidget.js
new file mode 100644
index 00000000..e00990bb
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/LabelWidget.js
@@ -0,0 +1,84 @@
+/**
+ * LabelWidgets help identify the function of interface elements. Each LabelWidget can
+ * be configured with a `label` option that is set to a string, a label node, or a function:
+ *
+ * - String: a plaintext string
+ * - jQuery selection: a jQuery selection, used for anything other than a plaintext label, e.g., a
+ * label that includes a link or special styling, such as a gray color or additional graphical elements.
+ * - Function: a function that will produce a string in the future. Functions are used
+ * in cases where the value of the label is not currently defined.
+ *
+ * In addition, the LabelWidget can be associated with an {@link OO.ui.InputWidget input widget}, which
+ * will come into focus when the label is clicked.
+ *
+ * @example
+ * // Examples of LabelWidgets
+ * var label1 = new OO.ui.LabelWidget( {
+ * label: 'plaintext label'
+ * } );
+ * var label2 = new OO.ui.LabelWidget( {
+ * label: $( '<a href="default.html">jQuery label</a>' )
+ * } );
+ * // Create a fieldset layout with fields for each example
+ * var fieldset = new OO.ui.FieldsetLayout();
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( label1 ),
+ * new OO.ui.FieldLayout( label2 )
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.InputWidget} [input] {@link OO.ui.InputWidget Input widget} that uses the label.
+ * Clicking the label will focus the specified input field.
+ */
+OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.LabelWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.LabelElement.call( this, $.extend( {}, config, { $label: this.$element } ) );
+ OO.ui.TitledElement.call( this, config );
+
+ // Properties
+ this.input = config.input;
+
+ // Events
+ if ( this.input instanceof OO.ui.InputWidget ) {
+ this.$element.on( 'click', this.onClick.bind( this ) );
+ }
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-labelWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.LabelWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.LabelWidget.static.tagName = 'span';
+
+/* Methods */
+
+/**
+ * Handles label mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.LabelWidget.prototype.onClick = function () {
+ this.input.simulateLabelClick();
+ return false;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/MenuOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/MenuOptionWidget.js
new file mode 100644
index 00000000..cc620d97
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/MenuOptionWidget.js
@@ -0,0 +1,33 @@
+/**
+ * MenuOptionWidget is an option widget that looks like a menu item. The class is used with
+ * OO.ui.MenuSelectWidget to create a menu of mutually exclusive options. Please see
+ * the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MenuOptionWidget = function OoUiMenuOptionWidget( config ) {
+ // Configuration initialization
+ config = $.extend( { icon: 'check' }, config );
+
+ // Parent constructor
+ OO.ui.MenuOptionWidget.super.call( this, config );
+
+ // Initialization
+ this.$element
+ .attr( 'role', 'menuitem' )
+ .addClass( 'oo-ui-menuOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.MenuOptionWidget.static.scrollIntoViewOnSelect = true;
diff --git a/vendor/oojs/oojs-ui/src/widgets/MenuSectionOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/MenuSectionOptionWidget.js
new file mode 100644
index 00000000..054df7fc
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/MenuSectionOptionWidget.js
@@ -0,0 +1,55 @@
+/**
+ * MenuSectionOptionWidgets are used inside {@link OO.ui.MenuSelectWidget menu select widgets} to group one or more related
+ * {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
+ *
+ * @example
+ * var myDropdown = new OO.ui.DropdownWidget( {
+ * menu: {
+ * items: [
+ * new OO.ui.MenuSectionOptionWidget( {
+ * label: 'Dogs'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'corgi',
+ * label: 'Welsh Corgi'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'poodle',
+ * label: 'Standard Poodle'
+ * } ),
+ * new OO.ui.MenuSectionOptionWidget( {
+ * label: 'Cats'
+ * } ),
+ * new OO.ui.MenuOptionWidget( {
+ * data: 'lion',
+ * label: 'Lion'
+ * } )
+ * ]
+ * }
+ * } );
+ * $( 'body' ).append( myDropdown.$element );
+ *
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
+ // Parent constructor
+ OO.ui.MenuSectionOptionWidget.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-menuSectionOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.MenuSectionOptionWidget.static.selectable = false;
+
+OO.ui.MenuSectionOptionWidget.static.highlightable = false;
diff --git a/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js
new file mode 100644
index 00000000..a1755edd
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js
@@ -0,0 +1,254 @@
+/**
+ * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
+ * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
+ * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxWidget ComboBoxWidget},
+ * and {@link OO.ui.LookupElement LookupElement} for examples of widgets that contain menus.
+ * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
+ * and customized to be opened, closed, and displayed as needed.
+ *
+ * By default, menus are clipped to the visible viewport and are not visible when a user presses the
+ * mouse outside the menu.
+ *
+ * Menus also have support for keyboard interaction:
+ *
+ * - Enter/Return key: choose and select a menu option
+ * - Up-arrow key: highlight the previous menu option
+ * - Down-arrow key: highlight the next menu option
+ * - Esc key: hide the menu
+ *
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.ClippableElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
+ * the text the user types. This config is used by {@link OO.ui.ComboBoxWidget ComboBoxWidget}
+ * and {@link OO.ui.LookupElement LookupElement}
+ * @cfg {OO.ui.Widget} [widget] Widget associated with the menu’s active state. If the user clicks the mouse
+ * anywhere on the page outside of this widget, the menu is hidden.
+ * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
+ */
+OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.MenuSelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
+
+ // Properties
+ this.newItems = null;
+ this.autoHide = config.autoHide === undefined || !!config.autoHide;
+ this.$input = config.input ? config.input.$input : null;
+ this.$widget = config.widget ? config.widget.$element : null;
+ this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-menuSelectWidget' )
+ .attr( 'role', 'menu' );
+
+ // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+ // that reference properties not initialized at that time of parent class construction
+ // TODO: Find a better way to handle post-constructor setup
+ this.visible = false;
+ this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.ClippableElement );
+
+/* Methods */
+
+/**
+ * Handles document mouse down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
+ if (
+ !OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
+ ( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) )
+ ) {
+ this.toggle( false );
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
+ var currentItem = this.getHighlightedItem() || this.getSelectedItem();
+
+ if ( !this.isDisabled() && this.isVisible() ) {
+ switch ( e.keyCode ) {
+ case OO.ui.Keys.LEFT:
+ case OO.ui.Keys.RIGHT:
+ // Do nothing if a text field is associated, arrow keys will be handled natively
+ if ( !this.$input ) {
+ OO.ui.MenuSelectWidget.super.prototype.onKeyDown.call( this, e );
+ }
+ break;
+ case OO.ui.Keys.ESCAPE:
+ case OO.ui.Keys.TAB:
+ if ( currentItem ) {
+ currentItem.setHighlighted( false );
+ }
+ this.toggle( false );
+ // Don't prevent tabbing away, prevent defocusing
+ if ( e.keyCode === OO.ui.Keys.ESCAPE ) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ break;
+ default:
+ OO.ui.MenuSelectWidget.super.prototype.onKeyDown.call( this, e );
+ return;
+ }
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
+ if ( this.$input ) {
+ this.$input.on( 'keydown', this.onKeyDownHandler );
+ } else {
+ OO.ui.MenuSelectWidget.super.prototype.bindKeyDownListener.call( this );
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
+ if ( this.$input ) {
+ this.$input.off( 'keydown', this.onKeyDownHandler );
+ } else {
+ OO.ui.MenuSelectWidget.super.prototype.unbindKeyDownListener.call( this );
+ }
+};
+
+/**
+ * Choose an item.
+ *
+ * When a user chooses an item, the menu is closed.
+ *
+ * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
+ * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
+ * @param {OO.ui.OptionWidget} item Item to choose
+ * @chainable
+ */
+OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
+ OO.ui.MenuSelectWidget.super.prototype.chooseItem.call( this, item );
+ this.toggle( false );
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
+ var i, len, item;
+
+ // Parent method
+ OO.ui.MenuSelectWidget.super.prototype.addItems.call( this, items, index );
+
+ // Auto-initialize
+ if ( !this.newItems ) {
+ this.newItems = [];
+ }
+
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ item = items[ i ];
+ if ( this.isVisible() ) {
+ // Defer fitting label until item has been attached
+ item.fitLabel();
+ } else {
+ this.newItems.push( item );
+ }
+ }
+
+ // Reevaluate clipping
+ this.clip();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
+ // Parent method
+ OO.ui.MenuSelectWidget.super.prototype.removeItems.call( this, items );
+
+ // Reevaluate clipping
+ this.clip();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.clearItems = function () {
+ // Parent method
+ OO.ui.MenuSelectWidget.super.prototype.clearItems.call( this );
+
+ // Reevaluate clipping
+ this.clip();
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
+ visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
+
+ var i, len,
+ change = visible !== this.isVisible();
+
+ // Parent method
+ OO.ui.MenuSelectWidget.super.prototype.toggle.call( this, visible );
+
+ if ( change ) {
+ if ( visible ) {
+ this.bindKeyDownListener();
+
+ if ( this.newItems && this.newItems.length ) {
+ for ( i = 0, len = this.newItems.length; i < len; i++ ) {
+ this.newItems[ i ].fitLabel();
+ }
+ this.newItems = null;
+ }
+ this.toggleClipping( true );
+
+ // Auto-hide
+ if ( this.autoHide ) {
+ this.getElementDocument().addEventListener(
+ 'mousedown', this.onDocumentMouseDownHandler, true
+ );
+ }
+ } else {
+ this.unbindKeyDownListener();
+ this.getElementDocument().removeEventListener(
+ 'mousedown', this.onDocumentMouseDownHandler, true
+ );
+ this.toggleClipping( false );
+ }
+ }
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/OptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/OptionWidget.js
new file mode 100644
index 00000000..0bc2486e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/OptionWidget.js
@@ -0,0 +1,177 @@
+/**
+ * OptionWidgets are special elements that can be selected and configured with data. The
+ * data is often unique for each option, but it does not have to be. OptionWidgets are used
+ * with OO.ui.SelectWidget to create a selection of mutually exclusive options. For more information
+ * and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.FlaggedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.OptionWidget = function OoUiOptionWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.OptionWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ItemWidget.call( this );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.FlaggedElement.call( this, config );
+
+ // Properties
+ this.selected = false;
+ this.highlighted = false;
+ this.pressed = false;
+
+ // Initialization
+ this.$element
+ .data( 'oo-ui-optionWidget', this )
+ .attr( 'role', 'option' )
+ .addClass( 'oo-ui-optionWidget' )
+ .append( this.$label );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OptionWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.ItemWidget );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.FlaggedElement );
+
+/* Static Properties */
+
+OO.ui.OptionWidget.static.selectable = true;
+
+OO.ui.OptionWidget.static.highlightable = true;
+
+OO.ui.OptionWidget.static.pressable = true;
+
+OO.ui.OptionWidget.static.scrollIntoViewOnSelect = false;
+
+/* Methods */
+
+/**
+ * Check if the option can be selected.
+ *
+ * @return {boolean} Item is selectable
+ */
+OO.ui.OptionWidget.prototype.isSelectable = function () {
+ return this.constructor.static.selectable && !this.isDisabled();
+};
+
+/**
+ * Check if the option can be highlighted. A highlight indicates that the option
+ * may be selected when a user presses enter or clicks. Disabled items cannot
+ * be highlighted.
+ *
+ * @return {boolean} Item is highlightable
+ */
+OO.ui.OptionWidget.prototype.isHighlightable = function () {
+ return this.constructor.static.highlightable && !this.isDisabled();
+};
+
+/**
+ * Check if the option can be pressed. The pressed state occurs when a user mouses
+ * down on an item, but has not yet let go of the mouse.
+ *
+ * @return {boolean} Item is pressable
+ */
+OO.ui.OptionWidget.prototype.isPressable = function () {
+ return this.constructor.static.pressable && !this.isDisabled();
+};
+
+/**
+ * Check if the option is selected.
+ *
+ * @return {boolean} Item is selected
+ */
+OO.ui.OptionWidget.prototype.isSelected = function () {
+ return this.selected;
+};
+
+/**
+ * Check if the option is highlighted. A highlight indicates that the
+ * item may be selected when a user presses enter or clicks.
+ *
+ * @return {boolean} Item is highlighted
+ */
+OO.ui.OptionWidget.prototype.isHighlighted = function () {
+ return this.highlighted;
+};
+
+/**
+ * Check if the option is pressed. The pressed state occurs when a user mouses
+ * down on an item, but has not yet let go of the mouse. The item may appear
+ * selected, but it will not be selected until the user releases the mouse.
+ *
+ * @return {boolean} Item is pressed
+ */
+OO.ui.OptionWidget.prototype.isPressed = function () {
+ return this.pressed;
+};
+
+/**
+ * Set the option’s selected state. In general, all modifications to the selection
+ * should be handled by the SelectWidget’s {@link OO.ui.SelectWidget#selectItem selectItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Select option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
+ if ( this.constructor.static.selectable ) {
+ this.selected = !!state;
+ this.$element
+ .toggleClass( 'oo-ui-optionWidget-selected', state )
+ .attr( 'aria-selected', state.toString() );
+ if ( state && this.constructor.static.scrollIntoViewOnSelect ) {
+ this.scrollElementIntoView();
+ }
+ this.updateThemeClasses();
+ }
+ return this;
+};
+
+/**
+ * Set the option’s highlighted state. In general, all programmatic
+ * modifications to the highlight should be handled by the
+ * SelectWidget’s {@link OO.ui.SelectWidget#highlightItem highlightItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Highlight option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
+ if ( this.constructor.static.highlightable ) {
+ this.highlighted = !!state;
+ this.$element.toggleClass( 'oo-ui-optionWidget-highlighted', state );
+ this.updateThemeClasses();
+ }
+ return this;
+};
+
+/**
+ * Set the option’s pressed state. In general, all
+ * programmatic modifications to the pressed state should be handled by the
+ * SelectWidget’s {@link OO.ui.SelectWidget#pressItem pressItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Press option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
+ if ( this.constructor.static.pressable ) {
+ this.pressed = !!state;
+ this.$element.toggleClass( 'oo-ui-optionWidget-pressed', state );
+ this.updateThemeClasses();
+ }
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/OutlineControlsWidget.js b/vendor/oojs/oojs-ui/src/widgets/OutlineControlsWidget.js
new file mode 100644
index 00000000..0219a44d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/OutlineControlsWidget.js
@@ -0,0 +1,145 @@
+/**
+ * OutlineControlsWidget is a set of controls for an {@link OO.ui.OutlineSelectWidget outline select widget}.
+ * Controls include moving items up and down, removing items, and adding different kinds of items.
+ * ####Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.####
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.GroupElement
+ * @mixins OO.ui.IconElement
+ *
+ * @constructor
+ * @param {OO.ui.OutlineSelectWidget} outline Outline to control
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [abilities] List of abilties
+ * @cfg {boolean} [abilities.move=true] Allow moving movable items
+ * @cfg {boolean} [abilities.remove=true] Allow removing removable items
+ */
+OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( outline ) && config === undefined ) {
+ config = outline;
+ outline = config.outline;
+ }
+
+ // Configuration initialization
+ config = $.extend( { icon: 'add' }, config );
+
+ // Parent constructor
+ OO.ui.OutlineControlsWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.GroupElement.call( this, config );
+ OO.ui.IconElement.call( this, config );
+
+ // Properties
+ this.outline = outline;
+ this.$movers = $( '<div>' );
+ this.upButton = new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'collapse',
+ title: OO.ui.msg( 'ooui-outline-control-move-up' )
+ } );
+ this.downButton = new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'expand',
+ title: OO.ui.msg( 'ooui-outline-control-move-down' )
+ } );
+ this.removeButton = new OO.ui.ButtonWidget( {
+ framed: false,
+ icon: 'remove',
+ title: OO.ui.msg( 'ooui-outline-control-remove' )
+ } );
+ this.abilities = { move: true, remove: true };
+
+ // Events
+ outline.connect( this, {
+ select: 'onOutlineChange',
+ add: 'onOutlineChange',
+ remove: 'onOutlineChange'
+ } );
+ this.upButton.connect( this, { click: [ 'emit', 'move', -1 ] } );
+ this.downButton.connect( this, { click: [ 'emit', 'move', 1 ] } );
+ this.removeButton.connect( this, { click: [ 'emit', 'remove' ] } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-outlineControlsWidget' );
+ this.$group.addClass( 'oo-ui-outlineControlsWidget-items' );
+ this.$movers
+ .addClass( 'oo-ui-outlineControlsWidget-movers' )
+ .append( this.removeButton.$element, this.upButton.$element, this.downButton.$element );
+ this.$element.append( this.$icon, this.$group, this.$movers );
+ this.setAbilities( config.abilities || {} );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineControlsWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.GroupElement );
+OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.IconElement );
+
+/* Events */
+
+/**
+ * @event move
+ * @param {number} places Number of places to move
+ */
+
+/**
+ * @event remove
+ */
+
+/* Methods */
+
+/**
+ * Set abilities.
+ *
+ * @param {Object} abilities List of abilties
+ * @param {boolean} [abilities.move] Allow moving movable items
+ * @param {boolean} [abilities.remove] Allow removing removable items
+ */
+OO.ui.OutlineControlsWidget.prototype.setAbilities = function ( abilities ) {
+ var ability;
+
+ for ( ability in this.abilities ) {
+ if ( abilities[ability] !== undefined ) {
+ this.abilities[ability] = !!abilities[ability];
+ }
+ }
+
+ this.onOutlineChange();
+};
+
+/**
+ *
+ * @private
+ * Handle outline change events.
+ */
+OO.ui.OutlineControlsWidget.prototype.onOutlineChange = function () {
+ var i, len, firstMovable, lastMovable,
+ items = this.outline.getItems(),
+ selectedItem = this.outline.getSelectedItem(),
+ movable = this.abilities.move && selectedItem && selectedItem.isMovable(),
+ removable = this.abilities.remove && selectedItem && selectedItem.isRemovable();
+
+ if ( movable ) {
+ i = -1;
+ len = items.length;
+ while ( ++i < len ) {
+ if ( items[ i ].isMovable() ) {
+ firstMovable = items[ i ];
+ break;
+ }
+ }
+ i = len;
+ while ( i-- ) {
+ if ( items[ i ].isMovable() ) {
+ lastMovable = items[ i ];
+ break;
+ }
+ }
+ }
+ this.upButton.setDisabled( !movable || selectedItem === firstMovable );
+ this.downButton.setDisabled( !movable || selectedItem === lastMovable );
+ this.removeButton.setDisabled( !removable );
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/OutlineOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/OutlineOptionWidget.js
new file mode 100644
index 00000000..d792fee6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/OutlineOptionWidget.js
@@ -0,0 +1,130 @@
+/**
+ * OutlineOptionWidget is an item in an {@link OO.ui.OutlineSelectWidget OutlineSelectWidget}.
+ *
+ * Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}, which contain
+ * {@link OO.ui.PageLayout page layouts}. See {@link OO.ui.BookletLayout BookletLayout}
+ * for an example.
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number} [level] Indentation level
+ * @cfg {boolean} [movable] Allow modification from {@link OO.ui.OutlineControlsWidget outline controls}.
+ */
+OO.ui.OutlineOptionWidget = function OoUiOutlineOptionWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.OutlineOptionWidget.super.call( this, config );
+
+ // Properties
+ this.level = 0;
+ this.movable = !!config.movable;
+ this.removable = !!config.removable;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-outlineOptionWidget' );
+ this.setLevel( config.level );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.OutlineOptionWidget.static.highlightable = false;
+
+OO.ui.OutlineOptionWidget.static.scrollIntoViewOnSelect = true;
+
+OO.ui.OutlineOptionWidget.static.levelClass = 'oo-ui-outlineOptionWidget-level-';
+
+OO.ui.OutlineOptionWidget.static.levels = 3;
+
+/* Methods */
+
+/**
+ * Check if item is movable.
+ *
+ * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @return {boolean} Item is movable
+ */
+OO.ui.OutlineOptionWidget.prototype.isMovable = function () {
+ return this.movable;
+};
+
+/**
+ * Check if item is removable.
+ *
+ * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @return {boolean} Item is removable
+ */
+OO.ui.OutlineOptionWidget.prototype.isRemovable = function () {
+ return this.removable;
+};
+
+/**
+ * Get indentation level.
+ *
+ * @return {number} Indentation level
+ */
+OO.ui.OutlineOptionWidget.prototype.getLevel = function () {
+ return this.level;
+};
+
+/**
+ * Set movability.
+ *
+ * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @param {boolean} movable Item is movable
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setMovable = function ( movable ) {
+ this.movable = !!movable;
+ this.updateThemeClasses();
+ return this;
+};
+
+/**
+ * Set removability.
+ *
+ * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @param {boolean} movable Item is removable
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
+ this.removable = !!removable;
+ this.updateThemeClasses();
+ return this;
+};
+
+/**
+ * Set indentation level.
+ *
+ * @param {number} [level=0] Indentation level, in the range of [0,#maxLevel]
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setLevel = function ( level ) {
+ var levels = this.constructor.static.levels,
+ levelClass = this.constructor.static.levelClass,
+ i = levels;
+
+ this.level = level ? Math.max( 0, Math.min( levels - 1, level ) ) : 0;
+ while ( i-- ) {
+ if ( this.level === i ) {
+ this.$element.addClass( levelClass + i );
+ } else {
+ this.$element.removeClass( levelClass + i );
+ }
+ }
+ this.updateThemeClasses();
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/OutlineSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/OutlineSelectWidget.js
new file mode 100644
index 00000000..cd7db546
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/OutlineSelectWidget.js
@@ -0,0 +1,34 @@
+/**
+ * OutlineSelectWidget is a structured list that contains {@link OO.ui.OutlineOptionWidget outline options}
+ * A set of controls can be provided with an {@link OO.ui.OutlineControlsWidget outline controls} widget.
+ *
+ * ####Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.####
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.OutlineSelectWidget = function OoUiOutlineSelectWidget( config ) {
+ // Parent constructor
+ OO.ui.OutlineSelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, config );
+
+ // Events
+ this.$element.on( {
+ focus: this.bindKeyDownListener.bind( this ),
+ blur: this.unbindKeyDownListener.bind( this )
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-outlineSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.TabIndexedElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/PopupButtonWidget.js b/vendor/oojs/oojs-ui/src/widgets/PopupButtonWidget.js
new file mode 100644
index 00000000..5643b77c
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/PopupButtonWidget.js
@@ -0,0 +1,57 @@
+/**
+ * PopupButtonWidgets toggle the visibility of a contained {@link OO.ui.PopupWidget PopupWidget},
+ * which is used to display additional information or options.
+ *
+ * @example
+ * // Example of a popup button.
+ * var popupButton = new OO.ui.PopupButtonWidget( {
+ * label: 'Popup button with options',
+ * icon: 'menu',
+ * popup: {
+ * $content: $( '<p>Additional options here.</p>' ),
+ * padded: true,
+ * align: 'force-left'
+ * }
+ * } );
+ * // Append the button to the DOM.
+ * $( 'body' ).append( popupButton.$element );
+ *
+ * @class
+ * @extends OO.ui.ButtonWidget
+ * @mixins OO.ui.PopupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.PopupButtonWidget = function OoUiPopupButtonWidget( config ) {
+ // Parent constructor
+ OO.ui.PopupButtonWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.PopupElement.call( this, config );
+
+ // Events
+ this.connect( this, { click: 'onAction' } );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-popupButtonWidget' )
+ .attr( 'aria-haspopup', 'true' )
+ .append( this.popup.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupButtonWidget, OO.ui.ButtonWidget );
+OO.mixinClass( OO.ui.PopupButtonWidget, OO.ui.PopupElement );
+
+/* Methods */
+
+/**
+ * Handle the button action being triggered.
+ *
+ * @private
+ */
+OO.ui.PopupButtonWidget.prototype.onAction = function () {
+ this.popup.toggle();
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js b/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js
new file mode 100644
index 00000000..0b1f4ca6
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js
@@ -0,0 +1,395 @@
+/**
+ * PopupWidget is a container for content. The popup is overlaid and positioned absolutely.
+ * By default, each popup has an anchor that points toward its origin.
+ * Please see the [OOjs UI documentation on Mediawiki] [1] for more information and examples.
+ *
+ * @example
+ * // A popup widget.
+ * var popup = new OO.ui.PopupWidget( {
+ * $content: $( '<p>Hi there!</p>' ),
+ * padded: true,
+ * width: 300
+ * } );
+ *
+ * $( 'body' ).append( popup.$element );
+ * // To display the popup, toggle the visibility to 'true'.
+ * popup.toggle( true );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number} [width=320] Width of popup in pixels
+ * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height.
+ * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup
+ * @cfg {string} [align='center'] Alignment of the popup: `center`, `force-left`, `force-right`, `backwards` or `forwards`.
+ * If the popup is forced-left the popup body is leaning towards the left. For force-right alignment, the body of the
+ * popup is leaning towards the right of the screen.
+ * Using 'backwards' is a logical direction which will result in the popup leaning towards the beginning of the sentence
+ * in the given language, which means it will flip to the correct positioning in right-to-left languages.
+ * Using 'forward' will also result in a logical alignment where the body of the popup leans towards the end of the
+ * sentence in the given language.
+ * @cfg {jQuery} [$container] Constrain the popup to the boundaries of the specified container.
+ * See the [OOjs UI docs on MediaWiki][3] for an example.
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#containerExample
+ * @cfg {number} [containerPadding=10] Padding between the popup and its container, specified as a number of pixels.
+ * @cfg {jQuery} [$content] Content to append to the popup's body
+ * @cfg {boolean} [autoClose=false] Automatically close the popup when it loses focus.
+ * @cfg {jQuery} [$autoCloseIgnore] Elements that will not close the popup when clicked.
+ * This config option is only relevant if #autoClose is set to `true`. See the [OOjs UI docs on MediaWiki][2]
+ * for an example.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#autocloseExample
+ * @cfg {boolean} [head] Show a popup header that contains a #label (if specified) and close
+ * button.
+ * @cfg {boolean} [padded] Add padding to the popup's body
+ */
+OO.ui.PopupWidget = function OoUiPopupWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.PopupWidget.super.call( this, config );
+
+ // Properties (must be set before ClippableElement constructor call)
+ this.$body = $( '<div>' );
+
+ // Mixin constructors
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$body } ) );
+
+ // Properties
+ this.$popup = $( '<div>' );
+ this.$head = $( '<div>' );
+ this.$anchor = $( '<div>' );
+ // If undefined, will be computed lazily in updateDimensions()
+ this.$container = config.$container;
+ this.containerPadding = config.containerPadding !== undefined ? config.containerPadding : 10;
+ this.autoClose = !!config.autoClose;
+ this.$autoCloseIgnore = config.$autoCloseIgnore;
+ this.transitionTimeout = null;
+ this.anchor = null;
+ this.width = config.width !== undefined ? config.width : 320;
+ this.height = config.height !== undefined ? config.height : null;
+ this.setAlignment( config.align );
+ this.closeButton = new OO.ui.ButtonWidget( { framed: false, icon: 'close' } );
+ this.onMouseDownHandler = this.onMouseDown.bind( this );
+ this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
+
+ // Events
+ this.closeButton.connect( this, { click: 'onCloseButtonClick' } );
+
+ // Initialization
+ this.toggleAnchor( config.anchor === undefined || config.anchor );
+ this.$body.addClass( 'oo-ui-popupWidget-body' );
+ this.$anchor.addClass( 'oo-ui-popupWidget-anchor' );
+ this.$head
+ .addClass( 'oo-ui-popupWidget-head' )
+ .append( this.$label, this.closeButton.$element );
+ if ( !config.head ) {
+ this.$head.addClass( 'oo-ui-element-hidden' );
+ }
+ this.$popup
+ .addClass( 'oo-ui-popupWidget-popup' )
+ .append( this.$head, this.$body );
+ this.$element
+ .addClass( 'oo-ui-popupWidget' )
+ .append( this.$popup, this.$anchor );
+ // Move content, which was added to #$element by OO.ui.Widget, to the body
+ if ( config.$content instanceof jQuery ) {
+ this.$body.append( config.$content );
+ }
+ if ( config.padded ) {
+ this.$body.addClass( 'oo-ui-popupWidget-body-padded' );
+ }
+
+ // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+ // that reference properties not initialized at that time of parent class construction
+ // TODO: Find a better way to handle post-constructor setup
+ this.visible = false;
+ this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.PopupWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.PopupWidget, OO.ui.ClippableElement );
+
+/* Methods */
+
+/**
+ * Handles mouse down events.
+ *
+ * @private
+ * @param {MouseEvent} e Mouse down event
+ */
+OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) {
+ if (
+ this.isVisible() &&
+ !$.contains( this.$element[ 0 ], e.target ) &&
+ ( !this.$autoCloseIgnore || !this.$autoCloseIgnore.has( e.target ).length )
+ ) {
+ this.toggle( false );
+ }
+};
+
+/**
+ * Bind mouse down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
+ // Capture clicks outside popup
+ this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
+};
+
+/**
+ * Handles close button click events.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.onCloseButtonClick = function () {
+ if ( this.isVisible() ) {
+ this.toggle( false );
+ }
+};
+
+/**
+ * Unbind mouse down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
+ this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
+};
+
+/**
+ * Handles key down events.
+ *
+ * @private
+ * @param {KeyboardEvent} e Key down event
+ */
+OO.ui.PopupWidget.prototype.onDocumentKeyDown = function ( e ) {
+ if (
+ e.which === OO.ui.Keys.ESCAPE &&
+ this.isVisible()
+ ) {
+ this.toggle( false );
+ e.preventDefault();
+ e.stopPropagation();
+ }
+};
+
+/**
+ * Bind key down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
+ this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
+};
+
+/**
+ * Unbind key down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
+ this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
+};
+
+/**
+ * Show, hide, or toggle the visibility of the anchor.
+ *
+ * @param {boolean} [show] Show anchor, omit to toggle
+ */
+OO.ui.PopupWidget.prototype.toggleAnchor = function ( show ) {
+ show = show === undefined ? !this.anchored : !!show;
+
+ if ( this.anchored !== show ) {
+ if ( show ) {
+ this.$element.addClass( 'oo-ui-popupWidget-anchored' );
+ } else {
+ this.$element.removeClass( 'oo-ui-popupWidget-anchored' );
+ }
+ this.anchored = show;
+ }
+};
+
+/**
+ * Check if the anchor is visible.
+ *
+ * @return {boolean} Anchor is visible
+ */
+OO.ui.PopupWidget.prototype.hasAnchor = function () {
+ return this.anchor;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.PopupWidget.prototype.toggle = function ( show ) {
+ show = show === undefined ? !this.isVisible() : !!show;
+
+ var change = show !== this.isVisible();
+
+ // Parent method
+ OO.ui.PopupWidget.super.prototype.toggle.call( this, show );
+
+ if ( change ) {
+ if ( show ) {
+ if ( this.autoClose ) {
+ this.bindMouseDownListener();
+ this.bindKeyDownListener();
+ }
+ this.updateDimensions();
+ this.toggleClipping( true );
+ } else {
+ this.toggleClipping( false );
+ if ( this.autoClose ) {
+ this.unbindMouseDownListener();
+ this.unbindKeyDownListener();
+ }
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Set the size of the popup.
+ *
+ * Changing the size may also change the popup's position depending on the alignment.
+ *
+ * @param {number} width Width in pixels
+ * @param {number} height Height in pixels
+ * @param {boolean} [transition=false] Use a smooth transition
+ * @chainable
+ */
+OO.ui.PopupWidget.prototype.setSize = function ( width, height, transition ) {
+ this.width = width;
+ this.height = height !== undefined ? height : null;
+ if ( this.isVisible() ) {
+ this.updateDimensions( transition );
+ }
+};
+
+/**
+ * Update the size and position.
+ *
+ * Only use this to keep the popup properly anchored. Use #setSize to change the size, and this will
+ * be called automatically.
+ *
+ * @param {boolean} [transition=false] Use a smooth transition
+ * @chainable
+ */
+OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
+ var popupOffset, originOffset, containerLeft, containerWidth, containerRight,
+ popupLeft, popupRight, overlapLeft, overlapRight, anchorWidth,
+ align = this.align,
+ widget = this;
+
+ if ( !this.$container ) {
+ // Lazy-initialize $container if not specified in constructor
+ this.$container = $( this.getClosestScrollableElementContainer() );
+ }
+
+ // Set height and width before measuring things, since it might cause our measurements
+ // to change (e.g. due to scrollbars appearing or disappearing)
+ this.$popup.css( {
+ width: this.width,
+ height: this.height !== null ? this.height : 'auto'
+ } );
+
+ // If we are in RTL, we need to flip the alignment, unless it is center
+ if ( align === 'forwards' || align === 'backwards' ) {
+ if ( this.$container.css( 'direction' ) === 'rtl' ) {
+ align = ( { forwards: 'force-left', backwards: 'force-right' } )[ this.align ];
+ } else {
+ align = ( { forwards: 'force-right', backwards: 'force-left' } )[ this.align ];
+ }
+
+ }
+
+ // Compute initial popupOffset based on alignment
+ popupOffset = this.width * ( { 'force-left': -1, center: -0.5, 'force-right': 0 } )[ align ];
+
+ // Figure out if this will cause the popup to go beyond the edge of the container
+ originOffset = this.$element.offset().left;
+ containerLeft = this.$container.offset().left;
+ containerWidth = this.$container.innerWidth();
+ containerRight = containerLeft + containerWidth;
+ popupLeft = popupOffset - this.containerPadding;
+ popupRight = popupOffset + this.containerPadding + this.width + this.containerPadding;
+ overlapLeft = ( originOffset + popupLeft ) - containerLeft;
+ overlapRight = containerRight - ( originOffset + popupRight );
+
+ // Adjust offset to make the popup not go beyond the edge, if needed
+ if ( overlapRight < 0 ) {
+ popupOffset += overlapRight;
+ } else if ( overlapLeft < 0 ) {
+ popupOffset -= overlapLeft;
+ }
+
+ // Adjust offset to avoid anchor being rendered too close to the edge
+ // $anchor.width() doesn't work with the pure CSS anchor (returns 0)
+ // TODO: Find a measurement that works for CSS anchors and image anchors
+ anchorWidth = this.$anchor[ 0 ].scrollWidth * 2;
+ if ( popupOffset + this.width < anchorWidth ) {
+ popupOffset = anchorWidth - this.width;
+ } else if ( -popupOffset < anchorWidth ) {
+ popupOffset = -anchorWidth;
+ }
+
+ // Prevent transition from being interrupted
+ clearTimeout( this.transitionTimeout );
+ if ( transition ) {
+ // Enable transition
+ this.$element.addClass( 'oo-ui-popupWidget-transitioning' );
+ }
+
+ // Position body relative to anchor
+ this.$popup.css( 'margin-left', popupOffset );
+
+ if ( transition ) {
+ // Prevent transitioning after transition is complete
+ this.transitionTimeout = setTimeout( function () {
+ widget.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
+ }, 200 );
+ } else {
+ // Prevent transitioning immediately
+ this.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
+ }
+
+ // Reevaluate clipping state since we've relocated and resized the popup
+ this.clip();
+
+ return this;
+};
+
+/**
+ * Set popup alignment
+ * @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
+ * `backwards` or `forwards`.
+ */
+OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
+ // Validate alignment and transform deprecated values
+ if ( [ 'left', 'right', 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
+ this.align = { left: 'force-right', right: 'force-left' }[ align ] || align;
+ } else {
+ this.align = 'center';
+ }
+};
+
+/**
+ * Get popup alignment
+ * @return {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
+ * `backwards` or `forwards`.
+ */
+OO.ui.PopupWidget.prototype.getAlignment = function () {
+ return this.align;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ProgressBarWidget.js b/vendor/oojs/oojs-ui/src/widgets/ProgressBarWidget.js
new file mode 100644
index 00000000..a4676282
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ProgressBarWidget.js
@@ -0,0 +1,96 @@
+/**
+ * Progress bars visually display the status of an operation, such as a download,
+ * and can be either determinate or indeterminate:
+ *
+ * - **determinate** process bars show the percent of an operation that is complete.
+ *
+ * - **indeterminate** process bars use a visual display of motion to indicate that an operation
+ * is taking place. Because the extent of an indeterminate operation is unknown, the bar does
+ * not use percentages.
+ *
+ * The value of the `progress` configuration determines whether the bar is determinate or indeterminate.
+ *
+ * @example
+ * // Examples of determinate and indeterminate progress bars.
+ * var progressBar1 = new OO.ui.ProgressBarWidget( {
+ * progress: 33
+ * } );
+ * var progressBar2 = new OO.ui.ProgressBarWidget();
+ *
+ * // Create a FieldsetLayout to layout progress bars
+ * var fieldset = new OO.ui.FieldsetLayout;
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
+ * new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number|boolean} [progress=false] The type of progress bar (determinate or indeterminate).
+ * To create a determinate progress bar, specify a number that reflects the initial percent complete.
+ * By default, the progress bar is indeterminate.
+ */
+OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ProgressBarWidget.super.call( this, config );
+
+ // Properties
+ this.$bar = $( '<div>' );
+ this.progress = null;
+
+ // Initialization
+ this.setProgress( config.progress !== undefined ? config.progress : false );
+ this.$bar.addClass( 'oo-ui-progressBarWidget-bar' );
+ this.$element
+ .attr( {
+ role: 'progressbar',
+ 'aria-valuemin': 0,
+ 'aria-valuemax': 100
+ } )
+ .addClass( 'oo-ui-progressBarWidget' )
+ .append( this.$bar );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ProgressBarWidget, OO.ui.Widget );
+
+/* Static Properties */
+
+OO.ui.ProgressBarWidget.static.tagName = 'div';
+
+/* Methods */
+
+/**
+ * Get the percent of the progress that has been completed. Indeterminate progresses will return `false`.
+ *
+ * @return {number|boolean} Progress percent
+ */
+OO.ui.ProgressBarWidget.prototype.getProgress = function () {
+ return this.progress;
+};
+
+/**
+ * Set the percent of the process completed or `false` for an indeterminate process.
+ *
+ * @param {number|boolean} progress Progress percent or `false` for indeterminate
+ */
+OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
+ this.progress = progress;
+
+ if ( progress !== false ) {
+ this.$bar.css( 'width', this.progress + '%' );
+ this.$element.attr( 'aria-valuenow', this.progress );
+ } else {
+ this.$bar.css( 'width', '' );
+ this.$element.removeAttr( 'aria-valuenow' );
+ }
+ this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', !progress );
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/RadioInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/RadioInputWidget.js
new file mode 100644
index 00000000..47ac20bf
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/RadioInputWidget.js
@@ -0,0 +1,94 @@
+/**
+ * RadioInputWidget creates a single radio button. Because radio buttons are usually used as a set,
+ * in most cases you will want to use a {@link OO.ui.RadioSelectWidget radio select}
+ * with {@link OO.ui.RadioOptionWidget radio options} instead of this class. For more information,
+ * please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ * @example
+ * // An example of selected, unselected, and disabled radio inputs
+ * var radio1 = new OO.ui.RadioInputWidget( {
+ * value: 'a',
+ * selected: true
+ * } );
+ * var radio2 = new OO.ui.RadioInputWidget( {
+ * value: 'b'
+ * } );
+ * var radio3 = new OO.ui.RadioInputWidget( {
+ * value: 'c',
+ * disabled: true
+ * } );
+ * // Create a fieldset layout with fields for each radio button.
+ * var fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'Radio inputs'
+ * } );
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( radio1, { label: 'Selected', align: 'inline' } ),
+ * new OO.ui.FieldLayout( radio2, { label: 'Unselected', align: 'inline' } ),
+ * new OO.ui.FieldLayout( radio3, { label: 'Disabled', align: 'inline' } ),
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [selected=false] Select the radio button initially. By default, the radio button is not selected.
+ */
+OO.ui.RadioInputWidget = function OoUiRadioInputWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.RadioInputWidget.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-radioInputWidget' );
+ this.setSelected( config.selected !== undefined ? config.selected : false );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @private
+ */
+OO.ui.RadioInputWidget.prototype.getInputElement = function () {
+ return $( '<input type="radio" />' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.prototype.onEdit = function () {
+ // RadioInputWidget doesn't track its state.
+};
+
+/**
+ * Set selection state of this radio button.
+ *
+ * @param {boolean} state `true` for selected
+ * @chainable
+ */
+OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
+ // RadioInputWidget doesn't track its state.
+ this.$input.prop( 'checked', state );
+ return this;
+};
+
+/**
+ * Check if this radio button is selected.
+ *
+ * @return {boolean} Radio is selected
+ */
+OO.ui.RadioInputWidget.prototype.isSelected = function () {
+ return this.$input.prop( 'checked' );
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/RadioOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/RadioOptionWidget.js
new file mode 100644
index 00000000..97187c0a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/RadioOptionWidget.js
@@ -0,0 +1,66 @@
+/**
+ * RadioOptionWidget is an option widget that looks like a radio button.
+ * The class is used with OO.ui.RadioSelectWidget to create a selection of radio options.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_option
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioOptionWidget = function OoUiRadioOptionWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties (must be done before parent constructor which calls #setDisabled)
+ this.radio = new OO.ui.RadioInputWidget( { value: config.data, tabIndex: -1 } );
+
+ // Parent constructor
+ OO.ui.RadioOptionWidget.super.call( this, config );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-radioOptionWidget' )
+ .prepend( this.radio.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioOptionWidget, OO.ui.OptionWidget );
+
+/* Static Properties */
+
+OO.ui.RadioOptionWidget.static.highlightable = false;
+
+OO.ui.RadioOptionWidget.static.scrollIntoViewOnSelect = true;
+
+OO.ui.RadioOptionWidget.static.pressable = false;
+
+OO.ui.RadioOptionWidget.static.tagName = 'label';
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioOptionWidget.prototype.setSelected = function ( state ) {
+ OO.ui.RadioOptionWidget.super.prototype.setSelected.call( this, state );
+
+ this.radio.setSelected( state );
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioOptionWidget.prototype.setDisabled = function ( disabled ) {
+ OO.ui.RadioOptionWidget.super.prototype.setDisabled.call( this, disabled );
+
+ this.radio.setDisabled( this.isDisabled() );
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/RadioSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/RadioSelectWidget.js
new file mode 100644
index 00000000..3859205f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/RadioSelectWidget.js
@@ -0,0 +1,58 @@
+/**
+ * RadioSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains radio
+ * options and is used together with OO.ui.RadioOptionWidget. The RadioSelectWidget provides
+ * an interface for adding, removing and selecting options.
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ * @example
+ * // A RadioSelectWidget with RadioOptions.
+ * var option1 = new OO.ui.RadioOptionWidget( {
+ * data: 'a',
+ * label: 'Selected radio option'
+ * } );
+ *
+ * var option2 = new OO.ui.RadioOptionWidget( {
+ * data: 'b',
+ * label: 'Unselected radio option'
+ * } );
+ *
+ * var radioSelect=new OO.ui.RadioSelectWidget( {
+ * items: [ option1, option2 ]
+ * } );
+ *
+ * // Select 'option 1' using the RadioSelectWidget's selectItem() method.
+ * radioSelect.selectItem( option1 );
+ *
+ * $( 'body' ).append( radioSelect.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioSelectWidget = function OoUiRadioSelectWidget( config ) {
+ // Parent constructor
+ OO.ui.RadioSelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, config );
+
+ // Events
+ this.$element.on( {
+ focus: this.bindKeyDownListener.bind( this ),
+ blur: this.unbindKeyDownListener.bind( this )
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-radioSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.TabIndexedElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/SearchWidget.js b/vendor/oojs/oojs-ui/src/widgets/SearchWidget.js
new file mode 100644
index 00000000..4909d9e1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/SearchWidget.js
@@ -0,0 +1,176 @@
+/**
+ * SearchWidgets combine a {@link OO.ui.TextInputWidget text input field}, where users can type a search query,
+ * and a {@link OO.ui.TextInputMenuSelectWidget menu} of search results, which is displayed beneath the query
+ * field. Unlike {@link OO.ui.LookupElement lookup menus}, search result menus are always visible to the user.
+ * Users can choose an item from the menu or type a query into the text field to search for a matching result item.
+ * In general, search widgets are used inside a separate {@link OO.ui.Dialog dialog} window.
+ *
+ * Each time the query is changed, the search result menu is cleared and repopulated. Please see
+ * the [OOjs UI demos][1] for an example.
+ *
+ * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/#dialogs-mediawiki-vector-ltr
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string|jQuery} [placeholder] Placeholder text for query input
+ * @cfg {string} [value] Initial query value
+ */
+OO.ui.SearchWidget = function OoUiSearchWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.SearchWidget.super.call( this, config );
+
+ // Properties
+ this.query = new OO.ui.TextInputWidget( {
+ icon: 'search',
+ placeholder: config.placeholder,
+ value: config.value
+ } );
+ this.results = new OO.ui.SelectWidget();
+ this.$query = $( '<div>' );
+ this.$results = $( '<div>' );
+
+ // Events
+ this.query.connect( this, {
+ change: 'onQueryChange',
+ enter: 'onQueryEnter'
+ } );
+ this.results.connect( this, {
+ highlight: 'onResultsHighlight',
+ select: 'onResultsSelect'
+ } );
+ this.query.$input.on( 'keydown', this.onQueryKeydown.bind( this ) );
+
+ // Initialization
+ this.$query
+ .addClass( 'oo-ui-searchWidget-query' )
+ .append( this.query.$element );
+ this.$results
+ .addClass( 'oo-ui-searchWidget-results' )
+ .append( this.results.$element );
+ this.$element
+ .addClass( 'oo-ui-searchWidget' )
+ .append( this.$results, this.$query );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SearchWidget, OO.ui.Widget );
+
+/* Events */
+
+/**
+ * A 'highlight' event is emitted when an item is highlighted. The highlight indicates which
+ * item will be selected. When a user mouses over a menu item, it is highlighted. If a search
+ * string is typed into the query field instead, the first menu item that matches the query
+ * will be highlighted.
+
+ * @event highlight
+ * @deprecated Connect straight to getResults() events instead
+ * @param {Object|null} item Item data or null if no item is highlighted
+ */
+
+/**
+ * A 'select' event is emitted when an item is selected. A menu item is selected when it is clicked,
+ * or when a user types a search query, a menu result is highlighted, and the user presses enter.
+ *
+ * @event select
+ * @deprecated Connect straight to getResults() events instead
+ * @param {Object|null} item Item data or null if no item is selected
+ */
+
+/* Methods */
+
+/**
+ * Handle query key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.SearchWidget.prototype.onQueryKeydown = function ( e ) {
+ var highlightedItem, nextItem,
+ dir = e.which === OO.ui.Keys.DOWN ? 1 : ( e.which === OO.ui.Keys.UP ? -1 : 0 );
+
+ if ( dir ) {
+ highlightedItem = this.results.getHighlightedItem();
+ if ( !highlightedItem ) {
+ highlightedItem = this.results.getSelectedItem();
+ }
+ nextItem = this.results.getRelativeSelectableItem( highlightedItem, dir );
+ this.results.highlightItem( nextItem );
+ nextItem.scrollElementIntoView();
+ }
+};
+
+/**
+ * Handle select widget select events.
+ *
+ * Clears existing results. Subclasses should repopulate items according to new query.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.SearchWidget.prototype.onQueryChange = function () {
+ // Reset
+ this.results.clearItems();
+};
+
+/**
+ * Handle select widget enter key events.
+ *
+ * Selects highlighted item.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.SearchWidget.prototype.onQueryEnter = function () {
+ // Reset
+ this.results.selectItem( this.results.getHighlightedItem() );
+};
+
+/**
+ * Handle select widget highlight events.
+ *
+ * @private
+ * @deprecated Connect straight to getResults() events instead
+ * @param {OO.ui.OptionWidget} item Highlighted item
+ * @fires highlight
+ */
+OO.ui.SearchWidget.prototype.onResultsHighlight = function ( item ) {
+ this.emit( 'highlight', item ? item.getData() : null );
+};
+
+/**
+ * Handle select widget select events.
+ *
+ * @private
+ * @deprecated Connect straight to getResults() events instead
+ * @param {OO.ui.OptionWidget} item Selected item
+ * @fires select
+ */
+OO.ui.SearchWidget.prototype.onResultsSelect = function ( item ) {
+ this.emit( 'select', item ? item.getData() : null );
+};
+
+/**
+ * Get the query input.
+ *
+ * @return {OO.ui.TextInputWidget} Query input
+ */
+OO.ui.SearchWidget.prototype.getQuery = function () {
+ return this.query;
+};
+
+/**
+ * Get the search results menu.
+ *
+ * @return {OO.ui.SelectWidget} Menu of search results
+ */
+OO.ui.SearchWidget.prototype.getResults = function () {
+ return this.results;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/SelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/SelectWidget.js
new file mode 100644
index 00000000..91172af0
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/SelectWidget.js
@@ -0,0 +1,630 @@
+/**
+ * A SelectWidget is of a generic selection of options. The OOjs UI library contains several types of
+ * select widgets, including {@link OO.ui.ButtonSelectWidget button selects},
+ * {@link OO.ui.RadioSelectWidget radio selects}, and {@link OO.ui.MenuSelectWidget
+ * menu selects}.
+ *
+ * This class should be used together with OO.ui.OptionWidget or OO.ui.DecoratedOptionWidget. For more
+ * information, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * @example
+ * // Example of a select widget with three options
+ * var select = new OO.ui.SelectWidget( {
+ * items: [
+ * new OO.ui.OptionWidget( {
+ * data: 'a',
+ * label: 'Option One',
+ * } ),
+ * new OO.ui.OptionWidget( {
+ * data: 'b',
+ * label: 'Option Two',
+ * } ),
+ * new OO.ui.OptionWidget( {
+ * data: 'c',
+ * label: 'Option Three',
+ * } )
+ * ]
+ * } );
+ * $( 'body' ).append( select.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.OptionWidget[]} [items] An array of options to add to the select.
+ * Options are created with {@link OO.ui.OptionWidget OptionWidget} classes. See
+ * the [OOjs UI documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ */
+OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.SelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.GroupWidget.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+ // Properties
+ this.pressed = false;
+ this.selecting = null;
+ this.onMouseUpHandler = this.onMouseUp.bind( this );
+ this.onMouseMoveHandler = this.onMouseMove.bind( this );
+ this.onKeyDownHandler = this.onKeyDown.bind( this );
+
+ // Events
+ this.$element.on( {
+ mousedown: this.onMouseDown.bind( this ),
+ mouseover: this.onMouseOver.bind( this ),
+ mouseleave: this.onMouseLeave.bind( this )
+ } );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-selectWidget oo-ui-selectWidget-depressed' )
+ .attr( 'role', 'listbox' );
+ if ( Array.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SelectWidget, OO.ui.Widget );
+
+// Need to mixin base class as well
+OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupElement );
+OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupWidget );
+
+/* Events */
+
+/**
+ * @event highlight
+ *
+ * A `highlight` event is emitted when the highlight is changed with the #highlightItem method.
+ *
+ * @param {OO.ui.OptionWidget|null} item Highlighted item
+ */
+
+/**
+ * @event press
+ *
+ * A `press` event is emitted when the #pressItem method is used to programmatically modify the
+ * pressed state of an option.
+ *
+ * @param {OO.ui.OptionWidget|null} item Pressed item
+ */
+
+/**
+ * @event select
+ *
+ * A `select` event is emitted when the selection is modified programmatically with the #selectItem method.
+ *
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+
+/**
+ * @event choose
+ * A `choose` event is emitted when an item is chosen with the #chooseItem method.
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+
+/**
+ * @event add
+ *
+ * An `add` event is emitted when options are added to the select with the #addItems method.
+ *
+ * @param {OO.ui.OptionWidget[]} items Added items
+ * @param {number} index Index of insertion point
+ */
+
+/**
+ * @event remove
+ *
+ * A `remove` event is emitted when options are removed from the select with the #clearItems
+ * or #removeItems methods.
+ *
+ * @param {OO.ui.OptionWidget[]} items Removed items
+ */
+
+/* Methods */
+
+/**
+ * Handle mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
+ var item;
+
+ if ( !this.isDisabled() && e.which === 1 ) {
+ this.togglePressed( true );
+ item = this.getTargetItem( e );
+ if ( item && item.isSelectable() ) {
+ this.pressItem( item );
+ this.selecting = item;
+ this.getElementDocument().addEventListener(
+ 'mouseup',
+ this.onMouseUpHandler,
+ true
+ );
+ this.getElementDocument().addEventListener(
+ 'mousemove',
+ this.onMouseMoveHandler,
+ true
+ );
+ }
+ }
+ return false;
+};
+
+/**
+ * Handle mouse up events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse up event
+ */
+OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
+ var item;
+
+ this.togglePressed( false );
+ if ( !this.selecting ) {
+ item = this.getTargetItem( e );
+ if ( item && item.isSelectable() ) {
+ this.selecting = item;
+ }
+ }
+ if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
+ this.pressItem( null );
+ this.chooseItem( this.selecting );
+ this.selecting = null;
+ }
+
+ this.getElementDocument().removeEventListener(
+ 'mouseup',
+ this.onMouseUpHandler,
+ true
+ );
+ this.getElementDocument().removeEventListener(
+ 'mousemove',
+ this.onMouseMoveHandler,
+ true
+ );
+
+ return false;
+};
+
+/**
+ * Handle mouse move events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse move event
+ */
+OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
+ var item;
+
+ if ( !this.isDisabled() && this.pressed ) {
+ item = this.getTargetItem( e );
+ if ( item && item !== this.selecting && item.isSelectable() ) {
+ this.pressItem( item );
+ this.selecting = item;
+ }
+ }
+ return false;
+};
+
+/**
+ * Handle mouse over events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse over event
+ */
+OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
+ var item;
+
+ if ( !this.isDisabled() ) {
+ item = this.getTargetItem( e );
+ this.highlightItem( item && item.isHighlightable() ? item : null );
+ }
+ return false;
+};
+
+/**
+ * Handle mouse leave events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse over event
+ */
+OO.ui.SelectWidget.prototype.onMouseLeave = function () {
+ if ( !this.isDisabled() ) {
+ this.highlightItem( null );
+ }
+ return false;
+};
+
+/**
+ * Handle key down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
+ var nextItem,
+ handled = false,
+ currentItem = this.getHighlightedItem() || this.getSelectedItem();
+
+ if ( !this.isDisabled() && this.isVisible() ) {
+ switch ( e.keyCode ) {
+ case OO.ui.Keys.ENTER:
+ if ( currentItem && currentItem.constructor.static.highlightable ) {
+ // Was only highlighted, now let's select it. No-op if already selected.
+ this.chooseItem( currentItem );
+ handled = true;
+ }
+ break;
+ case OO.ui.Keys.UP:
+ case OO.ui.Keys.LEFT:
+ nextItem = this.getRelativeSelectableItem( currentItem, -1 );
+ handled = true;
+ break;
+ case OO.ui.Keys.DOWN:
+ case OO.ui.Keys.RIGHT:
+ nextItem = this.getRelativeSelectableItem( currentItem, 1 );
+ handled = true;
+ break;
+ case OO.ui.Keys.ESCAPE:
+ case OO.ui.Keys.TAB:
+ if ( currentItem && currentItem.constructor.static.highlightable ) {
+ currentItem.setHighlighted( false );
+ }
+ this.unbindKeyDownListener();
+ // Don't prevent tabbing away / defocusing
+ handled = false;
+ break;
+ }
+
+ if ( nextItem ) {
+ if ( nextItem.constructor.static.highlightable ) {
+ this.highlightItem( nextItem );
+ } else {
+ this.chooseItem( nextItem );
+ }
+ nextItem.scrollElementIntoView();
+ }
+
+ if ( handled ) {
+ // Can't just return false, because e is not always a jQuery event
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+};
+
+/**
+ * Bind key down listener.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
+ this.getElementWindow().addEventListener( 'keydown', this.onKeyDownHandler, true );
+};
+
+/**
+ * Unbind key down listener.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
+ this.getElementWindow().removeEventListener( 'keydown', this.onKeyDownHandler, true );
+};
+
+/**
+ * Get the closest item to a jQuery.Event.
+ *
+ * @private
+ * @param {jQuery.Event} e
+ * @return {OO.ui.OptionWidget|null} Outline item widget, `null` if none was found
+ */
+OO.ui.SelectWidget.prototype.getTargetItem = function ( e ) {
+ return $( e.target ).closest( '.oo-ui-optionWidget' ).data( 'oo-ui-optionWidget' ) || null;
+};
+
+/**
+ * Get selected item.
+ *
+ * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
+ */
+OO.ui.SelectWidget.prototype.getSelectedItem = function () {
+ var i, len;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ if ( this.items[ i ].isSelected() ) {
+ return this.items[ i ];
+ }
+ }
+ return null;
+};
+
+/**
+ * Get highlighted item.
+ *
+ * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
+ */
+OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
+ var i, len;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ if ( this.items[ i ].isHighlighted() ) {
+ return this.items[ i ];
+ }
+ }
+ return null;
+};
+
+/**
+ * Toggle pressed state.
+ *
+ * Press is a state that occurs when a user mouses down on an item, but
+ * has not yet let go of the mouse. The item may appear selected, but it will not be selected
+ * until the user releases the mouse.
+ *
+ * @param {boolean} pressed An option is being pressed
+ */
+OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
+ if ( pressed === undefined ) {
+ pressed = !this.pressed;
+ }
+ if ( pressed !== this.pressed ) {
+ this.$element
+ .toggleClass( 'oo-ui-selectWidget-pressed', pressed )
+ .toggleClass( 'oo-ui-selectWidget-depressed', !pressed );
+ this.pressed = pressed;
+ }
+};
+
+/**
+ * Highlight an option. If the `item` param is omitted, no options will be highlighted
+ * and any existing highlight will be removed. The highlight is mutually exclusive.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to highlight, omit for no highlight
+ * @fires highlight
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
+ var i, len, highlighted,
+ changed = false;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ highlighted = this.items[ i ] === item;
+ if ( this.items[ i ].isHighlighted() !== highlighted ) {
+ this.items[ i ].setHighlighted( highlighted );
+ changed = true;
+ }
+ }
+ if ( changed ) {
+ this.emit( 'highlight', item );
+ }
+
+ return this;
+};
+
+/**
+ * Programmatically select an option by its data. If the `data` parameter is omitted,
+ * or if the item does not exist, all options will be deselected.
+ *
+ * @param {Object|string} [data] Value of the item to select, omit to deselect all
+ * @fires select
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
+ var itemFromData = this.getItemFromData( data );
+ if ( data === undefined || !itemFromData ) {
+ return this.selectItem();
+ }
+ return this.selectItem( itemFromData );
+};
+
+/**
+ * Programmatically select an option by its reference. If the `item` parameter is omitted,
+ * all options will be deselected.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
+ * @fires select
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
+ var i, len, selected,
+ changed = false;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ selected = this.items[ i ] === item;
+ if ( this.items[ i ].isSelected() !== selected ) {
+ this.items[ i ].setSelected( selected );
+ changed = true;
+ }
+ }
+ if ( changed ) {
+ this.emit( 'select', item );
+ }
+
+ return this;
+};
+
+/**
+ * Press an item.
+ *
+ * Press is a state that occurs when a user mouses down on an item, but has not
+ * yet let go of the mouse. The item may appear selected, but it will not be selected until the user
+ * releases the mouse.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
+ * @fires press
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
+ var i, len, pressed,
+ changed = false;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ pressed = this.items[ i ] === item;
+ if ( this.items[ i ].isPressed() !== pressed ) {
+ this.items[ i ].setPressed( pressed );
+ changed = true;
+ }
+ }
+ if ( changed ) {
+ this.emit( 'press', item );
+ }
+
+ return this;
+};
+
+/**
+ * Choose an item.
+ *
+ * Note that ‘choose’ should never be modified programmatically. A user can choose
+ * an option with the keyboard or mouse and it becomes selected. To select an item programmatically,
+ * use the #selectItem method.
+ *
+ * This method is identical to #selectItem, but may vary in subclasses that take additional action
+ * when users choose an item with the keyboard or mouse.
+ *
+ * @param {OO.ui.OptionWidget} item Item to choose
+ * @fires choose
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
+ this.selectItem( item );
+ this.emit( 'choose', item );
+
+ return this;
+};
+
+/**
+ * Get an option by its position relative to the specified item (or to the start of the option array,
+ * if item is `null`). The direction in which to search through the option array is specified with a
+ * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
+ * `null` if there are no options in the array.
+ *
+ * @param {OO.ui.OptionWidget|null} item Item to describe the start position, or `null` to start at the beginning of the array.
+ * @param {number} direction Direction to move in: -1 to move backward, 1 to move forward
+ * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
+ */
+OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction ) {
+ var currentIndex, nextIndex, i,
+ increase = direction > 0 ? 1 : -1,
+ len = this.items.length;
+
+ if ( item instanceof OO.ui.OptionWidget ) {
+ currentIndex = $.inArray( item, this.items );
+ nextIndex = ( currentIndex + increase + len ) % len;
+ } else {
+ // If no item is selected and moving forward, start at the beginning.
+ // If moving backward, start at the end.
+ nextIndex = direction > 0 ? 0 : len - 1;
+ }
+
+ for ( i = 0; i < len; i++ ) {
+ item = this.items[ nextIndex ];
+ if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
+ return item;
+ }
+ nextIndex = ( nextIndex + increase + len ) % len;
+ }
+ return null;
+};
+
+/**
+ * Get the next selectable item or `null` if there are no selectable items.
+ * Disabled options and menu-section markers and breaks are not selectable.
+ *
+ * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
+ */
+OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
+ var i, len, item;
+
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[ i ];
+ if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
+ return item;
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Add an array of options to the select. Optionally, an index number can be used to
+ * specify an insertion point.
+ *
+ * @param {OO.ui.OptionWidget[]} items Items to add
+ * @param {number} [index] Index to insert items after
+ * @fires add
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
+ // Mixin method
+ OO.ui.GroupWidget.prototype.addItems.call( this, items, index );
+
+ // Always provide an index, even if it was omitted
+ this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );
+
+ return this;
+};
+
+/**
+ * Remove the specified array of options from the select. Options will be detached
+ * from the DOM, not removed, so they can be reused later. To remove all options from
+ * the select, you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.OptionWidget[]} items Items to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
+ var i, len, item;
+
+ // Deselect items being removed
+ for ( i = 0, len = items.length; i < len; i++ ) {
+ item = items[ i ];
+ if ( item.isSelected() ) {
+ this.selectItem( null );
+ }
+ }
+
+ // Mixin method
+ OO.ui.GroupWidget.prototype.removeItems.call( this, items );
+
+ this.emit( 'remove', items );
+
+ return this;
+};
+
+/**
+ * Clear all options from the select. Options will be detached from the DOM, not removed,
+ * so that they can be reused later. To remove a subset of options from the select, use
+ * the #removeItems method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.clearItems = function () {
+ var items = this.items.slice();
+
+ // Mixin method
+ OO.ui.GroupWidget.prototype.clearItems.call( this );
+
+ // Clear selection
+ this.selectItem( null );
+
+ this.emit( 'remove', items );
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/TabOptionWidget.js b/vendor/oojs/oojs-ui/src/widgets/TabOptionWidget.js
new file mode 100644
index 00000000..08f5b30d
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/TabOptionWidget.js
@@ -0,0 +1,31 @@
+/**
+ * TabOptionWidget is an item in a {@link OO.ui.TabSelectWidget TabSelectWidget}.
+ *
+ * Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}, which contain
+ * {@link OO.ui.CardLayout card layouts}. See {@link OO.ui.IndexLayout IndexLayout}
+ * for an example.
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.TabOptionWidget = function OoUiTabOptionWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.TabOptionWidget.super.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-tabOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TabOptionWidget, OO.ui.OptionWidget );
+
+/* Static Properties */
+
+OO.ui.TabOptionWidget.static.highlightable = false;
diff --git a/vendor/oojs/oojs-ui/src/widgets/TabSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/TabSelectWidget.js
new file mode 100644
index 00000000..ced3218b
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/TabSelectWidget.js
@@ -0,0 +1,33 @@
+/**
+ * TabSelectWidget is a list that contains {@link OO.ui.TabOptionWidget tab options}
+ *
+ * ####Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}.####
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.TabSelectWidget = function OoUiTabSelectWidget( config ) {
+ // Parent constructor
+ OO.ui.TabSelectWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, config );
+
+ // Events
+ this.$element.on( {
+ focus: this.bindKeyDownListener.bind( this ),
+ blur: this.unbindKeyDownListener.bind( this )
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-tabSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TabSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.TabSelectWidget, OO.ui.TabIndexedElement );
diff --git a/vendor/oojs/oojs-ui/src/widgets/TextInputMenuSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/TextInputMenuSelectWidget.js
new file mode 100644
index 00000000..6b971e81
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/TextInputMenuSelectWidget.js
@@ -0,0 +1,103 @@
+/**
+ * TextInputMenuSelectWidget is a menu that is specially designed to be positioned beneath
+ * a {@link OO.ui.TextInputWidget text input} field. The menu's position is automatically
+ * calculated and maintained when the menu is toggled or the window is resized.
+ * See OO.ui.ComboBoxWidget for an example of a widget that uses this class.
+ *
+ * @class
+ * @extends OO.ui.MenuSelectWidget
+ *
+ * @constructor
+ * @param {OO.ui.TextInputWidget} inputWidget Text input widget to provide menu for
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$container=input.$element] Element to render menu under
+ */
+OO.ui.TextInputMenuSelectWidget = function OoUiTextInputMenuSelectWidget( inputWidget, config ) {
+ // Allow passing positional parameters inside the config object
+ if ( OO.isPlainObject( inputWidget ) && config === undefined ) {
+ config = inputWidget;
+ inputWidget = config.inputWidget;
+ }
+
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.TextInputMenuSelectWidget.super.call( this, config );
+
+ // Properties
+ this.inputWidget = inputWidget;
+ this.$container = config.$container || this.inputWidget.$element;
+ this.onWindowResizeHandler = this.onWindowResize.bind( this );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-textInputMenuSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TextInputMenuSelectWidget, OO.ui.MenuSelectWidget );
+
+/* Methods */
+
+/**
+ * Handle window resize event.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.TextInputMenuSelectWidget.prototype.onWindowResize = function () {
+ this.position();
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.TextInputMenuSelectWidget.prototype.toggle = function ( visible ) {
+ visible = visible === undefined ? !this.isVisible() : !!visible;
+
+ var change = visible !== this.isVisible();
+
+ if ( change && visible ) {
+ // Make sure the width is set before the parent method runs.
+ // After this we have to call this.position(); again to actually
+ // position ourselves correctly.
+ this.position();
+ }
+
+ // Parent method
+ OO.ui.TextInputMenuSelectWidget.super.prototype.toggle.call( this, visible );
+
+ if ( change ) {
+ if ( this.isVisible() ) {
+ this.position();
+ $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
+ } else {
+ $( this.getElementWindow() ).off( 'resize', this.onWindowResizeHandler );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Position the menu.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.TextInputMenuSelectWidget.prototype.position = function () {
+ var $container = this.$container,
+ pos = OO.ui.Element.static.getRelativePosition( $container, this.$element.offsetParent() );
+
+ // Position under input
+ pos.top += $container.height();
+ this.$element.css( pos );
+
+ // Set width
+ this.setIdealSize( $container.width() );
+ // We updated the position, so re-evaluate the clipping state
+ this.clip();
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js
new file mode 100644
index 00000000..1ba26641
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js
@@ -0,0 +1,535 @@
+/**
+ * TextInputWidgets, like HTML text inputs, can be configured with options that customize the
+ * size of the field as well as its presentation. In addition, these widgets can be configured
+ * with {@link OO.ui.IconElement icons}, {@link OO.ui.IndicatorElement indicators}, an optional
+ * validation-pattern (used to determine if an input value is valid or not) and an input filter,
+ * which modifies incoming values rather than validating them.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ * @example
+ * // Example of a text input widget
+ * var textInput = new OO.ui.TextInputWidget( {
+ * value: 'Text input'
+ * } )
+ * $( 'body' ).append( textInput.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.PendingElement
+ * @mixins OO.ui.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [type='text'] The value of the HTML `type` attribute
+ * @cfg {string} [placeholder] Placeholder text
+ * @cfg {boolean} [autofocus=false] Use an HTML `autofocus` attribute to
+ * instruct the browser to focus this widget.
+ * @cfg {boolean} [readOnly=false] Prevent changes to the value of the text input.
+ * @cfg {number} [maxLength] Maximum number of characters allowed in the input.
+ * @cfg {boolean} [multiline=false] Allow multiple lines of text
+ * @cfg {boolean} [autosize=false] Automatically resize the text input to fit its content.
+ * Use the #maxRows config to specify a maximum number of displayed rows.
+ * @cfg {boolean} [maxRows=10] Maximum number of rows to display when #autosize is set to true.
+ * @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
+ * the value or placeholder text: `'before'` or `'after'`
+ * @cfg {boolean} [required=false] Mark the field as required
+ * @cfg {RegExp|Function|string} [validate] Validation pattern: when string, a symbolic name of a
+ * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer'
+ * (the value must contain only numbers); when RegExp, a regular expression that must match the
+ * value for it to be considered valid; when Function, a function receiving the value as parameter
+ * that must return true, or promise resolving to true, for it to be considered valid.
+ */
+OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
+ // Configuration initialization
+ config = $.extend( {
+ type: 'text',
+ labelPosition: 'after',
+ maxRows: 10
+ }, config );
+
+ // Parent constructor
+ OO.ui.TextInputWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.PendingElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+
+ // Properties
+ this.readOnly = false;
+ this.multiline = !!config.multiline;
+ this.autosize = !!config.autosize;
+ this.maxRows = config.maxRows;
+ this.validate = null;
+
+ // Clone for resizing
+ if ( this.autosize ) {
+ this.$clone = this.$input
+ .clone()
+ .insertAfter( this.$input )
+ .attr( 'aria-hidden', 'true' )
+ .addClass( 'oo-ui-element-hidden' );
+ }
+
+ this.setValidation( config.validate );
+ this.setLabelPosition( config.labelPosition );
+
+ // Events
+ this.$input.on( {
+ keypress: this.onKeyPress.bind( this ),
+ blur: this.onBlur.bind( this )
+ } );
+ this.$input.one( {
+ focus: this.onElementAttach.bind( this )
+ } );
+ this.$icon.on( 'mousedown', this.onIconMouseDown.bind( this ) );
+ this.$indicator.on( 'mousedown', this.onIndicatorMouseDown.bind( this ) );
+ this.on( 'labelChange', this.updatePosition.bind( this ) );
+ this.connect( this, { change: 'onChange' } );
+
+ // Initialization
+ this.$element
+ .addClass( 'oo-ui-textInputWidget' )
+ .append( this.$icon, this.$indicator );
+ this.setReadOnly( !!config.readOnly );
+ if ( config.placeholder ) {
+ this.$input.attr( 'placeholder', config.placeholder );
+ }
+ if ( config.maxLength !== undefined ) {
+ this.$input.attr( 'maxlength', config.maxLength );
+ }
+ if ( config.autofocus ) {
+ this.$input.attr( 'autofocus', 'autofocus' );
+ }
+ if ( config.required ) {
+ this.$input.attr( 'required', 'required' );
+ this.$input.attr( 'aria-required', 'true' );
+ }
+ if ( this.label || config.autosize ) {
+ this.installParentChangeDetector();
+ }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.PendingElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.LabelElement );
+
+/* Static properties */
+
+OO.ui.TextInputWidget.static.validationPatterns = {
+ 'non-empty': /.+/,
+ integer: /^\d+$/
+};
+
+/* Events */
+
+/**
+ * An `enter` event is emitted when the user presses 'enter' inside the text box.
+ *
+ * Not emitted if the input is multiline.
+ *
+ * @event enter
+ */
+
+/* Methods */
+
+/**
+ * Handle icon mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ * @fires icon
+ */
+OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
+ if ( e.which === 1 ) {
+ this.$input[ 0 ].focus();
+ return false;
+ }
+};
+
+/**
+ * Handle indicator mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ * @fires indicator
+ */
+OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+ if ( e.which === 1 ) {
+ this.$input[ 0 ].focus();
+ return false;
+ }
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ * @fires enter If enter key is pressed and input is not multiline
+ */
+OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
+ if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
+ this.emit( 'enter', e );
+ }
+};
+
+/**
+ * Handle blur events.
+ *
+ * @private
+ * @param {jQuery.Event} e Blur event
+ */
+OO.ui.TextInputWidget.prototype.onBlur = function () {
+ this.setValidityFlag();
+};
+
+/**
+ * Handle element attach events.
+ *
+ * @private
+ * @param {jQuery.Event} e Element attach event
+ */
+OO.ui.TextInputWidget.prototype.onElementAttach = function () {
+ // Any previously calculated size is now probably invalid if we reattached elsewhere
+ this.valCache = null;
+ this.adjustSize();
+ this.positionLabel();
+};
+
+/**
+ * Handle change events.
+ *
+ * @param {string} value
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.onChange = function () {
+ this.setValidityFlag();
+ this.adjustSize();
+};
+
+/**
+ * Check if the input is {@link #readOnly read-only}.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isReadOnly = function () {
+ return this.readOnly;
+};
+
+/**
+ * Set the {@link #readOnly read-only} state of the input.
+ *
+ * @param {boolean} state Make input read-only
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
+ this.readOnly = !!state;
+ this.$input.prop( 'readOnly', this.readOnly );
+ return this;
+};
+
+/**
+ * Support function for making #onElementAttach work across browsers.
+ *
+ * This whole function could be replaced with one line of code using the DOMNodeInsertedIntoDocument
+ * event, but it's not supported by Firefox and allegedly deprecated, so we only use it as fallback.
+ *
+ * Due to MutationObserver performance woes, #onElementAttach is only somewhat reliably called the
+ * first time that the element gets attached to the documented.
+ */
+OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
+ var mutationObserver, onRemove, topmostNode, fakeParentNode,
+ MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
+ widget = this;
+
+ if ( MutationObserver ) {
+ // The new way. If only it wasn't so ugly.
+
+ if ( this.$element.closest( 'html' ).length ) {
+ // Widget is attached already, do nothing. This breaks the functionality of this function when
+ // the widget is detached and reattached. Alas, doing this correctly with MutationObserver
+ // would require observation of the whole document, which would hurt performance of other,
+ // more important code.
+ return;
+ }
+
+ // Find topmost node in the tree
+ topmostNode = this.$element[0];
+ while ( topmostNode.parentNode ) {
+ topmostNode = topmostNode.parentNode;
+ }
+
+ // We have no way to detect the $element being attached somewhere without observing the entire
+ // DOM with subtree modifications, which would hurt performance. So we cheat: we hook to the
+ // parent node of $element, and instead detect when $element is removed from it (and thus
+ // probably attached somewhere else). If there is no parent, we create a "fake" one. If it
+ // doesn't get attached, we end up back here and create the parent.
+
+ mutationObserver = new MutationObserver( function ( mutations ) {
+ var i, j, removedNodes;
+ for ( i = 0; i < mutations.length; i++ ) {
+ removedNodes = mutations[ i ].removedNodes;
+ for ( j = 0; j < removedNodes.length; j++ ) {
+ if ( removedNodes[ j ] === topmostNode ) {
+ setTimeout( onRemove, 0 );
+ return;
+ }
+ }
+ }
+ } );
+
+ onRemove = function () {
+ // If the node was attached somewhere else, report it
+ if ( widget.$element.closest( 'html' ).length ) {
+ widget.onElementAttach();
+ }
+ mutationObserver.disconnect();
+ widget.installParentChangeDetector();
+ };
+
+ // Create a fake parent and observe it
+ fakeParentNode = $( '<div>' ).append( this.$element )[0];
+ mutationObserver.observe( fakeParentNode, { childList: true } );
+ } else {
+ // Using the DOMNodeInsertedIntoDocument event is much nicer and less magical, and works for
+ // detachment and reattachment, but it's not supported by Firefox and allegedly deprecated.
+ this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
+ }
+};
+
+/**
+ * Automatically adjust the size of the text input.
+ *
+ * This only affects #multiline inputs that are {@link #autosize autosized}.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.adjustSize = function () {
+ var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
+
+ if ( this.multiline && this.autosize && this.$input.val() !== this.valCache ) {
+ this.$clone
+ .val( this.$input.val() )
+ .attr( 'rows', '' )
+ // Set inline height property to 0 to measure scroll height
+ .css( 'height', 0 );
+
+ this.$clone.removeClass( 'oo-ui-element-hidden' );
+
+ this.valCache = this.$input.val();
+
+ scrollHeight = this.$clone[ 0 ].scrollHeight;
+
+ // Remove inline height property to measure natural heights
+ this.$clone.css( 'height', '' );
+ innerHeight = this.$clone.innerHeight();
+ outerHeight = this.$clone.outerHeight();
+
+ // Measure max rows height
+ this.$clone
+ .attr( 'rows', this.maxRows )
+ .css( 'height', 'auto' )
+ .val( '' );
+ maxInnerHeight = this.$clone.innerHeight();
+
+ // Difference between reported innerHeight and scrollHeight with no scrollbars present
+ // Equals 1 on Blink-based browsers and 0 everywhere else
+ measurementError = maxInnerHeight - this.$clone[ 0 ].scrollHeight;
+ idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
+
+ this.$clone.addClass( 'oo-ui-element-hidden' );
+
+ // Only apply inline height when expansion beyond natural height is needed
+ if ( idealHeight > innerHeight ) {
+ // Use the difference between the inner and outer height as a buffer
+ this.$input.css( 'height', idealHeight + ( outerHeight - innerHeight ) );
+ } else {
+ this.$input.css( 'height', '' );
+ }
+ }
+ return this;
+};
+
+/**
+ * @inheritdoc
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
+ return config.multiline ? $( '<textarea>' ) : $( '<input type="' + config.type + '" />' );
+};
+
+/**
+ * Check if the input supports multiple lines.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isMultiline = function () {
+ return !!this.multiline;
+};
+
+/**
+ * Check if the input automatically adjusts its size.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isAutosizing = function () {
+ return !!this.autosize;
+};
+
+/**
+ * Select the entire text of the input.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.select = function () {
+ this.$input.select();
+ return this;
+};
+
+/**
+ * Set the validation pattern.
+ *
+ * The validation pattern is either a regular expression, a function, or the symbolic name of a
+ * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer' (the
+ * value must contain only numbers).
+ *
+ * @param {RegExp|Function|string|null} validate Regular expression, function, or the symbolic name
+ * of a pattern (either ‘integer’ or ‘non-empty’) defined by the class.
+ */
+OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
+ if ( validate instanceof RegExp || validate instanceof Function ) {
+ this.validate = validate;
+ } else {
+ this.validate = this.constructor.static.validationPatterns[ validate ] || /.*/;
+ }
+};
+
+/**
+ * Sets the 'invalid' flag appropriately.
+ *
+ * @param {boolean} [isValid] Optionally override validation result
+ */
+OO.ui.TextInputWidget.prototype.setValidityFlag = function ( isValid ) {
+ var widget = this,
+ setFlag = function ( valid ) {
+ if ( !valid ) {
+ widget.$input.attr( 'aria-invalid', 'true' );
+ } else {
+ widget.$input.removeAttr( 'aria-invalid' );
+ }
+ widget.setFlags( { invalid: !valid } );
+ };
+
+ if ( isValid !== undefined ) {
+ setFlag( isValid );
+ } else {
+ this.isValid().done( setFlag );
+ }
+};
+
+/**
+ * Check if a value is valid.
+ *
+ * This method returns a promise that resolves with a boolean `true` if the current value is
+ * considered valid according to the supplied {@link #validate validation pattern}.
+ *
+ * @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
+ */
+OO.ui.TextInputWidget.prototype.isValid = function () {
+ if ( this.validate instanceof Function ) {
+ var result = this.validate( this.getValue() );
+ if ( $.isFunction( result.promise ) ) {
+ return result.promise();
+ } else {
+ return $.Deferred().resolve( !!result ).promise();
+ }
+ } else {
+ return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
+ }
+};
+
+/**
+ * Set the position of the inline label relative to that of the value: `‘before’` or `‘after’`.
+ *
+ * @param {string} labelPosition Label position, 'before' or 'after'
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
+ this.labelPosition = labelPosition;
+ this.updatePosition();
+ return this;
+};
+
+/**
+ * Deprecated alias of #setLabelPosition
+ *
+ * @deprecated Use setLabelPosition instead.
+ */
+OO.ui.TextInputWidget.prototype.setPosition =
+ OO.ui.TextInputWidget.prototype.setLabelPosition;
+
+/**
+ * Update the position of the inline label.
+ *
+ * This method is called by #setLabelPosition, and can also be called on its own if
+ * something causes the label to be mispositioned.
+ *
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.updatePosition = function () {
+ var after = this.labelPosition === 'after';
+
+ this.$element
+ .toggleClass( 'oo-ui-textInputWidget-labelPosition-after', !!this.label && after )
+ .toggleClass( 'oo-ui-textInputWidget-labelPosition-before', !!this.label && !after );
+
+ if ( this.label ) {
+ this.positionLabel();
+ }
+
+ return this;
+};
+
+/**
+ * Position the label by setting the correct padding on the input.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.positionLabel = function () {
+ // Clear old values
+ this.$input
+ // Clear old values if present
+ .css( {
+ 'padding-right': '',
+ 'padding-left': ''
+ } );
+
+ if ( this.label ) {
+ this.$element.append( this.$label );
+ } else {
+ this.$label.detach();
+ return;
+ }
+
+ var after = this.labelPosition === 'after',
+ rtl = this.$element.css( 'direction' ) === 'rtl',
+ property = after === rtl ? 'padding-left' : 'padding-right';
+
+ this.$input.css( property, this.$label.outerWidth( true ) );
+
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ToggleButtonWidget.js b/vendor/oojs/oojs-ui/src/widgets/ToggleButtonWidget.js
new file mode 100644
index 00000000..bb36f083
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ToggleButtonWidget.js
@@ -0,0 +1,114 @@
+/**
+ * ToggleButtons are buttons that have a state (‘on’ or ‘off’) that is represented by a
+ * Boolean value. Like other {@link OO.ui.ButtonWidget buttons}, toggle buttons can be
+ * configured with {@link OO.ui.IconElement icons}, {@link OO.ui.IndicatorElement indicators},
+ * {@link OO.ui.TitledElement titles}, {@link OO.ui.FlaggedElement styling flags},
+ * and {@link OO.ui.LabelElement labels}. Please see
+ * the [OOjs UI documentation][1] on MediaWiki for more information.
+ *
+ * @example
+ * // Toggle buttons in the 'off' and 'on' state.
+ * var toggleButton1 = new OO.ui.ToggleButtonWidget( {
+ * label: 'Toggle Button off'
+ * } );
+ * var toggleButton2 = new OO.ui.ToggleButtonWidget( {
+ * label: 'Toggle Button on',
+ * value: true
+ * } );
+ * // Append the buttons to the DOM.
+ * $( 'body' ).append( toggleButton1.$element, toggleButton2.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Toggle_buttons
+ *
+ * @class
+ * @extends OO.ui.ToggleWidget
+ * @mixins OO.ui.ButtonElement
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.LabelElement
+ * @mixins OO.ui.TitledElement
+ * @mixins OO.ui.FlaggedElement
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle button’s initial on/off
+ * state. By default, the button is in the 'off' state.
+ */
+OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ToggleButtonWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.ButtonElement.call( this, config );
+ OO.ui.IconElement.call( this, config );
+ OO.ui.IndicatorElement.call( this, config );
+ OO.ui.LabelElement.call( this, config );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
+ OO.ui.FlaggedElement.call( this, config );
+ OO.ui.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
+
+ // Events
+ this.connect( this, { click: 'onAction' } );
+
+ // Initialization
+ this.$button.append( this.$icon, this.$label, this.$indicator );
+ this.$element
+ .addClass( 'oo-ui-toggleButtonWidget' )
+ .append( this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleButtonWidget, OO.ui.ToggleWidget );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.ButtonElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.TitledElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.FlaggedElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Handle the button action being triggered.
+ *
+ * @private
+ */
+OO.ui.ToggleButtonWidget.prototype.onAction = function () {
+ this.setValue( !this.value );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
+ value = !!value;
+ if ( value !== this.value ) {
+ // Might be called from parent constructor before ButtonElement constructor
+ if ( this.$button ) {
+ this.$button.attr( 'aria-pressed', value.toString() );
+ }
+ this.setActive( value );
+ }
+
+ // Parent method
+ OO.ui.ToggleButtonWidget.super.prototype.setValue.call( this, value );
+
+ return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToggleButtonWidget.prototype.setButtonElement = function ( $button ) {
+ if ( this.$button ) {
+ this.$button.removeAttr( 'aria-pressed' );
+ }
+ OO.ui.ButtonElement.prototype.setButtonElement.call( this, $button );
+ this.$button.attr( 'aria-pressed', this.value.toString() );
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ToggleSwitchWidget.js b/vendor/oojs/oojs-ui/src/widgets/ToggleSwitchWidget.js
new file mode 100644
index 00000000..94f0f996
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ToggleSwitchWidget.js
@@ -0,0 +1,92 @@
+/**
+ * ToggleSwitches are switches that slide on and off. Their state is represented by a Boolean
+ * value (`true` for ‘on’, and `false` otherwise, the default). The ‘off’ state is represented
+ * visually by a slider in the leftmost position.
+ *
+ * @example
+ * // Toggle switches in the 'off' and 'on' position.
+ * var toggleSwitch1 = new OO.ui.ToggleSwitchWidget();
+ * var toggleSwitch2 = new OO.ui.ToggleSwitchWidget( {
+ * value: true
+ * } );
+ *
+ * // Create a FieldsetLayout to layout and label switches
+ * var fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'Toggle switches'
+ * } );
+ * fieldset.addItems( [
+ * new OO.ui.FieldLayout( toggleSwitch1, { label: 'Off', align: 'top' } ),
+ * new OO.ui.FieldLayout( toggleSwitch2, { label: 'On', align: 'top' } )
+ * ] );
+ * $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.ToggleWidget
+ * @mixins OO.ui.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle switch’s initial on/off state.
+ * By default, the toggle switch is in the 'off' position.
+ */
+OO.ui.ToggleSwitchWidget = function OoUiToggleSwitchWidget( config ) {
+ // Parent constructor
+ OO.ui.ToggleSwitchWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.TabIndexedElement.call( this, config );
+
+ // Properties
+ this.dragging = false;
+ this.dragStart = null;
+ this.sliding = false;
+ this.$glow = $( '<span>' );
+ this.$grip = $( '<span>' );
+
+ // Events
+ this.$element.on( {
+ click: this.onClick.bind( this ),
+ keypress: this.onKeyPress.bind( this )
+ } );
+
+ // Initialization
+ this.$glow.addClass( 'oo-ui-toggleSwitchWidget-glow' );
+ this.$grip.addClass( 'oo-ui-toggleSwitchWidget-grip' );
+ this.$element
+ .addClass( 'oo-ui-toggleSwitchWidget' )
+ .attr( 'role', 'checkbox' )
+ .append( this.$glow, this.$grip );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleSwitchWidget, OO.ui.ToggleWidget );
+OO.mixinClass( OO.ui.ToggleSwitchWidget, OO.ui.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
+ this.setValue( !this.value );
+ }
+ return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) {
+ if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+ this.setValue( !this.value );
+ return false;
+ }
+};
diff --git a/vendor/oojs/oojs-ui/src/widgets/ToggleWidget.js b/vendor/oojs/oojs-ui/src/widgets/ToggleWidget.js
new file mode 100644
index 00000000..16d6ba50
--- /dev/null
+++ b/vendor/oojs/oojs-ui/src/widgets/ToggleWidget.js
@@ -0,0 +1,71 @@
+/**
+ * ToggleWidget implements basic behavior of widgets with an on/off state.
+ * Please see OO.ui.ToggleButtonWidget and OO.ui.ToggleSwitchWidget for examples.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle’s initial on/off state.
+ * By default, the toggle is in the 'off' state.
+ */
+OO.ui.ToggleWidget = function OoUiToggleWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ToggleWidget.super.call( this, config );
+
+ // Properties
+ this.value = null;
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-toggleWidget' );
+ this.setValue( !!config.value );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleWidget, OO.ui.Widget );
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the on/off state of the toggle changes.
+ *
+ * @param {boolean} value Value representing the new state of the toggle
+ */
+
+/* Methods */
+
+/**
+ * Get the value representing the toggle’s state.
+ *
+ * @return {boolean} The on/off state of the toggle
+ */
+OO.ui.ToggleWidget.prototype.getValue = function () {
+ return this.value;
+};
+
+/**
+ * Set the state of the toggle: `true` for 'on', `false' for 'off'.
+ *
+ * @param {boolean} value The state of the toggle
+ * @fires change
+ * @chainable
+ */
+OO.ui.ToggleWidget.prototype.setValue = function ( value ) {
+ value = !!value;
+ if ( this.value !== value ) {
+ this.value = value;
+ this.emit( 'change', value );
+ this.$element.toggleClass( 'oo-ui-toggleWidget-on', value );
+ this.$element.toggleClass( 'oo-ui-toggleWidget-off', !value );
+ this.$element.attr( 'aria-checked', value.toString() );
+ }
+ return this;
+};
diff --git a/vendor/oojs/oojs-ui/tests/Element.test.js b/vendor/oojs/oojs-ui/tests/Element.test.js
new file mode 100644
index 00000000..b37d8e35
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/Element.test.js
@@ -0,0 +1,52 @@
+QUnit.module( 'Element', {
+ setup: function () {
+ this.fixture = document.createElement( 'div' );
+ document.body.appendChild( this.fixture );
+
+ this.makeFrame = function () {
+ var frame = document.createElement( 'iframe' );
+ this.fixture.appendChild( frame );
+ return ( frame.contentWindow && frame.contentWindow.document ) || frame.contentDocument;
+ };
+ },
+ teardown: function () {
+ this.fixture.parentNode.removeChild( this.fixture );
+ this.fixture = null;
+ }
+} );
+
+QUnit.test( 'static.getDocument', 10, function ( assert ) {
+ var frameDoc, frameEl, frameDiv,
+ el = this.fixture,
+ div = document.createElement( 'div' ),
+ $el = $( this.fixture ),
+ $div = $( '<div>' ),
+ win = window,
+ doc = document;
+
+ frameDoc = this.makeFrame();
+ frameEl = frameDoc.createElement( 'span' );
+ frameDoc.documentElement.appendChild( frameEl );
+ frameDiv = frameDoc.createElement( 'div' );
+
+ assert.strictEqual( OO.ui.Element.static.getDocument( $el ), doc, 'jQuery' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( $div ), doc, 'jQuery (detached)' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( el ), doc, 'HTMLElement' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( div ), doc, 'HTMLElement (detached)' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( win ), doc, 'Window' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( doc ), doc, 'HTMLDocument' );
+
+ assert.strictEqual( OO.ui.Element.static.getDocument( frameEl ), frameDoc, 'HTMLElement (framed)' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( frameDiv ), frameDoc, 'HTMLElement (framed, detached)' );
+ assert.strictEqual( OO.ui.Element.static.getDocument( frameDoc ), frameDoc, 'HTMLDocument (framed)' );
+
+ assert.strictEqual( OO.ui.Element.static.getDocument( {} ), null, 'Invalid' );
+} );
+
+QUnit.test( 'getElementDocument', 1, function ( assert ) {
+ var el, doc;
+
+ doc = document;
+ el = new OO.ui.Element();
+ assert.strictEqual( el.getElementDocument(), doc );
+} );
diff --git a/vendor/oojs/oojs-ui/tests/JSPHP.test.karma.js b/vendor/oojs/oojs-ui/tests/JSPHP.test.karma.js
new file mode 100644
index 00000000..1a1473c1
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/JSPHP.test.karma.js
@@ -0,0 +1,61 @@
+QUnit.module( 'JSPHP' );
+
+( function () {
+ // Generate some tests based on the test suite data and HTML from PHP version.
+ var theme, klassName,
+ themes = {
+ ApexTheme: new OO.ui.ApexTheme(),
+ MediaWikiTheme: new OO.ui.MediaWikiTheme()
+ };
+
+ function unstub( value ) {
+ var config;
+ if ( typeof value === 'string' && value.substr( 0, 13 ) === '_placeholder_' ) {
+ value = JSON.parse( value.substr( 13 ) );
+ config = OO.copy( value.config, null, unstub );
+ return new OO.ui[ value.class ]( config );
+ }
+ }
+
+ function makeTest( theme, klassName, tests, output ) {
+ QUnit.test( theme + ': ' + klassName, tests.length * 2, function ( assert ) {
+ var test, config, instance, infused, $fromPhp, i, testName;
+ OO.ui.theme = themes[ theme ];
+ for ( i = 0; i < tests.length; i++ ) {
+ test = tests[ i ];
+ // Unstub placeholders
+ config = OO.copy( test.config, null, unstub );
+
+ instance = new OO.ui[ test.class ]( config );
+ $fromPhp = $( $.parseHTML( output[ i ] ) );
+
+ $( 'body' ).append( instance.$element, $fromPhp );
+
+ // Updating theme classes is normally debounced, we need to do it immediately
+ instance.debouncedUpdateThemeClasses();
+
+ testName = JSON.stringify( test.config );
+ assert.equalDomElement( instance.$element[ 0 ], $fromPhp[ 0 ], testName, true );
+
+ infused = OO.ui.infuse( $fromPhp[ 0 ] );
+ infused.debouncedUpdateThemeClasses();
+
+ assert.equalDomElement( instance.$element[ 0 ], infused.$element[ 0 ], testName + ' (infuse)' );
+ instance.$element.add( infused.$element ).detach();
+ }
+ } );
+ }
+
+ /*global testSuiteConfigs, testSuitePHPOutput */
+ for ( klassName in testSuiteConfigs ) {
+ for ( theme in themes ) {
+ makeTest(
+ theme,
+ klassName,
+ testSuiteConfigs[ klassName ],
+ testSuitePHPOutput[ theme ][ klassName ]
+ );
+ }
+ }
+
+} )();
diff --git a/vendor/oojs/oojs-ui/tests/JSPHP.test.standalone.js b/vendor/oojs/oojs-ui/tests/JSPHP.test.standalone.js
new file mode 100644
index 00000000..1cbebc3a
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/JSPHP.test.standalone.js
@@ -0,0 +1,55 @@
+QUnit.module( 'JSPHP' );
+
+( function () {
+ // Generate some tests based on the test suite data and HTML from PHP version.
+ var theme, klassName,
+ themes = {
+ ApexTheme: new OO.ui.ApexTheme(),
+ MediaWikiTheme: new OO.ui.MediaWikiTheme()
+ };
+
+ function unstub( value ) {
+ var config;
+ if ( typeof value === 'string' && value.substr( 0, 13 ) === '_placeholder_' ) {
+ value = JSON.parse( value.substr( 13 ) );
+ config = OO.copy( value.config, null, unstub );
+ return new OO.ui[ value.class ]( config );
+ }
+ }
+
+ function makeTest( theme, klassName, tests ) {
+ QUnit.test( theme + ': ' + klassName, tests.length * 2, function ( assert ) {
+ var test, config, instance, infused, id, fromPhp, i, testName;
+ OO.ui.theme = themes[ theme ];
+ for ( i = 0; i < tests.length; i++ ) {
+ test = tests[ i ];
+ // Unstub placeholders
+ config = OO.copy( test.config, null, unstub );
+
+ instance = new OO.ui[ test.class ]( config );
+
+ id = 'JSPHPTestSuite_' + theme + klassName + i;
+ fromPhp = document.getElementById( id ).firstChild;
+ instance.$element.insertBefore( fromPhp );
+
+ // Updating theme classes is normally debounced, we need to do it immediately
+ instance.debouncedUpdateThemeClasses();
+
+ testName = JSON.stringify( test.config );
+ assert.equalDomElement( instance.$element[ 0 ], fromPhp, testName );
+
+ infused = OO.ui.infuse( fromPhp );
+ infused.debouncedUpdateThemeClasses();
+
+ assert.equalDomElement( instance.$element[ 0 ], infused.$element[ 0 ], testName + ' (infuse)' );
+ }
+ } );
+ }
+
+ for ( klassName in OO.ui.JSPHPTestSuite ) {
+ for ( theme in themes ) {
+ makeTest( theme, klassName, OO.ui.JSPHPTestSuite[ klassName ] );
+ }
+ }
+
+} )();
diff --git a/vendor/oojs/oojs-ui/tests/Process.test.js b/vendor/oojs/oojs-ui/tests/Process.test.js
new file mode 100644
index 00000000..3f036407
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/Process.test.js
@@ -0,0 +1,179 @@
+QUnit.module( 'OO.ui.Process' );
+
+/* Tests */
+
+QUnit.test( 'next', 1, function ( assert ) {
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .next( function () {
+ result.push( 0 );
+ } )
+ .next( function () {
+ result.push( 1 );
+ } )
+ .next( function () {
+ result.push( 2 );
+ } )
+ .execute();
+
+ assert.deepEqual( result, [ 0, 1, 2 ], 'Steps can be added at the end' );
+} );
+
+QUnit.test( 'first', 1, function ( assert ) {
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .first( function () {
+ result.push( 0 );
+ } )
+ .first( function () {
+ result.push( 1 );
+ } )
+ .first( function () {
+ result.push( 2 );
+ } )
+ .execute();
+
+ assert.deepEqual( result, [ 2, 1, 0 ], 'Steps can be added at the beginning' );
+} );
+
+QUnit.asyncTest( 'execute (async)', 1, function ( assert ) {
+ // Async
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .next( function () {
+ var deferred = $.Deferred();
+
+ setTimeout( function () {
+ result.push( 1 );
+ deferred.resolve();
+ }, 10 );
+
+ return deferred.promise();
+ } )
+ .first( function () {
+ var deferred = $.Deferred();
+
+ setTimeout( function () {
+ result.push( 0 );
+ deferred.resolve();
+ }, 10 );
+
+ return deferred.promise();
+ } )
+ .next( function () {
+ result.push( 2 );
+ } );
+
+ process.execute().done( function () {
+ assert.deepEqual(
+ result,
+ [ 0, 1, 2 ],
+ 'Synchronous and asynchronous steps are executed in the correct order'
+ );
+ QUnit.start();
+ } );
+} );
+
+QUnit.asyncTest( 'execute (return false)', 1, function ( assert ) {
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .next( function () {
+ var deferred = $.Deferred();
+
+ setTimeout( function () {
+ result.push( 0 );
+ deferred.resolve();
+ }, 10 );
+
+ return deferred.promise();
+ } )
+ .next( function () {
+ result.push( 1 );
+ return false;
+ } )
+ .next( function () {
+ // Should never be run because previous step is rejected
+ result.push( 2 );
+ } );
+
+ process.execute().fail( function () {
+ assert.deepEqual(
+ result,
+ [ 0, 1 ],
+ 'Process is stopped when a step returns false'
+ );
+ QUnit.start();
+ } );
+} );
+
+QUnit.asyncTest( 'execute (async reject)', 1, function ( assert ) {
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .next( function () {
+ result.push( 0 );
+ } )
+ .next( function () {
+ var deferred = $.Deferred();
+
+ setTimeout( function () {
+ result.push( 1 );
+ deferred.reject();
+ }, 10 );
+
+ return deferred.promise();
+ } )
+ .next( function () {
+ // Should never be run because previous step is rejected
+ result.push( 2 );
+ } );
+
+ process.execute().fail( function () {
+ assert.deepEqual(
+ result,
+ [ 0, 1 ],
+ 'Process is stopped when a step returns a promise that is then rejected'
+ );
+ QUnit.start();
+ } );
+} );
+
+QUnit.asyncTest( 'execute (wait)', 1, function ( assert ) {
+ var process = new OO.ui.Process(),
+ result = [];
+
+ process
+ .next( function () {
+ result.push( 'A' );
+ return 10;
+ } )
+ .next( function () {
+ result.push( 'B' );
+ } );
+
+ // Steps defined above don't run until execute()
+ result.push( 'before' );
+
+ // Process yields between step A and B
+ setTimeout( function () {
+ result.push( 'yield' );
+ } );
+
+ process.execute().done( function () {
+ assert.deepEqual(
+ result,
+ [ 'before', 'A', 'yield', 'B' ],
+ 'Process is stopped when a step returns a promise that is then rejected'
+ );
+ QUnit.start();
+ } );
+} );
diff --git a/vendor/oojs/oojs-ui/tests/QUnit.assert.equalDomElement.js b/vendor/oojs/oojs-ui/tests/QUnit.assert.equalDomElement.js
new file mode 100644
index 00000000..f041c258
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/QUnit.assert.equalDomElement.js
@@ -0,0 +1,113 @@
+/*!
+ * A QUnit assertion to compare DOM node trees.
+ *
+ * Adapted from VisualEditor plugin for QUnit. Additionally supports comparing properties to
+ * attributes (for dynamically generated nodes) and order-insensitive comparison of classes on DOM
+ * nodes.
+ *
+ * @copyright 2011-2015 VisualEditor Team and others; see http://ve.mit-license.org
+ * @copyright 2011-2015 OOjs Team and other contributors
+ */
+
+( function ( QUnit ) {
+
+ /**
+ * Build a summary of an HTML element.
+ *
+ * Summaries include node name, text, attributes and recursive summaries of children.
+ * Used for serializing or comparing HTML elements.
+ *
+ * @private
+ * @param {HTMLElement} element Element to summarize
+ * @param {boolean} [includeHtml=false] Include an HTML summary for element nodes
+ * @return {Object} Summary of element.
+ */
+ function getDomElementSummary( element, includeHtml ) {
+ var i, name,
+ summary = {
+ type: element.nodeName.toLowerCase(),
+ // $( '<div><textarea>Foo</textarea></div>' )[0].textContent === 'Foo', which breaks
+ // comparisons :( childNodes are summarized anyway, this would just be a nicety
+ // text: element.textContent,
+ attributes: {},
+ children: []
+ };
+
+ if ( includeHtml && element.nodeType === Node.ELEMENT_NODE ) {
+ summary.html = element.outerHTML;
+ }
+
+ // Gather attributes
+ if ( element.attributes ) {
+ for ( i = 0; i < element.attributes.length; i++ ) {
+ name = element.attributes[ i ].name;
+ if ( name.substr( 0, 5 ) !== 'data-' && name !== 'id' ) {
+ summary.attributes[ name ] = element.attributes[ i ].value;
+ }
+ }
+ }
+
+ // Sort classes
+ if ( summary.attributes.class ) {
+ summary.attributes.class = summary.attributes.class.split( ' ' ).sort().join( ' ' );
+ }
+
+ // Gather certain properties and pretend they are attributes.
+ // Take note of casing differences.
+ if ( element.value !== undefined ) {
+ summary.attributes.value = element.value;
+ }
+ if ( element.readOnly !== undefined ) {
+ summary.attributes.readonly = element.readOnly;
+ }
+ if ( element.checked !== undefined ) {
+ summary.attributes.checked = element.checked;
+ }
+ if ( element.disabled !== undefined ) {
+ summary.attributes.disabled = element.disabled;
+ }
+ if ( element.tabIndex !== undefined ) {
+ summary.attributes.tabindex = element.tabIndex;
+ }
+
+ // Summarize children
+ if ( element.childNodes ) {
+ for ( i = 0; i < element.childNodes.length; i++ ) {
+ summary.children.push( getDomElementSummary( element.childNodes[ i ], includeHtml ) );
+ }
+ }
+
+ // Special handling for textareas, where we only want to account for the content as the 'value'
+ // property, never as childNodes or textContent
+ if ( summary.type === 'textarea' ) {
+ // summary.text = '';
+ summary.children = [];
+ }
+
+ return summary;
+ }
+
+ /**
+ * @method
+ * @static
+ */
+ QUnit.assert.equalDomElement = function ( actual, expected, message, stringify ) {
+ var actualSummary = getDomElementSummary( actual ),
+ expectedSummary = getDomElementSummary( expected ),
+ actualSummaryHtml = getDomElementSummary( actual, true ),
+ expectedSummaryHtml = getDomElementSummary( expected, true );
+
+ // When running with Karma, the objects are not nicely stringified in the output when the test
+ // fails, only showing "Expected: [object Object], Actual: [object Object]" instead. Running
+ // QUnit in browser does this, and stringifying causes double escaping in output.
+ if ( stringify ) {
+ actualSummaryHtml = JSON.stringify( actualSummaryHtml, null, 2 );
+ expectedSummaryHtml = JSON.stringify( expectedSummaryHtml, null, 2 );
+ }
+
+ QUnit.push(
+ QUnit.equiv( actualSummary, expectedSummary ), actualSummaryHtml, expectedSummaryHtml, message
+ );
+ };
+
+}( QUnit ) );
diff --git a/vendor/oojs/oojs-ui/tests/elements/FlaggedElement.test.js b/vendor/oojs/oojs-ui/tests/elements/FlaggedElement.test.js
new file mode 100644
index 00000000..f5c483ad
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/elements/FlaggedElement.test.js
@@ -0,0 +1,64 @@
+( function () {
+ QUnit.module( 'FlaggedElement' );
+
+ function TestElement( config ) {
+ TestElement.super.call( this, config );
+ OO.ui.FlaggedElement.call( this, config );
+ }
+ OO.inheritClass( TestElement, OO.ui.Widget );
+ OO.mixinClass( TestElement, OO.ui.FlaggedElement );
+
+ QUnit.test( 'constructor', 2, function ( assert ) {
+ var element;
+
+ element = new TestElement();
+ assert.deepEqual( element.getFlags(), [], 'No flags by default' );
+
+ element = new TestElement( {
+ flags: [ 'foo' ]
+ } );
+ assert.deepEqual( element.getFlags(), [ 'foo' ], 'Config option "flags"' );
+ } );
+
+ QUnit.test( 'getFlags', 2, function ( assert ) {
+ var element = new TestElement();
+
+ element.setFlags( 'foo' );
+ assert.deepEqual( element.getFlags(), [ 'foo' ], 'Flag was set' );
+
+ element.clearFlags();
+ assert.deepEqual( element.getFlags(), [], 'Flag was removed' );
+ } );
+
+ QUnit.test( 'hasFlag', 3, function ( assert ) {
+ var element = new TestElement();
+ assert.deepEqual( element.hasFlag( 'foo' ), false, 'Flag absent by default' );
+
+ element.setFlags( 'foo' );
+ assert.deepEqual( element.hasFlag( 'foo' ), true, 'Flag was set' );
+
+ element.clearFlags();
+ assert.deepEqual( element.hasFlag( 'foo' ), false, 'Flag was removed' );
+ } );
+
+ QUnit.test( 'clearFlags', 1, function ( assert ) {
+ var element = new TestElement();
+ element.setFlags( 'foo' );
+ element.clearFlags();
+ assert.deepEqual( element.hasFlag( 'foo' ), false, 'Flag was removed' );
+ } );
+
+ QUnit.test( 'setFlags', 5, function ( assert ) {
+ var element = new TestElement();
+ element.setFlags( 'foo' );
+ assert.deepEqual( element.hasFlag( 'foo' ), true, 'string' );
+
+ element.setFlags( [ 'bar', 'qux' ] );
+ assert.deepEqual( element.hasFlag( 'bar' ), true, 'array[ 0 ]' );
+ assert.deepEqual( element.hasFlag( 'qux' ), true, 'array[ 1 ]' );
+
+ element.setFlags( { bar: false, quux: true } );
+ assert.deepEqual( element.hasFlag( 'bar' ), false, 'object set' );
+ assert.deepEqual( element.hasFlag( 'quux' ), true, 'object remove' );
+ } );
+}() );
diff --git a/vendor/oojs/oojs-ui/tests/index.php b/vendor/oojs/oojs-ui/tests/index.php
new file mode 100644
index 00000000..d8e06835
--- /dev/null
+++ b/vendor/oojs/oojs-ui/tests/index.php
@@ -0,0 +1,77 @@
+<?php
+ $autoload = '../vendor/autoload.php';
+ if ( !file_exists( $autoload ) ) {
+ echo '<h1>Did you forget to run <code>composer install</code>?</h1>';
+ exit;
+ }
+ require_once $autoload;
+
+ $testSuiteFile = 'JSPHP-suite.json';
+ if ( !file_exists( $testSuiteFile ) ) {
+ echo '<h1>Did you forget to run <code>grunt build</code>?</h1>';
+ exit;
+ }
+ $testSuiteJSON = file_get_contents( $testSuiteFile );
+ $testSuite = json_decode( $testSuiteJSON, true );
+?>
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+ <meta charset="UTF-8">
+ <title>OOjs UI Test Suite</title>
+ <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css">
+ <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
+ <script src="./QUnit.assert.equalDomElement.js"></script>
+ <script>
+ QUnit.config.requireExpects = true;
+ </script>
+ <!-- Dependencies -->
+ <script src="../node_modules/jquery/dist/jquery.js"></script>
+ <script src="../node_modules/oojs/dist/oojs.jquery.js"></script>
+ <!-- Source code -->
+ <script src="../dist/oojs-ui.js"></script>
+ <script src="../dist/oojs-ui-apex.js"></script>
+ <script src="../dist/oojs-ui-mediawiki.js"></script>
+ <!-- Test suites -->
+ <script src="./Element.test.js"></script>
+ <script src="./Process.test.js"></script>
+ <script src="./elements/FlaggedElement.test.js"></script>
+ <!-- JS/PHP comparison tests -->
+ <script>OO.ui.JSPHPTestSuite = <?php echo $testSuiteJSON; ?></script>
+ <script src="./JSPHP.test.standalone.js"></script>
+</head>
+<body>
+ <div id="JSPHPTestSuite" style="display: none;">
+ <?php
+ function new_OOUI( $class, $config = array() ) {
+ $class = "OOUI\\" . $class;
+ return new $class( $config );
+ }
+ function unstub( &$value ) {
+ if ( is_string( $value ) && substr( $value, 0, 13 ) === '_placeholder_' ) {
+ $value = json_decode( substr( $value, 13 ), true );
+ array_walk_recursive( $value['config'], 'unstub' );
+ $value = new_OOUI( $value['class'], $value['config'] );
+ }
+ }
+ // Keep synchronized with bin/generate-JSPHP-for-karma.php
+ $themes = array( 'ApexTheme', 'MediaWikiTheme' );
+ foreach ( $themes as $theme ) {
+ OOUI\Theme::setSingleton( new_OOUI( $theme ) );
+ foreach ( $testSuite as $className => $tests ) {
+ foreach ( $tests as $index => $test ) {
+ // Unstub placeholders
+ $config = $test['config'];
+ array_walk_recursive( $config, 'unstub' );
+ $config['infusable'] = true;
+ $instance = new_OOUI( $test['class'], $config );
+ echo "<div id='JSPHPTestSuite_$theme$className$index'>$instance</div>\n";
+ }
+ }
+ }
+ ?>
+ </div>
+ <div id="qunit"></div>
+ <div id="qunit-fixture"></div>
+</body>
+</html>