summaryrefslogtreecommitdiff
path: root/resources
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-12-08 09:55:49 +0100
committerPierre Schmitz <pierre@archlinux.de>2013-12-08 09:55:49 +0100
commit4ac9fa081a7c045f6a9f1cfc529d82423f485b2e (patch)
treeaf68743f2f4a47d13f2b0eb05f5c4aaf86d8ea37 /resources
parentaf4da56f1ad4d3ef7b06557bae365da2ea27a897 (diff)
Update to MediaWiki 1.22.0
Diffstat (limited to 'resources')
-rw-r--r--resources/Resources.php220
-rw-r--r--resources/Resources.php.orig968
-rw-r--r--resources/jquery.chosen/LICENSE24
-rw-r--r--resources/jquery.chosen/chosen-sprite.pngbin0 -> 646 bytes
-rw-r--r--resources/jquery.chosen/chosen-sprite@2x.pngbin0 -> 871 bytes
-rw-r--r--resources/jquery.chosen/chosen.css440
-rw-r--r--resources/jquery.chosen/chosen.jquery.js1103
-rw-r--r--resources/jquery.tipsy/images/tipsy.pngbin175 -> 133 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_0_aaaaaa_40x100.pngbin180 -> 87 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_75_ffffff_40x100.pngbin178 -> 87 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.pngbin120 -> 115 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.pngbin105 -> 99 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.pngbin111 -> 111 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin101 -> 86 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.pngbin4369 -> 3702 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.pngbin4369 -> 3702 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.pngbin4369 -> 3702 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.pngbin4369 -> 3702 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.pngbin4369 -> 3702 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-blue-hover-large.pngbin260 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-blue-hover.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-blue-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-blue.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-disabled-blue.pngbin84 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-disabled-green.pngbin149 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-disabled-red.pngbin84 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-disabled.pngbin84 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-down-blue.pngbin130 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-down-green.pngbin141 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-down-red.pngbin130 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-down.pngbin130 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-green-hover-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-green-hover.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-green-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-green.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-large-disabled-green.pngbin277 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-large-off-green.pngbin282 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-off-blue.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-off-green.pngbin149 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-off-red.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-off.pngbin152 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-orange-hover-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-orange-hover.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-orange-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-orange.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-over-blue.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-over-green.pngbin149 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-over-red.pngbin174 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-over.pngbin155 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-red-hover-large.pngbin260 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-red-hover.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-red-large.pngbin265 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-red.pngbin175 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/titlebar-fade.pngbin188 -> 81 bytes
-rw-r--r--resources/jquery.ui/themes/vector/jquery.ui.button.css368
-rw-r--r--resources/jquery/images/jquery.arrowSteps.divider-ltr.pngbin135 -> 126 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.divider-rtl.pngbin139 -> 127 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.head-ltr.pngbin390 -> 303 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.head-rtl.pngbin365 -> 311 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.tail-ltr.pngbin223 -> 222 bytes
-rw-r--r--resources/jquery/images/marker.pngbin652 -> 472 bytes
-rw-r--r--resources/jquery/images/mask.pngbin2020 -> 1795 bytes
-rw-r--r--resources/jquery/jquery.badge.css9
-rw-r--r--resources/jquery/jquery.byteLength.js12
-rw-r--r--resources/jquery/jquery.byteLimit.js3
-rw-r--r--resources/jquery/jquery.checkboxShiftClick.js16
-rw-r--r--resources/jquery/jquery.client.js100
-rw-r--r--resources/jquery/jquery.makeCollapsible.js231
-rw-r--r--resources/jquery/jquery.placeholder.js9
-rw-r--r--resources/jquery/jquery.spinner.css2
-rw-r--r--resources/jquery/jquery.spinner.js47
-rw-r--r--resources/jquery/jquery.suggestions.js4
-rw-r--r--resources/jquery/jquery.tablesorter.js180
-rw-r--r--resources/jquery/jquery.textSelection.js4
-rw-r--r--resources/mediawiki.action/images/green-checkmark.pngbin0 -> 681 bytes
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css17
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js54
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.editWarning.js56
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.js167
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.preview.js53
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.styles.css44
-rw-r--r--resources/mediawiki.action/mediawiki.action.history.js2
-rw-r--r--resources/mediawiki.action/mediawiki.action.view.postEdit.css77
-rw-r--r--resources/mediawiki.action/mediawiki.action.view.postEdit.js75
-rw-r--r--resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js4
-rw-r--r--resources/mediawiki.api/mediawiki.api.category.js35
-rw-r--r--resources/mediawiki.api/mediawiki.api.edit.js65
-rw-r--r--resources/mediawiki.api/mediawiki.api.js88
-rw-r--r--resources/mediawiki.api/mediawiki.api.login.js54
-rw-r--r--resources/mediawiki.api/mediawiki.api.parse.js12
-rw-r--r--resources/mediawiki.api/mediawiki.api.watch.js12
-rw-r--r--resources/mediawiki.language/mediawiki.language.js23
-rw-r--r--resources/mediawiki.language/mediawiki.language.months.js54
-rw-r--r--resources/mediawiki.less/mediawiki.mixins.less46
-rw-r--r--resources/mediawiki.libs/mediawiki.libs.jpegmeta.js2
-rw-r--r--resources/mediawiki.page/mediawiki.page.gallery.js248
-rw-r--r--resources/mediawiki.page/mediawiki.page.image.pagination.js51
-rw-r--r--resources/mediawiki.page/mediawiki.page.patrol.ajax.js2
-rw-r--r--resources/mediawiki.page/mediawiki.page.ready.js38
-rw-r--r--resources/mediawiki.page/mediawiki.page.startup.js23
-rw-r--r--resources/mediawiki.page/mediawiki.page.watch.ajax.js6
-rw-r--r--resources/mediawiki.special/images/arrow-collapsed-ltr.pngbin206 -> 0 bytes
-rw-r--r--resources/mediawiki.special/images/arrow-collapsed-rtl.pngbin205 -> 0 bytes
-rw-r--r--resources/mediawiki.special/images/arrow-expanded.pngbin205 -> 0 bytes
-rw-r--r--resources/mediawiki.special/images/glyph-people-large.pngbin0 -> 1663 bytes
-rw-r--r--resources/mediawiki.special/images/icon-contributors.pngbin0 -> 1169 bytes
-rw-r--r--resources/mediawiki.special/images/icon-edits.pngbin0 -> 780 bytes
-rw-r--r--resources/mediawiki.special/images/icon-lock.pngbin0 -> 172 bytes
-rw-r--r--resources/mediawiki.special/images/icon-pages.pngbin0 -> 528 bytes
-rw-r--r--resources/mediawiki.special/mediawiki.special.block.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeemail.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeslist.css57
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeslist.enhanced.css66
-rw-r--r--resources/mediawiki.special/mediawiki.special.createAccount.css89
-rw-r--r--resources/mediawiki.special/mediawiki.special.createAccount.js112
-rw-r--r--resources/mediawiki.special/mediawiki.special.movePage.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.pagesWithProp.css4
-rw-r--r--resources/mediawiki.special/mediawiki.special.preferences.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.recentchanges.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.search.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.undelete.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.upload.js4
-rw-r--r--resources/mediawiki.special/mediawiki.special.userLogin.css39
-rw-r--r--resources/mediawiki.special/mediawiki.special.userLogin.signup.js10
-rw-r--r--resources/mediawiki.special/mediawiki.special.vforms.css46
-rw-r--r--resources/mediawiki.ui/mediawiki.ui.default.css272
-rw-r--r--resources/mediawiki.ui/mediawiki.ui.vector.css414
-rw-r--r--resources/mediawiki.ui/sourcefiles/Makefile24
-rw-r--r--resources/mediawiki.ui/sourcefiles/config.rb27
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/_default.scss3
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/_utilities.scss17
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/_vector.scss4
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/default/_buttons.scss69
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/default/_forms.scss114
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/vector/_buttons.scss19
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/vector/_containers.scss5
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/components/vector/_forms.scss7
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.default.scss16
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.vector.scss15
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mixins/_all.scss4
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mixins/_effects.scss62
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mixins/_forms.scss66
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mixins/_type.scss6
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/mixins/_utilities.scss19
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/settings/_all.scss2
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/settings/_colors.scss17
-rw-r--r--resources/mediawiki.ui/sourcefiles/scss/settings/_typography.scss5
-rw-r--r--resources/mediawiki/images/arrow-collapsed-ltr.pngbin0 -> 133 bytes
-rw-r--r--resources/mediawiki/images/arrow-collapsed-rtl.pngbin0 -> 136 bytes
-rw-r--r--resources/mediawiki/images/arrow-expanded.pngbin0 -> 134 bytes
-rw-r--r--resources/mediawiki/mediawiki.Title.js574
-rw-r--r--resources/mediawiki/mediawiki.Uri.js4
-rw-r--r--resources/mediawiki/mediawiki.debug.js2
-rw-r--r--resources/mediawiki/mediawiki.htmlform.js70
-rw-r--r--resources/mediawiki/mediawiki.icon.css15
-rw-r--r--resources/mediawiki/mediawiki.inspect.js204
-rw-r--r--resources/mediawiki/mediawiki.jqueryMsg.js335
-rw-r--r--resources/mediawiki/mediawiki.js496
-rw-r--r--resources/mediawiki/mediawiki.log.js65
-rw-r--r--resources/mediawiki/mediawiki.notification.css16
-rw-r--r--resources/mediawiki/mediawiki.notification.js25
-rw-r--r--resources/mediawiki/mediawiki.notify.js13
-rw-r--r--resources/mediawiki/mediawiki.searchSuggest.js48
-rw-r--r--resources/mediawiki/mediawiki.user.js253
-rw-r--r--resources/mediawiki/mediawiki.util.js204
-rw-r--r--resources/startup.js42
166 files changed, 6697 insertions, 2345 deletions
diff --git a/resources/Resources.php b/resources/Resources.php
index 6205bb91..06120008 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -20,7 +20,7 @@
* @file
*/
-if( !defined( 'MEDIAWIKI' ) ) {
+if ( !defined( 'MEDIAWIKI' ) ) {
die( 'Not an entry point.' );
}
@@ -60,14 +60,11 @@ return array(
*
* See Vector for an example.
*/
-
- 'skins.chick' => array(
- 'styles' => array( 'chick/main.css' => array( 'media' => 'screen, handheld' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
'skins.cologneblue' => array(
- 'styles' => array( 'cologneblue/screen.css' => array( 'media' => 'screen' ) ),
+ 'styles' => array(
+ 'cologneblue/screen.css' => array( 'media' => 'screen' ),
+ 'cologneblue/print.css' => array( 'media' => 'print' ),
+ ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
@@ -102,37 +99,53 @@ return array(
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
- 'skins.nostalgia' => array(
- 'styles' => array( 'nostalgia/screen.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.simple' => array(
- 'styles' => array( 'simple/main.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.standard' => array(
- 'styles' => array( 'standard/main.css' => array( 'media' => 'screen' ) ),
+ 'skins.vector' => array(
+ // Used in the web installer. Test it after modifying this definition!
+ 'styles' => array(
+ 'common/commonElements.css' => array( 'media' => 'screen' ),
+ 'common/commonContent.css' => array( 'media' => 'screen' ),
+ 'common/commonInterface.css' => array( 'media' => 'screen' ),
+ 'vector/styles.less',
+ ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
- 'skins.vector' => array(
- // Keep in sync with WebInstallerOutput::getCSS()
+ 'skins.vector.beta' => array(
+ // Keep in sync with skins.vector
'styles' => array(
'common/commonElements.css' => array( 'media' => 'screen' ),
'common/commonContent.css' => array( 'media' => 'screen' ),
'common/commonInterface.css' => array( 'media' => 'screen' ),
- 'vector/screen.css' => array( 'media' => 'screen' ),
- 'vector/screen-hd.css' => array( 'media' => 'screen and (min-width: 982px)' ),
+ 'vector/styles-beta.less',
),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
'skins.vector.js' => array(
- 'scripts' => 'vector/vector.js',
+ 'scripts' => array(
+ 'vector/collapsibleTabs.js',
+ 'vector/vector.js',
+ ),
+ 'position' => 'top',
+ 'dependencies' => 'jquery.delayedBind',
+ 'remoteBasePath' => $GLOBALS['wgStylePath'],
+ 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ ),
+ 'skins.vector.collapsibleNav' => array(
+ 'scripts' => array(
+ 'vector/collapsibleNav.js',
+ ),
+ 'messages' => array(
+ 'vector-collapsiblenav-more',
+ ),
+ 'dependencies' => array(
+ 'jquery.client',
+ 'jquery.cookie',
+ 'jquery.tabIndex',
+ ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ 'position' => 'bottom',
),
/* jQuery */
@@ -166,6 +179,7 @@ return array(
),
'jquery.byteLength' => array(
'scripts' => 'resources/jquery/jquery.byteLength.js',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.byteLimit' => array(
'scripts' => 'resources/jquery/jquery.byteLimit.js',
@@ -173,6 +187,11 @@ return array(
),
'jquery.checkboxShiftClick' => array(
'scripts' => 'resources/jquery/jquery.checkboxShiftClick.js',
+ 'targets' => array( 'desktop', 'mobile' ),
+ ),
+ 'jquery.chosen' => array(
+ 'scripts' => 'resources/jquery.chosen/chosen.jquery.js',
+ 'styles' => 'resources/jquery.chosen/chosen.css',
),
'jquery.client' => array(
'scripts' => 'resources/jquery/jquery.client.js',
@@ -210,6 +229,7 @@ return array(
),
'jquery.getAttrs' => array(
'scripts' => 'resources/jquery/jquery.getAttrs.js',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.hidpi' => array(
'scripts' => 'resources/jquery/jquery.hidpi.js',
@@ -233,12 +253,14 @@ return array(
'scripts' => 'resources/jquery/jquery.makeCollapsible.js',
'styles' => 'resources/jquery/jquery.makeCollapsible.css',
'messages' => array( 'collapsible-expand', 'collapsible-collapse' ),
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.mockjax' => array(
'scripts' => 'resources/jquery/jquery.mockjax.js',
),
'jquery.mw-jump' => array(
'scripts' => 'resources/jquery/jquery.mw-jump.js',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.mwExtension' => array(
'scripts' => 'resources/jquery/jquery.mwExtension.js',
@@ -246,15 +268,18 @@ return array(
),
'jquery.placeholder' => array(
'scripts' => 'resources/jquery/jquery.placeholder.js',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.qunit' => array(
'scripts' => 'resources/jquery/jquery.qunit.js',
'styles' => 'resources/jquery/jquery.qunit.css',
'position' => 'top',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.qunit.completenessTest' => array(
'scripts' => 'resources/jquery/jquery.qunit.completenessTest.js',
'dependencies' => 'jquery.qunit',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'jquery.spinner' => array(
'scripts' => 'resources/jquery/jquery.spinner.js',
@@ -276,7 +301,10 @@ return array(
'scripts' => 'resources/jquery/jquery.tablesorter.js',
'styles' => 'resources/jquery/jquery.tablesorter.css',
'messages' => array( 'sort-descending', 'sort-ascending' ),
- 'dependencies' => 'jquery.mwExtension',
+ 'dependencies' => array(
+ 'jquery.mwExtension',
+ 'mediawiki.language.months',
+ ),
),
'jquery.textSelection' => array(
'scripts' => 'resources/jquery/jquery.textSelection.js',
@@ -605,6 +633,12 @@ return array(
'mediawiki.Title',
),
),
+ 'mediawiki.api.login' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.login.js',
+ 'dependencies' => array(
+ 'mediawiki.api',
+ ),
+ ),
'mediawiki.api.parse' => array(
'scripts' => 'resources/mediawiki.api/mediawiki.api.parse.js',
'dependencies' => 'mediawiki.api',
@@ -616,6 +650,9 @@ return array(
'user.tokens',
),
),
+ 'mediawiki.icon' => array(
+ 'styles' => 'resources/mediawiki/mediawiki.icon.css',
+ ),
'mediawiki.debug' => array(
'scripts' => 'resources/mediawiki/mediawiki.debug.js',
'styles' => 'resources/mediawiki/mediawiki.debug.css',
@@ -629,6 +666,14 @@ return array(
// must be loaded on the bottom
'position' => 'bottom',
),
+ 'mediawiki.inspect' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.inspect.js',
+ 'dependencies' => array(
+ 'jquery.byteLength',
+ 'jquery.json',
+ ),
+ 'targets' => array( 'desktop', 'mobile' ),
+ ),
'mediawiki.feedback' => array(
'scripts' => 'resources/mediawiki/mediawiki.feedback.js',
'styles' => 'resources/mediawiki/mediawiki.feedback.css',
@@ -663,6 +708,7 @@ return array(
),
'mediawiki.htmlform' => array(
'scripts' => 'resources/mediawiki/mediawiki.htmlform.js',
+ 'messages' => array( 'htmlform-chosen-placeholder' ),
),
'mediawiki.notification' => array(
'styles' => 'resources/mediawiki/mediawiki.notification.css',
@@ -687,11 +733,15 @@ return array(
'jquery.client',
'jquery.placeholder',
'jquery.suggestions',
+ 'mediawiki.api',
),
),
'mediawiki.Title' => array(
'scripts' => 'resources/mediawiki/mediawiki.Title.js',
- 'dependencies' => 'mediawiki.util',
+ 'dependencies' => array(
+ 'jquery.byteLength',
+ 'mediawiki.util',
+ ),
),
'mediawiki.Uri' => array(
'scripts' => 'resources/mediawiki/mediawiki.Uri.js',
@@ -723,16 +773,30 @@ return array(
'mediawiki.action.edit' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.js',
'dependencies' => array(
+ 'mediawiki.action.edit.styles',
'jquery.textSelection',
'jquery.byteLimit',
),
'position' => 'top',
),
+ 'mediawiki.action.edit.styles' => array(
+ 'styles' => 'resources/mediawiki.action/mediawiki.action.edit.styles.css',
+ 'position' => 'top',
+ ),
+ 'mediawiki.action.edit.collapsibleFooter' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js',
+ 'styles' => 'resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css',
+ 'dependencies' => array(
+ 'jquery.makeCollapsible',
+ 'mediawiki.icon',
+ ),
+ ),
'mediawiki.action.edit.preview' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.preview.js',
'dependencies' => array(
'jquery.form',
'jquery.spinner',
+ 'mediawiki.action.history.diff',
),
),
'mediawiki.action.history' => array(
@@ -759,11 +823,24 @@ return array(
),
'mediawiki.action.view.postEdit' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.view.postEdit.js',
- 'dependencies' => 'jquery.cookie'
+ 'styles' => 'resources/mediawiki.action/mediawiki.action.view.postEdit.css',
+ 'dependencies' => array(
+ 'jquery.cookie',
+ 'mediawiki.jqueryMsg'
+ ),
+ 'messages' => array(
+ 'postedit-confirmation',
+ ),
),
'mediawiki.action.view.rightClickEdit' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js',
),
+ 'mediawiki.action.edit.editWarning' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.editWarning.js',
+ 'messages' => array(
+ 'editwarning-warning',
+ ),
+ ),
// Alias for backwards compatibility
'mediawiki.action.watch.ajax' => array(
'dependencies' => 'mediawiki.page.watch.ajax'
@@ -825,6 +902,16 @@ return array(
'targets' => array( 'desktop', 'mobile' ),
),
+ 'mediawiki.language.months' => array(
+ 'scripts' => 'resources/mediawiki.language/mediawiki.language.months.js',
+ 'dependencies' => 'mediawiki.language',
+ 'messages' => array_merge(
+ Language::$mMonthMsgs,
+ Language::$mMonthGenMsgs,
+ Language::$mMonthAbbrevMsgs
+ )
+ ),
+
/* MediaWiki Libs */
'mediawiki.libs.jpegmeta' => array(
@@ -833,6 +920,9 @@ return array(
/* MediaWiki Page */
+ 'mediawiki.page.gallery' => array(
+ 'scripts' => 'resources/mediawiki.page/mediawiki.page.gallery.js',
+ ),
'mediawiki.page.ready' => array(
'scripts' => 'resources/mediawiki.page/mediawiki.page.ready.js',
'dependencies' => array(
@@ -842,6 +932,7 @@ return array(
'jquery.mw-jump',
'mediawiki.util',
),
+ 'targets' => array( 'desktop', 'mobile' ),
),
'mediawiki.page.startup' => array(
'scripts' => 'resources/mediawiki.page/mediawiki.page.startup.js',
@@ -850,6 +941,7 @@ return array(
'mediawiki.util',
),
'position' => 'top',
+ 'targets' => array( 'desktop', 'mobile' ),
),
'mediawiki.page.patrol.ajax' => array(
'scripts' => 'resources/mediawiki.page/mediawiki.page.patrol.ajax.js',
@@ -887,6 +979,10 @@ return array(
'watcherrortext',
),
),
+ 'mediawiki.page.image.pagination' => array(
+ 'scripts' => 'resources/mediawiki.page/mediawiki.page.image.pagination.js',
+ 'dependencies' => array( 'jquery.spinner' )
+ ),
/* MediaWiki Special pages */
@@ -913,15 +1009,21 @@ return array(
),
'mediawiki.special.changeslist' => array(
'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
- 'dependencies' => array( 'jquery.makeCollapsible' ),
+ ),
+ 'mediawiki.special.changeslist.enhanced' => array(
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.enhanced.css',
),
'mediawiki.special.movePage' => array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
'dependencies' => 'jquery.byteLimit',
),
+ 'mediawiki.special.pagesWithProp' => array(
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.pagesWithProp.css',
+ ),
'mediawiki.special.preferences' => array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
+ 'position' => 'top',
),
'mediawiki.special.recentchanges' => array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.recentchanges.js',
@@ -941,7 +1043,7 @@ return array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.undelete.js',
),
'mediawiki.special.upload' => array(
- // @TODO: merge in remainder of mediawiki.legacy.upload
+ // @todo merge in remainder of mediawiki.legacy.upload
'scripts' => 'resources/mediawiki.special/mediawiki.special.upload.js',
'messages' => array(
'widthheight',
@@ -951,10 +1053,33 @@ return array(
'size-gigabytes',
'largefileserver',
),
- 'dependencies' => array( 'mediawiki.libs.jpegmeta', 'mediawiki.util' ),
+ 'dependencies' => array(
+ 'mediawiki.libs.jpegmeta',
+ 'mediawiki.util',
+ ),
+ ),
+ 'mediawiki.special.userlogin' => array(
+ 'styles' => array(
+ 'resources/mediawiki.special/mediawiki.special.vforms.css',
+ 'resources/mediawiki.special/mediawiki.special.userLogin.css',
+ ),
+ 'position' => 'top',
+ ),
+ 'mediawiki.special.createaccount' => array(
+ 'styles' => array(
+ 'resources/mediawiki.special/mediawiki.special.vforms.css',
+ 'resources/mediawiki.special/mediawiki.special.createAccount.css',
+ ),
),
- 'mediawiki.special.userlogin.signup' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.userLogin.signup.js',
+ 'mediawiki.special.createaccount.js' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.createAccount.js',
+ 'messages' => array(
+ 'createacct-captcha',
+ 'createacct-emailrequired',
+ 'createacct-imgcaptcha-ph'
+ ),
+ 'dependencies' => 'mediawiki.jqueryMsg',
+ 'position' => 'top',
),
'mediawiki.special.javaScriptTest' => array(
'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
@@ -964,6 +1089,7 @@ return array(
) ),
'dependencies' => array( 'jquery.qunit' ),
'position' => 'top',
+ 'targets' => array( 'desktop', 'mobile' ),
),
/* MediaWiki Tests */
@@ -978,6 +1104,7 @@ return array(
'mediawiki.page.ready',
),
'position' => 'top',
+ 'targets' => array( 'desktop', 'mobile' ),
),
/* MediaWiki Legacy */
@@ -998,14 +1125,9 @@ return array(
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
'mediawiki.legacy.config' => array(
+ // Used in the web installer. Test it after modifying this definition!
'scripts' => 'common/config.js',
- 'styles' => array( 'common/config.css', 'common/config-cc.css' ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.legacy.wikibits',
- ),
- 'mediawiki.legacy.IEFixes' => array(
- 'scripts' => 'common/IEFixes.js',
+ 'styles' => array( 'common/config.css' ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
'dependencies' => 'mediawiki.legacy.wikibits',
@@ -1015,12 +1137,12 @@ return array(
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
'dependencies' => array(
- 'mediawiki.legacy.wikibits',
'jquery.byteLimit',
),
'position' => 'top',
),
'mediawiki.legacy.shared' => array(
+ // Used in the web installer. Test it after modifying this definition!
'styles' => array( 'common/shared.css' => array( 'media' => 'screen' ) ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
@@ -1035,7 +1157,9 @@ return array(
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
'dependencies' => array(
- 'mediawiki.legacy.wikibits',
+ 'jquery.spinner',
+ 'mediawiki.api',
+ 'mediawiki.Title',
'mediawiki.util',
),
),
@@ -1048,9 +1172,11 @@ return array(
),
'position' => 'top',
),
- 'mediawiki.legacy.wikiprintable' => array(
- 'styles' => array( 'common/wikiprintable.css' => array( 'media' => 'print' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ 'mediawiki.ui' => array(
+ 'skinStyles' => array(
+ 'default' => 'resources/mediawiki.ui/mediawiki.ui.default.css',
+ 'vector' => 'resources/mediawiki.ui/mediawiki.ui.vector.css',
+ ),
+ 'position' => 'top',
),
);
diff --git a/resources/Resources.php.orig b/resources/Resources.php.orig
deleted file mode 100644
index 0a70c5a2..00000000
--- a/resources/Resources.php.orig
+++ /dev/null
@@ -1,968 +0,0 @@
-<?php
-
-return array(
-
- /* Special modules who have their own classes */
-
- // Scripts managed by the local wiki (stored in the MediaWiki namespace)
- 'site' => array( 'class' => 'ResourceLoaderSiteModule' ),
- 'noscript' => array( 'class' => 'ResourceLoaderNoscriptModule' ),
- 'startup' => array( 'class' => 'ResourceLoaderStartUpModule' ),
- 'filepage' => array( 'class' => 'ResourceLoaderFilePageModule' ),
- 'user.groups' => array( 'class' => 'ResourceLoaderUserGroupsModule' ),
-
- // Scripts managed by the current user (stored in their user space)
- 'user' => array( 'class' => 'ResourceLoaderUserModule' ),
-
- // Scripts generated based on the current user's preferences
- 'user.cssprefs' => array( 'class' => 'ResourceLoaderUserCSSPrefsModule' ),
-
- // Populate mediawiki.user placeholders with information about the current user
- 'user.options' => array( 'class' => 'ResourceLoaderUserOptionsModule' ),
- 'user.tokens' => array( 'class' => 'ResourceLoaderUserTokensModule' ),
-
- // Scripts for the dynamic language specific data, like grammar forms.
- 'mediawiki.language.data' => array( 'class' => 'ResourceLoaderLanguageDataModule' ),
-
- /* Skins */
-
- 'skins.chick' => array(
- 'styles' => array( 'chick/main.css' => array( 'media' => 'screen, handheld' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.cologneblue' => array(
- 'styles' => array( 'cologneblue/screen.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.modern' => array(
- 'styles' => array(
- 'modern/main.css' => array( 'media' => 'screen' ),
- 'modern/print.css' => array( 'media' => 'print' ),
- ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.monobook' => array(
- 'styles' => array(
- 'common/commonElements.css' => array( 'media' => 'screen' ),
- 'common/commonContent.css' => array( 'media' => 'screen' ),
- 'common/commonInterface.css' => array( 'media' => 'screen' ),
- 'monobook/main.css' => array( 'media' => 'screen' ),
- ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.archlinux' => array(
- 'styles' => array(
- 'common/commonElements.css' => array( 'media' => 'screen' ),
- 'common/commonContent.css' => array( 'media' => 'screen' ),
- 'common/commonInterface.css' => array( 'media' => 'screen' ),
- 'archlinux/main.css' => array( 'media' => 'screen' ),
- 'archlinux/archnavbar.css' => array( 'media' => 'screen' ),
- 'archlinux/arch.css' => array( 'media' => 'screen' ),
- 'archlinux/print.css' => array( 'media' => 'print' ),
- ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.nostalgia' => array(
- 'styles' => array( 'nostalgia/screen.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.simple' => array(
- 'styles' => array( 'simple/main.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.standard' => array(
- 'styles' => array( 'standard/main.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.vector' => array(
- // Keep in sync with WebInstallerOutput::getCSS()
- 'styles' => array(
- 'common/commonElements.css' => array( 'media' => 'screen' ),
- 'common/commonContent.css' => array( 'media' => 'screen' ),
- 'common/commonInterface.css' => array( 'media' => 'screen' ),
- 'vector/screen.css' => array( 'media' => 'screen' ),
- 'vector/screen-hd.css' => array( 'media' => 'screen and (min-width: 982px)' ),
- ),
- 'scripts' => 'vector/vector.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
-
- /* jQuery */
-
- 'jquery' => array(
- 'scripts' => 'resources/jquery/jquery.js',
- 'debugRaw' => false,
- ),
-
- /* jQuery Plugins */
-
- 'jquery.appear' => array(
- 'scripts' => 'resources/jquery/jquery.appear.js',
- ),
- 'jquery.arrowSteps' => array(
- 'scripts' => 'resources/jquery/jquery.arrowSteps.js',
- 'styles' => 'resources/jquery/jquery.arrowSteps.css',
- ),
- 'jquery.async' => array(
- 'scripts' => 'resources/jquery/jquery.async.js',
- ),
- 'jquery.autoEllipsis' => array(
- 'scripts' => 'resources/jquery/jquery.autoEllipsis.js',
- 'dependencies' => 'jquery.highlightText',
- ),
- 'jquery.badge' => array(
- 'scripts' => 'resources/jquery/jquery.badge.js',
- 'styles' => 'resources/jquery/jquery.badge.css',
- ),
- 'jquery.byteLength' => array(
- 'scripts' => 'resources/jquery/jquery.byteLength.js',
- ),
- 'jquery.byteLimit' => array(
- 'scripts' => 'resources/jquery/jquery.byteLimit.js',
- 'dependencies' => 'jquery.byteLength',
- ),
- 'jquery.checkboxShiftClick' => array(
- 'scripts' => 'resources/jquery/jquery.checkboxShiftClick.js',
- ),
- 'jquery.client' => array(
- 'scripts' => 'resources/jquery/jquery.client.js',
- ),
- 'jquery.collapsibleTabs' => array(
- 'scripts' => 'resources/jquery/jquery.collapsibleTabs.js',
- ),
- 'jquery.color' => array(
- 'scripts' => 'resources/jquery/jquery.color.js',
- 'dependencies' => 'jquery.colorUtil',
- ),
- 'jquery.colorUtil' => array(
- 'scripts' => 'resources/jquery/jquery.colorUtil.js',
- ),
- 'jquery.cookie' => array(
- 'scripts' => 'resources/jquery/jquery.cookie.js',
- ),
- 'jquery.delayedBind' => array(
- 'scripts' => 'resources/jquery/jquery.delayedBind.js',
- ),
- 'jquery.expandableField' => array(
- 'scripts' => 'resources/jquery/jquery.expandableField.js',
- 'dependencies' => 'jquery.delayedBind',
- ),
- 'jquery.farbtastic' => array(
- 'scripts' => 'resources/jquery/jquery.farbtastic.js',
- 'styles' => 'resources/jquery/jquery.farbtastic.css',
- 'dependencies' => 'jquery.colorUtil',
- ),
- 'jquery.footHovzer' => array(
- 'scripts' => 'resources/jquery/jquery.footHovzer.js',
- 'styles' => 'resources/jquery/jquery.footHovzer.css',
- ),
- 'jquery.form' => array(
- 'scripts' => 'resources/jquery/jquery.form.js',
- ),
- 'jquery.getAttrs' => array(
- 'scripts' => 'resources/jquery/jquery.getAttrs.js',
- ),
- 'jquery.highlightText' => array(
- 'scripts' => 'resources/jquery/jquery.highlightText.js',
- 'dependencies' => 'jquery.mwExtension',
- ),
- 'jquery.hoverIntent' => array(
- 'scripts' => 'resources/jquery/jquery.hoverIntent.js',
- ),
- 'jquery.json' => array(
- 'scripts' => 'resources/jquery/jquery.json.js',
- ),
- 'jquery.localize' => array(
- 'scripts' => 'resources/jquery/jquery.localize.js',
- ),
- 'jquery.makeCollapsible' => array(
- 'scripts' => 'resources/jquery/jquery.makeCollapsible.js',
- 'styles' => 'resources/jquery/jquery.makeCollapsible.css',
- 'messages' => array( 'collapsible-expand', 'collapsible-collapse' ),
- ),
- 'jquery.mockjax' => array(
- 'scripts' => 'resources/jquery/jquery.mockjax.js',
- ),
- 'jquery.mw-jump' => array(
- 'scripts' => 'resources/jquery/jquery.mw-jump.js',
- ),
- 'jquery.mwExtension' => array(
- 'scripts' => 'resources/jquery/jquery.mwExtension.js',
- ),
- 'jquery.placeholder' => array(
- 'scripts' => 'resources/jquery/jquery.placeholder.js',
- ),
- 'jquery.qunit' => array(
- 'scripts' => 'resources/jquery/jquery.qunit.js',
- 'styles' => 'resources/jquery/jquery.qunit.css',
- 'position' => 'top',
- ),
- 'jquery.qunit.completenessTest' => array(
- 'scripts' => 'resources/jquery/jquery.qunit.completenessTest.js',
- 'dependencies' => 'jquery.qunit',
- ),
- 'jquery.spinner' => array(
- 'scripts' => 'resources/jquery/jquery.spinner.js',
- 'styles' => 'resources/jquery/jquery.spinner.css',
- ),
- 'jquery.jStorage' => array(
- 'scripts' => 'resources/jquery/jquery.jStorage.js',
- 'dependencies' => 'jquery.json',
- ),
- 'jquery.suggestions' => array(
- 'scripts' => 'resources/jquery/jquery.suggestions.js',
- 'styles' => 'resources/jquery/jquery.suggestions.css',
- 'dependencies' => 'jquery.autoEllipsis',
- ),
- 'jquery.tabIndex' => array(
- 'scripts' => 'resources/jquery/jquery.tabIndex.js',
- ),
- 'jquery.tablesorter' => array(
- 'scripts' => 'resources/jquery/jquery.tablesorter.js',
- 'styles' => 'resources/jquery/jquery.tablesorter.css',
- 'messages' => array( 'sort-descending', 'sort-ascending' ),
- 'dependencies' => 'jquery.mwExtension',
- ),
- 'jquery.textSelection' => array(
- 'scripts' => 'resources/jquery/jquery.textSelection.js',
- 'dependencies' => 'jquery.client',
- ),
- 'jquery.validate' => array(
- 'scripts' => 'resources/jquery/jquery.validate.js',
- ),
- 'jquery.xmldom' => array(
- 'scripts' => 'resources/jquery/jquery.xmldom.js',
- ),
-
- /* jQuery Tipsy */
-
- 'jquery.tipsy' => array(
- 'scripts' => 'resources/jquery.tipsy/jquery.tipsy.js',
- 'styles' => 'resources/jquery.tipsy/jquery.tipsy.css',
- ),
-
- /* jQuery UI */
-
- // Core
- 'jquery.ui.core' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.core.js',
- 'skinStyles' => array(
- 'default' => array(
- 'resources/jquery.ui/themes/default/jquery.ui.core.css',
- 'resources/jquery.ui/themes/default/jquery.ui.theme.css',
- ),
- 'vector' => array(
- 'resources/jquery.ui/themes/vector/jquery.ui.core.css',
- 'resources/jquery.ui/themes/vector/jquery.ui.theme.css',
- ),
- ),
- 'dependencies' => 'jquery',
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.widget' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.widget.js',
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.mouse' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.mouse.js',
- 'dependencies' => 'jquery.ui.widget',
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.position' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.position.js',
- 'group' => 'jquery.ui',
- ),
- // Interactions
- 'jquery.ui.draggable' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.draggable.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.mouse', 'jquery.ui.widget' ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.droppable' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.droppable.js',
- 'dependencies' => array(
- 'jquery.ui.core', 'jquery.ui.mouse', 'jquery.ui.widget', 'jquery.ui.draggable',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.resizable' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.resizable.js',
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.resizable.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.resizable.css',
- ),
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget', 'jquery.ui.mouse' ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.selectable' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.selectable.js',
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.selectable.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.selectable.css',
- ),
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget', 'jquery.ui.mouse' ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.sortable' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.sortable.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget', 'jquery.ui.mouse' ),
- 'group' => 'jquery.ui',
- ),
- // Widgets
- 'jquery.ui.accordion' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.accordion.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.accordion.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.accordion.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.autocomplete' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.autocomplete.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget', 'jquery.ui.position' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.autocomplete.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.autocomplete.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.button' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.button.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.button.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.button.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.datepicker' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.datepicker.js',
- 'dependencies' => 'jquery.ui.core',
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.datepicker.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.datepicker.css',
- ),
- 'languageScripts' => array(
- 'af' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-af.js',
- 'ar' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js',
- 'az' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-az.js',
- 'bg' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js',
- 'bs' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js',
- 'ca' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ca.js',
- 'cs' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js',
- 'da' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-da.js',
- 'de' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-de.js',
- 'el' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-el.js',
- 'en-gb' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js',
- 'eo' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js',
- 'es' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-es.js',
- 'et' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-et.js',
- 'eu' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js',
- 'fa' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js',
- 'fi' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fi.js',
- 'fo' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js',
- 'fr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js',
- 'gl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js',
- 'he' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-he.js',
- 'hi' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hi.js',
- 'hr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js',
- 'hu' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js',
- 'hy' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hy.js',
- 'id' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-id.js',
- 'is' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-is.js',
- 'it' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-it.js',
- 'ja' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js',
- 'ka' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ka.js',
- 'kk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js',
- 'km' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-km.js',
- 'ko' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ko.js',
- 'lb' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js',
- 'lt' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lt.js',
- 'lv' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lv.js',
- 'mk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js',
- 'ml' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js',
- 'ms' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ms.js',
- 'nl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js',
- 'no' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-no.js',
- 'pl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pl.js',
- 'pt' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js',
- 'pt-br' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js',
- 'rm' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js',
- 'ro' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js',
- 'ru' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js',
- 'sk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js',
- 'sl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sl.js',
- 'sq' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js',
- 'sr-sr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js',
- 'sr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js',
- 'sv' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js',
- 'ta' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js',
- 'th' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-th.js',
- 'tr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-tr.js',
- 'uk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js',
- 'vi' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js',
- 'zh-cn' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-zh-CN.js',
- 'zh-hk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-zh-HK.js',
- 'zh-tw' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.dialog' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.dialog.js',
- 'dependencies' => array(
- 'jquery.ui.core',
- 'jquery.ui.widget',
- 'jquery.ui.button',
- 'jquery.ui.draggable',
- 'jquery.ui.mouse',
- 'jquery.ui.position',
- 'jquery.ui.resizable',
- ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.dialog.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.dialog.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.progressbar' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.progressbar.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.progressbar.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.progressbar.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.slider' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.slider.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget', 'jquery.ui.mouse' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.slider.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.slider.css',
- ),
- 'group' => 'jquery.ui',
- ),
- 'jquery.ui.tabs' => array(
- 'scripts' => 'resources/jquery.ui/jquery.ui.tabs.js',
- 'dependencies' => array( 'jquery.ui.core', 'jquery.ui.widget' ),
- 'skinStyles' => array(
- 'default' => 'resources/jquery.ui/themes/default/jquery.ui.tabs.css',
- 'vector' => 'resources/jquery.ui/themes/vector/jquery.ui.tabs.css',
- ),
- 'group' => 'jquery.ui',
- ),
- // Effects
- 'jquery.effects.core' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.core.js',
- 'dependencies' => 'jquery',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.blind' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.blind.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.bounce' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.bounce.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.clip' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.clip.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.drop' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.drop.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.explode' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.explode.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.fade' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.fade.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.fold' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.fold.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.highlight' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.highlight.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.pulsate' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.pulsate.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.scale' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.scale.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.shake' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.shake.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.slide' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.slide.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
- 'jquery.effects.transfer' => array(
- 'scripts' => 'resources/jquery.effects/jquery.effects.transfer.js',
- 'dependencies' => 'jquery.effects.core',
- 'group' => 'jquery.ui',
- ),
-
- /* MediaWiki */
-
- 'mediawiki' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.js',
- 'debugScripts' => 'resources/mediawiki/mediawiki.log.js',
- 'debugRaw' => false,
- ),
- 'mediawiki.api' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.js',
- 'dependencies' => 'mediawiki.util',
- ),
- 'mediawiki.api.category' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.category.js',
- 'dependencies' => array(
- 'mediawiki.api',
- 'mediawiki.Title',
- ),
- ),
- 'mediawiki.api.edit' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.edit.js',
- 'dependencies' => array(
- 'mediawiki.api',
- 'mediawiki.Title',
- ),
- ),
- 'mediawiki.api.parse' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.parse.js',
- 'dependencies' => 'mediawiki.api',
- ),
- 'mediawiki.api.titleblacklist' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.titleblacklist.js',
- 'dependencies' => array(
- 'mediawiki.api',
- 'mediawiki.Title',
- ),
- ),
- 'mediawiki.api.watch' => array(
- 'scripts' => 'resources/mediawiki.api/mediawiki.api.watch.js',
- 'dependencies' => array(
- 'mediawiki.api',
- 'user.tokens',
- ),
- ),
- 'mediawiki.debug' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.debug.js',
- 'styles' => 'resources/mediawiki/mediawiki.debug.css',
- 'dependencies' => 'jquery.footHovzer',
- 'position' => 'bottom',
- ),
- 'mediawiki.debug.init' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.debug.init.js',
- 'dependencies' => 'mediawiki.debug',
- // Uses a custom mw.config variable that is set in debughtml,
- // must be loaded on the bottom
- 'position' => 'bottom',
- ),
- 'mediawiki.feedback' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.feedback.js',
- 'styles' => 'resources/mediawiki/mediawiki.feedback.css',
- 'dependencies' => array(
- 'mediawiki.api.edit',
- 'mediawiki.Title',
- 'mediawiki.jqueryMsg',
- 'jquery.ui.dialog',
- ),
- 'messages' => array(
- 'feedback-bugornote',
- 'feedback-subject',
- 'feedback-message',
- 'feedback-cancel',
- 'feedback-submit',
- 'feedback-adding',
- 'feedback-error1',
- 'feedback-error2',
- 'feedback-error3',
- 'feedback-thanks',
- 'feedback-close',
- 'feedback-bugcheck',
- 'feedback-bugnew',
- ),
- ),
- 'mediawiki.htmlform' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.htmlform.js',
- ),
- 'mediawiki.notification' => array(
- 'styles' => 'resources/mediawiki/mediawiki.notification.css',
- 'scripts' => 'resources/mediawiki/mediawiki.notification.js',
- 'dependencies' => array(
- 'mediawiki.page.startup',
- ),
- ),
- 'mediawiki.notify' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.notify.js',
- ),
- 'mediawiki.searchSuggest' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.searchSuggest.js',
- 'messages' => array(
- 'searchsuggest-search',
- 'searchsuggest-containing',
- ),
- 'dependencies' => array(
- 'jquery.autoEllipsis',
- 'jquery.client',
- 'jquery.placeholder',
- 'jquery.suggestions',
- ),
- ),
- 'mediawiki.Title' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.Title.js',
- 'dependencies' => 'mediawiki.util',
- ),
- 'mediawiki.Uri' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.Uri.js',
- ),
- 'mediawiki.user' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.user.js',
- 'dependencies' => array(
- 'jquery.cookie',
- 'mediawiki.api',
- ),
- ),
- 'mediawiki.util' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.util.js',
- 'dependencies' => array(
- 'jquery.client',
- 'jquery.cookie',
- 'jquery.mwExtension',
- 'mediawiki.notify',
- ),
- 'messages' => array( 'showtoc', 'hidetoc' ),
- 'position' => 'top', // For $wgPreloadJavaScriptMwUtil
- ),
-
- /* MediaWiki Action */
-
- 'mediawiki.action.edit' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.js',
- 'dependencies' => array(
- 'jquery.textSelection',
- 'jquery.byteLimit',
- ),
- 'position' => 'top',
- ),
- 'mediawiki.action.edit.preview' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.preview.js',
- 'dependencies' => array(
- 'jquery.form',
- 'jquery.spinner',
- ),
- ),
- 'mediawiki.action.history' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js',
- 'group' => 'mediawiki.action.history',
- ),
- 'mediawiki.action.history.diff' => array(
- 'styles' => 'resources/mediawiki.action/mediawiki.action.history.diff.css',
- 'group' => 'mediawiki.action.history',
- ),
- 'mediawiki.action.view.dblClickEdit' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js',
- 'dependencies' => 'mediawiki.util',
- ),
- 'mediawiki.action.view.metadata' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.metadata.js',
- 'messages' => array(
- 'metadata-expand',
- 'metadata-collapse',
- ),
- ),
- 'mediawiki.action.view.rightClickEdit' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js',
- ),
- // Alias for backwards compatibility
- 'mediawiki.action.watch.ajax' => array(
- 'dependencies' => 'mediawiki.page.watch.ajax'
- ),
-
- /* MediaWiki Language */
-
- 'mediawiki.language' => array(
- 'scripts' => 'resources/mediawiki.language/mediawiki.language.js',
- 'languageScripts' => array(
- 'bs' => 'resources/mediawiki.language/languages/bs.js',
- 'dsb' => 'resources/mediawiki.language/languages/dsb.js',
- 'fi' => 'resources/mediawiki.language/languages/fi.js',
- 'ga' => 'resources/mediawiki.language/languages/ga.js',
- 'he' => 'resources/mediawiki.language/languages/he.js',
- 'hsb' => 'resources/mediawiki.language/languages/hsb.js',
- 'hu' => 'resources/mediawiki.language/languages/hu.js',
- 'hy' => 'resources/mediawiki.language/languages/hy.js',
- 'la' => 'resources/mediawiki.language/languages/la.js',
- 'os' => 'resources/mediawiki.language/languages/os.js',
- 'ru' => 'resources/mediawiki.language/languages/ru.js',
- 'sl' => 'resources/mediawiki.language/languages/sl.js',
- 'uk' => 'resources/mediawiki.language/languages/uk.js',
- ),
- 'dependencies' => array(
- 'mediawiki.language.data',
- 'mediawiki.cldr'
- ),
- ),
-
- 'mediawiki.cldr' => array(
- 'scripts' => 'resources/mediawiki.language/mediawiki.cldr.js',
- 'dependencies' => array(
- 'mediawiki.libs.pluralruleparser',
- ),
- ),
-
- 'mediawiki.libs.pluralruleparser' => array(
- 'scripts' => 'resources/mediawiki.libs/CLDRPluralRuleParser.js',
- ),
-
- 'mediawiki.language.init' => array(
- 'scripts' => 'resources/mediawiki.language/mediawiki.language.init.js',
- ),
-
- 'mediawiki.jqueryMsg' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.jqueryMsg.js',
- 'dependencies' => array(
- 'mediawiki.util',
- 'mediawiki.language',
- ),
- ),
-
- /* MediaWiki Libs */
-
- 'mediawiki.libs.jpegmeta' => array(
- 'scripts' => 'resources/mediawiki.libs/mediawiki.libs.jpegmeta.js',
- ),
-
- /* MediaWiki Page */
-
- 'mediawiki.page.ready' => array(
- 'scripts' => 'resources/mediawiki.page/mediawiki.page.ready.js',
- 'dependencies' => array(
- 'jquery.checkboxShiftClick',
- 'jquery.makeCollapsible',
- 'jquery.placeholder',
- 'jquery.mw-jump',
- 'mediawiki.util',
- ),
- ),
- 'mediawiki.page.startup' => array(
- 'scripts' => 'resources/mediawiki.page/mediawiki.page.startup.js',
- 'dependencies' => array(
- 'jquery.client',
- 'mediawiki.util',
- ),
- 'position' => 'top',
- ),
- 'mediawiki.page.watch.ajax' => array(
- 'scripts' => 'resources/mediawiki.page/mediawiki.page.watch.ajax.js',
- 'dependencies' => array(
- 'mediawiki.page.startup',
- 'mediawiki.api.watch',
- 'mediawiki.util',
- 'mediawiki.notify',
- 'jquery.mwExtension',
- ),
- 'messages' => array(
- 'watch',
- 'unwatch',
- 'watching',
- 'unwatching',
- 'tooltip-ca-watch',
- 'tooltip-ca-unwatch',
- 'watcherrortext',
- ),
- ),
-
- /* MediaWiki Special pages */
-
- 'mediawiki.special' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.css',
- ),
- 'mediawiki.special.block' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.block.js',
- 'dependencies' => array(
- 'mediawiki.util',
- ),
- ),
- 'mediawiki.special.changeemail' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.changeemail.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.changeemail.css',
- 'dependencies' => array(
- 'mediawiki.util',
- ),
- 'messages' => array(
- 'email-address-validity-valid',
- 'email-address-validity-invalid',
- ),
- ),
- 'mediawiki.special.changeslist' => array(
- 'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
- 'dependencies' => array( 'jquery.makeCollapsible' ),
- ),
- 'mediawiki.special.movePage' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
- 'dependencies' => 'jquery.byteLimit',
- ),
- 'mediawiki.special.preferences' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
- ),
- 'mediawiki.special.recentchanges' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.recentchanges.js',
- 'dependencies' => array( 'mediawiki.special' ),
- 'position' => 'top',
- ),
- 'mediawiki.special.search' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.search.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.search.css',
- 'messages' => array(
- 'powersearch-togglelabel',
- 'powersearch-toggleall',
- 'powersearch-togglenone',
- ),
- ),
- 'mediawiki.special.undelete' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.undelete.js',
- ),
- 'mediawiki.special.upload' => array(
- // @TODO: merge in remainder of mediawiki.legacy.upload
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.upload.js',
- 'messages' => array(
- 'widthheight',
- 'size-bytes',
- 'size-kilobytes',
- 'size-megabytes',
- 'size-gigabytes',
- 'largefileserver',
- ),
- 'dependencies' => array( 'mediawiki.libs.jpegmeta', 'mediawiki.util' ),
- ),
- 'mediawiki.special.javaScriptTest' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
- 'messages' => array_merge( Skin::getSkinNameMessages(), array(
- 'colon-separator',
- 'javascripttest-pagetext-skins',
- ) ),
- 'dependencies' => array( 'jquery.qunit' ),
- 'position' => 'top',
- ),
-
- /* MediaWiki Tests */
-
- 'mediawiki.tests.qunit.testrunner' => array(
- 'scripts' => 'tests/qunit/data/testrunner.js',
- 'dependencies' => array(
- 'jquery.qunit',
- 'jquery.qunit.completenessTest',
- 'mediawiki.page.startup',
- 'mediawiki.page.ready',
- ),
- 'position' => 'top',
- ),
-
- /* MediaWiki Legacy */
-
- 'mediawiki.legacy.ajax' => array(
- 'scripts' => 'common/ajax.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => array(
- 'mediawiki.util',
- 'mediawiki.legacy.wikibits',
- ),
- 'position' => 'top', // Temporary hack for legacy support
- ),
- 'mediawiki.legacy.commonPrint' => array(
- 'styles' => array( 'common/commonPrint.css' => array( 'media' => 'print' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'mediawiki.legacy.config' => array(
- 'scripts' => 'common/config.js',
- 'styles' => array( 'common/config.css', 'common/config-cc.css' ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.legacy.wikibits',
- ),
- 'mediawiki.legacy.IEFixes' => array(
- 'scripts' => 'common/IEFixes.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.legacy.wikibits',
- ),
- 'mediawiki.legacy.protect' => array(
- 'scripts' => 'common/protect.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => array(
- 'mediawiki.legacy.wikibits',
- 'jquery.byteLimit',
- ),
- 'position' => 'top',
- ),
- 'mediawiki.legacy.shared' => array(
- 'styles' => array( 'common/shared.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'mediawiki.legacy.oldshared' => array(
- 'styles' => array( 'common/oldshared.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'mediawiki.legacy.upload' => array(
- 'scripts' => 'common/upload.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => array(
- 'mediawiki.legacy.wikibits',
- 'mediawiki.util',
- ),
- ),
- 'mediawiki.legacy.wikibits' => array(
- 'scripts' => 'common/wikibits.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => array(
- 'mediawiki.util',
- ),
- 'position' => 'top',
- ),
- 'mediawiki.legacy.wikiprintable' => array(
- 'styles' => array( 'common/wikiprintable.css' => array( 'media' => 'print' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
-);
diff --git a/resources/jquery.chosen/LICENSE b/resources/jquery.chosen/LICENSE
new file mode 100644
index 00000000..0675dc52
--- /dev/null
+++ b/resources/jquery.chosen/LICENSE
@@ -0,0 +1,24 @@
+# Chosen, a Select Box Enhancer for jQuery and Protoype
+## by Patrick Filler for [Harvest](http://getharvest.com)
+
+Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
+
+Copyright (c) 2011-2013 by Harvest
+
+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/resources/jquery.chosen/chosen-sprite.png b/resources/jquery.chosen/chosen-sprite.png
new file mode 100644
index 00000000..3611ae4a
--- /dev/null
+++ b/resources/jquery.chosen/chosen-sprite.png
Binary files differ
diff --git a/resources/jquery.chosen/chosen-sprite@2x.png b/resources/jquery.chosen/chosen-sprite@2x.png
new file mode 100644
index 00000000..bd61d963
--- /dev/null
+++ b/resources/jquery.chosen/chosen-sprite@2x.png
Binary files differ
diff --git a/resources/jquery.chosen/chosen.css b/resources/jquery.chosen/chosen.css
new file mode 100644
index 00000000..17793ed7
--- /dev/null
+++ b/resources/jquery.chosen/chosen.css
@@ -0,0 +1,440 @@
+/* @group Base */
+.chzn-container {
+ font-size: 13px;
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ zoom: 1;
+ *display: inline;
+}
+.chzn-container .chzn-drop {
+ background: #fff;
+ border: 1px solid #aaa;
+ border-top: 0;
+ position: absolute;
+ top: 100%;
+ left: -9999px;
+ -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
+ -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
+ box-shadow : 0 4px 5px rgba(0,0,0,.15);
+ z-index: 1010;
+ width: 100%;
+ -moz-box-sizing : border-box;
+ -ms-box-sizing : border-box;
+ -webkit-box-sizing: border-box;
+ -khtml-box-sizing : border-box;
+ box-sizing : border-box;
+}
+
+.chzn-container.chzn-with-drop .chzn-drop {
+ left: 0;
+}
+
+/* @end */
+
+/* @group Single Chosen */
+.chzn-container-single .chzn-single {
+ background-color: #ffffff;
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
+ background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
+ background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
+ background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
+ background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
+ -webkit-border-radius: 5px;
+ -moz-border-radius : 5px;
+ border-radius : 5px;
+ -moz-background-clip : padding;
+ -webkit-background-clip: padding-box;
+ background-clip : padding-box;
+ border: 1px solid #aaaaaa;
+ -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
+ -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
+ box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ position: relative;
+ height: 23px;
+ line-height: 24px;
+ padding: 0 0 0 8px;
+ color: #444444;
+ text-decoration: none;
+}
+.chzn-container-single .chzn-default {
+ color: #999;
+}
+.chzn-container-single .chzn-single span {
+ margin-right: 26px;
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ -o-text-overflow: ellipsis;
+ -ms-text-overflow: ellipsis;
+ text-overflow: ellipsis;
+}
+.chzn-container-single .chzn-single abbr {
+ display: block;
+ position: absolute;
+ right: 26px;
+ top: 6px;
+ width: 12px;
+ height: 12px;
+ font-size: 1px;
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
+}
+.chzn-container-single .chzn-single abbr:hover {
+ background-position: -42px -10px;
+}
+.chzn-container-single.chzn-disabled .chzn-single abbr:hover {
+ background-position: -42px -10px;
+}
+.chzn-container-single .chzn-single div {
+ position: absolute;
+ right: 0;
+ top: 0;
+ display: block;
+ height: 100%;
+ width: 18px;
+}
+.chzn-container-single .chzn-single div b {
+ background: url('chosen-sprite.png') no-repeat 0px 2px;
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+.chzn-container-single .chzn-search {
+ padding: 3px 4px;
+ position: relative;
+ margin: 0;
+ white-space: nowrap;
+ z-index: 1010;
+}
+.chzn-container-single .chzn-search input {
+ background: #fff url('chosen-sprite.png') no-repeat 100% -20px;
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(#eeeeee 1%, #ffffff 15%);
+ margin: 1px 0;
+ padding: 4px 20px 4px 5px;
+ outline: 0;
+ border: 1px solid #aaa;
+ font-family: sans-serif;
+ font-size: 1em;
+ width: 100%;
+ -moz-box-sizing : border-box;
+ -ms-box-sizing : border-box;
+ -webkit-box-sizing: border-box;
+ -khtml-box-sizing : border-box;
+ box-sizing : border-box;
+}
+.chzn-container-single .chzn-drop {
+ margin-top: -1px;
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius : 0 0 4px 4px;
+ border-radius : 0 0 4px 4px;
+ -moz-background-clip : padding;
+ -webkit-background-clip: padding-box;
+ background-clip : padding-box;
+}
+.chzn-container-single-nosearch .chzn-search {
+ position: absolute;
+ left: -9999px;
+}
+/* @end */
+
+/* @group Multi Chosen */
+.chzn-container-multi .chzn-choices {
+ background-color: #fff;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
+ background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background-image: linear-gradient(#eeeeee 1%, #ffffff 15%);
+ border: 1px solid #aaa;
+ margin: 0;
+ padding: 0;
+ cursor: text;
+ overflow: hidden;
+ height: auto !important;
+ height: 1%;
+ position: relative;
+ width: 100%;
+ -moz-box-sizing : border-box;
+ -ms-box-sizing : border-box;
+ -webkit-box-sizing: border-box;
+ -khtml-box-sizing : border-box;
+ box-sizing : border-box;
+}
+.chzn-container-multi .chzn-choices li {
+ float: left;
+ list-style: none;
+}
+.chzn-container-multi .chzn-choices .search-field {
+ white-space: nowrap;
+ margin: 0;
+ padding: 0;
+}
+.chzn-container-multi .chzn-choices .search-field input {
+ color: #666;
+ background: transparent !important;
+ border: 0 !important;
+ font-family: sans-serif;
+ font-size: 100%;
+ height: 15px;
+ padding: 5px;
+ margin: 1px 0;
+ outline: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow : none;
+ box-shadow : none;
+}
+.chzn-container-multi .chzn-choices .search-field .default {
+ color: #999;
+}
+.chzn-container-multi .chzn-choices .search-choice {
+ -webkit-border-radius: 3px;
+ -moz-border-radius : 3px;
+ border-radius : 3px;
+ -moz-background-clip : padding;
+ -webkit-background-clip: padding-box;
+ background-clip : padding-box;
+ background-color: #e4e4e4;
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+ -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+ box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+ color: #333;
+ border: 1px solid #aaaaaa;
+ line-height: 13px;
+ padding: 3px 20px 3px 5px;
+ margin: 3px 0 3px 5px;
+ position: relative;
+ cursor: default;
+}
+.chzn-container-multi .chzn-choices .search-choice.search-choice-disabled {
+ background-color: #e4e4e4;
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+ color: #666;
+ border: 1px solid #cccccc;
+ padding-right: 5px;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus {
+ background: #d4d4d4;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close {
+ display: block;
+ position: absolute;
+ right: 3px;
+ top: 4px;
+ width: 12px;
+ height: 12px;
+ font-size: 1px;
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
+ background-position: -42px -10px;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
+ background-position: -42px -10px;
+}
+/* @end */
+
+/* @group Results */
+.chzn-container .chzn-results {
+ margin: 0 4px 4px 0;
+ max-height: 240px;
+ padding: 0 0 0 4px;
+ position: relative;
+ overflow-x: hidden;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+}
+.chzn-container-multi .chzn-results {
+ margin: 0;
+ padding: 0;
+}
+.chzn-container .chzn-results li {
+ display: none;
+ line-height: 15px;
+ padding: 5px 6px;
+ margin: 0;
+ list-style: none;
+}
+.chzn-container .chzn-results .active-result {
+ cursor: pointer;
+ display: list-item;
+}
+.chzn-container .chzn-results .highlighted {
+ background-color: #3875d7;
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 );
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
+ background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
+ background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
+ background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
+ background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
+ color: #fff;
+}
+.chzn-container .chzn-results li em {
+ background: #feffde;
+ font-style: normal;
+}
+.chzn-container .chzn-results .highlighted em {
+ background: transparent;
+}
+.chzn-container .chzn-results .no-results {
+ background: #f4f4f4;
+ display: list-item;
+}
+.chzn-container .chzn-results .group-result {
+ cursor: default;
+ color: #999;
+ font-weight: bold;
+}
+.chzn-container .chzn-results .group-option {
+ padding-left: 15px;
+}
+.chzn-container-multi .chzn-drop .result-selected {
+ display: none;
+}
+.chzn-container .chzn-results-scroll {
+ background: white;
+ margin: 0 4px;
+ position: absolute;
+ text-align: center;
+ width: 321px; /* This should by dynamic with js */
+ z-index: 1;
+}
+.chzn-container .chzn-results-scroll span {
+ display: inline-block;
+ height: 17px;
+ text-indent: -5000px;
+ width: 9px;
+}
+.chzn-container .chzn-results-scroll-down {
+ bottom: 0;
+}
+.chzn-container .chzn-results-scroll-down span {
+ background: url('chosen-sprite.png') no-repeat -4px -3px;
+}
+.chzn-container .chzn-results-scroll-up span {
+ background: url('chosen-sprite.png') no-repeat -22px -3px;
+}
+/* @end */
+
+/* @group Active */
+.chzn-container-active .chzn-single {
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
+ border: 1px solid #5897fb;
+}
+.chzn-container-active.chzn-with-drop .chzn-single {
+ border: 1px solid #aaa;
+ -webkit-box-shadow: 0 1px 0 #fff inset;
+ -moz-box-shadow : 0 1px 0 #fff inset;
+ box-shadow : 0 1px 0 #fff inset;
+ background-color: #eee;
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
+ background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
+ background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
+ background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
+ background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
+ -webkit-border-bottom-left-radius : 0;
+ -webkit-border-bottom-right-radius: 0;
+ -moz-border-radius-bottomleft : 0;
+ -moz-border-radius-bottomright: 0;
+ border-bottom-left-radius : 0;
+ border-bottom-right-radius: 0;
+}
+.chzn-container-active.chzn-with-drop .chzn-single div {
+ background: transparent;
+ border-left: none;
+}
+.chzn-container-active.chzn-with-drop .chzn-single div b {
+ background-position: -18px 2px;
+}
+.chzn-container-active .chzn-choices {
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
+ border: 1px solid #5897fb;
+}
+.chzn-container-active .chzn-choices .search-field input {
+ color: #111 !important;
+}
+/* @end */
+
+/* @group Disabled Support */
+.chzn-disabled {
+ cursor: default;
+ opacity:0.5 !important;
+}
+.chzn-disabled .chzn-single {
+ cursor: default;
+}
+.chzn-disabled .chzn-choices .search-choice .search-choice-close {
+ cursor: default;
+}
+
+/* @group Right to Left */
+.chzn-rtl { text-align: right; }
+.chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; }
+.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; }
+
+.chzn-rtl .chzn-single div { left: 3px; right: auto; }
+.chzn-rtl .chzn-single abbr {
+ left: 26px;
+ right: auto;
+}
+.chzn-rtl .chzn-choices .search-field input { direction: rtl; }
+.chzn-rtl .chzn-choices li { float: right; }
+.chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; }
+.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; }
+.chzn-rtl .chzn-search { left: 9999px; }
+.chzn-rtl.chzn-with-drop .chzn-search { left: 0px; }
+.chzn-rtl .chzn-drop { left: 9999px; }
+.chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; }
+.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; }
+.chzn-rtl.chzn-container-active.chzn-with-drop .chzn-single div { border-right: none; }
+.chzn-rtl .chzn-search input {
+ background: #fff url('chosen-sprite.png') no-repeat -30px -20px;
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+ background: url('chosen-sprite.png') no-repeat -30px -20px, linear-gradient(#eeeeee 1%, #ffffff 15%);
+ padding: 4px 5px 4px 20px;
+ direction: rtl;
+}
+.chzn-container-single.chzn-rtl .chzn-single div b {
+ background-position: 6px 2px;
+}
+.chzn-container-single.chzn-rtl.chzn-with-drop .chzn-single div b {
+ background-position: -12px 2px;
+}
+/* @end */
+
+/* @group Retina compatibility */
+@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) {
+ .chzn-rtl .chzn-search input, .chzn-container-single .chzn-single abbr, .chzn-container-single .chzn-single div b, .chzn-container-single .chzn-search input, .chzn-container-multi .chzn-choices .search-choice .search-choice-close, .chzn-container .chzn-results-scroll-down span, .chzn-container .chzn-results-scroll-up span {
+ background-image: url('chosen-sprite@2x.png') !important;
+ background-repeat: no-repeat !important;
+ background-size: 52px 37px !important;
+ }
+}
+/* @end */
diff --git a/resources/jquery.chosen/chosen.jquery.js b/resources/jquery.chosen/chosen.jquery.js
new file mode 100644
index 00000000..745174f7
--- /dev/null
+++ b/resources/jquery.chosen/chosen.jquery.js
@@ -0,0 +1,1103 @@
+// Chosen, a Select Box Enhancer for jQuery and Protoype
+// by Patrick Filler for Harvest, http://getharvest.com
+//
+// Version 0.9.14
+// Full source at https://github.com/harvesthq/chosen
+// Copyright (c) 2011 Harvest http://getharvest.com
+
+// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
+// This file is generated by `cake build`, do not edit it by hand.
+(function() {
+ var SelectParser;
+
+ SelectParser = (function() {
+
+ function SelectParser() {
+ this.options_index = 0;
+ this.parsed = [];
+ }
+
+ SelectParser.prototype.add_node = function(child) {
+ if (child.nodeName.toUpperCase() === "OPTGROUP") {
+ return this.add_group(child);
+ } else {
+ return this.add_option(child);
+ }
+ };
+
+ SelectParser.prototype.add_group = function(group) {
+ var group_position, option, _i, _len, _ref, _results;
+ group_position = this.parsed.length;
+ this.parsed.push({
+ array_index: group_position,
+ group: true,
+ label: group.label,
+ children: 0,
+ disabled: group.disabled
+ });
+ _ref = group.childNodes;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ option = _ref[_i];
+ _results.push(this.add_option(option, group_position, group.disabled));
+ }
+ return _results;
+ };
+
+ SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
+ if (option.nodeName.toUpperCase() === "OPTION") {
+ if (option.text !== "") {
+ if (group_position != null) {
+ this.parsed[group_position].children += 1;
+ }
+ this.parsed.push({
+ array_index: this.parsed.length,
+ options_index: this.options_index,
+ value: option.value,
+ text: option.text,
+ html: option.innerHTML,
+ selected: option.selected,
+ disabled: group_disabled === true ? group_disabled : option.disabled,
+ group_array_index: group_position,
+ classes: option.className,
+ style: option.style.cssText
+ });
+ } else {
+ this.parsed.push({
+ array_index: this.parsed.length,
+ options_index: this.options_index,
+ empty: true
+ });
+ }
+ return this.options_index += 1;
+ }
+ };
+
+ return SelectParser;
+
+ })();
+
+ SelectParser.select_to_array = function(select) {
+ var child, parser, _i, _len, _ref;
+ parser = new SelectParser();
+ _ref = select.childNodes;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ child = _ref[_i];
+ parser.add_node(child);
+ }
+ return parser.parsed;
+ };
+
+ this.SelectParser = SelectParser;
+
+}).call(this);
+
+/*
+Chosen source: generate output using 'cake build'
+Copyright (c) 2011 by Harvest
+*/
+
+
+(function() {
+ var AbstractChosen, root;
+
+ root = this;
+
+ AbstractChosen = (function() {
+
+ function AbstractChosen(form_field, options) {
+ this.form_field = form_field;
+ this.options = options != null ? options : {};
+ if (!AbstractChosen.browser_is_supported()) {
+ return;
+ }
+ this.is_multiple = this.form_field.multiple;
+ this.set_default_text();
+ this.set_default_values();
+ this.setup();
+ this.set_up_html();
+ this.register_observers();
+ this.finish_setup();
+ }
+
+ AbstractChosen.prototype.set_default_values = function() {
+ var _this = this;
+ this.click_test_action = function(evt) {
+ return _this.test_active_click(evt);
+ };
+ this.activate_action = function(evt) {
+ return _this.activate_field(evt);
+ };
+ this.active_field = false;
+ this.mouse_on_container = false;
+ this.results_showing = false;
+ this.result_highlighted = null;
+ this.result_single_selected = null;
+ this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
+ this.disable_search_threshold = this.options.disable_search_threshold || 0;
+ this.disable_search = this.options.disable_search || false;
+ this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
+ this.search_contains = this.options.search_contains || false;
+ this.choices = 0;
+ this.single_backstroke_delete = this.options.single_backstroke_delete || false;
+ this.max_selected_options = this.options.max_selected_options || Infinity;
+ return this.inherit_select_classes = this.options.inherit_select_classes || false;
+ };
+
+ AbstractChosen.prototype.set_default_text = function() {
+ if (this.form_field.getAttribute("data-placeholder")) {
+ this.default_text = this.form_field.getAttribute("data-placeholder");
+ } else if (this.is_multiple) {
+ this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
+ } else {
+ this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
+ }
+ return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
+ };
+
+ AbstractChosen.prototype.mouse_enter = function() {
+ return this.mouse_on_container = true;
+ };
+
+ AbstractChosen.prototype.mouse_leave = function() {
+ return this.mouse_on_container = false;
+ };
+
+ AbstractChosen.prototype.input_focus = function(evt) {
+ var _this = this;
+ if (this.is_multiple) {
+ if (!this.active_field) {
+ return setTimeout((function() {
+ return _this.container_mousedown();
+ }), 50);
+ }
+ } else {
+ if (!this.active_field) {
+ return this.activate_field();
+ }
+ }
+ };
+
+ AbstractChosen.prototype.input_blur = function(evt) {
+ var _this = this;
+ if (!this.mouse_on_container) {
+ this.active_field = false;
+ return setTimeout((function() {
+ return _this.blur_test();
+ }), 100);
+ }
+ };
+
+ AbstractChosen.prototype.result_add_option = function(option) {
+ var classes, style;
+ if (!option.disabled) {
+ option.dom_id = this.container_id + "_o_" + option.array_index;
+ classes = option.selected && this.is_multiple ? [] : ["active-result"];
+ if (option.selected) {
+ classes.push("result-selected");
+ }
+ if (option.group_array_index != null) {
+ classes.push("group-option");
+ }
+ if (option.classes !== "") {
+ classes.push(option.classes);
+ }
+ style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
+ return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
+ } else {
+ return "";
+ }
+ };
+
+ AbstractChosen.prototype.results_update_field = function() {
+ this.set_default_text();
+ if (!this.is_multiple) {
+ this.results_reset_cleanup();
+ }
+ this.result_clear_highlight();
+ this.result_single_selected = null;
+ return this.results_build();
+ };
+
+ AbstractChosen.prototype.results_toggle = function() {
+ if (this.results_showing) {
+ return this.results_hide();
+ } else {
+ return this.results_show();
+ }
+ };
+
+ AbstractChosen.prototype.results_search = function(evt) {
+ if (this.results_showing) {
+ return this.winnow_results();
+ } else {
+ return this.results_show();
+ }
+ };
+
+ AbstractChosen.prototype.choices_click = function(evt) {
+ evt.preventDefault();
+ if (!this.results_showing) {
+ return this.results_show();
+ }
+ };
+
+ AbstractChosen.prototype.keyup_checker = function(evt) {
+ var stroke, _ref;
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
+ this.search_field_scale();
+ switch (stroke) {
+ case 8:
+ if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) {
+ return this.keydown_backstroke();
+ } else if (!this.pending_backstroke) {
+ this.result_clear_highlight();
+ return this.results_search();
+ }
+ break;
+ case 13:
+ evt.preventDefault();
+ if (this.results_showing) {
+ return this.result_select(evt);
+ }
+ break;
+ case 27:
+ if (this.results_showing) {
+ this.results_hide();
+ }
+ return true;
+ case 9:
+ case 38:
+ case 40:
+ case 16:
+ case 91:
+ case 17:
+ break;
+ default:
+ return this.results_search();
+ }
+ };
+
+ AbstractChosen.prototype.generate_field_id = function() {
+ var new_id;
+ new_id = this.generate_random_id();
+ this.form_field.id = new_id;
+ return new_id;
+ };
+
+ AbstractChosen.prototype.generate_random_char = function() {
+ var chars, newchar, rand;
+ chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ rand = Math.floor(Math.random() * chars.length);
+ return newchar = chars.substring(rand, rand + 1);
+ };
+
+ AbstractChosen.prototype.container_width = function() {
+ var width;
+ if (this.options.width != null) {
+ return this.options.width;
+ }
+ width = window.getComputedStyle != null ? parseFloat(window.getComputedStyle(this.form_field).getPropertyValue('width')) : (typeof jQuery !== "undefined" && jQuery !== null) && (this.form_field_jq != null) ? this.form_field_jq.outerWidth() : this.form_field.getWidth();
+ return width + "px";
+ };
+
+ AbstractChosen.browser_is_supported = function() {
+ var _ref;
+ if (window.navigator.appName === "Microsoft Internet Explorer") {
+ return (null !== (_ref = document.documentMode) && _ref >= 8);
+ }
+ return true;
+ };
+
+ AbstractChosen.default_multiple_text = "Select Some Options";
+
+ AbstractChosen.default_single_text = "Select an Option";
+
+ AbstractChosen.default_no_result_text = "No results match";
+
+ return AbstractChosen;
+
+ })();
+
+ root.AbstractChosen = AbstractChosen;
+
+}).call(this);
+
+/*
+Chosen source: generate output using 'cake build'
+Copyright (c) 2011 by Harvest
+*/
+
+
+(function() {
+ var $, Chosen, root,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+ root = this;
+
+ $ = jQuery;
+
+ $.fn.extend({
+ chosen: function(options) {
+ if (!AbstractChosen.browser_is_supported()) {
+ return this;
+ }
+ return this.each(function(input_field) {
+ var $this;
+ $this = $(this);
+ if (!$this.hasClass("chzn-done")) {
+ return $this.data('chosen', new Chosen(this, options));
+ }
+ });
+ }
+ });
+
+ Chosen = (function(_super) {
+
+ __extends(Chosen, _super);
+
+ function Chosen() {
+ return Chosen.__super__.constructor.apply(this, arguments);
+ }
+
+ Chosen.prototype.setup = function() {
+ this.form_field_jq = $(this.form_field);
+ this.current_selectedIndex = this.form_field.selectedIndex;
+ return this.is_rtl = this.form_field_jq.hasClass("chzn-rtl");
+ };
+
+ Chosen.prototype.finish_setup = function() {
+ return this.form_field_jq.addClass("chzn-done");
+ };
+
+ Chosen.prototype.set_up_html = function() {
+ var container_classes, container_props;
+ this.container_id = this.form_field.id.length ? this.form_field.id.replace(/[^\w]/g, '_') : this.generate_field_id();
+ this.container_id += "_chzn";
+ container_classes = ["chzn-container"];
+ container_classes.push("chzn-container-" + (this.is_multiple ? "multi" : "single"));
+ if (this.inherit_select_classes && this.form_field.className) {
+ container_classes.push(this.form_field.className);
+ }
+ if (this.is_rtl) {
+ container_classes.push("chzn-rtl");
+ }
+ container_props = {
+ 'id': this.container_id,
+ 'class': container_classes.join(' '),
+ 'style': "width: " + (this.container_width()) + ";",
+ 'title': this.form_field.title
+ };
+ this.container = $("<div />", container_props);
+ if (this.is_multiple) {
+ this.container.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:auto;" /></li></ul><div class="chzn-drop"><ul class="chzn-results"></ul></div>');
+ } else {
+ this.container.html('<a href="javascript:void(0)" class="chzn-single chzn-default" tabindex="-1"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>');
+ }
+ this.form_field_jq.hide().after(this.container);
+ this.dropdown = this.container.find('div.chzn-drop').first();
+ this.search_field = this.container.find('input').first();
+ this.search_results = this.container.find('ul.chzn-results').first();
+ this.search_field_scale();
+ this.search_no_results = this.container.find('li.no-results').first();
+ if (this.is_multiple) {
+ this.search_choices = this.container.find('ul.chzn-choices').first();
+ this.search_container = this.container.find('li.search-field').first();
+ } else {
+ this.search_container = this.container.find('div.chzn-search').first();
+ this.selected_item = this.container.find('.chzn-single').first();
+ }
+ this.results_build();
+ this.set_tab_index();
+ this.set_label_behavior();
+ return this.form_field_jq.trigger("liszt:ready", {
+ chosen: this
+ });
+ };
+
+ Chosen.prototype.register_observers = function() {
+ var _this = this;
+ this.container.mousedown(function(evt) {
+ _this.container_mousedown(evt);
+ });
+ this.container.mouseup(function(evt) {
+ _this.container_mouseup(evt);
+ });
+ this.container.mouseenter(function(evt) {
+ _this.mouse_enter(evt);
+ });
+ this.container.mouseleave(function(evt) {
+ _this.mouse_leave(evt);
+ });
+ this.search_results.mouseup(function(evt) {
+ _this.search_results_mouseup(evt);
+ });
+ this.search_results.mouseover(function(evt) {
+ _this.search_results_mouseover(evt);
+ });
+ this.search_results.mouseout(function(evt) {
+ _this.search_results_mouseout(evt);
+ });
+ this.search_results.bind('mousewheel DOMMouseScroll', function(evt) {
+ _this.search_results_mousewheel(evt);
+ });
+ this.form_field_jq.bind("liszt:updated", function(evt) {
+ _this.results_update_field(evt);
+ });
+ this.form_field_jq.bind("liszt:activate", function(evt) {
+ _this.activate_field(evt);
+ });
+ this.form_field_jq.bind("liszt:open", function(evt) {
+ _this.container_mousedown(evt);
+ });
+ this.search_field.blur(function(evt) {
+ _this.input_blur(evt);
+ });
+ this.search_field.keyup(function(evt) {
+ _this.keyup_checker(evt);
+ });
+ this.search_field.keydown(function(evt) {
+ _this.keydown_checker(evt);
+ });
+ this.search_field.focus(function(evt) {
+ _this.input_focus(evt);
+ });
+ if (this.is_multiple) {
+ return this.search_choices.click(function(evt) {
+ _this.choices_click(evt);
+ });
+ } else {
+ return this.container.click(function(evt) {
+ evt.preventDefault();
+ });
+ }
+ };
+
+ Chosen.prototype.search_field_disabled = function() {
+ this.is_disabled = this.form_field_jq[0].disabled;
+ if (this.is_disabled) {
+ this.container.addClass('chzn-disabled');
+ this.search_field[0].disabled = true;
+ if (!this.is_multiple) {
+ this.selected_item.unbind("focus", this.activate_action);
+ }
+ return this.close_field();
+ } else {
+ this.container.removeClass('chzn-disabled');
+ this.search_field[0].disabled = false;
+ if (!this.is_multiple) {
+ return this.selected_item.bind("focus", this.activate_action);
+ }
+ }
+ };
+
+ Chosen.prototype.container_mousedown = function(evt) {
+ if (!this.is_disabled) {
+ if (evt && evt.type === "mousedown" && !this.results_showing) {
+ evt.preventDefault();
+ }
+ if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
+ if (!this.active_field) {
+ if (this.is_multiple) {
+ this.search_field.val("");
+ }
+ $(document).click(this.click_test_action);
+ this.results_show();
+ } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chzn-single").length)) {
+ evt.preventDefault();
+ this.results_toggle();
+ }
+ return this.activate_field();
+ }
+ }
+ };
+
+ Chosen.prototype.container_mouseup = function(evt) {
+ if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
+ return this.results_reset(evt);
+ }
+ };
+
+ Chosen.prototype.search_results_mousewheel = function(evt) {
+ var delta, _ref, _ref1;
+ delta = -((_ref = evt.originalEvent) != null ? _ref.wheelDelta : void 0) || ((_ref1 = evt.originialEvent) != null ? _ref1.detail : void 0);
+ if (delta != null) {
+ evt.preventDefault();
+ if (evt.type === 'DOMMouseScroll') {
+ delta = delta * 40;
+ }
+ return this.search_results.scrollTop(delta + this.search_results.scrollTop());
+ }
+ };
+
+ Chosen.prototype.blur_test = function(evt) {
+ if (!this.active_field && this.container.hasClass("chzn-container-active")) {
+ return this.close_field();
+ }
+ };
+
+ Chosen.prototype.close_field = function() {
+ $(document).unbind("click", this.click_test_action);
+ this.active_field = false;
+ this.results_hide();
+ this.container.removeClass("chzn-container-active");
+ this.winnow_results_clear();
+ this.clear_backstroke();
+ this.show_search_field_default();
+ return this.search_field_scale();
+ };
+
+ Chosen.prototype.activate_field = function() {
+ this.container.addClass("chzn-container-active");
+ this.active_field = true;
+ this.search_field.val(this.search_field.val());
+ return this.search_field.focus();
+ };
+
+ Chosen.prototype.test_active_click = function(evt) {
+ if ($(evt.target).parents('#' + this.container_id).length) {
+ return this.active_field = true;
+ } else {
+ return this.close_field();
+ }
+ };
+
+ Chosen.prototype.results_build = function() {
+ var content, data, _i, _len, _ref;
+ this.parsing = true;
+ this.results_data = root.SelectParser.select_to_array(this.form_field);
+ if (this.is_multiple && this.choices > 0) {
+ this.search_choices.find("li.search-choice").remove();
+ this.choices = 0;
+ } else if (!this.is_multiple) {
+ this.selected_item.addClass("chzn-default").find("span").text(this.default_text);
+ if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
+ this.container.addClass("chzn-container-single-nosearch");
+ } else {
+ this.container.removeClass("chzn-container-single-nosearch");
+ }
+ }
+ content = '';
+ _ref = this.results_data;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ data = _ref[_i];
+ if (data.group) {
+ content += this.result_add_group(data);
+ } else if (!data.empty) {
+ content += this.result_add_option(data);
+ if (data.selected && this.is_multiple) {
+ this.choice_build(data);
+ } else if (data.selected && !this.is_multiple) {
+ this.selected_item.removeClass("chzn-default").find("span").text(data.text);
+ if (this.allow_single_deselect) {
+ this.single_deselect_control_build();
+ }
+ }
+ }
+ }
+ this.search_field_disabled();
+ this.show_search_field_default();
+ this.search_field_scale();
+ this.search_results.html(content);
+ return this.parsing = false;
+ };
+
+ Chosen.prototype.result_add_group = function(group) {
+ if (!group.disabled) {
+ group.dom_id = this.container_id + "_g_" + group.array_index;
+ return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
+ } else {
+ return "";
+ }
+ };
+
+ Chosen.prototype.result_do_highlight = function(el) {
+ var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
+ if (el.length) {
+ this.result_clear_highlight();
+ this.result_highlight = el;
+ this.result_highlight.addClass("highlighted");
+ maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
+ visible_top = this.search_results.scrollTop();
+ visible_bottom = maxHeight + visible_top;
+ high_top = this.result_highlight.position().top + this.search_results.scrollTop();
+ high_bottom = high_top + this.result_highlight.outerHeight();
+ if (high_bottom >= visible_bottom) {
+ return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
+ } else if (high_top < visible_top) {
+ return this.search_results.scrollTop(high_top);
+ }
+ }
+ };
+
+ Chosen.prototype.result_clear_highlight = function() {
+ if (this.result_highlight) {
+ this.result_highlight.removeClass("highlighted");
+ }
+ return this.result_highlight = null;
+ };
+
+ Chosen.prototype.results_show = function() {
+ if (this.result_single_selected != null) {
+ this.result_do_highlight(this.result_single_selected);
+ } else if (this.is_multiple && this.max_selected_options <= this.choices) {
+ this.form_field_jq.trigger("liszt:maxselected", {
+ chosen: this
+ });
+ return false;
+ }
+ this.container.addClass("chzn-with-drop");
+ this.form_field_jq.trigger("liszt:showing_dropdown", {
+ chosen: this
+ });
+ this.results_showing = true;
+ this.search_field.focus();
+ this.search_field.val(this.search_field.val());
+ return this.winnow_results();
+ };
+
+ Chosen.prototype.results_hide = function() {
+ this.result_clear_highlight();
+ this.container.removeClass("chzn-with-drop");
+ this.form_field_jq.trigger("liszt:hiding_dropdown", {
+ chosen: this
+ });
+ return this.results_showing = false;
+ };
+
+ Chosen.prototype.set_tab_index = function(el) {
+ var ti;
+ if (this.form_field_jq.attr("tabindex")) {
+ ti = this.form_field_jq.attr("tabindex");
+ this.form_field_jq.attr("tabindex", -1);
+ return this.search_field.attr("tabindex", ti);
+ }
+ };
+
+ Chosen.prototype.set_label_behavior = function() {
+ var _this = this;
+ this.form_field_label = this.form_field_jq.parents("label");
+ if (!this.form_field_label.length && this.form_field.id.length) {
+ this.form_field_label = $("label[for=" + this.form_field.id + "]");
+ }
+ if (this.form_field_label.length > 0) {
+ return this.form_field_label.click(function(evt) {
+ if (_this.is_multiple) {
+ return _this.container_mousedown(evt);
+ } else {
+ return _this.activate_field();
+ }
+ });
+ }
+ };
+
+ Chosen.prototype.show_search_field_default = function() {
+ if (this.is_multiple && this.choices < 1 && !this.active_field) {
+ this.search_field.val(this.default_text);
+ return this.search_field.addClass("default");
+ } else {
+ this.search_field.val("");
+ return this.search_field.removeClass("default");
+ }
+ };
+
+ Chosen.prototype.search_results_mouseup = function(evt) {
+ var target;
+ target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
+ if (target.length) {
+ this.result_highlight = target;
+ this.result_select(evt);
+ return this.search_field.focus();
+ }
+ };
+
+ Chosen.prototype.search_results_mouseover = function(evt) {
+ var target;
+ target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
+ if (target) {
+ return this.result_do_highlight(target);
+ }
+ };
+
+ Chosen.prototype.search_results_mouseout = function(evt) {
+ if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
+ return this.result_clear_highlight();
+ }
+ };
+
+ Chosen.prototype.choice_build = function(item) {
+ var choice_id, html, link,
+ _this = this;
+ if (this.is_multiple && this.max_selected_options <= this.choices) {
+ this.form_field_jq.trigger("liszt:maxselected", {
+ chosen: this
+ });
+ return false;
+ }
+ choice_id = this.container_id + "_c_" + item.array_index;
+ this.choices += 1;
+ if (item.disabled) {
+ html = '<li class="search-choice search-choice-disabled" id="' + choice_id + '"><span>' + item.html + '</span></li>';
+ } else {
+ html = '<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>';
+ }
+ this.search_container.before(html);
+ link = $('#' + choice_id).find("a").first();
+ return link.click(function(evt) {
+ return _this.choice_destroy_link_click(evt);
+ });
+ };
+
+ Chosen.prototype.choice_destroy_link_click = function(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ if (!this.is_disabled) {
+ return this.choice_destroy($(evt.target));
+ }
+ };
+
+ Chosen.prototype.choice_destroy = function(link) {
+ if (this.result_deselect(link.attr("rel"))) {
+ this.choices -= 1;
+ this.show_search_field_default();
+ if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) {
+ this.results_hide();
+ }
+ link.parents('li').first().remove();
+ return this.search_field_scale();
+ }
+ };
+
+ Chosen.prototype.results_reset = function() {
+ this.form_field.options[0].selected = true;
+ this.selected_item.find("span").text(this.default_text);
+ if (!this.is_multiple) {
+ this.selected_item.addClass("chzn-default");
+ }
+ this.show_search_field_default();
+ this.results_reset_cleanup();
+ this.form_field_jq.trigger("change");
+ if (this.active_field) {
+ return this.results_hide();
+ }
+ };
+
+ Chosen.prototype.results_reset_cleanup = function() {
+ this.current_selectedIndex = this.form_field.selectedIndex;
+ return this.selected_item.find("abbr").remove();
+ };
+
+ Chosen.prototype.result_select = function(evt) {
+ var high, high_id, item, position;
+ if (this.result_highlight) {
+ high = this.result_highlight;
+ high_id = high.attr("id");
+ this.result_clear_highlight();
+ if (this.is_multiple) {
+ this.result_deactivate(high);
+ } else {
+ this.search_results.find(".result-selected").removeClass("result-selected");
+ this.result_single_selected = high;
+ this.selected_item.removeClass("chzn-default");
+ }
+ high.addClass("result-selected");
+ position = high_id.substr(high_id.lastIndexOf("_") + 1);
+ item = this.results_data[position];
+ item.selected = true;
+ this.form_field.options[item.options_index].selected = true;
+ if (this.is_multiple) {
+ this.choice_build(item);
+ } else {
+ this.selected_item.find("span").first().text(item.text);
+ if (this.allow_single_deselect) {
+ this.single_deselect_control_build();
+ }
+ }
+ if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) {
+ this.results_hide();
+ }
+ this.search_field.val("");
+ if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
+ this.form_field_jq.trigger("change", {
+ 'selected': this.form_field.options[item.options_index].value
+ });
+ }
+ this.current_selectedIndex = this.form_field.selectedIndex;
+ return this.search_field_scale();
+ }
+ };
+
+ Chosen.prototype.result_activate = function(el) {
+ return el.addClass("active-result");
+ };
+
+ Chosen.prototype.result_deactivate = function(el) {
+ return el.removeClass("active-result");
+ };
+
+ Chosen.prototype.result_deselect = function(pos) {
+ var result, result_data;
+ result_data = this.results_data[pos];
+ if (!this.form_field.options[result_data.options_index].disabled) {
+ result_data.selected = false;
+ this.form_field.options[result_data.options_index].selected = false;
+ result = $("#" + this.container_id + "_o_" + pos);
+ result.removeClass("result-selected").addClass("active-result").show();
+ this.result_clear_highlight();
+ this.winnow_results();
+ this.form_field_jq.trigger("change", {
+ deselected: this.form_field.options[result_data.options_index].value
+ });
+ this.search_field_scale();
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ Chosen.prototype.single_deselect_control_build = function() {
+ if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) {
+ return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
+ }
+ };
+
+ Chosen.prototype.winnow_results = function() {
+ var found, option, part, parts, regex, regexAnchor, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len1, _ref;
+ this.no_results_clear();
+ results = 0;
+ searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html();
+ regexAnchor = this.search_contains ? "" : "^";
+ regex = new RegExp(regexAnchor + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
+ zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
+ _ref = this.results_data;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ option = _ref[_i];
+ if (!option.disabled && !option.empty) {
+ if (option.group) {
+ $('#' + option.dom_id).css('display', 'none');
+ } else if (!(this.is_multiple && option.selected)) {
+ found = false;
+ result_id = option.dom_id;
+ result = $("#" + result_id);
+ if (regex.test(option.html)) {
+ found = true;
+ results += 1;
+ } else if (this.enable_split_word_search && (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0)) {
+ parts = option.html.replace(/\[|\]/g, "").split(" ");
+ if (parts.length) {
+ for (_j = 0, _len1 = parts.length; _j < _len1; _j++) {
+ part = parts[_j];
+ if (regex.test(part)) {
+ found = true;
+ results += 1;
+ }
+ }
+ }
+ }
+ if (found) {
+ if (searchText.length) {
+ startpos = option.html.search(zregex);
+ text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
+ text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
+ } else {
+ text = option.html;
+ }
+ result.html(text);
+ this.result_activate(result);
+ if (option.group_array_index != null) {
+ $("#" + this.results_data[option.group_array_index].dom_id).css('display', 'list-item');
+ }
+ } else {
+ if (this.result_highlight && result_id === this.result_highlight.attr('id')) {
+ this.result_clear_highlight();
+ }
+ this.result_deactivate(result);
+ }
+ }
+ }
+ }
+ if (results < 1 && searchText.length) {
+ return this.no_results(searchText);
+ } else {
+ return this.winnow_results_set_highlight();
+ }
+ };
+
+ Chosen.prototype.winnow_results_clear = function() {
+ var li, lis, _i, _len, _results;
+ this.search_field.val("");
+ lis = this.search_results.find("li");
+ _results = [];
+ for (_i = 0, _len = lis.length; _i < _len; _i++) {
+ li = lis[_i];
+ li = $(li);
+ if (li.hasClass("group-result")) {
+ _results.push(li.css('display', 'auto'));
+ } else if (!this.is_multiple || !li.hasClass("result-selected")) {
+ _results.push(this.result_activate(li));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Chosen.prototype.winnow_results_set_highlight = function() {
+ var do_high, selected_results;
+ if (!this.result_highlight) {
+ selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
+ do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
+ if (do_high != null) {
+ return this.result_do_highlight(do_high);
+ }
+ }
+ };
+
+ Chosen.prototype.no_results = function(terms) {
+ var no_results_html;
+ no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
+ no_results_html.find("span").first().html(terms);
+ return this.search_results.append(no_results_html);
+ };
+
+ Chosen.prototype.no_results_clear = function() {
+ return this.search_results.find(".no-results").remove();
+ };
+
+ Chosen.prototype.keydown_arrow = function() {
+ var first_active, next_sib;
+ if (!this.result_highlight) {
+ first_active = this.search_results.find("li.active-result").first();
+ if (first_active) {
+ this.result_do_highlight($(first_active));
+ }
+ } else if (this.results_showing) {
+ next_sib = this.result_highlight.nextAll("li.active-result").first();
+ if (next_sib) {
+ this.result_do_highlight(next_sib);
+ }
+ }
+ if (!this.results_showing) {
+ return this.results_show();
+ }
+ };
+
+ Chosen.prototype.keyup_arrow = function() {
+ var prev_sibs;
+ if (!this.results_showing && !this.is_multiple) {
+ return this.results_show();
+ } else if (this.result_highlight) {
+ prev_sibs = this.result_highlight.prevAll("li.active-result");
+ if (prev_sibs.length) {
+ return this.result_do_highlight(prev_sibs.first());
+ } else {
+ if (this.choices > 0) {
+ this.results_hide();
+ }
+ return this.result_clear_highlight();
+ }
+ }
+ };
+
+ Chosen.prototype.keydown_backstroke = function() {
+ var next_available_destroy;
+ if (this.pending_backstroke) {
+ this.choice_destroy(this.pending_backstroke.find("a").first());
+ return this.clear_backstroke();
+ } else {
+ next_available_destroy = this.search_container.siblings("li.search-choice").last();
+ if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
+ this.pending_backstroke = next_available_destroy;
+ if (this.single_backstroke_delete) {
+ return this.keydown_backstroke();
+ } else {
+ return this.pending_backstroke.addClass("search-choice-focus");
+ }
+ }
+ }
+ };
+
+ Chosen.prototype.clear_backstroke = function() {
+ if (this.pending_backstroke) {
+ this.pending_backstroke.removeClass("search-choice-focus");
+ }
+ return this.pending_backstroke = null;
+ };
+
+ Chosen.prototype.keydown_checker = function(evt) {
+ var stroke, _ref;
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
+ this.search_field_scale();
+ if (stroke !== 8 && this.pending_backstroke) {
+ this.clear_backstroke();
+ }
+ switch (stroke) {
+ case 8:
+ this.backstroke_length = this.search_field.val().length;
+ break;
+ case 9:
+ if (this.results_showing && !this.is_multiple) {
+ this.result_select(evt);
+ }
+ this.mouse_on_container = false;
+ break;
+ case 13:
+ evt.preventDefault();
+ break;
+ case 38:
+ evt.preventDefault();
+ this.keyup_arrow();
+ break;
+ case 40:
+ this.keydown_arrow();
+ break;
+ }
+ };
+
+ Chosen.prototype.search_field_scale = function() {
+ var div, h, style, style_block, styles, w, _i, _len;
+ if (this.is_multiple) {
+ h = 0;
+ w = 0;
+ style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
+ styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
+ for (_i = 0, _len = styles.length; _i < _len; _i++) {
+ style = styles[_i];
+ style_block += style + ":" + this.search_field.css(style) + ";";
+ }
+ div = $('<div />', {
+ 'style': style_block
+ });
+ div.text(this.search_field.val());
+ $('body').append(div);
+ w = div.width() + 25;
+ div.remove();
+ if (!this.f_width) {
+ this.f_width = this.container.outerWidth();
+ }
+ if (w > this.f_width - 10) {
+ w = this.f_width - 10;
+ }
+ return this.search_field.css({
+ 'width': w + 'px'
+ });
+ }
+ };
+
+ Chosen.prototype.generate_random_id = function() {
+ var string;
+ string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char();
+ while ($("#" + string).length > 0) {
+ string += this.generate_random_char();
+ }
+ return string;
+ };
+
+ return Chosen;
+
+ })(AbstractChosen);
+
+ root.Chosen = Chosen;
+
+}).call(this);
diff --git a/resources/jquery.tipsy/images/tipsy.png b/resources/jquery.tipsy/images/tipsy.png
index fef8c4b5..ef17cc32 100644
--- a/resources/jquery.tipsy/images/tipsy.png
+++ b/resources/jquery.tipsy/images/tipsy.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_0_aaaaaa_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_0_aaaaaa_40x100.png
index 5b5dab2a..e425e6e4 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_0_aaaaaa_40x100.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_ffffff_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_75_ffffff_40x100.png
index ac8b229a..72d47573 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_ffffff_40x100.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_flat_75_ffffff_40x100.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png
index ad3d6346..3b2914a2 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png
index 42ccba26..8569c1bc 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png
index 5a46b47c..d6cc3c58 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png
index 7c9fa6c6..3cd467e1 100644
--- a/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png
+++ b/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
index ee039dc0..9a9606f7 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
index 45e8928e..08d26179 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
index 7ec70d11..80cb644a 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
index 5ba708c3..8373712d 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
index 7930a558..34fc8937 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-blue-hover-large.png b/resources/jquery.ui/themes/vector/images/button-blue-hover-large.png
deleted file mode 100644
index 8f7cf74e..00000000
--- a/resources/jquery.ui/themes/vector/images/button-blue-hover-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-blue-hover.png b/resources/jquery.ui/themes/vector/images/button-blue-hover.png
deleted file mode 100644
index 1fc28163..00000000
--- a/resources/jquery.ui/themes/vector/images/button-blue-hover.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-blue-large.png b/resources/jquery.ui/themes/vector/images/button-blue-large.png
deleted file mode 100644
index 22ee5e59..00000000
--- a/resources/jquery.ui/themes/vector/images/button-blue-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-blue.png b/resources/jquery.ui/themes/vector/images/button-blue.png
deleted file mode 100644
index 2e6d121a..00000000
--- a/resources/jquery.ui/themes/vector/images/button-blue.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-disabled-blue.png b/resources/jquery.ui/themes/vector/images/button-disabled-blue.png
deleted file mode 100644
index 28eb1fcd..00000000
--- a/resources/jquery.ui/themes/vector/images/button-disabled-blue.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-disabled-green.png b/resources/jquery.ui/themes/vector/images/button-disabled-green.png
deleted file mode 100644
index 0e29d85b..00000000
--- a/resources/jquery.ui/themes/vector/images/button-disabled-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-disabled-red.png b/resources/jquery.ui/themes/vector/images/button-disabled-red.png
deleted file mode 100644
index ede69988..00000000
--- a/resources/jquery.ui/themes/vector/images/button-disabled-red.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-disabled.png b/resources/jquery.ui/themes/vector/images/button-disabled.png
deleted file mode 100644
index e4e4ec1c..00000000
--- a/resources/jquery.ui/themes/vector/images/button-disabled.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-down-blue.png b/resources/jquery.ui/themes/vector/images/button-down-blue.png
deleted file mode 100644
index 766e5746..00000000
--- a/resources/jquery.ui/themes/vector/images/button-down-blue.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-down-green.png b/resources/jquery.ui/themes/vector/images/button-down-green.png
deleted file mode 100644
index 90969c39..00000000
--- a/resources/jquery.ui/themes/vector/images/button-down-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-down-red.png b/resources/jquery.ui/themes/vector/images/button-down-red.png
deleted file mode 100644
index f6257298..00000000
--- a/resources/jquery.ui/themes/vector/images/button-down-red.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-down.png b/resources/jquery.ui/themes/vector/images/button-down.png
deleted file mode 100644
index c6467571..00000000
--- a/resources/jquery.ui/themes/vector/images/button-down.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-green-hover-large.png b/resources/jquery.ui/themes/vector/images/button-green-hover-large.png
deleted file mode 100644
index dd8851e1..00000000
--- a/resources/jquery.ui/themes/vector/images/button-green-hover-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-green-hover.png b/resources/jquery.ui/themes/vector/images/button-green-hover.png
deleted file mode 100644
index 19c39911..00000000
--- a/resources/jquery.ui/themes/vector/images/button-green-hover.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-green-large.png b/resources/jquery.ui/themes/vector/images/button-green-large.png
deleted file mode 100644
index a8e830eb..00000000
--- a/resources/jquery.ui/themes/vector/images/button-green-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-green.png b/resources/jquery.ui/themes/vector/images/button-green.png
deleted file mode 100644
index 54c418e9..00000000
--- a/resources/jquery.ui/themes/vector/images/button-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-large-disabled-green.png b/resources/jquery.ui/themes/vector/images/button-large-disabled-green.png
deleted file mode 100644
index f76f7b05..00000000
--- a/resources/jquery.ui/themes/vector/images/button-large-disabled-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-large-off-green.png b/resources/jquery.ui/themes/vector/images/button-large-off-green.png
deleted file mode 100644
index f997431b..00000000
--- a/resources/jquery.ui/themes/vector/images/button-large-off-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-off-blue.png b/resources/jquery.ui/themes/vector/images/button-off-blue.png
deleted file mode 100644
index 82dedb53..00000000
--- a/resources/jquery.ui/themes/vector/images/button-off-blue.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-off-green.png b/resources/jquery.ui/themes/vector/images/button-off-green.png
deleted file mode 100644
index 109907f0..00000000
--- a/resources/jquery.ui/themes/vector/images/button-off-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-off-red.png b/resources/jquery.ui/themes/vector/images/button-off-red.png
deleted file mode 100644
index 5a85b8aa..00000000
--- a/resources/jquery.ui/themes/vector/images/button-off-red.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-off.png b/resources/jquery.ui/themes/vector/images/button-off.png
deleted file mode 100644
index cc5eb119..00000000
--- a/resources/jquery.ui/themes/vector/images/button-off.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-orange-hover-large.png b/resources/jquery.ui/themes/vector/images/button-orange-hover-large.png
deleted file mode 100644
index 6f0dbd5d..00000000
--- a/resources/jquery.ui/themes/vector/images/button-orange-hover-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-orange-hover.png b/resources/jquery.ui/themes/vector/images/button-orange-hover.png
deleted file mode 100644
index a1077c58..00000000
--- a/resources/jquery.ui/themes/vector/images/button-orange-hover.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-orange-large.png b/resources/jquery.ui/themes/vector/images/button-orange-large.png
deleted file mode 100644
index 3d7f37f6..00000000
--- a/resources/jquery.ui/themes/vector/images/button-orange-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-orange.png b/resources/jquery.ui/themes/vector/images/button-orange.png
deleted file mode 100644
index 02347cf9..00000000
--- a/resources/jquery.ui/themes/vector/images/button-orange.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-over-blue.png b/resources/jquery.ui/themes/vector/images/button-over-blue.png
deleted file mode 100644
index 9edb7aa2..00000000
--- a/resources/jquery.ui/themes/vector/images/button-over-blue.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-over-green.png b/resources/jquery.ui/themes/vector/images/button-over-green.png
deleted file mode 100644
index 47a0b1b8..00000000
--- a/resources/jquery.ui/themes/vector/images/button-over-green.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-over-red.png b/resources/jquery.ui/themes/vector/images/button-over-red.png
deleted file mode 100644
index a2445365..00000000
--- a/resources/jquery.ui/themes/vector/images/button-over-red.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-over.png b/resources/jquery.ui/themes/vector/images/button-over.png
deleted file mode 100644
index 558f1f80..00000000
--- a/resources/jquery.ui/themes/vector/images/button-over.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-red-hover-large.png b/resources/jquery.ui/themes/vector/images/button-red-hover-large.png
deleted file mode 100644
index 11ccef06..00000000
--- a/resources/jquery.ui/themes/vector/images/button-red-hover-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-red-hover.png b/resources/jquery.ui/themes/vector/images/button-red-hover.png
deleted file mode 100644
index 55a61743..00000000
--- a/resources/jquery.ui/themes/vector/images/button-red-hover.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-red-large.png b/resources/jquery.ui/themes/vector/images/button-red-large.png
deleted file mode 100644
index 8d089efd..00000000
--- a/resources/jquery.ui/themes/vector/images/button-red-large.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-red.png b/resources/jquery.ui/themes/vector/images/button-red.png
deleted file mode 100644
index 7a292fc1..00000000
--- a/resources/jquery.ui/themes/vector/images/button-red.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/titlebar-fade.png b/resources/jquery.ui/themes/vector/images/titlebar-fade.png
index f9fde8b3..12a80c88 100644
--- a/resources/jquery.ui/themes/vector/images/titlebar-fade.png
+++ b/resources/jquery.ui/themes/vector/images/titlebar-fade.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/jquery.ui.button.css b/resources/jquery.ui/themes/vector/jquery.ui.button.css
index a6a1b544..ea14723f 100644
--- a/resources/jquery.ui/themes/vector/jquery.ui.button.css
+++ b/resources/jquery.ui/themes/vector/jquery.ui.button.css
@@ -12,23 +12,12 @@
zoom: 1;
overflow: visible; /* the overflow property removes extra width in IE */
}
-.ui-button-icon-only {
- width: 2.2em; /* to make room for the icon, a width needs to be set here */
-}
-button.ui-button-icon-only {
- width: 2.4em; /* button elements seem to need a little more width */
-}
-.ui-button-icons-only {
- width: 3.4em;
-}
-button.ui-button-icons-only {
- width: 3.7em;
-}
/*button text element */
.ui-button .ui-button-text {
display: block;
line-height: 1.4;
+ text-shadow: 0 1px 1px #fff;
}
.ui-button-text-only .ui-button-text {
padding: 0.3em 1em 0.25em 1em;
@@ -90,7 +79,7 @@ input.ui-button {
}
.ui-buttonset .ui-button {
margin-left: 0;
- margin-right: -.3em;
+ margin-right: -.4em;
}
/* workarounds */
@@ -98,17 +87,21 @@ button.ui-button::-moz-focus-inner {
border: 0;
padding: 0; /* reset extra padding in Firefox */
}
-
-body .ui-button {
- margin: 0.5em 0 0.5em 0.4em;
- border: 1px solid #a6a6a6 !important;
+/* Disables the annoying dashed border Firefox puts on active buttons */
+body button.ui-button::-moz-focus-inner {
+ border: 0;
+}
+/* Give large buttons some extra padding */
+body .ui-button-large {
+ padding: 5px;
+}
+/* Use white icons for colored buttons */
+.ui-button-green .ui-icon,
+.ui-button-blue .ui-icon,
+.ui-button-red .ui-icon,
+.ui-button-orange .ui-icon {
/* @embed */
- background: #f2f2f2 url(images/button-off.png) repeat-x scroll 50% 100% !important;
- cursor: pointer;
- font-size: 1em;
- line-height: 1.4em;
- width: auto;
- overflow: visible;
+ background-image: url(images/ui-icons_ffffff_256x240.png) !important;
}
/* Corner radius */
@@ -124,6 +117,7 @@ body .ui-button {
}
.ui-button.ui-corner-all,
.ui-button.ui-corner-top,
+
.ui-button.ui-corner-right,
.ui-button.ui-corner-tr {
-moz-border-radius-topright: 4px;
@@ -147,145 +141,239 @@ body .ui-button {
border-bottom-right-radius: 4px;
}
-body .ui-button:hover {
- border-color: #6e7273;
- /* @embed */
- background: #e1e1e1 url(images/button-over.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button:active,
-body .ui-button:focus {
- border-color: #707271;
- /* @embed */
- background: #bfbfbf url(images/button-down.png) repeat-x scroll 50% 100% !important;
+body .ui-button {
+ color: #2779aa;
+ margin: 0.5em 0 0.5em 0.4em;
+ border: 1px solid #aaa !important;
+ background: #f0f0f0 !important;
+ background: -moz-linear-gradient(top, #fff 0%, #ddd 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #fff 0%, #ddd 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #fff 0%, #ddd 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #fff 0%, #ddd 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #fff 0%, #ddd 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dddddd', GradientType=0); /* IE6-8 */
+ cursor: pointer;
+ font-size: 1em;
+ line-height: 1.4em;
+ width: auto;
+ overflow: visible;
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.2);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.2);
+ box-shadow: 0 1px 3px rgba(0,0,0,.2);
}
-body .ui-button.disabled {
- color: #7f7f7f;
- border-color: #cccccc;
- /* @embed */
- background: #f2f2f2 url(images/button-disabled.png) repeat-x scroll 50% 100% !important;
+
+body .ui-button-icon-only {
+ width: 2.2em;
}
-/* Disables the annoying dashed border Firefox puts on active buttons */
-body button.ui-button::-moz-focus-inner {
- border: 0;
+
+body .ui-button-icons-only {
+ width: 3.4em;
}
-/* Give large buttons some extra padding */
-body .ui-button-large {
- padding: 5px;
+
+body .ui-button:hover {
+ color: #2779aa;
+ border-color: #bbb !important;
+ background: #fff !important;
+ background: -moz-linear-gradient(top, #fff 0%, #eee 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #fff 0%, #eee 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #fff 0%, #eee 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #fff 0%, #eee 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #fff 0%, #eee 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.1);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.1);
+ box-shadow: 0 1px 3px rgba(0,0,0,.1);
}
-/* Use white icons for colored buttons */
-.ui-button-green .ui-icon, .ui-button-blue .ui-icon, .ui-button-red .ui-icon, .ui-button-orange .ui-icon {
- /* @embed */
- background-image: url(images/ui-icons_ffffff_256x240.png) !important;
+body .ui-button:active,
+body .ui-button:focus {
+ border-color: #8ad !important;
+ -webkit-box-shadow: 0 0 1px 1px rgba(167,215,249,.5);
+ -moz-box-shadow: 0 0 1px 1px rgba(167,215,249,.5);
+ box-shadow: 0 0 1px 1px rgba(167,215,249,.5);
+}
+body .ui-button:active {
+ background: #e0e0e0 !important;
+ background: -moz-linear-gradient(top, #f0f0f0 0%, #d0d0d0 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #f0f0f0 0%, #d0d0d0 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #f0f0f0 0%, #d0d0d0 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #f0f0f0 0%, #d0d0d0 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #f0f0f0 0%, #d0d0d0 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f0f0f0', endColorstr='#d0d0d0', GradientType=0); /* IE6-8 */
}
/* Green buttons */
-
+body .ui-button-green,
+body .ui-button-green .ui-button-text {
+ color: white;
+ text-shadow: 0 -1px 1px #072;
+}
body .ui-button.ui-button-green {
- color: white !important;
- border-color: #97af7e !important;
- /* @embed */
- background: #3cb677 url(images/button-green.png) repeat-x scroll 50% 100% !important;
+ border-color: #294 !important;
+ background: #295 !important;
+ background: -moz-linear-gradient(top, #3c8 0%, #295 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #3c8 0%, #295 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #3c8 0%, #295 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #3c8 0%, #295 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #3c8 0%, #295 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#33cc88', endColorstr='#229955', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.3);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.3);
+ box-shadow: 0 1px 3px rgba(0,0,0,.3);
}
body .ui-button.ui-button-green:hover {
- border-color: #778e61 !important;
- /* @embed */
- background: #339b65 url(images/button-green-hover.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-green.ui-button-large {
- /* @embed */
- background: #3cb677 url(images/button-green-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-green.ui-button-large:hover {
- /* @embed */
- background: #339b65 url(images/button-green-hover-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-green.disabled {
- filter:alpha(opacity=50);
- -moz-opacity:0.50;
- -khtml-opacity: 0.50;
- opacity: 0.50;
+ background: #33a055 !important;
+ background: -moz-linear-gradient(top, #44d388 0%, #33a055 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #44d388 0%, #33a055 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #44d388 0%, #33a055 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #44d388 0%, #33a055 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #44d388 0%, #33a055 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#44d388', endColorstr='#33a055', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.25);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.25);
+ box-shadow: 0 1px 3px rgba(0,0,0,.25);
+}
+body .ui-button.ui-button-green:active,
+body .ui-button.ui-button-green:focus {
+ border-color: #172 !important;
+ -webkit-box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+ -moz-box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+ box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+}
+body .ui-button.ui-button-green:active {
+ background: #338855 !important;
+ background: -moz-linear-gradient(top, #30c080 0%, #338855 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #30c080 0%, #338855 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #30c080 0%, #338855 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #30c080 0%, #338855 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #30c080 0%, #338855 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#30c080', endColorstr='#338855', GradientType=0); /* IE6-8 */
}
/* Blue buttons */
-
+body .ui-button-blue,
+body .ui-button-blue .ui-button-text {
+ color: white;
+ text-shadow: 0 -1px 1px #037;
+}
body .ui-button.ui-button-blue {
- color: white !important;
- border-color: #628acb !important;
- /* @embed */
- background: #3365ba url(images/button-blue.png) repeat-x scroll 50% 100% !important;
+ border-color: #468 !important;
+ background: #36b !important;
+ background: -moz-linear-gradient(top, #48e 0%, #36b 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #48e 0%, #36b 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #48e 0%, #36b 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #48e 0%, #36b 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #48e 0%, #36b 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4488ee', endColorstr='#3366bb', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.35);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.35);
+ box-shadow: 0 1px 3px rgba(0,0,0,.35);
}
body .ui-button.ui-button-blue:hover {
- border-color: #5375ad !important;
- /* @embed */
- background: #2b569e url(images/button-blue-hover.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-blue.ui-button-large {
- /* @embed */
- background: #3365ba url(images/button-blue-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-blue.ui-button-large:hover {
- /* @embed */
- background: #2b569e url(images/button-blue-hover-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-blue.disabled {
- filter:alpha(opacity=50);
- -moz-opacity:0.50;
- -khtml-opacity: 0.50;
- opacity: 0.50;
+ background: #36c !important;
+ background: -moz-linear-gradient(top, #59e 0%, #36c 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #59e 0%, #36c 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #59e 0%, #36c 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #59e 0%, #36c 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #59e 0%, #36c 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5599ee', endColorstr='#3366cc', GradientType=0); /* IE6-8 */
+}
+body .ui-button.ui-button-blue:active,
+body .ui-button.ui-button-blue:focus {
+ border-color: #357 !important;
+ -webkit-box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+ -moz-box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+ box-shadow: 0 0 2px 2px rgba(167,215,249,.75);
+}
+body .ui-button.ui-button-blue:active {
+ background: #3060a0 !important;
+ background: -moz-linear-gradient(top, #4080e0 0%, #3060a0 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #4080e0 0%, #3060a0 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #4080e0 0%, #3060a0 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #4080e0 0%, #3060a0 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #4080e0 0%, #3060a0 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4080e0', endColorstr='#3060a0', GradientType=0); /* IE6-8 */
}
/* Red buttons */
-
+body .ui-button-red,
+body .ui-button-red .ui-button-text {
+ color: white;
+ text-shadow: 0 -1px 1px #700;
+}
body .ui-button.ui-button-red {
- color: white !important;
- border-color: #af977e !important;
- /* @embed */
- background: #cb0000 url(images/button-red.png) repeat-x scroll 50% 100% !important;
+ border-color: #944 !important;
+ background: #a22 !important;
+ background: -moz-linear-gradient(top, #d44 0%, #a22 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #d44 0%, #a22 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #d44 0%, #a22 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #d44 0%, #a22 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #d44 0%, #a22 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dd4444', endColorstr='#aa2222', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.35);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.35);
+ box-shadow: 0 1px 3px rgba(0,0,0,.35);
}
body .ui-button.ui-button-red:hover {
- border-color: #8e7761 !important;
- /* @embed */
- background: #ad0000 url(images/button-red-hover.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-red.ui-button-large {
- /* @embed */
- background: #cb0000 url(images/button-red.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-red.ui-button-large:hover {
- /* @embed */
- background: #ad0000 url(images/button-red-hover.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-red.disabled {
- filter:alpha(opacity=50);
- -moz-opacity:0.50;
- -khtml-opacity: 0.50;
- opacity: 0.50;
+ border-color: #a44 !important;
+ background: #b03333 !important;
+ background: -moz-linear-gradient(top, #ee4646 0%, #b03333 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #ee4646 0%, #b03333 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #ee4646 0%, #b03333 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #ee4646 0%, #b03333 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #ee4646 0%, #b03333 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee4646', endColorstr='#b03333', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.3);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,.3);
+ box-shadow: 0 1px 3px rgba(0,0,0,.3);
+}
+body .ui-button.ui-button-red:active,
+body .ui-button.ui-button-red:focus {
+ border-color: #747 !important;
+ -webkit-box-shadow: 0 0 2px 2px rgba(167,215,249,.7);
+ -moz-box-shadow: 0 0 2px 2px rgba(167,215,249,.7);
+ box-shadow: 0 0 2px 2px rgba(167,215,249,.7);
+}
+body .ui-button.ui-button-red:active {
+ background: #952020 !important;
+ background: -moz-linear-gradient(top, #d04545 0%, #952020 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #d04545 0%, #952020 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #d04545 0%, #952020 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #d04545 0%, #952020 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #d04545 0%, #952020 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d04545', endColorstr='#952020', GradientType=0); /* IE6-8 */
}
-/* Orange buttons */
-
-body .ui-button.ui-button-orange {
- color: white !important;
- border-color: #f3a863 !important;
- /* @embed */
- background: #f07f14 url(images/button-orange.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-orange:hover {
- border-color: #ce9055 !important;
- /* @embed */
- background: #cc6c11 url(images/button-orange-hover.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-orange.ui-button-large {
- /* @embed */
- background: #f07f14 url(images/button-orange-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-orange.ui-button-large:hover {
- /* @embed */
- background: #cc6c11 url(images/button-orange-hover-large.png) repeat-x scroll 50% 100% !important;
-}
-body .ui-button.ui-button-orange.disabled {
- filter:alpha(opacity=50);
- -moz-opacity:0.50;
- -khtml-opacity: 0.50;
- opacity: 0.50;
+/* Disabled buttons */
+body .ui-button-green.disabled,
+body .ui-button-green.disabled:hover,
+body .ui-button-green.disabled:active,
+body .ui-button-green.disabled:focus,
+body .ui-button-blue.disabled,
+body .ui-button-blue.disabled:hover,
+body .ui-button-blue.disabled:active,
+body .ui-button-blue.disabled:focus,
+body .ui-button-red.disabled,
+body .ui-button-red.disabled:hover,
+body .ui-button-red.disabled:active,
+body .ui-button-red.disabled:focus,
+body .ui-button.disabled,
+body .ui-button.disabled:hover {
+ color: #aaa;
+ border-color: #ccc !important;
+ background: #eee !important;
+ background: -moz-linear-gradient(top, #f6f6f6 0%, #eee 90%) !important; /* FF3.6+ */
+ background: -webkit-linear-gradient(top, #f6f6f6 0%, #eee 90%) !important; /* Chrome10+, Safari5.1+ */
+ background: -o-linear-gradient(top, #f6f6f6 0%, #eee 90%) !important; /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #f6f6f6 0%, #eee 90%) !important; /* IE10+ */
+ background: linear-gradient(to bottom, #f6f6f6 0%, #eee 90%) !important;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#eeeeee', GradientType=0); /* IE6-8 */
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0);
+ -moz-box-shadow: 0 1px 3px rgba(0,0,0,0);
+ box-shadow: 0 1px 3px rgba(0,0,0,0);
+}
+body .ui-button-green.disabled .ui-button-text,
+body .ui-button-blue.disabled .ui-button-text,
+body .ui-button-red.disabled .ui-button-text {
+ color: #aaa;
+ text-shadow: 0 1px 1px #fff;
}
diff --git a/resources/jquery/images/jquery.arrowSteps.divider-ltr.png b/resources/jquery/images/jquery.arrowSteps.divider-ltr.png
index 83d6ff84..84ed2a2d 100644
--- a/resources/jquery/images/jquery.arrowSteps.divider-ltr.png
+++ b/resources/jquery/images/jquery.arrowSteps.divider-ltr.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.divider-rtl.png b/resources/jquery/images/jquery.arrowSteps.divider-rtl.png
index 529d7b84..7cfbfeba 100644
--- a/resources/jquery/images/jquery.arrowSteps.divider-rtl.png
+++ b/resources/jquery/images/jquery.arrowSteps.divider-rtl.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.head-ltr.png b/resources/jquery/images/jquery.arrowSteps.head-ltr.png
index 3289617d..eb070280 100644
--- a/resources/jquery/images/jquery.arrowSteps.head-ltr.png
+++ b/resources/jquery/images/jquery.arrowSteps.head-ltr.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.head-rtl.png b/resources/jquery/images/jquery.arrowSteps.head-rtl.png
index 3d9f70cb..7ea2fdb5 100644
--- a/resources/jquery/images/jquery.arrowSteps.head-rtl.png
+++ b/resources/jquery/images/jquery.arrowSteps.head-rtl.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.tail-ltr.png b/resources/jquery/images/jquery.arrowSteps.tail-ltr.png
index 92b872b2..3ad990b6 100644
--- a/resources/jquery/images/jquery.arrowSteps.tail-ltr.png
+++ b/resources/jquery/images/jquery.arrowSteps.tail-ltr.png
Binary files differ
diff --git a/resources/jquery/images/marker.png b/resources/jquery/images/marker.png
index 3929bbb5..19efb6ce 100644
--- a/resources/jquery/images/marker.png
+++ b/resources/jquery/images/marker.png
Binary files differ
diff --git a/resources/jquery/images/mask.png b/resources/jquery/images/mask.png
index b0a4d406..fe08de0e 100644
--- a/resources/jquery/images/mask.png
+++ b/resources/jquery/images/mask.png
Binary files differ
diff --git a/resources/jquery/jquery.badge.css b/resources/jquery/jquery.badge.css
index d961bf3d..f313663e 100644
--- a/resources/jquery/jquery.badge.css
+++ b/resources/jquery/jquery.badge.css
@@ -1,13 +1,12 @@
.mw-badge {
min-width: 7px;
- -moz-border-radius: 2px;
- -webkit-border-radius: 2px;
border-radius: 2px;
padding: 1px 4px;
text-align: center;
font-size: 12px;
line-height: 12px;
background-color: #d2d2d2;
+ cursor: pointer;
}
.mw-badge-content {
@@ -18,8 +17,12 @@
}
.mw-badge-inline {
- display: inline-block;
margin-left: 3px;
+ display: inline-block;
+ /* Hack for IE6 and IE7 (bug 47926) */
+ zoom: 1;
+ *display: inline;
+
}
.mw-badge-overlay {
position: absolute;
diff --git a/resources/jquery/jquery.byteLength.js b/resources/jquery/jquery.byteLength.js
index 3d5b7206..398937e6 100644
--- a/resources/jquery/jquery.byteLength.js
+++ b/resources/jquery/jquery.byteLength.js
@@ -4,6 +4,8 @@
* Calculate the byte length of a string (accounting for UTF-8).
*
* @author Jan Paul Posma, 2011
+ * @author Timo Tijhof, 2012
+ * @author David Chan, 2013
*/
jQuery.byteLength = function ( str ) {
@@ -12,8 +14,18 @@ jQuery.byteLength = function ( str ) {
// Note, surrogate (\uD800-\uDFFF) characters are counted as 2 bytes, since there's two of them
// and the actual character takes 4 bytes in UTF-8 (2*2=4). Might not work perfectly in
// edge cases such as illegal sequences, but that should never happen.
+
+ // https://en.wikipedia.org/wiki/UTF-8#Description
+ // The mapping from UTF-16 code units to UTF-8 bytes is as follows:
+ // > Range 0000-007F: codepoints that become 1 byte of UTF-8
+ // > Range 0080-07FF: codepoints that become 2 bytes of UTF-8
+ // > Range 0800-D7FF: codepoints that become 3 bytes of UTF-8
+ // > Range D800-DFFF: Surrogates (each pair becomes 4 bytes of UTF-8)
+ // > Range E000-FFFF: codepoints that become 3 bytes of UTF-8 (continued)
+
return str
.replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
.replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
.length;
+
};
diff --git a/resources/jquery/jquery.byteLimit.js b/resources/jquery/jquery.byteLimit.js
index f2b98f09..a8c0b065 100644
--- a/resources/jquery/jquery.byteLimit.js
+++ b/resources/jquery/jquery.byteLimit.js
@@ -78,7 +78,8 @@
// Chop off characters from the end of the "inserted content" string
// until the limit is statisfied.
if ( fn ) {
- while ( $.byteLength( fn( inpParts.join( '' ) ) ) > byteLimit ) {
+ // stop, when there is nothing to slice - bug 41450
+ while ( $.byteLength( fn( inpParts.join( '' ) ) ) > byteLimit && inpParts[1].length > 0 ) {
inpParts[1] = inpParts[1].slice( 0, -1 );
}
} else {
diff --git a/resources/jquery/jquery.checkboxShiftClick.js b/resources/jquery/jquery.checkboxShiftClick.js
index aced0633..b2065665 100644
--- a/resources/jquery/jquery.checkboxShiftClick.js
+++ b/resources/jquery/jquery.checkboxShiftClick.js
@@ -15,11 +15,17 @@
$box.click( function ( e ) {
// And one has been clicked before...
if ( prevCheckbox !== null && e.shiftKey ) {
- // Check or uncheck this one and all in-between checkboxes
- $box.slice(
- Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
- Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
- ).prop( 'checked', !!e.target.checked );
+ // Check or uncheck this one and all in-between checkboxes,
+ // except for disabled ones
+ $box
+ .slice(
+ Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
+ Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
+ )
+ .filter( function () {
+ return !this.disabled;
+ } )
+ .prop( 'checked', !!e.target.checked );
}
// Either way, update the prevCheckbox variable to the one clicked now
prevCheckbox = e.target;
diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js
index b0bd6850..5a95dc5b 100644
--- a/resources/jquery/jquery.client.js
+++ b/resources/jquery/jquery.client.js
@@ -6,7 +6,7 @@
/* Private Members */
/**
- * @var profileCache {Object} Keyed by userAgent string,
+ * @var {Object} profileCache Keyed by userAgent string,
* value is the parsed $.client.profile object for that user agent.
*/
var profileCache = {};
@@ -18,9 +18,9 @@
/**
* Get an object containing information about the client.
*
- * @param nav {Object} An object with atleast a 'userAgent' and 'platform' key.
+ * @param {Object} nav An object with atleast a 'userAgent' and 'platform' key.
* Defaults to the global Navigator object.
- * @return {Object} The resulting client object will be in the following format:
+ * @returns {Object} The resulting client object will be in the following format:
* {
* 'name': 'firefox',
* 'layout': 'gecko',
@@ -50,47 +50,47 @@
// Generic version digit
x = 'x',
// Strings found in user agent strings that need to be conformed
- wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'],
+ wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3', 'Iceweasel'],
// Translations for conforming user agent strings
userAgentTranslations = [
// Tons of browsers lie about being something they are not
- [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
+ [/(Firefox|MSIE|KHTML,?\slike\sGecko|Konqueror)/, ''],
// Chrome lives in the shadow of Safari still
['Chrome Safari', 'Chrome'],
// KHTML is the layout engine not the browser - LIES!
['KHTML', 'Konqueror'],
// Firefox nightly builds
['Minefield', 'Firefox'],
- // This helps keep differnt versions consistent
+ // This helps keep different versions consistent
['Navigator', 'Netscape'],
// This prevents version extraction issues, otherwise translation would happen later
['PLAYSTATION 3', 'PS3']
],
- // Strings which precede a version number in a user agent string - combined and used as match 1 in
- // version detectection
+ // Strings which precede a version number in a user agent string - combined and used as
+ // match 1 in version detection
versionPrefixes = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror',
- 'lynx', 'msie', 'safari', 'ps3'
+ 'lynx', 'msie', 'safari', 'ps3', 'android'
],
// Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)',
// Names of known browsers
names = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'konqueror', 'lynx', 'msie', 'opera',
- 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq'
+ 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq', 'android'
],
// Tanslations for conforming browser names
nameTranslations = [],
// Names of known layout engines
- layouts = ['gecko', 'konqueror', 'msie', 'opera', 'webkit'],
+ layouts = ['gecko', 'konqueror', 'msie', 'trident', 'opera', 'webkit'],
// Translations for conforming layout names
layoutTranslations = [ ['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto'] ],
// Names of supported layout engines for version number
- layoutVersions = ['applewebkit', 'gecko'],
+ layoutVersions = ['applewebkit', 'gecko', 'trident'],
// Names of known operating systems
- platforms = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'],
+ platforms = ['win', 'wow64', 'mac', 'linux', 'sunos', 'solaris', 'iphone'],
// Translations for conforming operating system names
- platformTranslations = [ ['sunos', 'solaris'] ],
+ platformTranslations = [ ['sunos', 'solaris'], ['wow64', 'win'] ],
/* Methods */
@@ -143,18 +143,33 @@
/* Edge Cases -- did I mention about how user agent string lie? */
// Decode Safari's crazy 400+ version numbers
- if ( name.match( /safari/ ) && version > 400 ) {
+ if ( name === 'safari' && version > 400 ) {
version = '2.0';
}
// Expose Opera 10's lies about being Opera 9.8
- if ( name === 'opera' && version >= 9.8) {
- match = ua.match( /version\/([0-9\.]*)/i );
+ if ( name === 'opera' && version >= 9.8 ) {
+ match = ua.match( /\bversion\/([0-9\.]*)/ );
if ( match && match[1] ) {
version = match[1];
} else {
version = '10';
}
}
+ // And Opera 15's lies about being Chrome
+ if ( name === 'chrome' && ( match = ua.match( /\bopr\/([0-9\.]*)/ ) ) ) {
+ if ( match[1] ) {
+ name = 'opera';
+ version = match[1];
+ }
+ }
+ // And IE 11's lies about being not being IE
+ if ( layout === 'trident' && layoutversion >= 7 && ( match = ua.match( /\brv[ :\/]([0-9\.]*)/ ) ) ) {
+ if ( match[1] ) {
+ name = 'msie';
+ version = match[1];
+ }
+ }
+
versionNumber = parseFloat( version, 10 ) || 0.0;
/* Caching */
@@ -173,46 +188,61 @@
},
/**
- * Checks the current browser against a support map object to determine if the browser has been black-listed or
- * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
- * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
+ * Checks the current browser against a support map object.
*
* A browser map is in the following format:
* {
+ * // Multiple rules with configurable operators
+ * 'msie': [['>=', 7], ['!=', 9]],
+ * // Match no versions
+ * 'iphone': false,
+ * // Match any version
+ * 'android': null
+ * }
+ *
+ * It can optionally be split into ltr/rtl sections:
+ * {
* 'ltr': {
- * // Multiple rules with configurable operators
- * 'msie': [['>=', 7], ['!=', 9]],
- * // Blocked entirely
+ * 'android': null,
* 'iphone': false
* },
* 'rtl': {
- * // Test against a string
- * 'msie': [['!==', '8.1.2.3']],
- * // RTL rules do not fall through to LTR rules, you must explicity set each of them
+ * 'android': false,
+ * // rules are not inherited from ltr
* 'iphone': false
* }
* }
*
- * @param map {Object} Browser support map
- * @param profile {Object} (optional) a client-profile object.
+ * @param {Object} map Browser support map
+ * @param {Object} [profile] A client-profile object
+ * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, otherwise
+ * returns true if the browser is not found.
*
- * @return Boolean true if browser known or assumed to be supported, false if blacklisted
+ * @returns {boolean} The current browser is in the support map
*/
- test: function ( map, profile ) {
+ test: function ( map, profile, exactMatchOnly ) {
/*jshint evil: true */
var conditions, dir, i, op, val;
profile = $.isPlainObject( profile ) ? profile : $.client.profile();
- dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+ if ( map.ltr && map.rtl ) {
+ dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+ map = map[dir];
+ }
// Check over each browser condition to determine if we are running in a compatible client
- if ( typeof map[dir] !== 'object' || map[dir][profile.name] === undefined ) {
- // Unknown, so we assume it's working
- return true;
+ if ( typeof map !== 'object' || map[profile.name] === undefined ) {
+ // Not found, return true if exactMatchOnly not set, false otherwise
+ return !exactMatchOnly;
}
- conditions = map[dir][profile.name];
+ conditions = map[profile.name];
if ( conditions === false ) {
+ // Match no versions
return false;
}
+ if ( conditions === null ) {
+ // Match all versions
+ return true;
+ }
for ( i = 0; i < conditions.length; i++ ) {
op = conditions[i][0];
val = conditions[i][1];
diff --git a/resources/jquery/jquery.makeCollapsible.js b/resources/jquery/jquery.makeCollapsible.js
index 1407f53b..0cd6417c 100644
--- a/resources/jquery/jquery.makeCollapsible.js
+++ b/resources/jquery/jquery.makeCollapsible.js
@@ -18,13 +18,15 @@
var lpx = 'jquery.makeCollapsible> ';
/**
+ * Handler for a click on a collapsible toggler.
+ *
* @param {jQuery} $collapsible
* @param {string} action The action this function will take ('expand' or 'collapse').
* @param {jQuery|null} [optional] $defaultToggle
* @param {Object|undefined} options
*/
function toggleElement( $collapsible, action, $defaultToggle, options ) {
- var $collapsibleContent, $containers;
+ var $collapsibleContent, $containers, hookCallback;
options = options || {};
// Validate parameters
@@ -47,6 +49,14 @@
return;
}
+ // Trigger a custom event to allow callers to hook to the collapsing/expanding,
+ // allowing the module to be testable, and making it possible to
+ // e.g. implement persistence via cookies
+ $collapsible.trigger( action === 'expand' ? 'beforeExpand.mw-collapsible' : 'beforeCollapse.mw-collapsible' );
+ hookCallback = function () {
+ $collapsible.trigger( action === 'expand' ? 'afterExpand.mw-collapsible' : 'afterCollapse.mw-collapsible' );
+ };
+
// Handle different kinds of elements
if ( !options.plainMode && $collapsible.is( 'table' ) ) {
@@ -63,11 +73,12 @@
// http://stackoverflow.com/questions/467336#920480
if ( options.instantHide ) {
$containers.hide();
+ hookCallback();
} else {
- $containers.stop( true, true ).fadeOut();
+ $containers.stop( true, true ).fadeOut().promise().done( hookCallback );
}
} else {
- $containers.stop( true, true ).fadeIn();
+ $containers.stop( true, true ).fadeIn().promise().done( hookCallback );
}
} else if ( !options.plainMode && ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) ) {
@@ -81,11 +92,12 @@
if ( action === 'collapse' ) {
if ( options.instantHide ) {
$containers.hide();
+ hookCallback();
} else {
- $containers.stop( true, true ).slideUp();
+ $containers.stop( true, true ).slideUp().promise().done( hookCallback );
}
} else {
- $containers.stop( true, true ).slideDown();
+ $containers.stop( true, true ).slideDown().promise().done( hookCallback );
}
} else {
@@ -97,11 +109,12 @@
if ( action === 'collapse' ) {
if ( options.instantHide ) {
$collapsibleContent.hide();
+ hookCallback();
} else {
- $collapsibleContent.slideUp();
+ $collapsibleContent.slideUp().promise().done( hookCallback );
}
} else {
- $collapsibleContent.slideDown();
+ $collapsibleContent.slideDown().promise().done( hookCallback );
}
// Otherwise assume this is a customcollapse with a remote toggle
@@ -110,18 +123,19 @@
if ( action === 'collapse' ) {
if ( options.instantHide ) {
$collapsible.hide();
+ hookCallback();
} else {
if ( $collapsible.is( 'tr' ) || $collapsible.is( 'td' ) || $collapsible.is( 'th' ) ) {
- $collapsible.fadeOut();
+ $collapsible.fadeOut().promise().done( hookCallback );
} else {
- $collapsible.slideUp();
+ $collapsible.slideUp().promise().done( hookCallback );
}
}
} else {
if ( $collapsible.is( 'tr' ) || $collapsible.is( 'td' ) || $collapsible.is( 'th' ) ) {
- $collapsible.fadeIn();
+ $collapsible.fadeIn().promise().done( hookCallback );
} else {
- $collapsible.slideDown();
+ $collapsible.slideDown().promise().done( hookCallback );
}
}
}
@@ -129,7 +143,7 @@
}
/**
- * Handles clicking on the collapsible element toggle and other
+ * Handles clicking/keypressing on the collapsible element toggle and other
* situations where a collapsible element is toggled (e.g. the initial
* toggle for collapsed ones).
*
@@ -138,20 +152,32 @@
* @param {jQuery.Event|null} e either the event or null if unavailable
* @param {Object|undefined} options
*/
- function togglingHandler( $toggle, $collapsible, event, options ) {
+ function togglingHandler( $toggle, $collapsible, e, options ) {
var wasCollapsed, $textContainer, collapseText, expandText;
- if ( event ) {
- // Don't fire if a link was clicked, if requested (for premade togglers by default)
- if ( options.linksPassthru && $.nodeName( event.target, 'a' ) ) {
- return true;
+ if ( options === undefined ) {
+ options = {};
+ }
+
+ if ( e ) {
+ if ( e.type === 'click' && options.linksPassthru && $.nodeName( e.target, 'a' ) ) {
+ // Don't fire if a link was clicked, if requested (for premade togglers by default)
+ return;
+ } else if ( e.type === 'keypress' && e.which !== 13 && e.which !== 32 ) {
+ // Only handle keypresses on the "Enter" or "Space" keys
+ return;
} else {
- event.preventDefault();
- event.stopPropagation();
+ e.preventDefault();
+ e.stopPropagation();
}
}
- wasCollapsed = $collapsible.hasClass( 'mw-collapsed' );
+ // This allows the element to be hidden on initial toggle without fiddling with the class
+ if ( options.wasCollapsed !== undefined ) {
+ wasCollapsed = options.wasCollapsed;
+ } else {
+ wasCollapsed = $collapsible.hasClass( 'mw-collapsed' );
+ }
// Toggle the state of the collapsible element (that is, expand or collapse)
$collapsible.toggleClass( 'mw-collapsed', !wasCollapsed );
@@ -180,45 +206,6 @@
}
/**
- * Toggles collapsible and togglelink class and updates text label.
- *
- * @param {jQuery} $that
- * @param {jQuery.Event} e
- * @param {Object|undefined} options
- */
- function toggleLinkDefault( $that, e, options ) {
- var $collapsible = $that.closest( '.mw-collapsible' );
- options = $.extend( { toggleClasses: true }, options );
- togglingHandler( $that, $collapsible, e, options );
- }
-
- /**
- * Toggles collapsible and togglelink class.
- *
- * @param {jQuery} $that
- * @param {jQuery.Event} e
- * @param {Object|undefined} options
- */
- function toggleLinkPremade( $that, e, options ) {
- var $collapsible = $that.eq( 0 ).closest( '.mw-collapsible' );
- options = $.extend( { toggleClasses: true, linksPassthru: true }, options );
- togglingHandler( $that, $collapsible, e, options );
- }
-
- /**
- * Toggles customcollapsible.
- *
- * @param {jQuery} $that
- * @param {jQuery.Event} e
- * @param {Object|undefined} options
- * @param {jQuery} $collapsible
- */
- function toggleLinkCustom( $that, e, options, $collapsible ) {
- options = $.extend( {}, options );
- togglingHandler( $that, $collapsible, e, options );
- }
-
- /**
* Make any element collapsible.
*
* Supported options:
@@ -243,17 +230,17 @@
* div.mw-collapsible-content. May only be used with custom togglers.
*/
$.fn.makeCollapsible = function ( options ) {
- return this.each(function () {
- var $collapsible, collapsetext, expandtext, $toggle, $toggleLink, $firstItem, collapsibleId,
- $customTogglers, firstval;
+ if ( options === undefined ) {
+ options = {};
+ }
- if ( options === undefined ) {
- options = {};
- }
+ return this.each( function () {
+ var $collapsible, collapseText, expandText, $toggle, actionHandler, buildDefaultToggleLink,
+ premadeToggleHandler, $toggleLink, $firstItem, collapsibleId, $customTogglers, firstval;
// Ensure class "mw-collapsible" is present in case .makeCollapsible()
// is called on element(s) that don't have it yet.
- $collapsible = $(this).addClass( 'mw-collapsible' );
+ $collapsible = $( this ).addClass( 'mw-collapsible' );
// Return if it has been enabled already.
if ( $collapsible.data( 'mw-made-collapsible' ) ) {
@@ -263,21 +250,35 @@
}
// Use custom text or default?
- collapsetext = options.collapseText || $collapsible.attr( 'data-collapsetext' ) || mw.msg( 'collapsible-collapse' );
- expandtext = options.expandText || $collapsible.attr( 'data-expandtext' ) || mw.msg( 'collapsible-expand' );
-
- // Create toggle link with a space around the brackets (&nbsp;[text]&nbsp;)
- $toggleLink =
- $( '<a href="#"></a>' )
- .text( collapsetext )
+ collapseText = options.collapseText || $collapsible.attr( 'data-collapsetext' ) || mw.msg( 'collapsible-collapse' );
+ expandText = options.expandText || $collapsible.attr( 'data-expandtext' ) || mw.msg( 'collapsible-expand' );
+
+ // Default click/keypress handler and toggle link to use when none is present
+ actionHandler = function ( e, opts ) {
+ var defaultOpts = {
+ toggleClasses: true,
+ toggleText: { collapseText: collapseText, expandText: expandText }
+ };
+ opts = $.extend( defaultOpts, options, opts );
+ togglingHandler( $( this ), $collapsible, e, opts );
+ };
+ // Default toggle link. Only build it when needed to avoid jQuery memory leaks (event data).
+ buildDefaultToggleLink = function () {
+ return $( '<a href="#"></a>' )
+ .text( collapseText )
.wrap( '<span class="mw-collapsible-toggle"></span>' )
.parent()
.prepend( '&nbsp;[' )
.append( ']&nbsp;' )
- .on( 'click.mw-collapse', function ( e, opts ) {
- opts = $.extend( { toggleText: { collapseText: collapsetext, expandText: expandtext } }, options, opts );
- toggleLinkDefault( $(this), e, opts );
- } );
+ .on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
+ };
+
+ // Default handler for clicking on premade toggles
+ premadeToggleHandler = function ( e, opts ) {
+ var defaultOpts = { toggleClasses: true, linksPassthru: true };
+ opts = $.extend( defaultOpts, options, opts );
+ togglingHandler( $( this ), $collapsible, e, opts );
+ };
// Check if this element has a custom position for the toggle link
// (ie. outside the container or deeper inside the tree)
@@ -296,25 +297,21 @@
}
}
- // Bind the custom togglers
+ // Bind the togglers
if ( $customTogglers && $customTogglers.length ) {
- $customTogglers.on( 'click.mw-collapse', function ( e, opts ) {
- opts = $.extend( {}, options, opts );
- toggleLinkCustom( $(this), e, opts, $collapsible );
- } );
-
- // Initial state
- if ( options.collapsed || $collapsible.hasClass( 'mw-collapsed' ) ) {
- // Remove here so that the toggler goes in the right direction,
- // It re-adds the class.
- $collapsible.removeClass( 'mw-collapsed' );
- toggleLinkCustom( $customTogglers, null, $.extend( { instantHide: true }, options ), $collapsible );
- }
+ actionHandler = function ( e, opts ) {
+ var defaultOpts = {};
+ opts = $.extend( defaultOpts, options, opts );
+ togglingHandler( $( this ), $collapsible, e, opts );
+ };
+
+ $toggleLink = $customTogglers;
+ $toggleLink.on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
- // If this is not a custom case, do the default:
- // Wrap the contents and add the toggle link
} else {
- // Elements are treated differently
+ // If this is not a custom case, do the default: wrap the
+ // contents and add the toggle link. Different elements are
+ // treated differently.
if ( $collapsible.is( 'table' ) ) {
// The toggle-link will be in one the the cells (td or th) of the first row
$firstItem = $collapsible.find( 'tr:first th, tr:first td' );
@@ -322,12 +319,10 @@
// If theres no toggle link, add it to the last cell
if ( !$toggle.length ) {
- $firstItem.eq(-1).prepend( $toggleLink );
+ $toggleLink = buildDefaultToggleLink().prependTo( $firstItem.eq( -1 ) );
} else {
- $toggleLink = $toggle.off( 'click.mw-collapse' ).on( 'click.mw-collapse', function ( e, opts ) {
- opts = $.extend( {}, options, opts );
- toggleLinkPremade( $toggle, e, opts );
- } );
+ actionHandler = premadeToggleHandler;
+ $toggleLink = $toggle.on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
}
} else if ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) {
@@ -339,17 +334,16 @@
if ( !$toggle.length ) {
// Make sure the numeral order doesn't get messed up, force the first (soon to be second) item
// to be "1". Except if the value-attribute is already used.
- // If no value was set WebKit returns "", Mozilla returns '-1', others return null or undefined.
+ // If no value was set WebKit returns "", Mozilla returns '-1', others return 0, null or undefined.
firstval = $firstItem.attr( 'value' );
if ( firstval === undefined || !firstval || firstval === '-1' || firstval === -1 ) {
$firstItem.attr( 'value', '1' );
}
- $collapsible.prepend( $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent() );
+ $toggleLink = buildDefaultToggleLink();
+ $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent().prependTo( $collapsible );
} else {
- $toggleLink = $toggle.off( 'click.mw-collapse' ).on( 'click.mw-collapse', function ( e, opts ) {
- opts = $.extend( {}, options, opts );
- toggleLinkPremade( $toggle, e, opts );
- } );
+ actionHandler = premadeToggleHandler;
+ $toggleLink = $toggle.on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
}
} else { // <div>, <p> etc.
@@ -364,28 +358,23 @@
// If theres no toggle link, add it
if ( !$toggle.length ) {
- $collapsible.prepend( $toggleLink );
+ $toggleLink = buildDefaultToggleLink().prependTo( $collapsible );
} else {
- $toggleLink = $toggle.off( 'click.mw-collapse' ).on( 'click.mw-collapse', function ( e, opts ) {
- opts = $.extend( {}, options, opts );
- toggleLinkPremade( $toggle, e, opts );
- } );
+ actionHandler = premadeToggleHandler;
+ $toggleLink = $toggle.on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
}
}
}
- // Initial state (only for those that are not custom,
- // because the initial state of those has been taken care of already).
- if (
- ( options.collapsed || $collapsible.hasClass( 'mw-collapsed' ) ) &&
- ( !$customTogglers || !$customTogglers.length )
- ) {
- $collapsible.removeClass( 'mw-collapsed' );
- // The collapsible element could have multiple togglers
- // To toggle the initial state only click one of them (ie. the first one, eq(0) )
- // Else it would go like: hide,show,hide,show for each toggle link.
- // This is just like it would be in reality (only one toggle is clicked at a time).
- $toggleLink.eq( 0 ).trigger( 'click', [ { instantHide: true } ] );
+ // Attributes for accessibility. This isn't necessary when the toggler is already
+ // an <a> or a <button> etc., but it doesn't hurt either, and it's consistent.
+ $toggleLink.prop( 'tabIndex', 0 );
+
+ // Initial state
+ if ( options.collapsed || $collapsible.hasClass( 'mw-collapsed' ) ) {
+ // One toggler can hook to multiple elements, and one element can have
+ // multiple togglers. This is the sanest way to handle that.
+ actionHandler.call( $toggleLink.get( 0 ), null, { instantHide: true, wasCollapsed: false } );
}
} );
};
diff --git a/resources/jquery/jquery.placeholder.js b/resources/jquery/jquery.placeholder.js
index 7badb11a..8044d880 100644
--- a/resources/jquery/jquery.placeholder.js
+++ b/resources/jquery/jquery.placeholder.js
@@ -10,17 +10,22 @@
*/
( function ( $ ) {
- $.fn.placeholder = function () {
+ $.fn.placeholder = function ( text ) {
+ var hasArg = arguments.length;
return this.each( function () {
var placeholder, $input;
+ if ( hasArg ) {
+ this.setAttribute( 'placeholder', text );
+ }
+
// If the HTML5 placeholder attribute is supported, use it
if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
return;
}
- placeholder = this.getAttribute( 'placeholder' );
+ placeholder = hasArg ? text : this.getAttribute( 'placeholder' );
$input = $(this);
// Show initially, if empty
diff --git a/resources/jquery/jquery.spinner.css b/resources/jquery/jquery.spinner.css
index 4a775283..a9e06dbe 100644
--- a/resources/jquery/jquery.spinner.css
+++ b/resources/jquery/jquery.spinner.css
@@ -33,7 +33,7 @@
.mw-spinner-inline {
display: inline-block;
vertical-align: middle;
-
+
/* IE < 8 */
zoom: 1;
*display: inline;
diff --git a/resources/jquery/jquery.spinner.js b/resources/jquery/jquery.spinner.js
index 93e30b9a..27dabc6c 100644
--- a/resources/jquery/jquery.spinner.js
+++ b/resources/jquery/jquery.spinner.js
@@ -1,7 +1,9 @@
/**
- * jQuery spinner
+ * jQuery Spinner
*
* Simple jQuery plugin to create, inject and remove spinners.
+ *
+ * @class jQuery.plugin.spinner
*/
( function ( $ ) {
@@ -15,36 +17,37 @@
$.extend({
/**
- * Creates a spinner element.
+ * Create a spinner element
*
* The argument is an object with options used to construct the spinner. These can be:
*
* It is a good practice to keep a reference to the created spinner to be able to remove it later.
- * Alternatively one can use the id option and removeSpinner() (but make sure to choose an id
+ * Alternatively one can use the id option and #removeSpinner (but make sure to choose an id
* that's unlikely to cause conflicts, e.g. with extensions, gadgets or user scripts).
*
* CSS classes used:
- * .mw-spinner for every spinner
- * .mw-spinner-small / .mw-spinner-large for size
- * .mw-spinner-block / .mw-spinner-inline for display types
+ * - .mw-spinner for every spinner
+ * - .mw-spinner-small / .mw-spinner-large for size
+ * - .mw-spinner-block / .mw-spinner-inline for display types
*
- * @example
* // Create a large spinner reserving all available horizontal space.
* var $spinner = $.createSpinner({ size: 'large', type: 'block' });
* // Insert above page content.
* $( '#mw-content-text' ).prepend( $spinner );
- * @example
+ *
* // Place a small inline spinner next to the "Save" button
* var $spinner = $.createSpinner({ size: 'small', type: 'inline' });
* // Alternatively, just `$.createSpinner();` as these are the default options.
* $( '#wpSave' ).after( $spinner );
- * @example
+ *
* // The following two are equivalent:
* $.createSpinner( 'magic' );
* $.createSpinner({ id: 'magic' });
*
- * @param {Object|String} opts [optional] ID string or options:
- * - id: If given, spinner will be given an id of "mw-spinner-<id>"
+ * @static
+ * @inheritable
+ * @param {Object|string} [opts] ID string or options:
+ * - id: If given, spinner will be given an id of "mw-spinner-{id}"
* - size: 'small' (default) or 'large' for a 20-pixel or 32-pixel spinner
* - type: 'inline' (default) or 'block'. Inline creates an inline-block with width and
* height equal to spinner size. Block is a block-level element with width 100%, height
@@ -72,10 +75,12 @@
},
/**
- * Removes a spinner element.
+ * Remove a spinner element
*
- * @param {String} id [optional] Id of the spinner, as passed to createSpinner.
- * @return {jQuery} The (now detached) spinner.
+ * @static
+ * @inheritable
+ * @param {string} id Id of the spinner, as passed to #createSpinner
+ * @return {jQuery} The (now detached) spinner element
*/
removeSpinner: function ( id ) {
return $( '#mw-spinner-' + id ).remove();
@@ -83,13 +88,21 @@
});
/**
- * Injects a spinner after the elements in the jQuery collection
- * (as siblings, not children). Collection contents remain unchanged.
+ * Inject a spinner after each element in the collection
+ *
+ * Inserts spinner as siblings, not children, of the target elements.
+ * Collection contents remain unchanged.
*
- * @param {Object|String} opts See createSpinner() for description.
+ * @param {Object|string} [opts] See #createSpinner
* @return {jQuery}
*/
$.fn.injectSpinner = function ( opts ) {
return this.after( $.createSpinner( opts ) );
};
+
+ /**
+ * @class jQuery
+ * @mixins jQuery.plugin.spinner
+ */
+
}( jQuery ) );
diff --git a/resources/jquery/jquery.suggestions.js b/resources/jquery/jquery.suggestions.js
index 44382f0d..28e2afc4 100644
--- a/resources/jquery/jquery.suggestions.js
+++ b/resources/jquery/jquery.suggestions.js
@@ -220,7 +220,7 @@ $.suggestions = {
} else {
// Expand from right
newCSS.left = 'auto';
- newCSS.right = $( 'body' ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
+ newCSS.right = $( document ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
}
context.data.$container.css( newCSS );
@@ -585,10 +585,12 @@ $.fn.suggestions = function () {
switch ( context.data.keypressed ) {
// This preventDefault logic is duplicated from
// $.suggestions.keypress(), which sucks
+ // Arrow down
case 40:
e.preventDefault();
e.stopImmediatePropagation();
break;
+ // Arrow up, Escape and Enter
case 38:
case 27:
case 13:
diff --git a/resources/jquery/jquery.tablesorter.js b/resources/jquery/jquery.tablesorter.js
index e08c9aaf..b3d7bb3d 100644
--- a/resources/jquery/jquery.tablesorter.js
+++ b/resources/jquery/jquery.tablesorter.js
@@ -8,8 +8,9 @@
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
- * Depends on mw.config (wgDigitTransformTable, wgMonthNames, wgMonthNamesShort,
- * wgDefaultDateFormat, wgContentLanguage)
+ * Depends on mw.config (wgDigitTransformTable, wgDefaultDateFormat, wgContentLanguage)
+ * and mw.language.months.
+ *
* Uses 'tableSorterCollation' in mw.config (if available)
*/
/**
@@ -287,44 +288,113 @@
function buildHeaders( table, msg ) {
var maxSeen = 0,
+ colspanOffset = 0,
longest,
- realCellIndex = 0,
- $tableHeaders = $( 'thead:eq(0) > tr', table );
- if ( $tableHeaders.length > 1 ) {
- $tableHeaders.each( function () {
- if ( this.cells.length > maxSeen ) {
- maxSeen = this.cells.length;
- longest = this;
+ columns,
+ i,
+ $tableHeaders = $( [] ),
+ $tableRows = $( 'thead:eq(0) > tr', table );
+ if ( $tableRows.length <= 1 ) {
+ $tableHeaders = $tableRows.children( 'th' );
+ } else {
+ // We need to find the cells of the row containing the most columns
+ var rowspan,
+ headersIndex = [];
+ $tableRows.each( function ( rowIndex ) {
+ $.each( this.cells, function( index2, cell ) {
+ rowspan = Number( cell.rowSpan );
+ for ( i = 0; i < rowspan; i++ ) {
+ if ( headersIndex[rowIndex+i] === undefined ) {
+ headersIndex[rowIndex+i] = $( [] );
+ }
+ headersIndex[rowIndex+i].push( cell );
+ }
+ } );
+ } );
+ $.each( headersIndex, function ( index, cellArray ) {
+ if ( cellArray.length >= maxSeen ) {
+ maxSeen = cellArray.length;
+ longest = index;
}
- });
- $tableHeaders = $( longest );
+ } );
+ $tableHeaders = headersIndex[longest];
}
- $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
- this.column = realCellIndex;
- var colspan = this.colspan;
- colspan = colspan ? parseInt( colspan, 10 ) : 1;
- realCellIndex += colspan;
+ // as each header can span over multiple columns (using colspan=N),
+ // we have to bidirectionally map headers to their columns and columns to their headers
+ table.headerToColumns = [];
+ table.columnToHeader = [];
+
+ $tableHeaders.each( function ( headerIndex ) {
+ columns = [];
+ for ( i = 0; i < this.colSpan; i++ ) {
+ table.columnToHeader[ colspanOffset + i ] = headerIndex;
+ columns.push( colspanOffset + i );
+ }
+
+ table.headerToColumns[ headerIndex ] = columns;
+ colspanOffset += this.colSpan;
+ this.headerIndex = headerIndex;
this.order = 0;
this.count = 0;
- if ( $( this ).is( '.unsortable' ) ) {
+ if ( $( this ).hasClass( table.config.unsortableClass ) ) {
this.sortDisabled = true;
}
if ( !this.sortDisabled ) {
- $( this ).addClass( table.config.cssHeader ).attr( 'title', msg[1] );
+ $( this )
+ .addClass( table.config.cssHeader )
+ .prop( 'tabIndex', 0 )
+ .attr( {
+ role: 'columnheader button',
+ title: msg[1]
+ } );
}
// add cell to headerList
- table.config.headerList[index] = this;
+ table.config.headerList[headerIndex] = this;
} );
return $tableHeaders;
}
+ /**
+ * Sets the sort count of the columns that are not affected by the sorting to have them sorted
+ * in default (ascending) order when their header cell is clicked the next time.
+ *
+ * @param {jQuery} $headers
+ * @param {Number[][]} sortList
+ * @param {Number[][]} headerToColumns
+ */
+ function setHeadersOrder( $headers, sortList, headerToColumns ) {
+ // Loop through all headers to retrieve the indices of the columns the header spans across:
+ $.each( headerToColumns, function( headerIndex, columns ) {
+
+ $.each( columns, function( i, columnIndex ) {
+ var header = $headers[headerIndex];
+
+ if ( !isValueInArray( columnIndex, sortList ) ) {
+ // Column shall not be sorted: Reset header count and order.
+ header.order = 0;
+ header.count = 0;
+ } else {
+ // Column shall be sorted: Apply designated count and order.
+ $.each( sortList, function( j, sortColumn ) {
+ if ( sortColumn[0] === i ) {
+ header.order = sortColumn[1];
+ header.count = sortColumn[1] + 1;
+ return false;
+ }
+ } );
+ }
+ } );
+
+ } );
+ }
+
function isValueInArray( v, a ) {
var l = a.length;
for ( var i = 0; i < l; i++ ) {
@@ -407,12 +477,15 @@
var regex = [];
ts.monthNames = {};
- for ( var i = 1; i < 13; i++ ) {
- var name = mw.config.get( 'wgMonthNames' )[i].toLowerCase();
- ts.monthNames[name] = i;
+ for ( var i = 0; i < 12; i++ ) {
+ var name = mw.language.months.names[i].toLowerCase();
+ ts.monthNames[name] = i + 1;
regex.push( $.escapeRE( name ) );
- name = mw.config.get( 'wgMonthNamesShort' )[i].toLowerCase().replace( '.', '' );
- ts.monthNames[name] = i;
+ name = mw.language.months.genitive[i].toLowerCase();
+ ts.monthNames[name] = i + 1;
+ regex.push( $.escapeRE( name ) );
+ name = mw.language.months.abbrev[i].toLowerCase().replace( '.', '' );
+ ts.monthNames[name] = i + 1;
regex.push( $.escapeRE( name ) );
}
@@ -424,10 +497,10 @@
ts.dateRegex[0] = new RegExp( /^\s*(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{2,4})\s*?/i);
// Written Month name, dmy
- ts.dateRegex[1] = new RegExp( '^\\s*(\\d{1,2})[\\,\\.\\-\\/\'\\s]*(' + regex + ')' + '[\\,\\.\\-\\/\'\\s]*(\\d{2,4})\\s*$', 'i' );
+ ts.dateRegex[1] = new RegExp( '^\\s*(\\d{1,2})[\\,\\.\\-\\/\'\\s]+(' + regex + ')' + '[\\,\\.\\-\\/\'\\s]+(\\d{2,4})\\s*$', 'i' );
// Written Month name, mdy
- ts.dateRegex[2] = new RegExp( '^\\s*(' + regex + ')' + '[\\,\\.\\-\\/\'\\s]*(\\d{1,2})[\\,\\.\\-\\/\'\\s]*(\\d{2,4})\\s*$', 'i' );
+ ts.dateRegex[2] = new RegExp( '^\\s*(' + regex + ')' + '[\\,\\.\\-\\/\'\\s]+(\\d{1,2})[\\,\\.\\-\\/\'\\s]+(\\d{2,4})\\s*$', 'i' );
}
@@ -572,7 +645,7 @@
$.each( sortObjects, function( i, sortObject ) {
$.each ( sortObject, function( columnIndex, order ) {
var orderIndex = ( order === 'desc' ) ? 1 : 0;
- sortList.push( [columnIndex, orderIndex] );
+ sortList.push( [parseInt( columnIndex, 10 ), orderIndex] );
} );
} );
return sortList;
@@ -590,6 +663,7 @@
sortInitialOrder: 'asc',
sortMultiSortKey: 'shiftKey',
sortLocaleCompare: false,
+ unsortableClass: 'unsortable',
parsers: {},
widgets: [],
headers: {},
@@ -611,7 +685,6 @@
return $tables.each( function ( i, table ) {
// Declare and cache.
var $headers, cache, config,
- headerToColumns, columnToHeader, colspanOffset,
$table = $( table ),
firstTime = true;
@@ -648,10 +721,9 @@
// Build headers
$headers = buildHeaders( table, sortMsg );
- // Grab and process locale settings
+ // Grab and process locale settings.
buildTransformTable();
buildDateTable();
- buildCollationTable();
// Precaching regexps can bring 10 fold
// performance improvements in some browsers.
@@ -660,6 +732,12 @@
function setupForFirstSort() {
firstTime = false;
+ // Defer buildCollationTable to first sort. As user and site scripts
+ // may customize tableSorterCollation but load after $.ready(), other
+ // scripts may call .tablesorter() before they have done the
+ // tableSorterCollation customizations.
+ buildCollationTable();
+
// Legacy fix of .sortbottoms
// Wrap them inside inside a tfoot (because that's what they actually want to be) &
// and put the <tfoot> at the end of the <table>
@@ -679,28 +757,17 @@
table.config.parsers = buildParserCache( table, $headers );
}
- // as each header can span over multiple columns (using colspan=N),
- // we have to bidirectionally map headers to their columns and columns to their headers
- headerToColumns = [];
- columnToHeader = [];
- colspanOffset = 0;
- $headers.each( function ( headerIndex ) {
- var columns = [];
- for ( var i = 0; i < this.colSpan; i++ ) {
- columnToHeader[ colspanOffset + i ] = headerIndex;
- columns.push( colspanOffset + i );
- }
-
- headerToColumns[ headerIndex ] = columns;
- colspanOffset += this.colSpan;
- } );
-
// Apply event handling to headers
// this is too big, perhaps break it out?
- $headers.filter( ':not(.unsortable)' ).click( function ( e ) {
- if ( e.target.nodeName.toLowerCase() === 'a' ) {
- // The user clicked on a link inside a table header
- // Do nothing and let the default link click action continue
+ $headers.not( '.' + table.config.unsortableClass ).on( 'keypress click', function ( e ) {
+ if ( e.type === 'click' && e.target.nodeName.toLowerCase() === 'a' ) {
+ // The user clicked on a link inside a table header.
+ // Do nothing and let the default link click action continue.
+ return true;
+ }
+
+ if ( e.type === 'keypress' && e.which !== 13 ) {
+ // Only handle keypresses on the "Enter" key.
return true;
}
@@ -724,7 +791,7 @@
var cell = this;
// Get current column index
- var columns = headerToColumns[this.column];
+ var columns = table.headerToColumns[ this.headerIndex ];
var newSortList = $.map( columns, function (c) {
// jQuery "helpfully" flattens the arrays...
return [[c, cell.order]];
@@ -758,8 +825,11 @@
}
}
+ // Reset order/counts of cells not affected by sorting
+ setHeadersOrder( $headers, config.sortList, table.headerToColumns );
+
// Set CSS for headers
- setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, columnToHeader );
+ setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, table.columnToHeader );
appendToTable(
$table[0], multisort( $table[0], config.sortList, cache )
);
@@ -798,11 +868,15 @@
sortList = convertSortList( sortList );
}
+ // Set each column's sort count to be able to determine the correct sort
+ // order when clicking on a header cell the next time
+ setHeadersOrder( $headers, sortList, table.headerToColumns );
+
// re-build the cache for the tbody cells
cache = buildCache( table );
// set css for headers
- setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, columnToHeader );
+ setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, table.columnToHeader );
// sort the table and append it to the dom
appendToTable( table, multisort( table, sortList, cache ) );
diff --git a/resources/jquery/jquery.textSelection.js b/resources/jquery/jquery.textSelection.js
index daff9d93..2b5a4406 100644
--- a/resources/jquery/jquery.textSelection.js
+++ b/resources/jquery/jquery.textSelection.js
@@ -89,7 +89,7 @@
* Ported from skins/common/edit.js by Trevor Parscal
* (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
*
- * Inserts text at the begining and end of a text selection, optionally
+ * Inserts text at the beginning and end of a text selection, optionally
* inserting text at the caret when selection is empty.
*
* @fixme document the options parameters
@@ -257,7 +257,7 @@
* Some code copied from
* http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
*
- * Get the position (in resolution of bytes not nessecarily characters)
+ * Get the position (in resolution of bytes not necessarily characters)
* in a textarea
*
* Will focus the textarea in some browsers (IE/Opera)
diff --git a/resources/mediawiki.action/images/green-checkmark.png b/resources/mediawiki.action/images/green-checkmark.png
new file mode 100644
index 00000000..8ec604ea
--- /dev/null
+++ b/resources/mediawiki.action/images/green-checkmark.png
Binary files differ
diff --git a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
new file mode 100644
index 00000000..1af4a7a0
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
@@ -0,0 +1,17 @@
+/* Styles for collapsible lists of templates used and hidden categories */
+.mw-editfooter-toggler {
+ cursor: pointer;
+ background-position: left center;
+ padding-left: 16px;
+}
+
+.mw-editfooter-list {
+ margin-bottom: 1em;
+ margin-left: 2.5em;
+}
+
+/* Show/hide animation is incorrect if the table has a margin set. Extra
+ * "table.wikitable" is needed in the selector for CSS specificity. */
+table.wikitable.preview-limit-report {
+ margin: 0;
+}
diff --git a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
new file mode 100644
index 00000000..7ae51aba
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
@@ -0,0 +1,54 @@
+jQuery( document ).ready( function ( $ ) {
+ var collapsibleLists, i, handleOne;
+
+ // Collapsible lists of categories and templates
+ collapsibleLists = [
+ {
+ $list: $( '.templatesUsed ul' ),
+ $toggler: $( '.mw-templatesUsedExplanation' ),
+ cookieName: 'templates-used-list'
+ },
+ {
+ $list: $( '.hiddencats ul' ),
+ $toggler: $( '.mw-hiddenCategoriesExplanation' ),
+ cookieName: 'hidden-categories-list'
+ },
+ {
+ $list: $( '.preview-limit-report-wrapper' ),
+ $toggler: $( '.mw-limitReportExplanation' ),
+ cookieName: 'preview-limit-report'
+ }
+ ];
+
+ handleOne = function ( $list, $toggler, cookieName ) {
+ var isCollapsed = $.cookie( cookieName ) !== 'expanded';
+
+ // Style the toggler with an arrow icon and add a tabIndex and a role for accessibility
+ $toggler.addClass( 'mw-editfooter-toggler' ).prop( 'tabIndex', 0 ).attr( 'role', 'button' );
+ $list.addClass( 'mw-editfooter-list' );
+
+ $list.makeCollapsible( {
+ $customTogglers: $toggler,
+ linksPassthru: true,
+ plainMode: true,
+ collapsed: isCollapsed
+ } );
+
+ $toggler.addClass( isCollapsed ? 'mw-icon-arrow-collapsed' : 'mw-icon-arrow-expanded' );
+
+ $list.on( 'beforeExpand.mw-collapsible', function () {
+ $toggler.removeClass( 'mw-icon-arrow-collapsed' ).addClass( 'mw-icon-arrow-expanded' );
+ $.cookie( cookieName, 'expanded' );
+ } );
+
+ $list.on( 'beforeCollapse.mw-collapsible', function () {
+ $toggler.removeClass( 'mw-icon-arrow-expanded' ).addClass( 'mw-icon-arrow-collapsed' );
+ $.cookie( cookieName, 'collapsed' );
+ } );
+ };
+
+ for ( i = 0; i < collapsibleLists.length; i++ ) {
+ // Pass to a function for iteration-local variables
+ handleOne( collapsibleLists[i].$list, collapsibleLists[i].$toggler, collapsibleLists[i].cookieName );
+ }
+} );
diff --git a/resources/mediawiki.action/mediawiki.action.edit.editWarning.js b/resources/mediawiki.action/mediawiki.action.edit.editWarning.js
new file mode 100644
index 00000000..89bb64df
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.edit.editWarning.js
@@ -0,0 +1,56 @@
+/*
+ * Javascript for module editWarning
+ */
+( function ( mw, $ ) {
+ $( function () {
+ // Check if EditWarning is enabled and if we need it
+ if ( $( '#wpTextbox1' ).length === 0 ) {
+ return true;
+ }
+ // Get the original values of some form elements
+ $( '#wpTextbox1, #wpSummary' ).each( function () {
+ $( this ).data( 'origtext', $( this ).val() );
+ });
+ var savedWindowOnBeforeUnload;
+ $( window )
+ .on( 'beforeunload.editwarning', function () {
+ var retval;
+
+ // Check if the current values of some form elements are the same as
+ // the original values
+ if (
+ mw.config.get( 'wgAction' ) === 'submit' ||
+ $( '#wpTextbox1' ).data( 'origtext' ) !== $( '#wpTextbox1' ).val() ||
+ $( '#wpSummary' ).data( 'origtext' ) !== $( '#wpSummary' ).val()
+ ) {
+ // Return our message
+ retval = mw.msg( 'editwarning-warning' );
+ }
+
+ // Unset the onbeforeunload handler so we don't break page caching in Firefox
+ savedWindowOnBeforeUnload = window.onbeforeunload;
+ window.onbeforeunload = null;
+ if ( retval !== undefined ) {
+ // ...but if the user chooses not to leave the page, we need to rebind it
+ setTimeout( function () {
+ window.onbeforeunload = savedWindowOnBeforeUnload;
+ }, 1 );
+ return retval;
+ }
+ } )
+ .on( 'pageshow.editwarning', function () {
+ // Re-add onbeforeunload handler
+ if ( !window.onbeforeunload ) {
+ window.onbeforeunload = savedWindowOnBeforeUnload;
+ }
+ } );
+
+ // Add form submission handler
+ $( '#editform' ).submit( function () {
+ // Unbind our handlers
+ $( window ).off( '.editwarning' );
+ } );
+ } );
+
+}( mediaWiki, jQuery ) );
+
diff --git a/resources/mediawiki.action/mediawiki.action.edit.js b/resources/mediawiki.action/mediawiki.action.edit.js
index 2835c9cc..ba711aae 100644
--- a/resources/mediawiki.action/mediawiki.action.edit.js
+++ b/resources/mediawiki.action/mediawiki.action.edit.js
@@ -1,17 +1,20 @@
+/**
+ * Interface for the classic edit toolbar.
+ *
+ * @class mw.toolbar
+ * @singleton
+ */
( function ( mw, $ ) {
- var isReady, toolbar, currentFocused, queue, $toolbar, slice;
-
- isReady = false;
- queue = [];
- $toolbar = false;
- slice = Array.prototype.slice;
+ var toolbar, isReady, $toolbar, queue, slice, $currentFocused;
/**
- * Internal helper that does the actual insertion
- * of the button into the toolbar.
- * See mw.toolbar.addButton for parameter documentation.
+ * Internal helper that does the actual insertion of the button into the toolbar.
+ *
+ * See #addButton for parameter documentation.
+ *
+ * @private
*/
- function insertButton( b /* imageFile */, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
+ function insertButton( b, speedTip, tagOpen, tagClose, sampleText, imageId ) {
// Backwards compatibility
if ( typeof b !== 'object' ) {
b = {
@@ -20,11 +23,10 @@
tagOpen: tagOpen,
tagClose: tagClose,
sampleText: sampleText,
- imageId: imageId,
- selectText: selectText
+ imageId: imageId
};
}
- var $image = $( '<img>', {
+ var $image = $( '<img>' ).attr( {
width : 23,
height: 22,
src : b.imageFile,
@@ -33,30 +35,43 @@
id : b.imageId || undefined,
'class': 'mw-toolbar-editbutton'
} ).click( function () {
- toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText, b.selectText );
+ toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText );
return false;
} );
$toolbar.append( $image );
- return true;
}
+ isReady = false;
+ $toolbar = false;
+ /**
+ * @private
+ * @property {Array}
+ * Contains button objects (and for backwards compatibilty, it can
+ * also contains an arguments array for insertButton).
+ */
+ queue = [];
+ slice = queue.slice;
+
toolbar = {
+
/**
* Add buttons to the toolbar.
+ *
* Takes care of race conditions and time-based dependencies
* by placing buttons in a queue if this method is called before
* the toolbar is created.
- * @param {Object} button: Object with the following properties:
- * - imageFile
- * - speedTip
- * - tagOpen
- * - tagClose
- * - sampleText
- * - imageId
- * - selectText
- * For compatiblity, passing the above as separate arguments
+ *
+ * For compatiblity, passing the properties listed below as separate arguments
* (in the listed order) is also supported.
+ *
+ * @param {Object} button Object with the following properties:
+ * @param {string} button.imageFile
+ * @param {string} button.speedTip
+ * @param {string} button.tagOpen
+ * @param {string} button.tagClose
+ * @param {string} button.sampleText
+ * @param {string} [button.imageId]
*/
addButton: function () {
if ( isReady ) {
@@ -66,18 +81,44 @@
queue.push( slice.call( arguments ) );
}
},
+ /**
+ * Example usage:
+ * addButtons( [ { .. }, { .. }, { .. } ] );
+ * addButtons( { .. }, { .. } );
+ *
+ * @param {Object|Array} [buttons...] An array of button objects or the first
+ * button object in a list of variadic arguments.
+ */
+ addButtons: function ( buttons ) {
+ if ( !$.isArray( buttons ) ) {
+ buttons = slice.call( arguments );
+ }
+ if ( isReady ) {
+ $.each( buttons, function () {
+ insertButton( this );
+ } );
+ } else {
+ // Push each button into the queue
+ queue.push.apply( queue, buttons );
+ }
+ },
/**
- * Apply tagOpen/tagClose to selection in textarea,
- * use sampleText instead of selection if there is none.
+ * Apply tagOpen/tagClose to selection in currently focused textarea.
+ *
+ * Uses `sampleText` if selection is empty.
+ *
+ * @param {string} tagOpen
+ * @param {string} tagClose
+ * @param {string} sampleText
*/
insertTags: function ( tagOpen, tagClose, sampleText ) {
- if ( currentFocused && currentFocused.length ) {
- currentFocused.textSelection(
+ if ( $currentFocused && $currentFocused.length ) {
+ $currentFocused.textSelection(
'encapsulateSelection', {
- 'pre': tagOpen,
- 'peri': sampleText,
- 'post': tagClose
+ pre: tagOpen,
+ peri: sampleText,
+ post: tagClose
}
);
}
@@ -95,64 +136,58 @@
// Explose API publicly
mw.toolbar = toolbar;
- $( document ).ready( function () {
- var buttons, i, b, $iframe;
+ $( function () {
+ var i, b, $iframe, editBox, scrollTop, $editForm;
// currentFocus is used to determine where to insert tags
- currentFocused = $( '#wpTextbox1' );
+ $currentFocused = $( '#wpTextbox1' );
// Populate the selector cache for $toolbar
$toolbar = $( '#toolbar' );
- // Legacy: Merge buttons from mwCustomEditButtons
- buttons = [].concat( queue, window.mwCustomEditButtons );
- // Clear queue
- queue.length = 0;
- for ( i = 0; i < buttons.length; i++ ) {
- b = buttons[i];
+ for ( i = 0; i < queue.length; i++ ) {
+ b = queue[i];
if ( $.isArray( b ) ) {
// Forwarded arguments array from mw.toolbar.addButton
insertButton.apply( toolbar, b );
} else {
- // Raw object from legacy mwCustomEditButtons
+ // Raw object from mw.toolbar.addButtons
insertButton( b );
}
}
+ // Clear queue
+ queue.length = 0;
+
// This causes further calls to addButton to go to insertion directly
- // instead of to the toolbar.buttons queue.
+ // instead of to the queue.
// It is important that this is after the one and only loop through
- // the the toolbar.buttons queue
+ // the the queue
isReady = true;
// Make sure edit summary does not exceed byte limit
$( '#wpSummary' ).byteLimit( 255 );
- /**
- * Restore the edit box scroll state following a preview operation,
- * and set up a form submission handler to remember this state
- */
- ( function scrollEditBox() {
- var editBox, scrollTop, $editForm;
-
- editBox = document.getElementById( 'wpTextbox1' );
- scrollTop = document.getElementById( 'wpScrolltop' );
- $editForm = $( '#editform' );
- if ( $editForm.length && editBox && scrollTop ) {
- if ( scrollTop.value ) {
- editBox.scrollTop = scrollTop.value;
- }
- $editForm.submit( function () {
- scrollTop.value = editBox.scrollTop;
- });
+ // Restore the edit box scroll state following a preview operation,
+ // and set up a form submission handler to remember this state.
+ editBox = document.getElementById( 'wpTextbox1' );
+ scrollTop = document.getElementById( 'wpScrolltop' );
+ $editForm = $( '#editform' );
+ if ( $editForm.length && editBox && scrollTop ) {
+ if ( scrollTop.value ) {
+ editBox.scrollTop = scrollTop.value;
}
- }() );
+ $editForm.submit( function () {
+ scrollTop.value = editBox.scrollTop;
+ });
+ }
- $( 'textarea, input:text' ).focus( function () {
- currentFocused = $(this);
- });
+ // Apply to dynamically created textboxes as well as normal ones
+ $( document ).on( 'focus', 'textarea, input:text', function () {
+ $currentFocused = $( this );
+ } );
- // HACK: make currentFocused work with the usability iframe
+ // HACK: make $currentFocused work with the usability iframe
// With proper focus detection support (HTML 5!) this'll be much cleaner
// TODO: Get rid of this WikiEditor code from MediaWiki core!
$iframe = $( '.wikiEditor-ui-text iframe' );
@@ -161,7 +196,7 @@
// for IE
.add( $iframe.get( 0 ).contentWindow.document.body )
.focus( function () {
- currentFocused = $iframe;
+ $currentFocused = $iframe;
} );
}
});
diff --git a/resources/mediawiki.action/mediawiki.action.edit.preview.js b/resources/mediawiki.action/mediawiki.action.edit.preview.js
index 602aadb0..c5cd61ef 100644
--- a/resources/mediawiki.action/mediawiki.action.edit.preview.js
+++ b/resources/mediawiki.action/mediawiki.action.edit.preview.js
@@ -7,14 +7,16 @@
* @param {jQuery.Event} e
*/
function doLivePreview( e ) {
- var $wikiPreview, copySelectors, removeSelectors, $copyElements, $spinner,
+ var $wikiPreview, $editform, copySelectors, $copyElements, $spinner,
targetUrl, postData, $previewDataHolder;
e.preventDefault();
+ // Deprecated: Use mw.hook instead
$( mw ).trigger( 'LivePreviewPrepare' );
$wikiPreview = $( '#wikiPreview' );
+ $editform = $( '#editform' );
// Show #wikiPreview if it's hidden to be able to scroll to it
// (if it is hidden, it's also empty, so nothing changes in the rendering)
@@ -34,16 +36,13 @@
'#p-lang',
// Editing-related
'.templatesUsed',
+ '.limitreport',
'.mw-summary-preview'
];
$copyElements = $( copySelectors.join( ',' ) );
// Not shown during normal preview, to be removed if present
- removeSelectors = [
- '.mw-newarticletext'
- ];
-
- $( removeSelectors.join( ',' ) ).remove();
+ $( '.mw-newarticletext' ).remove();
$spinner = $.createSpinner( {
size: 'large',
@@ -51,36 +50,29 @@
});
$wikiPreview.before( $spinner );
$spinner.css( {
- position: 'absolute',
marginTop: $spinner.height()
} );
- // Make sure preview area is at least as tall as 2x the height of the spinner.
- // 1x because if its smaller, it will spin behind the edit toolbar.
- // (this happens on the first preview when editPreview is still empty)
- // 2x because the spinner has 1x margin top breathing room.
- $wikiPreview.css( 'minHeight', $spinner.height() * 2 );
// Can't use fadeTo because it calls show(), and we might want to keep some elements hidden
// (e.g. empty #catlinks)
- $copyElements.animate( {
- opacity: 0.4
- }, 'fast' );
+ $copyElements.animate( { opacity: 0.4 }, 'fast' );
$previewDataHolder = $( '<div>' );
- targetUrl = $( '#editform' ).attr( 'action' );
+ targetUrl = $editform.attr( 'action' );
// Gather all the data from the form
- postData = $( '#editform' ).formToArray();
+ postData = $editform.formToArray();
postData.push( {
name: e.target.name,
value: ''
} );
// Load new preview data.
- // TODO: This should use the action=parse API instead of loading the entire page
- // Though that requires figuring out how to conver that raw data into proper HTML.
+ // TODO: This should use the action=parse API instead of loading the entire page,
+ // although that requires figuring out how to convert that raw data into proper HTML.
$previewDataHolder.load( targetUrl + ' ' + copySelectors.join( ',' ), postData, function () {
var i, $from;
+
// Copy the contents of the specified elements from the loaded page to the real page.
// Also copy their class attributes.
for ( i = 0; i < copySelectors.length; i++ ) {
@@ -92,16 +84,19 @@
.attr( 'class', $from.attr( 'class' ) );
}
+ // Deprecated: Use mw.hook instead
+ $( mw ).trigger( 'LivePreviewDone', [copySelectors] );
+
+ mw.hook( 'wikipage.content' ).fire( $wikiPreview );
+
$spinner.remove();
$copyElements.animate( {
opacity: 1
}, 'fast' );
-
- $( mw ).trigger( 'LivePreviewDone', [copySelectors] );
} );
}
- $( document ).ready( function () {
+ $( function () {
// Do not enable on user .js/.css pages, as there's no sane way of "previewing"
// the scripts or styles without reloading the page.
if ( $( '#mw-userjsyoucanpreview' ).length || $( '#mw-usercssyoucanpreview' ).length ) {
@@ -109,32 +104,32 @@
}
// The following elements can change in a preview but are not output
- // by the server when they're empty until the preview reponse.
+ // by the server when they're empty until the preview response.
// TODO: Make the server output these always (in a hidden state), so we don't
// have to fish and (hopefully) put them in the right place (since skins
// can change where they are output).
if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) ) {
$( '#p-tb' ).after(
- $( '<div>' ).prop( 'id', 'p-lang' )
+ $( '<div>' ).attr( 'id', 'p-lang' )
);
}
if ( !$( '.mw-summary-preview' ).length ) {
$( '.editCheckboxes' ).before(
- $( '<div>' ).prop( 'className', 'mw-summary-preview' )
+ $( '<div>' ).addClass( 'mw-summary-preview' )
);
}
if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
$( '#wikiPreview' ).after(
- $( '<div>' ).prop( 'id', 'wikiDiff')
+ $( '<div>' ).attr( 'id', 'wikiDiff' )
);
}
- // Make sure diff styles are loaded
- mw.loader.load( 'mediawiki.action.history.diff' );
-
+ // This should be moved down to '#editform', but is kept on the body for now
+ // because the LiquidThreads extension is re-using this module with only half
+ // the EditPage (doesn't include #editform presumably, bug 55463).
$( document.body ).on( 'click', '#wpPreview, #wpDiff', doLivePreview );
} );
diff --git a/resources/mediawiki.action/mediawiki.action.edit.styles.css b/resources/mediawiki.action/mediawiki.action.edit.styles.css
new file mode 100644
index 00000000..4a2bab3d
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.edit.styles.css
@@ -0,0 +1,44 @@
+/**
+ * Styles for elements of the editing form.
+ */
+
+/* General layout */
+#wpTextbox1 {
+ margin: 0;
+ display: block;
+}
+
+.editOptions {
+ background-color: #F0F0F0;
+ border: 1px solid silver;
+ border-top: none;
+ padding: 1em 1em 1.5em 1em;
+ margin-bottom: 2em;
+}
+
+/* Adjustments to edit form elements */
+.editCheckboxes {
+ margin-bottom: 1em;
+}
+
+.editCheckboxes input:first-child {
+ margin-left: 0;
+}
+
+.cancelLink {
+ margin-left: 0.5em;
+}
+
+#editpage-copywarn {
+ font-size: 0.9em;
+}
+
+#wpSummary {
+ display: block;
+ margin-top: 0;
+ margin-bottom: 0.5em;
+}
+
+.editButtons input:first-child {
+ margin-left: .1em;
+}
diff --git a/resources/mediawiki.action/mediawiki.action.history.js b/resources/mediawiki.action/mediawiki.action.history.js
index e9d320c1..04f045a5 100644
--- a/resources/mediawiki.action/mediawiki.action.history.js
+++ b/resources/mediawiki.action/mediawiki.action.history.js
@@ -1,7 +1,7 @@
/**
* JavaScript for History action
*/
-jQuery( document ).ready( function ( $ ) {
+jQuery( function ( $ ) {
var $historyCompareForm = $( '#mw-history-compare' ),
$historySubmitter,
$lis = $( '#pagehistory > li' );
diff --git a/resources/mediawiki.action/mediawiki.action.view.postEdit.css b/resources/mediawiki.action/mediawiki.action.view.postEdit.css
new file mode 100644
index 00000000..be88337e
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.view.postEdit.css
@@ -0,0 +1,77 @@
+.postedit-container {
+ margin: 0 auto;
+ position: fixed;
+ top: 0;
+ height: 0;
+ left: 50%;
+ z-index: 1000;
+ font-size: 13px;
+}
+
+.postedit-container:hover {
+ cursor: pointer;
+}
+
+.postedit {
+ position: relative;
+ top: 0.6em;
+ left: -50%;
+ padding: .6em 3.6em .6em 1.1em;
+ line-height: 1.5625em;
+ color: #626465;
+ background-color: #f4f4f4;
+ border: 1px solid #dcd9d9;
+ text-shadow: 0 0.0625em 0 rgba(255, 255, 255, 0.5);
+ border-radius: 5px;
+ -webkit-box-shadow: 0 2px 5px 0 #ccc;
+ box-shadow: 0 2px 5px 0 #ccc;
+ -webkit-transition: all 0.25s ease-in-out;
+ -moz-transition: all 0.25s ease-in-out;
+ -ms-transition: all 0.25s ease-in-out;
+ -o-transition: all 0.25s ease-in-out;
+ transition: all 0.25s ease-in-out;
+}
+
+.skin-monobook .postedit {
+ top: 6em !important;
+}
+
+.postedit-faded {
+ opacity: 0;
+}
+
+.postedit-icon {
+ padding-left: 41px; /* 25 + 8 + 8 */
+ /* like min-height, but old IE compatible and keeps text vertically aligned, too */
+ line-height: 25px;
+ background-repeat: no-repeat;
+ background-position: 8px 50%;
+}
+
+.postedit-icon-checkmark {
+ /* @embed */
+ background-image: url(images/green-checkmark.png);
+ background-position: left;
+}
+
+.postedit-close {
+ position: absolute;
+ padding: 0 .8em;
+ right: 0;
+ top: 0;
+ font-size: 1.25em;
+ font-weight: bold;
+ line-height: 2.3em;
+ color: black;
+ text-shadow: 0 0.0625em 0 white;
+ text-decoration: none;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.postedit-close:hover {
+ color: black;
+ text-decoration: none;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
diff --git a/resources/mediawiki.action/mediawiki.action.view.postEdit.js b/resources/mediawiki.action/mediawiki.action.view.postEdit.js
index a11233fa..6e4df9f0 100644
--- a/resources/mediawiki.action/mediawiki.action.view.postEdit.js
+++ b/resources/mediawiki.action/mediawiki.action.view.postEdit.js
@@ -1,15 +1,76 @@
( function ( mw, $ ) {
- // Only a view can be a post-edit.
- if ( mw.config.get( 'wgAction' ) !== 'view' ) {
- return;
+ 'use strict';
+
+ /**
+ * @event postEdit
+ * @member mw.hook
+ * @param {Object} [data] Optional data
+ * @param {string|jQuery|Array} [data.message] Message that listeners
+ * should use when displaying notifications. String for plain text,
+ * use array or jQuery object to pass actual nodes.
+ * @param {string|mw.user} [data.user=mw.user] User that made the edit.
+ */
+
+ /**
+ * After the listener for #postEdit removes the notification.
+ *
+ * @event postEdit_afterRemoval
+ * @member mw.hook
+ */
+
+ var config = mw.config.get( [ 'wgAction', 'wgCookiePrefix', 'wgCurRevisionId' ] ),
+ // This should match EditPage::POST_EDIT_COOKIE_KEY_PREFIX:
+ cookieKey = config.wgCookiePrefix + 'PostEditRevision' + config.wgCurRevisionId,
+ $div, id;
+
+ function showConfirmation( data ) {
+ data = data || {};
+ if ( data.message === undefined ) {
+ data.message = $.parseHTML( mw.message( 'postedit-confirmation', data.user || mw.user ).escaped() );
+ }
+
+ $div = $(
+ '<div class="postedit-container">' +
+ '<div class="postedit">' +
+ '<div class="postedit-icon postedit-icon-checkmark postedit-content"></div>' +
+ '<a href="#" class="postedit-close">&times;</a>' +
+ '</div>' +
+ '</div>'
+ );
+
+ if ( typeof data.message === 'string' ) {
+ $div.find( '.postedit-content' ).text( data.message );
+ } else if ( typeof data.message === 'object' ) {
+ $div.find( '.postedit-content' ).append( data.message );
+ }
+
+ $div
+ .click( fadeOutConfirmation )
+ .prependTo( 'body' );
+
+ id = setTimeout( fadeOutConfirmation, 3000 );
}
- // Matches EditPage::POST_EDIT_COOKIE_KEY_PREFIX
- var cookieKey = mw.config.get( 'wgCookiePrefix' ) + 'PostEditRevision' + mw.config.get( 'wgCurRevisionId' );
+ function fadeOutConfirmation() {
+ clearTimeout( id );
+ $div.find( '.postedit' ).addClass( 'postedit postedit-faded' );
+ setTimeout( removeConfirmation, 500 );
- if ( $.cookie( cookieKey ) === '1' ) {
- // We just saved this page
+ return false;
+ }
+
+ function removeConfirmation() {
+ $div.remove();
+ mw.hook( 'postEdit.afterRemoval' ).fire();
+ }
+
+ mw.hook( 'postEdit' ).add( showConfirmation );
+
+ if ( config.wgAction === 'view' && $.cookie( cookieKey ) === '1' ) {
$.cookie( cookieKey, null, { path: '/' } );
mw.config.set( 'wgPostEdit', true );
+
+ mw.hook( 'postEdit' ).fire();
}
+
} ( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js b/resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js
index 61d9d150..93befe3a 100644
--- a/resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js
+++ b/resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js
@@ -5,10 +5,10 @@
*/
jQuery( function ( $ ) {
// Select all h1-h6 elements that contain editsection links
- // Don't use the ":has:(.editsection a)" selector because it performs very bad.
+ // Don't use the ":has:(.mw-editsection a)" selector because it performs very bad.
// http://jsperf.com/jq-1-7-2-vs-jq-1-8-1-performance-of-mw-has/2
$( document ).on( 'contextmenu', 'h1, h2, h3, h4, h5, h6', function ( e ) {
- var $edit = $( this ).find( '.editsection a' );
+ var $edit = $( this ).find( '.mw-editsection a' );
if ( !$edit.length ) {
return;
}
diff --git a/resources/mediawiki.api/mediawiki.api.category.js b/resources/mediawiki.api/mediawiki.api.category.js
index 4de52911..98a9c54b 100644
--- a/resources/mediawiki.api/mediawiki.api.category.js
+++ b/resources/mediawiki.api/mediawiki.api.category.js
@@ -14,12 +14,13 @@
* @return {boolean} return.done.isCategory Whether the category exists.
*/
isCategory: function ( title, ok, err ) {
- var d = $.Deferred();
+ var d = $.Deferred(),
+ apiPromise;
+
// Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
+ d.done( ok ).fail( err );
- this.get( {
+ apiPromise = this.get( {
prop: 'categoryinfo',
titles: title.toString()
} )
@@ -36,7 +37,7 @@
})
.fail( d.reject );
- return d.promise();
+ return d.promise( { abort: apiPromise.abort } );
},
/**
@@ -50,13 +51,14 @@
* @return {String[]} return.done.categories Matched categories
*/
getCategoriesByPrefix: function ( prefix, ok, err ) {
- var d = $.Deferred();
+ var d = $.Deferred(),
+ apiPromise;
+
// Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
+ d.done( ok ).fail( err );
// Fetch with allpages to only get categories that have a corresponding description page.
- this.get( {
+ apiPromise = this.get( {
list: 'allpages',
apprefix: prefix,
apnamespace: mw.config.get('wgNamespaceIds').category
@@ -72,7 +74,7 @@
})
.fail( d.reject );
- return d.promise();
+ return d.promise( { abort: apiPromise.abort } );
},
@@ -88,12 +90,13 @@
* if title was not found.
*/
getCategories: function ( title, ok, err, async ) {
- var d = $.Deferred();
+ var d = $.Deferred(),
+ apiPromise;
+
// Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
+ d.done( ok ).fail( err );
- this.get( {
+ apiPromise = this.get( {
prop: 'categories',
titles: title.toString()
}, {
@@ -114,10 +117,10 @@
} );
}
d.resolve( ret );
- })
+ } )
.fail( d.reject );
- return d.promise();
+ return d.promise( { abort: apiPromise.abort } );
}
} );
diff --git a/resources/mediawiki.api/mediawiki.api.edit.js b/resources/mediawiki.api/mediawiki.api.edit.js
index 3c775ad0..cc83a4b8 100644
--- a/resources/mediawiki.api/mediawiki.api.edit.js
+++ b/resources/mediawiki.api/mediawiki.api.edit.js
@@ -3,9 +3,6 @@
*/
( function ( mw, $ ) {
- // Cache token so we don't have to keep fetching new ones for every single request.
- var cachedToken = null;
-
$.extend( mw.Api.prototype, {
/**
@@ -19,32 +16,7 @@
* @return {jQuery.Promise} See #post
*/
postWithEditToken: function ( params, ok, err ) {
- var useTokenToPost, getTokenIfBad,
- api = this;
- if ( cachedToken === null ) {
- // We don't have a valid cached token, so get a fresh one and try posting.
- // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
- // an infinite loop. If this fresh token is bad, something else is very wrong.
- useTokenToPost = function ( token ) {
- params.token = token;
- api.post( params, ok, err );
- };
- return api.getEditToken( useTokenToPost, err );
- } else {
- // We do have a token, but it might be expired. So if it is 'bad' then
- // start over with a new token.
- params.token = cachedToken;
- getTokenIfBad = function ( code, result ) {
- if ( code === 'badtoken' ) {
- // force a new token, clear any old one
- cachedToken = null;
- api.postWithEditToken( params, ok, err );
- } else {
- err( code, result );
- }
- };
- return api.post( params, { ok : ok, err : getTokenIfBad });
- }
+ return this.postWithToken( 'edit', params ).done( ok ).fail( err );
},
/**
@@ -57,37 +29,7 @@
* @return {string} return.done.token Received token.
*/
getEditToken: function ( ok, err ) {
- var d = $.Deferred();
- // Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
-
- this.get( {
- action: 'tokens',
- type: 'edit'
- }, {
- // Due to the API assuming we're logged out if we pass the callback-parameter,
- // we have to disable jQuery's callback system, and instead parse JSON string,
- // by setting 'jsonp' to false.
- // TODO: This concern seems genuine but no other module has it. Is it still
- // needed and/or should we pass this by default?
- jsonp: false
- } )
- .done( function ( data ) {
- var token;
- // If token type is not available for this user,
- // key 'edittoken' is missing or can contain Boolean false
- if ( data.tokens && data.tokens.edittoken ) {
- token = data.tokens.edittoken;
- cachedToken = token;
- d.resolve( token );
- } else {
- d.reject( 'token-missing', data );
- }
- })
- .fail( d.reject );
-
- return d.promise();
+ return this.getToken( 'edit' ).done( ok ).fail( err );
},
/**
@@ -110,8 +52,7 @@
text: message
}, ok, err );
}
-
- } );
+ } );
/**
* @class mw.Api
diff --git a/resources/mediawiki.api/mediawiki.api.js b/resources/mediawiki.api/mediawiki.api.js
index cf7443f3..cdc67679 100644
--- a/resources/mediawiki.api/mediawiki.api.js
+++ b/resources/mediawiki.api/mediawiki.api.js
@@ -20,7 +20,8 @@
dataType: 'json'
}
- };
+ },
+ tokenCache = {};
/**
* Constructor to create an object to interact with the API of a particular MediaWiki server.
@@ -115,7 +116,8 @@
*/
ajax: function ( parameters, ajaxOptions ) {
var token,
- apiDeferred = $.Deferred();
+ apiDeferred = $.Deferred(),
+ xhr;
parameters = $.extend( {}, this.defaults.parameters, parameters );
ajaxOptions = $.extend( {}, this.defaults.ajax, ajaxOptions );
@@ -147,7 +149,7 @@
}
// Make the AJAX request
- $.ajax( ajaxOptions )
+ xhr = $.ajax( ajaxOptions )
// If AJAX fails, reject API call with error code 'http'
// and details in second argument.
.fail( function ( xhr, textStatus, exception ) {
@@ -172,11 +174,85 @@
} );
// Return the Promise
- return apiDeferred.promise().fail( function ( code, details ) {
+ return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) {
mw.log( 'mw.Api error: ', code, details );
- });
- }
+ } );
+ },
+
+ /**
+ * Post to API with specified type of token. If we have no token, get one and try to post.
+ * If we have a cached token try using that, and if it fails, blank out the
+ * cached token and start over. For example to change an user option you could do:
+ *
+ * new mw.Api().postWithToken( 'options', {
+ * action: 'options',
+ * optionname: 'gender',
+ * optionvalue: 'female'
+ * } );
+ *
+ * @param {string} tokenType The name of the token, like options or edit.
+ * @param {Object} params API parameters
+ * @return {jQuery.Promise} See #post
+ */
+ postWithToken: function ( tokenType, params ) {
+ var api = this, hasOwn = tokenCache.hasOwnProperty;
+ if ( hasOwn.call( tokenCache, tokenType ) && tokenCache[tokenType] !== undefined ) {
+ params.token = tokenCache[tokenType];
+ return api.post( params ).then(
+ null,
+ function ( code ) {
+ if ( code === 'badtoken' ) {
+ // force a new token, clear any old one
+ tokenCache[tokenType] = params.token = undefined;
+ return api.post( params );
+ }
+ // Pass the promise forward, so the caller gets error codes
+ return this;
+ }
+ );
+ } else {
+ return api.getToken( tokenType ).then( function ( token ) {
+ tokenCache[tokenType] = params.token = token;
+ return api.post( params );
+ } );
+ }
+ },
+ /**
+ * Api helper to grab any token.
+ *
+ * @param {string} type Token type.
+ * @return {jQuery.Promise}
+ * @return {Function} return.done
+ * @return {string} return.done.token Received token.
+ */
+ getToken: function ( type ) {
+ var apiPromise,
+ d = $.Deferred();
+
+ apiPromise = this.get( {
+ action: 'tokens',
+ type: type
+ }, {
+ // Due to the API assuming we're logged out if we pass the callback-parameter,
+ // we have to disable jQuery's callback system, and instead parse JSON string,
+ // by setting 'jsonp' to false.
+ // TODO: This concern seems genuine but no other module has it. Is it still
+ // needed and/or should we pass this by default?
+ } )
+ .done( function ( data ) {
+ // If token type is not available for this user,
+ // key '...token' is missing or can contain Boolean false
+ if ( data.tokens && data.tokens[type + 'token'] ) {
+ d.resolve( data.tokens[type + 'token'] );
+ } else {
+ d.reject( 'token-missing', data );
+ }
+ } )
+ .fail( d.reject );
+
+ return d.promise( { abort: apiPromise.abort } );
+ }
};
/**
diff --git a/resources/mediawiki.api/mediawiki.api.login.js b/resources/mediawiki.api/mediawiki.api.login.js
new file mode 100644
index 00000000..ccbae06c
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.login.js
@@ -0,0 +1,54 @@
+/**
+ * Make the two-step login easier.
+ * @author Niklas Laxström
+ * @class mw.Api.plugin.login
+ * @since 1.22
+ */
+( function ( mw, $ ) {
+ 'use strict';
+
+ $.extend( mw.Api.prototype, {
+ /**
+ * @param {string} username
+ * @param {string} password
+ * @return {jQuery.Promise} See mw.Api#post
+ */
+ login: function ( username, password ) {
+ var params, request,
+ deferred = $.Deferred(),
+ api = this;
+
+ params = {
+ action: 'login',
+ lgname: username,
+ lgpassword: password
+ };
+
+ request = api.post( params );
+ request.fail( deferred.reject );
+ request.done( function ( data ) {
+ params.lgtoken = data.login.token;
+ api.post( params )
+ .fail( deferred.reject )
+ .done( function ( data ) {
+ var code;
+ if ( data.login && data.login.result === 'Success' ) {
+ deferred.resolve( data );
+ } else {
+ // Set proper error code whenever possible
+ code = data.error && data.error.code || 'unknown';
+ deferred.reject( code, data );
+ }
+ } );
+ } );
+
+ return deferred.promise( { abort: request.abort } );
+ }
+ } );
+
+ /**
+ * @class mw.Api
+ * @mixins mw.Api.plugin.login
+ */
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.api/mediawiki.api.parse.js b/resources/mediawiki.api/mediawiki.api.parse.js
index ea0388c1..c4d23b82 100644
--- a/resources/mediawiki.api/mediawiki.api.parse.js
+++ b/resources/mediawiki.api/mediawiki.api.parse.js
@@ -15,13 +15,15 @@
* @return {string} return.done.data Parsed HTML of `wikitext`.
*/
parse: function ( wikitext, ok, err ) {
- var d = $.Deferred();
+ var d = $.Deferred(),
+ apiPromise;
+
// Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
+ d.done( ok ).fail( err );
- this.get( {
+ apiPromise = this.get( {
action: 'parse',
+ contentmodel: 'wikitext',
text: wikitext
} )
.done( function ( data ) {
@@ -31,7 +33,7 @@
} )
.fail( d.reject );
- return d.promise();
+ return d.promise( { abort: apiPromise.abort } );
}
} );
diff --git a/resources/mediawiki.api/mediawiki.api.watch.js b/resources/mediawiki.api/mediawiki.api.watch.js
index c86a90a7..49a4c622 100644
--- a/resources/mediawiki.api/mediawiki.api.watch.js
+++ b/resources/mediawiki.api/mediawiki.api.watch.js
@@ -19,10 +19,12 @@
* @return {string} return.done.watch.message Parsed HTML of the confirmational interface message
*/
function doWatchInternal( page, ok, err, addParams ) {
- var params, d = $.Deferred();
+ var params,
+ d = $.Deferred(),
+ apiPromise;
+
// Backwards compatibility (< MW 1.20)
- d.done( ok );
- d.fail( err );
+ d.done( ok ).fail( err );
params = {
action: 'watch',
@@ -35,13 +37,13 @@
$.extend( params, addParams );
}
- this.post( params )
+ apiPromise = this.post( params )
.done( function ( data ) {
d.resolve( data.watch );
} )
.fail( d.reject );
- return d.promise();
+ return d.promise( { abort: apiPromise.abort } );
}
$.extend( mw.Api.prototype, {
diff --git a/resources/mediawiki.language/mediawiki.language.js b/resources/mediawiki.language/mediawiki.language.js
index 7f729bdc..631d13df 100644
--- a/resources/mediawiki.language/mediawiki.language.js
+++ b/resources/mediawiki.language/mediawiki.language.js
@@ -45,11 +45,34 @@ var language = {
*/
convertPlural: function ( count, forms ) {
var pluralRules,
+ formCount,
+ form,
+ index,
+ equalsPosition,
pluralFormIndex = 0;
if ( !forms || forms.length === 0 ) {
return '';
}
+
+ // Handle for explicit n= forms
+ for ( index = 0; index < forms.length; index++ ) {
+ form = forms[index];
+ if ( /^\d+=/.test( form ) ) {
+ equalsPosition = form.indexOf( '=' );
+ formCount = parseInt( form.substring( 0, equalsPosition ), 10 );
+ if ( formCount === count ) {
+ return form.substr( equalsPosition + 1 );
+ }
+ forms[index] = undefined;
+ }
+ }
+
+ // Remove explicit plural forms from the forms.
+ forms = $.map( forms, function ( form ) {
+ return form;
+ } );
+
pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' );
if ( !pluralRules ) {
// default fallback.
diff --git a/resources/mediawiki.language/mediawiki.language.months.js b/resources/mediawiki.language/mediawiki.language.months.js
new file mode 100644
index 00000000..3d4b7ee7
--- /dev/null
+++ b/resources/mediawiki.language/mediawiki.language.months.js
@@ -0,0 +1,54 @@
+/**
+ * Transfer of month names from messages into mw.language.
+ *
+ * Loading this module also ensures the availability of appropriate messages via mw.msg.
+ */
+( function ( mw, $ ) {
+ var
+ monthMessages = [
+ 'january', 'february', 'march', 'april',
+ 'may_long', 'june', 'july', 'august',
+ 'september', 'october', 'november', 'december'
+ ],
+ monthGenMessages = [
+ 'january-gen', 'february-gen', 'march-gen', 'april-gen',
+ 'may-gen', 'june-gen', 'july-gen', 'august-gen',
+ 'september-gen', 'october-gen', 'november-gen', 'december-gen'
+ ],
+ monthAbbrevMessages = [
+ 'jan', 'feb', 'mar', 'apr',
+ 'may', 'jun', 'jul', 'aug',
+ 'sep', 'oct', 'nov', 'dec'
+ ];
+
+ // Function suitable for passing to jQuery.map
+ // Can't use mw.msg directly because jQuery.map passes element index as second argument
+ function mwMsgMapper( key ) {
+ return mw.msg( key );
+ }
+
+ /**
+ * Information about month names in current UI language.
+ *
+ * Object keys:
+ * - `names`: array of month names (in nominative case in languages which have the distinction),
+ * zero-indexed
+ * - `genitive`: array of month names in genitive case, zero-indexed
+ * - `abbrev`: array of three-letter-long abbreviated month names, zero-indexed
+ * - `keys`: object with three keys like the above, containing zero-indexed arrays of message keys
+ * for appropriate messages which can be passed to mw.msg.
+ *
+ * @property
+ */
+ mw.language.months = {
+ keys: {
+ names: monthMessages,
+ genitive: monthGenMessages,
+ abbrev: monthAbbrevMessages
+ },
+ names: $.map( monthMessages, mwMsgMapper ),
+ genitive: $.map( monthGenMessages, mwMsgMapper ),
+ abbrev: $.map( monthAbbrevMessages, mwMsgMapper )
+ };
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.less/mediawiki.mixins.less b/resources/mediawiki.less/mediawiki.mixins.less
new file mode 100644
index 00000000..19a715b9
--- /dev/null
+++ b/resources/mediawiki.less/mediawiki.mixins.less
@@ -0,0 +1,46 @@
+/**
+ * Common LESS mixin library for MediaWiki
+ *
+ * By default the folder containing this file is included in $wgResourceLoaderLESSImportPaths,
+ * which makes this file importable by all less files via '@import "mediawiki.mixins";'.
+ *
+ * The mixins included below are considered a public interface for MediaWiki extensions.
+ * The signatures of parametrized mixins should be kept as stable as possible.
+ *
+ * See <http://lesscss.org/#-mixins> for more information about how to write mixins.
+ */
+
+.background-image(@url) when (embeddable(@url)) {
+ background-image: embed(@url);
+ background-image: url(@url)!ie;
+}
+
+.background-image(@url) when not (embeddable(@url)) {
+ background-image: url(@url);
+}
+
+/* Note gzip compression means that it is okay to embed twice */
+.background-image-svg(@svg, @fallback) {
+ background-image: url(@fallback);
+ /* SVG support using a transparent gradient to guarantee cross-browser
+ * compatibility (browsers able to understand gradient syntax support also SVG) */
+ /* @embed */ background-image: -webkit-linear-gradient(transparent, transparent), url(@svg);
+ /* @embed */ background-image: linear-gradient(transparent, transparent), url(@svg);
+}
+
+/* Caution: Does not support localisable images */
+.list-style-image(@url) when (embeddable(@url)) {
+ list-style-image: embed(@url);
+ list-style-image: url(@url)!ie;
+}
+
+.list-style-image(@url) when not (embeddable(@url)) {
+ list-style-image: url(@url);
+}
+
+.transition(@string) {
+ -webkit-transition: @string;
+ -moz-transition: @string;
+ -o-transition: @string;
+ transition: @string;
+}
diff --git a/resources/mediawiki.libs/mediawiki.libs.jpegmeta.js b/resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
index af49889a..22429246 100644
--- a/resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
+++ b/resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
@@ -274,7 +274,7 @@
this.JpegMeta.JpegFile.prototype._JFIF_IDENT = "JFIF\x00";
this.JpegMeta.JpegFile.prototype._JFXX_IDENT = "JFXX\x00";
- /* EXIF idents */
+ /* Exif idents */
this.JpegMeta.JpegFile.prototype._EXIF_IDENT = "Exif\x00";
/* TIFF types */
diff --git a/resources/mediawiki.page/mediawiki.page.gallery.js b/resources/mediawiki.page/mediawiki.page.gallery.js
new file mode 100644
index 00000000..147a869d
--- /dev/null
+++ b/resources/mediawiki.page/mediawiki.page.gallery.js
@@ -0,0 +1,248 @@
+/**
+ * Show gallery captions when focused. Copied directly from jquery.mw-jump.js.
+ * Also Dynamically resize images to justify them.
+ */
+( function ( $, mw ) {
+ $( function () {
+ var isTouchScreen,
+ gettingFocus,
+ galleries = 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed';
+
+ // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
+ isTouchScreen = !!( window.ontouchstart !== undefined || window.DocumentTouch !== undefined && document instanceof window.DocumentTouch );
+
+ if ( isTouchScreen ) {
+ // Always show the caption for a touch screen.
+ $( 'ul.mw-gallery-packed-hover' )
+ .addClass( 'mw-gallery-packed-overlay' )
+ .removeClass( 'mw-gallery-packed-hover' );
+ } else {
+ // Note use of just "a", not a.image, since we want this to trigger if a link in
+ // the caption receives focus
+ $( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
+ // Confusingly jQuery leaves e.type as focusout for delegated blur events
+ gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
+ $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
+ } );
+ }
+
+ // Now on to justification.
+ // We may still get ragged edges if someone resizes their window. Could bind to
+ // that event, otoh do we really want to constantly be resizing galleries?
+ $( galleries ).each( function() {
+ var lastTop,
+ $img,
+ imgWidth,
+ imgHeight,
+ rows = [],
+ $gallery = $( this );
+
+ $gallery.children( 'li' ).each( function() {
+ // Math.floor to be paranoid if things are off by 0.00000000001
+ var top = Math.floor( $(this ).position().top ),
+ $this = $( this );
+
+ if ( top !== lastTop ) {
+ rows[rows.length] = [];
+ lastTop = top;
+ }
+
+ $img = $this.find( 'div.thumb a.image img' );
+ if ( $img.length && $img[0].height ) {
+ imgHeight = $img[0].height;
+ imgWidth = $img[0].width;
+ } else {
+ // If we don't have a real image, get the containing divs width/height.
+ // Note that if we do have a real image, using this method will generally
+ // give the same answer, but can be different in the case of a very
+ // narrow image where extra padding is added.
+ imgHeight = $this.children().children( 'div:first' ).height();
+ imgWidth = $this.children().children( 'div:first' ).width();
+ }
+
+ // Hack to make an edge case work ok
+ if ( imgHeight < 30 ) {
+ // Don't try and resize this item.
+ imgHeight = 0;
+ }
+
+ rows[rows.length-1][rows[rows.length-1].length] = {
+ $elm: $this,
+ width: $this.outerWidth(),
+ imgWidth: imgWidth,
+ aspect: imgWidth / imgHeight, // XXX: can divide by 0 ever happen?
+ captionWidth: $this.children().children( 'div.gallerytextwrapper' ).width(),
+ height: imgHeight
+ };
+ });
+
+ (function () {
+ var maxWidth,
+ combinedAspect,
+ combinedPadding,
+ curRow,
+ curRowHeight,
+ wantedWidth,
+ preferredHeight,
+ newWidth,
+ padding,
+ $outerDiv,
+ $innerDiv,
+ $imageDiv,
+ $imageElm,
+ imageElm,
+ $caption,
+ hookInfo,
+ i,
+ j,
+ avgZoom,
+ totalZoom = 0;
+
+ for ( i = 0; i < rows.length; i++ ) {
+ maxWidth = $gallery.width();
+ combinedAspect = 0;
+ combinedPadding = 0;
+ curRow = rows[i];
+ curRowHeight = 0;
+
+ for ( j = 0; j < curRow.length; j++ ) {
+ if ( curRowHeight === 0 ) {
+ if ( isFinite( curRow[j].height ) ) {
+ // Get the height of this row, by taking the first
+ // non-out of bounds height
+ curRowHeight = curRow[j].height;
+ }
+ }
+
+ if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) {
+ mw.log( 'Skipping item ' + j + ' due to aspect: ' + curRow[j].aspect );
+ // One of the dimensions are 0. Probably should
+ // not try to resize.
+ combinedPadding += curRow[j].width;
+ } else {
+ combinedAspect += curRow[j].aspect;
+ combinedPadding += curRow[j].width - curRow[j].imgWidth;
+ }
+ }
+
+ // Add some padding for inter-element spacing.
+ combinedPadding += 5 * curRow.length;
+ wantedWidth = maxWidth - combinedPadding;
+ preferredHeight = wantedWidth / combinedAspect;
+
+ if ( preferredHeight > curRowHeight * 1.5 ) {
+ // Only expand at most 1.5 times current size
+ // As that's as high a resolution as we have.
+ // Also on the off chance there is a bug in this
+ // code, would prevent accidentally expanding to
+ // be 10 billion pixels wide.
+ mw.log( 'mw.page.gallery: Cannot fit row, aspect is ' + preferredHeight/curRowHeight );
+ if ( i === rows.length - 1 ) {
+ // If its the last row, and we can't fit it,
+ // don't make the entire row huge.
+ avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
+ if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
+ preferredHeight = avgZoom;
+ } else {
+ // Probably a single row gallery
+ preferredHeight = curRowHeight;
+ }
+ } else {
+ preferredHeight = 1.5 * curRowHeight;
+ }
+ }
+ if ( !isFinite( preferredHeight ) ) {
+ // This *definitely* should not happen.
+ mw.log( 'mw.page.gallery: Trying to resize row ' + i + ' to ' + preferredHeight + '?!' );
+ // Skip this row.
+ continue;
+ }
+ if ( preferredHeight < 5 ) {
+ // Well something clearly went wrong...
+ mw.log( {maxWidth: maxWidth, combinedPadding: combinedPadding, combinedAspect: combinedAspect, wantedWidth: wantedWidth } );
+ mw.log( 'mw.page.gallery: [BUG!] Fitting row ' + i + ' to too small a size: ' + preferredHeight );
+ // Skip this row.
+ continue;
+ }
+
+ if ( preferredHeight / curRowHeight > 1 ) {
+ totalZoom += preferredHeight / curRowHeight;
+ } else {
+ // If we shrink, still consider that a zoom of 1
+ totalZoom += 1;
+ }
+
+ for ( j = 0; j < curRow.length; j++ ) {
+ newWidth = preferredHeight * curRow[j].aspect;
+ padding = curRow[j].width - curRow[j].imgWidth;
+ $outerDiv = curRow[j].$elm;
+ $innerDiv = $outerDiv.children( 'div' ).first();
+ $imageDiv = $innerDiv.children( 'div.thumb' );
+ $imageElm = $imageDiv.find( 'img' ).first();
+ imageElm = $imageElm.length ? $imageElm[0] : null;
+ $caption = $outerDiv.find( 'div.gallerytextwrapper' );
+
+
+ // Since we are going to re-adjust the height, the vertical
+ // centering margins need to be reset.
+ $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
+
+ if ( newWidth < 60 || !isFinite( newWidth ) ) {
+ // Making something skinnier than this will mess up captions,
+ mw.log( 'mw.page.gallery: Tried to make image ' + newWidth + 'px wide but too narrow.' );
+ if ( newWidth < 1 || !isFinite( newWidth ) ) {
+ $innerDiv.height( preferredHeight );
+ // Don't even try and touch the image size if it could mean
+ // making it disappear.
+ continue;
+ }
+ } else {
+ $outerDiv.width( newWidth + padding );
+ $innerDiv.width( newWidth + padding );
+ $imageDiv.width( newWidth );
+ $caption.width( curRow[j].captionWidth + (newWidth - curRow[j].imgWidth ) );
+ }
+
+ hookInfo = {
+ fullWidth: newWidth + padding,
+ imgWidth: newWidth,
+ imgHeight: preferredHeight,
+ $innerDiv: $innerDiv,
+ $imageDiv: $imageDiv,
+ $outerDiv: $outerDiv,
+ // Whether the hook took action
+ resolved: false
+ };
+
+ /**
+ * Gallery resize.
+ *
+ * If your handler resizes an image, it should also set the resolved
+ * property to true. Additionally, because this module only exposes this
+ * logic temporarily, you should load your module in position top to
+ * ensure it is registered before this runs (FIXME: Don't use mw.hook)
+ *
+ * See TimedMediaHandler for an example.
+ *
+ * @event mediawiki_page_gallery_resize
+ * @member mw.hook
+ * @param {Object} hookInfo
+ */
+ mw.hook( 'mediawiki.page.gallery.resize' ).fire( hookInfo );
+
+ if ( !hookInfo.resolved ) {
+ if ( imageElm ) {
+ // We don't always have an img, e.g. in the case of an invalid file.
+ imageElm.width = newWidth;
+ imageElm.height = preferredHeight;
+ } else {
+ // Not a file box.
+ $imageDiv.height( preferredHeight );
+ }
+ }
+ }
+ }
+ } )();
+ } );
+ } );
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.page/mediawiki.page.image.pagination.js b/resources/mediawiki.page/mediawiki.page.image.pagination.js
new file mode 100644
index 00000000..fb44a76f
--- /dev/null
+++ b/resources/mediawiki.page/mediawiki.page.image.pagination.js
@@ -0,0 +1,51 @@
+/**
+ * Change multi-page image navigation so that the current page display can be changed
+ * without a page reload. Currently, the only image formats that can be multi-page images are
+ * PDF and DjVu files
+ */
+( function (mw, $) {
+ // Use jQuery's load function to specifically select and replace table.multipageimage's child
+ // tr with the new page's table.multipageimage's tr element.
+ // table.multipageimage always has only one row.
+ function loadPage( page ) {
+ var $multipageimage = $( 'table.multipageimage' ),
+ $spinner = $.createSpinner( {
+ size: 'large',
+ type: 'block'
+ } );
+
+ // Set the spinner's dimensions equal to the table's dimensions so that
+ // the current scroll position is not lost after the table is emptied prior to
+ // its contents being updated
+ $spinner.css( {
+ height: $multipageimage.find( 'tr' ).height(),
+ width: $multipageimage.find( 'tr' ).width()
+ } );
+
+ $multipageimage.empty().append( $spinner ).load(
+ page + ' table.multipageimage tr',
+ ajaxifyPageNavigation
+ );
+ }
+
+ function ajaxifyPageNavigation() {
+ // Intercept the default action of the links in the thumbnail navigation
+ $( '.multipageimagenavbox' ).one( 'click', 'a', function ( e ) {
+ loadPage( this.href );
+ e.preventDefault();
+ } );
+
+ // Prevent the submission of the page select form and instead call loadPage
+ $( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) {
+ loadPage( this.action + '?' + $( this ).serialize() );
+ e.preventDefault();
+ } );
+ }
+
+ $( document ).ready( function() {
+ // The presence of table.multipageimage signifies that this file is a multi-page image
+ if( mw.config.get( 'wgNamespaceNumber' ) === 6 && $( 'table.multipageimage' ).length !== 0 ) {
+ ajaxifyPageNavigation();
+ }
+ } );
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.page/mediawiki.page.patrol.ajax.js b/resources/mediawiki.page/mediawiki.page.patrol.ajax.js
index d7a07d71..75908eee 100644
--- a/resources/mediawiki.page/mediawiki.page.patrol.ajax.js
+++ b/resources/mediawiki.page/mediawiki.page.patrol.ajax.js
@@ -11,7 +11,7 @@
// that didn't have patrolToken yet.
return;
}
- $( document ).ready( function () {
+ $( function () {
var $patrolLinks = $( '.patrollink a' );
$patrolLinks.on( 'click', function ( e ) {
var $spinner, href, rcid, apiRequest;
diff --git a/resources/mediawiki.page/mediawiki.page.ready.js b/resources/mediawiki.page/mediawiki.page.ready.js
index 684f582f..ee416d67 100644
--- a/resources/mediawiki.page/mediawiki.page.ready.js
+++ b/resources/mediawiki.page/mediawiki.page.ready.js
@@ -1,28 +1,40 @@
-( function ( mw, $ ) {
- $( function () {
+( function ( mw , $ ) {
+ var supportsPlaceholder = 'placeholder' in document.createElement( 'input' );
+
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
var $sortableTables;
- /* Emulate placeholder if not supported by browser */
- if ( !( 'placeholder' in document.createElement( 'input' ) ) ) {
- $( 'input[placeholder]' ).placeholder();
+ // Run jquery.placeholder polyfill if placeholder is not supported
+ if ( !supportsPlaceholder ) {
+ $content.find( 'input[placeholder]' ).placeholder();
}
- /* Enable makeCollapsible */
- $( '.mw-collapsible' ).makeCollapsible();
+ // Run jquery.makeCollapsible
+ $content.find( '.mw-collapsible' ).makeCollapsible();
- /* Lazy load jquery.tablesorter */
- $sortableTables = $( 'table.sortable' );
+ // Lazy load jquery.tablesorter
+ $sortableTables = $content.find( 'table.sortable' );
if ( $sortableTables.length ) {
mw.loader.using( 'jquery.tablesorter', function () {
$sortableTables.tablesorter();
- });
+ } );
}
- /* Enable CheckboxShiftClick */
- $( 'input[type=checkbox]:not(.noshiftselect)' ).checkboxShiftClick();
+ // Run jquery.checkboxShiftClick
+ $content.find( 'input[type="checkbox"]:not(.noshiftselect)' ).checkboxShiftClick();
+ } );
+
+ // Things outside the wikipage content
+ $( function () {
+
+ if ( !supportsPlaceholder ) {
+ // Exclude content to avoid hitting it twice for the (first) wikipage content
+ $( 'input[placeholder]' ).not( '#mw-content-text input' ).placeholder();
+ }
- /* Add accesskey hints to the tooltips */
+ // Add accesskey hints to the tooltips
mw.util.updateTooltipAccessKeys();
} );
+
}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.page/mediawiki.page.startup.js b/resources/mediawiki.page/mediawiki.page.startup.js
index 6a11d3e1..38466818 100644
--- a/resources/mediawiki.page/mediawiki.page.startup.js
+++ b/resources/mediawiki.page/mediawiki.page.startup.js
@@ -5,14 +5,23 @@
// Client profile classes for <html>
// Allows for easy hiding/showing of JS or no-JS-specific UI elements
$( 'html' )
- .addClass('client-js' )
+ .addClass( 'client-js' )
.removeClass( 'client-nojs' );
- // Initialize utilities as soon as the document is ready (mw.util.$content,
- // messageBoxNew, profile, tooltip access keys, Table of contents toggle, ..).
- // Enqueued into domready from here instead of mediawiki.page.ready to ensure that it gets enqueued
- // before other modules hook into document ready, so that mw.util.$content (defined by mw.util.init),
- // is defined for them.
- $( mw.util.init );
+ $( function () {
+ // Initialize utilities as soon as the document is ready (mw.util.$content,
+ // messageBoxNew, profile, tooltip access keys, Table of contents toggle, ..).
+ // In the domready here instead of in mediawiki.page.ready to ensure that it gets enqueued
+ // before other modules hook into domready, so that mw.util.$content (defined by
+ // mw.util.init), is defined for them.
+ mw.util.init();
+
+ /**
+ * @event wikipage_content
+ * @member mw.hook
+ * @param {jQuery} $content
+ */
+ mw.hook( 'wikipage.content' ).fire( $( '#mw-content-text' ) );
+ } );
}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.page/mediawiki.page.watch.ajax.js b/resources/mediawiki.page/mediawiki.page.watch.ajax.js
index f945fa9d..e9afa4a2 100644
--- a/resources/mediawiki.page/mediawiki.page.watch.ajax.js
+++ b/resources/mediawiki.page/mediawiki.page.watch.ajax.js
@@ -71,7 +71,7 @@
actionPaths = mw.config.get( 'wgActionPaths' );
- // @todo: Does MediaWiki give action path or query param
+ // @todo Does MediaWiki give action path or query param
// precedence ? If the former, move this to the bottom
action = mw.util.getParamValue( 'action', url );
if ( action !== null ) {
@@ -100,7 +100,7 @@
updateWatchLink: updateWatchLink
};
- $( document ).ready( function () {
+ $( function () {
var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
'#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
'#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
@@ -161,7 +161,7 @@
cleanTitle = title.replace( /_/g, ' ' );
link = mw.html.element(
'a', {
- href: mw.util.wikiGetlink( title ),
+ href: mw.util.getUrl( title ),
title: cleanTitle
}, cleanTitle
);
diff --git a/resources/mediawiki.special/images/arrow-collapsed-ltr.png b/resources/mediawiki.special/images/arrow-collapsed-ltr.png
deleted file mode 100644
index 467a555a..00000000
--- a/resources/mediawiki.special/images/arrow-collapsed-ltr.png
+++ /dev/null
Binary files differ
diff --git a/resources/mediawiki.special/images/arrow-collapsed-rtl.png b/resources/mediawiki.special/images/arrow-collapsed-rtl.png
deleted file mode 100644
index 2246254f..00000000
--- a/resources/mediawiki.special/images/arrow-collapsed-rtl.png
+++ /dev/null
Binary files differ
diff --git a/resources/mediawiki.special/images/arrow-expanded.png b/resources/mediawiki.special/images/arrow-expanded.png
deleted file mode 100644
index 58a9fc66..00000000
--- a/resources/mediawiki.special/images/arrow-expanded.png
+++ /dev/null
Binary files differ
diff --git a/resources/mediawiki.special/images/glyph-people-large.png b/resources/mediawiki.special/images/glyph-people-large.png
new file mode 100644
index 00000000..0578be0b
--- /dev/null
+++ b/resources/mediawiki.special/images/glyph-people-large.png
Binary files differ
diff --git a/resources/mediawiki.special/images/icon-contributors.png b/resources/mediawiki.special/images/icon-contributors.png
new file mode 100644
index 00000000..f933aa69
--- /dev/null
+++ b/resources/mediawiki.special/images/icon-contributors.png
Binary files differ
diff --git a/resources/mediawiki.special/images/icon-edits.png b/resources/mediawiki.special/images/icon-edits.png
new file mode 100644
index 00000000..39f4f2de
--- /dev/null
+++ b/resources/mediawiki.special/images/icon-edits.png
Binary files differ
diff --git a/resources/mediawiki.special/images/icon-lock.png b/resources/mediawiki.special/images/icon-lock.png
new file mode 100644
index 00000000..03f0eecd
--- /dev/null
+++ b/resources/mediawiki.special/images/icon-lock.png
Binary files differ
diff --git a/resources/mediawiki.special/images/icon-pages.png b/resources/mediawiki.special/images/icon-pages.png
new file mode 100644
index 00000000..59513db2
--- /dev/null
+++ b/resources/mediawiki.special/images/icon-pages.png
Binary files differ
diff --git a/resources/mediawiki.special/mediawiki.special.block.js b/resources/mediawiki.special/mediawiki.special.block.js
index 2a158dfb..b8bcf177 100644
--- a/resources/mediawiki.special/mediawiki.special.block.js
+++ b/resources/mediawiki.special/mediawiki.special.block.js
@@ -2,7 +2,7 @@
* JavaScript for Special:Block
*/
( function ( mw, $ ) {
- $( document ).ready( function () {
+ $( function () {
var $blockTarget = $( '#mw-bi-target' ),
$anonOnlyRow = $( '#mw-input-wpHardBlock' ).closest( 'tr' ),
$enableAutoblockRow = $( '#mw-input-wpAutoBlock' ).closest( 'tr' ),
diff --git a/resources/mediawiki.special/mediawiki.special.changeemail.js b/resources/mediawiki.special/mediawiki.special.changeemail.js
index 14c2f036..2d22bad0 100644
--- a/resources/mediawiki.special/mediawiki.special.changeemail.js
+++ b/resources/mediawiki.special/mediawiki.special.changeemail.js
@@ -23,7 +23,7 @@
}
}
- $( document ).ready( function () {
+ $( function () {
// Lame tip to let user know if its email is valid. See bug 22449.
// Only bind once for 'blur' so that the user can fill it in without errors;
// after that, look at every keypress for immediate feedback.
diff --git a/resources/mediawiki.special/mediawiki.special.changeslist.css b/resources/mediawiki.special/mediawiki.special.changeslist.css
index fcdeba1b..5e4af7b6 100644
--- a/resources/mediawiki.special/mediawiki.special.changeslist.css
+++ b/resources/mediawiki.special/mediawiki.special.changeslist.css
@@ -2,63 +2,6 @@
* Styling for Special:Watchlist and Special:RecentChanges
*/
-table.mw-enhanced-rc {
- border: 0;
- border-spacing: 0;
-}
-
-table.mw-enhanced-rc th,
-table.mw-enhanced-rc td {
- padding: 0;
- vertical-align: top;
-}
-
-td.mw-enhanced-rc {
- white-space: nowrap;
- font-family: monospace;
-}
-
-.mw-enhanced-rc-time {
- font-family: monospace;
-}
-
-table.mw-enhanced-rc td.mw-enhanced-rc-nested {
- padding-left: 1em;
-}
-
-/* Show/hide arrows in enhanced changeslist */
-.mw-enhanced-rc .collapsible-expander {
- float: none;
-}
-
-/* If JS is disabled, the arrows or the placeholder space shouldn't be shown */
-.client-nojs .mw-enhancedchanges-arrow-space {
- display: none;
-}
-
-.mw-enhancedchanges-arrow-space {
- display: inline-block;
- *display: inline; /* IE7 and below */
- zoom: 1;
- width: 15px;
- height: 15px;
-}
-
-/* let it look like it is clickable */
-.mw-enhancedchanges-arrow.mw-collapsible-toggle {
- cursor: pointer;
-}
-
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-collapsed {
- /* @embed */
- background: url(images/arrow-collapsed-ltr.png) no-repeat left center;
-}
-
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-expanded {
- /* @embed */
- background: url(images/arrow-expanded.png) no-repeat left center;
-}
-
.mw-changeslist-line-watched .mw-title {
font-weight: bold;
}
diff --git a/resources/mediawiki.special/mediawiki.special.changeslist.enhanced.css b/resources/mediawiki.special/mediawiki.special.changeslist.enhanced.css
new file mode 100644
index 00000000..bed580d7
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.changeslist.enhanced.css
@@ -0,0 +1,66 @@
+/**
+ * Styling for Special:Watchlist and Special:RecentChanges when preference 'usenewrc'
+ * a.k.a. Enhanced Recent Changes is enabled.
+ */
+
+table.mw-enhanced-rc {
+ border: 0;
+ border-spacing: 0;
+}
+
+table.mw-enhanced-rc th,
+table.mw-enhanced-rc td {
+ padding: 0;
+ vertical-align: top;
+}
+
+td.mw-enhanced-rc {
+ white-space: nowrap;
+ font-family: monospace;
+}
+
+.mw-enhanced-rc-time {
+ font-family: monospace;
+}
+
+table.mw-enhanced-rc td.mw-enhanced-rc-nested {
+ padding-left: 1em;
+}
+
+/* Show/hide arrows in enhanced changeslist */
+.mw-enhanced-rc .collapsible-expander {
+ float: none;
+}
+
+/* If JS is disabled, the arrows or the placeholder space shouldn't be shown */
+.client-nojs .mw-enhancedchanges-arrow-space {
+ display: none;
+}
+
+/*
+ * And if it's enabled, let's optimize the collapsing a little: hide the rows
+ * that would be hidden by jquery.makeCollapsible with CSS to save us some
+ * reflows and repaints. This doesn't work on browsers that don't fully support
+ * CSS2 (IE6), but it's okay, this will be done in JavaScript with old degraded
+ * performance instead.
+ */
+.client-js table.mw-enhanced-rc.mw-collapsed tr + tr {
+ display: none;
+}
+
+.mw-enhancedchanges-arrow-space {
+ display: inline-block;
+ *display: inline; /* IE7 and below */
+ zoom: 1;
+ width: 15px;
+ height: 15px;
+}
+
+/* let it look like it is clickable */
+.mw-enhancedchanges-arrow.mw-collapsible-toggle {
+ cursor: pointer;
+}
+
+.mw-enhanced-watched .mw-enhanced-rc-time {
+ font-weight: bold;
+}
diff --git a/resources/mediawiki.special/mediawiki.special.createAccount.css b/resources/mediawiki.special/mediawiki.special.createAccount.css
new file mode 100644
index 00000000..11d00e75
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.createAccount.css
@@ -0,0 +1,89 @@
+/* Disable the underline that Vector puts on h2 headings, and bold them. */
+.mw-ui-container h2 {
+ border: 0;
+ font-weight: bold;
+}
+
+/**** shuffled CAPTCHA ****/
+#wpCaptchaWord {
+ margin-top: 6px;
+}
+
+.mw-createacct-captcha-container {
+ background-color: #f8f8f8;
+ border: 1px solid #c9c9c9;
+ padding: 10px;
+ text-align: center;
+}
+
+.mw-createacct-captcha-assisted {
+ display: block;
+ margin-top: 0.5em;
+}
+
+/* Put a border around the fancycaptcha-image-container. */
+.mw-createacct-captcha-and-reload {
+ border: 1px solid #c9c9c9;
+ display: table-cell; /* Other display formats end up too wide */
+ width: 270px;
+ background-color: #FFF;
+}
+
+/* Make the fancycaptcha-image-container full-width within its parent. */
+.fancycaptcha-image-container
+{
+ width: 100%;
+}
+
+/**** Benefits column CSS to the right (if it fits) of the form. ****/
+.mw-ui-container #userloginForm {
+ float: left;
+}
+
+div.mw-createacct-benefits-container {
+ /* Keeps this column compact and close to the form, but tends to squish contents. */
+ float: left;
+}
+
+div.mw-createacct-benefits-container h2 {
+ margin-bottom: 30px;
+}
+
+.mw-number-text.icon-edits {
+ /* @embed */
+ background: url(images/icon-edits.png) no-repeat left center;
+}
+
+.mw-number-text.icon-pages {
+ /* @embed */
+ background: url(images/icon-pages.png) no-repeat left center;
+}
+
+.mw-number-text.icon-contributors {
+ /* @embed */
+ background: url(images/icon-contributors.png) no-repeat left center;
+}
+
+/* Special font for numbers in benefits*/
+div.mw-number-text h3 {
+ top: 0;
+ margin: 0;
+ padding: 0;
+ color: #252525;
+ font-family: 'Georgia', serif;
+ font-weight: normal;
+ font-size: 2.2em;
+ line-height: 1.2;
+ text-align: center;
+}
+
+/* Contains a number and explanatory text, with space for an icon */
+div.mw-number-text {
+ display: block;
+ font-size: 1.2em;
+ color: #444;
+ margin-top: 1em;
+ padding: 0 0 0 95px; /* 80px wide icon plus "margin" */
+ min-height: 75px; /* matches max icon height, ensures icon emblem is visible */
+ text-align: center;
+}
diff --git a/resources/mediawiki.special/mediawiki.special.createAccount.js b/resources/mediawiki.special/mediawiki.special.createAccount.js
new file mode 100644
index 00000000..5cbb1ee0
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.createAccount.js
@@ -0,0 +1,112 @@
+/**
+ * JavaScript for Create account form (Special:UserLogin?type=signup).
+ */
+( function ( mw, $ ) {
+
+ // When sending password by email, hide the password input fields.
+ // This function doesn't need to be loaded early by ResourceLoader, but is tiny.
+ function hidePasswordOnEmail() {
+ // Always required if checked, otherwise it depends, so we use the original
+ var $emailLabel = $( 'label[for="wpEmail"]' ),
+ originalText = $emailLabel.text(),
+ requiredText = mw.message( 'createacct-emailrequired' ).text(),
+ $createByMailCheckbox = $( '#wpCreateaccountMail' ),
+ $beforePwds = $( '.mw-row-password:first' ).prev(),
+ $pwds;
+
+ function updateForCheckbox() {
+ var checked = $createByMailCheckbox.prop( 'checked' );
+ if ( checked ) {
+ $pwds = $( '.mw-row-password' ).detach();
+ $emailLabel.text( requiredText );
+ } else {
+ if ( $pwds ) {
+ $beforePwds.after( $pwds );
+ $pwds = null;
+ }
+ $emailLabel.text( originalText );
+ }
+ }
+
+ $createByMailCheckbox.on( 'change', updateForCheckbox );
+ updateForCheckbox();
+ }
+
+ // Move the FancyCaptcha image into a more attractive container.
+ // This function does need to be run early by ResourceLoader.
+ function adjustFancyCaptcha() {
+ var $content = $( '#mw-content-text' ),
+ $submit = $content.find( '#wpCreateaccount' ),
+ tabIndex,
+ $captchaStuff,
+ $captchaImageContainer,
+ // JavaScript can't yet parse the message createacct-imgcaptcha-help when it
+ // contains a MediaWiki transclusion, so PHP parses it and sends the HTML.
+ helpMsg = mw.config.get( 'wgCreateacctImgcaptchaHelp' ),
+ helpHtml = '';
+
+ /*
+ * CAPTCHA
+ * The CAPTCHA is in a div style="captcha" at the top of the form.
+ * If it's a FancyCaptcha, then we remove it and insert it lower down,
+ * in a customized div with just what we need (e.g. no
+ * fancycaptcha-createaccount message).
+ */
+ if ( !$submit.length) {
+ return;
+ }
+ tabIndex = $submit.prop( 'tabindex' ) - 1;
+ $captchaStuff = $content.find ( '.captcha' );
+
+ if ( $captchaStuff.length ) {
+
+ // The FancyCaptcha has this class in the ConfirmEdit extension
+ // after 2013-04-18.
+ $captchaImageContainer = $captchaStuff.find( '.fancycaptcha-image-container' );
+ if ( $captchaImageContainer.length !== 1 ) {
+ return;
+ }
+
+ $captchaStuff.remove();
+
+ if ( helpMsg) {
+ helpHtml = '<small class="mw-createacct-captcha-assisted">' + helpMsg + '</small>';
+ }
+
+ // Insert another div before the submit button that will include the
+ // repositioned FancyCaptcha div, an input field, and possible help.
+ $submit.closest( 'div' )
+ .before( [
+ '<div>',
+ '<label for="wpCaptchaWord">' + mw.message( 'createacct-captcha' ).escaped() + '</label>',
+ '<div class="mw-createacct-captcha-container">',
+ '<div class="mw-createacct-captcha-and-reload" />',
+ '<input id="wpCaptchaWord" name="wpCaptchaWord" type="text" placeholder="' +
+ mw.message( 'createacct-imgcaptcha-ph' ).escaped() +
+ '" tabindex="' + tabIndex + '" autocapitalize="off" autocorrect="off">',
+ helpHtml,
+ '</div>',
+ '</div>'
+ ].join( '' )
+ );
+
+ // Stick the FancyCaptcha container inside our bordered and framed parents.
+ $captchaImageContainer
+ .prependTo( $content.find( '.mw-createacct-captcha-and-reload' ) );
+
+ // Find the input field, add the text (if any) of the existing CAPTCHA
+ // field (although usually it's blanked out on every redisplay),
+ // and after it move over the hidden field that tells the CAPTCHA
+ // what to do.
+ $content.find( '#wpCaptchaWord' )
+ .val( $captchaStuff.find( '#wpCaptchaWord' ).val() )
+ .after( $captchaStuff.find( '#wpCaptchaId' ) );
+ }
+ }
+
+ $( function () {
+ adjustFancyCaptcha();
+ hidePasswordOnEmail();
+ } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki.special/mediawiki.special.movePage.js b/resources/mediawiki.special/mediawiki.special.movePage.js
index f719d07c..922eba5e 100644
--- a/resources/mediawiki.special/mediawiki.special.movePage.js
+++ b/resources/mediawiki.special/mediawiki.special.movePage.js
@@ -1,6 +1,6 @@
/**
* JavaScript for Special:MovePage
*/
-jQuery( document ).ready( function ( $ ) {
+jQuery( function ( $ ) {
$( '#wpReason, #wpNewTitleMain' ).byteLimit();
} );
diff --git a/resources/mediawiki.special/mediawiki.special.pagesWithProp.css b/resources/mediawiki.special/mediawiki.special.pagesWithProp.css
new file mode 100644
index 00000000..7ef75d0c
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.pagesWithProp.css
@@ -0,0 +1,4 @@
+/* Distinguish actual data from information about it being hidden visually */
+.prop-value-hidden {
+ font-style: italic;
+}
diff --git a/resources/mediawiki.special/mediawiki.special.preferences.js b/resources/mediawiki.special/mediawiki.special.preferences.js
index 6eaec6a1..03d93d00 100644
--- a/resources/mediawiki.special/mediawiki.special.preferences.js
+++ b/resources/mediawiki.special/mediawiki.special.preferences.js
@@ -1,7 +1,7 @@
/**
* JavaScript for Special:Preferences
*/
-jQuery( document ).ready( function ( $ ) {
+jQuery( function ( $ ) {
var $preftoc, $preferences, $fieldsets, $legends,
hash,
$tzSelect, $tzTextbox, $localtimeHolder, servertime;
diff --git a/resources/mediawiki.special/mediawiki.special.recentchanges.js b/resources/mediawiki.special/mediawiki.special.recentchanges.js
index d1c1354f..79d793af 100644
--- a/resources/mediawiki.special/mediawiki.special.recentchanges.js
+++ b/resources/mediawiki.special/mediawiki.special.recentchanges.js
@@ -27,7 +27,7 @@
}
};
- $( document ).ready( rc.init );
+ $( rc.init );
mw.special.recentchanges = rc;
diff --git a/resources/mediawiki.special/mediawiki.special.search.js b/resources/mediawiki.special/mediawiki.special.search.js
index 2dab3026..035252bf 100644
--- a/resources/mediawiki.special/mediawiki.special.search.js
+++ b/resources/mediawiki.special/mediawiki.special.search.js
@@ -2,7 +2,7 @@
* JavaScript for Special:Search
*/
( function ( mw, $ ) {
- $( document ).ready( function () {
+ $( function () {
var $checkboxes, $headerLinks;
// Emulate HTML5 autofocus behavior in non HTML5 compliant browsers
diff --git a/resources/mediawiki.special/mediawiki.special.undelete.js b/resources/mediawiki.special/mediawiki.special.undelete.js
index d20aab51..0dea3ef9 100644
--- a/resources/mediawiki.special/mediawiki.special.undelete.js
+++ b/resources/mediawiki.special/mediawiki.special.undelete.js
@@ -1,7 +1,7 @@
/**
* JavaScript for Special:Undelete
*/
-jQuery( document ).ready( function ( $ ) {
+jQuery( function ( $ ) {
$( '#mw-undelete-invert' ).click( function ( e ) {
$( '#undelete input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
return !val;
diff --git a/resources/mediawiki.special/mediawiki.special.upload.js b/resources/mediawiki.special/mediawiki.special.upload.js
index 75532f18..3f40c549 100644
--- a/resources/mediawiki.special/mediawiki.special.upload.js
+++ b/resources/mediawiki.special/mediawiki.special.upload.js
@@ -6,7 +6,7 @@
/**
* Add a preview to the upload form
*/
- $( document ).ready( function () {
+ $( function () {
/**
* Is the FileAPI available with sufficient functionality?
*/
@@ -290,7 +290,7 @@
/**
* Disable all upload source fields except the selected one
*/
- $( document ).ready( function () {
+ $( function () {
var i, $row,
$rows = $( '.mw-htmlform-field-UploadSourceField' );
diff --git a/resources/mediawiki.special/mediawiki.special.userLogin.css b/resources/mediawiki.special/mediawiki.special.userLogin.css
new file mode 100644
index 00000000..24c8d771
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.userLogin.css
@@ -0,0 +1,39 @@
+/* Styles just for VForm user login */
+#mw-userlogin-help {
+ text-align: center;
+}
+
+.mw-ui-vform .mw-secure {
+ /* @embed */
+ background: url(images/icon-lock.png) no-repeat scroll left center transparent;
+ margin: 0 0 0 1px;
+ padding: 0 0 0 11px;
+}
+
+/* The login form invites users to create an account */
+#mw-createaccount-cta {
+ width: 20em;
+ height: 10em;
+ /* @embed */
+ background: url(images/glyph-people-large.png) no-repeat 50%;
+ margin: 0 auto;
+}
+
+#mw-createaccount-cta h3,
+#mw-createaccount-another h3 {
+ font-size: 0.9em;
+ font-weight: normal;
+ text-align: center;
+}
+
+#mw-createaccount-cta h3 {
+ padding-top: 4em;
+}
+
+#mw-createaccount-join {
+ margin-left: 0.75em;
+ /* Separate from background image */
+ box-shadow: 4px 4px 4px 4px rgba(255, 255, 255, 1);
+ width: auto;
+ display: inline-block;
+}
diff --git a/resources/mediawiki.special/mediawiki.special.userLogin.signup.js b/resources/mediawiki.special/mediawiki.special.userLogin.signup.js
deleted file mode 100644
index bba42605..00000000
--- a/resources/mediawiki.special/mediawiki.special.userLogin.signup.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * JavaScript for Special:UserLogin/signup
- */
-jQuery( document ).ready( function ( $ ) {
- $( '#wpCreateaccountMail' )
- .on( 'change', function() {
- $( '.mw-row-password' ).toggle( !$( this ).attr( 'checked' ) );
- } )
- .trigger( 'change' );
-} );
diff --git a/resources/mediawiki.special/mediawiki.special.vforms.css b/resources/mediawiki.special/mediawiki.special.vforms.css
new file mode 100644
index 00000000..768a9c6e
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.vforms.css
@@ -0,0 +1,46 @@
+/*
+ * When inside the VForm style, disable the border that Vector and other skins
+ * put on the div surrounding the login/create account form.
+ * Also disable the margin and padding that Vector puts around the form.
+ */
+.mw-ui-container #userloginForm,
+.mw-ui-container #userlogin {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+/* Reposition and resize language links, which appear on a per-wiki basis */
+.mw-ui-container #languagelinks {
+ margin-bottom: 2em;
+ font-size: 0.8em;
+}
+
+/* Put some space under template's header, which may contain CAPTCHA HTML.*/
+section.mw-form-header {
+ margin-bottom: 10px;
+}
+
+/*
+ * Styles for information boxes.
+ */
+.mw-ui-vform .errorbox,
+.mw-ui-vform .warningbox,
+.mw-ui-vform .successbox {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ word-wrap: break-word;
+}
+
+/*
+ * Override the right margin of the form to give space in case a benefits
+ * column appears to the side.
+ *
+ */
+.mw-ui-container #userloginForm {
+ margin-right: 100px;
+}
diff --git a/resources/mediawiki.ui/mediawiki.ui.default.css b/resources/mediawiki.ui/mediawiki.ui.default.css
new file mode 100644
index 00000000..b0726165
--- /dev/null
+++ b/resources/mediawiki.ui/mediawiki.ui.default.css
@@ -0,0 +1,272 @@
+@charset "UTF-8";
+/**
+ * Provide Agora appearance for mw-ui-* classes when using a skin other than
+ * Vector.
+ * Compass builds these Agora styles from source Sass files in
+ * extensions/Agora/modules/scss
+ */
+/* _effects.scss */
+/* Mixins for visual effects in CSS3 */
+/* line 7, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-flush-left {
+ float: left;
+ margin-left: 0;
+ padding-left: 0;
+}
+
+/* line 11, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-flush-right {
+ float: right;
+ margin-right: 0;
+ padding-right: 0;
+}
+
+/* line 15, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-center-block {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* line 4, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button {
+ display: -moz-inline-stack;
+ display: inline-block;
+ vertical-align: middle;
+ *vertical-align: auto;
+ zoom: 1;
+ *display: inline;
+ padding: 0.4em 1em 0.4em 1em;
+ margin: 0;
+ background-color: #c9c9c9;
+ *background-color: #c9c9c9;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFDCDCDC', endColorstr='#FFC9C9C9');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dcdcdc), color-stop(100%, #c9c9c9));
+ background-image: -webkit-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: -moz-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: -o-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: linear-gradient(top, #dcdcdc, #c9c9c9);
+ color: black;
+ text-shadow: 0 1px 1px rgba(201, 201, 201, 0.3);
+ border: 1px solid #c4c4c4;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -ms-border-radius: 3px;
+ -o-border-radius: 3px;
+ border-radius: 3px;
+ vertical-align: middle;
+ text-align: center;
+ text-decoration: none;
+ font-weight: bold;
+ cursor: pointer;
+}
+/* line 38, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:hover, .mw-ui-button.mw-ui-hover {
+ background-color: gainsboro;
+ *background-color: gainsboro;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFE9E9E9', endColorstr='#FFDCDCDC');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e9e9e9), color-stop(100%, #dcdcdc));
+ background-image: -webkit-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: -moz-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: -o-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: linear-gradient(top, #e9e9e9, #dcdcdc);
+ text-decoration: none;
+}
+/* line 44, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:active, .mw-ui-button.mw-ui-active {
+ background-image: none;
+ background-color: #c1c1c1;
+ text-shadow: none;
+}
+/* line 54, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:disabled, .mw-ui-button.mw-ui-disabled {
+ background-image: none;
+ background-color: #c9c9c9;
+ opacity: 0.5;
+ text-shadow: none;
+}
+/* line 30, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button:disabled, .mw-ui-button.mw-ui-disabled {
+ cursor: default;
+}
+/* line 36, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button.mw-ui-big {
+ font-size: 1.3em;
+}
+/* line 41, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button.mw-ui-block {
+ display: block;
+ width: 100%;
+}
+
+/* line 49, sourcefiles/scss/components/default/_buttons.scss */
+a.mw-ui-button {
+ text-decoration: none;
+}
+
+/* line 56, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > * {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ -ms-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+ float: left;
+}
+/* line 60, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > *:first-child {
+ -moz-border-radius-topleft: 3px;
+ -webkit-border-top-left-radius: 3px;
+ border-top-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ -webkit-border-bottom-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+/* line 65, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > *:last-child {
+ -moz-border-radius-topright: 3px;
+ -webkit-border-top-right-radius: 3px;
+ border-top-right-radius: 3px;
+ -moz-border-radius-bottomright: 3px;
+ -webkit-border-bottom-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+
+/* line 14, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 290px;
+}
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input,
+.mw-ui-vform > div .mw-ui-button {
+ display: block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0;
+ width: 100%;
+}
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ color: #252525;
+ padding: 0.35em 0 0.35em 0.5em;
+}
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus {
+ box-shadow: #4091ed 0px 0px 5px;
+ border-color: #4091ed;
+}
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
+ outline: 0;
+}
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div label {
+ display: block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ color: #4a4a4a;
+ width: auto;
+ margin: 0 0 0.2em 0;
+ padding: 0;
+}
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div label * {
+ font-weight: normal;
+}
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input[type="checkbox"],
+.mw-ui-vform > div input[type="radio"] {
+ display: inline;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ width: auto;
+}
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-input {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ color: #252525;
+ padding: 0.35em 0 0.35em 0.5em;
+}
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus {
+ box-shadow: #4091ed 0px 0px 5px;
+ border-color: #4091ed;
+}
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus:not([type=checkbox]):not([type=radio]) {
+ outline: 0;
+}
+
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-label {
+ font-size: 0.9em;
+ color: #4a4a4a;
+}
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-label * {
+ font-weight: normal;
+}
+
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-checkbox-label, .mw-ui-radio-label {
+ margin-bottom: 0.5em;
+ cursor: pointer;
+ vertical-align: bottom;
+ line-height: normal;
+ font-weight: normal;
+}
+/* line 54, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-checkbox-label > input[type="checkbox"], .mw-ui-checkbox-label > input[type="radio"], .mw-ui-radio-label > input[type="checkbox"], .mw-ui-radio-label > input[type="radio"] {
+ width: auto;
+ height: auto;
+ margin: 0 0.1em 0em 0;
+ padding: 0;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ cursor: pointer;
+}
diff --git a/resources/mediawiki.ui/mediawiki.ui.vector.css b/resources/mediawiki.ui/mediawiki.ui.vector.css
new file mode 100644
index 00000000..fd9e0915
--- /dev/null
+++ b/resources/mediawiki.ui/mediawiki.ui.vector.css
@@ -0,0 +1,414 @@
+@charset "UTF-8";
+/**
+ * Provide Agora appearance for mw-ui-* classes when using the Vector skin.
+ * Compass builds these Agora styles from source Sass files in
+ * extensions/Agora/modules/scss
+ */
+/* _effects.scss */
+/* Mixins for visual effects in CSS3 */
+/* line 7, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-flush-left {
+ float: left;
+ margin-left: 0;
+ padding-left: 0;
+}
+
+/* line 11, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-flush-right {
+ float: right;
+ margin-right: 0;
+ padding-right: 0;
+}
+
+/* line 15, sourcefiles/scss/components/_utilities.scss */
+.mw-ui-center-block {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* line 4, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button {
+ display: -moz-inline-stack;
+ display: inline-block;
+ vertical-align: middle;
+ *vertical-align: auto;
+ zoom: 1;
+ *display: inline;
+ padding: 0.4em 1em 0.4em 1em;
+ margin: 0;
+ background-color: #c9c9c9;
+ *background-color: #c9c9c9;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFDCDCDC', endColorstr='#FFC9C9C9');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dcdcdc), color-stop(100%, #c9c9c9));
+ background-image: -webkit-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: -moz-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: -o-linear-gradient(top, #dcdcdc, #c9c9c9);
+ background-image: linear-gradient(top, #dcdcdc, #c9c9c9);
+ color: black;
+ text-shadow: 0 1px 1px rgba(201, 201, 201, 0.3);
+ border: 1px solid #c4c4c4;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -ms-border-radius: 3px;
+ -o-border-radius: 3px;
+ border-radius: 3px;
+ vertical-align: middle;
+ text-align: center;
+ text-decoration: none;
+ font-weight: bold;
+ cursor: pointer;
+}
+/* line 38, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:hover, .mw-ui-button.mw-ui-hover {
+ background-color: gainsboro;
+ *background-color: gainsboro;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFE9E9E9', endColorstr='#FFDCDCDC');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e9e9e9), color-stop(100%, #dcdcdc));
+ background-image: -webkit-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: -moz-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: -o-linear-gradient(top, #e9e9e9, #dcdcdc);
+ background-image: linear-gradient(top, #e9e9e9, #dcdcdc);
+ text-decoration: none;
+}
+/* line 44, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:active, .mw-ui-button.mw-ui-active {
+ background-image: none;
+ background-color: #c1c1c1;
+ text-shadow: none;
+}
+/* line 54, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button:disabled, .mw-ui-button.mw-ui-disabled {
+ background-image: none;
+ background-color: #c9c9c9;
+ opacity: 0.5;
+ text-shadow: none;
+}
+/* line 30, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button:disabled, .mw-ui-button.mw-ui-disabled {
+ cursor: default;
+}
+/* line 36, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button.mw-ui-big {
+ font-size: 1.3em;
+}
+/* line 41, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button.mw-ui-block {
+ display: block;
+ width: 100%;
+}
+
+/* line 49, sourcefiles/scss/components/default/_buttons.scss */
+a.mw-ui-button {
+ text-decoration: none;
+}
+
+/* line 56, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > * {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ -ms-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+ float: left;
+}
+/* line 60, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > *:first-child {
+ -moz-border-radius-topleft: 3px;
+ -webkit-border-top-left-radius: 3px;
+ border-top-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ -webkit-border-bottom-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+/* line 65, sourcefiles/scss/components/default/_buttons.scss */
+.mw-ui-button-group > *:last-child {
+ -moz-border-radius-topright: 3px;
+ -webkit-border-top-right-radius: 3px;
+ border-top-right-radius: 3px;
+ -moz-border-radius-bottomright: 3px;
+ -webkit-border-bottom-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+
+/* line 3, sourcefiles/scss/components/vector/_buttons.scss */
+.mw-ui-button {
+ font-size: 1em;
+ line-height: 1.4em;
+}
+/* line 6, sourcefiles/scss/components/vector/_buttons.scss */
+.mw-ui-button.mw-ui-primary {
+ background-color: #3366bb;
+ *background-color: #3366bb;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FF4779CD', endColorstr='#FF3366BB');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4779cd), color-stop(100%, #3366bb));
+ background-image: -webkit-linear-gradient(top, #4779cd, #3366bb);
+ background-image: -moz-linear-gradient(top, #4779cd, #3366bb);
+ background-image: -o-linear-gradient(top, #4779cd, #3366bb);
+ background-image: linear-gradient(top, #4779cd, #3366bb);
+ color: white;
+ text-shadow: 0 1px 1px rgba(51, 102, 187, 0.75);
+ border: 1px solid #3162b3;
+}
+/* line 38, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-primary:hover, .mw-ui-button.mw-ui-primary.mw-ui-hover {
+ background-color: #4779cd;
+ *background-color: #4779cd;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FF5B88D2', endColorstr='#FF4779CD');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5b88d2), color-stop(100%, #4779cd));
+ background-image: -webkit-linear-gradient(top, #5b88d2, #4779cd);
+ background-image: -moz-linear-gradient(top, #5b88d2, #4779cd);
+ background-image: -o-linear-gradient(top, #5b88d2, #4779cd);
+ background-image: linear-gradient(top, #5b88d2, #4779cd);
+ text-decoration: none;
+}
+/* line 44, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-primary:active, .mw-ui-button.mw-ui-primary.mw-ui-active {
+ background-image: none;
+ background-color: #305faf;
+ text-shadow: none;
+}
+/* line 54, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-primary:disabled, .mw-ui-button.mw-ui-primary.mw-ui-disabled {
+ background-image: none;
+ background-color: #3366bb;
+ opacity: 0.5;
+ text-shadow: none;
+}
+/* line 10, sourcefiles/scss/components/vector/_buttons.scss */
+.mw-ui-button.mw-ui-constructive {
+ background-color: #27aa65;
+ *background-color: #27aa65;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FF2EC977', endColorstr='#FF27AA65');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2ec977), color-stop(100%, #27aa65));
+ background-image: -webkit-linear-gradient(top, #2ec977, #27aa65);
+ background-image: -moz-linear-gradient(top, #2ec977, #27aa65);
+ background-image: -o-linear-gradient(top, #2ec977, #27aa65);
+ background-image: linear-gradient(top, #2ec977, #27aa65);
+ color: white;
+ text-shadow: 0 1px 1px rgba(39, 170, 101, 0.75);
+ border: 1px solid #25a260;
+}
+/* line 38, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-constructive:hover, .mw-ui-button.mw-ui-constructive.mw-ui-hover {
+ background-color: #2ec977;
+ *background-color: #2ec977;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FF3ED384', endColorstr='#FF2EC977');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #3ed384), color-stop(100%, #2ec977));
+ background-image: -webkit-linear-gradient(top, #3ed384, #2ec977);
+ background-image: -moz-linear-gradient(top, #3ed384, #2ec977);
+ background-image: -o-linear-gradient(top, #3ed384, #2ec977);
+ background-image: linear-gradient(top, #3ed384, #2ec977);
+ text-decoration: none;
+}
+/* line 44, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-constructive:active, .mw-ui-button.mw-ui-constructive.mw-ui-active {
+ background-image: none;
+ background-color: #249e5e;
+ text-shadow: none;
+}
+/* line 54, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-constructive:disabled, .mw-ui-button.mw-ui-constructive.mw-ui-disabled {
+ background-image: none;
+ background-color: #27aa65;
+ opacity: 0.5;
+ text-shadow: none;
+}
+/* line 14, sourcefiles/scss/components/vector/_buttons.scss */
+.mw-ui-button.mw-ui-destructive {
+ background-color: #cc0000;
+ *background-color: #cc0000;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFF20000', endColorstr='#FFCC0000');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f20000), color-stop(100%, #cc0000));
+ background-image: -webkit-linear-gradient(top, #f20000, #cc0000);
+ background-image: -moz-linear-gradient(top, #f20000, #cc0000);
+ background-image: -o-linear-gradient(top, #f20000, #cc0000);
+ background-image: linear-gradient(top, #f20000, #cc0000);
+ color: white;
+ text-shadow: 0 1px 1px rgba(204, 0, 0, 0.75);
+ border: 1px solid #c20000;
+}
+/* line 38, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-destructive:hover, .mw-ui-button.mw-ui-destructive.mw-ui-hover {
+ background-color: #f20000;
+ *background-color: #f20000;
+ *zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFFF0D0D', endColorstr='#FFF20000');
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0d0d), color-stop(100%, #f20000));
+ background-image: -webkit-linear-gradient(top, #ff0d0d, #f20000);
+ background-image: -moz-linear-gradient(top, #ff0d0d, #f20000);
+ background-image: -o-linear-gradient(top, #ff0d0d, #f20000);
+ background-image: linear-gradient(top, #ff0d0d, #f20000);
+ text-decoration: none;
+}
+/* line 44, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-destructive:active, .mw-ui-button.mw-ui-destructive.mw-ui-active {
+ background-image: none;
+ background-color: #bd0000;
+ text-shadow: none;
+}
+/* line 54, sourcefiles/scss/mixins/_effects.scss */
+.mw-ui-button.mw-ui-destructive:disabled, .mw-ui-button.mw-ui-destructive.mw-ui-disabled {
+ background-image: none;
+ background-color: #cc0000;
+ opacity: 0.5;
+ text-shadow: none;
+}
+
+/* line 14, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 290px;
+}
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input,
+.mw-ui-vform > div .mw-ui-button {
+ display: block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0;
+ width: 100%;
+}
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ color: #252525;
+ padding: 0.35em 0 0.35em 0.5em;
+}
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus {
+ box-shadow: #4091ed 0px 0px 5px;
+ border-color: #4091ed;
+}
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
+ outline: 0;
+}
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div label {
+ display: block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ color: #4a4a4a;
+ width: auto;
+ margin: 0 0 0.2em 0;
+ padding: 0;
+}
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div label * {
+ font-weight: normal;
+}
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform > div input[type="checkbox"],
+.mw-ui-vform > div input[type="radio"] {
+ display: inline;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ width: auto;
+}
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-input {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ color: #252525;
+ padding: 0.35em 0 0.35em 0.5em;
+}
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus {
+ box-shadow: #4091ed 0px 0px 5px;
+ border-color: #4091ed;
+}
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus:not([type=checkbox]):not([type=radio]) {
+ outline: 0;
+}
+
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-label {
+ font-size: 0.9em;
+ color: #4a4a4a;
+}
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-label * {
+ font-weight: normal;
+}
+
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-checkbox-label, .mw-ui-radio-label {
+ margin-bottom: 0.5em;
+ cursor: pointer;
+ vertical-align: bottom;
+ line-height: normal;
+ font-weight: normal;
+}
+/* line 54, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-checkbox-label > input[type="checkbox"], .mw-ui-checkbox-label > input[type="radio"], .mw-ui-radio-label > input[type="checkbox"], .mw-ui-radio-label > input[type="radio"] {
+ width: auto;
+ height: auto;
+ margin: 0 0.1em 0em 0;
+ padding: 0;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #c9c9c9;
+ cursor: pointer;
+}
+
+/* line 5, sourcefiles/scss/components/vector/_forms.scss */
+.mw-ui-vform,
+.mw-ui-vform > div input,
+.mw-ui-input {
+ font-size: 1em;
+ line-height: 1.4em;
+}
+
+/* line 3, sourcefiles/scss/components/vector/_containers.scss */
+.mw-ui-container {
+ font-size: 1em;
+ line-height: 1.4em;
+}
diff --git a/resources/mediawiki.ui/sourcefiles/Makefile b/resources/mediawiki.ui/sourcefiles/Makefile
new file mode 100644
index 00000000..dea90139
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/Makefile
@@ -0,0 +1,24 @@
+DATE=$(shell date +%I:%M%p)
+CHECK=\033[32m✔\033[39m
+HR=\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#
+
+build:
+ @echo "\n${HR}"
+ @echo "Building Agora..."
+ @echo "${HR}\n"
+ @compass compile
+ @echo "Compiling Compass project... ${CHECK} Done"
+ @rm -rf .sass-cache
+ @echo "Removing .sass-cache... ${CHECK} Done"
+ @echo "\n${HR}"
+ @echo "Agora successfully built at ${DATE}."
+ @echo "${HR}\n"
+
+all: build
+
+watch:
+ @echo "\n${HR}"
+ @echo "Watching SCSS files for Agora..."
+ @echo "${HR}\n"
+ @compass watch
+ @echo "Started watching modules/scss at ${DATE}..."
diff --git a/resources/mediawiki.ui/sourcefiles/config.rb b/resources/mediawiki.ui/sourcefiles/config.rb
new file mode 100644
index 00000000..28c65240
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/config.rb
@@ -0,0 +1,27 @@
+# Require any additional compass plugins here.
+
+# Set this to the root of your project when deployed:
+# (unused so far): http_path = "/"
+
+# Output to parent of build directory
+css_dir = ".."
+sass_dir = "scss"
+# (unused so far): images_dir = "modules/img"
+# (unused so far): javascripts_dir = "modules/js"
+
+# You can select your preferred output style here (can be overridden via the command line):
+# output_style = :expanded or :nested or :compact or :compressed
+output_style = :expanded
+
+# To enable relative paths to assets via compass helper functions. Uncomment:
+relative_assets = true
+
+# To disable debugging comments that display the original location of your selectors. Uncomment:
+line_comments = true
+
+
+# If you prefer the indented syntax, you might want to regenerate this
+# project again passing --syntax sass, or you can uncomment this:
+# preferred_syntax = :sass
+# and then run:
+# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/_default.scss b/resources/mediawiki.ui/sourcefiles/scss/components/_default.scss
new file mode 100644
index 00000000..e7090ebc
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/_default.scss
@@ -0,0 +1,3 @@
+@import "utilities";
+@import "default/buttons";
+@import "default/forms"; \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/_utilities.scss b/resources/mediawiki.ui/sourcefiles/scss/components/_utilities.scss
new file mode 100644
index 00000000..4f1dba2f
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/_utilities.scss
@@ -0,0 +1,17 @@
+// Generic helper classes that could be used in many elements/layouts
+
+// --------------------------------------------------------------------------
+// Positioning
+// --------------------------------------------------------------------------
+
+.mw-ui-flush-left {
+ @include agora-flush-left;
+}
+
+.mw-ui-flush-right {
+ @include agora-flush-right;
+}
+
+.mw-ui-center-block {
+ @include agora-center-block;
+} \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/_vector.scss b/resources/mediawiki.ui/sourcefiles/scss/components/_vector.scss
new file mode 100644
index 00000000..d7cb34ae
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/_vector.scss
@@ -0,0 +1,4 @@
+@import "utilities";
+@import "vector/buttons";
+@import "vector/forms";
+@import "vector/containers";
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/default/_buttons.scss b/resources/mediawiki.ui/sourcefiles/scss/components/default/_buttons.scss
new file mode 100644
index 00000000..d67810f7
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/default/_buttons.scss
@@ -0,0 +1,69 @@
+$buttonBorderRadius: 3px;
+
+// Button styling
+.mw-ui-button {
+ // Container layout
+ @include inline-block;
+ padding: 0.4em 1em 0.4em 1em;
+ margin: 0;
+
+ // Container styling
+ @include buttonColors($agoraGray);
+ @include border-radius($buttonBorderRadius);
+
+ // Content styling
+ vertical-align: middle;
+
+ text: {
+ align: center;
+ decoration: none;
+ }
+
+ font: {
+ weight: bold;
+ }
+
+ // Interaction styling
+ cursor: pointer;
+
+ &:disabled,
+ &.mw-ui-disabled {
+ cursor: default;
+ }
+
+ // Button sizes and displays
+ // -----------------------------------------
+ &.mw-ui-big {
+ font: {
+ size: $baseFontSize * 1.3;
+ }
+ }
+ &.mw-ui-block {
+ display: block;
+ width: 100%;
+ }
+}
+
+// This overrides an underline declaration on a:hover and a:focus in commonElements.css, which the
+// class alone isn't specific enough to do
+a.mw-ui-button {
+ text: {
+ decoration: none;
+ }
+}
+
+// Button groups
+.mw-ui-button-group > * {
+ @include border-radius(0);
+ float: left;
+
+ &:first-child{
+ @include border-top-left-radius($buttonBorderRadius);
+ @include border-bottom-left-radius($buttonBorderRadius);
+ }
+
+ &:last-child{
+ @include border-top-right-radius($buttonBorderRadius);
+ @include border-bottom-right-radius($buttonBorderRadius);
+ }
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/default/_forms.scss b/resources/mediawiki.ui/sourcefiles/scss/components/default/_forms.scss
new file mode 100644
index 00000000..a9cec39a
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/default/_forms.scss
@@ -0,0 +1,114 @@
+// Form elements and layouts
+
+// --------------------------------------------------------------------------
+// Layouts
+// --------------------------------------------------------------------------
+
+// The FancyCaptcha image CAPTCHA used on WMF wikis drives the width of the
+// 'VForm' design, the form can't be narrower than this.
+$captchaContainerWidth: 290px;
+$defaultFormWidth: $captchaContainerWidth;
+
+// Style a compact vertical stacked form ("VForm") and the elements in divs
+// within it.
+.mw-ui-vform {
+ @include box-sizing(border-box);
+
+ width: $defaultFormWidth;
+
+ // Immediate divs in a vform are block and spaced-out.
+ & > div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+
+ // MW currently doesn't use the type attribute everywhere on inputs.
+ input,
+ .mw-ui-button {
+ display: block;
+ @include box-sizing(border-box);
+ margin: 0;
+ width: 100%;
+ }
+
+ // We exclude these because they'll generally use mw-ui-button.
+ // Otherwise, we'll unintentionally override that.
+ input:not([type=button]):not([type=submit]):not([type=file]), {
+ @include agora-field-styling; // mixins/_forms.scss
+ }
+
+ label {
+ display: block;
+ @include box-sizing(border-box);
+ @include agora-label-styling;
+ width: auto;
+ margin: 0 0 0.2em 0;
+ padding: 0;
+ }
+
+ // Override input styling just for checkboxes and radio inputs.
+ input[type="checkbox"],
+ input[type="radio"] {
+ display: inline;
+ @include box-sizing(content-box);
+ width: auto;
+ }
+
+ }
+
+ // HTMLForm uses error, SpecialUserlogin (login and create account) uses
+ // errorbox.
+ // TODO move errorbox from mediawiki.special.vforms.css into here.
+ .error {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ font-size: 0.9em;
+ margin: 0 0 1em 0;
+ padding: 0.5em;
+ color: #cc0000;
+ border: 1px solid #fac5c5;
+ background-color: #fae3e3;
+ text-shadow: 0 1px #fae3e3;
+ word-wrap: break-word;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Elements
+// --------------------------------------------------------------------------
+
+// Apply this to individual elements to style them.
+// You generally don't need to use this class on divs within an Agora
+// form container such as mw-ui-vform
+// XXX DRY: This repeats earlier styling, use an @include agora-div-styling ?
+.mw-ui-vform-div {
+ display: block;
+ margin: 0 0 15px 0;
+ padding: 0;
+ width: 100%;
+}
+
+// Apply mw-ui-input to individual input fields to style them.
+// You generally don't need to use this class if <input> is within an Agora
+// form container such as mw-ui-vform
+.mw-ui-input {
+ @include agora-field-styling; // mixins/_forms.scss
+}
+
+// Apply mw-ui-label to individual elements to style them.
+// You generally don't need to use this class if <label> is within an Agora
+// form container such as mw-ui-vform
+.mw-ui-label {
+ @include agora-label-styling; // mixins/_forms.scss
+}
+
+// Nesting an input checkbox or radio button inside a label with this class
+// improves alignment, e.g.
+// <label class="mw-ui-checkbox-label">
+// <input type="checkbox">The label text
+// </label>
+.mw-ui-checkbox-label, .mw-ui-radio-label {
+ @include agora-inline-label-styling;
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/vector/_buttons.scss b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_buttons.scss
new file mode 100644
index 00000000..8d5f0b6a
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_buttons.scss
@@ -0,0 +1,19 @@
+@import "../default/buttons"; // Layer Vector on top of the default settings.
+
+.mw-ui-button {
+ // Button colors determined by function.
+ // -----------------------------------------
+ &.mw-ui-primary {
+ @include buttonColors($agoraBlue);
+ }
+
+ &.mw-ui-constructive {
+ @include buttonColors($agoraGreen);
+ }
+
+ &.mw-ui-destructive {
+ @include buttonColors($agoraRed);
+ }
+
+ @include vector-type;
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/vector/_containers.scss b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_containers.scss
new file mode 100644
index 00000000..ed01603d
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_containers.scss
@@ -0,0 +1,5 @@
+// No default settings for containers yet.
+
+.mw-ui-container {
+ @include vector-type;
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/components/vector/_forms.scss b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_forms.scss
new file mode 100644
index 00000000..73ea24e2
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/components/vector/_forms.scss
@@ -0,0 +1,7 @@
+@import "../default/forms"; // Layer Vector on top of the default settings.
+
+.mw-ui-vform,
+.mw-ui-vform > div input,
+.mw-ui-input {
+ @include vector-type;
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.default.scss b/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.default.scss
new file mode 100644
index 00000000..e6db5237
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.default.scss
@@ -0,0 +1,16 @@
+/**
+ * Provide Agora appearance for mw-ui-* classes when using a skin other than
+ * Vector.
+ * Compass builds these Agora styles from source Sass files in
+ * extensions/Agora/modules/scss
+ */
+
+@charset "UTF-8";
+
+@import "compass";
+
+@import "settings/all";
+
+@import "mixins/all";
+
+@import "components/default";
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.vector.scss b/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.vector.scss
new file mode 100644
index 00000000..ac113eec
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mediawiki.ui.vector.scss
@@ -0,0 +1,15 @@
+/**
+ * Provide Agora appearance for mw-ui-* classes when using the Vector skin.
+ * Compass builds these Agora styles from source Sass files in
+ * extensions/Agora/modules/scss
+ */
+
+@charset "UTF-8";
+
+@import "compass";
+
+@import "settings/all";
+
+@import "mixins/all";
+
+@import "components/vector";
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mixins/_all.scss b/resources/mediawiki.ui/sourcefiles/scss/mixins/_all.scss
new file mode 100644
index 00000000..adc48cd8
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mixins/_all.scss
@@ -0,0 +1,4 @@
+@import "utilities";
+@import "type";
+@import "effects";
+@import "forms"; \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mixins/_effects.scss b/resources/mediawiki.ui/sourcefiles/scss/mixins/_effects.scss
new file mode 100644
index 00000000..2efff820
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mixins/_effects.scss
@@ -0,0 +1,62 @@
+/* _effects.scss */
+
+/* Mixins for visual effects in CSS3 */
+
+// ----------------------------------------------------------------------------
+// Gradients
+// ----------------------------------------------------------------------------
+@mixin vertical-gradient ($startColor: lighten($agoraGray, 95%), $endColor: $agoraGray) {
+ // Fallback
+ background-color: $endColor;
+ *background-color: $endColor; // IE7
+
+ // IE6-8
+ @include filter-gradient($startColor, $endColor, vertical);
+
+ // IE9+, Opera, Gecko, WebKit
+ @include background-image(linear-gradient(top, $startColor, $endColor));
+}
+
+// ----------------------------------------------------------------------------
+// Button styling
+// ----------------------------------------------------------------------------
+@mixin buttonColors ($baseColor: $agoraGray) {
+ // Background color
+ @include vertical-gradient(lighten($baseColor, 7.5%), $baseColor);
+
+ @if $baseColor == $agoraGray {
+ color: black;
+ @include text-shadow(0 1px 1px rgba($baseColor, 0.3));
+ } @else {
+ color: white;
+ @include text-shadow(0 1px 1px rgba($baseColor, 0.75));
+ }
+
+ border: 1px solid darken($baseColor, 2%);
+
+ &:hover,
+ &.mw-ui-hover {
+ @include vertical-gradient(lighten($baseColor, 12.5%), lighten($baseColor, 7.5%));
+ text-decoration: none;
+ }
+
+ &:active,
+ &.mw-ui-active {
+ background: {
+ image: none;
+ color: darken($baseColor, 3%);
+ }
+
+ text-shadow: none;
+ }
+
+ &:disabled,
+ &.mw-ui-disabled {
+ background: {
+ image: none;
+ color: $baseColor;
+ }
+ opacity: 0.5;
+ text-shadow: none;
+ }
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mixins/_forms.scss b/resources/mediawiki.ui/sourcefiles/scss/mixins/_forms.scss
new file mode 100644
index 00000000..0f3f6ad3
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mixins/_forms.scss
@@ -0,0 +1,66 @@
+// Font is not included.
+// For Vector, that should be layered on top with vector-type
+@mixin agora-field-styling() {
+
+ border: {
+ style: solid;
+ width: 1px;
+ color: $agoraGray;
+ };
+
+ &:focus {
+ // Styling focus of native checkboxes etc on Mac is almost impossible.
+ &:not([type=checkbox]):not([type=radio]) {
+ @include reset-focus; // Removes OS field focus
+ };
+
+ // @include box-shadow generates unneeded prefixes
+ // https://github.com/chriseppstein/compass/issues/1054 , so specify
+ // directly.
+ box-shadow: $agoraBlueShadow 0px 0px 5px;
+
+ border: {
+ color: $agoraBlueShadow;
+ };
+ }
+
+ color: $agoraTextColor;
+ padding: 0.35em 0 0.35em 0.5em;
+}
+
+@mixin agora-label-styling() {
+ font: {
+ //weight: bold;
+ size: 0.9em;
+ };
+ color: darken($agoraGray, 50%);
+
+ & * {
+ font-weight: normal;
+ }
+}
+
+@mixin agora-inline-label-styling() {
+ margin-bottom: 0.5em;
+ cursor: pointer;
+ vertical-align: bottom;
+ line-height: normal;
+
+ font: {
+ weight: normal;
+ };
+
+ & > input[type="checkbox"],
+ & > input[type="radio"] {
+ width: auto;
+ height: auto;
+ margin: 0 0.1em 0em 0;
+ padding: 0;
+ border: {
+ style: solid;
+ width: 1px;
+ color: $agoraGray;
+ }
+ cursor: pointer;
+ }
+}
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mixins/_type.scss b/resources/mediawiki.ui/sourcefiles/scss/mixins/_type.scss
new file mode 100644
index 00000000..8a93a08b
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mixins/_type.scss
@@ -0,0 +1,6 @@
+@mixin vector-type {
+ font: {
+ size: $baseFontSize;
+ }
+ line-height: $baseLineHeight;
+} \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/mixins/_utilities.scss b/resources/mediawiki.ui/sourcefiles/scss/mixins/_utilities.scss
new file mode 100644
index 00000000..71a93b60
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/mixins/_utilities.scss
@@ -0,0 +1,19 @@
+@mixin agora-flush-left() {
+ float: left;
+ margin-left: 0;
+ padding-left: 0;
+}
+
+@mixin agora-flush-right() {
+ float: right;
+ margin-right: 0;
+ padding-right: 0;
+}
+
+@mixin agora-center-block() {
+ display: block;
+ margin: {
+ left: auto;
+ right: auto;
+ };
+} \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/settings/_all.scss b/resources/mediawiki.ui/sourcefiles/scss/settings/_all.scss
new file mode 100644
index 00000000..21ac292f
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/settings/_all.scss
@@ -0,0 +1,2 @@
+@import "colors";
+@import "typography"; \ No newline at end of file
diff --git a/resources/mediawiki.ui/sourcefiles/scss/settings/_colors.scss b/resources/mediawiki.ui/sourcefiles/scss/settings/_colors.scss
new file mode 100644
index 00000000..0c18bdb4
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/settings/_colors.scss
@@ -0,0 +1,17 @@
+// Grays
+// -----------------------------------------
+$agoraGray: #c9c9c9;
+$agoraTextColor: #252525;
+
+// Blues
+// -----------------------------------------
+$agoraBlue: #3366bb;
+$agoraBlueShadow: #4091ed;
+
+// Greens
+// -----------------------------------------
+$agoraGreen: #27aa65;
+
+// Reds
+// -----------------------------------------
+$agoraRed: #cc0000;
diff --git a/resources/mediawiki.ui/sourcefiles/scss/settings/_typography.scss b/resources/mediawiki.ui/sourcefiles/scss/settings/_typography.scss
new file mode 100644
index 00000000..013d12b3
--- /dev/null
+++ b/resources/mediawiki.ui/sourcefiles/scss/settings/_typography.scss
@@ -0,0 +1,5 @@
+$baseFontSize: 1em;
+$baseLineHeight: 1.4 * $baseFontSize;
+$baseFontColor: $agoraTextColor;
+
+$smallFontSize: 0.75em; \ No newline at end of file
diff --git a/resources/mediawiki/images/arrow-collapsed-ltr.png b/resources/mediawiki/images/arrow-collapsed-ltr.png
new file mode 100644
index 00000000..b17e578b
--- /dev/null
+++ b/resources/mediawiki/images/arrow-collapsed-ltr.png
Binary files differ
diff --git a/resources/mediawiki/images/arrow-collapsed-rtl.png b/resources/mediawiki/images/arrow-collapsed-rtl.png
new file mode 100644
index 00000000..a834548e
--- /dev/null
+++ b/resources/mediawiki/images/arrow-collapsed-rtl.png
Binary files differ
diff --git a/resources/mediawiki/images/arrow-expanded.png b/resources/mediawiki/images/arrow-expanded.png
new file mode 100644
index 00000000..2bec798e
--- /dev/null
+++ b/resources/mediawiki/images/arrow-expanded.png
Binary files differ
diff --git a/resources/mediawiki/mediawiki.Title.js b/resources/mediawiki/mediawiki.Title.js
index b86a14ba..5038c515 100644
--- a/resources/mediawiki/mediawiki.Title.js
+++ b/resources/mediawiki/mediawiki.Title.js
@@ -1,192 +1,368 @@
/*!
* @author Neil Kandalgaonkar, 2010
- * @author Timo Tijhof, 2011
+ * @author Timo Tijhof, 2011-2013
* @since 1.18
- *
- * Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink
*/
( function ( mw, $ ) {
- /* Local space */
-
/**
* @class mw.Title
*
+ * Parse titles into an object struture. Note that when using the constructor
+ * directly, passing invalid titles will result in an exception. Use #newFromText to use the
+ * logic directly and get null for invalid titles which is easier to work with.
+ *
* @constructor
* @param {string} title Title of the page. If no second argument given,
- * this will be searched for a namespace.
- * @param {number} [namespace] Namespace id. If given, title will be taken as-is.
+ * this will be searched for a namespace
+ * @param {number} [namespace=NS_MAIN] If given, will used as default namespace for the given title
+ * @throws {Error} When the title is invalid
*/
function Title( title, namespace ) {
- this.ns = 0; // integer namespace id
- this.name = null; // name in canonical 'database' form
- this.ext = null; // extension
-
- if ( arguments.length === 2 ) {
- setNameAndExtension( this, title );
- this.ns = fixNsId( namespace );
- } else if ( arguments.length === 1 ) {
- setAll( this, title );
+ var parsed = parse( title, namespace );
+ if ( !parsed ) {
+ throw new Error( 'Unable to parse title' );
}
+
+ this.namespace = parsed.namespace;
+ this.title = parsed.title;
+ this.ext = parsed.ext;
+ this.fragment = parsed.fragment;
+
return this;
}
-var
- /* Public methods (defined later) */
- fn,
+ /* Private members */
+
+ var
/**
- * Strip some illegal chars: control chars, colon, less than, greater than,
- * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
- * intact, like unicode bidi chars, but it's a good start..
- * @ignore
- * @param {string} s
- * @return {string}
+ * @private
+ * @static
+ * @property NS_MAIN
*/
- clean = function ( s ) {
- if ( s !== undefined ) {
- return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
- }
- },
+ NS_MAIN = 0,
/**
- * Convert db-key to readable text.
- * @ignore
- * @param {string} s
- * @return {string}
+ * @private
+ * @static
+ * @property NS_TALK
*/
- text = function ( s ) {
- if ( s !== null && s !== undefined ) {
- return s.replace( /_/g, ' ' );
- } else {
- return '';
- }
- },
+ NS_TALK = 1,
/**
- * Sanitize name.
- * @ignore
+ * @private
+ * @static
+ * @property NS_SPECIAL
*/
- fixName = function ( s ) {
- return clean( $.trim( s ) );
- },
+ NS_SPECIAL = -1,
/**
- * Sanitize extension.
- * @ignore
+ * Get the namespace id from a namespace name (either from the localized, canonical or alias
+ * name).
+ *
+ * Example: On a German wiki this would return 6 for any of 'File', 'Datei', 'Image' or
+ * even 'Bild'.
+ *
+ * @private
+ * @static
+ * @method getNsIdByName
+ * @param {string} ns Namespace name (case insensitive, leading/trailing space ignored)
+ * @return {number|boolean} Namespace id or boolean false
*/
- fixExt = function ( s ) {
- return clean( s );
+ getNsIdByName = function ( ns ) {
+ var id;
+
+ // Don't cast non-strings to strings, because null or undefined should not result in
+ // returning the id of a potential namespace called "Null:" (e.g. on null.example.org/wiki)
+ // Also, toLowerCase throws exception on null/undefined, because it is a String method.
+ if ( typeof ns !== 'string' ) {
+ return false;
+ }
+ ns = ns.toLowerCase();
+ id = mw.config.get( 'wgNamespaceIds' )[ns];
+ if ( id === undefined ) {
+ return false;
+ }
+ return id;
},
+ rUnderscoreTrim = /^_+|_+$/g,
+
+ rSplit = /^(.+?)_*:_*(.*)$/,
+
+ // See Title.php#getTitleInvalidRegex
+ rInvalid = new RegExp(
+ '[^' + mw.config.get( 'wgLegalTitleChars' ) + ']' +
+ // URL percent encoding sequences interfere with the ability
+ // to round-trip titles -- you can't link to them consistently.
+ '|%[0-9A-Fa-f]{2}' +
+ // XML/HTML character references produce similar issues.
+ '|&[A-Za-z0-9\u0080-\uFFFF]+;' +
+ '|&#[0-9]+;' +
+ '|&#x[0-9A-Fa-f]+;'
+ ),
+
/**
- * Sanitize namespace id.
- * @ignore
- * @param id {Number} Namespace id.
- * @return {Number|Boolean} The id as-is or boolean false if invalid.
+ * Internal helper for #constructor and #newFromtext.
+ *
+ * Based on Title.php#secureAndSplit
+ *
+ * @private
+ * @static
+ * @method parse
+ * @param {string} title
+ * @param {number} [defaultNamespace=NS_MAIN]
+ * @return {Object|boolean}
*/
- fixNsId = function ( id ) {
- // wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] )
- var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()];
+ parse = function ( title, defaultNamespace ) {
+ var namespace, m, id, i, fragment, ext;
- // Check only undefined (may be false-y, such as '' (main namespace) ).
- if ( ns === undefined ) {
+ namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
+
+ title = title
+ // Normalise whitespace to underscores and remove duplicates
+ .replace( /[ _\s]+/g, '_' )
+ // Trim underscores
+ .replace( rUnderscoreTrim, '' );
+
+ if ( title === '' ) {
return false;
+ }
+
+ // Process initial colon
+ if ( title.charAt( 0 ) === ':' ) {
+ // Initial colon means main namespace instead of specified default
+ namespace = NS_MAIN;
+ title = title
+ // Strip colon
+ .substr( 1 )
+ // Trim underscores
+ .replace( rUnderscoreTrim, '' );
+ }
+
+ // Process namespace prefix (if any)
+ m = title.match( rSplit );
+ if ( m ) {
+ id = getNsIdByName( m[1] );
+ if ( id !== false ) {
+ // Ordinary namespace
+ namespace = id;
+ title = m[2];
+
+ // For Talk:X pages, make sure X has no "namespace" prefix
+ if ( namespace === NS_TALK && ( m = title.match( rSplit ) ) ) {
+ // Disallow titles like Talk:File:x (subject should roundtrip: talk:file:x -> file:x -> file_talk:x)
+ if ( getNsIdByName( m[1] ) !== false ) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // Process fragment
+ i = title.indexOf( '#' );
+ if ( i === -1 ) {
+ fragment = null;
} else {
- return Number( id );
+ fragment = title
+ // Get segment starting after the hash
+ .substr( i + 1 )
+ // Convert to text
+ // NB: Must not be trimmed ("Example#_foo" is not the same as "Example#foo")
+ .replace( /_/g, ' ' );
+
+ title = title
+ // Strip hash
+ .substr( 0, i )
+ // Trim underscores, again (strips "_" from "bar" in "Foo_bar_#quux")
+ .replace( rUnderscoreTrim, '' );
}
- },
- /**
- * Get namespace id from namespace name by any known namespace/id pair (localized, canonical or alias).
- * Example: On a German wiki this would return 6 for any of 'File', 'Datei', 'Image' or even 'Bild'.
- * @ignore
- * @param ns {String} Namespace name (case insensitive, leading/trailing space ignored).
- * @return {Number|Boolean} Namespace id or boolean false if unrecognized.
- */
- getNsIdByName = function ( ns ) {
- // Don't cast non-strings to strings, because null or undefined
- // should not result in returning the id of a potential namespace
- // called "Null:" (e.g. on nullwiki.example.org)
- // Also, toLowerCase throws exception on null/undefined, because
- // it is a String.prototype method.
- if ( typeof ns !== 'string' ) {
+
+ // Reject illegal characters
+ if ( title.match( rInvalid ) ) {
return false;
}
- ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize
- var id = mw.config.get( 'wgNamespaceIds' )[ns];
- if ( id === undefined ) {
- mw.log( 'mw.Title: Unrecognized namespace: ' + ns );
+
+ // Disallow titles that browsers or servers might resolve as directory navigation
+ if (
+ title.indexOf( '.' ) !== -1 && (
+ title === '.' || title === '..' ||
+ title.indexOf( './' ) === 0 ||
+ title.indexOf( '../' ) === 0 ||
+ title.indexOf( '/./' ) !== -1 ||
+ title.indexOf( '/../' ) !== -1 ||
+ title.substr( -2 ) === '/.' ||
+ title.substr( -3 ) === '/..'
+ )
+ ) {
+ return false;
+ }
+
+ // Disallow magic tilde sequence
+ if ( title.indexOf( '~~~' ) !== -1 ) {
+ return false;
+ }
+
+ // Disallow titles exceeding the 255 byte size limit (size of underlying database field)
+ // Except for special pages, e.g. [[Special:Block/Long name]]
+ // Note: The PHP implementation also asserts that even in NS_SPECIAL, the title should
+ // be less than 512 bytes.
+ if ( namespace !== NS_SPECIAL && $.byteLength( title ) > 255 ) {
+ return false;
+ }
+
+ // Can't make a link to a namespace alone.
+ if ( title === '' && namespace !== NS_MAIN ) {
+ return false;
+ }
+
+ // Any remaining initial :s are illegal.
+ if ( title.charAt( 0 ) === ':' ) {
return false;
}
- return fixNsId( id );
+
+ // For backwards-compatibility with old mw.Title, we separate the extension from the
+ // rest of the title.
+ i = title.lastIndexOf( '.' );
+ if ( i === -1 || title.length <= i + 1 ) {
+ // Extensions are the non-empty segment after the last dot
+ ext = null;
+ } else {
+ ext = title.substr( i + 1 );
+ title = title.substr( 0, i );
+ }
+
+ return {
+ namespace: namespace,
+ title: title,
+ ext: ext,
+ fragment: fragment
+ };
},
/**
- * Helper to extract namespace, name and extension from a string.
+ * Convert db-key to readable text.
*
- * @ignore
- * @param {mw.Title} title
- * @param {string} raw
- * @return {mw.Title}
+ * @private
+ * @static
+ * @method text
+ * @param {string} s
+ * @return {string}
*/
- setAll = function ( title, s ) {
- // In normal browsers the match-array contains null/undefined if there's no match,
- // IE returns an empty string.
- var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w+))?$/ ),
- nsMatch = getNsIdByName( matches[1] );
-
- // Namespace must be valid, and title must be a non-empty string.
- if ( nsMatch && typeof matches[2] === 'string' && matches[2] !== '' ) {
- title.ns = nsMatch;
- title.name = fixName( matches[2] );
- if ( typeof matches[3] === 'string' && matches[3] !== '' ) {
- title.ext = fixExt( matches[3] );
- }
+ text = function ( s ) {
+ if ( s !== null && s !== undefined ) {
+ return s.replace( /_/g, ' ' );
} else {
- // Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace.
- title.ns = 0;
- setNameAndExtension( title, s );
+ return '';
}
- return title;
},
+ // Polyfill for ES5 Object.create
+ createObject = Object.create || ( function () {
+ return function ( o ) {
+ function Title() {}
+ if ( o !== Object( o ) ) {
+ throw new Error( 'Cannot inherit from a non-object' );
+ }
+ Title.prototype = o;
+ return new Title();
+ };
+ }() );
+
+
+ /* Static members */
+
/**
- * Helper to extract name and extension from a string.
+ * Constructor for Title objects with a null return instead of an exception for invalid titles.
*
- * @ignore
- * @param {mw.Title} title
- * @param {string} raw
- * @return {mw.Title}
+ * @static
+ * @method
+ * @param {string} title
+ * @param {number} [namespace=NS_MAIN] Default namespace
+ * @return {mw.Title|null} A valid Title object or null if the title is invalid
*/
- setNameAndExtension = function ( title, raw ) {
- // In normal browsers the match-array contains null/undefined if there's no match,
- // IE returns an empty string.
- var matches = raw.match( /^(?:)?(.*?)(?:\.(\w+))?$/ );
-
- // Title must be a non-empty string.
- if ( typeof matches[1] === 'string' && matches[1] !== '' ) {
- title.name = fixName( matches[1] );
- if ( typeof matches[2] === 'string' && matches[2] !== '' ) {
- title.ext = fixExt( matches[2] );
- }
- } else {
- throw new Error( 'mw.Title: Could not parse title "' + raw + '"' );
+ Title.newFromText = function ( title, namespace ) {
+ var t, parsed = parse( title, namespace );
+ if ( !parsed ) {
+ return null;
}
- return title;
+
+ t = createObject( Title.prototype );
+ t.namespace = parsed.namespace;
+ t.title = parsed.title;
+ t.ext = parsed.ext;
+ t.fragment = parsed.fragment;
+
+ return t;
};
+ /**
+ * Get the file title from an image element
+ *
+ * var title = mw.Title.newFromImg( $( 'img:first' ) );
+ *
+ * @static
+ * @param {HTMLElement|jQuery} img The image to use as a base
+ * @return {mw.Title|null} The file title or null if unsuccessful
+ */
+ Title.newFromImg = function ( img ) {
+ var matches, i, regex, src, decodedSrc,
+
+ // thumb.php-generated thumbnails
+ thumbPhpRegex = /thumb\.php/,
+
+ regexes = [
+ // Thumbnails
+ /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)\/[0-9]+px-\1[^\s\/]*$/,
+
+ // Thumbnails in non-hashed upload directories
+ /\/([^\s\/]+)\/[0-9]+px-\1[^\s\/]*$/,
+
+ // Full size images
+ /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)$/,
+
+ // Full-size images in non-hashed upload directories
+ /\/([^\s\/]+)$/
+ ],
- /* Static space */
+ recount = regexes.length;
+
+ src = img.jquery ? img[0].src : img.src;
+
+ matches = src.match( thumbPhpRegex );
+
+ if ( matches ) {
+ return mw.Title.newFromText( 'File:' + mw.util.getParamValue( 'f', src ) );
+ }
+
+ decodedSrc = decodeURIComponent( src );
+
+ for ( i = 0; i < recount; i++ ) {
+ regex = regexes[i];
+ matches = decodedSrc.match( regex );
+
+ if ( matches && matches[1] ) {
+ return mw.Title.newFromText( 'File:' + matches[1] );
+ }
+ }
+
+ return null;
+ };
/**
* Whether this title exists on the wiki.
+ *
* @static
- * @param {Mixed} title prefixed db-key name (string) or instance of Title
- * @return {Mixed} Boolean true/false if the information is available. Otherwise null.
+ * @param {string|mw.Title} title prefixed db-key name (string) or instance of Title
+ * @return {boolean|null} Boolean if the information is available, otherwise null
*/
Title.exists = function ( title ) {
- var type = $.type( title ), obj = Title.exist.pages, match;
+ var match,
+ type = $.type( title ),
+ obj = Title.exist.pages;
+
if ( type === 'string' ) {
match = obj[title];
} else if ( type === 'object' && title instanceof Title ) {
@@ -194,23 +370,23 @@ var
} else {
throw new Error( 'mw.Title.exists: title must be a string or an instance of Title' );
}
+
if ( typeof match === 'boolean' ) {
return match;
}
+
return null;
};
- /**
- * @static
- * @property
- */
Title.exist = {
/**
+ * Boolean true value indicates page does exist.
+ *
* @static
* @property {Object} exist.pages Keyed by PrefixedDb title.
- * Boolean true value indicates page does exist.
*/
pages: {},
+
/**
* Example to declare existing titles:
* Title.exist.set(['User:John_Doe', ...]);
@@ -219,8 +395,8 @@ var
*
* @static
* @property exist.set
- * @param {string|Array} titles Title(s) in strict prefixedDb title form.
- * @param {boolean} [state] State of the given titles. Defaults to true.
+ * @param {string|Array} titles Title(s) in strict prefixedDb title form
+ * @param {boolean} [state=true] State of the given titles
* @return {boolean}
*/
set: function ( titles, state ) {
@@ -234,42 +410,60 @@ var
}
};
- /* Public methods */
+ /* Public members */
- fn = {
+ Title.prototype = {
constructor: Title,
/**
- * Get the namespace number.
+ * Get the namespace number
+ *
+ * Example: 6 for "File:Example_image.svg".
+ *
* @return {number}
*/
- getNamespaceId: function (){
- return this.ns;
+ getNamespaceId: function () {
+ return this.namespace;
},
/**
- * Get the namespace prefix (in the content-language).
- * In NS_MAIN this is '', otherwise namespace name plus ':'
+ * Get the namespace prefix (in the content language)
+ *
+ * Example: "File:" for "File:Example_image.svg".
+ * In #NS_MAIN this is '', otherwise namespace name plus ':'
+ *
* @return {string}
*/
- getNamespacePrefix: function (){
- return mw.config.get( 'wgFormattedNamespaces' )[this.ns].replace( / /g, '_' ) + (this.ns === 0 ? '' : ':');
+ getNamespacePrefix: function () {
+ return this.namespace === NS_MAIN ?
+ '' :
+ ( mw.config.get( 'wgFormattedNamespaces' )[ this.namespace ].replace( / /g, '_' ) + ':' );
},
/**
- * The name, like "Foo_bar"
+ * Get the page name without extension or namespace prefix
+ *
+ * Example: "Example_image" for "File:Example_image.svg".
+ *
+ * For the page title (full page name without namespace prefix), see #getMain.
+ *
* @return {string}
*/
getName: function () {
- if ( $.inArray( this.ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
- return this.name;
+ if ( $.inArray( this.namespace, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
+ return this.title;
} else {
- return $.ucFirst( this.name );
+ return $.ucFirst( this.title );
}
},
/**
- * The name, like "Foo bar"
+ * Get the page name (transformed by #text)
+ *
+ * Example: "Example image" for "File:Example_image.svg".
+ *
+ * For the page title (full page name without namespace prefix), see #getMainText.
+ *
* @return {string}
*/
getNameText: function () {
@@ -277,24 +471,30 @@ var
},
/**
- * Get full name in prefixed DB form, like File:Foo_bar.jpg,
- * most useful for API calls, anything that must identify the "title".
- * @return {string}
+ * Get the extension of the page name (if any)
+ *
+ * @return {string|null} Name extension or null if there is none
*/
- getPrefixedDb: function () {
- return this.getNamespacePrefix() + this.getMain();
+ getExtension: function () {
+ return this.ext;
},
/**
- * Get full name in text form, like "File:Foo bar.jpg".
+ * Shortcut for appendable string to form the main page name.
+ *
+ * Returns a string like ".json", or "" if no extension.
+ *
* @return {string}
*/
- getPrefixedText: function () {
- return text( this.getPrefixedDb() );
+ getDotExtension: function () {
+ return this.ext === null ? '' : '.' + this.ext;
},
/**
- * The main title (without namespace), like "Foo_bar.jpg"
+ * Get the main page name (transformed by #text)
+ *
+ * Example: "Example_image.svg" for "File:Example_image.svg".
+ *
* @return {string}
*/
getMain: function () {
@@ -302,7 +502,10 @@ var
},
/**
- * The "text" form, like "Foo bar.jpg"
+ * Get the main page name (transformed by #text)
+ *
+ * Example: "Example image.svg" for "File:Example_image.svg".
+ *
* @return {string}
*/
getMainText: function () {
@@ -310,46 +513,73 @@ var
},
/**
- * Get the extension (returns null if there was none)
- * @return {string|null}
+ * Get the full page name
+ *
+ * Eaxample: "File:Example_image.svg".
+ * Most useful for API calls, anything that must identify the "title".
+ *
+ * @return {string}
*/
- getExtension: function () {
- return this.ext;
+ getPrefixedDb: function () {
+ return this.getNamespacePrefix() + this.getMain();
},
/**
- * Convenience method: return string like ".jpg", or "" if no extension
+ * Get the full page name (transformed by #text)
+ *
+ * Example: "File:Example image.svg" for "File:Example_image.svg".
+ *
* @return {string}
*/
- getDotExtension: function () {
- return this.ext === null ? '' : '.' + this.ext;
+ getPrefixedText: function () {
+ return text( this.getPrefixedDb() );
+ },
+
+ /**
+ * Get the fragment (if any).
+ *
+ * Note that this method (by design) does not include the hash character and
+ * the value is not url encoded.
+ *
+ * @return {string|null}
+ */
+ getFragment: function () {
+ return this.fragment;
},
/**
- * Return the URL to this title
- * @see mw.util#wikiGetlink
+ * Get the URL to this title
+ *
+ * @see mw.util#getUrl
* @return {string}
*/
getUrl: function () {
- return mw.util.wikiGetlink( this.toString() );
+ return mw.util.getUrl( this.toString() );
},
/**
* Whether this title exists on the wiki.
+ *
* @see #static-method-exists
- * @return {boolean|null} If the information is available. Otherwise null.
+ * @return {boolean|null} Boolean if the information is available, otherwise null
*/
exists: function () {
return Title.exists( this );
}
};
- // Alias
- fn.toString = fn.getPrefixedDb;
- fn.toText = fn.getPrefixedText;
+ /**
+ * @alias #getPrefixedDb
+ * @method
+ */
+ Title.prototype.toString = Title.prototype.getPrefixedDb;
+
- // Assign
- Title.prototype = fn;
+ /**
+ * @alias #getPrefixedText
+ * @method
+ */
+ Title.prototype.toText = Title.prototype.getPrefixedText;
// Expose
mw.Title = Title;
diff --git a/resources/mediawiki/mediawiki.Uri.js b/resources/mediawiki/mediawiki.Uri.js
index 643e5c3e..a2d4d6cb 100644
--- a/resources/mediawiki/mediawiki.Uri.js
+++ b/resources/mediawiki/mediawiki.Uri.js
@@ -201,7 +201,7 @@
uri = this,
matches = parser[ options.strictMode ? 'strict' : 'loose' ].exec( str );
$.each( properties, function ( i, property ) {
- uri[ property ] = matches[ i+1 ];
+ uri[ property ] = matches[ i + 1 ];
} );
// uri.query starts out as the query string; we will parse it into key-val pairs then make
@@ -210,7 +210,7 @@
q = {};
// using replace to iterate over a string
if ( uri.query ) {
- uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ($0, $1, $2, $3) {
+ uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ( $0, $1, $2, $3 ) {
var k, v;
if ( $1 ) {
k = Uri.decode( $1 );
diff --git a/resources/mediawiki/mediawiki.debug.js b/resources/mediawiki/mediawiki.debug.js
index 88af3c65..986917a1 100644
--- a/resources/mediawiki/mediawiki.debug.js
+++ b/resources/mediawiki/mediawiki.debug.js
@@ -229,7 +229,7 @@
$( '<colgroup>' ).css( 'width', 350 ).appendTo( $table );
- entryTypeText = function( entryType ) {
+ entryTypeText = function ( entryType ) {
switch ( entryType ) {
case 'log':
return 'Log';
diff --git a/resources/mediawiki/mediawiki.htmlform.js b/resources/mediawiki/mediawiki.htmlform.js
index 83bf2e3a..de068598 100644
--- a/resources/mediawiki/mediawiki.htmlform.js
+++ b/resources/mediawiki/mediawiki.htmlform.js
@@ -1,7 +1,7 @@
/**
* Utility functions for jazzing up HTMLForm elements.
*/
-( function ( $ ) {
+( function ( mw, $ ) {
/**
* jQuery plugin to fade or snap to visible state.
@@ -59,4 +59,70 @@
} );
-}( jQuery ) );
+ function addMulti( $oldContainer, $container ) {
+ var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
+ oldClass = ( ' ' + $oldContainer.attr( 'class' ) + ' ' ).replace( /(mw-htmlform-field-HTMLMultiSelectField|mw-chosen)/g, '' ),
+ $select = $( '<select>' ),
+ dataPlaceholder = mw.message( 'htmlform-chosen-placeholder' );
+ oldClass = $.trim( oldClass );
+ $select.attr( {
+ name: name,
+ multiple: 'multiple',
+ 'data-placeholder': dataPlaceholder.plain(),
+ 'class': 'htmlform-chzn-select mw-input ' + oldClass
+ } );
+ $oldContainer.find( 'input' ).each( function () {
+ var $oldInput = $(this),
+ checked = $oldInput.prop( 'checked' ),
+ $option = $( '<option>' );
+ $option.prop( 'value', $oldInput.prop( 'value' ) );
+ if ( checked ) {
+ $option.prop( 'selected', true );
+ }
+ $option.text( $oldInput.prop( 'value' ) );
+ $select.append( $option );
+ } );
+ $container.append( $select );
+ }
+
+ function convertCheckboxesToMulti( $oldContainer, type ) {
+ var $fieldLabel = $( '<td>' ),
+ $td = $( '<td>' ),
+ $fieldLabelText = $( '<label>' ),
+ $container;
+ if ( type === 'tr' ) {
+ addMulti( $oldContainer, $td );
+ $container = $( '<tr>' );
+ $container.append( $td );
+ } else if ( type === 'div' ) {
+ $fieldLabel = $( '<div>' );
+ $container = $( '<div>' );
+ addMulti( $oldContainer, $container );
+ }
+ $fieldLabel.attr( 'class', 'mw-label' );
+ $fieldLabelText.text( $oldContainer.find( '.mw-label label' ).text() );
+ $fieldLabel.append( $fieldLabelText );
+ $container.prepend( $fieldLabel );
+ $oldContainer.replaceWith( $container );
+ return $container;
+ }
+
+ if ( $( '.mw-chosen' ).length ) {
+ mw.loader.using( 'jquery.chosen', function () {
+ $( '.mw-chosen' ).each( function () {
+ var type = this.nodeName.toLowerCase(),
+ $converted = convertCheckboxesToMulti( $( this ), type );
+ $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
+ } );
+ } );
+ }
+
+ $( function () {
+ var $matrixTooltips = $( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
+ if ( $matrixTooltips.length ) {
+ mw.loader.using( 'jquery.tipsy', function () {
+ $matrixTooltips.tipsy( { gravity: 's' } );
+ } );
+ }
+ } );
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki/mediawiki.icon.css b/resources/mediawiki/mediawiki.icon.css
new file mode 100644
index 00000000..f61b7257
--- /dev/null
+++ b/resources/mediawiki/mediawiki.icon.css
@@ -0,0 +1,15 @@
+/* General-purpose icons via CSS. Classes here should be named "mw-icon-*". */
+
+/* For the collapsed and expanded arrows, we also provide selectors to make it
+ * easy to use them with jquery.makeCollapsible. */
+.mw-icon-arrow-collapsed,
+.mw-collapsible-arrow.mw-collapsible-toggle-collapsed {
+ /* @embed */
+ background: url(images/arrow-collapsed-ltr.png) no-repeat left bottom;
+}
+
+.mw-icon-arrow-expanded,
+.mw-collapsible-arrow.mw-collapsible-toggle-expanded {
+ /* @embed */
+ background: url(images/arrow-expanded.png) no-repeat left bottom;
+}
diff --git a/resources/mediawiki/mediawiki.inspect.js b/resources/mediawiki/mediawiki.inspect.js
new file mode 100644
index 00000000..2f2ca335
--- /dev/null
+++ b/resources/mediawiki/mediawiki.inspect.js
@@ -0,0 +1,204 @@
+/*!
+ * Tools for inspecting page composition and performance.
+ *
+ * @author Ori Livneh
+ * @since 1.22
+ */
+/*jshint devel:true */
+( function ( mw, $ ) {
+
+ function sortByProperty( array, prop, descending ) {
+ var order = descending ? -1 : 1;
+ return array.sort( function ( a, b ) {
+ return a[prop] > b[prop] ? order : a[prop] < b[prop] ? -order : 0;
+ } );
+ }
+
+ /**
+ * @class mw.inspect
+ * @singleton
+ */
+ var inspect = {
+
+ /**
+ * Calculate the byte size of a ResourceLoader module.
+ *
+ * @param {string} moduleName The name of the module
+ * @return {number|null} Module size in bytes or null
+ */
+ getModuleSize: function ( moduleName ) {
+ var module = mw.loader.moduleRegistry[ moduleName ],
+ payload = 0;
+
+ if ( mw.loader.getState( moduleName ) !== 'ready' ) {
+ return null;
+ }
+
+ if ( !module.style && !module.script ) {
+ return null;
+ }
+
+ // Tally CSS
+ if ( module.style && $.isArray( module.style.css ) ) {
+ $.each( module.style.css, function ( i, stylesheet ) {
+ payload += $.byteLength( stylesheet );
+ } );
+ }
+
+ // Tally JavaScript
+ if ( $.isFunction( module.script ) ) {
+ payload += $.byteLength( module.script.toString() );
+ }
+
+ return payload;
+ },
+
+ /**
+ * Given CSS source, count both the total number of selectors it
+ * contains and the number which match some element in the current
+ * document.
+ *
+ * @param {string} css CSS source
+ * @return Selector counts
+ * @return {number} return.selectors Total number of selectors
+ * @return {number} return.matched Number of matched selectors
+ */
+ auditSelectors: function ( css ) {
+ var selectors = { total: 0, matched: 0 },
+ style = document.createElement( 'style' ),
+ sheet, rules;
+
+ style.textContent = css;
+ document.body.appendChild( style );
+ // Standards-compliant browsers use .sheet.cssRules, IE8 uses .styleSheet.rules…
+ sheet = style.sheet || style.styleSheet;
+ rules = sheet.cssRules || sheet.rules;
+ $.each( rules, function ( index, rule ) {
+ selectors.total++;
+ if ( document.querySelector( rule.selectorText ) !== null ) {
+ selectors.matched++;
+ }
+ } );
+ document.body.removeChild( style );
+ return selectors;
+ },
+
+ /**
+ * Get a list of all loaded ResourceLoader modules.
+ *
+ * @return {Array} List of module names
+ */
+ getLoadedModules: function () {
+ return $.grep( mw.loader.getModuleNames(), function ( module ) {
+ return mw.loader.getState( module ) === 'ready';
+ } );
+ },
+
+ /**
+ * Print tabular data to the console, using console.table, console.log,
+ * or mw.log (in declining order of preference).
+ *
+ * @param {Array} data Tabular data represented as an array of objects
+ * with common properties.
+ */
+ dumpTable: function ( data ) {
+ try {
+ // Bartosz made me put this here.
+ if ( window.opera ) { throw window.opera; }
+ // Use Function.prototype#call to force an exception on Firefox,
+ // which doesn't define console#table but doesn't complain if you
+ // try to invoke it.
+ console.table.call( console, data );
+ return;
+ } catch (e) {}
+ try {
+ console.log( $.toJSON( data, null, 2 ) );
+ return;
+ } catch (e) {}
+ mw.log( data );
+ },
+
+ /**
+ * Generate and print one more reports. When invoked with no arguments,
+ * print all reports.
+ *
+ * @param {string...} [reports] Report names to run, or unset to print
+ * all available reports.
+ */
+ runReports: function () {
+ var reports = arguments.length > 0 ?
+ Array.prototype.slice.call( arguments ) :
+ $.map( inspect.reports, function ( v, k ) { return k; } );
+
+ $.each( reports, function ( index, name ) {
+ inspect.dumpTable( inspect.reports[name]() );
+ } );
+ },
+
+ /**
+ * @class mw.inspect.reports
+ * @singleton
+ */
+ reports: {
+ /**
+ * Generate a breakdown of all loaded modules and their size in
+ * kilobytes. Modules are ordered from largest to smallest.
+ */
+ size: function () {
+ // Map each module to a descriptor object.
+ var modules = $.map( inspect.getLoadedModules(), function ( module ) {
+ return {
+ name: module,
+ size: inspect.getModuleSize( module )
+ };
+ } );
+
+ // Sort module descriptors by size, largest first.
+ sortByProperty( modules, 'size', true );
+
+ // Convert size to human-readable string.
+ $.each( modules, function ( i, module ) {
+ module.size = module.size > 1024 ?
+ ( module.size / 1024 ).toFixed( 2 ) + ' KB' :
+ ( module.size !== null ? module.size + ' B' : null );
+ } );
+
+ return modules;
+ },
+
+ /**
+ * For each module with styles, count the number of selectors, and
+ * count how many match against some element currently in the DOM.
+ */
+ css: function () {
+ var modules = [];
+
+ $.each( inspect.getLoadedModules(), function ( index, name ) {
+ var css, stats, module = mw.loader.moduleRegistry[name];
+
+ try {
+ css = module.style.css.join();
+ } catch (e) { return; } // skip
+
+ stats = inspect.auditSelectors( css );
+ modules.push( {
+ module: name,
+ allSelectors: stats.total,
+ matchedSelectors: stats.matched,
+ percentMatched: stats.total !== 0 ?
+ ( stats.matched / stats.total * 100 ).toFixed( 2 ) + '%' : null
+ } );
+ } );
+ sortByProperty( modules, 'allSelectors', true );
+ return modules;
+ },
+ }
+ };
+
+ if ( mw.config.get( 'debug' ) ) {
+ mw.log( 'mw.inspect: reports are not available in debug mode.' );
+ }
+
+ mw.inspect = inspect;
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js
index 183b525e..70b9be93 100644
--- a/resources/mediawiki/mediawiki.jqueryMsg.js
+++ b/resources/mediawiki/mediawiki.jqueryMsg.js
@@ -3,6 +3,7 @@
* See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
*
* @author neilk@wikimedia.org
+* @author mflaschen@wikimedia.org
*/
( function ( mw, $ ) {
var oldParser,
@@ -11,6 +12,31 @@
magic : {
'SITENAME' : mw.config.get( 'wgSiteName' )
},
+ // This is a whitelist based on, but simpler than, Sanitizer.php.
+ // Self-closing tags are not currently supported.
+ allowedHtmlElements : [
+ 'b',
+ 'i'
+ ],
+ // Key tag name, value allowed attributes for that tag.
+ // See Sanitizer::setupAttributeWhitelist
+ allowedHtmlCommonAttributes : [
+ // HTML
+ 'id',
+ 'class',
+ 'style',
+ 'lang',
+ 'dir',
+ 'title',
+
+ // WAI-ARIA
+ 'role'
+ ],
+
+ // Attributes allowed for specific elements.
+ // Key is element name in lower case
+ // Value is array of allowed attributes for that element
+ allowedHtmlAttributesByElement : {},
messages : mw.messages,
language : mw.language,
@@ -18,7 +44,7 @@
//
// Only 'text', 'parse', and 'escaped' are supported, and the
// actual escaping for 'escaped' is done by other code (generally
- // through jqueryMsg).
+ // through mediawiki.js).
//
// However, note that this default only
// applies to direct calls to jqueryMsg. The default for mediawiki.js itself
@@ -28,6 +54,47 @@
};
/**
+ * Wrapper around jQuery append that converts all non-objects to TextNode so append will not
+ * convert what it detects as an htmlString to an element.
+ *
+ * Object elements of children (jQuery, HTMLElement, TextNode, etc.) will be left as is.
+ *
+ * @param {jQuery} $parent Parent node wrapped by jQuery
+ * @param {Object|string|Array} children What to append, with the same possible types as jQuery
+ * @return {jQuery} $parent
+ */
+ function appendWithoutParsing( $parent, children ) {
+ var i, len;
+
+ if ( !$.isArray( children ) ) {
+ children = [children];
+ }
+
+ for ( i = 0, len = children.length; i < len; i++ ) {
+ if ( typeof children[i] !== 'object' ) {
+ children[i] = document.createTextNode( children[i] );
+ }
+ }
+
+ return $parent.append( children );
+ }
+
+ /**
+ * Decodes the main HTML entities, those encoded by mw.html.escape.
+ *
+ * @param {string} encode Encoded string
+ * @return {string} String with those entities decoded
+ */
+ function decodePrimaryHtmlEntities( encoded ) {
+ return encoded
+ .replace( /&#039;/g, '\'' )
+ .replace( /&quot;/g, '"' )
+ .replace( /&lt;/g, '<' )
+ .replace( /&gt;/g, '>' )
+ .replace( /&amp;/g, '&' );
+ }
+
+ /**
* Given parser options, return a function that parses a key and replacements, returning jQuery object
* @param {Object} parser options
* @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery}
@@ -48,7 +115,7 @@
try {
return parser.parse( key, argsArray );
} catch ( e ) {
- return $( '<span>' ).append( key + ': ' + e.message );
+ return $( '<span>' ).text( key + ': ' + e.message );
}
};
}
@@ -125,10 +192,10 @@
*/
return function () {
var $target = this.empty();
- // TODO: Simply $target.append( failableParserFn( arguments ).contents() )
- // or Simply $target.append( failableParserFn( arguments ) )
+ // TODO: Simply appendWithoutParsing( $target, failableParserFn( arguments ).contents() )
+ // or Simply appendWithoutParsing( $target, failableParserFn( arguments ) )
$.each( failableParserFn( arguments ).contents(), function ( i, node ) {
- $target.append( node );
+ appendWithoutParsing( $target, node );
} );
return $target;
};
@@ -206,11 +273,13 @@
* @return {Mixed} abstract syntax tree
*/
wikiTextToAst: function ( input ) {
- var pos,
+ var pos, settings = this.settings, concat = Array.prototype.concat,
regularLiteral, regularLiteralWithoutBar, regularLiteralWithoutSpace, regularLiteralWithSquareBrackets,
- backslash, anyCharacter, escapedOrLiteralWithoutSpace, escapedOrLiteralWithoutBar, escapedOrRegularLiteral,
- whitespace, dollar, digits,
- openExtlink, closeExtlink, wikilinkPage, wikilinkContents, openLink, closeLink, templateName, pipe, colon,
+ doubleQuote, singleQuote, backslash, anyCharacter, asciiAlphabetLiteral,
+ escapedOrLiteralWithoutSpace, escapedOrLiteralWithoutBar, escapedOrRegularLiteral,
+ whitespace, dollar, digits, htmlDoubleQuoteAttributeValue, htmlSingleQuoteAttributeValue,
+ htmlAttributeEquals, openHtmlStartTag, optionalForwardSlash, openHtmlEndTag, closeHtmlTag,
+ openExtlink, closeExtlink, wikilinkPage, wikilinkContents, openWikilink, closeWikilink, templateName, pipe, colon,
templateContents, openTemplate, closeTemplate,
nonWhitespaceExpression, paramExpression, expression, curlyBraceTransformExpression, result;
@@ -289,6 +358,15 @@
return result;
};
}
+
+ /**
+ * Makes a regex parser, given a RegExp object.
+ * The regex being passed in should start with a ^ to anchor it to the start
+ * of the string.
+ *
+ * @param {RegExp} regex anchored regex
+ * @return {Function} function to parse input based on the regex
+ */
function makeRegexParser( regex ) {
return function () {
var matches = input.substr( pos ).match( regex );
@@ -315,12 +393,23 @@
// but some debuggers can't tell you exactly where they come from. Also the mutually
// recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
// This may be because, to save code, memoization was removed
- regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
+
+ regularLiteral = makeRegexParser( /^[^{}\[\]$<\\]/ );
regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/);
regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/);
regularLiteralWithSquareBrackets = makeRegexParser( /^[^{}$\\]/ );
+
backslash = makeStringParser( '\\' );
+ doubleQuote = makeStringParser( '"' );
+ singleQuote = makeStringParser( '\'' );
anyCharacter = makeRegexParser( /^./ );
+
+ openHtmlStartTag = makeStringParser( '<' );
+ optionalForwardSlash = makeRegexParser( /^\/?/ );
+ openHtmlEndTag = makeStringParser( '</' );
+ htmlAttributeEquals = makeRegexParser( /^\s*=\s*/ );
+ closeHtmlTag = makeRegexParser( /^\s*>/ );
+
function escapedLiteral() {
var result = sequence( [
backslash,
@@ -369,6 +458,10 @@
return result === null ? null : result.join('');
}
+ asciiAlphabetLiteral = makeRegexParser( /[A-Za-z]+/ );
+ htmlDoubleQuoteAttributeValue = makeRegexParser( /^[^"]*/ );
+ htmlSingleQuoteAttributeValue = makeRegexParser( /^[^']*/ );
+
whitespace = makeRegexParser( /^\s+/ );
dollar = makeStringParser( '$' );
digits = makeRegexParser( /^\d+/ );
@@ -385,7 +478,7 @@
}
openExtlink = makeStringParser( '[' );
closeExtlink = makeStringParser( ']' );
- // this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed
+ // this extlink MUST have inner contents, e.g. [foo] not allowed; [foo bar] [foo <i>bar</i>], etc. are allowed
function extlink() {
var result, parsedResult;
result = null;
@@ -393,11 +486,18 @@
openExtlink,
nonWhitespaceExpression,
whitespace,
- expression,
+ nOrMore( 1, expression ),
closeExtlink
] );
if ( parsedResult !== null ) {
- result = [ 'LINK', parsedResult[1], parsedResult[3] ];
+ result = [ 'EXTLINK', parsedResult[1] ];
+ // TODO (mattflaschen, 2013-03-22): Clean this up if possible.
+ // It's avoiding CONCAT for single nodes, so they at least doesn't get the htmlEmitter span.
+ if ( parsedResult[3].length === 1 ) {
+ result.push( parsedResult[3][0] );
+ } else {
+ result.push( ['CONCAT'].concat( parsedResult[3] ) );
+ }
}
return result;
}
@@ -414,10 +514,10 @@
if ( result === null ) {
return null;
}
- return [ 'LINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ];
+ return [ 'EXTLINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ];
}
- openLink = makeStringParser( '[[' );
- closeLink = makeStringParser( ']]' );
+ openWikilink = makeStringParser( '[[' );
+ closeWikilink = makeStringParser( ']]' );
pipe = makeStringParser( '|' );
function template() {
@@ -448,21 +548,158 @@
wikilinkPage // unpiped link
] );
- function link() {
+ function wikilink() {
var result, parsedResult, parsedLinkContents;
result = null;
parsedResult = sequence( [
- openLink,
+ openWikilink,
wikilinkContents,
- closeLink
+ closeWikilink
] );
if ( parsedResult !== null ) {
parsedLinkContents = parsedResult[1];
- result = [ 'WLINK' ].concat( parsedLinkContents );
+ result = [ 'WIKILINK' ].concat( parsedLinkContents );
+ }
+ return result;
+ }
+
+ // TODO: Support data- if appropriate
+ function doubleQuotedHtmlAttributeValue() {
+ var parsedResult = sequence( [
+ doubleQuote,
+ htmlDoubleQuoteAttributeValue,
+ doubleQuote
+ ] );
+ return parsedResult === null ? null : parsedResult[1];
+ }
+
+ function singleQuotedHtmlAttributeValue() {
+ var parsedResult = sequence( [
+ singleQuote,
+ htmlSingleQuoteAttributeValue,
+ singleQuote
+ ] );
+ return parsedResult === null ? null : parsedResult[1];
+ }
+
+ function htmlAttribute() {
+ var parsedResult = sequence( [
+ whitespace,
+ asciiAlphabetLiteral,
+ htmlAttributeEquals,
+ choice( [
+ doubleQuotedHtmlAttributeValue,
+ singleQuotedHtmlAttributeValue
+ ] )
+ ] );
+ return parsedResult === null ? null : [parsedResult[1], parsedResult[3]];
+ }
+
+ /**
+ * Checks if HTML is allowed
+ *
+ * @param {string} startTagName HTML start tag name
+ * @param {string} endTagName HTML start tag name
+ * @param {Object} attributes array of consecutive key value pairs,
+ * with index 2 * n being a name and 2 * n + 1 the associated value
+ * @return {boolean} true if this is HTML is allowed, false otherwise
+ */
+ function isAllowedHtml( startTagName, endTagName, attributes ) {
+ var i, len, attributeName;
+
+ startTagName = startTagName.toLowerCase();
+ endTagName = endTagName.toLowerCase();
+ if ( startTagName !== endTagName || $.inArray( startTagName, settings.allowedHtmlElements ) === -1 ) {
+ return false;
+ }
+
+ for ( i = 0, len = attributes.length; i < len; i += 2 ) {
+ attributeName = attributes[i];
+ if ( $.inArray( attributeName, settings.allowedHtmlCommonAttributes ) === -1 &&
+ $.inArray( attributeName, settings.allowedHtmlAttributesByElement[startTagName] || [] ) === -1 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function htmlAttributes() {
+ var parsedResult = nOrMore( 0, htmlAttribute )();
+ // Un-nest attributes array due to structure of jQueryMsg operations (see emit).
+ return concat.apply( ['HTMLATTRIBUTES'], parsedResult );
+ }
+
+ // Subset of allowed HTML markup.
+ // Most elements and many attributes allowed on the server are not supported yet.
+ function html() {
+ var result = null, parsedOpenTagResult, parsedHtmlContents,
+ parsedCloseTagResult, wrappedAttributes, attributes,
+ startTagName, endTagName, startOpenTagPos, startCloseTagPos,
+ endOpenTagPos, endCloseTagPos;
+
+ // Break into three sequence calls. That should allow accurate reconstruction of the original HTML, and requiring an exact tag name match.
+ // 1. open through closeHtmlTag
+ // 2. expression
+ // 3. openHtmlEnd through close
+ // This will allow recording the positions to reconstruct if HTML is to be treated as text.
+
+ startOpenTagPos = pos;
+ parsedOpenTagResult = sequence( [
+ openHtmlStartTag,
+ asciiAlphabetLiteral,
+ htmlAttributes,
+ optionalForwardSlash,
+ closeHtmlTag
+ ] );
+
+ if ( parsedOpenTagResult === null ) {
+ return null;
}
+
+ endOpenTagPos = pos;
+ startTagName = parsedOpenTagResult[1];
+
+ parsedHtmlContents = nOrMore( 0, expression )();
+
+ startCloseTagPos = pos;
+ parsedCloseTagResult = sequence( [
+ openHtmlEndTag,
+ asciiAlphabetLiteral,
+ closeHtmlTag
+ ] );
+
+ if ( parsedCloseTagResult === null ) {
+ // Closing tag failed. Return the start tag and contents.
+ return [ 'CONCAT', input.substring( startOpenTagPos, endOpenTagPos ) ].concat( parsedHtmlContents );
+ }
+
+ endCloseTagPos = pos;
+ endTagName = parsedCloseTagResult[1];
+ wrappedAttributes = parsedOpenTagResult[2];
+ attributes = wrappedAttributes.slice( 1 );
+ if ( isAllowedHtml( startTagName, endTagName, attributes) ) {
+ result = [ 'HTMLELEMENT', startTagName, wrappedAttributes ].concat( parsedHtmlContents );
+ } else {
+ // HTML is not allowed, so contents will remain how
+ // it was, while HTML markup at this level will be
+ // treated as text
+ // E.g. assuming script tags are not allowed:
+ //
+ // <script>[[Foo|bar]]</script>
+ //
+ // results in '&lt;script&gt;' and '&lt;/script&gt;'
+ // (not treated as an HTML tag), surrounding a fully
+ // parsed HTML link.
+ //
+ // Concatenate everything from the tag, flattening the contents.
+ result = [ 'CONCAT', input.substring( startOpenTagPos, endOpenTagPos ) ].concat( parsedHtmlContents, input.substring( startCloseTagPos, endCloseTagPos ) );
+ }
+
return result;
}
+
templateName = transform(
// see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1"
@@ -525,7 +762,7 @@
closeTemplate = makeStringParser('}}');
nonWhitespaceExpression = choice( [
template,
- link,
+ wikilink,
extLinkParam,
extlink,
replacement,
@@ -533,7 +770,7 @@
] );
paramExpression = choice( [
template,
- link,
+ wikilink,
extLinkParam,
extlink,
replacement,
@@ -542,10 +779,11 @@
expression = choice( [
template,
- link,
+ wikilink,
extLinkParam,
extlink,
replacement,
+ html,
literal
] );
@@ -659,12 +897,12 @@
$.each( nodes, function ( i, node ) {
if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) {
$.each( node.contents(), function ( j, childNode ) {
- $span.append( childNode );
+ appendWithoutParsing( $span, childNode );
} );
} else {
// Let jQuery append nodes, arrays of nodes and jQuery objects
// other things (strings, numbers, ..) are appended as text nodes (not as HTML strings)
- $span.append( $.type( node ) === 'object' ? node : document.createTextNode( node ) );
+ appendWithoutParsing( $span, node );
}
} );
return $span;
@@ -704,11 +942,11 @@
*
* @param nodes
*/
- wlink: function ( nodes ) {
+ wikilink: function ( nodes ) {
var page, anchor, url;
page = nodes[0];
- url = mw.util.wikiGetlink( page );
+ url = mw.util.getUrl( page );
// [[Some Page]] or [[Namespace:Some Page]]
if ( nodes.length === 1 ) {
@@ -730,6 +968,36 @@
},
/**
+ * Converts array of HTML element key value pairs to object
+ *
+ * @param {Array} nodes array of consecutive key value pairs, with index 2 * n being a name and 2 * n + 1 the associated value
+ * @return {Object} object mapping attribute name to attribute value
+ */
+ htmlattributes: function ( nodes ) {
+ var i, len, mapping = {};
+ for ( i = 0, len = nodes.length; i < len; i += 2 ) {
+ mapping[nodes[i]] = decodePrimaryHtmlEntities( nodes[i + 1] );
+ }
+ return mapping;
+ },
+
+ /**
+ * Handles an (already-validated) HTML element.
+ *
+ * @param {Array} nodes nodes to process when creating element
+ * @return {jQuery|Array} jQuery node for valid HTML or array for disallowed element
+ */
+ htmlelement: function ( nodes ) {
+ var tagName, attributes, contents, $element;
+
+ tagName = nodes.shift();
+ attributes = nodes.shift();
+ contents = nodes;
+ $element = $( document.createElement( tagName ) ).attr( attributes );
+ return appendWithoutParsing( $element, contents );
+ },
+
+ /**
* Transform parsed structure into external link
* If the href is a jQuery object, treat it as "enclosing" the link text.
* ... function, treat it as the click handler
@@ -738,7 +1006,7 @@
* @param {Array} of two elements, {jQuery|Function|String} and {String}
* @return {jQuery}
*/
- link: function ( nodes ) {
+ extlink: function ( nodes ) {
var $el,
arg = nodes[0],
contents = nodes[1];
@@ -752,12 +1020,11 @@
$el.attr( 'href', arg.toString() );
}
}
- $el.append( contents );
- return $el;
+ return appendWithoutParsing( $el, contents );
},
/**
- * This is basically use a combination of replace + link (link with parameter
+ * This is basically use a combination of replace + external link (link with parameter
* as url), but we don't want to run the regular replace here-on: inserting a
* url as href-attribute of a link will automatically escape it already, so
* we don't want replace to (manually) escape it as well.
@@ -765,7 +1032,7 @@
* @param {Array} of one element, integer, n >= 0
* @return {String} replacement
*/
- linkparam: function ( nodes, replacements ) {
+ extlinkparam: function ( nodes, replacements ) {
var replacement,
index = parseInt( nodes[0], 10 );
if ( index < replacements.length) {
@@ -773,7 +1040,7 @@
} else {
replacement = '$' + ( index + 1 );
}
- return this.link( [ replacement, nodes[1] ] );
+ return this.extlink( [ replacement, nodes[1] ] );
},
/**
@@ -865,7 +1132,7 @@
// Caching is somewhat problematic, because we do need different message functions for different maps, so
// we'd have to cache the parser as a member of this.map, which sounds a bit ugly.
// Do not use mw.jqueryMsg unless required
- if ( this.format === 'plain' || !/\{\{|\[/.test(this.map.get( this.key ) ) ) {
+ if ( this.format === 'plain' || !/\{\{|[\[<>]/.test(this.map.get( this.key ) ) ) {
// Fall back to mw.msg's simple parser
return oldParser.apply( this );
}
diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js
index ca987543..80223e5d 100644
--- a/resources/mediawiki/mediawiki.js
+++ b/resources/mediawiki/mediawiki.js
@@ -1,5 +1,9 @@
-/*
- * Core MediaWiki JavaScript Library
+/**
+ * Base library for MediaWiki.
+ *
+ * @class mw
+ * @alternateClassName mediaWiki
+ * @singleton
*/
var mw = ( function ( $, undefined ) {
@@ -10,15 +14,67 @@ var mw = ( function ( $, undefined ) {
var hasOwn = Object.prototype.hasOwnProperty,
slice = Array.prototype.slice;
+ /**
+ * Log a message to window.console, if possible. Useful to force logging of some
+ * errors that are otherwise hard to detect (I.e., this logs also in production mode).
+ * Gets console references in each invocation, so that delayed debugging tools work
+ * fine. No need for optimization here, which would only result in losing logs.
+ *
+ * @private
+ * @param {string} msg text for the log entry.
+ * @param {Error} [e]
+ */
+ function log( msg, e ) {
+ var console = window.console;
+ if ( console && console.log ) {
+ console.log( msg );
+ // If we have an exception object, log it through .error() to trigger
+ // proper stacktraces in browsers that support it. There are no (known)
+ // browsers that don't support .error(), that do support .log() and
+ // have useful exception handling through .log().
+ if ( e && console.error ) {
+ console.error( String( e ), e );
+ }
+ }
+ }
+
/* Object constructors */
/**
* Creates an object that can be read from or written to from prototype functions
* that allow both single and multiple variables at once.
+ *
+ * @example
+ *
+ * var addies, wanted, results;
+ *
+ * // Create your address book
+ * addies = new mw.Map();
+ *
+ * // This data could be coming from an external source (eg. API/AJAX)
+ * addies.set( {
+ * 'John Doe' : '10 Wall Street, New York, USA',
+ * 'Jane Jackson' : '21 Oxford St, London, UK',
+ * 'Dominique van Halen' : 'Kalverstraat 7, Amsterdam, NL'
+ * } );
+ *
+ * wanted = ['Dominique van Halen', 'George Johnson', 'Jane Jackson'];
+ *
+ * // You can detect missing keys first
+ * if ( !addies.exists( wanted ) ) {
+ * // One or more are missing (in this case: "George Johnson")
+ * mw.log( 'One or more names were not found in your address book' );
+ * }
+ *
+ * // Or just let it give you what it can
+ * results = addies.get( wanted, 'Middle of Nowhere, Alaska, US' );
+ * mw.log( results['Jane Jackson'] ); // "21 Oxford St, London, UK"
+ * mw.log( results['George Johnson'] ); // "Middle of Nowhere, Alaska, US"
+ *
* @class mw.Map
*
* @constructor
- * @param {boolean} global Whether to store the values in the global window
+ * @param {boolean} [global=false] Whether to store the values in the global window
* object or a exclusively in the object property 'values'.
*/
function Map( global ) {
@@ -32,8 +88,8 @@ var mw = ( function ( $, undefined ) {
*
* If called with no arguments, all values will be returned.
*
- * @param selection mixed String key or array of keys to get values for.
- * @param fallback mixed Value to use in case key(s) do not exist (optional).
+ * @param {string|Array} selection String key or array of keys to get values for.
+ * @param {Mixed} [fallback] Value to use in case key(s) do not exist.
* @return mixed If selection was a string returns the value or null,
* If selection was an array, returns an object of key/values (value is null if not found),
* If selection was not passed or invalid, will return the 'values' object member (be careful as
@@ -73,8 +129,8 @@ var mw = ( function ( $, undefined ) {
/**
* Sets one or multiple key/value pairs.
*
- * @param selection {mixed} String key or array of keys to set values for.
- * @param value {mixed} Value to set (optional, only in use when key is a string)
+ * @param {string|Object} selection String key to set value for, or object mapping keys to values.
+ * @param {Mixed} [value] Value to set (optional, only in use when key is a string)
* @return {Boolean} This returns true on success, false on failure.
*/
set: function ( selection, value ) {
@@ -96,7 +152,7 @@ var mw = ( function ( $, undefined ) {
/**
* Checks if one or multiple keys exist.
*
- * @param selection {mixed} String key or array of keys to check
+ * @param {Mixed} selection String key or array of keys to check
* @return {boolean} Existence of key(s)
*/
exists: function ( selection ) {
@@ -115,8 +171,12 @@ var mw = ( function ( $, undefined ) {
};
/**
- * Object constructor for messages,
- * similar to the Message class in MediaWiki PHP.
+ * Object constructor for messages.
+ *
+ * Similar to the Message class in MediaWiki PHP.
+ *
+ * Format defaults to 'text'.
+ *
* @class mw.Message
*
* @constructor
@@ -134,8 +194,7 @@ var mw = ( function ( $, undefined ) {
Message.prototype = {
/**
- * Simple message parser, does $N replacement, HTML-escaping (only for
- * 'escaped' format), and nothing else.
+ * Simple message parser, does $N replacement and nothing else.
*
* This may be overridden to provide a more complex message parser.
*
@@ -259,19 +318,21 @@ var mw = ( function ( $, undefined ) {
}
};
- /**
- * @class mw
- * @alternateClassName mediaWiki
- * @singleton
- */
return {
/* Public Members */
/**
- * Dummy function which in debug mode can be replaced with a function that
- * emulates console.log in console-less environments.
+ * Dummy placeholder for {@link mw.log}
+ * @method
*/
- log: function () { },
+ log: ( function () {
+ var log = function () {};
+ log.warn = function () {};
+ log.deprecate = function ( obj, key, val ) {
+ obj[key] = val;
+ };
+ return log;
+ }() ),
// Make the Map constructor publicly available.
Map: Map,
@@ -280,13 +341,17 @@ var mw = ( function ( $, undefined ) {
Message: Message,
/**
- * List of configuration values
+ * Map of configuration values
*
- * Dummy placeholder. Initiated in startUp module as a new instance of mw.Map().
- * If `$wgLegacyJavaScriptGlobals` is true, this Map will have its values
- * in the global window object.
- * @property
+ * Check out [the complete list of configuration values](https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config)
+ * on MediaWiki.org.
+ *
+ * If `$wgLegacyJavaScriptGlobals` is true, this Map will put its values in the
+ * global window object.
+ *
+ * @property {mw.Map} config
*/
+ // Dummy placeholder. Re-assigned in ResourceLoaderStartupModule with an instance of `mw.Map`.
config: null,
/**
@@ -295,9 +360,15 @@ var mw = ( function ( $, undefined ) {
*/
libs: {},
- /* Extension points */
-
/**
+ * Access container for deprecated functionality that can be moved from
+ * from their legacy location and attached to this object (e.g. a global
+ * function that is deprecated and as stop-gap can be exposed through here).
+ *
+ * This was reserved for future use but never ended up being used.
+ *
+ * @deprecated since 1.22: Let deprecated identifiers keep their original name
+ * and use mw.log#deprecate to create an access container for tracking.
* @property
*/
legacy: {},
@@ -311,7 +382,9 @@ var mw = ( function ( $, undefined ) {
/* Public Methods */
/**
- * Gets a message object, similar to wfMessage().
+ * Get a message object.
+ *
+ * Similar to wfMessage() in MediaWiki PHP.
*
* @param {string} key Key of message to get
* @param {Mixed...} parameters Parameters for the $N replacements in messages.
@@ -324,14 +397,16 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Gets a message string, similar to wfMessage()
+ * Get a message string using 'text' format.
+ *
+ * Similar to wfMsg() in MediaWiki PHP.
*
- * @see mw.Message#toString
+ * @see mw.Message
* @param {string} key Key of message to get
* @param {Mixed...} parameters Parameters for the $N replacements in messages.
* @return {string}
*/
- msg: function ( /* key, parameters... */ ) {
+ msg: function () {
return mw.message.apply( mw.message, arguments ).toString();
},
@@ -420,11 +495,11 @@ var mw = ( function ( $, undefined ) {
*
* @private
* @param {string} text CSS text
- * @param {Mixed} [nextnode] An Element or jQuery object for an element where
- * the style tag should be inserted before. Otherwise appended to the `<head>`.
- * @return {HTMLElement} Node reference to the created `<style>` tag.
+ * @param {HTMLElement|jQuery} [nextnode=document.head] The element where the style tag should be
+ * inserted before. Otherwise it will be appended to `<head>`.
+ * @return {HTMLElement} Reference to the created `<style>` element.
*/
- function addStyleTag( text, nextnode ) {
+ function newStyleTag( text, nextnode ) {
var s = document.createElement( 'style' );
// Insert into document before setting cssText (bug 33305)
if ( nextnode ) {
@@ -457,7 +532,7 @@ var mw = ( function ( $, undefined ) {
/**
* Checks whether it is safe to add this css to a stylesheet.
- *
+ *
* @private
* @param {string} cssText
* @return {boolean} False if a new one must be created.
@@ -470,8 +545,13 @@ var mw = ( function ( $, undefined ) {
}
/**
+ * Add a bit of CSS text to the current browser page.
+ *
+ * The CSS will be appended to an existing ResourceLoader-created `<style>` tag
+ * or create a new one based on whether the given `cssText` is safe for extension.
+ *
* @param {string} [cssText=cssBuffer] If called without cssText,
- * the internal buffer will be inserted instead.
+ * the internal buffer will be inserted instead.
* @param {Function} [callback]
*/
function addEmbeddedCSS( cssText, callback ) {
@@ -533,7 +613,7 @@ var mw = ( function ( $, undefined ) {
try {
styleEl.styleSheet.cssText += cssText; // IE
} catch ( e ) {
- log( 'addEmbeddedCSS fail\ne.message: ' + e.message, e );
+ log( 'addEmbeddedCSS fail', e );
}
} else {
styleEl.appendChild( document.createTextNode( String( cssText ) ) );
@@ -543,7 +623,7 @@ var mw = ( function ( $, undefined ) {
}
}
- $( addStyleTag( cssText, getMarker() ) ).data( 'ResourceLoaderDynamicStyleTag', true );
+ $( newStyleTag( cssText, getMarker() ) ).data( 'ResourceLoaderDynamicStyleTag', true );
cssCallbacks.fire().empty();
}
@@ -659,7 +739,7 @@ var mw = ( function ( $, undefined ) {
*
* @private
* @param {string|string[]} states Module states to filter by
- * @param {Array} modules List of module names to filter (optional, by default the entire
+ * @param {Array} [modules] List of module names to filter (optional, by default the entire
* registry is used)
* @return {Array} List of filtered module names
*/
@@ -712,30 +792,6 @@ var mw = ( function ( $, undefined ) {
}
/**
- * Log a message to window.console, if possible. Useful to force logging of some
- * errors that are otherwise hard to detect (I.e., this logs also in production mode).
- * Gets console references in each invocation, so that delayed debugging tools work
- * fine. No need for optimization here, which would only result in losing logs.
- *
- * @private
- * @param {string} msg text for the log entry.
- * @param {Error} [e]
- */
- function log( msg, e ) {
- var console = window.console;
- if ( console && console.log ) {
- console.log( msg );
- // If we have an exception object, log it through .error() to trigger
- // proper stacktraces in browsers that support it. There are no (known)
- // browsers that don't support .error(), that do support .log() and
- // have useful exception handling through .log().
- if ( e && console.error ) {
- console.error( e );
- }
- }
- }
-
- /**
* A module has entered state 'ready', 'error', or 'missing'. Automatically update pending jobs
* and modules that depend upon this module. if the given module failed, propagate the 'error'
* state up the dependency tree; otherwise, execute all jobs/modules that now have all their
@@ -775,22 +831,18 @@ var mw = ( function ( $, undefined ) {
j -= 1;
try {
if ( hasErrors ) {
- throw new Error( 'Module ' + module + ' failed.');
+ if ( $.isFunction( job.error ) ) {
+ job.error( new Error( 'Module ' + module + ' has failed dependencies' ), [module] );
+ }
} else {
if ( $.isFunction( job.ready ) ) {
job.ready();
}
}
} catch ( e ) {
- if ( $.isFunction( job.error ) ) {
- try {
- job.error( e, [module] );
- } catch ( ex ) {
- // A user-defined operation raised an exception. Swallow to protect
- // our state machine!
- log( 'Exception thrown by job.error()', ex );
- }
- }
+ // A user-defined callback raised an exception.
+ // Swallow it to protect our state machine!
+ log( 'Exception thrown by job.error', e );
}
}
}
@@ -816,8 +868,7 @@ var mw = ( function ( $, undefined ) {
*/
function addScript( src, callback, async ) {
/*jshint evil:true */
- var script, head,
- done = false;
+ var script, head, done;
// Using isReady directly instead of storing it locally from
// a $.fn.ready callback (bug 31895).
@@ -829,6 +880,7 @@ var mw = ( function ( $, undefined ) {
// IE-safe way of getting the <head>. document.head isn't supported
// in old IE, and doesn't work when in the <head>.
+ done = false;
head = document.getElementsByTagName( 'head' )[0] || document.body;
script = document.createElement( 'script' );
@@ -848,12 +900,12 @@ var mw = ( function ( $, undefined ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
- // Remove the script
+ // Detach the element from the document
if ( script.parentNode ) {
script.parentNode.removeChild( script );
}
- // Dereference the script
+ // Dereference the element from javascript
script = undefined;
callback();
@@ -950,7 +1002,7 @@ var mw = ( function ( $, undefined ) {
} catch ( e ) {
// This needs to NOT use mw.log because these errors are common in production mode
// and not in debug mode, such as when a symbol that should be global isn't exported
- log( 'Exception thrown by ' + module + ': ' + e.message, e );
+ log( 'Exception thrown by ' + module, e );
registry[module].state = 'error';
handlePending( module );
}
@@ -967,30 +1019,37 @@ var mw = ( function ( $, undefined ) {
mw.messages.set( registry[module].messages );
}
- // Make sure we don't run the scripts until all (potentially asynchronous)
- // stylesheet insertions have completed.
- ( function () {
- var pending = 0;
- checkCssHandles = function () {
- // cssHandlesRegistered ensures we don't take off too soon, e.g. when
- // one of the cssHandles is fired while we're still creating more handles.
- if ( cssHandlesRegistered && pending === 0 && runScript ) {
- runScript();
- runScript = undefined; // Revoke
- }
- };
- cssHandle = function () {
- var check = checkCssHandles;
- pending++;
- return function () {
- if (check) {
- pending--;
- check();
- check = undefined; // Revoke
+ if ( $.isReady || registry[module].async ) {
+ // Make sure we don't run the scripts until all (potentially asynchronous)
+ // stylesheet insertions have completed.
+ ( function () {
+ var pending = 0;
+ checkCssHandles = function () {
+ // cssHandlesRegistered ensures we don't take off too soon, e.g. when
+ // one of the cssHandles is fired while we're still creating more handles.
+ if ( cssHandlesRegistered && pending === 0 && runScript ) {
+ runScript();
+ runScript = undefined; // Revoke
}
};
- };
- }() );
+ cssHandle = function () {
+ var check = checkCssHandles;
+ pending++;
+ return function () {
+ if (check) {
+ pending--;
+ check();
+ check = undefined; // Revoke
+ }
+ };
+ };
+ }() );
+ } else {
+ // We are in blocking mode, and so we can't afford to wait for CSS
+ cssHandle = function () {};
+ // Run immediately
+ checkCssHandles = runScript;
+ }
// Process styles (see also mw.loader.implement)
// * back-compat: { <media>: css }
@@ -1131,7 +1190,7 @@ var mw = ( function ( $, undefined ) {
* @param {Object} moduleMap Module map, see #buildModulesString
* @param {Object} currReqBase Object with other parameters (other than 'modules') to use in the request
* @param {string} sourceLoadScript URL of load.php
- * @param {boolean} async If true, use an asynchrounous request even if document ready has not yet occurred
+ * @param {boolean} async If true, use an asynchronous request even if document ready has not yet occurred
*/
function doRequest( moduleMap, currReqBase, sourceLoadScript, async ) {
var request = $.extend(
@@ -1146,10 +1205,24 @@ var mw = ( function ( $, undefined ) {
/* Public Methods */
return {
- addStyleTag: addStyleTag,
+ /**
+ * The module registry is exposed as an aid for debugging and inspecting page
+ * state; it is not a public interface for modifying the registry.
+ *
+ * @see #registry
+ * @property
+ * @private
+ */
+ moduleRegistry: registry,
/**
- * Requests dependencies from server, loading and executing when things when ready.
+ * @inheritdoc #newStyleTag
+ * @method
+ */
+ addStyleTag: newStyleTag,
+
+ /**
+ * Batch-request queued dependencies from the server.
*/
work: function () {
var reqBase, splits, maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
@@ -1311,15 +1384,15 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Registers a module, letting the system know about it and its
+ * Register a module, letting the system know about it and its
* properties. Startup modules contain calls to this function.
*
- * @param module {String}: Module name
- * @param version {Number}: Module version number as a timestamp (falls backs to 0)
- * @param dependencies {String|Array|Function}: One string or array of strings of module
+ * @param {string} module Module name
+ * @param {number} version Module version number as a timestamp (falls backs to 0)
+ * @param {string|Array|Function} dependencies One string or array of strings of module
* names on which this module depends, or a function that returns that array.
- * @param group {String}: Group which the module is in (optional, defaults to null)
- * @param source {String}: Name of the source. Defaults to local.
+ * @param {string} [group=null] Group which the module is in
+ * @param {string} [source='local'] Name of the source
*/
register: function ( module, version, dependencies, group, source ) {
var m;
@@ -1362,9 +1435,10 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Implements a module, giving the system a course of action to take
- * upon loading. Results of a request for one or more modules contain
- * calls to this function.
+ * Implement a module given the components that make up the module.
+ *
+ * When #load or #using requests one or more modules, the server
+ * response contain calls to this function.
*
* All arguments are required.
*
@@ -1419,12 +1493,12 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Executes a function as soon as one or more required modules are ready
+ * Execute a function as soon as one or more required modules are ready.
*
- * @param dependencies {String|Array} Module name or array of modules names the callback
+ * @param {string|Array} dependencies Module name or array of modules names the callback
* dependends on to be ready before executing
- * @param ready {Function} callback to execute when all dependencies are ready (optional)
- * @param error {Function} callback to execute when if dependencies have a errors (optional)
+ * @param {Function} [ready] callback to execute when all dependencies are ready
+ * @param {Function} [error] callback to execute when if dependencies have a errors
*/
using: function ( dependencies, ready, error ) {
var tod = typeof dependencies;
@@ -1456,17 +1530,17 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Loads an external script or one or more modules for future use
+ * Load an external script or one or more modules.
*
- * @param modules {mixed} Either the name of a module, array of modules,
+ * @param {string|Array} modules Either the name of a module, array of modules,
* or a URL of an external script or style
- * @param type {String} mime-type to use if calling with a URL of an
+ * @param {string} [type='text/javascript'] mime-type to use if calling with a URL of an
* external script or style; acceptable values are "text/css" and
* "text/javascript"; if no type is provided, text/javascript is assumed.
- * @param async {Boolean} (optional) If true, load modules asynchronously
- * even if document ready has not yet occurred. If false (default),
- * block before document ready and load async after. If not set, true will
- * be assumed if loading a URL, and false will be assumed otherwise.
+ * @param {boolean} [async] If true, load modules asynchronously
+ * even if document ready has not yet occurred. If false, block before
+ * document ready and load async after. If not set, true will be
+ * assumed if loading a URL, and false will be assumed otherwise.
*/
load: function ( modules, type, async ) {
var filtered, m, module, l;
@@ -1536,10 +1610,10 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Changes the state of a module
+ * Change the state of one or more modules.
*
- * @param module {String|Object} module name or object of module name/state pairs
- * @param state {String} state name
+ * @param {string|Object} module module name or object of module name/state pairs
+ * @param {string} state state name
*/
state: function ( module, state ) {
var m;
@@ -1565,9 +1639,9 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Gets the version of a module
+ * Get the version of a module.
*
- * @param module string name of module to get version for
+ * @param {string} module Name of module to get version for
*/
getVersion: function ( module ) {
if ( registry[module] !== undefined && registry[module].version !== undefined ) {
@@ -1577,16 +1651,17 @@ var mw = ( function ( $, undefined ) {
},
/**
- * @deprecated since 1.18 use mw.loader.getVersion() instead
+ * @inheritdoc #getVersion
+ * @deprecated since 1.18 use #getVersion instead
*/
version: function () {
return mw.loader.getVersion.apply( mw.loader, arguments );
},
/**
- * Gets the state of a module
+ * Get the state of a module.
*
- * @param module string name of module to get state for
+ * @param {string} module name of module to get state for
*/
getState: function ( module ) {
if ( registry[module] !== undefined && registry[module].state !== undefined ) {
@@ -1607,16 +1682,45 @@ var mw = ( function ( $, undefined ) {
},
/**
- * For backwards-compatibility with Squid-cached pages. Loads mw.user
+ * Load the `mediawiki.user` module.
+ *
+ * For backwards-compatibility with cached pages from before 2013 where:
+ *
+ * - the `mediawiki.user` module didn't exist yet
+ * - `mw.user` was still part of mediawiki.js
+ * - `mw.loader.go` still existed and called after `mw.loader.load()`
*/
go: function () {
mw.loader.load( 'mediawiki.user' );
+ },
+
+ /**
+ * @inheritdoc mw.inspect#runReports
+ * @method
+ */
+ inspect: function () {
+ var args = slice.call( arguments );
+ mw.loader.using( 'mediawiki.inspect', function () {
+ mw.inspect.runReports.apply( mw.inspect, args );
+ } );
}
+
};
}() ),
/**
* HTML construction helper functions
+ *
+ * @example
+ *
+ * var Html, output;
+ *
+ * Html = mw.html;
+ * output = Html.element( 'div', {}, new Html.Raw(
+ * Html.element( 'img', { src: '<' } )
+ * ) );
+ * mw.log( output ); // <div><img src="&lt;"/></div>
+ *
* @class mw.html
* @singleton
*/
@@ -1646,39 +1750,17 @@ var mw = ( function ( $, undefined ) {
},
/**
- * Wrapper object for raw HTML passed to mw.html.element().
- * @class mw.html.Raw
- */
- Raw: function ( value ) {
- this.value = value;
- },
-
- /**
- * Wrapper object for CDATA element contents passed to mw.html.element()
- * @class mw.html.Cdata
- */
- Cdata: function ( value ) {
- this.value = value;
- },
-
- /**
* Create an HTML element string, with safe escaping.
*
- * @param name The tag name.
- * @param attrs An object with members mapping element names to values
- * @param contents The contents of the element. May be either:
+ * @param {string} name The tag name.
+ * @param {Object} attrs An object with members mapping element names to values
+ * @param {Mixed} contents The contents of the element. May be either:
* - string: The string is escaped.
* - null or undefined: The short closing form is used, e.g. <br/>.
* - this.Raw: The value attribute is included without escaping.
* - this.Cdata: The value attribute is included, and an exception is
* thrown if it contains an illegal ETAGO delimiter.
* See http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.3.2
- *
- * Example:
- * var h = mw.html;
- * return h.element( 'div', {},
- * new h.Raw( h.element( 'img', {src: '<'} ) ) );
- * Returns <div><img src="&lt;"/></div>
*/
element: function ( name, attrs, contents ) {
var v, attrName, s = '<' + name;
@@ -1727,6 +1809,22 @@ var mw = ( function ( $, undefined ) {
}
s += '</' + name + '>';
return s;
+ },
+
+ /**
+ * Wrapper object for raw HTML passed to mw.html.element().
+ * @class mw.html.Raw
+ */
+ Raw: function ( value ) {
+ this.value = value;
+ },
+
+ /**
+ * Wrapper object for CDATA element contents passed to mw.html.element()
+ * @class mw.html.Cdata
+ */
+ Cdata: function ( value ) {
+ this.value = value;
}
};
}() ),
@@ -1735,7 +1833,87 @@ var mw = ( function ( $, undefined ) {
user: {
options: new Map(),
tokens: new Map()
- }
+ },
+
+ /**
+ * Registry and firing of events.
+ *
+ * MediaWiki has various interface components that are extended, enhanced
+ * or manipulated in some other way by extensions, gadgets and even
+ * in core itself.
+ *
+ * This framework helps streamlining the timing of when these other
+ * code paths fire their plugins (instead of using document-ready,
+ * which can and should be limited to firing only once).
+ *
+ * Features like navigating to other wiki pages, previewing an edit
+ * and editing itself – without a refresh – can then retrigger these
+ * hooks accordingly to ensure everything still works as expected.
+ *
+ * Example usage:
+ *
+ * mw.hook( 'wikipage.content' ).add( fn ).remove( fn );
+ * mw.hook( 'wikipage.content' ).fire( $content );
+ *
+ * Handlers can be added and fired for arbitrary event names at any time. The same
+ * event can be fired multiple times. The last run of an event is memorized
+ * (similar to `$(document).ready` and `$.Deferred().done`).
+ * This means if an event is fired, and a handler added afterwards, the added
+ * function will be fired right away with the last given event data.
+ *
+ * Like Deferreds and Promises, the mw.hook object is both detachable and chainable.
+ * Thus allowing flexible use and optimal maintainability and authority control.
+ * You can pass around the `add` and/or `fire` method to another piece of code
+ * without it having to know the event name (or `mw.hook` for that matter).
+ *
+ * var h = mw.hook( 'bar.ready' );
+ * new mw.Foo( .. ).fetch( { callback: h.fire } );
+ *
+ * Note: Events are documented with an underscore instead of a dot in the event
+ * name due to jsduck not supporting dots in that position.
+ *
+ * @class mw.hook
+ */
+ hook: ( function () {
+ var lists = {};
+
+ /**
+ * Create an instance of mw.hook.
+ *
+ * @method hook
+ * @member mw
+ * @param {string} name Name of hook.
+ * @return {mw.hook}
+ */
+ return function ( name ) {
+ var list = lists[name] || ( lists[name] = $.Callbacks( 'memory' ) );
+
+ return {
+ /**
+ * Register a hook handler
+ * @param {Function...} handler Function to bind.
+ * @chainable
+ */
+ add: list.add,
+
+ /**
+ * Unregister a hook handler
+ * @param {Function...} handler Function to unbind.
+ * @chainable
+ */
+ remove: list.remove,
+
+ /**
+ * Run a hook.
+ * @param {Mixed...} data
+ * @chainable
+ */
+ fire: function () {
+ return list.fireWith( null, slice.call( arguments ) );
+ }
+ };
+ };
+ }() )
};
}( jQuery ) );
diff --git a/resources/mediawiki/mediawiki.log.js b/resources/mediawiki/mediawiki.log.js
index ee08b12b..75e4c961 100644
--- a/resources/mediawiki/mediawiki.log.js
+++ b/resources/mediawiki/mediawiki.log.js
@@ -1,4 +1,4 @@
-/**
+/*!
* Logger for MediaWiki javascript.
* Implements the stub left by the main 'mediawiki' module.
*
@@ -9,15 +9,20 @@
( function ( mw, $ ) {
/**
+ * @class mw.log
+ * @singleton
+ */
+
+ /**
* Logs a message to the console.
*
* In the case the browser does not have a console API, a console is created on-the-fly by appending
- * a <div id="mw-log-console"> element to the bottom of the body and then appending this and future
+ * a `<div id="mw-log-console">` element to the bottom of the body and then appending this and future
* messages to that, instead of the console.
*
- * @param {String} First in list of variadic messages to output to console.
+ * @param {string...} msg Messages to output to console.
*/
- mw.log = function ( /* logmsg, logmsg, */ ) {
+ mw.log = function () {
// Turn arguments into an array
var args = Array.prototype.slice.call( arguments ),
// Allow log messages to use a configured prefix to identify the source window (ie. frame)
@@ -54,7 +59,7 @@
hovzer.update();
}
$log.append(
- $( '<div></div>' )
+ $( '<div>' )
.css( {
borderBottom: 'solid 1px #DDDDDD',
fontSize: 'small',
@@ -68,4 +73,54 @@
} );
};
+ /**
+ * Write a message the console's warning channel.
+ * Also logs a stacktrace for easier debugging.
+ * Each action is silently ignored if the browser doesn't support it.
+ *
+ * @param {string...} msg Messages to output to console
+ */
+ mw.log.warn = function () {
+ var console = window.console;
+ if ( console && console.warn ) {
+ console.warn.apply( console, arguments );
+ if ( console.trace ) {
+ console.trace();
+ }
+ }
+ };
+
+ /**
+ * Create a property in a host object that, when accessed, will produce
+ * a deprecation warning in the console with backtrace.
+ *
+ * @param {Object} obj Host object of deprecated property
+ * @param {string} key Name of property to create in `obj`
+ * @param {Mixed} val The value this property should return when accessed
+ * @param {string} [msg] Optional text to include in the deprecation message.
+ */
+ mw.log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
+ obj[key] = val;
+ } : function ( obj, key, val, msg ) {
+ msg = 'MWDeprecationWarning: Use of "' + key + '" property is deprecated.' +
+ ( msg ? ( ' ' + msg ) : '' );
+ try {
+ Object.defineProperty( obj, key, {
+ configurable: true,
+ enumerable: true,
+ get: function () {
+ mw.log.warn( msg );
+ return val;
+ },
+ set: function ( newVal ) {
+ mw.log.warn( msg );
+ val = newVal;
+ }
+ } );
+ } catch ( err ) {
+ // IE8 can throw on Object.defineProperty
+ obj[key] = val;
+ }
+ };
+
}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki/mediawiki.notification.css b/resources/mediawiki/mediawiki.notification.css
index 9a7b651d..3aa358ac 100644
--- a/resources/mediawiki/mediawiki.notification.css
+++ b/resources/mediawiki/mediawiki.notification.css
@@ -2,15 +2,25 @@
* Stylesheet for mediawiki.notification module
*/
-#mw-notification-area {
+.mw-notification-area {
position: absolute;
- top: 1em;
- right: 1em;
+ top: 0;
+ right: 0;
+ padding: 1em 1em 0 0;
width: 20em;
line-height: 1.35;
z-index: 10000;
}
+.mw-notification-area-floating {
+ position: fixed;
+}
+
+* html .mw-notification-area-floating {
+ /* Make it at least 'absolute' in IE6 since 'fixed' is not supported */
+ position: absolute;
+}
+
.mw-notification {
padding: 0.25em 1em;
margin-bottom: 0.5em;
diff --git a/resources/mediawiki/mediawiki.notification.js b/resources/mediawiki/mediawiki.notification.js
index fd34e7ee..4ede8096 100644
--- a/resources/mediawiki/mediawiki.notification.js
+++ b/resources/mediawiki/mediawiki.notification.js
@@ -2,10 +2,10 @@
'use strict';
var notification,
- isPageReady = false,
- preReadyNotifQueue = [],
// The #mw-notification-area div that all notifications are contained inside.
- $area = null;
+ $area,
+ isPageReady = false,
+ preReadyNotifQueue = [];
/**
* Creates a Notification object for 1 message.
@@ -350,7 +350,9 @@
* @ignore
*/
function init() {
- $area = $( '<div id="mw-notification-area"></div>' )
+ var offset, $window = $( window );
+
+ $area = $( '<div id="mw-notification-area" class="mw-notification-area mw-notification-area-layout"></div>' )
// Pause auto-hide timers when the mouse is in the notification area.
.on( {
mouseenter: notification.pause,
@@ -371,6 +373,19 @@
// Prepend the notification area to the content area and save it's object.
mw.util.$content.prepend( $area );
+ offset = $area.offset();
+
+ function updateAreaMode() {
+ var isFloating = $window.scrollTop() > offset.top;
+ $area
+ .toggleClass( 'mw-notification-area-floating', isFloating )
+ .toggleClass( 'mw-notification-area-layout', !isFloating );
+ }
+
+ $window.on( 'scroll', updateAreaMode );
+
+ // Initial mode
+ updateAreaMode();
}
/**
@@ -411,6 +426,7 @@
* @param {HTMLElement|jQuery|mw.Message|string} message
* @param {Object} options The options to use for the notification.
* See #defaults for details.
+ * @return {Object} Object with a close function to close the notification
*/
notify: function ( message, options ) {
var notif;
@@ -423,6 +439,7 @@
} else {
preReadyNotifQueue.push( notif );
}
+ return { close: $.proxy( notif.close, notif ) };
},
/**
diff --git a/resources/mediawiki/mediawiki.notify.js b/resources/mediawiki/mediawiki.notify.js
index 83d95b61..743d6517 100644
--- a/resources/mediawiki/mediawiki.notify.js
+++ b/resources/mediawiki/mediawiki.notify.js
@@ -1,22 +1,23 @@
/**
* @class mw.plugin.notify
*/
-( function ( mw ) {
+( function ( mw, $ ) {
'use strict';
/**
* @see mw.notification#notify
* @param message
* @param options
+ * @return {jQuery.Promise}
*/
mw.notify = function ( message, options ) {
+ var d = $.Deferred();
// Don't bother loading the whole notification system if we never use it.
mw.loader.using( 'mediawiki.notification', function () {
- // Don't bother calling mw.loader.using a second time after we've already loaded mw.notification.
- mw.notify = mw.notification.notify;
// Call notify with the notification the user requested of us.
- mw.notify( message, options );
- } );
+ d.resolve( mw.notification.notify( message, options ) );
+ }, d.reject );
+ return d.promise();
};
/**
@@ -24,4 +25,4 @@
* @mixins mw.plugin.notify
*/
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki/mediawiki.searchSuggest.js b/resources/mediawiki/mediawiki.searchSuggest.js
index 2bc7cea9..7f078626 100644
--- a/resources/mediawiki/mediawiki.searchSuggest.js
+++ b/resources/mediawiki/mediawiki.searchSuggest.js
@@ -2,7 +2,7 @@
* Add search suggestions to the search form.
*/
( function ( mw, $ ) {
- $( document ).ready( function ( $ ) {
+ $( function () {
var map, resultRenderCache, searchboxesSelectors,
// Region where the suggestions box will appear directly below
// (using the same width). Can be a container element or the input
@@ -130,8 +130,6 @@
searchboxesSelectors = [
// Primary searchbox on every page in standard skins
'#searchInput',
- // Secondary searchbox in legacy skins (LegacyTemplate::searchForm uses id "searchInput + unique id")
- '#searchInput2',
// Special:Search
'#powerSearchText',
'#searchText',
@@ -141,36 +139,27 @@
$( searchboxesSelectors.join(', ') )
.suggestions( {
fetch: function ( query ) {
- var $el, jqXhr;
+ var $el;
if ( query.length !== 0 ) {
- $el = $(this);
- jqXhr = $.ajax( {
- url: mw.util.wikiScript( 'api' ),
- data: {
- format: 'json',
- action: 'opensearch',
- search: query,
- namespace: 0,
- suggest: ''
- },
- dataType: 'json',
- success: function ( data ) {
- if ( $.isArray( data ) && data.length ) {
- $el.suggestions( 'suggestions', data[1] );
- }
- }
- });
- $el.data( 'request', jqXhr );
+ $el = $( this );
+ $el.data( 'request', ( new mw.Api() ).get( {
+ action: 'opensearch',
+ search: query,
+ namespace: 0,
+ suggest: ''
+ } ).done( function ( data ) {
+ $el.suggestions( 'suggestions', data[1] );
+ } ) );
}
},
cancel: function () {
- var jqXhr = $(this).data( 'request' );
+ var apiPromise = $( this ).data( 'request' );
// If the delay setting has caused the fetch to have not even happened
- // yet, the jqXHR object will have never been set.
- if ( jqXhr && $.isFunction( jqXhr.abort ) ) {
- jqXhr.abort();
- $(this).removeData( 'request' );
+ // yet, the apiPromise object will have never been set.
+ if ( apiPromise && $.isFunction( apiPromise.abort ) ) {
+ apiPromise.abort();
+ $( this ).removeData( 'request' );
}
},
result: {
@@ -196,11 +185,6 @@
return;
}
- // Placeholder text for search box
- $searchInput
- .attr( 'placeholder', mw.msg( 'searchsuggest-search' ) )
- .placeholder();
-
// Special suggestions functionality for skin-provided search box
$searchInput.suggestions( {
result: {
diff --git a/resources/mediawiki/mediawiki.user.js b/resources/mediawiki/mediawiki.user.js
index e0329597..3e375fb6 100644
--- a/resources/mediawiki/mediawiki.user.js
+++ b/resources/mediawiki/mediawiki.user.js
@@ -1,67 +1,60 @@
-/*
- * Implementation for mediaWiki.user
+/**
+ * @class mw.user
+ * @singleton
*/
-
( function ( mw, $ ) {
+ var user,
+ callbacks = {},
+ // Extend the skeleton mw.user from mediawiki.js
+ // This is kind of ugly but we're stuck with this for b/c reasons
+ options = mw.user.options || new mw.Map(),
+ tokens = mw.user.tokens || new mw.Map();
/**
- * User object
+ * Get the current user's groups or rights
+ *
+ * @private
+ * @param {string} info One of 'groups' or 'rights'
+ * @param {Function} callback
*/
- function User( options, tokens ) {
- var user, callbacks;
-
- /* Private Members */
-
- user = this;
- callbacks = {};
-
- /**
- * Gets the current user's groups or rights.
- * @param {String} info: One of 'groups' or 'rights'.
- * @param {Function} callback
- */
- function getUserInfo( info, callback ) {
- var api;
- if ( callbacks[info] ) {
- callbacks[info].add( callback );
- return;
- }
- callbacks.rights = $.Callbacks('once memory');
- callbacks.groups = $.Callbacks('once memory');
+ function getUserInfo( info, callback ) {
+ var api;
+ if ( callbacks[info] ) {
callbacks[info].add( callback );
- api = new mw.Api();
- api.get( {
- action: 'query',
- meta: 'userinfo',
- uiprop: 'rights|groups'
- } ).always( function ( data ) {
- var rights, groups;
- if ( data.query && data.query.userinfo ) {
- rights = data.query.userinfo.rights;
- groups = data.query.userinfo.groups;
- }
- callbacks.rights.fire( rights || [] );
- callbacks.groups.fire( groups || [] );
- } );
+ return;
}
+ callbacks.rights = $.Callbacks('once memory');
+ callbacks.groups = $.Callbacks('once memory');
+ callbacks[info].add( callback );
+ api = new mw.Api();
+ api.get( {
+ action: 'query',
+ meta: 'userinfo',
+ uiprop: 'rights|groups'
+ } ).always( function ( data ) {
+ var rights, groups;
+ if ( data.query && data.query.userinfo ) {
+ rights = data.query.userinfo.rights;
+ groups = data.query.userinfo.groups;
+ }
+ callbacks.rights.fire( rights || [] );
+ callbacks.groups.fire( groups || [] );
+ } );
+ }
- /* Public Members */
-
- this.options = options || new mw.Map();
-
- this.tokens = tokens || new mw.Map();
-
- /* Public Methods */
+ mw.user = user = {
+ options: options,
+ tokens: tokens,
/**
- * Generates a random user session ID (32 alpha-numeric characters).
+ * Generate a random user session ID (32 alpha-numeric characters)
*
* This information would potentially be stored in a cookie to identify a user during a
* session or series of sessions. Its uniqueness should not be depended on.
*
- * @return String: Random set of 32 alpha-numeric characters
+ * @return {string} Random set of 32 alpha-numeric characters
*/
- this.generateRandomSessionId = function () {
+ generateRandomSessionId: function () {
var i, r,
id = '',
seed = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
@@ -70,33 +63,45 @@
id += seed.substring( r, r + 1 );
}
return id;
- };
+ },
+
+ /**
+ * Get the current user's database id
+ *
+ * Not to be confused with #id.
+ *
+ * @return {number} Current user's id, or 0 if user is anonymous
+ */
+ getId: function () {
+ return mw.config.get( 'wgUserId', 0 );
+ },
/**
- * Gets the current user's name.
+ * Get the current user's name
*
- * @return Mixed: User name string or null if users is anonymous
+ * @return {string|null} User name string or null if user is anonymous
*/
- this.getName = function () {
+ getName: function () {
return mw.config.get( 'wgUserName' );
- };
+ },
/**
- * @deprecated since 1.20 use mw.user.getName() instead
+ * @inheritdoc #getName
+ * @deprecated since 1.20 use #getName instead
*/
- this.name = function () {
- return this.getName();
- };
+ name: function () {
+ return user.getName();
+ },
/**
- * Get date user registered, if available.
+ * Get date user registered, if available
*
- * @return {Date|false|null} date user registered, or false for anonymous users, or
+ * @return {Date|boolean|null} Date user registered, or false for anonymous users, or
* null when data is not available
*/
- this.getRegistration = function () {
+ getRegistration: function () {
var registration = mw.config.get( 'wgUserRegistration' );
- if ( this.isAnon() ) {
+ if ( user.isAnon() ) {
return false;
} else if ( registration === null ) {
// Information may not be available if they signed up before
@@ -105,110 +110,109 @@
} else {
return new Date( registration );
}
- };
+ },
/**
- * Checks if the current user is anonymous.
+ * Whether the current user is anonymous
*
- * @return Boolean
+ * @return {boolean}
*/
- this.isAnon = function () {
+ isAnon: function () {
return user.getName() === null;
- };
+ },
/**
- * @deprecated since 1.20 use mw.user.isAnon() instead
+ * @inheritdoc #isAnon
+ * @deprecated since 1.20 use #isAnon instead
*/
- this.anonymous = function () {
+ anonymous: function () {
return user.isAnon();
- };
+ },
/**
- * Gets a random session ID automatically generated and kept in a cookie.
+ * Get an automatically generated random ID (stored in a session cookie)
*
* This ID is ephemeral for everyone, staying in their browser only until they close
* their browser.
*
- * @return String: User name or random session ID
+ * @return {string} Random session ID
*/
- this.sessionId = function () {
+ sessionId: function () {
var sessionId = $.cookie( 'mediaWiki.user.sessionId' );
- if ( typeof sessionId === 'undefined' || sessionId === null ) {
+ if ( sessionId === undefined || sessionId === null ) {
sessionId = user.generateRandomSessionId();
- $.cookie( 'mediaWiki.user.sessionId', sessionId, { 'expires': null, 'path': '/' } );
+ $.cookie( 'mediaWiki.user.sessionId', sessionId, { expires: null, path: '/' } );
}
return sessionId;
- };
+ },
/**
- * Gets the current user's name or the session ID
+ * Get the current user's name or the session ID
*
- * @return String: User name or random session ID
+ * Not to be confused with #getId.
+ *
+ * @return {string} User name or random session ID
*/
- this.id = function() {
- var name = user.getName();
- if ( name ) {
- return name;
- }
- return user.sessionId();
- };
+ id: function () {
+ return user.getName() || user.sessionId();
+ },
/**
- * Gets the user's bucket, placing them in one at random based on set odds if needed.
- *
- * @param key String: Name of bucket
- * @param options Object: Bucket configuration options
- * @param options.buckets Object: List of bucket-name/relative-probability pairs (required,
- * must have at least one pair)
- * @param options.version Number: Version of bucket test, changing this forces rebucketing
- * (optional, default: 0)
- * @param options.tracked Boolean: Track the event of bucketing through the API module of
- * the ClickTracking extension (optional, default: false)
- * @param options.expires Number: Length of time (in days) until the user gets rebucketed
- * (optional, default: 30)
- * @return String: Bucket name - the randomly chosen key of the options.buckets object
+ * Get the user's bucket (place them in one if not done already)
*
- * @example
* mw.user.bucket( 'test', {
- * 'buckets': { 'ignored': 50, 'control': 25, 'test': 25 },
- * 'version': 1,
- * 'tracked': true,
- * 'expires': 7
+ * buckets: { ignored: 50, control: 25, test: 25 },
+ * version: 1,
+ * expires: 7
* } );
+ *
+ * @param {string} key Name of bucket
+ * @param {Object} options Bucket configuration options
+ * @param {Object} options.buckets List of bucket-name/relative-probability pairs (required,
+ * must have at least one pair)
+ * @param {number} [options.version=0] Version of bucket test, changing this forces
+ * rebucketing
+ * @param {number} [options.expires=30] Length of time (in days) until the user gets
+ * rebucketed
+ * @return {string} Bucket name - the randomly chosen key of the `options.buckets` object
*/
- this.bucket = function ( key, options ) {
+ bucket: function ( key, options ) {
var cookie, parts, version, bucket,
range, k, rand, total;
options = $.extend( {
buckets: {},
version: 0,
- tracked: false,
expires: 30
}, options || {} );
cookie = $.cookie( 'mediaWiki.user.bucket:' + key );
// Bucket information is stored as 2 integers, together as version:bucket like: "1:2"
- if ( typeof cookie === 'string' && cookie.length > 2 && cookie.indexOf( ':' ) > 0 ) {
+ if ( typeof cookie === 'string' && cookie.length > 2 && cookie.indexOf( ':' ) !== -1 ) {
parts = cookie.split( ':' );
if ( parts.length > 1 && Number( parts[0] ) === options.version ) {
version = Number( parts[0] );
bucket = String( parts[1] );
}
}
+
if ( bucket === undefined ) {
if ( !$.isPlainObject( options.buckets ) ) {
- throw 'Invalid buckets error. Object expected for options.buckets.';
+ throw new Error( 'Invalid bucket. Object expected for options.buckets.' );
}
+
version = Number( options.version );
+
// Find range
range = 0;
for ( k in options.buckets ) {
range += options.buckets[k];
}
+
// Select random value within range
rand = Math.random() * range;
+
// Determine which bucket the value landed in
total = 0;
for ( k in options.buckets ) {
@@ -218,39 +222,34 @@
break;
}
}
- if ( options.tracked ) {
- mw.loader.using( 'jquery.clickTracking', function () {
- $.trackAction(
- 'mediaWiki.user.bucket:' + key + '@' + version + ':' + bucket
- );
- } );
- }
+
$.cookie(
'mediaWiki.user.bucket:' + key,
version + ':' + bucket,
- { 'path': '/', 'expires': Number( options.expires ) }
+ { path: '/', expires: Number( options.expires ) }
);
}
+
return bucket;
- };
+ },
/**
- * Gets the current user's groups.
+ * Get the current user's groups
+ *
+ * @param {Function} callback
*/
- this.getGroups = function ( callback ) {
+ getGroups: function ( callback ) {
getUserInfo( 'groups', callback );
- };
+ },
/**
- * Gets the current user's rights.
+ * Get the current user's rights
+ *
+ * @param {Function} callback
*/
- this.getRights = function ( callback ) {
+ getRights: function ( callback ) {
getUserInfo( 'rights', callback );
- };
- }
-
- // Extend the skeleton mw.user from mediawiki.js
- // This is kind of ugly but we're stuck with this for b/c reasons
- mw.user = new User( mw.user.options, mw.user.tokens );
+ }
+ };
}( mediaWiki, jQuery ) );
diff --git a/resources/mediawiki/mediawiki.util.js b/resources/mediawiki/mediawiki.util.js
index 5211b0d0..7383df2d 100644
--- a/resources/mediawiki/mediawiki.util.js
+++ b/resources/mediawiki/mediawiki.util.js
@@ -13,7 +13,7 @@
* (don't call before document ready)
*/
init: function () {
- var profile, $tocTitle, $tocToggleLink, hideTocCookie;
+ var profile;
/* Set tooltipAccessKeyPrefix */
profile = $.client.profile();
@@ -53,8 +53,9 @@
|| profile.name === 'konqueror' ) ) {
util.tooltipAccessKeyPrefix = 'ctrl-';
- // Firefox 2.x and later
- } else if ( profile.name === 'firefox' && profile.versionBase > '1' ) {
+ // Firefox/Iceweasel 2.x and later
+ } else if ( ( profile.name === 'firefox' || profile.name === 'iceweasel' )
+ && profile.versionBase > '1' ) {
util.tooltipAccessKeyPrefix = 'alt-shift-';
}
@@ -105,29 +106,32 @@
} )();
// Table of contents toggle
- $tocTitle = $( '#toctitle' );
- $tocToggleLink = $( '#togglelink' );
- // Only add it if there is a TOC and there is no toggle added already
- if ( $( '#toc' ).length && $tocTitle.length && !$tocToggleLink.length ) {
- hideTocCookie = $.cookie( 'mw_hidetoc' );
+ mw.hook( 'wikipage.content' ).add( function () {
+ var $tocTitle, $tocToggleLink, hideTocCookie;
+ $tocTitle = $( '#toctitle' );
+ $tocToggleLink = $( '#togglelink' );
+ // Only add it if there is a TOC and there is no toggle added already
+ if ( $( '#toc' ).length && $tocTitle.length && !$tocToggleLink.length ) {
+ hideTocCookie = $.cookie( 'mw_hidetoc' );
$tocToggleLink = $( '<a href="#" class="internal" id="togglelink"></a>' )
.text( mw.msg( 'hidetoc' ) )
.click( function ( e ) {
e.preventDefault();
util.toggleToc( $(this) );
} );
- $tocTitle.append(
- $tocToggleLink
- .wrap( '<span class="toctoggle"></span>' )
- .parent()
- .prepend( '&nbsp;[' )
- .append( ']&nbsp;' )
- );
-
- if ( hideTocCookie === '1' ) {
- util.toggleToc( $tocToggleLink );
+ $tocTitle.append(
+ $tocToggleLink
+ .wrap( '<span class="toctoggle"></span>' )
+ .parent()
+ .prepend( '&nbsp;[' )
+ .append( ']&nbsp;' )
+ );
+
+ if ( hideTocCookie === '1' ) {
+ util.toggleToc( $tocToggleLink );
+ }
}
- }
+ } );
},
/* Main body */
@@ -160,11 +164,18 @@
* Get the link to a page name (relative to `wgServer`),
*
* @param {string} str Page name to get the link for.
+ * @param {Object} params A mapping of query parameter names to values,
+ * e.g. { action: 'edit' }. Optional.
* @return {string} Location for a page with name of `str` or boolean false on error.
*/
- wikiGetlink: function ( str ) {
- return mw.config.get( 'wgArticlePath' ).replace( '$1',
+ getUrl: function ( str, params ) {
+ var url = mw.config.get( 'wgArticlePath' ).replace( '$1',
util.wikiUrlencode( typeof str === 'string' ? str : mw.config.get( 'wgPageName' ) ) );
+ if ( params && !$.isEmptyObject( params ) ) {
+ url += url.indexOf( '?' ) !== -1 ? '&' : '?';
+ url += $.param( params );
+ }
+ return url;
},
/**
@@ -251,7 +262,7 @@
* Returns null if not found.
*
* @param {string} param The parameter name.
- * @param {string} [url] URL to search through.
+ * @param {string} [url=document.location.href] URL to search through, defaulting to the current document's URL.
* @return {Mixed} Parameter value or null.
*/
getParamValue: function ( param, url ) {
@@ -279,8 +290,17 @@
/**
* @property {RegExp}
* Regex to match accesskey tooltips.
+ *
+ * Should match:
+ *
+ * - "ctrl-option-"
+ * - "alt-shift-"
+ * - "ctrl-alt-"
+ * - "ctrl-"
+ *
+ * The accesskey is matched in group $6.
*/
- tooltipAccessKeyRegexp: /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
+ tooltipAccessKeyRegexp: /\[(ctrl-)?(option-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
/**
* Add the appropriate prefix to the accesskey shown in the tooltip.
@@ -301,9 +321,9 @@
}
$nodes.attr( 'title', function ( i, val ) {
- if ( val && util.tooltipAccessKeyRegexp.exec( val ) ) {
+ if ( val && util.tooltipAccessKeyRegexp.test( val ) ) {
return val.replace( util.tooltipAccessKeyRegexp,
- '[' + util.tooltipAccessKeyPrefix + '$5]' );
+ '[' + util.tooltipAccessKeyPrefix + '$6]' );
}
return val;
} );
@@ -364,87 +384,86 @@
$link.attr( 'title', tooltip );
}
- // Some skins don't have any portlets
- // just add it to the bottom of their 'sidebar' element as a fallback
- switch ( mw.config.get( 'skin' ) ) {
- case 'standard':
- $( '#quickbar' ).append( $link.after( '<br/>' ) );
- return $link[0];
- case 'nostalgia':
- $( '#searchform' ).before( $link ).before( ' &#124; ' );
- return $link[0];
- default: // Skins like chick, modern, monobook, myskin, simple, vector...
-
- // Select the specified portlet
- $portlet = $( '#' + portlet );
- if ( $portlet.length === 0 ) {
- return null;
- }
- // Select the first (most likely only) unordered list inside the portlet
- $ul = $portlet.find( 'ul' ).eq( 0 );
+ // Select the specified portlet
+ $portlet = $( '#' + portlet );
+ if ( $portlet.length === 0 ) {
+ return null;
+ }
+ // Select the first (most likely only) unordered list inside the portlet
+ $ul = $portlet.find( 'ul' ).eq( 0 );
- // If it didn't have an unordered list yet, create it
- if ( $ul.length === 0 ) {
+ // If it didn't have an unordered list yet, create it
+ if ( $ul.length === 0 ) {
- $ul = $( '<ul>' );
+ $ul = $( '<ul>' );
- // If there's no <div> inside, append it to the portlet directly
- if ( $portlet.find( 'div:first' ).length === 0 ) {
- $portlet.append( $ul );
- } else {
- // otherwise if there's a div (such as div.body or div.pBody)
- // append the <ul> to last (most likely only) div
- $portlet.find( 'div' ).eq( -1 ).append( $ul );
- }
- }
- // Just in case..
- if ( $ul.length === 0 ) {
- return null;
+ // If there's no <div> inside, append it to the portlet directly
+ if ( $portlet.find( 'div:first' ).length === 0 ) {
+ $portlet.append( $ul );
+ } else {
+ // otherwise if there's a div (such as div.body or div.pBody)
+ // append the <ul> to last (most likely only) div
+ $portlet.find( 'div' ).eq( -1 ).append( $ul );
}
+ }
+ // Just in case..
+ if ( $ul.length === 0 ) {
+ return null;
+ }
- // Unhide portlet if it was hidden before
- $portlet.removeClass( 'emptyPortlet' );
+ // Unhide portlet if it was hidden before
+ $portlet.removeClass( 'emptyPortlet' );
- // Wrap the anchor tag in a list item (and a span if $portlet is a Vector tab)
- // and back up the selector to the list item
- if ( $portlet.hasClass( 'vectorTabs' ) ) {
- $item = $link.wrap( '<li><span></span></li>' ).parent().parent();
- } else {
- $item = $link.wrap( '<li></li>' ).parent();
- }
+ // Wrap the anchor tag in a list item (and a span if $portlet is a Vector tab)
+ // and back up the selector to the list item
+ if ( $portlet.hasClass( 'vectorTabs' ) ) {
+ $item = $link.wrap( '<li><span></span></li>' ).parent().parent();
+ } else {
+ $item = $link.wrap( '<li></li>' ).parent();
+ }
- // Implement the properties passed to the function
- if ( id ) {
- $item.attr( 'id', id );
- }
+ // Implement the properties passed to the function
+ if ( id ) {
+ $item.attr( 'id', id );
+ }
+
+ if ( tooltip ) {
+ // Trim any existing accesskey hint and the trailing space
+ tooltip = $.trim( tooltip.replace( util.tooltipAccessKeyRegexp, '' ) );
if ( accesskey ) {
- $link.attr( 'accesskey', accesskey );
tooltip += ' [' + accesskey + ']';
- $link.attr( 'title', tooltip );
}
- if ( accesskey && tooltip ) {
+ $link.attr( 'title', tooltip );
+ if ( accesskey ) {
util.updateTooltipAccessKeys( $link );
}
+ }
- // Where to put our node ?
- // - nextnode is a DOM element (was the only option before MW 1.17, in wikibits.js)
- if ( nextnode && nextnode.parentNode === $ul[0] ) {
- $(nextnode).before( $item );
-
- // - nextnode is a CSS selector for jQuery
- } else if ( typeof nextnode === 'string' && $ul.find( nextnode ).length !== 0 ) {
- $ul.find( nextnode ).eq( 0 ).before( $item );
+ if ( accesskey ) {
+ $link.attr( 'accesskey', accesskey );
+ }
- // If the jQuery selector isn't found within the <ul>,
- // or if nextnode was invalid or not passed at all,
- // then just append it at the end of the <ul> (this is the default behavior)
- } else {
+ if ( nextnode ) {
+ if ( nextnode.nodeType || typeof nextnode === 'string' ) {
+ // nextnode is a DOM element (was the only option before MW 1.17, in wikibits.js)
+ // or nextnode is a CSS selector for jQuery
+ nextnode = $ul.find( nextnode );
+ } else if ( !nextnode.jquery || ( nextnode.length && nextnode[0].parentNode !== $ul[0] ) ) {
+ // Fallback
$ul.append( $item );
+ return $item[0];
}
+ if ( nextnode.length === 1 ) {
+ // nextnode is a jQuery object that represents exactly one element
+ nextnode.before( $item );
+ return $item[0];
+ }
+ }
+ // Fallback (this is the default behavior)
+ $ul.append( $item );
+ return $item[0];
- return $item[0];
- }
},
/**
@@ -454,7 +473,7 @@
*
* @param {Mixed} message The DOM-element, jQuery object or HTML-string to be put inside the message box.
* to allow CSS/JS to hide different boxes. null = no class used.
- * @deprecated Use mw#notify
+ * @deprecated since 1.20 Use mw#notify
*/
jsMessage: function ( message ) {
if ( !arguments.length || message === '' || message === null ) {
@@ -593,6 +612,13 @@
}
};
+ /**
+ * @method wikiGetlink
+ * @inheritdoc #getUrl
+ * @deprecated since 1.23 Use #getUrl instead.
+ */
+ mw.log.deprecate( util, 'wikiGetlink', util.getUrl, 'Use mw.util.getUrl instead.' );
+
mw.util = util;
}( mediaWiki, jQuery ) );
diff --git a/resources/startup.js b/resources/startup.js
index deff7e6e..b6a27d2d 100644
--- a/resources/startup.js
+++ b/resources/startup.js
@@ -10,23 +10,39 @@
* This function will be deleted after it's used, so do not expand it to be
* generally useful beyond startup.
*
- * MediaWiki & jQuery compatibility:
- * - Internet Explorer 6.0+
- * - Firefox 10+
- * - Safari 5.0+
- * - Opera 11+
- * - Chrome
+ * See also:
+ * - https://www.mediawiki.org/wiki/Compatibility#Browser
+ * - http://jquerymobile.com/gbs/
+ * - http://jquery.com/browser-support/
*/
/*jshint unused: false */
-function isCompatible() {
- // IE < 6.0
- if ( navigator.appVersion.indexOf( 'MSIE' ) !== -1
- && parseFloat( navigator.appVersion.split( 'MSIE' )[1] ) < 6 )
- {
- return false;
+function isCompatible( ua ) {
+ if ( ua === undefined ) {
+ ua = navigator.userAgent;
}
- return true;
+
+ // MediaWiki JS or jQuery is known to have issues with:
+ return !(
+ // Internet Explorer < 6
+ ( ua.indexOf( 'MSIE' ) !== -1 && parseFloat( ua.split( 'MSIE' )[1] ) < 6 ) ||
+ // Firefox < 3
+ ( ua.indexOf( 'Firefox/' ) !== -1 && parseFloat( ua.split( 'Firefox/' )[1] ) < 3 ) ||
+ // BlackBerry < 6
+ ua.match( /BlackBerry[^\/]*\/[1-5]\./ ) ||
+ // Open WebOS < 1.5
+ ua.match( /webOS\/1\.[0-4]/ ) ||
+ // Anything PlayStation based.
+ ua.match( /PlayStation/i ) ||
+ // Any Symbian based browsers
+ ua.match( /SymbianOS|Series60/ ) ||
+ // Any NetFront based browser
+ ua.match( /NetFront/ ) ||
+ // Opera Mini, all versions
+ ua.match( /Opera Mini/ ) ||
+ // Nokia's Ovi Browser
+ ua.match( /S40OviBrowser/ )
+ );
}
/**