From f6d65e533c62f6deb21342d4901ece24497b433e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 4 Jun 2015 07:31:04 +0200 Subject: Update to MediaWiki 1.25.1 --- resources/src/jquery.json-deprecate.js | 8 - resources/src/jquery.tipsy/jquery.tipsy.js | 23 +- resources/src/jquery/jquery.accessKeyLabel.js | 2 +- resources/src/jquery/jquery.arrowSteps.js | 2 +- resources/src/jquery/jquery.badge.css | 2 +- resources/src/jquery/jquery.badge.js | 3 +- resources/src/jquery/jquery.client.js | 301 ------- resources/src/jquery/jquery.confirmable.js | 14 +- .../src/jquery/jquery.confirmable.mediawiki.js | 4 +- resources/src/jquery/jquery.expandableField.js | 2 +- resources/src/jquery/jquery.footHovzer.js | 16 +- resources/src/jquery/jquery.getAttrs.js | 49 +- resources/src/jquery/jquery.hidpi.js | 4 +- resources/src/jquery/jquery.makeCollapsible.css | 24 +- resources/src/jquery/jquery.makeCollapsible.js | 4 +- resources/src/jquery/jquery.mwExtension.js | 8 +- resources/src/jquery/jquery.placeholder.js | 10 +- .../src/jquery/jquery.qunit.completenessTest.js | 14 +- resources/src/jquery/jquery.suggestions.js | 149 ++-- resources/src/jquery/jquery.tabIndex.js | 8 +- resources/src/jquery/jquery.tablesorter.js | 167 ++-- resources/src/jquery/jquery.textSelection.js | 39 +- .../mediawiki.action/images/nextredirect-ltr.png | Bin 121 -> 122 bytes .../mediawiki.action/images/nextredirect-ltr.svg | 9 + .../mediawiki.action/images/nextredirect-rtl.png | Bin 121 -> 118 bytes .../mediawiki.action/images/nextredirect-rtl.svg | 9 + .../src/mediawiki.action/images/redirect-ltr.png | Bin 128 -> 169 bytes .../src/mediawiki.action/images/redirect-ltr.svg | 9 + .../src/mediawiki.action/images/redirect-rtl.png | Bin 132 -> 139 bytes .../src/mediawiki.action/images/redirect-rtl.svg | 9 + .../mediawiki.action.edit.editWarning.js | 61 +- .../src/mediawiki.action/mediawiki.action.edit.js | 232 +----- .../mediawiki.action.edit.preview.js | 257 ++++-- .../mediawiki.action.edit.stash.js | 76 ++ .../mediawiki.action.edit.styles.css | 8 - .../images/ar/button_bold.png | Bin 533 -> 0 bytes .../images/ar/button_headline.png | Bin 484 -> 0 bytes .../images/ar/button_italic.png | Bin 532 -> 0 bytes .../images/ar/button_link.png | Bin 557 -> 0 bytes .../images/ar/button_nowiki.png | Bin 874 -> 0 bytes .../images/be-tarask/button_bold.png | Bin 550 -> 0 bytes .../images/be-tarask/button_italic.png | Bin 539 -> 0 bytes .../images/be-tarask/button_link.png | Bin 419 -> 0 bytes .../images/de/button_bold.png | Bin 255 -> 0 bytes .../images/de/button_italic.png | Bin 260 -> 0 bytes .../images/en/button_bold.png | Bin 250 -> 0 bytes .../images/en/button_extlink.png | Bin 435 -> 0 bytes .../images/en/button_headline.png | Bin 440 -> 0 bytes .../images/en/button_hr.png | Bin 200 -> 0 bytes .../images/en/button_image.png | Bin 483 -> 0 bytes .../images/en/button_italic.png | Bin 250 -> 0 bytes .../images/en/button_link.png | Bin 280 -> 0 bytes .../images/en/button_media.png | Bin 728 -> 0 bytes .../images/en/button_nowiki.png | Bin 322 -> 0 bytes .../images/en/button_sig.png | Bin 920 -> 0 bytes .../images/fa/button_bold.png | Bin 459 -> 0 bytes .../images/fa/button_headline.png | Bin 392 -> 0 bytes .../images/fa/button_italic.png | Bin 512 -> 0 bytes .../images/fa/button_link.png | Bin 485 -> 0 bytes .../images/fa/button_nowiki.png | Bin 874 -> 0 bytes .../images/ksh/LICENSE | 7 - .../images/ksh/button_italic.png | Bin 368 -> 0 bytes .../images/ru/LICENSE | 17 - .../images/ru/button_bold.png | Bin 254 -> 0 bytes .../images/ru/button_italic.png | Bin 423 -> 0 bytes .../images/ru/button_link.png | Bin 278 -> 0 bytes .../mediawiki.action.edit.toolbar.less | 42 - .../mediawiki.action.history.diff.css | 7 +- .../mediawiki.action.history.diff.print.css | 16 + .../mediawiki.action/mediawiki.action.history.js | 3 +- .../mediawiki.action.view.categoryPage.less | 11 + .../mediawiki.action.view.dblClickEdit.js | 13 +- .../mediawiki.action.view.metadata.css | 10 + .../mediawiki.action.view.postEdit.js | 11 +- .../mediawiki.action.view.redirect.js | 3 - .../mediawiki.action.view.redirectPage.css | 20 +- .../src/mediawiki.action/templates/postEdit.html | 6 + .../src/mediawiki.api/mediawiki.api.category.js | 44 +- resources/src/mediawiki.api/mediawiki.api.edit.js | 43 +- resources/src/mediawiki.api/mediawiki.api.js | 87 +-- resources/src/mediawiki.api/mediawiki.api.login.js | 45 +- .../src/mediawiki.api/mediawiki.api.options.js | 89 +++ resources/src/mediawiki.api/mediawiki.api.parse.js | 12 +- resources/src/mediawiki.api/mediawiki.api.watch.js | 21 +- resources/src/mediawiki.language/languages/fi.js | 2 +- resources/src/mediawiki.language/languages/hsb.js | 2 +- resources/src/mediawiki.language/languages/hy.js | 2 +- resources/src/mediawiki.language/languages/os.js | 18 +- .../mediawiki.language/mediawiki.language.init.js | 2 + .../src/mediawiki.language/mediawiki.language.js | 41 +- .../mediawiki.language.numbers.js | 6 +- .../src/mediawiki.language/specialcharacters.json | 1 + resources/src/mediawiki.legacy/ajax.js | 304 ++++---- resources/src/mediawiki.legacy/commonPrint.css | 42 +- .../mediawiki.legacy/images/magnify-clip-ltr.png | Bin 0 -> 336 bytes .../mediawiki.legacy/images/magnify-clip-ltr.svg | 7 + .../mediawiki.legacy/images/magnify-clip-rtl.png | Bin 0 -> 360 bytes .../mediawiki.legacy/images/magnify-clip-rtl.svg | 7 + resources/src/mediawiki.legacy/oldshared.css | 6 +- resources/src/mediawiki.legacy/protect.js | 5 +- resources/src/mediawiki.legacy/shared.css | 28 +- resources/src/mediawiki.legacy/wikibits.js | 388 +++++----- resources/src/mediawiki.less/mediawiki.mixins.less | 73 +- .../src/mediawiki.less/mediawiki.ui/mixins.less | 29 +- .../src/mediawiki.libs/CLDRPluralRuleParser.js | 246 ++++-- .../mediawiki.messagePoster.MessagePoster.js | 38 + ...ediawiki.messagePoster.WikitextMessagePoster.js | 53 ++ .../mediawiki.messagePoster.factory.js | 109 +++ .../src/mediawiki.page/mediawiki.page.gallery.js | 425 +++++----- .../mediawiki.page.image.pagination.js | 77 +- .../src/mediawiki.page/mediawiki.page.ready.js | 2 +- .../src/mediawiki.page/mediawiki.page.startup.js | 2 +- resources/src/mediawiki.skinning/content.css | 29 +- resources/src/mediawiki.skinning/elements.css | 13 +- .../mediawiki.skinning/images/magnify-clip-ltr.png | Bin 204 -> 336 bytes .../mediawiki.skinning/images/magnify-clip-ltr.svg | 7 + .../mediawiki.skinning/images/magnify-clip-rtl.png | Bin 149 -> 360 bytes .../mediawiki.skinning/images/magnify-clip-rtl.svg | 7 + resources/src/mediawiki.skinning/interface.css | 8 + .../mediawiki.special/mediawiki.special.block.js | 2 +- .../mediawiki.special.changeslist.css | 8 + .../mediawiki.special.changeslist.legend.css | 6 +- .../src/mediawiki.special/mediawiki.special.css | 5 + .../mediawiki.special.edittags.css | 15 + .../mediawiki.special.edittags.js | 24 + .../mediawiki.special/mediawiki.special.import.js | 6 +- .../mediawiki.special.pageLanguage.js | 2 +- .../mediawiki.special.preferences.js | 66 +- .../mediawiki.special/mediawiki.special.search.css | 17 +- .../mediawiki.special/mediawiki.special.upload.js | 93 ++- .../mediawiki.special.userlogin.common.css | 2 +- .../mediawiki.special.userlogin.common.js | 2 +- .../mediawiki.special.userlogin.login.css | 13 - .../mediawiki.special.userlogin.signup.js | 2 +- .../mediawiki.special.version.css | 4 + .../src/mediawiki.special/templates/thumbnail.html | 9 + .../mediawiki.toolbar/images/ar/button_bold.png | Bin 0 -> 533 bytes .../images/ar/button_headline.png | Bin 0 -> 484 bytes .../mediawiki.toolbar/images/ar/button_italic.png | Bin 0 -> 532 bytes .../mediawiki.toolbar/images/ar/button_link.png | Bin 0 -> 557 bytes .../mediawiki.toolbar/images/ar/button_nowiki.png | Bin 0 -> 874 bytes .../images/be-tarask/button_bold.png | Bin 0 -> 550 bytes .../images/be-tarask/button_italic.png | Bin 0 -> 539 bytes .../images/be-tarask/button_link.png | Bin 0 -> 419 bytes .../mediawiki.toolbar/images/de/button_bold.png | Bin 0 -> 255 bytes .../mediawiki.toolbar/images/de/button_italic.png | Bin 0 -> 260 bytes .../mediawiki.toolbar/images/en/button_bold.png | Bin 0 -> 250 bytes .../mediawiki.toolbar/images/en/button_extlink.png | Bin 0 -> 435 bytes .../images/en/button_headline.png | Bin 0 -> 440 bytes .../src/mediawiki.toolbar/images/en/button_hr.png | Bin 0 -> 200 bytes .../mediawiki.toolbar/images/en/button_image.png | Bin 0 -> 483 bytes .../mediawiki.toolbar/images/en/button_italic.png | Bin 0 -> 250 bytes .../mediawiki.toolbar/images/en/button_link.png | Bin 0 -> 280 bytes .../mediawiki.toolbar/images/en/button_media.png | Bin 0 -> 728 bytes .../mediawiki.toolbar/images/en/button_nowiki.png | Bin 0 -> 322 bytes .../src/mediawiki.toolbar/images/en/button_sig.png | Bin 0 -> 920 bytes .../mediawiki.toolbar/images/fa/button_bold.png | Bin 0 -> 459 bytes .../images/fa/button_headline.png | Bin 0 -> 392 bytes .../mediawiki.toolbar/images/fa/button_italic.png | Bin 0 -> 512 bytes .../mediawiki.toolbar/images/fa/button_link.png | Bin 0 -> 485 bytes .../mediawiki.toolbar/images/fa/button_nowiki.png | Bin 0 -> 874 bytes resources/src/mediawiki.toolbar/images/ksh/LICENSE | 7 + .../mediawiki.toolbar/images/ksh/button_italic.png | Bin 0 -> 368 bytes resources/src/mediawiki.toolbar/images/ru/LICENSE | 17 + .../mediawiki.toolbar/images/ru/button_bold.png | Bin 0 -> 254 bytes .../mediawiki.toolbar/images/ru/button_italic.png | Bin 0 -> 423 bytes .../mediawiki.toolbar/images/ru/button_link.png | Bin 0 -> 278 bytes resources/src/mediawiki.toolbar/toolbar.js | 202 +++++ resources/src/mediawiki.toolbar/toolbar.less | 42 + resources/src/mediawiki.ui/components/anchors.less | 89 ++- resources/src/mediawiki.ui/components/buttons.less | 11 +- .../src/mediawiki.ui/components/checkbox.less | 120 +-- resources/src/mediawiki.ui/components/forms.less | 15 +- resources/src/mediawiki.ui/components/icons.less | 107 +++ .../src/mediawiki.ui/components/images/checked.svg | 2 +- .../components/images/checked_disabled.png | Bin 0 -> 333 bytes .../components/images/checked_disabled.svg | 1 + .../src/mediawiki.ui/components/images/ok.png | Bin 0 -> 442 bytes .../src/mediawiki.ui/components/images/ok.svg | 1 + .../components/images/radio_checked.png | Bin 0 -> 286 bytes .../components/images/radio_checked.svg | 1 + .../components/images/radio_disabled.png | Bin 0 -> 251 bytes .../components/images/radio_disabled.svg | 1 + resources/src/mediawiki.ui/components/inputs.less | 20 +- resources/src/mediawiki.ui/components/radio.less | 116 +++ resources/src/mediawiki.ui/components/text.less | 40 + resources/src/mediawiki/images/help.png | Bin 0 -> 460 bytes resources/src/mediawiki/images/help.svg | 1 + .../pager-arrow-disabled-fastforward-ltr.svg | 44 ++ .../pager-arrow-disabled-fastforward-rtl.svg | 44 ++ .../images/pager-arrow-disabled-forward-ltr.svg | 36 + .../images/pager-arrow-disabled-forward-rtl.svg | 36 + .../images/pager-arrow-fastforward-ltr.svg | 43 + .../images/pager-arrow-fastforward-rtl.svg | 69 ++ .../mediawiki/images/pager-arrow-forward-ltr.svg | 36 + .../mediawiki/images/pager-arrow-forward-rtl.svg | 36 + resources/src/mediawiki/mediawiki.Title.js | 11 +- resources/src/mediawiki/mediawiki.Uri.js | 41 +- resources/src/mediawiki/mediawiki.apihelp.css | 86 ++ resources/src/mediawiki/mediawiki.apipretty.css | 11 + .../src/mediawiki/mediawiki.confirmCloseWindow.js | 68 ++ resources/src/mediawiki/mediawiki.content.json.css | 18 +- resources/src/mediawiki/mediawiki.cookie.js | 25 +- resources/src/mediawiki/mediawiki.debug.js | 9 +- .../src/mediawiki/mediawiki.debug.profile.css | 45 -- resources/src/mediawiki/mediawiki.debug.profile.js | 556 ------------- resources/src/mediawiki/mediawiki.errorLogger.js | 49 ++ resources/src/mediawiki/mediawiki.feedback.css | 13 + resources/src/mediawiki/mediawiki.feedback.js | 699 +++++++++++------ resources/src/mediawiki/mediawiki.filewarning.js | 68 ++ resources/src/mediawiki/mediawiki.filewarning.less | 29 + resources/src/mediawiki/mediawiki.helplink.less | 11 + resources/src/mediawiki/mediawiki.hlist.js | 30 +- resources/src/mediawiki/mediawiki.htmlform.js | 38 +- resources/src/mediawiki/mediawiki.inspect.js | 24 +- resources/src/mediawiki/mediawiki.jqueryMsg.js | 69 +- resources/src/mediawiki/mediawiki.js | 862 ++++++++++++--------- resources/src/mediawiki/mediawiki.notification.js | 2 +- .../src/mediawiki/mediawiki.pager.tablePager.less | 32 +- resources/src/mediawiki/mediawiki.searchSuggest.js | 8 +- .../src/mediawiki/mediawiki.sectionAnchor.css | 3 + resources/src/mediawiki/mediawiki.startUp.js | 11 + resources/src/mediawiki/mediawiki.template.js | 123 +++ .../src/mediawiki/mediawiki.template.mustache.js | 14 + resources/src/mediawiki/mediawiki.user.js | 101 ++- resources/src/mediawiki/mediawiki.userSuggest.js | 41 + resources/src/mediawiki/mediawiki.util.js | 73 +- 227 files changed, 5104 insertions(+), 3488 deletions(-) delete mode 100644 resources/src/jquery.json-deprecate.js delete mode 100644 resources/src/jquery/jquery.client.js create mode 100644 resources/src/mediawiki.action/images/nextredirect-ltr.svg create mode 100644 resources/src/mediawiki.action/images/nextredirect-rtl.svg create mode 100644 resources/src/mediawiki.action/images/redirect-ltr.svg create mode 100644 resources/src/mediawiki.action/images/redirect-rtl.svg create mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.stash.js delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_headline.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_link.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_nowiki.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_link.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_extlink.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_headline.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_hr.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_image.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_link.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_media.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_nowiki.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_sig.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_headline.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_link.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_nowiki.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/LICENSE delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/LICENSE delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_bold.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_italic.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_link.png delete mode 100644 resources/src/mediawiki.action/mediawiki.action.edit.toolbar/mediawiki.action.edit.toolbar.less create mode 100644 resources/src/mediawiki.action/mediawiki.action.history.diff.print.css create mode 100644 resources/src/mediawiki.action/mediawiki.action.view.categoryPage.less create mode 100644 resources/src/mediawiki.action/templates/postEdit.html create mode 100644 resources/src/mediawiki.api/mediawiki.api.options.js create mode 100644 resources/src/mediawiki.language/specialcharacters.json create mode 100644 resources/src/mediawiki.legacy/images/magnify-clip-ltr.png create mode 100644 resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg create mode 100644 resources/src/mediawiki.legacy/images/magnify-clip-rtl.png create mode 100644 resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg create mode 100644 resources/src/mediawiki.messagePoster/mediawiki.messagePoster.MessagePoster.js create mode 100644 resources/src/mediawiki.messagePoster/mediawiki.messagePoster.WikitextMessagePoster.js create mode 100644 resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js create mode 100644 resources/src/mediawiki.skinning/images/magnify-clip-ltr.svg create mode 100644 resources/src/mediawiki.skinning/images/magnify-clip-rtl.svg create mode 100644 resources/src/mediawiki.special/mediawiki.special.edittags.css create mode 100644 resources/src/mediawiki.special/mediawiki.special.edittags.js create mode 100644 resources/src/mediawiki.special/templates/thumbnail.html create mode 100644 resources/src/mediawiki.toolbar/images/ar/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/ar/button_headline.png create mode 100644 resources/src/mediawiki.toolbar/images/ar/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/ar/button_link.png create mode 100644 resources/src/mediawiki.toolbar/images/ar/button_nowiki.png create mode 100644 resources/src/mediawiki.toolbar/images/be-tarask/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/be-tarask/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/be-tarask/button_link.png create mode 100644 resources/src/mediawiki.toolbar/images/de/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/de/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_extlink.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_headline.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_hr.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_image.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_link.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_media.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_nowiki.png create mode 100644 resources/src/mediawiki.toolbar/images/en/button_sig.png create mode 100644 resources/src/mediawiki.toolbar/images/fa/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/fa/button_headline.png create mode 100644 resources/src/mediawiki.toolbar/images/fa/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/fa/button_link.png create mode 100644 resources/src/mediawiki.toolbar/images/fa/button_nowiki.png create mode 100644 resources/src/mediawiki.toolbar/images/ksh/LICENSE create mode 100644 resources/src/mediawiki.toolbar/images/ksh/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/ru/LICENSE create mode 100644 resources/src/mediawiki.toolbar/images/ru/button_bold.png create mode 100644 resources/src/mediawiki.toolbar/images/ru/button_italic.png create mode 100644 resources/src/mediawiki.toolbar/images/ru/button_link.png create mode 100644 resources/src/mediawiki.toolbar/toolbar.js create mode 100644 resources/src/mediawiki.toolbar/toolbar.less create mode 100644 resources/src/mediawiki.ui/components/icons.less create mode 100644 resources/src/mediawiki.ui/components/images/checked_disabled.png create mode 100644 resources/src/mediawiki.ui/components/images/checked_disabled.svg create mode 100644 resources/src/mediawiki.ui/components/images/ok.png create mode 100644 resources/src/mediawiki.ui/components/images/ok.svg create mode 100644 resources/src/mediawiki.ui/components/images/radio_checked.png create mode 100644 resources/src/mediawiki.ui/components/images/radio_checked.svg create mode 100644 resources/src/mediawiki.ui/components/images/radio_disabled.png create mode 100644 resources/src/mediawiki.ui/components/images/radio_disabled.svg create mode 100644 resources/src/mediawiki.ui/components/radio.less create mode 100644 resources/src/mediawiki.ui/components/text.less create mode 100644 resources/src/mediawiki/images/help.png create mode 100644 resources/src/mediawiki/images/help.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-disabled-fastforward-ltr.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-disabled-fastforward-rtl.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-disabled-forward-ltr.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-disabled-forward-rtl.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-fastforward-ltr.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-fastforward-rtl.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-forward-ltr.svg create mode 100644 resources/src/mediawiki/images/pager-arrow-forward-rtl.svg create mode 100644 resources/src/mediawiki/mediawiki.apihelp.css create mode 100644 resources/src/mediawiki/mediawiki.apipretty.css create mode 100644 resources/src/mediawiki/mediawiki.confirmCloseWindow.js delete mode 100644 resources/src/mediawiki/mediawiki.debug.profile.css delete mode 100644 resources/src/mediawiki/mediawiki.debug.profile.js create mode 100644 resources/src/mediawiki/mediawiki.errorLogger.js create mode 100644 resources/src/mediawiki/mediawiki.filewarning.js create mode 100644 resources/src/mediawiki/mediawiki.filewarning.less create mode 100644 resources/src/mediawiki/mediawiki.helplink.less create mode 100644 resources/src/mediawiki/mediawiki.sectionAnchor.css create mode 100644 resources/src/mediawiki/mediawiki.startUp.js create mode 100644 resources/src/mediawiki/mediawiki.template.js create mode 100644 resources/src/mediawiki/mediawiki.template.mustache.js create mode 100644 resources/src/mediawiki/mediawiki.userSuggest.js (limited to 'resources/src') diff --git a/resources/src/jquery.json-deprecate.js b/resources/src/jquery.json-deprecate.js deleted file mode 100644 index f38decd9..00000000 --- a/resources/src/jquery.json-deprecate.js +++ /dev/null @@ -1,8 +0,0 @@ -( function ( mw, $ ) { - // @deprecated since 1.24. The 'jquery.json' module will be removed in MW 1.25. Use the 'json' module. - - mw.log.deprecate( $, 'toJSON', $.toJSON, 'Use JSON.stringify instead (module "json" for polyfill).' ); - mw.log.deprecate( $, 'evalJSON', $.evalJSON, 'Use JSON.parse instead (module "json" for polyfill).' ); - mw.log.deprecate( $, 'secureEvalJSON', $.secureEvalJSON, 'Use JSON.parse instead (module "json" for polyfill).' ); - mw.log.deprecate( $, 'quoteString', $.quoteString, 'Use JSON.stringify instead (module "json" for polyfill).' ); -}( mediaWiki, jQuery ) ); diff --git a/resources/src/jquery.tipsy/jquery.tipsy.js b/resources/src/jquery.tipsy/jquery.tipsy.js index 58a99a59..2a37fa86 100644 --- a/resources/src/jquery.tipsy/jquery.tipsy.js +++ b/resources/src/jquery.tipsy/jquery.tipsy.js @@ -6,7 +6,7 @@ // * This installation of tipsy includes several local modifications to both Javascript and CSS. // Please be careful when upgrading. -(function($) { +( function ( mw, $ ) { function maybeCall(thing, ctx) { return (typeof thing == 'function') ? (thing.call(ctx)) : thing; @@ -182,11 +182,22 @@ if (!options.live) this.each(function() { get(this); }); - if (options.trigger != 'manual') { - var binder = options.live ? 'live' : 'bind', - eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', + if ( options.trigger != 'manual' ) { + var eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; - this[binder](eventIn, enter)[binder](eventOut, leave); + if ( options.live ) { + mw.track( 'mw.deprecate', 'tipsy-live' ); + mw.log.warn( 'Use of the "live" option of jquery.tipsy is deprecated.' ); + // XXX: The official status of 'context' is deprecated, and the official status of + // 'selector' is removed, so this really needs to go. + $( this.context ) + .on( eventIn, this.selector, enter ) + .on( eventOut, this.selector, leave ); + } else { + this + .on( eventIn, enter ) + .on( eventOut, leave ); + } } return this; @@ -256,4 +267,4 @@ } }; -})(jQuery); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/jquery/jquery.accessKeyLabel.js b/resources/src/jquery/jquery.accessKeyLabel.js index 7b49cb2d..867c25e7 100644 --- a/resources/src/jquery/jquery.accessKeyLabel.js +++ b/resources/src/jquery/jquery.accessKeyLabel.js @@ -8,7 +8,7 @@ // Cached access key prefix for used browser var cachedAccessKeyPrefix, - // Wether to use 'test-' instead of correct prefix (used for testing) + // Whether to use 'test-' instead of correct prefix (used for testing) useTestPrefix = false, // tag names which can have a label tag diff --git a/resources/src/jquery/jquery.arrowSteps.js b/resources/src/jquery/jquery.arrowSteps.js index f8641e10..629ce32c 100644 --- a/resources/src/jquery/jquery.arrowSteps.js +++ b/resources/src/jquery/jquery.arrowSteps.js @@ -80,7 +80,7 @@ $.each( $steps, function ( i, step ) { var $step = $( step ); if ( $step.is( selector ) ) { - if ($previous) { + if ( $previous ) { $previous.addClass( 'tail' ); } $step.addClass( 'head' ); diff --git a/resources/src/jquery/jquery.badge.css b/resources/src/jquery/jquery.badge.css index fa7ea702..34cdf76c 100644 --- a/resources/src/jquery/jquery.badge.css +++ b/resources/src/jquery/jquery.badge.css @@ -13,7 +13,7 @@ font-weight: bold; color: white; vertical-align: baseline; - text-shadow: 0 1px rgba(0, 0, 0, 0.4); + text-shadow: 0 1px rgba(0, 0, 0, 0.4); } .mw-badge-inline { diff --git a/resources/src/jquery/jquery.badge.js b/resources/src/jquery/jquery.badge.js index 023b6e28..77738661 100644 --- a/resources/src/jquery/jquery.badge.js +++ b/resources/src/jquery/jquery.badge.js @@ -45,7 +45,8 @@ $.fn.badge = function ( text, inline, displayZero ) { var $badge = this.find( '.mw-badge' ), badgeStyleClass = 'mw-badge-' + ( inline ? 'inline' : 'overlay' ), - isImportant = true, displayBadge = true; + isImportant = true, + displayBadge = true; // If we're displaying zero, ensure style to be non-important if ( mw.language.convertNumber( text, true ) === 0 ) { diff --git a/resources/src/jquery/jquery.client.js b/resources/src/jquery/jquery.client.js deleted file mode 100644 index 662a6887..00000000 --- a/resources/src/jquery/jquery.client.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * User-agent detection - * - * @class jQuery.client - * @singleton - */ -( function ( $ ) { - - /** - * @private - * @property {Object} profileCache Keyed by userAgent string, - * value is the parsed $.client.profile object for that user agent. - */ - var profileCache = {}; - - $.client = { - - /** - * Get an object containing information about the client. - * - * @param {Object} [nav] An object with a 'userAgent' and 'platform' property. - * Defaults to the global `navigator` object. - * @return {Object} The resulting client object will be in the following format: - * - * { - * 'name': 'firefox', - * 'layout': 'gecko', - * 'layoutVersion': 20101026, - * 'platform': 'linux' - * 'version': '3.5.1', - * 'versionBase': '3', - * 'versionNumber': 3.5, - * } - */ - profile: function ( nav ) { - /*jshint boss: true */ - - if ( nav === undefined ) { - nav = window.navigator; - } - - // Use the cached version if possible - if ( profileCache[ nav.userAgent + '|' + nav.platform ] !== undefined ) { - return profileCache[ nav.userAgent + '|' + nav.platform ]; - } - - var - versionNumber, - key = nav.userAgent + '|' + nav.platform, - - // Configuration - - // Name of browsers or layout engines we don't recognize - uk = 'unknown', - // Generic version digit - x = 'x', - // Strings found in user agent strings that need to be conformed - 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)/, ''], - // 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 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 detection - versionPrefixes = [ - 'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', - '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', 'android' - ], - // Tanslations for conforming browser names - nameTranslations = [], - // Names of known layout engines - 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', 'trident'], - // Names of known operating systems - platforms = ['win', 'wow64', 'mac', 'linux', 'sunos', 'solaris', 'iphone'], - // Translations for conforming operating system names - platformTranslations = [ ['sunos', 'solaris'], ['wow64', 'win'] ], - - /** - * Performs multiple replacements on a string - * @ignore - */ - translate = function ( source, translations ) { - var i; - for ( i = 0; i < translations.length; i++ ) { - source = source.replace( translations[i][0], translations[i][1] ); - } - return source; - }, - - // Pre-processing - - ua = nav.userAgent, - match, - name = uk, - layout = uk, - layoutversion = uk, - platform = uk, - version = x; - - if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) { - // Takes a userAgent string and translates given text into something we can more easily work with - ua = translate( ua, userAgentTranslations ); - } - // Everything will be in lowercase from now on - ua = ua.toLowerCase(); - - // Extraction - - if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) { - name = translate( match[1], nameTranslations ); - } - if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) { - layout = translate( match[1], layoutTranslations ); - } - if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\\/(\\d+)').exec( ua ) ) { - layoutversion = parseInt( match[2], 10 ); - } - if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) { - platform = translate( match[1], platformTranslations ); - } - if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) { - version = match[3]; - } - - // Edge Cases -- did I mention about how user agent string lie? - - // Decode Safari's crazy 400+ version numbers - 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( /\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]; - } - } - // And Amazon Silk's lies about being Android on mobile or Safari on desktop - if ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) { - if ( match[1] ) { - name = 'silk'; - version = match[1]; - } - } - - versionNumber = parseFloat( version, 10 ) || 0.0; - - // Caching - - return profileCache[ key ] = { - name: name, - layout: layout, - layoutVersion: layoutversion, - platform: platform, - version: version, - versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ), - versionNumber: versionNumber - }; - }, - - /** - * Checks the current browser against a support map object. - * - * Version numbers passed as numeric values will be compared like numbers (1.2 > 1.11). - * Version numbers passed as string values will be compared using a simple component-wise - * algorithm, similar to PHP's version_compare ('1.2' < '1.11'). - * - * 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': { - * 'android': null, - * 'iphone': false - * }, - * 'rtl': { - * 'android': false, - * // rules are not inherited from ltr - * 'iphone': false - * } - * } - * - * @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} The current browser is in the support map - */ - test: function ( map, profile, exactMatchOnly ) { - /*jshint evil: true */ - - var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare; - profile = $.isPlainObject( profile ) ? profile : $.client.profile(); - 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 !== 'object' || map[profile.name] === undefined ) { - // Not found, return true if exactMatchOnly not set, false otherwise - return !exactMatchOnly; - } - 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]; - if ( typeof val === 'string' ) { - // Perform a component-wise comparison of versions, similar to PHP's version_compare - // but simpler. '1.11' is larger than '1.2'. - pieceVersion = profile.version.toString().split( '.' ); - pieceVal = val.split( '.' ); - // Extend with zeroes to equal length - while ( pieceVersion.length < pieceVal.length ) { - pieceVersion.push( '0' ); - } - while ( pieceVal.length < pieceVersion.length ) { - pieceVal.push( '0' ); - } - // Compare components - compare = 0; - for ( j = 0; j < pieceVersion.length; j++ ) { - if ( Number( pieceVersion[j] ) < Number( pieceVal[j] ) ) { - compare = -1; - break; - } else if ( Number( pieceVersion[j] ) > Number( pieceVal[j] ) ) { - compare = 1; - break; - } - } - // compare will be -1, 0 or 1, depending on comparison result - if ( !( eval( '' + compare + op + '0' ) ) ) { - return false; - } - } else if ( typeof val === 'number' ) { - if ( !( eval( 'profile.versionNumber' + op + val ) ) ) { - return false; - } - } - } - - return true; - } - }; -}( jQuery ) ); diff --git a/resources/src/jquery/jquery.confirmable.js b/resources/src/jquery/jquery.confirmable.js index 339e65a4..1ecce6ca 100644 --- a/resources/src/jquery/jquery.confirmable.js +++ b/resources/src/jquery/jquery.confirmable.js @@ -40,6 +40,8 @@ * @param {string} [options.i18n.confirm] Text to use for the confirmation question. * @param {string} [options.i18n.yes] Text to use for the 'Yes' button. * @param {string} [options.i18n.no] Text to use for the 'No' button. + * @param {string} [options.i18n.yesTitle] Title text to use for the 'Yes' button. + * @param {string} [options.i18n.noTitle] Title text to use for the 'No' button. * * @chainable */ @@ -108,6 +110,9 @@ if ( options.handler ) { $buttonYes.on( options.events, options.handler ); } + if ( options.i18n.yesTitle ) { + $buttonYes.attr( 'title', options.i18n.yesTitle ); + } $buttonYes = options.buttonCallback( $buttonYes, 'yes' ); // Clone it without any events and prevent default action to represent the 'No' button. @@ -120,6 +125,11 @@ $interface.css( 'width', 0 ); e.preventDefault(); } ); + if ( options.i18n.noTitle ) { + $buttonNo.attr( 'title', options.i18n.noTitle ); + } else { + $buttonNo.removeAttr( 'title' ); + } $buttonNo = options.buttonCallback( $buttonNo, 'no' ); // Prevent memory leaks @@ -164,7 +174,9 @@ space: ' ', confirm: 'Are you sure?', yes: 'Yes', - no: 'No' + no: 'No', + yesTitle: undefined, + noTitle: undefined } }; }( jQuery ) ); diff --git a/resources/src/jquery/jquery.confirmable.mediawiki.js b/resources/src/jquery/jquery.confirmable.mediawiki.js index d4a106e3..daf23a99 100644 --- a/resources/src/jquery/jquery.confirmable.mediawiki.js +++ b/resources/src/jquery/jquery.confirmable.mediawiki.js @@ -9,6 +9,8 @@ space: mw.message( 'word-separator' ).text(), confirm: mw.message( 'confirmable-confirm', mw.user ).text(), yes: mw.message( 'confirmable-yes' ).text(), - no: mw.message( 'confirmable-no' ).text() + no: mw.message( 'confirmable-no' ).text(), + yesTitle: undefined, + noTitle: undefined }; }( mediaWiki, jQuery ) ); diff --git a/resources/src/jquery/jquery.expandableField.js b/resources/src/jquery/jquery.expandableField.js index 732cc6ec..48341bc5 100644 --- a/resources/src/jquery/jquery.expandableField.js +++ b/resources/src/jquery/jquery.expandableField.js @@ -134,7 +134,7 @@ // Store the context for next time $( this ).data( 'expandableField-context', context ); } ); - return returnValue !== undefined ? returnValue : $(this); + return returnValue !== undefined ? returnValue : $( this ); }; }( jQuery ) ); diff --git a/resources/src/jquery/jquery.footHovzer.js b/resources/src/jquery/jquery.footHovzer.js index de745c33..e601ddb1 100644 --- a/resources/src/jquery/jquery.footHovzer.js +++ b/resources/src/jquery/jquery.footHovzer.js @@ -2,7 +2,7 @@ * @class jQuery.plugin.footHovzer */ ( function ( $ ) { - var $hovzer, footHovzer, prevHeight, newHeight; + var $hovzer, footHovzer, $spacer; function getHovzer() { if ( $hovzer === undefined ) { @@ -46,15 +46,15 @@ var $body; $body = $( 'body' ); - if ( prevHeight === undefined ) { - prevHeight = getHovzer().outerHeight( /* includeMargin = */ true ); - $body.css( 'paddingBottom', '+=' + prevHeight + 'px' ); - } else { - newHeight = getHovzer().outerHeight( true ); - $body.css( 'paddingBottom', ( parseFloat( $body.css( 'paddingBottom' ) ) - prevHeight ) + newHeight ); - prevHeight = newHeight; + if ( $spacer === undefined ) { + $spacer = $( '
' ).attr( 'id', 'jquery-foot-hovzer-spacer' ); + $spacer.appendTo( $body ); } + // Ensure CSS is applied by browser before using .outerHeight() + setTimeout( function () { + $spacer.css( 'height', getHovzer().outerHeight( /* includeMargin = */ true ) ); + }, 0 ); } }; diff --git a/resources/src/jquery/jquery.getAttrs.js b/resources/src/jquery/jquery.getAttrs.js index c44831c4..64827fb7 100644 --- a/resources/src/jquery/jquery.getAttrs.js +++ b/resources/src/jquery/jquery.getAttrs.js @@ -2,38 +2,37 @@ * @class jQuery.plugin.getAttrs */ +function serializeControls( controls ) { + var i, + data = {}, + len = controls.length; + + for ( i = 0; i < len; i++ ) { + data[ controls[i].name ] = controls[i].value; + } + + return data; +} + /** * Get the attributes of an element directy as a plain object. * - * If there are more elements in the collection, like most jQuery get/read methods, - * this method will use the first element in the collection. - * - * In IE6, the `attributes` map of a node includes *all* allowed attributes - * for an element (including those not set). Those will have values like - * `undefined`, `null`, `0`, `false`, `""` or `"inherit"`. + * If there is more than one element in the collection, similar to most other jQuery getter methods, + * this will use the first element in the collection. * - * However there may be attributes genuinely set to one of those values, and there - * is no way to distinguish between attributes set to that and those not set and - * it being the default. If you need them, set `all` to `true`. They are filtered out - * by default. - * - * @param {boolean} [all=false] * @return {Object} */ -jQuery.fn.getAttrs = function ( all ) { - var map = this[0].attributes, - attrs = {}, - len = map.length, - i, v; - - for ( i = 0; i < len; i++ ) { - v = map[i].nodeValue; - if ( all || ( v && v !== 'inherit' ) ) { - attrs[ map[i].nodeName ] = v; - } - } +jQuery.fn.getAttrs = function () { + return serializeControls( this[0].attributes ); +}; - return attrs; +/** + * Get form data as a plain object mapping form control names to their values. + * + * @return {Object} + */ +jQuery.fn.serializeObject = function () { + return serializeControls( this.serializeArray() ); }; /** diff --git a/resources/src/jquery/jquery.hidpi.js b/resources/src/jquery/jquery.hidpi.js index 4ecfeb88..8fca0567 100644 --- a/resources/src/jquery/jquery.hidpi.js +++ b/resources/src/jquery/jquery.hidpi.js @@ -73,11 +73,11 @@ $.fn.hidpi = function () { match; if ( typeof srcset === 'string' && srcset !== '' ) { match = $.matchSrcSet( devicePixelRatio, srcset ); - if (match !== null ) { + if ( match !== null ) { $img.attr( 'src', match ); } } - }); + } ); } return $target; diff --git a/resources/src/jquery/jquery.makeCollapsible.css b/resources/src/jquery/jquery.makeCollapsible.css index 0f471509..2e5efbac 100644 --- a/resources/src/jquery/jquery.makeCollapsible.css +++ b/resources/src/jquery/jquery.makeCollapsible.css @@ -6,18 +6,38 @@ -ms-user-select: none; user-select: none; } +/* Align the toggle based on the direction of the content language */ +/* @noflip */ +.mw-content-ltr .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr .mw-collapsible-toggle { + float: right; +} +/* @noflip */ +.mw-content-rtl .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl .mw-collapsible-toggle { + float: left; +} + .mw-customtoggle, .mw-collapsible-toggle { cursor: pointer; } /* collapse links in captions should be inline */ -caption .mw-collapsible-toggle { +caption .mw-collapsible-toggle, +.mw-content-ltr caption .mw-collapsible-toggle, +.mw-content-rtl caption .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr caption .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl caption .mw-collapsible-toggle { float: none; } /* list-items go as wide as their parent element, don't float them inside list items */ -li .mw-collapsible-toggle { +li .mw-collapsible-toggle, +.mw-content-ltr li .mw-collapsible-toggle, +.mw-content-rtl li .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr li .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl li .mw-collapsible-toggle { float: none; } diff --git a/resources/src/jquery/jquery.makeCollapsible.js b/resources/src/jquery/jquery.makeCollapsible.js index c4e25203..f7c42177 100644 --- a/resources/src/jquery/jquery.makeCollapsible.js +++ b/resources/src/jquery/jquery.makeCollapsible.js @@ -208,7 +208,7 @@ * Enable collapsible-functionality on all elements in the collection. * * - Will prevent binding twice to the same element. - * - Initial state is expanded by default, this can be overriden by adding class + * - Initial state is expanded by default, this can be overridden by adding class * "mw-collapsed" to the "mw-collapsible" element. * - Elements made collapsible have jQuery data "mw-made-collapsible" set to true. * - The inner content is wrapped in a "div.mw-collapsible-content" (except for tables and lists). @@ -330,7 +330,7 @@ .prop( 'tabIndex', 0 ); } } else { - // The toggle-link will be in one the the cells (td or th) of the first row + // The toggle-link will be in one of the cells (td or th) of the first row $firstItem = $collapsible.find( 'tr:first th, tr:first td' ); $toggle = $firstItem.find( '> .mw-collapsible-toggle' ); diff --git a/resources/src/jquery/jquery.mwExtension.js b/resources/src/jquery/jquery.mwExtension.js index dc7aaa45..e6e33ade 100644 --- a/resources/src/jquery/jquery.mwExtension.js +++ b/resources/src/jquery/jquery.mwExtension.js @@ -15,16 +15,16 @@ return str.charAt( 0 ).toUpperCase() + str.slice( 1 ); }, escapeRE: function ( str ) { - return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); + return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); }, isDomElement: function ( el ) { return !!el && !!el.nodeType; }, isEmpty: function ( v ) { var key; - if ( v === '' || v === 0 || v === '0' || v === null - || v === false || v === undefined ) - { + if ( + v === '' || v === 0 || v === '0' || v === null || v === false || v === undefined + ) { return true; } // the for-loop could potentially contain prototypes diff --git a/resources/src/jquery/jquery.placeholder.js b/resources/src/jquery/jquery.placeholder.js index d4580190..d50422e2 100644 --- a/resources/src/jquery/jquery.placeholder.js +++ b/resources/src/jquery/jquery.placeholder.js @@ -13,7 +13,7 @@ * @version 2.1.0 * @license MIT */ -(function ($) { +( function ($) { var isInputSupported = 'placeholder' in document.createElement('input'), isTextareaSupported = 'placeholder' in document.createElement('textarea'), @@ -49,7 +49,7 @@ $this .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') - .filter(function () { + .filter( function () { return !$(this).data('placeholder-enabled'); }) .bind({ @@ -114,12 +114,12 @@ propHooks.value = hooks; } - $(function () { + $( function () { // Look for forms $(document).delegate('form', 'submit.placeholder', function () { // Clear the placeholder values so they don't get submitted var $inputs = $('.placeholder', this).each(clearPlaceholder); - setTimeout(function () { + setTimeout( function () { $inputs.each(setPlaceholder); }, 10); }); @@ -127,7 +127,7 @@ // Clear placeholder values upon page reload $(window).bind('beforeunload.placeholder', function () { - $('.placeholder').each(function () { + $('.placeholder').each( function () { this.value = ''; }); }); diff --git a/resources/src/jquery/jquery.qunit.completenessTest.js b/resources/src/jquery/jquery.qunit.completenessTest.js index 8d38401e..556bf8c7 100644 --- a/resources/src/jquery/jquery.qunit.completenessTest.js +++ b/resources/src/jquery/jquery.qunit.completenessTest.js @@ -17,8 +17,8 @@ var util, hasOwn = Object.prototype.hasOwnProperty, - log = (window.console && window.console.log) - ? function () { return window.console.log.apply(window.console, arguments); } + log = ( window.console && window.console.log ) + ? function () { return window.console.log.apply( window.console, arguments ); } : function () {}; // Simplified version of a few jQuery methods, except that they don't @@ -91,7 +91,7 @@ // Restore warnings mw.log.warn = warn; warn = undefined; - }); + } ); QUnit.done( function () { that.populateMissingTests(); @@ -114,7 +114,7 @@ var elItem = document.createElement( 'li' ); elItem.textContent = key; elList.appendChild( elItem ); - }); + } ); elFoot = document.createElement( 'p' ); elFoot.innerHTML = '— CompletenessTest'; @@ -133,7 +133,7 @@ util.each( style, function ( key, value ) { elOutputWrapper.style[key] = value; - }); + } ); return elOutputWrapper; } @@ -171,7 +171,7 @@ if ( toolbar ) { toolbar.insertBefore( testResults, toolbar.firstChild ); } - }); + } ); return this; } @@ -248,7 +248,7 @@ var ct = this; util.each( ct.injectionTracker, function ( key ) { ct.hasTest( key ); - }); + } ); }, /** diff --git a/resources/src/jquery/jquery.suggestions.js b/resources/src/jquery/jquery.suggestions.js index 3369cde2..813c37ce 100644 --- a/resources/src/jquery/jquery.suggestions.js +++ b/resources/src/jquery/jquery.suggestions.js @@ -1,58 +1,102 @@ /** * This plugin provides a generic way to add suggestions to a text box. * - * Usage: - * * Set options: + * * $( '#textbox' ).suggestions( { option1: value1, option2: value2 } ); * $( '#textbox' ).suggestions( option, value ); + * * Get option: + * * value = $( '#textbox' ).suggestions( option ); + * * Initialize: + * * $( '#textbox' ).suggestions(); * - * Options: + * Uses jQuery.suggestions singleteon internally. * - * fetch(query): Callback that should fetch suggestions and set the suggestions property. - * Executed in the context of the textbox - * Type: Function - * cancel: Callback function to call when any pending asynchronous suggestions fetches - * should be canceled. Executed in the context of the textbox - * Type: Function - * special: Set of callbacks for rendering and selecting - * Type: Object of Functions 'render' and 'select' - * result: Set of callbacks for rendering and selecting - * Type: Object of Functions 'render' and 'select' - * $region: jQuery selection of element to place the suggestions below and match width of - * Type: jQuery Object, Default: $( this ) - * suggestions: Suggestions to display - * Type: Array of strings - * maxRows: Maximum number of suggestions to display at one time - * Type: Number, Range: 1 - 100, Default: 7 - * delay: Number of ms to wait for the user to stop typing - * Type: Number, Range: 0 - 1200, Default: 120 - * cache: Whether to cache results from a fetch - * Type: Boolean, Default: false - * cacheMaxAge: Number of ms to cache results from a fetch - * Type: Number, Range: 1 - Infinity, Default: 60000 (1 minute) - * submitOnClick: Whether to submit the form containing the textbox when a suggestion is clicked - * Type: Boolean, Default: false - * maxExpandFactor: Maximum suggestions box width relative to the textbox width. If set - * to e.g. 2, the suggestions box will never be grown beyond 2 times the width of the textbox. - * Type: Number, Range: 1 - infinity, Default: 3 - * expandFrom: Which direction to offset the suggestion box from. - * Values 'start' and 'end' translate to left and right respectively depending on the - * directionality of the current document, according to $( 'html' ).css( 'direction' ). - * Type: String, default: 'auto', options: 'left', 'right', 'start', 'end', 'auto'. - * positionFromLeft: Sets expandFrom=left, for backwards compatibility - * Type: Boolean, Default: true - * highlightInput: Whether to hightlight matched portions of the input or not - * Type: Boolean, Default: false + * @class jQuery.plugin.suggestions + */ +/** + * @method suggestions + * @return {jQuery} + * @chainable + * + * @param {Object} options + * + * @param {Function} [options.fetch] Callback that should fetch suggestions and set the suggestions + * property. Called in context of the text box. + * @param {string} options.fetch.query + * @param {Function} options.fetch.response Callback to receive the suggestions with + * @param {Array} options.fetch.response.suggestions + * @param {number} options.fetch.maxRows + * + * @param {Function} [options.cancel] Callback function to call when any pending asynchronous + * suggestions fetches. Called in context of the text box. + * + * @param {Object} [options.special] Set of callbacks for rendering and selecting. + * + * @param {Function} options.special.render Called in context of the suggestions-special element. + * @param {string} options.special.render.query + * @param {Object} options.special.render.context + * + * @param {Function} options.special.select Called in context of the suggestions-result-current element. + * @param {jQuery} options.special.select.$textbox + * + * @param {Object} [options.result] Set of callbacks for rendering and selecting + * + * @param {Function} options.result.render Called in context of the suggestions-result element. + * @param {string} options.result.render.suggestion + * @param {Object} options.result.render.context + * + * @param {Function} options.result.select Called in context of the suggestions-result-current element. + * @param {jQuery} options.result.select.$textbox + * + * @param {jQuery} [options.$region=this] The element to place the suggestions below and match width of. + * + * @param {string[]} [options.suggestions] Array of suggestions to display. + * + * @param {number} [options.maxRows=10] Maximum number of suggestions to display at one time. + * Must be between 1 and 100. + * + * @param {number} [options.delay=120] Number of milliseconds to wait for the user to stop typing. + * Must be between 0 and 1200. + * + * @param {boolean} [options.cache=false] Whether to cache results from a fetch. + * + * @param {number} [options.cacheMaxAge=60000] Number of milliseconds to cache results from a fetch. + * Must be higher than 1. Defaults to 1 minute. + * + * @param {boolean} [options.submitOnClick=false] Whether to submit the form containing the textbox + * when a suggestion is clicked. + * + * @param {number} [options.maxExpandFactor=3] Maximum suggestions box width relative to the textbox + * width. If set to e.g. 2, the suggestions box will never be grown beyond 2 times the width of + * the textbox. Must be higher than 1. + * + * @param {string} [options.expandFrom=auto] Which direction to offset the suggestion box from. + * Values 'start' and 'end' translate to left and right respectively depending on the directionality + * of the current document, according to `$( 'html' ).css( 'direction' )`. + * Valid values: "left", "right", "start", "end", and "auto". + * + * @param {boolean} [options.positionFromLeft] Sets `expandFrom=left`, for backwards + * compatibility. + * + * @param {boolean} [options.highlightInput=false] Whether to hightlight matched portions of the + * input or not. */ ( function ( $ ) { var hasOwn = Object.hasOwnProperty; +/** + * Used by jQuery.plugin.suggestions. + * + * @class jQuery.suggestions + * @singleton + * @private + */ $.suggestions = { /** * Cancel any delayed maybeFetch() call and callback the context so @@ -92,7 +136,7 @@ $.suggestions = { * call to this function still pending will be canceled. If the value in the * textbox is empty or hasn't changed since the last time suggestions were fetched, * this function does nothing. - * @param {Boolean} delayed Whether or not to delay this by the currently configured amount of time + * @param {boolean} delayed Whether or not to delay this by the currently configured amount of time */ update: function ( context, delayed ) { function maybeFetch() { @@ -125,6 +169,7 @@ $.suggestions = { context.data.$textbox, val, function ( suggestions ) { + suggestions = suggestions.slice( 0, context.config.maxRows ); context.data.$textbox.suggestions( 'suggestions', suggestions ); if ( context.config.cache ) { cache[ val ] = { @@ -132,7 +177,8 @@ $.suggestions = { timestamp: +new Date() }; } - } + }, + context.config.maxRows ); } } @@ -167,8 +213,8 @@ $.suggestions = { /** * Sets the value of a property, and updates the widget accordingly - * @param property String Name of property - * @param value Mixed Value to set property with + * @param {string} property Name of property + * @param {Mixed} value Value to set property with */ configure: function ( context, property, value ) { var newCSS, @@ -352,8 +398,8 @@ $.suggestions = { /** * Highlight a result in the results table - * @param result to highlight: jQuery object, or 'prev' or 'next' - * @param updateTextbox If true, put the suggestion in the textbox + * @param {jQuery|string} result `` to highlight, or 'prev' or 'next' + * @param {boolean} updateTextbox If true, put the suggestion in the textbox */ highlight: function ( context, result, updateTextbox ) { var selected = context.data.$container.find( '.suggestions-result-current' ); @@ -421,7 +467,7 @@ $.suggestions = { /** * Respond to keypress event - * @param key Integer Code of key pressed + * @param {number} key Code of key pressed */ keypress: function ( e, context, key ) { var selected, @@ -474,8 +520,6 @@ $.suggestions = { } } } else { - $.suggestions.highlight( context, selected, true ); - if ( typeof context.config.result.select === 'function' ) { // Allow the callback to decide whether to prevent default or not if ( context.config.result.select.call( selected, context.data.$textbox ) === true ) { @@ -494,6 +538,8 @@ $.suggestions = { } } }; + +// See file header for method documentation $.fn.suggestions = function () { // Multi-context fields @@ -503,7 +549,7 @@ $.fn.suggestions = function () { $( this ).each( function () { var context, key; - /* Construction / Loading */ + /* Construction and Loading */ context = $( this ).data( 'suggestions-context' ); if ( context === undefined || context === null ) { @@ -515,7 +561,7 @@ $.fn.suggestions = function () { result: {}, $region: $( this ), suggestions: [], - maxRows: 7, + maxRows: 10, delay: 120, cache: false, cacheMaxAge: 60000, @@ -681,4 +727,9 @@ $.fn.suggestions = function () { return returnValue !== undefined ? returnValue : $( this ); }; +/** + * @class jQuery + * @mixins jQuery.plugin.suggestions + */ + }( jQuery ) ); diff --git a/resources/src/jquery/jquery.tabIndex.js b/resources/src/jquery/jquery.tabIndex.js index 46cc8f2c..ed37aa1e 100644 --- a/resources/src/jquery/jquery.tabIndex.js +++ b/resources/src/jquery/jquery.tabIndex.js @@ -10,8 +10,8 @@ */ $.fn.firstTabIndex = function () { var minTabIndex = null; - $(this).find( '[tabindex]' ).each( function () { - var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 ); + $( this ).find( '[tabindex]' ).each( function () { + var tabIndex = parseInt( $( this ).prop( 'tabindex' ), 10 ); // In IE6/IE7 the above jQuery selector returns all elements, // becuase it has a default value for tabIndex in IE6/IE7 of 0 // (rather than null/undefined). Therefore check "> 0" as well. @@ -35,8 +35,8 @@ */ $.fn.lastTabIndex = function () { var maxTabIndex = null; - $(this).find( '[tabindex]' ).each( function () { - var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 ); + $( this ).find( '[tabindex]' ).each( function () { + var tabIndex = parseInt( $( this ).prop( 'tabindex' ), 10 ); if ( tabIndex > 0 && !isNaN( tabIndex ) ) { // Initial value if ( maxTabIndex === null ) { diff --git a/resources/src/jquery/jquery.tablesorter.js b/resources/src/jquery/jquery.tablesorter.js index ea2c5f92..ff5ff0a9 100644 --- a/resources/src/jquery/jquery.tablesorter.js +++ b/resources/src/jquery/jquery.tablesorter.js @@ -15,7 +15,7 @@ */ /** * - * @description Create a sortable table with multi-column sorting capabilitys + * @description Create a sortable table with multi-column sorting capabilities * * @example $( 'table' ).tablesorter(); * @desc Create a simple tablesorter interface. @@ -35,15 +35,9 @@ * to sortable tr elements in the thead on a descending sort. Default * value: "headerSortDown" * - * @option String sortInitialOrder ( optional ) A string of the inital sorting - * order can be asc or desc. Default value: "asc" - * * @option String sortMultisortKey ( optional ) A string of the multi-column sort * key. Default value: "shiftKey" * - * @option Boolean sortLocaleCompare ( optional ) Boolean flag indicating whatever - * to use String.localeCampare method or not. Set to false. - * * @option Boolean cancelSelection ( optional ) Boolean flag indicating if * tablesorter should cancel selection of the table headers text. * Default value: true @@ -53,9 +47,6 @@ * { : } * Default value: [] * - * @option Boolean debug ( optional ) Boolean flag indicating if tablesorter - * should display debuging information usefull for development. - * * @event sortEnd.tablesorter: Triggered as soon as any sorting has been applied. * * @type jQuery @@ -192,7 +183,8 @@ var i, j, $row, cols, totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0, totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0, - parsers = table.config.parsers, + config = $( table ).data( 'tablesorter' ).config, + parsers = config.parsers, cache = { row: [], normalized: [] @@ -206,7 +198,7 @@ // if this is a child row, add it to the last row's children and // continue to the next row - if ( $row.hasClass( table.config.cssChildRow ) ) { + if ( $row.hasClass( config.cssChildRow ) ) { cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add( $row ); // go to the next for loop continue; @@ -288,10 +280,12 @@ } function buildHeaders( table, msg ) { - var maxSeen = 0, + var config = $( table ).data( 'tablesorter' ).config, + maxSeen = 0, colspanOffset = 0, columns, i, + $cell, rowspan, colspan, headerCount, @@ -344,30 +338,31 @@ // 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 ) { + $cell = $( this ); columns = []; + for ( i = 0; i < this.colSpan; i++ ) { - table.columnToHeader[ colspanOffset + i ] = headerIndex; + config.columnToHeader[ colspanOffset + i ] = headerIndex; columns.push( colspanOffset + i ); } - table.headerToColumns[ headerIndex ] = columns; + config.headerToColumns[ headerIndex ] = columns; colspanOffset += this.colSpan; - this.headerIndex = headerIndex; - this.order = 0; - this.count = 0; + $cell.data( { + headerIndex: headerIndex, + order: 0, + count: 0 + } ); - if ( $( this ).hasClass( table.config.unsortableClass ) ) { - this.sortDisabled = true; + if ( $cell.hasClass( config.unsortableClass ) ) { + $cell.data( 'sortDisabled', true ); } - if ( !this.sortDisabled ) { - $( this ) - .addClass( table.config.cssHeader ) + if ( !$cell.data( 'sortDisabled' ) ) { + $cell + .addClass( config.cssHeader ) .prop( 'tabIndex', 0 ) .attr( { role: 'columnheader button', @@ -376,7 +371,7 @@ } // add cell to headerList - table.config.headerList[headerIndex] = this; + config.headerList[headerIndex] = this; } ); return $tableHeaders; @@ -396,18 +391,23 @@ $.each( headerToColumns, function ( headerIndex, columns ) { $.each( columns, function ( i, columnIndex ) { - var header = $headers[headerIndex]; + var header = $headers[headerIndex], + $header = $( header ); if ( !isValueInArray( columnIndex, sortList ) ) { // Column shall not be sorted: Reset header count and order. - header.order = 0; - header.count = 0; + $header.data( { + order: 0, + 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; + $header.data( { + order: sortColumn[1], + count: sortColumn[1] + 1 + } ); return false; } } ); @@ -550,7 +550,7 @@ */ function explodeRowspans( $table ) { var spanningRealCellIndex, rowSpan, colSpan, - cell, i, $tds, $clone, $nextRows, + cell, cellData, i, $tds, $clone, $nextRows, rowspanCells = $table.find( '> tbody > tr > [rowspan]' ).get(); // Short circuit @@ -566,8 +566,10 @@ col = 0, l = this.cells.length; for ( i = 0; i < l; i++ ) { - this.cells[i].realCellIndex = col; - this.cells[i].realRowIndex = this.rowIndex; + $( this.cells[i] ).data( 'tablesorter', { + realCellIndex: col, + realRowIndex: this.rowIndex + } ); col += this.cells[i].colSpan; } } ); @@ -577,45 +579,55 @@ // Re-sort whenever a rowspanned cell's realCellIndex is changed, because it // might change the sort order. function resortCells() { + var cellAData, + cellBData, + ret; rowspanCells = rowspanCells.sort( function ( a, b ) { - var ret = a.realCellIndex - b.realCellIndex; + cellAData = $.data( a, 'tablesorter' ); + cellBData = $.data( b, 'tablesorter' ); + ret = cellAData.realCellIndex - cellBData.realCellIndex; if ( !ret ) { - ret = a.realRowIndex - b.realRowIndex; + ret = cellAData.realRowIndex - cellBData.realRowIndex; } return ret; } ); $.each( rowspanCells, function () { - this.needResort = false; + $.data( this, 'tablesorter' ).needResort = false; } ); } resortCells(); function filterfunc() { - return this.realCellIndex >= spanningRealCellIndex; + return $.data( this, 'tablesorter' ).realCellIndex >= spanningRealCellIndex; } function fixTdCellIndex() { - this.realCellIndex += colSpan; + $.data( this, 'tablesorter' ).realCellIndex += colSpan; if ( this.rowSpan > 1 ) { - this.needResort = true; + $.data( this, 'tablesorter' ).needResort = true; } } while ( rowspanCells.length ) { - if ( rowspanCells[0].needResort ) { + if ( $.data( rowspanCells[0], 'tablesorter' ).needResort ) { resortCells(); } cell = rowspanCells.shift(); + cellData = $.data( cell, 'tablesorter' ); rowSpan = cell.rowSpan; colSpan = cell.colSpan; - spanningRealCellIndex = cell.realCellIndex; + spanningRealCellIndex = cellData.realCellIndex; cell.rowSpan = 1; $nextRows = $( cell ).parent().nextAll(); for ( i = 0; i < rowSpan - 1; i++ ) { $tds = $( $nextRows[i].cells ).filter( filterfunc ); $clone = $( cell ).clone(); - $clone[0].realCellIndex = spanningRealCellIndex; + $clone.data( 'tablesorter', { + realCellIndex: spanningRealCellIndex, + realRowIndex: cellData.realRowIndex + i, + needResort: true + } ); if ( $tds.length ) { $tds.each( fixTdCellIndex ); $tds.first().before( $clone ); @@ -702,18 +714,14 @@ cssAsc: 'headerSortUp', cssDesc: 'headerSortDown', cssChildRow: 'expand-child', - sortInitialOrder: 'asc', sortMultiSortKey: 'shiftKey', - sortLocaleCompare: false, unsortableClass: 'unsortable', parsers: {}, - widgets: [], - headers: {}, cancelSelection: true, sortList: [], headerList: [], - selectorHeaders: 'thead tr:eq(0) th', - debug: false + headerToColumns: [], + columnToHeader: [] }, dateRegex: [], @@ -746,17 +754,13 @@ } $table.addClass( 'jquery-tablesorter' ); - // FIXME config should probably not be stored in the plain table node - // New config object. - table.config = {}; - - // Merge and extend. - config = $.extend( table.config, $.tablesorter.defaultOptions, settings ); + // Merge and extend + config = $.extend( {}, $.tablesorter.defaultOptions, settings ); // Save the settings where they read $.data( table, 'tablesorter', { config: config } ); - // Get the CSS class names, could be done else where. + // Get the CSS class names, could be done elsewhere sortCSS = [ config.cssDesc, config.cssAsc ]; sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ]; @@ -781,7 +785,7 @@ buildCollationTable(); // Legacy fix of .sortbottoms - // Wrap them inside inside a tfoot (because that's what they actually want to be) & + // Wrap them inside a tfoot (because that's what they actually want to be) // and put the at the end of the var $tfoot, $sortbottoms = $table.find( '> tbody > tr.sortbottom' ); @@ -796,14 +800,14 @@ explodeRowspans( $table ); - // try to auto detect column type, and store in tables config - table.config.parsers = buildParserCache( table, $headers ); + // Try to auto detect column type, and store in tables config + config.parsers = buildParserCache( table, $headers ); } // Apply event handling to headers // this is too big, perhaps break it out? - $headers.not( '.' + table.config.unsortableClass ).on( 'keypress click', function ( e ) { - var cell, columns, newSortList, i, + $headers.not( '.' + config.unsortableClass ).on( 'keypress click', function ( e ) { + var cell, $cell, columns, newSortList, i, totalRows, j, s, o; @@ -832,16 +836,21 @@ totalRows = ( $table[0].tBodies[0] && $table[0].tBodies[0].rows.length ) || 0; if ( !table.sortDisabled && totalRows > 0 ) { + cell = this; + $cell = $( cell ); + // Get current column sort order - this.order = this.count % 2; - this.count++; + $cell.data( { + order: $cell.data( 'count' ) % 2, + count: $cell.data( 'count' ) + 1 + } ); cell = this; // Get current column index - columns = table.headerToColumns[ this.headerIndex ]; + columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ]; newSortList = $.map( columns, function ( c ) { // jQuery "helpfully" flattens the arrays... - return [[c, cell.order]]; + return [[c, $cell.data( 'order' )]]; } ); // Index of first column belonging to this header i = columns[0]; @@ -861,9 +870,8 @@ s = config.sortList[j]; o = config.headerList[s[0]]; if ( isValueInArray( s[0], newSortList ) ) { - o.count = s[1]; - o.count++; - s[1] = o.count % 2; + $( o ).data( 'count', s[1] + 1 ); + s[1] = $( o ).data( 'count' ) % 2; } } } else { @@ -873,10 +881,10 @@ } // Reset order/counts of cells not affected by sorting - setHeadersOrder( $headers, config.sortList, table.headerToColumns ); + setHeadersOrder( $headers, config.sortList, config.headerToColumns ); // Set CSS for headers - setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, table.columnToHeader ); + setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader ); appendToTable( $table[0], multisort( $table[0], config.sortList, cache ) ); @@ -917,13 +925,13 @@ // 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 ); + setHeadersOrder( $headers, sortList, config.headerToColumns ); // re-build the cache for the tbody cells cache = buildCache( table ); // set css for headers - setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, table.columnToHeader ); + setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader ); // sort the table and append it to the dom appendToTable( table, multisort( table, sortList, cache ) ); @@ -983,6 +991,15 @@ clearTableBody: function ( table ) { $( table.tBodies[0] ).empty(); + }, + + getParser: function ( id ) { + buildTransformTable(); + buildDateTable(); + cacheRegexs(); + buildCollationTable(); + + return getParserById( id ); } }; @@ -1104,9 +1121,9 @@ return '99999999'; } } else if ( ( match = s.match( ts.dateRegex[1] ) ) !== null ) { - s = [ match[3], '' + ts.monthNames[match[2]], match[1] ]; + s = [ match[3], String( ts.monthNames[match[2]] ), match[1] ]; } else if ( ( match = s.match( ts.dateRegex[2] ) ) !== null ) { - s = [ match[3], '' + ts.monthNames[match[1]], match[2] ]; + s = [ match[3], String( ts.monthNames[match[1]] ), match[2] ]; } else { // Should never get here return '99999999'; diff --git a/resources/src/jquery/jquery.textSelection.js b/resources/src/jquery/jquery.textSelection.js index 8d440fdc..51119305 100644 --- a/resources/src/jquery/jquery.textSelection.js +++ b/resources/src/jquery/jquery.textSelection.js @@ -24,8 +24,9 @@ $.fn.textSelection = function ( command, options ) { var fn, + alternateFn, context, - hasWikiEditorSurface, // The alt edit surface needs to implement the WikiEditor API + hasWikiEditor, needSave, retval; @@ -210,9 +211,10 @@ endPos = this.selectionEnd; scrollTop = this.scrollTop; checkSelectedText(); - if ( options.selectionStart !== undefined - && endPos - startPos !== options.selectionEnd - options.selectionStart ) - { + if ( + options.selectionStart !== undefined && + endPos - startPos !== options.selectionEnd - options.selectionStart + ) { // This means there is a difference in the selection range returned by browser and what we passed. // This happens for Chrome in the case of composite characters. Ref bug #30130 // Set the startPos to the correct position. @@ -242,7 +244,7 @@ selText = selText.replace( /\r?\n/g, '\r\n' ); post = post.replace( /\r?\n/g, '\r\n' ); } - if ( isSample && options.selectPeri && !options.splitlines ) { + if ( isSample && options.selectPeri && ( !options.splitlines || ( options.splitlines && selText.indexOf( '\n' ) === -1 ) ) ) { this.selectionStart = startPos + pre.length; this.selectionEnd = startPos + pre.length + selText.length; } else { @@ -507,11 +509,13 @@ } }; + alternateFn = $( this ).data( 'jquery.textSelection' ); + // Apply defaults switch ( command ) { - //case 'getContents': // no params - //case 'setContents': // no params with defaults - //case 'getSelection': // no params + // case 'getContents': // no params + // case 'setContents': // no params with defaults + // case 'getSelection': // no params case 'encapsulateSelection': options = $.extend( { pre: '', // Text to insert before the cursor/selection @@ -550,19 +554,30 @@ force: false // Force a scroll even if the caret position is already visible }, options ); break; + case 'register': + if ( alternateFn ) { + throw new Error( 'Another textSelection API was already registered' ); + } + $( this ).data( 'jquery.textSelection', options ); + // No need to update alternateFn as this command only stores the options. + // A command that uses it will set it again. + return; + case 'unregister': + $( this ).removeData( 'jquery.textSelection' ); + return; } context = $( this ).data( 'wikiEditor-context' ); - hasWikiEditorSurface = ( context !== undefined && context.$iframe !== undefined ); + hasWikiEditor = ( context !== undefined && context.$iframe !== undefined ); // IE selection restore voodoo needSave = false; - if ( hasWikiEditorSurface && context.savedSelection !== null ) { + if ( hasWikiEditor && context.savedSelection !== null ) { context.fn.restoreSelection(); needSave = true; } - retval = ( hasWikiEditorSurface && context.fn[command] !== undefined ? context.fn : fn )[command].call( this, options ); - if ( hasWikiEditorSurface && needSave ) { + retval = ( alternateFn && alternateFn[command] || fn[command] ).call( this, options ); + if ( hasWikiEditor && needSave ) { context.fn.saveSelection(); } diff --git a/resources/src/mediawiki.action/images/nextredirect-ltr.png b/resources/src/mediawiki.action/images/nextredirect-ltr.png index cd657c33..e0a6bd7f 100644 Binary files a/resources/src/mediawiki.action/images/nextredirect-ltr.png and b/resources/src/mediawiki.action/images/nextredirect-ltr.png differ diff --git a/resources/src/mediawiki.action/images/nextredirect-ltr.svg b/resources/src/mediawiki.action/images/nextredirect-ltr.svg new file mode 100644 index 00000000..6932e580 --- /dev/null +++ b/resources/src/mediawiki.action/images/nextredirect-ltr.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/src/mediawiki.action/images/nextredirect-rtl.png b/resources/src/mediawiki.action/images/nextredirect-rtl.png index b788f334..ddb5273b 100644 Binary files a/resources/src/mediawiki.action/images/nextredirect-rtl.png and b/resources/src/mediawiki.action/images/nextredirect-rtl.png differ diff --git a/resources/src/mediawiki.action/images/nextredirect-rtl.svg b/resources/src/mediawiki.action/images/nextredirect-rtl.svg new file mode 100644 index 00000000..b309da94 --- /dev/null +++ b/resources/src/mediawiki.action/images/nextredirect-rtl.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/src/mediawiki.action/images/redirect-ltr.png b/resources/src/mediawiki.action/images/redirect-ltr.png index 695f2a13..0734d731 100644 Binary files a/resources/src/mediawiki.action/images/redirect-ltr.png and b/resources/src/mediawiki.action/images/redirect-ltr.png differ diff --git a/resources/src/mediawiki.action/images/redirect-ltr.svg b/resources/src/mediawiki.action/images/redirect-ltr.svg new file mode 100644 index 00000000..713be6cb --- /dev/null +++ b/resources/src/mediawiki.action/images/redirect-ltr.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/src/mediawiki.action/images/redirect-rtl.png b/resources/src/mediawiki.action/images/redirect-rtl.png index c954a2ad..c883795e 100644 Binary files a/resources/src/mediawiki.action/images/redirect-rtl.png and b/resources/src/mediawiki.action/images/redirect-rtl.png differ diff --git a/resources/src/mediawiki.action/images/redirect-rtl.svg b/resources/src/mediawiki.action/images/redirect-rtl.svg new file mode 100644 index 00000000..ce0c7c92 --- /dev/null +++ b/resources/src/mediawiki.action/images/redirect-rtl.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js b/resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js index b5654400..6b330128 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js +++ b/resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js @@ -5,54 +5,37 @@ 'use strict'; $( function () { - var savedWindowOnBeforeUnload, - $wpTextbox1 = $( '#wpTextbox1' ), - $wpSummary = $( '#wpSummary' ); + var allowCloseWindow, + $textBox = $( '#wpTextbox1' ), + $summary = $( '#wpSummary' ), + $both = $textBox.add( $summary ); + // Check if EditWarning is enabled and if we need it - if ( $wpTextbox1.length === 0 ) { + if ( !mw.user.options.get( 'useeditwarning' ) ) { return true; } - // Get the original values of some form elements - $wpTextbox1.add( $wpSummary ).each( function () { - $( this ).data( 'origtext', $( this ).val() ); + + // Save the original value of the text fields + $both.each( function ( index, element ) { + var $element = $( element ); + $element.data( 'origtext', $element.textSelection( 'getContents' ) ); } ); - $( 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.textSelection( 'getContents' ) || - $wpSummary.data( 'origtext' ) !== $wpSummary.textSelection( 'getContents' ) - ) { - // Return our message - retval = mw.msg( 'editwarning-warning' ); - } + allowCloseWindow = mw.confirmCloseWindow( { + test: function () { + // We use .textSelection, because editors might not have updated the form yet. + return mw.config.get( 'wgAction' ) === 'submit' || + $textBox.data( 'origtext' ) !== $textBox.textSelection( 'getContents' ) || + $summary.data( 'origtext' ) !== $summary.textSelection( 'getContents' ); + }, - // 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; - } - } ); + message: mw.msg( 'editwarning-warning' ), + namespace: 'editwarning' + } ); // Add form submission handler $( '#editform' ).submit( function () { - // Unbind our handlers - $( window ).off( '.editwarning' ); + allowCloseWindow(); } ); } ); diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.js b/resources/src/mediawiki.action/mediawiki.action.edit.js index 4519b049..01a25f3b 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.js +++ b/resources/src/mediawiki.action/mediawiki.action.edit.js @@ -1,217 +1,23 @@ -/** - * Interface for the classic edit toolbar. - * - * @class mw.toolbar - * @singleton +/*! + * Scripts for action=edit at domready */ -( function ( mw, $ ) { - var toolbar, isReady, $toolbar, queue, slice, $currentFocused; - - /** - * Internal helper that does the actual insertion of the button into the toolbar. - * - * See #addButton for parameter documentation. - * - * @private - */ - function insertButton( b, speedTip, tagOpen, tagClose, sampleText, imageId ) { - var $button; - - // Backwards compatibility - if ( typeof b !== 'object' ) { - b = { - imageFile: b, - speedTip: speedTip, - tagOpen: tagOpen, - tagClose: tagClose, - sampleText: sampleText, - imageId: imageId - }; - } - - if ( b.imageFile ) { - $button = $( '' ).attr( { - src: b.imageFile, - alt: b.speedTip, - title: b.speedTip, - id: b.imageId || undefined, - 'class': 'mw-toolbar-editbutton' - } ); - } else { - $button = $( '
' ).attr( { - title: b.speedTip, - id: b.imageId || undefined, - 'class': 'mw-toolbar-editbutton' - } ); +jQuery( function ( $ ) { + var editBox, scrollTop, $editForm; + + // 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. + editBox = document.getElementById( 'wpTextbox1' ); + scrollTop = document.getElementById( 'wpScrolltop' ); + $editForm = $( '#editform' ); + if ( $editForm.length && editBox && scrollTop ) { + if ( scrollTop.value ) { + editBox.scrollTop = scrollTop.value; } - - $button.click( function ( e ) { - if ( b.onClick !== undefined ) { - b.onClick( e ); - } else { - toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText ); - } - - return false; + $editForm.submit( function () { + scrollTop.value = editBox.scrollTop; } ); - - $toolbar.append( $button ); } - - 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. - * - * For backwards-compatibility, passing `imageFile`, `speedTip`, `tagOpen`, `tagClose`, - * `sampleText` and `imageId` as separate arguments (in this order) is also supported. - * - * @param {Object} button Object with the following properties. - * You are required to provide *either* the `onClick` parameter, or the three parameters - * `tagOpen`, `tagClose` and `sampleText`, but not both (they're mutually exclusive). - * @param {string} [button.imageFile] Image to use for the button. - * @param {string} button.speedTip Tooltip displayed when user mouses over the button. - * @param {Function} [button.onClick] Function to be executed when the button is clicked. - * @param {string} [button.tagOpen] - * @param {string} [button.tagClose] - * @param {string} [button.sampleText] Alternative to `onClick`. `tagOpen`, `tagClose` and - * `sampleText` together provide the markup that should be inserted into page text at - * current cursor position. - * @param {string} [button.imageId] `id` attribute of the button HTML element. Can be - * used to define the image with CSS if it's not provided as `imageFile`. - */ - addButton: function () { - if ( isReady ) { - insertButton.apply( toolbar, arguments ); - } else { - // Convert arguments list to array - queue.push( slice.call( arguments ) ); - } - }, - /** - * Add multiple buttons to the toolbar (see also #addButton). - * - * 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 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( - 'encapsulateSelection', { - pre: tagOpen, - peri: sampleText, - post: tagClose - } - ); - } - }, - - // For backwards compatibility, - // Called from EditPage.php, maybe in other places as well. - init: function () {} - }; - - // Legacy (for compatibility with the code previously in skins/common.edit.js) - mw.log.deprecate( window, 'addButton', toolbar.addButton, 'Use mw.toolbar.addButton instead.' ); - mw.log.deprecate( window, 'insertTags', toolbar.insertTags, 'Use mw.toolbar.insertTags instead.' ); - - // Expose API publicly - mw.toolbar = toolbar; - - $( function () { - var i, b, editBox, scrollTop, $editForm; - - // Used to determine where to insert tags - $currentFocused = $( '#wpTextbox1' ); - - // Populate the selector cache for $toolbar - $toolbar = $( '#toolbar' ); - - 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 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 queue. - // It is important that this is after the one and only loop through - // 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. - 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; - } ); - } - - // Apply to dynamically created textboxes as well as normal ones - $( document ).on( 'focus', 'textarea, input:text', function () { - $currentFocused = $( this ); - } ); - } ); - -}( mediaWiki, jQuery ) ); +} ); diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.preview.js b/resources/src/mediawiki.action/mediawiki.action.edit.preview.js index 6b212c28..f24703af 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.preview.js +++ b/resources/src/mediawiki.action/mediawiki.action.edit.preview.js @@ -8,17 +8,29 @@ * @param {jQuery.Event} e */ function doLivePreview( e ) { - var $wikiPreview, $editform, copySelectors, $copyElements, $spinner, - targetUrl, postData, $previewDataHolder; - - e.preventDefault(); - - // Deprecated: Use mw.hook instead - $( mw ).trigger( 'LivePreviewPrepare' ); + var isDiff, api, request, postData, copySelectors, section, + $wikiPreview, $wikiDiff, $editform, $textbox, $summary, $copyElements, $spinner, $errorBox; + isDiff = ( e.target.name === 'wpDiff' ); $wikiPreview = $( '#wikiPreview' ); + $wikiDiff = $( '#wikiDiff' ); $editform = $( '#editform' ); + $textbox = $editform.find( '#wpTextbox1' ); + $summary = $editform.find( '#wpSummary' ); + $errorBox = $( '.errorbox' ); + section = $editform.find( '[name="wpSection"]' ).val(); + + if ( $textbox.length === 0 ) { + return; + } + // Show changes for a new section is not yet supported + if ( isDiff && section === 'new' ) { + return; + } + e.preventDefault(); + // Remove any previously displayed errors + $errorBox.remove(); // 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) $wikiPreview.show(); @@ -26,15 +38,12 @@ // Jump to where the preview will appear $wikiPreview[0].scrollIntoView(); - // List of selectors matching elements that we will - // update from from the ajax-loaded preview page. copySelectors = [ // Main '#firstHeading', '#wikiPreview', '#wikiDiff', '#catlinks', - '.hiddencats', '#p-lang', // Editing-related '.templatesUsed', @@ -59,70 +68,184 @@ // (e.g. empty #catlinks) $copyElements.animate( { opacity: 0.4 }, 'fast' ); - $previewDataHolder = $( '
' ); - targetUrl = $editform.attr( 'action' ); - targetUrl += targetUrl.indexOf( '?' ) !== -1 ? '&' : '?'; - targetUrl += $.param( { - debug: mw.config.get( 'debug' ), + api = new mw.Api(); + postData = { + action: 'parse', uselang: mw.config.get( 'wgUserLanguage' ), - useskin: mw.config.get( 'skin' ) - } ); + title: mw.config.get( 'wgPageName' ), + text: $textbox.textSelection( 'getContents' ), + summary: $summary.textSelection( 'getContents' ) + }; - // Gather all the data from the form - postData = $editform.formToArray(); - postData.push( { - name: e.target.name, - value: '' - } ); + if ( section !== '' ) { + postData.sectionpreview = ''; + if ( section === 'new' ) { + postData.section = section; + postData.sectiontitle = postData.summary; + } + } - // Load new preview data. - // 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, $next, $parent; + if ( isDiff ) { + $wikiPreview.hide(); - // 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++ ) { - $from = $previewDataHolder.find( copySelectors[i] ); + // First PST the input, then diff it + postData.onlypst = ''; + request = api.post( postData ); + request.done( function ( response ) { + var postData; + postData = { + action: 'query', + indexpageids: '', + prop: 'revisions', + titles: mw.config.get( 'wgPageName' ), + rvdifftotext: response.parse.text['*'], + rvprop: '' + }; + if ( section !== '' ) { + postData.rvsection = section; + } + return api.post( postData ).done( function ( result2 ) { + try { + var diffHtml = result2.query.pages[result2.query.pageids[0]] + .revisions[0].diff['*']; + $wikiDiff.find( 'table.diff tbody' ).html( diffHtml ); + } catch ( e ) { + // "result.blah is undefined" error, ignore + mw.log.warn( e ); + } + $wikiDiff.show(); + } ); + } ); + } else { + $wikiDiff.hide(); + $.extend( postData, { + pst: '', + preview: '', + prop: 'text|displaytitle|modules|categorieshtml|templates|langlinks|limitreporthtml', + disableeditsection: true + } ); + request = api.post( postData ); + request.done( function ( response ) { + var li, newList, $displaytitle, $content, $parent, $list; + if ( response.parse.modules ) { + mw.loader.load( response.parse.modules.concat( + response.parse.modulescripts, + response.parse.modulestyles, + response.parse.modulemessages ) ); + } + if ( response.parse.displaytitle ) { + $displaytitle = $( $.parseHTML( response.parse.displaytitle ) ); + $( '#firstHeading' ).msg( + mw.config.get( 'wgEditMessage', 'editing' ), + $displaytitle + ); + document.title = mw.msg( + 'pagetitle', + mw.msg( + mw.config.get( 'wgEditMessage', 'editing' ), + $displaytitle.text() + ) + ); + } + if ( response.parse.categorieshtml ) { + $( '#catlinks' ).replaceWith( response.parse.categorieshtml['*'] ); + } + if ( response.parse.templates ) { + newList = []; + $.each( response.parse.templates, function ( i, template ) { + li = $( '
  • ' ) + .append( $( '' ) + .attr( { + 'href': mw.util.getUrl( template['*'] ), + 'class': ( template.exists !== undefined ? '' : 'new' ) + } ) + .text( template['*'] ) + ); + newList.push( li ); + } ); - if ( copySelectors[i] === '#wikiPreview' ) { - $next = $wikiPreview.next(); - // If there is no next node, use parent instead. - // Only query parent if needed, false otherwise. - $parent = !$next.length && $wikiPreview.parent(); + $editform.find( '.templatesUsed .mw-editfooter-list' ).detach().empty().append( newList ).appendTo( '.templatesUsed' ); + } + if ( response.parse.limitreporthtml ) { + $( '.limitreport' ).html( response.parse.limitreporthtml['*'] ); + } + if ( response.parse.langlinks && mw.config.get( 'skin' ) === 'vector' ) { + newList = []; + $.each( response.parse.langlinks, function ( i, langlink ) { + li = $( '
  • ' ) + .addClass( 'interlanguage-link interwiki-' + langlink.lang ) + .append( $( '' ) + .attr( { + 'href': langlink.url, + 'title': langlink['*'] + ' - ' + langlink.langname, + 'lang': langlink.lang, + 'hreflang': langlink.lang + } ) + .text( langlink.autonym ) + ); + newList.push( li ); + } ); + $list = $( '#p-lang ul' ); + $parent = $list.parent(); + $list.detach().empty().append( newList ).prependTo( $parent ); + } - $wikiPreview + if ( response.parse.text['*'] ) { + $content = $wikiPreview.children( '.mw-content-ltr,.mw-content-rtl' ); + $content .detach() - .empty() - .append( $from.contents() ) - .attr( 'class', $from.attr( 'class' ) ); + .html( response.parse.text['*'] ); - mw.hook( 'wikipage.content' ).fire( $wikiPreview ); + mw.hook( 'wikipage.content' ).fire( $content ); // Reattach - if ( $parent ) { - $parent.append( $wikiPreview ); - } else { - $next.before( $wikiPreview ); - } + $wikiPreview.append( $content ); + + $wikiPreview.show(); - } else { - $( copySelectors[i] ) - .empty() - .append( $from.contents() ) - .attr( 'class', $from.attr( 'class' ) ); } + } ); + } + request.done( function ( response ) { + var isSubject = ( section === 'new' ), + summaryMsg = isSubject ? 'subject-preview' : 'summary-preview'; + if ( response.parse.parsedsummary ) { + $editform.find( '.mw-summary-preview' ) + .empty() + .append( + mw.message( summaryMsg ).parse(), + ' ', + $( '' ).addClass( 'comment' ).html( + // There is no equivalent to rawParams + mw.message( 'parentheses' ).escaped() + .replace( '$1', response.parse.parsedsummary['*'] ) + ) + ); } - - // Deprecated: Use mw.hook instead - $( mw ).trigger( 'LivePreviewDone', [copySelectors] ); - + } ); + request.always( function () { $spinner.remove(); $copyElements.animate( { opacity: 1 }, 'fast' ); } ); + request.fail( function ( code, result ) { + var errorMsg = 'API error: ' + code; + if ( code === 'http' ) { + errorMsg = 'HTTP error: '; + if ( result.exception ) { + errorMsg += result.exception; + } else { + errorMsg += result.textStatus; + } + } + $errorBox = $( '
    ' ) + .addClass( 'errorbox' ) + .html( '' + mw.message( 'previewerrortext' ).escaped() + '
    ' ) + .append( document.createTextNode( errorMsg ) ); + $wikiDiff.hide(); + $wikiPreview.hide().before( $errorBox ); + } ); } $( function () { @@ -138,21 +261,33 @@ // 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( - $( '
    ' ).attr( 'id', 'p-lang' ) + if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) && mw.config.get( 'skin' ) === 'vector' ) { + $( '.portal:last' ).after( + $( '
    ' ).attr( { + 'class': 'portal', + 'id': 'p-lang', + 'role': 'navigation', + 'title': mw.msg( 'tooltip-p-lang' ), + 'aria-labelledby': 'p-lang-label' + } ) + .append( $( '

    ' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) ) + .append( $( '
    ' ).addClass( 'body' ).append( '
      ' ) ) ); } if ( !$( '.mw-summary-preview' ).length ) { - $( '.editCheckboxes' ).before( + $( '#wpSummary' ).after( $( '
      ' ).addClass( 'mw-summary-preview' ) ); } if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) { $( '#wikiPreview' ).after( - $( '
      ' ).attr( 'id', 'wikiDiff' ) + $( '
      ' ) + .hide() + .attr( 'id', 'wikiDiff' ) + .html( '

  • ' + + '
    ' ) ); } diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.stash.js b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js new file mode 100644 index 00000000..29c533d8 --- /dev/null +++ b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js @@ -0,0 +1,76 @@ +/*! + * Scripts for pre-emptive edit preparing on action=edit + */ +( function ( mw, $ ) { + $( function () { + var idleTimeout = 4000, + api = new mw.Api(), + pending = null, + $form = $( '#editform' ), + $text = $form.find( '#wpTextbox1' ), + data = {}, + timer = null; + + function stashEdit( token ) { + data = $form.serializeObject(); + + pending = api.post( { + action: 'stashedit', + token: token, + title: mw.config.get( 'wgPageName' ), + section: data.wpSection, + sectiontitle: '', + text: data.wpTextbox1, + contentmodel: data.model, + contentformat: data.format, + baserevid: data.parentRevId + } ); + } + + /* Has the edit body text changed since the last stashEdit() call? */ + function isChanged() { + // Normalize line endings to CRLF, like $.fn.serializeObject does. + var newText = $text.val().replace( /\r?\n/g, '\r\n' ); + return newText !== data.wpTextbox1; + } + + function onEditChanged() { + if ( !isChanged() ) { + return; + } + + // If a request is in progress, abort it; its payload is stale. + if ( pending ) { + pending.abort(); + } + + api.getToken( 'edit' ).then( stashEdit ); + } + + function onKeyPress( e ) { + // Ignore keystrokes that don't modify text, like cursor movements. + // See . + if ( e.which === 0 ) { + return; + } + + clearTimeout( timer ); + + if ( pending ) { + pending.abort(); + } + + timer = setTimeout( onEditChanged, idleTimeout ); + } + + // We don't attempt to stash new section edits because in such cases + // the parser output varies on the edit summary (since it determines + // the new section's name). + if ( $form.find( 'input[name=wpSection]' ).val() === 'new' ) { + return; + } + + $text.on( { change: onEditChanged, keypress: onKeyPress } ); + + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.styles.css b/resources/src/mediawiki.action/mediawiki.action.edit.styles.css index 7148b964..4209aa1f 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.styles.css +++ b/resources/src/mediawiki.action/mediawiki.action.edit.styles.css @@ -8,14 +8,6 @@ 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; diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_bold.png deleted file mode 100644 index e524f6cb..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_headline.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_headline.png deleted file mode 100644 index 398e5614..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_headline.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_italic.png deleted file mode 100644 index 6ec73e9e..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_link.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_link.png deleted file mode 100644 index c9c63f6c..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_link.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_nowiki.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_nowiki.png deleted file mode 100644 index 743ea61b..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ar/button_nowiki.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_bold.png deleted file mode 100644 index 5c10cfe2..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_italic.png deleted file mode 100644 index 72209d74..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_link.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_link.png deleted file mode 100644 index 09c86fb1..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/be-tarask/button_link.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_bold.png deleted file mode 100644 index 367d5bc1..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_italic.png deleted file mode 100644 index fdd8c9f9..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/de/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_bold.png deleted file mode 100644 index 75c3f109..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_extlink.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_extlink.png deleted file mode 100644 index 458943c1..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_extlink.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_headline.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_headline.png deleted file mode 100644 index 9cf751d9..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_headline.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_hr.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_hr.png deleted file mode 100644 index 47e1ca40..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_hr.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_image.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_image.png deleted file mode 100644 index 69192965..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_image.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_italic.png deleted file mode 100644 index 527fbd14..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_link.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_link.png deleted file mode 100644 index eb5634b9..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_link.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_media.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_media.png deleted file mode 100644 index 4194ec18..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_media.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_nowiki.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_nowiki.png deleted file mode 100644 index 2ba818de..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_nowiki.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_sig.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_sig.png deleted file mode 100644 index fe34b3fb..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/en/button_sig.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_bold.png deleted file mode 100644 index c54d094c..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_headline.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_headline.png deleted file mode 100644 index 9890d155..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_headline.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_italic.png deleted file mode 100644 index 33f91ed6..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_link.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_link.png deleted file mode 100644 index 76b939e6..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_link.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_nowiki.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_nowiki.png deleted file mode 100644 index 743ea61b..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/fa/button_nowiki.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/LICENSE b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/LICENSE deleted file mode 100644 index 47ecfe4e..00000000 --- a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ - -button_italic.png -------------------- -Source : http://commons.wikimedia.org/wiki/Image:Button_S_italic.png -License: Public domain -Author : Purodha Blissenbach, http://ksh.wikipedia.org/wiki/User:Purodha - diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/button_italic.png deleted file mode 100644 index 15496c08..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ksh/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/LICENSE b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/LICENSE deleted file mode 100644 index bedcec66..00000000 --- a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -button_bold.png ---------------- -Source : http://commons.wikimedia.org/wiki/Image:Button_bold_ukr.png -License: Public domain -Author : Alexey Belomoev - -button_italic.png ------------------------- -Source : http://commons.wikimedia.org/wiki/Image:Button_italic_ukr.png -License: Public domain -Author : Alexey Belomoev - -button_link.png ------------------ -Source : http://commons.wikimedia.org/wiki/Image:Button_internal_link_ukr.png -License: GPL -Author : Saproj, Erik Möller diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_bold.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_bold.png deleted file mode 100644 index eae30d98..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_bold.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_italic.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_italic.png deleted file mode 100644 index b958d220..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_italic.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_link.png b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_link.png deleted file mode 100644 index 12ad3731..00000000 Binary files a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/images/ru/button_link.png and /dev/null differ diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/mediawiki.action.edit.toolbar.less b/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/mediawiki.action.edit.toolbar.less deleted file mode 100644 index d65b2842..00000000 --- a/resources/src/mediawiki.action/mediawiki.action.edit.toolbar/mediawiki.action.edit.toolbar.less +++ /dev/null @@ -1,42 +0,0 @@ -@import "mediawiki.mixins"; - -#mw-editbutton-bold { - .background-image("images/@{button-bold}"); -} - -#mw-editbutton-italic { - .background-image("images/@{button-italic}"); -} - -#mw-editbutton-link { - .background-image("images/@{button-link}"); -} - -#mw-editbutton-extlink { - .background-image("images/@{button-extlink}"); -} - -#mw-editbutton-headline { - .background-image("images/@{button-headline}"); -} - -#mw-editbutton-image { - .background-image("images/@{button-image}"); -} - -#mw-editbutton-media { - .background-image("images/@{button-media}"); -} - -#mw-editbutton-nowiki { - .background-image("images/@{button-nowiki}"); -} - -// Who decided to make only this single one different than the name of the data item? -#mw-editbutton-signature { - .background-image("images/@{button-sig}"); -} - -#mw-editbutton-hr { - .background-image("images/@{button-hr}"); -} diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.css index afe92468..0887476e 100644 --- a/resources/src/mediawiki.action/mediawiki.action.history.diff.css +++ b/resources/src/mediawiki.action/mediawiki.action.history.diff.css @@ -1,8 +1,7 @@ -/* -** Diff rendering -*/ +/*! + * Diff rendering + */ table.diff { - background-color: white; border: none; border-spacing: 4px; margin: 0; diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css new file mode 100644 index 00000000..76b5c9b7 --- /dev/null +++ b/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css @@ -0,0 +1,16 @@ +/*! + * Diff rendering + */ +td.diff-context, +td.diff-addedline .diffchange, +td.diff-deletedline .diffchange { + background-color: transparent; +} + +td.diff-addedline .diffchange { + text-decoration: underline; +} + +td.diff-deletedline .diffchange { + text-decoration: line-through; +} diff --git a/resources/src/mediawiki.action/mediawiki.action.history.js b/resources/src/mediawiki.action/mediawiki.action.history.js index ac48c596..2ebfe921 100644 --- a/resources/src/mediawiki.action/mediawiki.action.history.js +++ b/resources/src/mediawiki.action/mediawiki.action.history.js @@ -85,7 +85,8 @@ jQuery( function ( $ ) { $copyForm.find( 'input[name^="ids["]:checked' ).prop( 'checked', false ); // Remove diff=&oldid=, change action=historysubmit to revisiondelete, remove revisiondelete - } else if ( $historySubmitter.hasClass( 'mw-history-revisiondelete-button' ) ) { + } else if ( $historySubmitter.hasClass( 'mw-history-revisiondelete-button' ) || + $historySubmitter.hasClass( 'mw-history-editchangetags-button' ) ) { $copyRadios.remove(); $copyAction.val( $historySubmitter.attr( 'name' ) ); $copyForm.find( ':submit' ).remove(); diff --git a/resources/src/mediawiki.action/mediawiki.action.view.categoryPage.less b/resources/src/mediawiki.action/mediawiki.action.view.categoryPage.less new file mode 100644 index 00000000..387b0207 --- /dev/null +++ b/resources/src/mediawiki.action/mediawiki.action.view.categoryPage.less @@ -0,0 +1,11 @@ +@import "mediawiki.mixins"; + +.mw-category { + .column-count(3); + .column-width(24em); + .mw-category-group { + li { + .column-break-inside-avoid; + } + } +} diff --git a/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js b/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js index 2ded40cf..2be29f09 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js @@ -4,9 +4,16 @@ ( function ( mw, $ ) { $( function () { mw.util.$content.dblclick( function ( e ) { - e.preventDefault(); - // Trigger native HTMLElement click instead of opening URL (bug 43052) - $( '#ca-edit a' ).get( 0 ).click(); + // Recheck preference so extensions can do a hack to disable this code. + if ( parseInt( mw.user.options.get( 'editondblclick' ), 10 ) ) { + e.preventDefault(); + // Trigger native HTMLElement click instead of opening URL (bug 43052) + var $a = $( '#ca-edit a' ); + // Not every page has an edit link (bug 57713) + if ( $a.length ) { + $a.get( 0 ).click(); + } + } } ); } ); }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.action/mediawiki.action.view.metadata.css b/resources/src/mediawiki.action/mediawiki.action.view.metadata.css index 2c8d2e65..9f786ecb 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.metadata.css +++ b/resources/src/mediawiki.action/mediawiki.action.view.metadata.css @@ -4,3 +4,13 @@ table.collapsed tr.collapsable { display: none; } + +/* + * Exclude user interface elements from selection. + */ +.mw-metadata-show-hide-extended { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} diff --git a/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js b/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js index 4d2c47a5..c008dfd8 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js @@ -30,14 +30,7 @@ data.message = $.parseHTML( mw.message( 'postedit-confirmation-saved', data.user || mw.user ).escaped() ); } - $div = $( - '' - ); + $div = mw.template.get( 'mediawiki.action.view.postEdit', 'postEdit.html' ).render(); if ( typeof data.message === 'string' ) { $div.find( '.postedit-content' ).text( data.message ); @@ -83,4 +76,4 @@ mw.cookie.set( cookieKey, null ); } -} ( mediaWiki, jQuery ) ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.action/mediawiki.action.view.redirect.js b/resources/src/mediawiki.action/mediawiki.action.view.redirect.js index 52e0d4e3..e66d8f69 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.redirect.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.redirect.js @@ -11,9 +11,6 @@ fragment = null, shouldChangeFragment, index; - // Clear internal mw.config entries, so that no one tries to depend on them - mw.config.set( 'wgInternalRedirectTargetUrl', null ); - index = canonical.indexOf( '#' ); if ( index !== -1 ) { fragment = canonical.slice( index ); diff --git a/resources/src/mediawiki.action/mediawiki.action.view.redirectPage.css b/resources/src/mediawiki.action/mediawiki.action.view.redirectPage.css index fdbb655f..a92f1c16 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.redirectPage.css +++ b/resources/src/mediawiki.action/mediawiki.action.view.redirectPage.css @@ -24,15 +24,21 @@ margin: 0; padding: 0; padding-left: 42px; + background: transparent url(images/nextredirect-ltr.png) bottom left no-repeat; /* @embed */ - background: url(images/nextredirect-ltr.png) bottom left no-repeat; + background-image: -webkit-linear-gradient(transparent, transparent), url(images/nextredirect-ltr.svg); + /* @embed */ + background-image: linear-gradient(transparent, transparent), url(images/nextredirect-ltr.svg); } /* @noflip */ .mw-content-ltr .redirectText li:first-child { padding-left: 47px; + background: transparent url(images/redirect-ltr.png) bottom left no-repeat; + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/redirect-ltr.svg); /* @embed */ - background: url(images/redirect-ltr.png) bottom left no-repeat; + background-image: linear-gradient(transparent, transparent), url(images/redirect-ltr.svg); } /* @noflip */ @@ -41,13 +47,19 @@ margin: 0; padding: 0; padding-right: 42px; + background: transparent url(images/nextredirect-rtl.png) bottom right no-repeat; /* @embed */ - background: url(images/nextredirect-rtl.png) bottom right no-repeat; + background-image: -webkit-linear-gradient(transparent, transparent), url(images/nextredirect-rtl.svg); + /* @embed */ + background-image: linear-gradient(transparent, transparent), url(images/nextredirect-rtl.svg); } /* @noflip */ .mw-content-rtl .redirectText li:first-child { padding-right: 47px; + background: transparent url(images/redirect-rtl.png) bottom right no-repeat; + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/redirect-rtl.svg); /* @embed */ - background: url(images/redirect-rtl.png) bottom right no-repeat; + background-image: linear-gradient(transparent, transparent), url(images/redirect-rtl.svg); } diff --git a/resources/src/mediawiki.action/templates/postEdit.html b/resources/src/mediawiki.action/templates/postEdit.html new file mode 100644 index 00000000..dbb482a6 --- /dev/null +++ b/resources/src/mediawiki.action/templates/postEdit.html @@ -0,0 +1,6 @@ +
    +
    +
    + × +
    +
    diff --git a/resources/src/mediawiki.api/mediawiki.api.category.js b/resources/src/mediawiki.api/mediawiki.api.category.js index 7dd9730f..14077e02 100644 --- a/resources/src/mediawiki.api/mediawiki.api.category.js +++ b/resources/src/mediawiki.api/mediawiki.api.category.js @@ -3,29 +3,21 @@ */ ( function ( mw, $ ) { - var msg = 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.'; $.extend( mw.Api.prototype, { /** * Determine if a category exists. * * @param {mw.Title|string} title - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {boolean} return.done.isCategory Whether the category exists. */ - isCategory: function ( title, ok, err ) { + isCategory: function ( title ) { var apiPromise = this.get( { prop: 'categoryinfo', titles: String( title ) } ); - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - return apiPromise .then( function ( data ) { var exists = false; @@ -38,8 +30,6 @@ } return exists; } ) - .done( ok ) - .fail( err ) .promise( { abort: apiPromise.abort } ); }, @@ -49,13 +39,11 @@ * E.g. given "Foo", return "Food", "Foolish people", "Foosball tables"... * * @param {string} prefix Prefix to match. - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {string[]} return.done.categories Matched categories */ - getCategoriesByPrefix: function ( prefix, ok, err ) { + getCategoriesByPrefix: function ( prefix ) { // Fetch with allpages to only get categories that have a corresponding description page. var apiPromise = this.get( { list: 'allpages', @@ -63,11 +51,6 @@ apnamespace: mw.config.get( 'wgNamespaceIds' ).category } ); - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - return apiPromise .then( function ( data ) { var texts = []; @@ -78,8 +61,6 @@ } return texts; } ) - .done( ok ) - .fail( err ) .promise( { abort: apiPromise.abort } ); }, @@ -87,34 +68,17 @@ * Get the categories that a particular page on the wiki belongs to. * * @param {mw.Title|string} title - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) - * @param {boolean} [async=true] Asynchronousness (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {boolean|mw.Title[]} return.done.categories List of category titles or false * if title was not found. */ - getCategories: function ( title, ok, err, async ) { + getCategories: function ( title ) { var apiPromise = this.get( { prop: 'categories', titles: String( title ) - }, { - async: async === undefined ? true : async } ); - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - if ( async !== undefined ) { - mw.track( 'mw.deprecate', 'api.async' ); - mw.log.warn( - 'Use of mediawiki.api async=false param is deprecated. ' + - 'The sychronous mode will be removed in the future.' - ); - } - return apiPromise .then( function ( data ) { var titles = false; @@ -132,8 +96,6 @@ } return titles; } ) - .done( ok ) - .fail( err ) .promise( { abort: apiPromise.abort } ); } } ); diff --git a/resources/src/mediawiki.api/mediawiki.api.edit.js b/resources/src/mediawiki.api/mediawiki.api.edit.js index e88ae5e2..dbe45bf6 100644 --- a/resources/src/mediawiki.api/mediawiki.api.edit.js +++ b/resources/src/mediawiki.api/mediawiki.api.edit.js @@ -3,7 +3,6 @@ */ ( function ( mw, $ ) { - var msg = 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.'; $.extend( mw.Api.prototype, { /** @@ -12,35 +11,21 @@ * cached token and start over. * * @param {Object} params API parameters - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} See #post */ - postWithEditToken: function ( params, ok, err ) { - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - - return this.postWithToken( 'edit', params ).done( ok ).fail( err ); + postWithEditToken: function ( params ) { + return this.postWithToken( 'edit', params ); }, /** * API helper to grab an edit token. * - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {string} return.done.token Received token. */ - getEditToken: function ( ok, err ) { - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - - return this.getToken( 'edit' ).done( ok ).fail( err ); + getEditToken: function () { + return this.getToken( 'edit' ); }, /** @@ -50,32 +35,16 @@ * @param {string} header * @param {string} message wikitext message * @param {Object} [additionalParams] Additional API parameters, e.g. `{ redirect: true }` - * @param {Function} [ok] Success handler (deprecated) - * @param {Function} [err] Error handler (deprecated) * @return {jQuery.Promise} */ - newSection: function ( title, header, message, additionalParams, ok, err ) { - // Until we remove 'ok' and 'err' parameters, we have to support code that passes them, - // but not additionalParams... - if ( $.isFunction( additionalParams ) ) { - err = ok; - ok = additionalParams; - additionalParams = undefined; - } - - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - } - + newSection: function ( title, header, message, additionalParams ) { return this.postWithEditToken( $.extend( { action: 'edit', section: 'new', - format: 'json', title: String( title ), summary: header, text: message - }, additionalParams ) ).done( ok ).fail( err ); + }, additionalParams ) ); } } ); diff --git a/resources/src/mediawiki.api/mediawiki.api.js b/resources/src/mediawiki.api/mediawiki.api.js index 51b3238c..3a19e021 100644 --- a/resources/src/mediawiki.api/mediawiki.api.js +++ b/resources/src/mediawiki.api/mediawiki.api.js @@ -49,6 +49,16 @@ * console.log( data ); * } ); * + * Multiple values for a parameter can be specified using an array (since MW 1.25): + * + * var api = new mw.Api(); + * api.get( { + * action: 'query', + * meta: [ 'userinfo', 'siteinfo' ] // same effect as 'userinfo|siteinfo' + * } ).done ( function ( data ) { + * console.log( data ); + * } ); + * * @class * * @constructor @@ -74,32 +84,15 @@ mw.Api.prototype = { - /** - * Normalize the ajax options for compatibility and/or convenience methods. - * - * @param {Object} [arg] An object contaning one or more of options.ajax. - * @return {Object} Normalized ajax options. - */ - normalizeAjaxOptions: function ( arg ) { - // Arg argument is usually empty - // (before MW 1.20 it was used to pass ok callbacks) - var opts = arg || {}; - // Options can also be a success callback handler - if ( typeof arg === 'function' ) { - opts = { ok: arg }; - } - return opts; - }, - /** * Perform API get request * * @param {Object} parameters - * @param {Object|Function} [ajaxOptions] + * @param {Object} [ajaxOptions] * @return {jQuery.Promise} */ get: function ( parameters, ajaxOptions ) { - ajaxOptions = this.normalizeAjaxOptions( ajaxOptions ); + ajaxOptions = ajaxOptions || {}; ajaxOptions.type = 'GET'; return this.ajax( parameters, ajaxOptions ); }, @@ -110,11 +103,11 @@ * TODO: Post actions for non-local hostnames will need proxy. * * @param {Object} parameters - * @param {Object|Function} [ajaxOptions] + * @param {Object} [ajaxOptions] * @return {jQuery.Promise} */ post: function ( parameters, ajaxOptions ) { - ajaxOptions = this.normalizeAjaxOptions( ajaxOptions ); + ajaxOptions = ajaxOptions || {}; ajaxOptions.type = 'POST'; return this.ajax( parameters, ajaxOptions ); }, @@ -130,7 +123,6 @@ ajax: function ( parameters, ajaxOptions ) { var token, apiDeferred = $.Deferred(), - msg = 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.', xhr, key, formData; parameters = $.extend( {}, this.defaults.parameters, parameters ); @@ -142,6 +134,12 @@ delete parameters.token; } + for ( key in parameters ) { + if ( $.isArray( parameters[key] ) ) { + parameters[key] = parameters[key].join( '|' ); + } + } + // If multipart/form-data has been requested and emulation is possible, emulate it if ( ajaxOptions.type === 'POST' && @@ -183,21 +181,6 @@ } } - // Backwards compatibility: Before MediaWiki 1.20, - // callbacks were done with the 'ok' and 'err' property in ajaxOptions. - if ( ajaxOptions.ok ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - apiDeferred.done( ajaxOptions.ok ); - delete ajaxOptions.ok; - } - if ( ajaxOptions.err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( msg ); - apiDeferred.fail( ajaxOptions.err ); - delete ajaxOptions.err; - } - // Make the AJAX request xhr = $.ajax( ajaxOptions ) // If AJAX fails, reject API call with error code 'http' @@ -251,13 +234,7 @@ postWithToken: function ( tokenType, params, ajaxOptions ) { var api = this; - // Do not allow deprecated ok-callback - // FIXME: Remove this check when the deprecated ok-callback is removed in #post - if ( $.isFunction( ajaxOptions ) ) { - ajaxOptions = undefined; - } - - return api.getToken( tokenType ).then( function ( token ) { + return api.getToken( tokenType, params.assert ).then( function ( token ) { params.token = token; return api.post( params, ajaxOptions ).then( // If no error, return to caller as-is @@ -270,7 +247,7 @@ params.token = undefined; // Try again, once - return api.getToken( tokenType ).then( function ( token ) { + return api.getToken( tokenType, params.assert ).then( function ( token ) { params.token = token; return api.post( params, ajaxOptions ); } ); @@ -286,19 +263,21 @@ /** * Get a token for a certain action from the API. * + * The assert parameter is only for internal use by postWithToken. + * * @param {string} type Token type * @return {jQuery.Promise} * @return {Function} return.done * @return {string} return.done.token Received token. * @since 1.22 */ - getToken: function ( type ) { + getToken: function ( type, assert ) { var apiPromise, promiseGroup = promises[ this.defaults.ajax.url ], d = promiseGroup && promiseGroup[ type + 'Token' ]; if ( !d ) { - apiPromise = this.get( { action: 'tokens', type: type } ); + apiPromise = this.get( { action: 'tokens', type: type, assert: assert } ); d = apiPromise .then( function ( data ) { @@ -357,7 +336,6 @@ 'nomodule', 'mustbeposted', 'badaccess-groups', - 'stashfailed', 'missingresult', 'missingparam', 'invalid-file-key', @@ -379,7 +357,18 @@ 'fetchfileerror', 'fileexists-shared-forbidden', 'invalidtitle', - 'notloggedin' + 'notloggedin', + + // Stash-specific errors - expanded + 'stashfailed', + 'stasherror', + 'stashedfilenotfound', + 'stashpathinvalid', + 'stashfilestorage', + 'stashzerolength', + 'stashnotloggedin', + 'stashwrongowner', + 'stashnosuchfilekey' ]; /** diff --git a/resources/src/mediawiki.api/mediawiki.api.login.js b/resources/src/mediawiki.api/mediawiki.api.login.js index ccbae06c..25257927 100644 --- a/resources/src/mediawiki.api/mediawiki.api.login.js +++ b/resources/src/mediawiki.api/mediawiki.api.login.js @@ -14,8 +14,7 @@ * @return {jQuery.Promise} See mw.Api#post */ login: function ( username, password ) { - var params, request, - deferred = $.Deferred(), + var params, apiPromise, innerPromise, api = this; params = { @@ -24,25 +23,31 @@ 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 ); - } - } ); - } ); + apiPromise = api.post( params ); - return deferred.promise( { abort: request.abort } ); + return apiPromise + .then( function ( data ) { + params.lgtoken = data.login.token; + innerPromise = api.post( params ) + .then( function ( data ) { + var code; + if ( data.login.result !== 'Success' ) { + // Set proper error code whenever possible + code = data.error && data.error.code || 'unknown'; + return $.Deferred().reject( code, data ); + } + return data; + } ); + return innerPromise; + } ) + .promise( { + abort: function () { + apiPromise.abort(); + if ( innerPromise ) { + innerPromise.abort(); + } + } + } ); } } ); diff --git a/resources/src/mediawiki.api/mediawiki.api.options.js b/resources/src/mediawiki.api/mediawiki.api.options.js new file mode 100644 index 00000000..b839fbdc --- /dev/null +++ b/resources/src/mediawiki.api/mediawiki.api.options.js @@ -0,0 +1,89 @@ +/** + * @class mw.Api.plugin.options + */ +( function ( mw, $ ) { + + $.extend( mw.Api.prototype, { + + /** + * Asynchronously save the value of a single user option using the API. See #saveOptions. + * + * @param {string} name + * @param {string|null} value + * @return {jQuery.Promise} + */ + saveOption: function ( name, value ) { + var param = {}; + param[name] = value; + return this.saveOptions( param ); + }, + + /** + * Asynchronously save the values of user options using the API. + * + * If a value of `null` is provided, the given option will be reset to the default value. + * + * Any warnings returned by the API, including warnings about invalid option names or values, + * are ignored. However, do not rely on this behavior. + * + * If necessary, the options will be saved using several parallel API requests. Only one promise + * is always returned that will be resolved when all requests complete. + * + * @param {Object} options Options as a `{ name: value, … }` object + * @return {jQuery.Promise} + */ + saveOptions: function ( options ) { + var name, value, bundleable, + grouped = [], + deferreds = []; + + for ( name in options ) { + value = options[name] === null ? null : String( options[name] ); + + // Can we bundle this option, or does it need a separate request? + bundleable = + ( value === null || value.indexOf( '|' ) === -1 ) && + ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 ); + + if ( bundleable ) { + if ( value !== null ) { + grouped.push( name + '=' + value ); + } else { + // Omitting value resets the option + grouped.push( name ); + } + } else { + if ( value !== null ) { + deferreds.push( this.postWithToken( 'options', { + action: 'options', + optionname: name, + optionvalue: value + } ) ); + } else { + // Omitting value resets the option + deferreds.push( this.postWithToken( 'options', { + action: 'options', + optionname: name + } ) ); + } + } + } + + if ( grouped.length ) { + deferreds.push( this.postWithToken( 'options', { + action: 'options', + change: grouped.join( '|' ) + } ) ); + } + + return $.when.apply( $, deferreds ); + } + + } ); + + /** + * @class mw.Api + * @mixins mw.Api.plugin.options + */ + +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.api/mediawiki.api.parse.js b/resources/src/mediawiki.api/mediawiki.api.parse.js index b1f1d2b0..2dcf8078 100644 --- a/resources/src/mediawiki.api/mediawiki.api.parse.js +++ b/resources/src/mediawiki.api/mediawiki.api.parse.js @@ -8,31 +8,21 @@ * Convenience method for 'action=parse'. * * @param {string} wikitext - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {string} return.done.data Parsed HTML of `wikitext`. */ - parse: function ( wikitext, ok, err ) { + parse: function ( wikitext ) { var apiPromise = this.get( { action: 'parse', contentmodel: 'wikitext', text: wikitext } ); - // Backwards compatibility (< MW 1.20) - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.' ); - } - return apiPromise .then( function ( data ) { return data.parse.text['*']; } ) - .done( ok ) - .fail( err ) .promise( { abort: apiPromise.abort } ); } } ); diff --git a/resources/src/mediawiki.api/mediawiki.api.watch.js b/resources/src/mediawiki.api/mediawiki.api.watch.js index af2dee10..40ba136d 100644 --- a/resources/src/mediawiki.api/mediawiki.api.watch.js +++ b/resources/src/mediawiki.api/mediawiki.api.watch.js @@ -12,8 +12,6 @@ * @param {string|mw.Title|string[]|mw.Title[]} pages Full page name or instance of mw.Title, or an * array thereof. If an array is passed, the return value passed to the promise will also be an * array of appropriate objects. - * @param {Function} [ok] Success callback (deprecated) - * @param {Function} [err] Error callback (deprecated) * @return {jQuery.Promise} * @return {Function} return.done * @return {Object|Object[]} return.done.watch Object or list of objects (depends on the `pages` @@ -22,7 +20,7 @@ * @return {boolean} return.done.watch.watched Whether the page is now watched or unwatched * @return {string} return.done.watch.message Parsed HTML of the confirmational interface message */ - function doWatchInternal( pages, ok, err, addParams ) { + function doWatchInternal( pages, addParams ) { // XXX: Parameter addParams is undocumented because we inherit this // documentation in the public method... var apiPromise = this.postWithToken( 'watch', @@ -36,19 +34,11 @@ ) ); - // Backwards compatibility (< MW 1.20) - if ( ok || err ) { - mw.track( 'mw.deprecate', 'api.cbParam' ); - mw.log.warn( 'Use of mediawiki.api callback params is deprecated. Use the Promise instead.' ); - } - return apiPromise .then( function ( data ) { // If a single page was given (not an array) respond with a single item as well. return $.isArray( pages ) ? data.watch : data.watch[0]; } ) - .done( ok ) - .fail( err ) .promise( { abort: apiPromise.abort } ); } @@ -58,16 +48,17 @@ * * @inheritdoc #doWatchInternal */ - watch: function ( pages, ok, err ) { - return doWatchInternal.call( this, pages, ok, err ); + watch: function ( pages ) { + return doWatchInternal.call( this, pages ); }, + /** * Convenience method for `action=watch&unwatch=1`. * * @inheritdoc #doWatchInternal */ - unwatch: function ( pages, ok, err ) { - return doWatchInternal.call( this, pages, ok, err, { unwatch: 1 } ); + unwatch: function ( pages ) { + return doWatchInternal.call( this, pages, { unwatch: 1 } ); } } ); diff --git a/resources/src/mediawiki.language/languages/fi.js b/resources/src/mediawiki.language/languages/fi.js index 453a675d..d9c2b06d 100644 --- a/resources/src/mediawiki.language/languages/fi.js +++ b/resources/src/mediawiki.language/languages/fi.js @@ -17,7 +17,7 @@ mediaWiki.language.convertGrammar = function ( word, form ) { if ( word.match( /wiki$/i ) ) { aou = false; } - //append i after final consonant + // append i after final consonant if ( word.match( /[bcdfghjklmnpqrstvwxz]$/i ) ) { word += 'i'; } diff --git a/resources/src/mediawiki.language/languages/hsb.js b/resources/src/mediawiki.language/languages/hsb.js index 2d6b733e..2c0abd3d 100644 --- a/resources/src/mediawiki.language/languages/hsb.js +++ b/resources/src/mediawiki.language/languages/hsb.js @@ -14,6 +14,6 @@ mediaWiki.language.convertGrammar = function ( word, form ) { case 'lokatiw': // lokatiw word = 'wo ' + word; break; - } + } return word; }; diff --git a/resources/src/mediawiki.language/languages/hy.js b/resources/src/mediawiki.language/languages/hy.js index 9cae360b..c4a1cf73 100644 --- a/resources/src/mediawiki.language/languages/hy.js +++ b/resources/src/mediawiki.language/languages/hy.js @@ -24,6 +24,6 @@ mediaWiki.language.convertGrammar = function ( word, form ) { word = word + 'ի'; } break; - } + } return word; }; diff --git a/resources/src/mediawiki.language/languages/os.js b/resources/src/mediawiki.language/languages/os.js index 787be36d..554e99d4 100644 --- a/resources/src/mediawiki.language/languages/os.js +++ b/resources/src/mediawiki.language/languages/os.js @@ -21,15 +21,14 @@ mediaWiki.language.convertGrammar = function ( word, form ) { if ( word.match( /тæ$/i ) ) { word = word.slice( 0, -1 ); endAllative = 'æм'; - } - // Works if word is in singular form. - // Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю, я. - else if ( word.match( /[аæеёиоыэюя]$/i ) ) { + } else if ( word.match( /[аæеёиоыэюя]$/i ) ) { + // Works if word is in singular form. + // Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю, я. jot = 'й'; - } - // Checking if word ends on 'у'. 'У' can be either consonant 'W' or vowel 'U' in cyrillic Ossetic. - // Examples: {{grammar:genitive|аунеу}} = аунеуы, {{grammar:genitive|лæппу}} = лæппуйы. - else if ( word.match( /у$/i ) ) { + } else if ( word.match( /у$/i ) ) { + // Checking if word ends on 'у'. 'У' can be either consonant 'W' or vowel 'U' in cyrillic Ossetic. + // Examples: {{grammar:genitive|аунеу}} = аунеуы, {{grammar:genitive|лæппу}} = лæппуйы. + if ( !word.slice( -2, -1 ).match( /[аæеёиоыэюя]$/i ) ) { jot = 'й'; } @@ -50,8 +49,7 @@ mediaWiki.language.convertGrammar = function ( word, form ) { case 'ablative': if ( jot === 'й' ) { ending = hyphen + jot + 'æ'; - } - else { + } else { ending = hyphen + jot + 'æй'; } break; diff --git a/resources/src/mediawiki.language/mediawiki.language.init.js b/resources/src/mediawiki.language/mediawiki.language.init.js index df95d751..b3765c85 100644 --- a/resources/src/mediawiki.language/mediawiki.language.init.js +++ b/resources/src/mediawiki.language/mediawiki.language.init.js @@ -54,6 +54,7 @@ */ getData: function ( langCode, dataKey ) { var langData = mw.language.data; + langCode = langCode.toLowerCase(); if ( langData && langData[langCode] instanceof mw.Map ) { return langData[langCode].get( dataKey ); } @@ -71,6 +72,7 @@ */ setData: function ( langCode, dataKey, value ) { var langData = mw.language.data; + langCode = langCode.toLowerCase(); if ( !( langData[langCode] instanceof mw.Map ) ) { langData[langCode] = new mw.Map(); } diff --git a/resources/src/mediawiki.language/mediawiki.language.js b/resources/src/mediawiki.language/mediawiki.language.js index d4f3c69e..78e39191 100644 --- a/resources/src/mediawiki.language/mediawiki.language.js +++ b/resources/src/mediawiki.language/mediawiki.language.js @@ -40,39 +40,18 @@ $.extend( mw.language, { * * @param {number} count Non-localized quantifier * @param {Array} forms List of plural forms + * @param {Object} [explicitPluralForms] List of explicit plural forms * @return {string} Correct form for quantifier in this language */ - convertPlural: function ( count, forms ) { + convertPlural: function ( count, forms, explicitPluralForms ) { 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.slice( 0, equalsPosition ), 10 ); - if ( formCount === count ) { - return form.slice( equalsPosition + 1 ); - } - forms[index] = undefined; - } + if ( explicitPluralForms && explicitPluralForms[count] ) { + return explicitPluralForms[count]; } - // Remove explicit plural forms from the forms. - forms = $.map( forms, function ( form ) { - return form; - } ); - - if ( forms.length === 0 ) { + if ( !forms || forms.length === 0 ) { return ''; } @@ -107,7 +86,7 @@ $.extend( mw.language, { * Usage in message text: `{{gender:[gender|user object]|masculine|feminine|neutral}}`. * If second or third parameter are not specified, masculine is used. * - * These details may be overriden per language. + * These details may be overridden per language. * * @param {string} gender 'male', 'female', or anything else for neutral. * @param {Array} forms List of gender forms @@ -155,7 +134,9 @@ $.extend( mw.language, { * @return {string} */ listToText: function ( list ) { - var text = '', i = 0; + var text = '', + i = 0; + for ( ; i < list.length; i++ ) { text += list[i]; if ( list.length - 2 === i ) { @@ -165,6 +146,10 @@ $.extend( mw.language, { } } return text; + }, + + setSpecialCharacters: function ( data ) { + this.specialCharacters = data; } } ); diff --git a/resources/src/mediawiki.language/mediawiki.language.numbers.js b/resources/src/mediawiki.language/mediawiki.language.numbers.js index a0b81410..3c13055b 100644 --- a/resources/src/mediawiki.language/mediawiki.language.numbers.js +++ b/resources/src/mediawiki.language/mediawiki.language.numbers.js @@ -187,8 +187,12 @@ tmp[ transformTable[ i ] ] = i; } transformTable = tmp; - numberString = num + ''; + numberString = String( num ); } else { + // Ignore transform table if wgTranslateNumerals is false + if ( !mw.config.get( 'wgTranslateNumerals' ) ) { + transformTable = []; + } numberString = mw.language.commafy( num, pattern ); } diff --git a/resources/src/mediawiki.language/specialcharacters.json b/resources/src/mediawiki.language/specialcharacters.json new file mode 100644 index 00000000..bab92a1b --- /dev/null +++ b/resources/src/mediawiki.language/specialcharacters.json @@ -0,0 +1 @@ +{"latin":["Á","á","À","à","Â","â","Ä","ä","Ã","ã","Ǎ","ǎ","Ā","ā","Ă","ă","Ą","ą","Å","å","Ć","ć","Ĉ","ĉ","Ç","ç","Č","č","Ċ","ċ","Đ","đ","Ď","ď","É","é","È","è","Ê","ê","Ë","ë","Ě","ě","Ē","ē","Ĕ","ĕ","Ė","ė","Ę","ę","Ĝ","ĝ","Ģ","ģ","Ğ","ğ","Ġ","ġ","Ĥ","ĥ","Ħ","ħ","Í","í","Ì","ì","Î","î","Ï","ï","Ĩ","ĩ","Ǐ","ǐ","Ī","ī","Ĭ","ĭ","İ","ı","Į","į","Ĵ","ĵ","Ķ","ķ","Ĺ","ĺ","Ļ","ļ","Ľ","ľ","Ł","ł","Ń","ń","Ñ","ñ","Ņ","ņ","Ň","ň","Ó","ó","Ò","ò","Ô","ô","Ö","ö","Õ","õ","Ǒ","ǒ","Ō","ō","Ŏ","ŏ","Ǫ","ǫ","Ő","ő","Ŕ","ŕ","Ŗ","ŗ","Ř","ř","Ś","ś","Ŝ","ŝ","Ş","ş","Š","š","Ș","ș","Ț","ț","Ť","ť","Ú","ú","Ù","ù","Û","û","Ü","ü","Ũ","ũ","Ů","ů","Ǔ","ǔ","Ū","ū","ǖ","ǘ","ǚ","ǜ","Ŭ","ŭ","Ų","ų","Ű","ű","Ŵ","ŵ","Ý","ý","Ŷ","ŷ","Ÿ","ÿ","Ȳ","ȳ","Ź","ź","Ž","ž","Ż","ż","Æ","æ","Ǣ","ǣ","Ø","ø","Œ","œ","ß","Ð","ð","Þ","þ","Ə","ə"],"latinextended":["Ḁ","ḁ","ẚ","Ạ","ạ","Ả","ả","Ấ","ấ","Ầ","ầ","Ẩ","ẩ","Ẫ","ẫ","Ậ","ậ","Ắ","ắ","Ằ","ằ","Ẳ","ẳ","Ẵ","ẵ","Ặ","ặ","Ḃ","ḃ","Ḅ","ḅ","Ḇ","ḇ","Ḉ","ḉ","Ḋ","ḋ","Ḍ","ḍ","Ḏ","ḏ","Ḑ","ḑ","Ḓ","ḓ","Ḕ","ḕ","Ḗ","ḗ","Ḙ","ḙ","Ḛ","ḛ","Ḝ","ḝ","Ẹ","ẹ","Ẻ","ẻ","Ẽ","ẽ","Ế","ế","Ề","ề","Ể","ể","Ễ","ễ","Ệ","ệ","Ḟ","ḟ","Ḡ","ḡ","Ḣ","ḣ","Ḥ","ḥ","Ḧ","ḧ","Ḩ","ḩ","Ḫ","ḫ","ẖ","Ḭ","ḭ","Ḯ","ḯ","Ỉ","ỉ","Ị","ị","Ḱ","ḱ","Ḳ","ḳ","Ḵ","ḵ","Ḷ","ḷ","Ḹ","ḹ","Ḻ","ḻ","Ḽ","ḽ","Ỻ","ỻ","Ḿ","ḿ","Ṁ","ṁ","Ṃ","ṃ","Ṅ","ṅ","Ṇ","ṇ","Ṉ","ṉ","Ṋ","ṋ","Ṍ","ṍ","Ṏ","ṏ","Ṑ","ṑ","Ṓ","ṓ","Ọ","ọ","Ỏ","ỏ","Ố","ố","Ồ","ồ","Ổ","ổ","Ỗ","ỗ","Ộ","ộ","Ớ","ớ","Ờ","ờ","Ở","ở","Ỡ","ỡ","Ợ","ợ","Ǿ","ǿ","Ơ","ơ","Ṕ","ṕ","Ṗ","ṗ","Ṙ","ṙ","Ṛ","ṛ","Ṝ","ṝ","Ṟ","ṟ","Ṡ","ṡ","ẛ","Ṣ","ṣ","Ṥ","ṥ","Ṧ","ṧ","Ṩ","ṩ","ẜ","ẝ","Ṫ","ṫ","Ṭ","ṭ","Ṯ","ṯ","Ṱ","ṱ","ẗ","Ṳ","ṳ","Ṵ","ṵ","Ṷ","ṷ","Ṹ","ṹ","Ṻ","ṻ","Ụ","ụ","Ủ","ủ","Ứ","ứ","Ừ","ừ","Ử","ử","Ữ","ữ","Ự","ự","Ư","ư","Ǖ","Ǘ","Ǚ","Ǜ","Ṽ","ṽ","Ṿ","ṿ","Ỽ","ỽ","Ẁ","ẁ","Ẃ","ẃ","Ẅ","ẅ","Ẇ","ẇ","Ẉ","ẉ","ẘ","Ẋ","ẋ","Ẍ","ẍ","Ẏ","ẏ","ẙ","Ỳ","ỳ","Ỵ","ỵ","Ỷ","ỷ","Ỹ","ỹ","Ỿ","ỿ","Ẑ","ẑ","Ẓ","ẓ","Ẕ","ẕ","Ǽ","ǽ","ẞ","ẟ"],"ipa":["p","t̪","t","ʈ","c","k","q","ʡ","ʔ","b","d̪","d","ɖ","ɟ","ɡ","ɢ","ɓ","ɗ","ʄ","ɠ","ʛ","t͡s","t͡ʃ","t͡ɕ","d͡z","d͡ʒ","d͡ʑ","ɸ","f","θ","s","ʃ","ʅ","ʆ","ʂ","ɕ","ç","ɧ","x","χ","ħ","ʜ","h","β","v","ʍ","ð","z","ʒ","ʓ","ʐ","ʑ","ʝ","ɣ","ʁ","ʕ","ʖ","ʢ","ɦ","ɬ","ɮ","m","m̩","ɱ","ɱ̩","ɱ̍","n̪","n̪̍","n","n̩","ɳ","ɳ̩","ɲ","ɲ̩","ŋ","ŋ̍","ŋ̩","ɴ","ɴ̩","ʙ","ʙ̩","r","r̩","ʀ","ʀ̩","ɾ","ɽ","ɿ","ɺ","l̪","l̪̩","l","l̩","ɫ","ɫ̩","ɭ","ɭ̩","ʎ","ʎ̩","ʟ","ʟ̩","w","ɥ","ʋ","ɹ","ɻ","j","ɰ","ʘ","ǂ","ǀ","!","ǁ","ʰ","ʱ","ʷ","ʸ","ʲ","ʳ","ⁿ","ˡ","ʴ","ʵ","ˢ","ˣ","ˠ","ʶ","ˤ","ˁ","ˀ","ʼ","i","i̯","ĩ","y","y̯","ỹ","ɪ","ɪ̯","ɪ̃","ʏ","ʏ̯","ʏ̃","ɨ","ɨ̯","ɨ̃","ʉ","ʉ̯","ʉ̃","ɯ","ɯ̯","ɯ̃","u","u̯","ũ","ʊ","ʊ̯","ʊ̃","e","e̯","ẽ","ø","ø̯","ø̃","ɘ","ɘ̯","ɘ̃","ɵ","ɵ̯","ɵ̃","ɤ","ɤ̯","ɤ̃","o","o̯","õ","ɛ","ɛ̯","ɛ̃","œ","œ̯","œ̃","ɜ","ɜ̯","ɜ̃","ə","ə̯","ə̃","ɞ","ɞ̯","ɞ̃","ʌ","ʌ̯","ʌ̃","ɔ","ɔ̯","ɔ̃","æ","æ̯","æ̃","ɶ","ɶ̯","ɶ̃","a","a̯","ã","ɐ","ɐ̯","ɐ̃","ɑ","ɑ̯","ɑ̃","ɒ","ɒ̯","ɒ̃","ˈ","ˌ","ː","ˑ","˘",".","‿","|","‖","ɚ","ɝ"],"symbols":["~","|","¡","¿","†","‡","↔","↑","↓","•","¶","#","½","⅓","⅔","¼","¾","⅛","⅜","⅝","⅞","∞","‘","’",{"label":"“”","action":{"type":"encapsulate","options":{"pre":"“","post":"”"}}},{"label":"„“","action":{"type":"encapsulate","options":{"pre":"„","post":"“"}}},{"label":"„”","action":{"type":"encapsulate","options":{"pre":"„","post":"”"}}},{"label":"«»","action":{"type":"encapsulate","options":{"pre":"«","post":"»"}}},"¤","₳","฿","₵","¢","₡","₢","$","₫","₯","€","₠","₣","ƒ","₴","₭","₤","ℳ","₥","₦","№","₧","₰","£","៛","₨","₪","৳","₮","₩","¥","♠","♣","♥","♦","m²","m³",{"label":"–","titleMsg":"special-characters-title-endash","action":{"type":"replace","options":{"peri":"–","selectPeri":false}}},{"label":"—","titleMsg":"special-characters-title-emdash","action":{"type":"replace","options":{"peri":"—","selectPeri":false}}},"…","‘","’","“","”","°","′","″","≈","≠","≤","≥","±",{"label":"−","titleMsg":"special-characters-title-minus","action":{"type":"replace","options":{"peri":"−","selectPeri":false}}},"×","÷","←","→","·","§","‽"],"greek":["Α","Ά","α","ά","Β","β","Γ","γ","Δ","δ","Ε","Έ","ε","έ","Ζ","ζ","Η","Ή","η","ή","Θ","θ","Ι","Ί","ι","ί","Κ","κ","Λ","λ","Μ","μ","Ν","ν","Ξ","ξ","Ο","Ό","ο","ό","Π","π","Ρ","ρ","Σ","σ","ς","Τ","τ","Υ","Ύ","υ","ύ","Φ","φ","Χ","χ","Ψ","ψ","Ω","Ώ","ω","ώ"],"cyrillic":["А","а","Ӑ","ӑ","Ӓ","ӓ","Ә","ә","Ӛ","ӛ","Б","б","В","в","Г","г","Ґ","ґ","Ӷ","ӷ","Ѓ","ѓ","Ӻ","ӻ","Ғ","ғ","Ҕ","ҕ","Д","д","Ԁ","ԁ","Ԃ","ԃ","Ђ","ђ","Е","е","Ѐ","ѐ","Є","є","Ё","ё","Ӗ","ӗ","Ҽ","ҽ","Ҿ","ҿ","Ж","ж","Җ","җ","Ӂ","ӂ","Ӝ","ӝ","З","з","Ҙ","ҙ","Ӟ","ӟ","Ԑ","ԑ","Ӡ","ӡ","Ѕ","ѕ","Ԅ","ԅ","Ԇ","ԇ","И","и","І","і","Ї","ї",["◌Ӏ","Ӏ"],["◌ӏ","ӏ"],"Й","й","Ӣ","ӣ","Ѝ","ѝ","Ҋ","ҋ","Ӥ","ӥ","Ј","ј","К","к","Ќ","ќ","Қ","қ","Ҝ","ҝ","Ҟ","ҟ","Ҡ","ҡ","Ӄ","ӄ","Ԛ","ԛ","Л","л","Љ","љ","Ԉ","ԉ","Ԓ","ԓ","Ӆ","ӆ","М","м","Ӎ","ӎ","Н","н","Њ","њ","Ң","ң","Ҥ","ҥ","Ӈ","ӈ","Ԋ","ԋ","Ӊ","ӊ","О","о","Ҩ","ҩ","Ӧ","ӧ","Ө","ө","Ӫ","ӫ","П","п","Ԥ","ԥ","Ҧ","ҧ","Р","р","Ҏ","ҏ","С","с","Ҫ","ҫ","Т","т","Ћ","ћ","Ԍ","ԍ","Ҭ","ҭ","Ԏ","ԏ","У","у","Ў","ў","Ӯ","ӯ","Ӱ","ӱ","Ӳ","ӳ","Ү","ү","Ұ","ұ","Ф","ф","Х","х","Ҳ","ҳ","Ӽ","ӽ","Ӿ","ӿ","Һ","һ","Ц","ц","Ч","ч","Ҵ","ҵ","Ҷ","ҷ","Ҹ","ҹ","Ӌ","ӌ","Ӵ","ӵ","Џ","џ","Ш","ш","Щ","щ","Ъ","ъ","Ы","ы","Ӹ","ӹ","Ь","ь","Ҍ","ҍ","Э","э","Ӭ","ӭ","Ю","ю","Я","я","Ԝ","ԝ","Ѡ","ѡ","Ѣ","ѣ","Ѥ","ѥ","Ѧ","ѧ","Ѩ","ѩ","Ѫ","ѫ","Ѭ","ѭ","Ѯ","ѯ","Ѱ","ѱ","Ѳ","ѳ","Ѵ","ѵ","Ѷ","ѷ","Ѹ","ѹ","Ѻ","ѻ","Ѽ","ѽ","Ѿ","ѿ","Ҁ","ҁ"],"arabic":["ا","ب","ت","ث","ج","ح","خ","د","ذ","ر","ز","س","ش","ص","ض","ط","ظ","ع","غ","ف","ق","ك","ل","م","ن","ه","و","ي","ء","آ","أ","إ","ٱ","ؤ","ئ","ى","ة","َ","ُ","ِ","ً","ٌ","ٍ","ّ","ْ","ٰ","،","؛","؟","ـ","٠","١","٢","٣","٤","٥","٦","٧","٨","٩","٪","٫","٬","٭",["ZWNJ","‌"],["ZWJ","‍"]],"arabicextended":["ٲ","ٳ","ٴ","ٵ","ݳ","ݴ","ٮ","ٻ","پ","ڀ","ݐ","ݑ","ݒ","ݓ","ݔ","ݕ","ݖ","ٹ","ٺ","ټ","ٽ","ٿ","ځ","ڂ","ڃ","ڄ","څ","چ","ڇ","ڿ","ݗ","ݘ","ݮ","ݯ","ݲ","ݼ","ڈ","ډ","ڊ","ڋ","ڌ","ڍ","ڎ","ڏ","ڐ","ۮ","ݙ","ݚ","ڑ","ڒ","ړ","ڔ","ڕ","ږ","ڗ","ژ","ڙ","ۯ","ݛ","ݫ","ݬ","ݱ","ښ","ڛ","ڜ","ݽ","ۺ","ݜ","ݭ","ݰ","ݾ","ڝ","ڞ","ۻ","ڟ","ڠ","ݝ","ݞ","ݟ","ۼ","ڡ","ڢ","ڣ","ڤ","ڥ","ڦ","ݠ","ݡ","ٯ","ڧ","ڨ","ػ","ؼ","ک","ڪ","ګ","ڬ","ڭ","ڮ","گ","ڰ","ڱ","ڲ","ڳ","ڴ","ݢ","ݣ","ݤ","ݿ","ڵ","ڶ","ڷ","ڸ","ݪ","ݥ","ݦ","ڹ","ں","ڻ","ڼ","ڽ","ݧ","ݨ","ݩ","ھ","ۀ","ہ","ۂ","ۃ","ە","ۿ","ٶ","ٷ","ۄ","ۅ","ۆ","ۇ","ۈ","ۉ","ۊ","ۋ","ۏ","ݸ","ݹ","ؠ","ؽ","ؾ","ؿ","ٸ","ی","ۍ","ێ","ې","ۑ","ے","ۓ","ݵ","ݶ","ݷ","ݺ","ݻ","ٖ","ٗ","٘","ٙ","ٚ","ٛ","ٜ","ٝ","ٞ","ٟ","۔","۽","۾","۰","۱","۲","۳","۴","۵","۶","۷","۸","۹"],"hebrew":["א","ב","ג","ד","ה","ו","ז","ח","ט","י","כ","ך","ל","מ","ם","נ","ן","ס","ע","פ","ף","צ","ץ","ק","ר","ש","ת","װ","ױ","ײ","׳","״","־","–",{"label":"„”","action":{"type":"encapsulate","options":{"pre":"„","post":"”"}}},{"label":"‚’","action":{"type":"encapsulate","options":{"pre":"‚","post":"’"}}},["◌ְ","ְ"],["◌ֱ","ֱ"],["◌ֲ","ֲ"],["◌ֳ","ֳ"],["◌ִ","ִ"],["◌ֵ","ֵ"],["◌ֶ","ֶ"],["◌ַ","ַ"],["◌ָ","ָ"],["◌ֹ","ֹ"],["◌ֻ","ֻ"],["◌ּ","ּ"],["◌ׁ","ׁ"],["◌ׂ","ׂ"],["◌ׇ","ׇ"],["◌֑","֑"],["◌֒","֒"],["◌֓","֓"],["◌֔","֔"],["◌֕","֕"],["◌֖","֖"],["◌֗","֗"],["◌֘","֘"],["◌֙","֙"],["◌֚","֚"],["◌֛","֛"],["◌֜","֜"],["◌֝","֝"],["◌֞","֞"],["◌֟","֟"],["◌֠","֠"],["◌֡","֡"],["◌֢","֢"],["◌֣","֣"],["◌֤","֤"],["◌֥","֥"],["◌֦","֦"],["◌֧","֧"],["◌֨","֨"],["◌֩","֩"],["◌֪","֪"],["◌֫","֫"],["◌֬","֬"],["◌֭","֭"],["◌֮","֮"],["◌֯","֯"],["◌ֿ","ֿ"],["◌׀","׀"],["◌׃","׃"]],"bangla":["অ","আ","ই","ঈ","উ","ঊ","ঋ","এ","ঐ","ও","ঔ","া","ি","ী","ু","ূ","ৃ","ে","ৈ","ো","ৌ","ক","খ","গ","ঘ","ঙ","চ","ছ","জ","ঝ","ঞ","ট","ঠ","ড","ঢ","ণ","ত","থ","দ","ধ","ন","প","ফ","ব","ভ","ম","য","র","ল","শ","ষ","স","হ","ড়","ঢ়","য়","ৎ","ং","ঃ","ঁ","্","১","২","৩","৪","৫","৬","৭","৮","৯","০"],"tamil":["௦","௧","௨","௩","௪","௫","௬","௭","௮","௯","௰","௱","௲","௳","௴","௵","௶","௷","௸","௹","௺","ௐ"],"telugu":["ఁ","ం","ః","అ","ఆ","ఇ","ఈ","ఉ","ఊ","ఋ","ౠ","ఌ","ౡ","ఎ","ఏ","ఐ","ఒ","ఓ","ఔ","క","ఖ","గ","ఘ","ఙ","చ","ఛ","జ","ఝ","ఞ","ట","ఠ","డ","ఢ","ణ","త","థ","ద","ధ","న","ప","ఫ","బ","భ","మ","య","ర","ఱ","ల","ళ","వ","శ","ష","స","హ","ా","ి","ీ","ు","ూ","ృ","ౄ","ె","ే","ై","ొ","ో","ౌ","్","ౢ","ౣ","ౘ","ౙ","౦","౧","౨","౩","౪","౫","౬","౭","౮","౯","ఽ","౸","౹","౺","౻","౼","౽","౾","౿"],"sinhala":["අ","ආ","ඇ","ඈ","ඉ","ඊ","උ","ඌ","ඍ","ඎ","ඏ","ඐ","එ","ඒ","ඓ","ඔ","ඕ","ඖ","ක","ඛ","ග","ඝ","ඞ","ඟ","ච","ඡ","ජ","ඣ","ඤ","ඥ","ඦ","ට","ඨ","ඩ","ඪ","ණ","ඬ","ත","ථ","ද","ධ","න","ඳ","ප","ඵ","බ","භ","ම","ඹ","ය","ර","ල","ව","ශ","ෂ","ස","හ","ළ","ෆ",["◌ා","ා"],["◌ැ","ැ"],["◌ෑ","ෑ"],["◌ි","ි"],["◌ී","ී"],["◌ු","ු"],["◌ූ","ූ"],["◌ෘ","ෘ"],["◌ෲ","ෲ"],["◌ෟ","ෟ"],["◌ෳ","ෳ"],["◌ෙ","ෙ"],["◌ේ","ේ"],["◌ො","ො"],["◌ෝ","ෝ"],["◌ෞ","ෞ"],["◌්","්"]],"devanagari":["ऀ","ँ","ं","ः","ऄ","अ","आ","इ","ई","उ","ऊ","ऋ","ऌ","ऍ","ऎ","ए","ऐ","ऑ","ऒ","ओ","औ","क","ख","ग","घ","ङ","च","छ","ज","झ","ञ","ट","ठ","ड","ढ","ण","त","थ","द","ध","न","ऩ","प","फ","ब","भ","म","य","र","ऱ","ल","ळ","ऴ","व","श","ष","स","ह","ऺ","ऻ","़","ऽ","ा","ि","ी","ु","ू","ृ","ॄ","ॅ","ॆ","े","ै","ॉ","ॊ","ो","ौ","्","ॎ","ॏ","ॐ","॑","॒","॓","॔","ॕ","ॖ","ॗ","क़","ख़","ग़","ज़","ड़","ढ़","फ़","य़","ॠ","ॡ","ॢ","ॣ","।","॥","०","१","२","३","४","५","६","७","८","९","॰","ॱ","ॲ","ॳ","ॴ","ॵ","ॶ","ॷ","ॹ","ॺ","ॻ","ॼ","ॽ","ॾ","ॿ"],"gujarati":["ૐ","ઁ","ં","ઃ","અ","આ","ઇ","ઈ","ઉ","ઊ","એ","ઐ","ઓ","ઔ","અં","ઋ","ઍ","ઑ","ઌ","ૠ","ૡ","ક","ખ","ગ","ઘ","ઙ","ચ","છ","જ","ઝ","ઞ","ટ","ઠ","ડ","ઢ","ણ","ત","થ","દ","ધ","ન","પ","ફ","બ","ભ","મ","ય","ર","લ","ળ","વ","શ","ષ","સ","હ","ક્ષ","જ્ઞ","ઽ","ા","િ","ી","ી","ુ","ૂ","ૃ","ૄ","ૅ","ે","ૈ","ૉ","ો","ૌ","ૢ","ૣ","્","૦","૧","૨","૩","૪","૫","૬","૭","૮","૯","૱"],"thai":["ก","ข","ฃ","ค","ฅ","ฆ","ง","จ","ฉ","ช","ซ","ฌ","ญ","ฎ","ฏ","ฐ","ฑ","ฒ","ณ","ด","ต","ถ","ท","ธ","น","บ","ป","ผ","ฝ","พ","ฟ","ภ","ม","ย","ร","ฤ","ล","ฦ","ว","ศ","ษ","ส","ห","ฬ","อ","ฮ","ะ","ั","า","ๅ","ำ","ิ","ี","ึ","ื","ุ","ู","เ","แ","โ","ใ","ไ","็","่","้","๊","๋","์","ํ","ฺ","๎","๐","๑","๒","๓","๔","๕","๖","๗","๘","๙","฿","ๆ","ฯ","๚","๏","๛"],"lao":["ກ","ຂ","ຄ","ງ","ຈ","ສ","ຊ","ຍ","ດ","ຕ","ຖ","ທ","ນ","ບ","ປ","ຜ","ຝ","ພ","ຟ","ມ","ຢ","ລ","ວ","ຫ","ອ","ຮ","ຣ","ໜ","ໝ","ຼ","ຽ","ະ","ັ","າ","ຳ","ິ","ີ","ຶ","ື","ຸ","ູ","ົ","ເ","ແ","ໂ","ໃ","ໄ","່","້","໊","໋","໌","ໍ","໐","໑","໒","໓","໔","໕","໖","໗","໘","໙","₭","ໆ","ຯ"],"khmer":["ក","ខ","គ","ឃ","ង","ច","ឆ","ជ","ឈ","ញ","ដ","ឋ","ឌ","ឍ","ណ","ត","ថ","ទ","ធ","ន","ប","ផ","ព","ភ","ម","យ","រ","ល","វ","ស","ហ","ឡ","អ","ឣ","ឤ","ឥ","ឦ","ឧ","ឨ","ឩ","ឪ","ឫ","ឬ","ឭ","ឮ","ឯ","ឰ","ឱ","ឲ","ឳ","្","឴","឵","ា","ិ","ី","ឹ","ឺ","ុ","ូ","ួ","ើ","ឿ","ៀ","េ","ែ","ៃ","ោ","ៅ","ំ","ះ","ៈ","៉","៊","់","៌","៍","៎","៏","័","៑","៓","៝","ៜ","០","១","២","៣","៤","៥","៦","៧","៨","៩","៛","។","៕","៖","ៗ","៘","៙","៚","៰","៱","៲","៳","៴","៵","៶","៷","៸","៹","᧠","᧡","᧢","᧣","᧤","᧥","᧦","᧧","᧨","᧩","᧪","᧫","᧬","᧭","᧮","᧯","᧰","᧱","᧲","᧳","᧴","᧵","᧶","᧷","᧸","᧹","᧺","᧻","᧼","᧽","᧾","᧿"]} \ No newline at end of file diff --git a/resources/src/mediawiki.legacy/ajax.js b/resources/src/mediawiki.legacy/ajax.js index 6b9464a9..3660c205 100644 --- a/resources/src/mediawiki.legacy/ajax.js +++ b/resources/src/mediawiki.legacy/ajax.js @@ -5,190 +5,190 @@ * http://www.modernmethod.com/sajax/ */ -/*jshint camelcase:false */ +/*jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ /*global alert */ ( function ( mw ) { -/** - * if sajax_debug_mode is true, this function outputs given the message into - * the element with id = sajax_debug; if no such element exists in the document, - * it is injected. - */ -function debug( text ) { - if ( !window.sajax_debug_mode ) { - return false; - } + /** + * if sajax_debug_mode is true, this function outputs given the message into + * the element with id = sajax_debug; if no such element exists in the document, + * it is injected. + */ + function debug( text ) { + if ( !window.sajax_debug_mode ) { + return false; + } - var b, m, - e = document.getElementById( 'sajax_debug' ); + var b, m, + e = document.getElementById( 'sajax_debug' ); - if ( !e ) { - e = document.createElement( 'p' ); - e.className = 'sajax_debug'; - e.id = 'sajax_debug'; + if ( !e ) { + e = document.createElement( 'p' ); + e.className = 'sajax_debug'; + e.id = 'sajax_debug'; - b = document.getElementsByTagName( 'body' )[0]; + b = document.getElementsByTagName( 'body' )[0]; - if ( b.firstChild ) { - b.insertBefore( e, b.firstChild ); - } else { - b.appendChild( e ); + if ( b.firstChild ) { + b.insertBefore( e, b.firstChild ); + } else { + b.appendChild( e ); + } } - } - m = document.createElement( 'div' ); - m.appendChild( document.createTextNode( text ) ); + m = document.createElement( 'div' ); + m.appendChild( document.createTextNode( text ) ); - e.appendChild( m ); + e.appendChild( m ); - return true; -} + return true; + } -/** - * Compatibility wrapper for creating a new XMLHttpRequest object. - */ -function createXhr() { - debug( 'sajax_init_object() called..' ); - var a; - try { - // Try the new style before ActiveX so we don't - // unnecessarily trigger warnings in IE 7 when - // set to prompt about ActiveX usage - a = new XMLHttpRequest(); - } catch ( xhrE ) { + /** + * Compatibility wrapper for creating a new XMLHttpRequest object. + */ + function createXhr() { + debug( 'sajax_init_object() called..' ); + var a; try { - a = new window.ActiveXObject( 'Msxml2.XMLHTTP' ); - } catch ( msXmlE ) { + // Try the new style before ActiveX so we don't + // unnecessarily trigger warnings in IE 7 when + // set to prompt about ActiveX usage + a = new XMLHttpRequest(); + } catch ( xhrE ) { try { - a = new window.ActiveXObject( 'Microsoft.XMLHTTP' ); - } catch ( msXhrE ) { - a = null; + a = new window.ActiveXObject( 'Msxml2.XMLHTTP' ); + } catch ( msXmlE ) { + try { + a = new window.ActiveXObject( 'Microsoft.XMLHTTP' ); + } catch ( msXhrE ) { + a = null; + } } } - } - if ( !a ) { - debug( 'Could not create connection object.' ); - } + if ( !a ) { + debug( 'Could not create connection object.' ); + } - return a; -} + return a; + } -/** - * Perform an AJAX call to MediaWiki. Calls are handled by AjaxDispatcher.php - * func_name - the name of the function to call. Must be registered in $wgAjaxExportList - * args - an array of arguments to that function - * target - the target that will handle the result of the call. If this is a function, - * if will be called with the XMLHttpRequest as a parameter; if it's an input - * element, its value will be set to the resultText; if it's another type of - * element, its innerHTML will be set to the resultText. - * - * Example: - * sajax_do_call( 'doFoo', [1, 2, 3], document.getElementById( 'showFoo' ) ); - * - * This will call the doFoo function via MediaWiki's AjaxDispatcher, with - * (1, 2, 3) as the parameter list, and will show the result in the element - * with id = showFoo - */ -function doAjaxRequest( func_name, args, target ) { - var i, x, uri, post_data; - uri = mw.util.wikiScript() + '?action=ajax'; - if ( window.sajax_request_type === 'GET' ) { - if ( uri.indexOf( '?' ) === -1 ) { - uri = uri + '?rs=' + encodeURIComponent( func_name ); + /** + * Perform an AJAX call to MediaWiki. Calls are handled by AjaxDispatcher.php + * func_name - the name of the function to call. Must be registered in $wgAjaxExportList + * args - an array of arguments to that function + * target - the target that will handle the result of the call. If this is a function, + * if will be called with the XMLHttpRequest as a parameter; if it's an input + * element, its value will be set to the resultText; if it's another type of + * element, its innerHTML will be set to the resultText. + * + * Example: + * sajax_do_call( 'doFoo', [1, 2, 3], document.getElementById( 'showFoo' ) ); + * + * This will call the doFoo function via MediaWiki's AjaxDispatcher, with + * (1, 2, 3) as the parameter list, and will show the result in the element + * with id = showFoo + */ + function doAjaxRequest( func_name, args, target ) { + var i, x, uri, post_data; + uri = mw.util.wikiScript() + '?action=ajax'; + if ( window.sajax_request_type === 'GET' ) { + if ( uri.indexOf( '?' ) === -1 ) { + uri = uri + '?rs=' + encodeURIComponent( func_name ); + } else { + uri = uri + '&rs=' + encodeURIComponent( func_name ); + } + for ( i = 0; i < args.length; i++ ) { + uri = uri + '&rsargs[]=' + encodeURIComponent( args[i] ); + } + // uri = uri + '&rsrnd=' + new Date().getTime(); + post_data = null; } else { - uri = uri + '&rs=' + encodeURIComponent( func_name ); - } - for ( i = 0; i < args.length; i++ ) { - uri = uri + '&rsargs[]=' + encodeURIComponent( args[i] ); + post_data = 'rs=' + encodeURIComponent( func_name ); + for ( i = 0; i < args.length; i++ ) { + post_data = post_data + '&rsargs[]=' + encodeURIComponent( args[i] ); + } } - //uri = uri + '&rsrnd=' + new Date().getTime(); - post_data = null; - } else { - post_data = 'rs=' + encodeURIComponent( func_name ); - for ( i = 0; i < args.length; i++ ) { - post_data = post_data + '&rsargs[]=' + encodeURIComponent( args[i] ); + x = createXhr(); + if ( !x ) { + alert( 'AJAX not supported' ); + return false; } - } - x = createXhr(); - if ( !x ) { - alert( 'AJAX not supported' ); - return false; - } - try { - x.open( window.sajax_request_type, uri, true ); - } catch ( e ) { - if ( location.hostname === 'localhost' ) { - alert( 'Your browser blocks XMLHttpRequest to "localhost", try using a real hostname for development/testing.' ); + try { + x.open( window.sajax_request_type, uri, true ); + } catch ( e ) { + if ( location.hostname === 'localhost' ) { + alert( 'Your browser blocks XMLHttpRequest to "localhost", try using a real hostname for development/testing.' ); + } + throw e; } - throw e; - } - if ( window.sajax_request_type === 'POST' ) { - x.setRequestHeader( 'Method', 'POST ' + uri + ' HTTP/1.1' ); - x.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); - } - x.setRequestHeader( 'Pragma', 'cache=yes' ); - x.setRequestHeader( 'Cache-Control', 'no-transform' ); - x.onreadystatechange = function () { - if ( x.readyState !== 4 ) { - return; + if ( window.sajax_request_type === 'POST' ) { + x.setRequestHeader( 'Method', 'POST ' + uri + ' HTTP/1.1' ); + x.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); } + x.setRequestHeader( 'Pragma', 'cache=yes' ); + x.setRequestHeader( 'Cache-Control', 'no-transform' ); + x.onreadystatechange = function () { + if ( x.readyState !== 4 ) { + return; + } - debug( 'received (' + x.status + ' ' + x.statusText + ') ' + x.responseText ); + debug( 'received (' + x.status + ' ' + x.statusText + ') ' + x.responseText ); - //if ( x.status != 200 ) - // alert( 'Error: ' + x.status + ' ' + x.statusText + ': ' + x.responseText ); - //else + // if ( x.status != 200 ) + // alert( 'Error: ' + x.status + ' ' + x.statusText + ': ' + x.responseText ); + // else - if ( typeof target === 'function' ) { - target( x ); - } else if ( typeof target === 'object' ) { - if ( target.tagName === 'INPUT' ) { - if ( x.status === 200 ) { - target.value = x.responseText; - } - //else alert( 'Error: ' + x.status + ' ' + x.statusText + ' (' + x.responseText + ')' ); - } else { - if ( x.status === 200 ) { - target.innerHTML = x.responseText; + if ( typeof target === 'function' ) { + target( x ); + } else if ( typeof target === 'object' ) { + if ( target.tagName === 'INPUT' ) { + if ( x.status === 200 ) { + target.value = x.responseText; + } + // else alert( 'Error: ' + x.status + ' ' + x.statusText + ' (' + x.responseText + ')' ); } else { - target.innerHTML = '
    Error: ' + x.status + - ' ' + x.statusText + ' (' + x.responseText + ')
    '; + if ( x.status === 200 ) { + target.innerHTML = x.responseText; + } else { + target.innerHTML = '
    Error: ' + x.status + + ' ' + x.statusText + ' (' + x.responseText + ')
    '; + } } + } else { + alert( 'Bad target for sajax_do_call: not a function or object: ' + target ); } - } else { - alert( 'Bad target for sajax_do_call: not a function or object: ' + target ); - } - }; + }; + + debug( func_name + ' uri = ' + uri + ' / post = ' + post_data ); + x.send( post_data ); + debug( func_name + ' waiting..' ); - debug( func_name + ' uri = ' + uri + ' / post = ' + post_data ); - x.send( post_data ); - debug( func_name + ' waiting..' ); + return true; + } - return true; -} + /** + * @return {boolean} Whether the browser supports AJAX + */ + function wfSupportsAjax() { + var request = createXhr(), + supportsAjax = request ? true : false; -/** - * @return {boolean} Whether the browser supports AJAX - */ -function wfSupportsAjax() { - var request = createXhr(), - supportsAjax = request ? true : false; - - request = undefined; - return supportsAjax; -} - -// Expose + Mark as deprecated -var deprecationNotice = 'Sajax is deprecated, use jQuery.ajax or mediawiki.api instead.'; - -// Variables -mw.log.deprecate( window, 'sajax_debug_mode', false, deprecationNotice ); -mw.log.deprecate( window, 'sajax_request_type', 'GET', deprecationNotice ); -// Methods -mw.log.deprecate( window, 'sajax_debug', debug, deprecationNotice ); -mw.log.deprecate( window, 'sajax_init_object', createXhr, deprecationNotice ); -mw.log.deprecate( window, 'sajax_do_call', doAjaxRequest, deprecationNotice ); -mw.log.deprecate( window, 'wfSupportsAjax', wfSupportsAjax, deprecationNotice ); + request = undefined; + return supportsAjax; + } + + // Expose + Mark as deprecated + var deprecationNotice = 'Sajax is deprecated, use jQuery.ajax or mediawiki.api instead.'; + + // Variables + mw.log.deprecate( window, 'sajax_debug_mode', false, deprecationNotice ); + mw.log.deprecate( window, 'sajax_request_type', 'GET', deprecationNotice ); + // Methods + mw.log.deprecate( window, 'sajax_debug', debug, deprecationNotice ); + mw.log.deprecate( window, 'sajax_init_object', createXhr, deprecationNotice ); + mw.log.deprecate( window, 'sajax_do_call', doAjaxRequest, deprecationNotice ); + mw.log.deprecate( window, 'wfSupportsAjax', wfSupportsAjax, deprecationNotice ); }( mediaWiki ) ); diff --git a/resources/src/mediawiki.legacy/commonPrint.css b/resources/src/mediawiki.legacy/commonPrint.css index 830b02fa..9a8d3918 100644 --- a/resources/src/mediawiki.legacy/commonPrint.css +++ b/resources/src/mediawiki.legacy/commonPrint.css @@ -23,7 +23,6 @@ div#column-one, #toc.tochidden, div#f-poweredbyico, div#f-copyrightico, -li#viewcount, li#about, li#disclaimer, li#mobileview, @@ -35,6 +34,7 @@ span.mw-filepage-other-resolutions, #filetoc, .usermessage, .patrollink, +.ns-0 .mw-redirectedfrom, #mw-navigation, #siteNotice { display: none; @@ -203,6 +203,7 @@ a.stub { /** * Floating divs */ +/* @noflip */ div.floatright { float: right; clear: right; @@ -214,6 +215,7 @@ div.floatright p { font-style: italic; } +/* @noflip */ div.floatleft { float: left; clear: left; @@ -322,44 +324,6 @@ div.gallerytext { word-wrap: break-word; } -/** - * Diff rendering - */ -table.diff { - background: white; -} - -td.diff-otitle { - background: #ffffff; -} - -td.diff-ntitle { - background: #ffffff; -} - -td.diff-addedline { - background: #ccffcc; - font-size: smaller; - border: solid 2px black; -} - -td.diff-deletedline { - background: #ffffaa; - font-size: smaller; - border: dotted 2px black; -} - -td.diff-context { - background: #eeeeee; - font-size: smaller; -} - -.diffchange { - color: silver; - font-weight: bold; - text-decoration: underline; -} - /** * Table rendering * As on shared.css but with white background. diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png new file mode 100644 index 00000000..712b1b48 Binary files /dev/null and b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png differ diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg new file mode 100644 index 00000000..4d3dcb65 --- /dev/null +++ b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png new file mode 100644 index 00000000..1d03a8c0 Binary files /dev/null and b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png differ diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg new file mode 100644 index 00000000..582e4ae7 --- /dev/null +++ b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/src/mediawiki.legacy/oldshared.css b/resources/src/mediawiki.legacy/oldshared.css index d92d3bb9..c2bd5a73 100644 --- a/resources/src/mediawiki.legacy/oldshared.css +++ b/resources/src/mediawiki.legacy/oldshared.css @@ -119,8 +119,12 @@ div.magnify a { /* …and replace it with the image */ width: 15px; height: 11px; - /* @embed */ + /* Use same SVG support hack as mediawiki.legacy's shared.css */ background: url(images/magnify-clip-ltr.png) center center no-repeat; + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); + /* @embed */ + background-image: linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); /* Don't annoy people who copy-paste everything too much */ -moz-user-select: none; -webkit-user-select: none; diff --git a/resources/src/mediawiki.legacy/protect.js b/resources/src/mediawiki.legacy/protect.js index f9069b6f..3f4b263e 100644 --- a/resources/src/mediawiki.legacy/protect.js +++ b/resources/src/mediawiki.legacy/protect.js @@ -6,7 +6,8 @@ var ProtectionForm = window.ProtectionForm = { * on the protection form */ init: function () { - var $cell = $( '' ), $row = $( '' ).append( $cell ); + var $cell = $( '' ), + $row = $( '' ).append( $cell ); if ( !$( '#mwProtectSet' ).length ) { return false; @@ -63,7 +64,7 @@ var ProtectionForm = window.ProtectionForm = { }, /** - * Checks if a cerain protection level is cascadeable. + * Checks if a certain protection level is cascadeable. * * @param {string} level * @return {boolean} diff --git a/resources/src/mediawiki.legacy/shared.css b/resources/src/mediawiki.legacy/shared.css index 0604773e..3657b127 100644 --- a/resources/src/mediawiki.legacy/shared.css +++ b/resources/src/mediawiki.legacy/shared.css @@ -91,6 +91,18 @@ abbr[title], color: #aaa; /* gray */ } +/* + * Bidi-isolate these numbers. + * See https://phabricator.wikimedia.org/T93484 + */ +.mw-plusminus-pos, +.mw-plusminus-neg, +.mw-plusminus-null { + unicode-bidi: -moz-isolate; + unicode-bidi: -webkit-isolate; + unicode-bidi: isolate; +} + /** * Links to redirects appear italicized on [[Special:AllPages]], [[Special:PrefixIndex]], * [[Special:Watchlist/edit]] and in category listings. @@ -414,7 +426,7 @@ p.mw-upload-editlicenses { border: 1px dashed #aaa; } -.mw-history-revisiondelete-button, #mw-fileduplicatesearch-icon { +.mw-history-revisionactions, #mw-fileduplicatesearch-icon { float: right; } @@ -506,7 +518,7 @@ a.feedlink { table.wikitable { margin: 1em 0; background-color: #f9f9f9; - border: 1px #aaa solid; + border: 1px solid #aaa; border-collapse: collapse; color: black; } @@ -515,8 +527,8 @@ table.wikitable > tr > th, table.wikitable > tr > td, table.wikitable > * > tr > th, table.wikitable > * > tr > td { - border: 1px #aaa solid; - padding: 0.2em; + border: 1px solid #aaa; + padding: 0.2em 0.4em; } table.wikitable > tr > th, @@ -584,7 +596,7 @@ table.wikitable > caption { } .successbox { - color: #009000; + color: #008000; border-color: #b7fdb5; background-color: #e1fddf; } @@ -934,11 +946,14 @@ h2:lang(te), h3:lang(te), h4:lang(te), h5:lang(te), h6:lang(te) { /* Localised ordered list numbering for some languages */ ol:lang(bcc) li, +ol:lang(bgn) li, ol:lang(bqi) li, ol:lang(fa) li, ol:lang(glk) li, ol:lang(kk-arab) li, -ol:lang(mzn) li { +ol:lang(lrc) li, +ol:lang(mzn) li, +ol:lang(sdh) li { list-style-type: -moz-persian; list-style-type: persian; } @@ -1124,6 +1139,7 @@ table.floatleft { .mw-editsection, .toctoggle, +.tochidden, #jump-to-nav { -moz-user-select: none; -webkit-user-select: none; diff --git a/resources/src/mediawiki.legacy/wikibits.js b/resources/src/mediawiki.legacy/wikibits.js index a4039966..32cd79a5 100644 --- a/resources/src/mediawiki.legacy/wikibits.js +++ b/resources/src/mediawiki.legacy/wikibits.js @@ -5,200 +5,214 @@ var msg, win = window, ua = navigator.userAgent.toLowerCase(), - onloadFuncts = []; - -/** - * User-agent sniffing. - * - * @deprecated since 1.17 Use jquery.client instead - */ - -msg = 'Use feature detection or module jquery.client instead.'; - -mw.log.deprecate( win, 'clientPC', ua, msg ); - -// Ignored dummy values -mw.log.deprecate( win, 'is_gecko', false, msg ); -mw.log.deprecate( win, 'is_chrome_mac', false, msg ); -mw.log.deprecate( win, 'is_chrome', false, msg ); -mw.log.deprecate( win, 'webkit_version', false, msg ); -mw.log.deprecate( win, 'is_safari_win', false, msg ); -mw.log.deprecate( win, 'is_safari', false, msg ); -mw.log.deprecate( win, 'webkit_match', false, msg ); -mw.log.deprecate( win, 'is_ff2', false, msg ); -mw.log.deprecate( win, 'ff2_bugs', false, msg ); -mw.log.deprecate( win, 'is_ff2_win', false, msg ); -mw.log.deprecate( win, 'is_ff2_x11', false, msg ); -mw.log.deprecate( win, 'opera95_bugs', false, msg ); -mw.log.deprecate( win, 'opera7_bugs', false, msg ); -mw.log.deprecate( win, 'opera6_bugs', false, msg ); -mw.log.deprecate( win, 'is_opera_95', false, msg ); -mw.log.deprecate( win, 'is_opera_preseven', false, msg ); -mw.log.deprecate( win, 'is_opera', false, msg ); -mw.log.deprecate( win, 'ie6_bugs', false, msg ); - -/** - * DOM utilities for handling of events, text nodes and selecting elements - * - * @deprecated since 1.17 Use jQuery instead - */ -msg = 'Use jQuery instead.'; - -// Ignored dummy values -mw.log.deprecate( win, 'doneOnloadHook', undefined, msg ); -mw.log.deprecate( win, 'onloadFuncts', [], msg ); -mw.log.deprecate( win, 'runOnloadHook', $.noop, msg ); -mw.log.deprecate( win, 'changeText', $.noop, msg ); -mw.log.deprecate( win, 'killEvt', $.noop, msg ); -mw.log.deprecate( win, 'addHandler', $.noop, msg ); -mw.log.deprecate( win, 'hookEvent', $.noop, msg ); -mw.log.deprecate( win, 'addClickHandler', $.noop, msg ); -mw.log.deprecate( win, 'removeHandler', $.noop, msg ); -mw.log.deprecate( win, 'getElementsByClassName', function () { return []; }, msg ); -mw.log.deprecate( win, 'getInnerText', function () { return ''; }, msg ); - -// Run a function after the window onload event is fired -mw.log.deprecate( win, 'addOnloadHook', function ( hookFunct ) { - if ( onloadFuncts ) { - onloadFuncts.push(hookFunct); - } else { - // If func queue is gone the event has happened already, - // run immediately instead of queueing. - hookFunct(); + onloadFuncts = [], + loadedScripts = {}; + + /** + * User-agent sniffing. + * + * @deprecated since 1.17 Use jquery.client instead + */ + + msg = 'Use feature detection or module jquery.client instead.'; + + mw.log.deprecate( win, 'clientPC', ua, msg ); + + // Ignored dummy values + mw.log.deprecate( win, 'is_gecko', false, msg ); + mw.log.deprecate( win, 'is_chrome_mac', false, msg ); + mw.log.deprecate( win, 'is_chrome', false, msg ); + mw.log.deprecate( win, 'webkit_version', false, msg ); + mw.log.deprecate( win, 'is_safari_win', false, msg ); + mw.log.deprecate( win, 'is_safari', false, msg ); + mw.log.deprecate( win, 'webkit_match', false, msg ); + mw.log.deprecate( win, 'is_ff2', false, msg ); + mw.log.deprecate( win, 'ff2_bugs', false, msg ); + mw.log.deprecate( win, 'is_ff2_win', false, msg ); + mw.log.deprecate( win, 'is_ff2_x11', false, msg ); + mw.log.deprecate( win, 'opera95_bugs', false, msg ); + mw.log.deprecate( win, 'opera7_bugs', false, msg ); + mw.log.deprecate( win, 'opera6_bugs', false, msg ); + mw.log.deprecate( win, 'is_opera_95', false, msg ); + mw.log.deprecate( win, 'is_opera_preseven', false, msg ); + mw.log.deprecate( win, 'is_opera', false, msg ); + mw.log.deprecate( win, 'ie6_bugs', false, msg ); + + /** + * DOM utilities for handling of events, text nodes and selecting elements + * + * @deprecated since 1.17 Use jQuery instead + */ + msg = 'Use jQuery instead.'; + + // Ignored dummy values + mw.log.deprecate( win, 'doneOnloadHook', undefined, msg ); + mw.log.deprecate( win, 'onloadFuncts', [], msg ); + mw.log.deprecate( win, 'runOnloadHook', $.noop, msg ); + mw.log.deprecate( win, 'changeText', $.noop, msg ); + mw.log.deprecate( win, 'killEvt', $.noop, msg ); + mw.log.deprecate( win, 'addHandler', $.noop, msg ); + mw.log.deprecate( win, 'hookEvent', $.noop, msg ); + mw.log.deprecate( win, 'addClickHandler', $.noop, msg ); + mw.log.deprecate( win, 'removeHandler', $.noop, msg ); + mw.log.deprecate( win, 'getElementsByClassName', function () { return []; }, msg ); + mw.log.deprecate( win, 'getInnerText', function () { return ''; }, msg ); + + // Run a function after the window onload event is fired + mw.log.deprecate( win, 'addOnloadHook', function ( hookFunct ) { + if ( onloadFuncts ) { + onloadFuncts.push( hookFunct ); + } else { + // If func queue is gone the event has happened already, + // run immediately instead of queueing. + hookFunct(); + } + }, msg ); + + $( win ).on( 'load', function () { + var i, functs; + + // Don't run twice + if ( !onloadFuncts ) { + return; + } + + // Deference and clear onloadFuncts before running any + // hooks to make sure we don't miss any addOnloadHook + // calls. + functs = onloadFuncts.slice(); + onloadFuncts = undefined; + + // Execute the queued functions + for ( i = 0; i < functs.length; i++ ) { + functs[i](); + } + } ); + + /** + * Toggle checkboxes with shift selection + * + * @deprecated since 1.17 Use jquery.checkboxShiftClick instead + */ + msg = 'Use jquery.checkboxShiftClick instead.'; + mw.log.deprecate( win, 'checkboxes', [], msg ); + mw.log.deprecate( win, 'lastCheckbox', null, msg ); + mw.log.deprecate( win, 'setupCheckboxShiftClick', $.noop, msg ); + mw.log.deprecate( win, 'addCheckboxClickHandlers', $.noop, msg ); + mw.log.deprecate( win, 'checkboxClickHandler', $.noop, msg ); + + /** + * Add a button to the default editor toolbar + * + * @deprecated since 1.17 Use mw.toolbar instead + */ + mw.log.deprecate( win, 'mwEditButtons', [], 'Use mw.toolbar instead.' ); + mw.log.deprecate( win, 'mwCustomEditButtons', [], 'Use mw.toolbar instead.' ); + + /** + * Spinner creation, injection and removal + * + * @deprecated since 1.18 Use jquery.spinner instead + */ + mw.log.deprecate( win, 'injectSpinner', $.noop, 'Use jquery.spinner instead.' ); + mw.log.deprecate( win, 'removeSpinner', $.noop, 'Use jquery.spinner instead.' ); + + /** + * Escape utilities + * + * @deprecated since 1.18 Use mw.html instead + */ + mw.log.deprecate( win, 'escapeQuotes', $.noop, 'Use mw.html instead.' ); + mw.log.deprecate( win, 'escapeQuotesHTML', $.noop, 'Use mw.html instead.' ); + + /** + * Display a message to the user + * + * @deprecated since 1.17 Use mediawiki.notify instead + * @param {string|HTMLElement} message To be put inside the message box + */ + mw.log.deprecate( win, 'jsMsg', function ( message ) { + if ( !arguments.length || message === '' || message === null ) { + return true; + } + if ( typeof message !== 'object' ) { + message = $.parseHTML( message ); + } + mw.notify( message, { autoHide: true, tag: 'legacy' } ); + return true; + }, 'Use mediawiki.notify instead.' ); + + /** + * Misc. utilities + * + * @deprecated since 1.17 Use mediawiki.util or jquery.accessKeyLabel instead + */ + msg = 'Use mediawiki.util instead.'; + mw.log.deprecate( win, 'addPortletLink', mw.util.addPortletLink, msg ); + mw.log.deprecate( win, 'appendCSS', mw.util.addCSS, msg ); + msg = 'Use jquery.accessKeyLabel instead.'; + mw.log.deprecate( win, 'tooltipAccessKeyPrefix', 'alt-', msg ); + mw.log.deprecate( win, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, msg ); + // mw.util.updateTooltipAccessKeys already generates a deprecation message. + win.updateTooltipAccessKeys = function () { + return mw.util.updateTooltipAccessKeys.apply( null, arguments ); + }; + + /** + * Wikipage import methods + * + * See https://www.mediawiki.org/wiki/ResourceLoader/Legacy_JavaScript#wikibits.js + */ + + function importScript( page ) { + var uri = mw.config.get( 'wgScript' ) + '?title=' + + mw.util.wikiUrlencode( page ) + + '&action=raw&ctype=text/javascript'; + return importScriptURI( uri ); } -}, msg ); - -$( win ).on( 'load', function () { - var i, functs; - // Don't run twice - if ( !onloadFuncts ) { - return; + /** + * @deprecated since 1.17 Use mw.loader instead. Warnings added in 1.25. + */ + function importScriptURI( url ) { + if ( loadedScripts[url] ) { + return null; + } + loadedScripts[url] = true; + var s = document.createElement( 'script' ); + s.setAttribute( 'src', url ); + s.setAttribute( 'type', 'text/javascript' ); + document.getElementsByTagName( 'head' )[0].appendChild( s ); + return s; } - // Deference and clear onloadFuncts before running any - // hooks to make sure we don't miss any addOnloadHook - // calls. - functs = onloadFuncts.slice(); - onloadFuncts = undefined; - - // Execute the queued functions - for ( i = 0; i < functs.length; i++ ) { - functs[i](); + function importStylesheet( page ) { + var uri = mw.config.get( 'wgScript' ) + '?title=' + + mw.util.wikiUrlencode( page ) + + '&action=raw&ctype=text/css'; + return importStylesheetURI( uri ); } -} ); - -/** - * Toggle checkboxes with shift selection - * - * @deprecated since 1.17 Use jquery.checkboxShiftClick instead - */ -msg = 'Use jquery.checkboxShiftClick instead.'; -mw.log.deprecate( win, 'checkboxes', [], msg ); -mw.log.deprecate( win, 'lastCheckbox', null, msg ); -mw.log.deprecate( win, 'setupCheckboxShiftClick', $.noop, msg ); -mw.log.deprecate( win, 'addCheckboxClickHandlers', $.noop, msg ); -mw.log.deprecate( win, 'checkboxClickHandler', $.noop, msg ); - -/** - * Add a button to the default editor toolbar - * - * @deprecated since 1.17 Use mw.toolbar instead - */ -mw.log.deprecate( win, 'mwEditButtons', [], 'Use mw.toolbar instead.' ); -mw.log.deprecate( win, 'mwCustomEditButtons', [], 'Use mw.toolbar instead.' ); - -/** - * Spinner creation, injection and removal - * - * @deprecated since 1.18 Use jquery.spinner instead - */ -mw.log.deprecate( win, 'injectSpinner', $.noop, 'Use jquery.spinner instead.' ); -mw.log.deprecate( win, 'removeSpinner', $.noop, 'Use jquery.spinner instead.' ); -/** - * Escape utilities - * - * @deprecated since 1.18 Use mw.html instead - */ -mw.log.deprecate( win, 'escapeQuotes', $.noop, 'Use mw.html instead.' ); -mw.log.deprecate( win, 'escapeQuotesHTML', $.noop, 'Use mw.html instead.' ); - -/** - * Display a message to the user - * - * @deprecated since 1.17 Use mediawiki.notify instead - * @param {string|HTMLElement} message To be put inside the message box - */ -mw.log.deprecate( win, 'jsMsg', function ( message ) { - if ( !arguments.length || message === '' || message === null ) { - return true; + /** + * @deprecated since 1.17 Use mw.loader instead. Warnings added in 1.25. + */ + function importStylesheetURI( url, media ) { + var l = document.createElement( 'link' ); + l.rel = 'stylesheet'; + l.href = url; + if ( media ) { + l.media = media; + } + document.getElementsByTagName( 'head' )[0].appendChild( l ); + return l; } - if ( typeof message !== 'object' ) { - message = $.parseHTML( message ); - } - mw.notify( message, { autoHide: true, tag: 'legacy' } ); - return true; -}, 'Use mediawiki.notify instead.' ); - -/** - * Misc. utilities - * - * @deprecated since 1.17 Use mediawiki.util or jquery.accessKeyLabel instead - */ -msg = 'Use mediawiki.util instead.'; -mw.log.deprecate( win, 'addPortletLink', mw.util.addPortletLink, msg ); -mw.log.deprecate( win, 'appendCSS', mw.util.addCSS, msg ); -msg = 'Use jquery.accessKeyLabel instead.'; -mw.log.deprecate( win, 'tooltipAccessKeyPrefix', 'alt-', msg ); -mw.log.deprecate( win, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, msg ); -// mw.util.updateTooltipAccessKeys already generates a deprecation message. -win.updateTooltipAccessKeys = function () { - return mw.util.updateTooltipAccessKeys.apply( null, arguments ); -}; - -/** - * Wikipage import methods - */ -// included-scripts tracker -win.loadedScripts = {}; - -win.importScript = function ( page ) { - var uri = mw.config.get( 'wgScript' ) + '?title=' + - mw.util.wikiUrlencode( page ) + - '&action=raw&ctype=text/javascript'; - return win.importScriptURI( uri ); -}; - -win.importScriptURI = function ( url ) { - if ( win.loadedScripts[url] ) { - return null; - } - win.loadedScripts[url] = true; - var s = document.createElement( 'script' ); - s.setAttribute( 'src', url ); - s.setAttribute( 'type', 'text/javascript' ); - document.getElementsByTagName( 'head' )[0].appendChild( s ); - return s; -}; - -win.importStylesheet = function ( page ) { - var uri = mw.config.get( 'wgScript' ) + '?title=' + - mw.util.wikiUrlencode( page ) + - '&action=raw&ctype=text/css'; - return win.importStylesheetURI( uri ); -}; - -win.importStylesheetURI = function ( url, media ) { - var l = document.createElement( 'link' ); - l.rel = 'stylesheet'; - l.href = url; - if ( media ) { - l.media = media; - } - document.getElementsByTagName('head')[0].appendChild( l ); - return l; -}; + msg = 'Use mw.loader instead.'; + mw.log.deprecate( win, 'loadedScripts', loadedScripts, msg ); + mw.log.deprecate( win, 'importScriptURI', importScriptURI, msg ); + mw.log.deprecate( win, 'importStylesheetURI', importStylesheetURI, msg ); + // Not quite deprecated yet. + win.importScript = importScript; + win.importStylesheet = importStylesheet; }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.less/mediawiki.mixins.less b/resources/src/mediawiki.less/mediawiki.mixins.less index c360e1f4..3366f1e1 100644 --- a/resources/src/mediawiki.less/mediawiki.mixins.less +++ b/resources/src/mediawiki.less/mediawiki.mixins.less @@ -1,19 +1,26 @@ -/** - * 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 for more information about how to write mixins. - */ +// 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 for more information about how to write mixins. .background-image(@url) { background-image: e('/* @embed */') url(@url); } +.background-size(@width, @height) { + // Vendor prefix for certain older opera browsers e.g. nintendo ds + -o-background-size: @width @height; + // Vendor prefix is added to support Android 2 + -webkit-background-size: @width @height; + background-size: @width @height; +} + + .vertical-gradient(@startColor: gray, @endColor: white, @startPos: 0, @endPos: 100%) { background-color: @endColor; background-image: -moz-linear-gradient( top, @startColor @startPos, @endColor @endPos ); // Firefox 3.6+ @@ -22,26 +29,32 @@ background-image: linear-gradient( @startColor @startPos, @endColor @endPos ); // Standard } -/* - * SVG support using a transparent gradient to guarantee cross-browser - * compatibility (browsers able to understand gradient syntax support also SVG). - * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique - * - * We use gzip compression, which means that it is okay to embed twice. - * - * We do not embed the fallback image on the assumption that the gain for old browsers - * is not worth the harm done to modern ones. - */ +// SVG support using a transparent gradient to guarantee cross-browser +// compatibility (browsers able to understand gradient syntax support also SVG). +// http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique +// +// We use gzip compression, which means that it is okay to embed twice. +// +// We do not embed the fallback image on the assumption that the gain for old browsers +// is not worth the harm done to modern ones. .background-image-svg(@svg, @fallback) { background-image: url(@fallback); background-image: -webkit-linear-gradient(transparent, transparent), e('/* @embed */') url(@svg); background-image: linear-gradient(transparent, transparent), e('/* @embed */') url(@svg); + // Do not serve SVG to Opera 12, bad rendering with border-radius or background-size (T87504) + background-image: -o-linear-gradient(transparent, transparent), url(@fallback); } .list-style-image(@url) { list-style-image: e('/* @embed */') url(@url); } +.list-style-image-svg(@svg, @fallback) { + list-style-image: e('/* @embed */') url(@svg); + /* Fallback to PNG bullet for IE 8 and below using CSS hack */ + list-style-image: e('/* @embed */') url(@fallback)\9; +} + .transition(@value) { -webkit-transition: @value; // Safari 3.1-6.0, iOS 3.2-6.1, Android 2.1-4.3 -moz-transition: @value; // Firefox 4-15 @@ -59,3 +72,21 @@ -webkit-box-shadow: @value; // Safari 3.1-5.0, iOS 3.2-4.3, Android 2.1-3.0 box-shadow: @value; // Chrome 10+, Firefox 4+, IE 9+, Safari 5.1+, Opera 11+, iOS 5+, Android 4+ } + +.column-count(@value) { + -webkit-column-count: @value; + -moz-column-count: @value; + column-count: @value; +} + +.column-width(@value) { + -webkit-column-width: @value;// Chrome Any, Safari 3+, Opera 11.1+ + -moz-column-width: @value;// Firefox 1.5+ + column-width: @value;// IE 10+ +} + +.column-break-inside-avoid { + -webkit-column-break-inside: avoid; // Chrome Any, Safari 3+, Opera 11.1+ + page-break-inside: avoid; // Firefox 1.5+ + break-inside: avoid-column; // IE 10+ +} diff --git a/resources/src/mediawiki.less/mediawiki.ui/mixins.less b/resources/src/mediawiki.less/mediawiki.ui/mixins.less index ec9888f2..2d684572 100644 --- a/resources/src/mediawiki.less/mediawiki.ui/mixins.less +++ b/resources/src/mediawiki.less/mediawiki.ui/mixins.less @@ -36,11 +36,16 @@ .button-colors(@bgColor) { background: @bgColor; - &:hover, - &:focus { + &:hover { // The inner bottom bevel should match the active background color. box-shadow: 0 1px rgba(0, 0, 0, 10%), inset 0 -3px rgba(0, 0, 0, 20%); border-bottom-color: mix(#000, @bgColor, 20%); + } + + &:focus { + border-color: rgba(0,0,0,0.2); + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.2); + outline: none; // remove outline in Firefox &::-moz-focus-inner { @@ -62,6 +67,13 @@ color: @colorButtonText; border: 1px solid @colorGray12; + &:hover, + &:active, + &:visited { + // make sure that is isn't inheriting from a general rule + color: @colorButtonText; + } + &:disabled { color: @colorDisabledText; @@ -77,16 +89,13 @@ .button-colors(@bgColor) when (lightness(@bgColor) < 70%) { color: #fff; // border of the same color as background so that light background and - // dark background buttons are the same height (only top and bottom to - // make box shadow on hover cover the corners too) + // dark background buttons are the same height and width border: 1px solid @bgColor; - border-left: none; - border-right: none; text-shadow: 0 1px rgba(0, 0, 0, .1); &:disabled { - background: @colorGray12; - border-color: @colorGray12; + background: @colorGray13; + border-color: @colorGray13; // make sure disabled buttons don't have hover and active states &:hover, @@ -104,9 +113,7 @@ &:hover, &:focus { - // lessphp doesn't implement tint, see above - // color: tint(@textColor, 20%); - color: mix(#fff, @textColor, 20%); + color: @textColor; } &:active, diff --git a/resources/src/mediawiki.libs/CLDRPluralRuleParser.js b/resources/src/mediawiki.libs/CLDRPluralRuleParser.js index 83c25245..31c8fef9 100644 --- a/resources/src/mediawiki.libs/CLDRPluralRuleParser.js +++ b/resources/src/mediawiki.libs/CLDRPluralRuleParser.js @@ -1,12 +1,13 @@ -/* This is CLDRPluralRuleParser v1.1, ported to MediaWiki ResourceLoader */ +/* This is CLDRPluralRuleParser v1.1.3, ported to MediaWiki ResourceLoader */ /** * CLDRPluralRuleParser.js * A parser engine for CLDR plural rules. * -* Copyright 2012 GPLV3+, Santhosh Thottingal +* Copyright 2012-2014 Santhosh Thottingal and other contributors +* Released under the MIT license +* http://opensource.org/licenses/MIT * -* @version 0.1.0-alpha * @source https://github.com/santhoshtr/CLDRPluralRuleParser * @author Santhosh Thottingal * @author Timo Tijhof @@ -22,6 +23,8 @@ */ function pluralRuleParser(rule, number) { + 'use strict'; + /* Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules ----------------------------------------------------------------- @@ -44,14 +47,15 @@ function pluralRuleParser(rule, number) { decimalValue = value ('.' value)? */ - // we don't evaluate the samples section of the rule. Ignore it. + // We don't evaluate the samples section of the rule. Ignore it. rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, ''); if (!rule.length) { - // empty rule or 'other' rule. + // Empty rule or 'other' rule. return true; } - // Indicates current position in the rule as we parse through it. + + // Indicates the current position in the rule as we parse through it. // Shared among all parsing functions below. var pos = 0, operand, @@ -87,15 +91,18 @@ function pluralRuleParser(rule, number) { debug('pluralRuleParser', rule, number); // Try parsers until one works, if none work return null - function choice(parserSyntax) { return function() { - for (var i = 0; i < parserSyntax.length; i++) { - var result = parserSyntax[i](); + var i, result; + + for (i = 0; i < parserSyntax.length; i++) { + result = parserSyntax[i](); + if (result !== null) { return result; } } + return null; }; } @@ -103,46 +110,56 @@ function pluralRuleParser(rule, number) { // Try several parserSyntax-es in a row. // All must succeed; otherwise, return null. // This is the only eager one. - function sequence(parserSyntax) { - var originalPos = pos; - var result = []; - for (var i = 0; i < parserSyntax.length; i++) { - var res = parserSyntax[i](); - if (res === null) { + var i, parserRes, + originalPos = pos, + result = []; + + for (i = 0; i < parserSyntax.length; i++) { + parserRes = parserSyntax[i](); + + if (parserRes === null) { pos = originalPos; + return null; } - result.push(res); + + result.push(parserRes); } + return result; } // Run the same parser over and over until it fails. // Must succeed a minimum of n times; otherwise, return null. - function nOrMore(n, p) { return function() { - var originalPos = pos; - var result = []; - var parsed = p(); + var originalPos = pos, + result = [], + parsed = p(); + while (parsed !== null) { result.push(parsed); parsed = p(); } + if (result.length < n) { pos = originalPos; + return null; } + return result; }; } - // Helpers -- just make parserSyntax out of simpler JS builtin types + // Helpers - just make parserSyntax out of simpler JS builtin types function makeStringParser(s) { var len = s.length; + return function() { var result = null; + if (rule.substr(pos, len) === s) { result = s; pos += len; @@ -155,95 +172,122 @@ function pluralRuleParser(rule, number) { function makeRegexParser(regex) { return function() { var matches = rule.substr(pos).match(regex); + if (matches === null) { return null; } + pos += matches[0].length; + return matches[0]; }; } - /* - * integer digits of n. + /** + * Integer digits of n. */ function i() { var result = _i_(); + if (result === null) { debug(' -- failed i', parseInt(number, 10)); + return result; } + result = parseInt(number, 10); debug(' -- passed i ', result); + return result; } - /* - * absolute value of the source number (integer and decimals). + /** + * Absolute value of the source number (integer and decimals). */ function n() { var result = _n_(); + if (result === null) { debug(' -- failed n ', number); + return result; } + result = parseFloat(number, 10); debug(' -- passed n ', result); + return result; } - /* - * visible fractional digits in n, with trailing zeros. + /** + * Visible fractional digits in n, with trailing zeros. */ function f() { var result = _f_(); + if (result === null) { debug(' -- failed f ', number); + return result; } + result = (number + '.').split('.')[1] || 0; debug(' -- passed f ', result); + return result; } - /* - * visible fractional digits in n, without trailing zeros. + /** + * Visible fractional digits in n, without trailing zeros. */ function t() { var result = _t_(); + if (result === null) { debug(' -- failed t ', number); + return result; } + result = (number + '.').split('.')[1].replace(/0$/, '') || 0; debug(' -- passed t ', result); + return result; } - /* - * number of visible fraction digits in n, with trailing zeros. + /** + * Number of visible fraction digits in n, with trailing zeros. */ function v() { var result = _v_(); + if (result === null) { debug(' -- failed v ', number); + return result; } + result = (number + '.').split('.')[1].length || 0; debug(' -- passed v ', result); + return result; } - /* - * number of visible fraction digits in n, without trailing zeros. + /** + * Number of visible fraction digits in n, without trailing zeros. */ function w() { var result = _w_(); + if (result === null) { debug(' -- failed w ', number); + return result; } + result = (number + '.').split('.')[1].replace(/0$/, '').length || 0; debug(' -- passed w ', result); + return result; } @@ -254,19 +298,27 @@ function pluralRuleParser(rule, number) { expression = choice([mod, operand]); function mod() { - var result = sequence([operand, whitespace, choice([_mod_, _percent_]), whitespace, value]); + var result = sequence( + [operand, whitespace, choice([_mod_, _percent_]), whitespace, value] + ); + if (result === null) { debug(' -- failed mod'); + return null; } + debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10)); + return parseInt(result[0], 10) % parseInt(result[4], 10); } function not() { var result = sequence([whitespace, _not_]); + if (result === null) { debug(' -- failed not'); + return null; } @@ -276,120 +328,170 @@ function pluralRuleParser(rule, number) { // is_relation = expr 'is' ('not')? value function is() { var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]); + if (result !== null) { debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10)); + return result[0] === parseInt(result[4], 10); } + debug(' -- failed is'); + return null; } // is_relation = expr 'is' ('not')? value function isnot() { - var result = sequence([expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]); + var result = sequence( + [expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value] + ); + if (result !== null) { debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10)); + return result[0] !== parseInt(result[4], 10); } + debug(' -- failed isnot'); + return null; } function not_in() { - var result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]); + var i, range_list, + result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]); + if (result !== null) { debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]); - var range_list = result[4]; - for (var i = 0; i < range_list.length; i++) { + range_list = result[4]; + + for (i = 0; i < range_list.length; i++) { if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) { return false; } } + return true; } + debug(' -- failed not_in'); + return null; } // range_list = (range | value) (',' range_list)* function rangeList() { - var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]); - var resultList = []; + var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]), + resultList = []; + if (result !== null) { resultList = resultList.concat(result[0]); + if (result[1][0]) { resultList = resultList.concat(result[1][0]); } + return resultList; } + debug(' -- failed rangeList'); + return null; } function rangeTail() { // ',' range_list var result = sequence([_comma_, rangeList]); + if (result !== null) { return result[1]; } + debug(' -- failed rangeTail'); + return null; } // range = value'..'value - function range() { - var i; - var result = sequence([value, _range_, value]); + var i, array, left, right, + result = sequence([value, _range_, value]); + if (result !== null) { debug(' -- passed range'); - var array = []; - var left = parseInt(result[0], 10); - var right = parseInt(result[2], 10); + + array = []; + left = parseInt(result[0], 10); + right = parseInt(result[2], 10); + for (i = left; i <= right; i++) { array.push(i); } + return array; } + debug(' -- failed range'); + return null; } function _in() { + var result, range_list, i; + // in_relation = expr ('not')? 'in' range_list - var result = sequence([expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]); + result = sequence( + [expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList] + ); + if (result !== null) { debug(' -- passed _in:' + result); - var range_list = result[5]; - for (var i = 0; i < range_list.length; i++) { + + range_list = result[5]; + + for (i = 0; i < range_list.length; i++) { if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) { return (result[1][0] !== 'not'); } } + return (result[1][0] === 'not'); } + debug(' -- failed _in '); + return null; } - /* - * The difference between in and within is that in only includes integers in the specified range, - * while within includes all values. + /** + * The difference between "in" and "within" is that + * "in" only includes integers in the specified range, + * while "within" includes all values. */ - function within() { + var range_list, result; + // within_relation = expr ('not')? 'within' range_list - var result = sequence([expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]); + result = sequence( + [expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList] + ); + if (result !== null) { debug(' -- passed within'); - var range_list = result[5]; + + range_list = result[5]; + if ((result[0] >= parseInt(range_list[0], 10)) && (result[0] < parseInt(range_list[range_list.length - 1], 10))) { + return (result[1][0] !== 'not'); } + return (result[1][0] === 'not'); } + debug(' -- failed within '); + return null; } @@ -398,65 +500,83 @@ function pluralRuleParser(rule, number) { // and_condition = relation ('and' relation)* function and() { - var result = sequence([relation, nOrMore(0, andTail)]); + var i, + result = sequence([relation, nOrMore(0, andTail)]); + if (result) { if (!result[0]) { return false; } - for (var i = 0; i < result[1].length; i++) { + + for (i = 0; i < result[1].length; i++) { if (!result[1][i]) { return false; } } + return true; } + debug(' -- failed and'); + return null; } // ('and' relation)* function andTail() { var result = sequence([whitespace, _and_, whitespace, relation]); + if (result !== null) { debug(' -- passed andTail' + result); + return result[3]; } + debug(' -- failed andTail'); + return null; } // ('or' and_condition)* function orTail() { var result = sequence([whitespace, _or_, whitespace, and]); + if (result !== null) { debug(' -- passed orTail: ' + result[3]); + return result[3]; } + debug(' -- failed orTail'); - return null; + return null; } // condition = and_condition ('or' and_condition)* function condition() { - var result = sequence([and, nOrMore(0, orTail)]); + var i, + result = sequence([and, nOrMore(0, orTail)]); + if (result) { - for (var i = 0; i < result[1].length; i++) { + for (i = 0; i < result[1].length; i++) { if (result[1][i]) { return true; } } - return result[0]; + return result[0]; } + return false; } result = condition(); - /* + + /** * For success, the pos must have gotten to the end of the rule * and returned a non-null. - * n.b. This is part of language infrastructure, so we do not throw an internationalizable message. + * n.b. This is part of language infrastructure, + * so we do not throw an internationalizable message. */ if (result === null) { throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule); diff --git a/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.MessagePoster.js b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.MessagePoster.js new file mode 100644 index 00000000..91366ff5 --- /dev/null +++ b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.MessagePoster.js @@ -0,0 +1,38 @@ +/*global OO*/ +( function ( mw ) { + /** + * This is the abstract base class for MessagePoster implementations. + * + * @abstract + * @class + * + * @constructor + * @param {mw.Title} title Title to post to + */ + mw.messagePoster.MessagePoster = function MwMessagePoster() {}; + + OO.initClass( mw.messagePoster.MessagePoster ); + + /** + * Post a message (with subject and body) to a talk page. + * + * @param {string} subject Subject/topic title; plaintext only (no wikitext or HTML) + * @param {string} body Body, as wikitext. Signature code will automatically be added + * by MessagePosters that require one, unless the message already contains the string + * ~~~. + * @return {jQuery.Promise} Promise completing when the post succeeds or fails. + * For failure, will be rejected with three arguments: + * + * - primaryError - Primary error code. For a mw.Api failure, + * this should be 'api-fail'. + * - secondaryError - Secondary error code. For a mw.Api failure, + * this, should be mw.Api's code, e.g. 'http', 'ok-but-empty', or the error passed through + * from the server. + * - details - Further details about the error + * + * @localdoc + * The base class currently does nothing, but could be used for shared analytics or + * something. + */ + mw.messagePoster.MessagePoster.prototype.post = function () {}; +}( mediaWiki ) ); diff --git a/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.WikitextMessagePoster.js b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.WikitextMessagePoster.js new file mode 100644 index 00000000..296576b4 --- /dev/null +++ b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.WikitextMessagePoster.js @@ -0,0 +1,53 @@ +/*global OO*/ +( function ( mw, $ ) { + /** + * This is an implementation of MessagePoster for wikitext talk pages. + * + * @class mw.messagePoster.WikitextMessagePoster + * @extends mw.messagePoster.MessagePoster + * + * @constructor + * @param {mw.Title} title Wikitext page in a talk namespace, to post to + */ + function WikitextMessagePoster( title ) { + this.api = new mw.Api(); + this.title = title; + } + + OO.inheritClass( + WikitextMessagePoster, + mw.messagePoster.MessagePoster + ); + + /** + * @inheritdoc + */ + WikitextMessagePoster.prototype.post = function ( subject, body ) { + mw.messagePoster.WikitextMessagePoster.parent.prototype.post.call( this, subject, body ); + + // Add signature if needed + if ( body.indexOf( '~~~' ) === -1 ) { + body += '\n\n~~~~'; + } + + return this.api.newSection( + this.title, + subject, + body, + { redirect: true } + ).then( function ( resp, jqXHR ) { + if ( resp.edit.result === 'Success' ) { + return $.Deferred().resolve( resp, jqXHR ); + } else { + // mediawiki.api.js checks for resp.error. Are there actually cases where the + // request fails, but it's not caught there? + return $.Deferred().reject( 'api-unexpected' ); + } + }, function ( code, details ) { + return $.Deferred().reject( 'api-fail', code, details ); + } ).promise(); + }; + + mw.messagePoster.factory.register( 'wikitext', WikitextMessagePoster ); + mw.messagePoster.WikitextMessagePoster = WikitextMessagePoster; +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js new file mode 100644 index 00000000..9d280800 --- /dev/null +++ b/resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js @@ -0,0 +1,109 @@ +/*global OO*/ +( function ( mw, $ ) { + /** + * This is a factory for MessagePoster objects, which allows a pluggable to way to script leaving a + * talk page message. + * + * @class mw.messagePoster.factory + * @singleton + */ + function MwMessagePosterFactory() { + this.api = new mw.Api(); + this.contentModelToClass = {}; + } + + OO.initClass( MwMessagePosterFactory ); + + // Note: This registration scheme is currently not compatible with LQT, since that doesn't + // have its own content model, just islqttalkpage. LQT pages will be passed to the wikitext + // MessagePoster. + /** + * Registers a MessagePoster subclass for a given content model. + * + * @param {string} contentModel Content model of pages this MessagePoster can post to + * @param {Function} messagePosterConstructor Constructor for MessagePoster + */ + MwMessagePosterFactory.prototype.register = function ( contentModel, messagePosterConstructor ) { + if ( this.contentModelToClass[contentModel] !== undefined ) { + throw new Error( 'The content model \'' + contentModel + '\' is already registered.' ); + } + + this.contentModelToClass[contentModel] = messagePosterConstructor; + }; + + /** + * Unregisters a given content model + * This is exposed for testing and should not normally be needed. + * + * @param {string} contentModel Content model to unregister + */ + MwMessagePosterFactory.prototype.unregister = function ( contentModel ) { + delete this.contentModelToClass[contentModel]; + }; + + /** + * Creates a MessagePoster, given a title. A promise for this is returned. + * This works by determining the content model, then loading the corresponding + * module (which will register the MessagePoster class), and finally constructing it. + * + * This does not require the message and should be called as soon as possible, so it does the + * API and ResourceLoader requests in the background. + * + * @param {mw.Title} title Title that will be posted to + * @return {jQuery.Promise} Promise resolving to a mw.messagePoster.MessagePoster. + * For failure, rejected with up to three arguments: + * + * - errorCode Error code string + * - error Error explanation + * - details Further error details + */ + MwMessagePosterFactory.prototype.create = function ( title ) { + var pageId, page, contentModel, moduleName, + factory = this; + + return this.api.get( { + action: 'query', + prop: 'info', + indexpageids: 1, + titles: title.getPrefixedDb() + } ).then( function ( result ) { + if ( result.query.pageids.length > 0 ) { + pageId = result.query.pageids[0]; + page = result.query.pages[pageId]; + + contentModel = page.contentmodel; + moduleName = 'mediawiki.messagePoster.' + contentModel; + return mw.loader.using( moduleName ).then( function () { + return factory.createForContentModel( + contentModel, + title + ); + }, function () { + return $.Deferred().reject( 'failed-to-load-module', 'Failed to load the \'' + moduleName + '\' module' ); + } ); + } else { + return $.Deferred().reject( 'unexpected-response', 'Unexpected API response' ); + } + }, function ( errorCode, details ) { + return $.Deferred().reject( 'content-model-query-failed', errorCode, details ); + } ).promise(); + }; + + /** + * Creates a MessagePoster instance, given a title and content model + * + * @private + * + * @param {string} contentModel Content model of title + * @param {mw.Title} title Title being posted to + * @return {mw.messagePoster.MessagePoster} + * + */ + MwMessagePosterFactory.prototype.createForContentModel = function ( contentModel, title ) { + return new this.contentModelToClass[contentModel]( title ); + }; + + mw.messagePoster = { + factory: new MwMessagePosterFactory() + }; +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.page/mediawiki.page.gallery.js b/resources/src/mediawiki.page/mediawiki.page.gallery.js index 1892967a..95140704 100644 --- a/resources/src/mediawiki.page/mediawiki.page.gallery.js +++ b/resources/src/mediawiki.page/mediawiki.page.gallery.js @@ -2,211 +2,266 @@ * Show gallery captions when focused. Copied directly from jquery.mw-jump.js. * Also Dynamically resize images to justify them. */ -( function ( $ ) { - $( function () { - var isTouchScreen, - gettingFocus, - galleries = 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed'; - +( function ( mw, $ ) { + var $galleries, + bound = false, // 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 ); + 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 ); - } ); - } + /** + * Perform the layout justification. + * @ignore + * @context {HTMLElement} A `ul.mw-gallery-*` element + */ + function justify() { + var lastTop, + $img, + imgWidth, + imgHeight, + captionWidth, + rows = [], + $gallery = $( this ); - // 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; - } + $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 ); - $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(); - } + if ( top !== lastTop ) { + rows[rows.length] = []; + lastTop = top; + } - // Hack to make an edge case work ok - if ( imgHeight < 30 ) { - // Don't try and resize this item. - imgHeight = 0; - } + $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(); + } - rows[rows.length - 1][rows[rows.length - 1].length] = { - $elm: $this, - width: $this.outerWidth(), - imgWidth: imgWidth, - // XXX: can divide by 0 ever happen? - aspect: imgWidth / imgHeight, - captionWidth: $this.children().children( 'div.gallerytextwrapper' ).width(), - height: imgHeight - }; - } ); + // Hack to make an edge case work ok + if ( imgHeight < 30 ) { + // Don't try and resize this item. + imgHeight = 0; + } - ( function () { - var maxWidth, - combinedAspect, - combinedPadding, - curRow, - curRowHeight, - wantedWidth, - preferredHeight, - newWidth, - padding, - $outerDiv, - $innerDiv, - $imageDiv, - $imageElm, - imageElm, - $caption, - 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; - } - } + captionWidth = $this.children().children( 'div.gallerytextwrapper' ).width(); + rows[rows.length - 1][rows[rows.length - 1].length] = { + $elm: $this, + width: $this.outerWidth(), + imgWidth: imgWidth, + // XXX: can divide by 0 ever happen? + aspect: imgWidth / imgHeight, + captionWidth: captionWidth, + height: imgHeight + }; - if ( curRow[j].aspect === 0 || !isFinite( 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; - } - } + // Save all boundaries so we can restore them on window resize + $this.data( 'imgWidth', imgWidth ); + $this.data( 'imgHeight', imgHeight ); + $this.data( 'width', $this.outerWidth() ); + $this.data( 'captionWidth', captionWidth ); + } ); - // 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. - 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; + ( function () { + var maxWidth, + combinedAspect, + combinedPadding, + curRow, + curRowHeight, + wantedWidth, + preferredHeight, + newWidth, + padding, + $outerDiv, + $innerDiv, + $imageDiv, + $imageElm, + imageElm, + $caption, + 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 ( !isFinite( preferredHeight ) ) { - // This *definitely* should not happen. - // Skip this row. - continue; - } - if ( preferredHeight < 5 ) { - // Well something clearly went wrong... - // Skip this row. - continue; - } - if ( preferredHeight / curRowHeight > 1 ) { - totalZoom += preferredHeight / curRowHeight; + if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) { + // One of the dimensions are 0. Probably should + // not try to resize. + combinedPadding += curRow[j].width; } else { - // If we shrink, still consider that a zoom of 1 - totalZoom += 1; + combinedAspect += curRow[j].aspect; + combinedPadding += curRow[j].width - curRow[j].imgWidth; } + } - 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, - 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; - } + // 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. + 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 { - $outerDiv.width( newWidth + padding ); - $innerDiv.width( newWidth + padding ); - $imageDiv.width( newWidth ); - $caption.width( curRow[j].captionWidth + ( newWidth - curRow[j].imgWidth ) ); + // Probably a single row gallery + preferredHeight = curRowHeight; } + } else { + preferredHeight = 1.5 * curRowHeight; + } + } + if ( !isFinite( preferredHeight ) ) { + // This *definitely* should not happen. + // Skip this row. + continue; + } + if ( preferredHeight < 5 ) { + // Well something clearly went wrong... + // Skip this row. + continue; + } - 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 ); + 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, + 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 ) ); + } + + 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 ); } } - }() ); + } + }() ); + } + + function handleResizeStart() { + $galleries.children( 'li' ).each( function () { + var imgWidth = $( this ).data( 'imgWidth' ), + imgHeight = $( this ).data( 'imgHeight' ), + width = $( this ).data( 'width' ), + captionWidth = $( this ).data( 'captionWidth' ), + $innerDiv = $( this ).children( 'div' ).first(), + $imageDiv = $innerDiv.children( 'div.thumb' ), + $imageElm, imageElm; + + // Restore original sizes so we can arrange the elements as on freshly loaded page + $( this ).width( width ); + $innerDiv.width( width ); + $imageDiv.width( imgWidth ); + $( this ).find( 'div.gallerytextwrapper' ).width( captionWidth ); + + $imageElm = $( this ).find( 'img' ).first(); + imageElm = $imageElm.length ? $imageElm[0] : null; + if ( imageElm ) { + imageElm.width = imgWidth; + imageElm.height = imgHeight; + } else { + $imageDiv.height( imgHeight ); + } + } ); + } + + function handleResizeEnd() { + $galleries.each( justify ); + } + + mw.hook( 'wikipage.content' ).add( function ( $content ) { + if ( isTouchScreen ) { + // Always show the caption for a touch screen. + $content.find( '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 + $content.find( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) { + // Confusingly jQuery leaves e.type as focusout for delegated blur events + var gettingFocus = e.type !== 'blur' && e.type !== 'focusout'; + $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus ); + } ); + } + + $galleries = $content.find( 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed' ); + // Call the justification asynchronous because live preview fires the hook with detached $content. + setTimeout( function () { + $galleries.each( justify ); + + // Bind here instead of in the top scope as the callbacks use $galleries. + if ( !bound ) { + bound = true; + $( window ) + .resize( $.debounce( 300, true, handleResizeStart ) ) + .resize( $.debounce( 300, handleResizeEnd ) ); + } } ); } ); -}( jQuery ) ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.page/mediawiki.page.image.pagination.js b/resources/src/mediawiki.page/mediawiki.page.image.pagination.js index 622e818d..9ad9c30a 100644 --- a/resources/src/mediawiki.page/mediawiki.page.image.pagination.js +++ b/resources/src/mediawiki.page/mediawiki.page.image.pagination.js @@ -2,23 +2,66 @@ * Implement AJAX navigation for multi-page images so the user may browse without a full page reload. */ ( function ( mw, $ ) { - var jqXhr, $multipageimage, $spinner; + var jqXhr, $multipageimage, $spinner, + cache = {}, + cacheOrder = []; - /* Fetch the next page and use jQuery to swap the table.multipageimage contents. + /* Fetch the next page, caching up to 10 last-loaded pages. * @param {string} url - * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if - * true, this function won't push a new history state, for the browser did so already). + * @return {jQuery.Promise} */ - function loadPage( url, hist ) { - var $tr; - if ( jqXhr ) { + function fetchPageData( url ) { + if ( jqXhr && jqXhr.abort ) { // Prevent race conditions and piling up pending requests jqXhr.abort(); - jqXhr = undefined; } + jqXhr = undefined; + + // Try the cache + if ( cache[url] ) { + // Update access freshness + cacheOrder.splice( $.inArray( url, cacheOrder ), 1 ); + cacheOrder.push( url ); + return $.Deferred().resolve( cache[url] ).promise(); + } + + // @todo Don't fetch the entire page. Ideally we'd only fetch the content portion or the data + // (thumbnail urls) and update the interface manually. + jqXhr = $.ajax( url ).then( function ( data ) { + return $( data ).find( 'table.multipageimage' ).contents(); + } ); - // Add a new spinner if one doesn't already exist - if ( !$spinner ) { + // Handle cache updates + jqXhr.done( function ( $contents ) { + jqXhr = undefined; + + // Cache the newly loaded page + cache[url] = $contents; + cacheOrder.push( url ); + + // Remove the oldest entry if we're over the limit + if ( cacheOrder.length > 10 ) { + delete cache[ cacheOrder[0] ]; + cacheOrder = cacheOrder.slice( 1 ); + } + } ); + + return jqXhr.promise(); + } + + /* Fetch the next page and use jQuery to swap the table.multipageimage contents. + * @param {string} url + * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if + * true, this function won't push a new history state, for the browser did so already). + */ + function switchPage( url, hist ) { + var $tr, promise; + + // Start fetching data (might be cached) + promise = fetchPageData( url ); + + // Add a new spinner if one doesn't already exist and the data is not already ready + if ( !$spinner && promise.state() !== 'resolved' ) { $tr = $multipageimage.find( 'tr' ); $spinner = $.createSpinner( { size: 'large', @@ -34,13 +77,11 @@ $multipageimage.empty().append( $spinner ); } - // @todo Don't fetch the entire page. Ideally we'd only fetch the content portion or the data - // (thumbnail urls) and update the interface manually. - jqXhr = $.ajax( url ).done( function ( data ) { - jqXhr = $spinner = undefined; + promise.done( function ( $contents ) { + $spinner = undefined; // Replace table contents - $multipageimage.empty().append( $( data ).find( 'table.multipageimage' ).contents() ); + $multipageimage.empty().append( $contents.clone() ); bindPageNavigation( $multipageimage ); @@ -66,12 +107,12 @@ .extend( { title: mw.config.get( 'wgPageName' ), page: page } ) .toString(); - loadPage( uri ); + switchPage( uri ); e.preventDefault(); } ); $container.find( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) { - loadPage( this.action + '?' + $( this ).serialize() ); + switchPage( this.action + '?' + $( this ).serialize() ); e.preventDefault(); } ); } @@ -93,7 +134,7 @@ $( window ).on( 'popstate', function ( e ) { var state = e.originalEvent.state; if ( state && state.tag === 'mw-pagination' ) { - loadPage( location.href, true ); + switchPage( location.href, true ); } } ); } diff --git a/resources/src/mediawiki.page/mediawiki.page.ready.js b/resources/src/mediawiki.page/mediawiki.page.ready.js index 246cc817..36eb9d4f 100644 --- a/resources/src/mediawiki.page/mediawiki.page.ready.js +++ b/resources/src/mediawiki.page/mediawiki.page.ready.js @@ -7,7 +7,7 @@ // it works only comparing to window.self or window.window (http://stackoverflow.com/q/4850978/319266) if ( window.top !== window.self ) { // Un-trap us from framesets - window.top.location = window.location; + window.top.location.href = location.href; } } diff --git a/resources/src/mediawiki.page/mediawiki.page.startup.js b/resources/src/mediawiki.page/mediawiki.page.startup.js index 4aae6069..ddd4f0c4 100644 --- a/resources/src/mediawiki.page/mediawiki.page.startup.js +++ b/resources/src/mediawiki.page/mediawiki.page.startup.js @@ -4,7 +4,7 @@ // Client profile classes for // Allows for easy hiding/showing of JS or no-JS-specific UI elements - $( 'html' ) + $( document.documentElement ) .addClass( 'client-js' ) .removeClass( 'client-nojs' ); diff --git a/resources/src/mediawiki.skinning/content.css b/resources/src/mediawiki.skinning/content.css index 7a417081..7dd5ee7f 100644 --- a/resources/src/mediawiki.skinning/content.css +++ b/resources/src/mediawiki.skinning/content.css @@ -8,7 +8,8 @@ /* Table of Contents */ #toc, .toc, -.mw-warning { +.mw-warning, +.toccolours { border: 1px solid #aaa; background-color: #f9f9f9; padding: 5px; @@ -87,13 +88,6 @@ table.toc td { font-size: 94%; } -.toccolours { - border: 1px solid #aaa; - background-color: #f9f9f9; - padding: 5px; - font-size: 95%; -} - /* Warning */ .mw-warning { margin-left: 50px; @@ -165,8 +159,13 @@ div.magnify a { width: 15px; height: 11px; /* Default styles when there's no .mw-content-ltr or .mw-content-rtl, overridden below */ + + /* Use same SVG support hack as mediawiki.legacy's shared.css */ + background-image: url(images/magnify-clip-ltr.png); + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); /* @embed */ - background: url(images/magnify-clip-ltr.png) center center no-repeat; + background-image: linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); /* Don't annoy people who copy-paste everything too much */ -moz-user-select: none; -webkit-user-select: none; @@ -194,8 +193,12 @@ img.thumbborder { /* @noflip */ .mw-content-ltr div.magnify a { - /* @embed */ + /* Use same SVG support hack as mediawiki.legacy's shared.css */ background-image: url(images/magnify-clip-ltr.png); + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); + /* @embed */ + background-image: linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg); } /* @noflip */ @@ -212,8 +215,12 @@ img.thumbborder { /* @noflip */ .mw-content-rtl div.magnify a { - /* @embed */ + /* Use same SVG support hack as mediawiki.legacy's shared.css */ background-image: url(images/magnify-clip-rtl.png); + /* @embed */ + background-image: -webkit-linear-gradient(transparent, transparent), url(images/magnify-clip-rtl.svg); + /* @embed */ + background-image: linear-gradient(transparent, transparent), url(images/magnify-clip-rtl.svg); } /* @noflip */ diff --git a/resources/src/mediawiki.skinning/elements.css b/resources/src/mediawiki.skinning/elements.css index 392a2a66..8140d1a5 100644 --- a/resources/src/mediawiki.skinning/elements.css +++ b/resources/src/mediawiki.skinning/elements.css @@ -112,7 +112,7 @@ h6 { } h3 { - font-size: 132%; + font-size: 128%; } h4 { @@ -141,7 +141,6 @@ h5 { p { margin: .4em 0 .5em 0; - line-height: 1.5em; } p img { @@ -149,14 +148,12 @@ p img { } ul { - line-height: 1.5em; list-style-type: square; margin: .3em 0 0 1.6em; padding: 0; } ol { - line-height: 1.5em; margin: .3em 0 0 3.2em; padding: 0; list-style-image: none; @@ -177,17 +174,10 @@ dl { } dd { - line-height: 1.5em; margin-left: 1.6em; margin-bottom: .1em; } -/* IE 6 and 7 lack support for quotes aroud the element ('::before' and '::after' - pseudoelements, 'quotes' property). Let's italicize it instead (using the star hack). */ -q { - *font-style: italic; -} - pre, code, tt, kbd, samp, .mw-code { /* * Some browsers will render the monospace text too small, namely Firefox, Chrome and Safari. @@ -221,7 +211,6 @@ fieldset { border: 1px solid #2f6fab; margin: 1em 0 1em 0; padding: 0 1em 1em; - line-height: 1.5em; } fieldset.nested { diff --git a/resources/src/mediawiki.skinning/images/magnify-clip-ltr.png b/resources/src/mediawiki.skinning/images/magnify-clip-ltr.png index 00a9cee1..712b1b48 100644 Binary files a/resources/src/mediawiki.skinning/images/magnify-clip-ltr.png and b/resources/src/mediawiki.skinning/images/magnify-clip-ltr.png differ diff --git a/resources/src/mediawiki.skinning/images/magnify-clip-ltr.svg b/resources/src/mediawiki.skinning/images/magnify-clip-ltr.svg new file mode 100644 index 00000000..4d3dcb65 --- /dev/null +++ b/resources/src/mediawiki.skinning/images/magnify-clip-ltr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/src/mediawiki.skinning/images/magnify-clip-rtl.png b/resources/src/mediawiki.skinning/images/magnify-clip-rtl.png index ff85c077..1d03a8c0 100644 Binary files a/resources/src/mediawiki.skinning/images/magnify-clip-rtl.png and b/resources/src/mediawiki.skinning/images/magnify-clip-rtl.png differ diff --git a/resources/src/mediawiki.skinning/images/magnify-clip-rtl.svg b/resources/src/mediawiki.skinning/images/magnify-clip-rtl.svg new file mode 100644 index 00000000..582e4ae7 --- /dev/null +++ b/resources/src/mediawiki.skinning/images/magnify-clip-rtl.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/src/mediawiki.skinning/interface.css b/resources/src/mediawiki.skinning/interface.css index 398a132d..b57ee367 100644 --- a/resources/src/mediawiki.skinning/interface.css +++ b/resources/src/mediawiki.skinning/interface.css @@ -15,6 +15,14 @@ clear: both; } +.editOptions { + background-color: #F0F0F0; + border: 1px solid silver; + border-top: none; + padding: 1em 1em 1.5em 1em; + margin-bottom: 2em; +} + .usermessage { background-color: #ffce7b; border: 1px solid #ffa500; diff --git a/resources/src/mediawiki.special/mediawiki.special.block.js b/resources/src/mediawiki.special/mediawiki.special.block.js index 8579e054..aca335ee 100644 --- a/resources/src/mediawiki.special/mediawiki.special.block.js +++ b/resources/src/mediawiki.special/mediawiki.special.block.js @@ -12,7 +12,7 @@ function updateBlockOptions( instant ) { var blocktarget = $.trim( $blockTarget.val() ), isEmpty = blocktarget === '', - isIp = mw.util.isIPv4Address( blocktarget, true ) || mw.util.isIPv6Address( blocktarget, true ), + isIp = mw.util.isIPAddress( blocktarget, true ), isIpRange = isIp && blocktarget.match( /\/\d+$/ ); if ( isIp && !isEmpty ) { diff --git a/resources/src/mediawiki.special/mediawiki.special.changeslist.css b/resources/src/mediawiki.special/mediawiki.special.changeslist.css index c92db167..16fdf38a 100644 --- a/resources/src/mediawiki.special/mediawiki.special.changeslist.css +++ b/resources/src/mediawiki.special/mediawiki.special.changeslist.css @@ -5,3 +5,11 @@ .mw-changeslist-line-watched .mw-title { font-weight: bold; } + +/* + * Titles, including username links, are especially prone for getting jumbled up + * with other titles, usernames, etc. in mixed RTL-LTR environment. + */ +.mw-changeslist .mw-title { + unicode-bidi: embed; +} diff --git a/resources/src/mediawiki.special/mediawiki.special.changeslist.legend.css b/resources/src/mediawiki.special/mediawiki.special.changeslist.legend.css index 6b0bf991..14f6aeee 100644 --- a/resources/src/mediawiki.special/mediawiki.special.changeslist.legend.css +++ b/resources/src/mediawiki.special/mediawiki.special.changeslist.legend.css @@ -20,10 +20,14 @@ .mw-changeslist-legend dt { float: left; - margin-right: 0.5em; + margin: 0 0.5em 0 0; } .mw-changeslist-legend dd { margin-left: 1.5em; +} + +.mw-changeslist-legend dt, +.mw-changeslist-legend dd { line-height: 1.3em; } diff --git a/resources/src/mediawiki.special/mediawiki.special.css b/resources/src/mediawiki.special/mediawiki.special.css index 0356fc74..d2457262 100644 --- a/resources/src/mediawiki.special/mediawiki.special.css +++ b/resources/src/mediawiki.special/mediawiki.special.css @@ -118,3 +118,8 @@ table.mw-userrights-groups * td, table.mw-userrights-groups * th { padding-right: 1.5em; } + +/* Special:Contributions */ +.mw-contributions-form select { + vertical-align: middle; +} diff --git a/resources/src/mediawiki.special/mediawiki.special.edittags.css b/resources/src/mediawiki.special/mediawiki.special.edittags.css new file mode 100644 index 00000000..204009c9 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.edittags.css @@ -0,0 +1,15 @@ +/*! + * Styling for Special:EditTags and action=editchangetags + */ +#mw-edittags-tags-selector td { + vertical-align: top; +} + +#mw-edittags-tags-selector-multi td { + vertical-align: top; + padding-right: 1.5em; +} + +#mw-edittags-tag-list { + min-width: 20em; +} diff --git a/resources/src/mediawiki.special/mediawiki.special.edittags.js b/resources/src/mediawiki.special/mediawiki.special.edittags.js new file mode 100644 index 00000000..69a2a67a --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.edittags.js @@ -0,0 +1,24 @@ +/*! + * JavaScript for Special:EditTags + */ +( function ( mw, $ ) { + $( function () { + var $tagList = $( '#mw-edittags-tag-list' ); + if ( $tagList.length ) { + $tagList.chosen( { + /*jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ + placeholder_text_multiple: mw.msg( 'tags-edit-chosen-placeholder' ), + no_results_text: mw.msg( 'tags-edit-chosen-no-results' ) + } ); + } + + $( '#mw-edittags-remove-all' ).on( 'change', function ( e ) { + $( '.mw-edittags-remove-checkbox' ).prop( 'checked', e.target.checked ); + } ); + $( '.mw-edittags-remove-checkbox' ).on( 'change', function ( e ) { + if ( !e.target.checked ) { + $( '#mw-edittags-remove-all' ).prop( 'checked', false ); + } + } ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.import.js b/resources/src/mediawiki.special/mediawiki.special.import.js index a9a985eb..5622b32a 100644 --- a/resources/src/mediawiki.special/mediawiki.special.import.js +++ b/resources/src/mediawiki.special/mediawiki.special.import.js @@ -2,7 +2,7 @@ * JavaScript for Special:Import */ ( function ( $ ) { - function updateImportSubprojectList() { + function updateImportSubprojectList( firstTime ) { var $projectField = $( '#mw-import-table-interwiki #interwiki' ), $subprojectField = $projectField.parent().find( '#subproject' ), $selected = $projectField.find( ':selected' ), @@ -14,7 +14,7 @@ option = document.createElement( 'option' ); option.appendChild( document.createTextNode( el ) ); option.setAttribute( 'value', el ); - if ( oldValue === el ) { + if ( oldValue === el && firstTime !== true ) { option.setAttribute( 'selected', 'selected' ); } return option; @@ -29,7 +29,7 @@ var $projectField = $( '#mw-import-table-interwiki #interwiki' ); if ( $projectField.length ) { $projectField.change( updateImportSubprojectList ); - updateImportSubprojectList(); + updateImportSubprojectList( true ); } } ); }( jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.pageLanguage.js b/resources/src/mediawiki.special/mediawiki.special.pageLanguage.js index ba7f7342..7c2269fa 100644 --- a/resources/src/mediawiki.special/mediawiki.special.pageLanguage.js +++ b/resources/src/mediawiki.special/mediawiki.special.pageLanguage.js @@ -6,4 +6,4 @@ $( '#mw-pl-options-2' ).prop( 'checked', true ); } ); } ); -} ( jQuery ) ); +}( jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.js b/resources/src/mediawiki.special/mediawiki.special.preferences.js index 1f6429b2..4bd747b2 100644 --- a/resources/src/mediawiki.special/mediawiki.special.preferences.js +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.js @@ -5,15 +5,18 @@ jQuery( function ( $ ) { var $preftoc, $preferences, $fieldsets, $legends, hash, labelFunc, $tzSelect, $tzTextbox, $localtimeHolder, servertime, - $checkBoxes, savedWindowOnBeforeUnload; + $checkBoxes, allowCloseWindowFn; labelFunc = function () { return this.id.replace( /^mw-prefsection/g, 'preftab' ); }; $( '#prefsubmit' ).attr( 'id', 'prefcontrol' ); - $preftoc = $( '
      ' ) - .attr( 'role', 'tablist' ); + $preftoc = $( '