summaryrefslogtreecommitdiff
path: root/resources
diff options
context:
space:
mode:
Diffstat (limited to 'resources')
-rw-r--r--resources/Resources.php455
-rw-r--r--resources/jquery.effects/jquery.effects.blind.js12
-rw-r--r--resources/jquery.effects/jquery.effects.bounce.js12
-rw-r--r--resources/jquery.effects/jquery.effects.clip.js12
-rw-r--r--resources/jquery.effects/jquery.effects.core.js131
-rw-r--r--resources/jquery.effects/jquery.effects.drop.js12
-rw-r--r--resources/jquery.effects/jquery.effects.explode.js10
-rw-r--r--resources/jquery.effects/jquery.effects.fade.js32
-rw-r--r--resources/jquery.effects/jquery.effects.fold.js12
-rw-r--r--resources/jquery.effects/jquery.effects.highlight.js10
-rw-r--r--resources/jquery.effects/jquery.effects.pulsate.js10
-rw-r--r--resources/jquery.effects/jquery.effects.scale.js14
-rw-r--r--resources/jquery.effects/jquery.effects.shake.js12
-rw-r--r--resources/jquery.effects/jquery.effects.slide.js14
-rw-r--r--resources/jquery.effects/jquery.effects.transfer.js10
-rw-r--r--resources/jquery.tipsy/jquery.tipsy.css17
-rw-r--r--resources/jquery.tipsy/jquery.tipsy.js17
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-af.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js17
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-az.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-da.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-de.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-el.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-et.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js24
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js23
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-he.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js14
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js23
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js23
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js23
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js23
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js6
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-no.js38
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js6
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js22
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js21
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-th.js4
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js3
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js2
-rw-r--r--resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js2
-rw-r--r--resources/jquery.ui/jquery.ui.accordion.js14
-rw-r--r--resources/jquery.ui/jquery.ui.autocomplete.js16
-rw-r--r--resources/jquery.ui/jquery.ui.button.js103
-rw-r--r--resources/jquery.ui/jquery.ui.core.js52
-rw-r--r--resources/jquery.ui/jquery.ui.datepicker.js335
-rw-r--r--resources/jquery.ui/jquery.ui.dialog.js45
-rw-r--r--resources/jquery.ui/jquery.ui.draggable.js106
-rw-r--r--resources/jquery.ui/jquery.ui.droppable.js17
-rw-r--r--resources/jquery.ui/jquery.ui.mouse.js18
-rw-r--r--resources/jquery.ui/jquery.ui.position.js54
-rw-r--r--resources/jquery.ui/jquery.ui.progressbar.js5
-rw-r--r--resources/jquery.ui/jquery.ui.resizable.js40
-rw-r--r--resources/jquery.ui/jquery.ui.selectable.js13
-rw-r--r--resources/jquery.ui/jquery.ui.slider.js62
-rw-r--r--resources/jquery.ui/jquery.ui.sortable.js31
-rw-r--r--resources/jquery.ui/jquery.ui.tabs.js4
-rw-r--r--resources/jquery.ui/jquery.ui.widget.js32
-rw-r--r--resources/jquery.ui/themes/default/images/ui-anim_basic_16x16.gifbin1553 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_55_fbf9ee_40x100.pngbin182 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_65_ffffff_40x100.pngbin178 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_75_cccccc_40x100.pngbin180 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_75_dadada_40x100.pngbin180 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_75_e6e6e6_40x100.pngbin180 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_flat_95_fef1ec_40x100.pngbin182 -> 0 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.pngbin4369 -> 4369 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.pngbin5355 -> 4369 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.pngbin4369 -> 4369 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.pngbin4369 -> 4369 bytes
-rw-r--r--resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.pngbin4369 -> 4369 bytes
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.accordion.css17
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.autocomplete.css23
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.button.css25
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.core.css23
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.datepicker.css37
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.dialog.css20
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.progressbar.css13
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.resizable.css13
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.selectable.css13
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.slider.css11
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.tabs.css11
-rw-r--r--resources/jquery.ui/themes/default/jquery.ui.theme.css83
-rw-r--r--resources/jquery.ui/themes/vector/images/button-disabled-green.pngbin126 -> 1033 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-down-green.pngbin170 -> 1042 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-off-green.pngbin216 -> 1035 bytes
-rw-r--r--resources/jquery.ui/themes/vector/images/button-over-green.pngbin216 -> 1035 bytes
-rw-r--r--resources/jquery.ui/themes/vector/jquery.ui.datepicker.css2
-rw-r--r--resources/jquery.ui/themes/vector/jquery.ui.dialog.css2
-rw-r--r--resources/jquery/images/jquery.arrowSteps.divider-ltr.pngbin0 -> 307 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.divider-rtl.pngbin0 -> 310 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.head-ltr.pngbin0 -> 939 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.head-rtl.pngbin0 -> 1006 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.tail-ltr.pngbin0 -> 338 bytes
-rw-r--r--resources/jquery/images/jquery.arrowSteps.tail-rtl.pngbin0 -> 485 bytes
-rw-r--r--resources/jquery/images/marker.pngbin0 -> 652 bytes
-rw-r--r--resources/jquery/images/mask.pngbin0 -> 2020 bytes
-rw-r--r--resources/jquery/images/spinner.gifbin0 -> 4648 bytes
-rw-r--r--resources/jquery/images/wheel.pngbin0 -> 11733 bytes
-rw-r--r--resources/jquery/jquery.arrowSteps.css45
-rw-r--r--resources/jquery/jquery.arrowSteps.js81
-rw-r--r--resources/jquery/jquery.autoEllipsis.js8
-rw-r--r--resources/jquery/jquery.byteLimit.js61
-rw-r--r--resources/jquery/jquery.checkboxShiftClick.js6
-rw-r--r--resources/jquery/jquery.client.js34
-rw-r--r--resources/jquery/jquery.collapsibleTabs.js10
-rw-r--r--resources/jquery/jquery.color.js8
-rw-r--r--resources/jquery/jquery.cycle.all.js1529
-rw-r--r--resources/jquery/jquery.expandableField.js2
-rw-r--r--resources/jquery/jquery.farbtastic.css54
-rw-r--r--resources/jquery/jquery.farbtastic.js286
-rw-r--r--resources/jquery/jquery.footHovzer.css6
-rw-r--r--resources/jquery/jquery.footHovzer.js45
-rw-r--r--resources/jquery/jquery.form.js310
-rw-r--r--resources/jquery/jquery.js3166
-rw-r--r--resources/jquery/jquery.makeCollapsible.js52
-rw-r--r--resources/jquery/jquery.messageBox.js20
-rw-r--r--resources/jquery/jquery.mockjax.js382
-rw-r--r--resources/jquery/jquery.mw-jump.js15
-rw-r--r--resources/jquery/jquery.mwExtension.js121
-rw-r--r--resources/jquery/jquery.mwPrototypes.js120
-rw-r--r--resources/jquery/jquery.placeholder.js4
-rw-r--r--resources/jquery/jquery.qunit.completenessTest.js36
-rw-r--r--resources/jquery/jquery.qunit.css21
-rw-r--r--resources/jquery/jquery.qunit.js783
-rw-r--r--resources/jquery/jquery.spinner.css12
-rw-r--r--resources/jquery/jquery.spinner.js44
-rw-r--r--resources/jquery/jquery.suggestions.css13
-rw-r--r--resources/jquery/jquery.suggestions.js150
-rw-r--r--resources/jquery/jquery.tabIndex.js8
-rw-r--r--resources/jquery/jquery.tablesorter.css12
-rw-r--r--resources/jquery/jquery.tablesorter.js15
-rw-r--r--resources/jquery/jquery.textSelection.js116
-rw-r--r--resources/jquery/jquery.validate.js1166
-rw-r--r--resources/jquery/jquery.xmldom.js46
-rw-r--r--resources/mediawiki.action/mediawiki.action.edit.js165
-rw-r--r--resources/mediawiki.action/mediawiki.action.history.js135
-rw-r--r--resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js12
-rw-r--r--resources/mediawiki.action/mediawiki.action.view.metadata.js2
-rw-r--r--resources/mediawiki.action/mediawiki.action.watch.ajax.js271
-rw-r--r--resources/mediawiki.api/mediawiki.api.category.js105
-rw-r--r--resources/mediawiki.api/mediawiki.api.edit.js119
-rw-r--r--resources/mediawiki.api/mediawiki.api.js224
-rw-r--r--resources/mediawiki.api/mediawiki.api.parse.js31
-rw-r--r--resources/mediawiki.api/mediawiki.api.titleblacklist.js51
-rw-r--r--resources/mediawiki.api/mediawiki.api.watch.js58
-rw-r--r--resources/mediawiki.language/languages/cs.js2
-rw-r--r--resources/mediawiki.language/languages/lt.js3
-rw-r--r--resources/mediawiki.language/languages/mk.js2
-rw-r--r--resources/mediawiki.language/languages/se.js10
-rw-r--r--resources/mediawiki.language/languages/sma.js11
-rw-r--r--resources/mediawiki.language/mediawiki.language.js30
-rw-r--r--resources/mediawiki.page/images/AJAXCategorySprite.pngbin384 -> 0 bytes
-rw-r--r--resources/mediawiki.page/mediawiki.page.ajaxCategories.css64
-rw-r--r--resources/mediawiki.page/mediawiki.page.ready.js2
-rw-r--r--resources/mediawiki.page/mediawiki.page.startup.js14
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeemail.css (renamed from resources/mediawiki.special/mediawiki.special.preferences.css)6
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeemail.js40
-rw-r--r--resources/mediawiki.special/mediawiki.special.changeslist.css9
-rw-r--r--resources/mediawiki.special/mediawiki.special.javaScriptTest.js33
-rw-r--r--resources/mediawiki.special/mediawiki.special.movePage.js2
-rw-r--r--resources/mediawiki.special/mediawiki.special.preferences.js43
-rw-r--r--resources/mediawiki.special/mediawiki.special.recentchanges.js8
-rw-r--r--resources/mediawiki.special/mediawiki.special.search.css10
-rw-r--r--resources/mediawiki.special/mediawiki.special.search.js4
-rw-r--r--resources/mediawiki.special/mediawiki.special.upload.js83
-rw-r--r--resources/mediawiki/mediawiki.Uri.js327
-rw-r--r--resources/mediawiki/mediawiki.debug.css185
-rw-r--r--resources/mediawiki/mediawiki.debug.init.js3
-rw-r--r--resources/mediawiki/mediawiki.debug.js351
-rw-r--r--resources/mediawiki/mediawiki.feedback.css9
-rw-r--r--resources/mediawiki/mediawiki.feedback.js242
-rw-r--r--resources/mediawiki/mediawiki.feedback.spinner.gifbin0 -> 1108 bytes
-rw-r--r--resources/mediawiki/mediawiki.htmlform.js6
-rw-r--r--resources/mediawiki/mediawiki.jqueryMsg.js685
-rw-r--r--resources/mediawiki/mediawiki.jqueryMsg.peg76
-rw-r--r--resources/mediawiki/mediawiki.js2342
-rw-r--r--resources/mediawiki/mediawiki.log.js95
-rw-r--r--resources/mediawiki/mediawiki.user.js12
-rw-r--r--resources/mediawiki/mediawiki.util.js410
204 files changed, 12519 insertions, 4918 deletions
diff --git a/resources/Resources.php b/resources/Resources.php
index 940d646d..ca64263a 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -2,7 +2,7 @@
return array(
- /* Special resources who have their own classes */
+ /* Special modules who have their own classes */
'site' => array( 'class' => 'ResourceLoaderSiteModule' ),
'noscript' => array( 'class' => 'ResourceLoaderNoscriptModule' ),
@@ -10,19 +10,35 @@ return array(
'user' => array( 'class' => 'ResourceLoaderUserModule' ),
'user.groups' => array( 'class' => 'ResourceLoaderUserGroupsModule' ),
'user.options' => array( 'class' => 'ResourceLoaderUserOptionsModule' ),
+ 'user.cssprefs' => array( 'class' => 'ResourceLoaderUserCSSPrefsModule' ),
'user.tokens' => array( 'class' => 'ResourceLoaderUserTokensModule' ),
'filepage' => array( 'class' => 'ResourceLoaderFilePageModule' ),
/* Skins */
- 'skins.vector' => array(
- 'styles' => array( 'vector/screen.css' => array( 'media' => 'screen' ) ),
- 'scripts' => 'vector/vector.js',
+ 'skins.chick' => array(
+ 'styles' => array( 'chick/main.css' => array( 'media' => 'screen, handheld' ) ),
+ 'remoteBasePath' => $GLOBALS['wgStylePath'],
+ 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ ),
+ 'skins.cologneblue' => array(
+ 'styles' => array( 'cologneblue/screen.css' => array( 'media' => 'screen' ) ),
+ 'remoteBasePath' => $GLOBALS['wgStylePath'],
+ 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ ),
+ 'skins.modern' => array(
+ 'styles' => array(
+ 'modern/main.css' => array( 'media' => 'screen' ),
+ 'modern/print.css' => array( 'media' => 'print' ),
+ ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
'skins.monobook' => array(
'styles' => array(
+ 'common/commonElements.css' => array( 'media' => 'screen' ),
+ 'common/commonContent.css' => array( 'media' => 'screen' ),
+ 'common/commonInterface.css' => array( 'media' => 'screen' ),
'monobook/main.css' => array( 'media' => 'screen' ),
),
'remoteBasePath' => $GLOBALS['wgStylePath'],
@@ -30,6 +46,9 @@ return array(
),
'skins.archlinux' => array(
'styles' => array(
+ 'common/commonElements.css' => array( 'media' => 'screen' ),
+ 'common/commonContent.css' => array( 'media' => 'screen' ),
+ 'common/commonInterface.css' => array( 'media' => 'screen' ),
'archlinux/main.css' => array( 'media' => 'screen' ),
'archlinux/archnavbar.css' => array( 'media' => 'screen' ),
'archlinux/arch.css' => array( 'media' => 'screen' ),
@@ -38,36 +57,29 @@ return array(
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
+ 'skins.nostalgia' => array(
+ 'styles' => array( 'nostalgia/screen.css' => array( 'media' => 'screen' ) ),
+ 'remoteBasePath' => $GLOBALS['wgStylePath'],
+ 'localBasePath' => $GLOBALS['wgStyleDirectory'],
+ ),
'skins.simple' => array(
'styles' => array( 'simple/main.css' => array( 'media' => 'screen' ) ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
- 'skins.chick' => array(
- 'styles' => array( 'chick/main.css' => array( 'media' => 'screen,handheld' ) ),
+ 'skins.standard' => array(
+ 'styles' => array( 'common/wikistandard.css' => array( 'media' => 'screen' ) ),
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
- 'skins.modern' => array(
+ 'skins.vector' => array(
'styles' => array(
- 'modern/main.css' => array( 'media' => 'screen' ),
- 'modern/print.css' => array( 'media' => 'print' ),
+ 'common/commonElements.css' => array( 'media' => 'screen' ),
+ 'common/commonContent.css' => array( 'media' => 'screen' ),
+ 'common/commonInterface.css' => array( 'media' => 'screen' ),
+ 'vector/screen.css' => array( 'media' => 'screen' ),
),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.cologneblue' => array(
- 'styles' => array( 'cologneblue/screen.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.nostalgia' => array(
- 'styles' => array( 'nostalgia/screen.css' => array( 'media' => 'screen' ) ),
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- ),
- 'skins.standard' => array(
- 'styles' => array( 'common/wikistandard.css' => array( 'media' => 'screen' ) ),
+ 'scripts' => 'vector/vector.js',
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
),
@@ -76,17 +88,21 @@ return array(
'jquery' => array(
'scripts' => 'resources/jquery/jquery.js',
- 'debugRaw' => false
+ 'debugRaw' => false,
),
/* jQuery Plugins */
- 'jquery.async' => array(
- 'scripts' => 'resources/jquery/jquery.async.js',
- ),
'jquery.appear' => array(
'scripts' => 'resources/jquery/jquery.appear.js',
),
+ 'jquery.arrowSteps' => array(
+ 'scripts' => 'resources/jquery/jquery.arrowSteps.js',
+ 'styles' => 'resources/jquery/jquery.arrowSteps.css',
+ ),
+ 'jquery.async' => array(
+ 'scripts' => 'resources/jquery/jquery.async.js',
+ ),
'jquery.autoEllipsis' => array(
'scripts' => 'resources/jquery/jquery.autoEllipsis.js',
'dependencies' => 'jquery.highlightText',
@@ -107,13 +123,13 @@ return array(
'jquery.collapsibleTabs' => array(
'scripts' => 'resources/jquery/jquery.collapsibleTabs.js',
),
- 'jquery.colorUtil' => array(
- 'scripts' => 'resources/jquery/jquery.colorUtil.js',
- ),
'jquery.color' => array(
'scripts' => 'resources/jquery/jquery.color.js',
'dependencies' => 'jquery.colorUtil',
),
+ 'jquery.colorUtil' => array(
+ 'scripts' => 'resources/jquery/jquery.colorUtil.js',
+ ),
'jquery.cookie' => array(
'scripts' => 'resources/jquery/jquery.cookie.js',
),
@@ -124,6 +140,15 @@ return array(
'scripts' => 'resources/jquery/jquery.expandableField.js',
'dependencies' => 'jquery.delayedBind',
),
+ 'jquery.farbtastic' => array(
+ 'scripts' => 'resources/jquery/jquery.farbtastic.js',
+ 'styles' => 'resources/jquery/jquery.farbtastic.css',
+ 'dependencies' => 'jquery.colorUtil',
+ ),
+ 'jquery.footHovzer' => array(
+ 'scripts' => 'resources/jquery/jquery.footHovzer.js',
+ 'styles' => 'resources/jquery/jquery.footHovzer.css',
+ ),
'jquery.form' => array(
'scripts' => 'resources/jquery/jquery.form.js',
),
@@ -136,13 +161,6 @@ return array(
'jquery.hoverIntent' => array(
'scripts' => 'resources/jquery/jquery.hoverIntent.js',
),
- 'jquery.messageBox' => array(
- 'scripts' => 'resources/jquery/jquery.messageBox.js',
- 'styles' => 'resources/jquery/jquery.messageBox.css',
- ),
- 'jquery.placeholder' => array(
- 'scripts' => 'resources/jquery/jquery.placeholder.js',
- ),
'jquery.json' => array(
'scripts' => 'resources/jquery/jquery.json.js',
),
@@ -154,12 +172,34 @@ return array(
'styles' => 'resources/jquery/jquery.makeCollapsible.css',
'messages' => array( 'collapsible-expand', 'collapsible-collapse' ),
),
- 'jquery.mwPrototypes' => array(
- 'scripts' => 'resources/jquery/jquery.mwPrototypes.js',
+ 'jquery.messageBox' => array(
+ 'scripts' => 'resources/jquery/jquery.messageBox.js',
+ 'styles' => 'resources/jquery/jquery.messageBox.css',
+ ),
+ 'jquery.mockjax' => array(
+ 'scripts' => 'resources/jquery/jquery.mockjax.js',
+ ),
+ 'jquery.mw-jump' => array(
+ 'scripts' => 'resources/jquery/jquery.mw-jump.js',
+ ),
+ 'jquery.mwExtension' => array(
+ 'scripts' => 'resources/jquery/jquery.mwExtension.js',
+ ),
+ 'jquery.placeholder' => array(
+ 'scripts' => 'resources/jquery/jquery.placeholder.js',
),
'jquery.qunit' => array(
'scripts' => 'resources/jquery/jquery.qunit.js',
'styles' => 'resources/jquery/jquery.qunit.css',
+ 'position' => 'top',
+ ),
+ 'jquery.qunit.completenessTest' => array(
+ 'scripts' => 'resources/jquery/jquery.qunit.completenessTest.js',
+ 'dependencies' => 'jquery.qunit',
+ ),
+ 'jquery.spinner' => array(
+ 'scripts' => 'resources/jquery/jquery.spinner.js',
+ 'styles' => 'resources/jquery/jquery.spinner.css',
),
'jquery.suggestions' => array(
'scripts' => 'resources/jquery/jquery.suggestions.js',
@@ -177,6 +217,15 @@ return array(
'jquery.textSelection' => array(
'scripts' => 'resources/jquery/jquery.textSelection.js',
),
+ 'jquery.validate' => array(
+ 'scripts' => 'resources/jquery/jquery.validate.js',
+ ),
+ 'jquery.xmldom' => array(
+ 'scripts' => 'resources/jquery/jquery.xmldom.js',
+ ),
+
+ /* jQuery Tipsy */
+
'jquery.tipsy' => array(
'scripts' => 'resources/jquery.tipsy/jquery.tipsy.js',
'styles' => 'resources/jquery.tipsy/jquery.tipsy.css',
@@ -305,6 +354,7 @@ return array(
'fo' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js',
'fr-ch' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js',
'fr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js',
+ 'gl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js',
'he' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-he.js',
'hr' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js',
'hu' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js',
@@ -313,14 +363,20 @@ return array(
'is' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-is.js',
'it' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-it.js',
'ja' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js',
+ 'kk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js',
'ko' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ko.js',
+ 'lb' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js',
'lt' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lt.js',
'lv' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-lv.js',
+ 'mk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js',
+ 'ml' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js',
'ms' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ms.js',
'nl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js',
'no' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-no.js',
'pl' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pl.js',
+ 'pt' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js',
'pt-br' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js',
+ 'rm' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js',
'ro' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js',
'ru' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js',
'sk' => 'resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js',
@@ -415,6 +471,11 @@ return array(
'dependencies' => 'jquery.effects.core',
'group' => 'jquery.ui',
),
+ 'jquery.effects.fade' => array(
+ 'scripts' => 'resources/jquery.effects/jquery.effects.fade.js',
+ 'dependencies' => 'jquery.effects.core',
+ 'group' => 'jquery.ui',
+ ),
'jquery.effects.fold' => array(
'scripts' => 'resources/jquery.effects/jquery.effects.fold.js',
'dependencies' => 'jquery.effects.core',
@@ -458,48 +519,113 @@ return array(
'debugScripts' => 'resources/mediawiki/mediawiki.log.js',
'debugRaw' => false,
),
- 'mediawiki.Title' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.Title.js',
+ 'mediawiki.api' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.js',
'dependencies' => 'mediawiki.util',
),
- 'mediawiki.Uri' => array(
- 'scripts' => 'resources/mediawiki/mediawiki.Uri.js',
+ 'mediawiki.api.category' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.category.js',
+ 'dependencies' => array(
+ 'mediawiki.api',
+ 'mediawiki.Title'
+ ),
+ ),
+ 'mediawiki.api.edit' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.edit.js',
+ 'dependencies' => array(
+ 'mediawiki.api',
+ 'mediawiki.Title'
+ ),
+ ),
+ 'mediawiki.api.parse' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.parse.js',
+ 'dependencies' => 'mediawiki.api',
+ ),
+ 'mediawiki.api.titleblacklist' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.titleblacklist.js',
+ 'dependencies' => array(
+ 'mediawiki.api',
+ 'mediawiki.Title'
+ ),
+ ),
+ 'mediawiki.api.watch' => array(
+ 'scripts' => 'resources/mediawiki.api/mediawiki.api.watch.js',
+ 'dependencies' => array('mediawiki.api', 'mediawiki.user'),
+ ),
+ 'mediawiki.debug' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.debug.js',
+ 'styles' => 'resources/mediawiki/mediawiki.debug.css',
+ 'dependencies' => 'jquery.footHovzer',
+ 'position' => 'bottom',
+ ),
+ 'mediawiki.debug.init' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.debug.init.js',
+ 'dependencies' => 'mediawiki.debug',
+ // Uses a custom mw.config variable that is set in debughtml,
+ // must be loaded on the bottom
+ 'position' => 'bottom',
+ ),
+ 'mediawiki.feedback' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.feedback.js',
+ 'styles' => 'resources/mediawiki/mediawiki.feedback.css',
+ 'dependencies' => array(
+ 'mediawiki.api.edit',
+ 'mediawiki.Title',
+ 'mediawiki.jqueryMsg',
+ 'jquery.ui.dialog',
+ ),
+ 'messages' => array(
+ 'feedback-bugornote',
+ 'feedback-subject',
+ 'feedback-message',
+ 'feedback-cancel',
+ 'feedback-submit',
+ 'feedback-adding',
+ 'feedback-error1',
+ 'feedback-error2',
+ 'feedback-error3',
+ 'feedback-thanks',
+ 'feedback-close',
+ 'feedback-bugcheck',
+ 'feedback-bugnew',
+ ),
),
'mediawiki.htmlform' => array(
'scripts' => 'resources/mediawiki/mediawiki.htmlform.js',
),
+ 'mediawiki.Title' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.Title.js',
+ 'dependencies' => 'mediawiki.util',
+ ),
+ 'mediawiki.Uri' => array(
+ 'scripts' => 'resources/mediawiki/mediawiki.Uri.js',
+ ),
'mediawiki.user' => array(
'scripts' => 'resources/mediawiki/mediawiki.user.js',
'dependencies' => array(
'jquery.cookie',
),
),
- 'mediawiki.page.startup' => array(
- 'scripts' => 'resources/mediawiki.page/mediawiki.page.startup.js',
- 'dependencies' => array(
- 'jquery.client',
- ),
- 'position' => 'top',
- ),
- 'mediawiki.page.ready' => array(
- 'scripts' => 'resources/mediawiki.page/mediawiki.page.ready.js',
- 'dependencies' => array(
- 'jquery.checkboxShiftClick',
- 'jquery.makeCollapsible',
- 'jquery.placeholder',
- ),
- ),
'mediawiki.util' => array(
'scripts' => 'resources/mediawiki/mediawiki.util.js',
'dependencies' => array(
'jquery.client',
'jquery.cookie',
'jquery.messageBox',
- 'jquery.mwPrototypes',
+ 'jquery.mwExtension',
),
+ 'messages' => array( 'showtoc', 'hidetoc' ),
+ 'position' => 'top', // For $wgPreloadJavaScriptMwUtil
),
- 'mediawiki.libs.jpegmeta' => array(
- 'scripts' => 'resources/mediawiki.libs/mediawiki.libs.jpegmeta.js',
+
+ /* MediaWiki Action */
+
+ 'mediawiki.action.edit' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.js',
+ 'dependencies' => array(
+ 'jquery.textSelection',
+ 'jquery.byteLimit',
+ ),
),
'mediawiki.action.history' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js',
@@ -510,22 +636,26 @@ return array(
'styles' => 'resources/mediawiki.action/mediawiki.action.history.diff.css',
'group' => 'mediawiki.action.history',
),
- 'mediawiki.action.edit' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.js',
- 'dependencies' => array(
- 'jquery.textSelection',
- 'jquery.byteLimit',
+ 'mediawiki.action.view.dblClickEdit' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js',
+ 'dependencies' => 'mediawiki.util',
+ ),
+ 'mediawiki.action.view.metadata' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.metadata.js',
+ 'messages' => array(
+ 'metadata-expand',
+ 'metadata-collapse',
),
),
'mediawiki.action.view.rightClickEdit' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.view.rightClickEdit.js',
),
- 'mediawiki.action.view.metadata' => array(
- 'scripts' => 'resources/mediawiki.action/mediawiki.action.view.metadata.js',
- 'messages' => array( 'metadata-expand', 'metadata-collapse' ),
- ),
'mediawiki.action.watch.ajax' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.watch.ajax.js',
+ 'dependencies' => array(
+ 'mediawiki.api.watch',
+ 'mediawiki.util',
+ ),
'messages' => array(
'watch',
'unwatch',
@@ -537,53 +667,7 @@ return array(
),
),
- /* Special pages */
-
- 'mediawiki.special' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.css',
- ),
- 'mediawiki.special.preferences' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
- 'messages' => array( 'email-address-validity-valid', 'email-address-validity-invalid' ),
- ),
- 'mediawiki.special.changeslist' => array(
- 'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
- 'dependencies' => array( 'jquery.makeCollapsible' ),
- ),
- 'mediawiki.special.search' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.search.js',
- 'styles' => 'resources/mediawiki.special/mediawiki.special.search.css',
- ),
- 'mediawiki.special.block' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.block.js',
- ),
- 'mediawiki.special.undelete' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.undelete.js',
- ),
- 'mediawiki.special.movePage' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
- 'dependencies' => 'jquery.byteLimit',
- ),
- 'mediawiki.special.recentchanges' => array(
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.recentchanges.js',
- 'dependencies' => array( 'mediawiki.special' ),
- 'position' => 'top',
- ),
- 'mediawiki.special.upload' => array(
- // @TODO: merge in remainder of mediawiki.legacy.upload
- 'scripts' => 'resources/mediawiki.special/mediawiki.special.upload.js',
- 'messages' => array(
- 'widthheight',
- 'size-bytes',
- 'size-kilobytes',
- 'size-megabytes',
- 'size-gigabytes',
- 'largefileserver',
- ),
- 'dependencies' => array( 'mediawiki.libs.jpegmeta' ),
- ),
+ /* MediaWiki Language */
'mediawiki.language' => array(
'scripts' => 'resources/mediawiki.language/mediawiki.language.js',
@@ -638,13 +722,132 @@ return array(
),
),
- /* mediawiki Legacy */
+ 'mediawiki.jqueryMsg' => array(
+ 'dependencies' => array( 'mediawiki.language', 'mediawiki.util' ),
+ 'scripts' => 'resources/mediawiki/mediawiki.jqueryMsg.js'
+ ),
+
+ /* MediaWiki Libs */
+
+ 'mediawiki.libs.jpegmeta' => array(
+ 'scripts' => 'resources/mediawiki.libs/mediawiki.libs.jpegmeta.js',
+ ),
+
+ /* MediaWiki Page */
+
+ 'mediawiki.page.ready' => array(
+ 'scripts' => 'resources/mediawiki.page/mediawiki.page.ready.js',
+ 'dependencies' => array(
+ 'jquery.checkboxShiftClick',
+ 'jquery.makeCollapsible',
+ 'jquery.placeholder',
+ 'jquery.mw-jump',
+ 'mediawiki.util',
+ ),
+ ),
+ 'mediawiki.page.startup' => array(
+ 'scripts' => 'resources/mediawiki.page/mediawiki.page.startup.js',
+ 'dependencies' => array(
+ 'jquery.client',
+ 'mediawiki.util',
+ ),
+ 'position' => 'top',
+ ),
+
+
+ /* MediaWiki Special pages */
+
+ 'mediawiki.special' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.js',
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.css',
+ ),
+ 'mediawiki.special.block' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.block.js',
+ 'dependencies' => array(
+ 'mediawiki.util',
+ ),
+ ),
+ 'mediawiki.special.changeemail' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.changeemail.js',
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.changeemail.css',
+ 'dependencies' => array(
+ 'mediawiki.util',
+ ),
+ 'messages' => array(
+ 'email-address-validity-valid',
+ 'email-address-validity-invalid',
+ ),
+ ),
+ 'mediawiki.special.changeslist' => array(
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
+ 'dependencies' => array( 'jquery.makeCollapsible' ),
+ ),
+ 'mediawiki.special.movePage' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
+ 'dependencies' => 'jquery.byteLimit',
+ ),
+ 'mediawiki.special.preferences' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
+ ),
+ 'mediawiki.special.recentchanges' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.recentchanges.js',
+ 'dependencies' => array( 'mediawiki.special' ),
+ 'position' => 'top',
+ ),
+ 'mediawiki.special.search' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.search.js',
+ 'styles' => 'resources/mediawiki.special/mediawiki.special.search.css',
+ ),
+ 'mediawiki.special.undelete' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.undelete.js',
+ ),
+ 'mediawiki.special.upload' => array(
+ // @TODO: merge in remainder of mediawiki.legacy.upload
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.upload.js',
+ 'messages' => array(
+ 'widthheight',
+ 'size-bytes',
+ 'size-kilobytes',
+ 'size-megabytes',
+ 'size-gigabytes',
+ 'largefileserver',
+ ),
+ 'dependencies' => array( 'mediawiki.libs.jpegmeta', 'mediawiki.util' ),
+ ),
+ 'mediawiki.special.javaScriptTest' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
+ 'messages' => array_merge( Skin::getSkinNameMessages(), array(
+ 'colon-separator',
+ 'javascripttest-pagetext-skins',
+ ) ),
+ 'dependencies' => array( 'jquery.qunit' ),
+ 'position' => 'top',
+ ),
+
+ /* MediaWiki Tests */
+
+ 'mediawiki.tests.qunit.testrunner' => array(
+ 'scripts' => 'tests/qunit/data/testrunner.js',
+ 'dependencies' => array(
+ 'jquery.qunit',
+ 'jquery.qunit.completenessTest',
+ 'mediawiki.page.startup',
+ 'mediawiki.page.ready',
+ ),
+ 'position' => 'top',
+ ),
+
+ /* MediaWiki Legacy */
'mediawiki.legacy.ajax' => array(
'scripts' => 'common/ajax.js',
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.legacy.wikibits',
+ 'dependencies' => array(
+ 'mediawiki.util',
+ 'mediawiki.legacy.wikibits',
+ ),
+ 'position' => 'top', // Temporary hack for legacy support
),
'mediawiki.legacy.commonPrint' => array(
'styles' => array( 'common/commonPrint.css' => array( 'media' => 'print' ) ),
@@ -685,6 +888,7 @@ return array(
'mediawiki.legacy.wikibits',
'jquery.byteLimit',
),
+ 'position' => 'top',
),
'mediawiki.legacy.shared' => array(
'styles' => array( 'common/shared.css' => array( 'media' => 'screen' ) ),
@@ -700,14 +904,19 @@ return array(
'scripts' => 'common/upload.js',
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.legacy.wikibits',
+ 'dependencies' => array(
+ 'mediawiki.legacy.wikibits',
+ 'mediawiki.util',
+ ),
),
'mediawiki.legacy.wikibits' => array(
'scripts' => 'common/wikibits.js',
'remoteBasePath' => $GLOBALS['wgStylePath'],
'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => 'mediawiki.language',
- 'messages' => array( 'showtoc', 'hidetoc' ),
+ 'dependencies' => array(
+ 'mediawiki.util',
+ ),
+ 'position' => 'top',
),
'mediawiki.legacy.wikiprintable' => array(
'styles' => array( 'common/wikiprintable.css' => array( 'media' => 'print' ) ),
diff --git a/resources/jquery.effects/jquery.effects.blind.js b/resources/jquery.effects/jquery.effects.blind.js
index d9a3b06b..2ecbbed3 100644
--- a/resources/jquery.effects/jquery.effects.blind.js
+++ b/resources/jquery.effects/jquery.effects.blind.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Blind 1.8.2
+ * jQuery UI Effects Blind 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Blind
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.blind = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left'];
+ var el = $(this), props = ['position','top','bottom','left','right'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.bounce.js b/resources/jquery.effects/jquery.effects.bounce.js
index 010585bf..f8e44730 100644
--- a/resources/jquery.effects/jquery.effects.bounce.js
+++ b/resources/jquery.effects/jquery.effects.bounce.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Bounce 1.8.2
+ * jQuery UI Effects Bounce 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Bounce
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.bounce = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left'];
+ var el = $(this), props = ['position','top','bottom','left','right'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.clip.js b/resources/jquery.effects/jquery.effects.clip.js
index 796ba89e..74845c08 100644
--- a/resources/jquery.effects/jquery.effects.clip.js
+++ b/resources/jquery.effects/jquery.effects.clip.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Clip 1.8.2
+ * jQuery UI Effects Clip 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Clip
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.clip = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left','height','width'];
+ var el = $(this), props = ['position','top','bottom','left','right','height','width'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.core.js b/resources/jquery.effects/jquery.effects.core.js
index 4589fe21..7f568964 100644
--- a/resources/jquery.effects/jquery.effects.core.js
+++ b/resources/jquery.effects/jquery.effects.core.js
@@ -1,13 +1,13 @@
/*
- * jQuery UI Effects 1.8.2
+ * jQuery UI Effects 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/
*/
-;jQuery.effects || (function($) {
+;jQuery.effects || (function($, undefined) {
$.effects = {};
@@ -19,7 +19,7 @@ $.effects = {};
// override the animation for color styles
$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor',
- 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'],
+ 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'],
function(i, attr) {
$.fx.step[attr] = function(fx) {
if (!fx.colorInit) {
@@ -52,7 +52,7 @@ function getRGB(color) {
return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
// Look for rgb(num%,num%,num%)
- if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color))
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
// Look for #a0b1c2
@@ -231,13 +231,12 @@ $.effects.animateClass = function(value, duration, easing, callback) {
easing = null;
}
- return this.each(function() {
-
+ return this.queue(function() {
var that = $(this),
originalStyleAttr = that.attr('style') || ' ',
originalStyle = filterStyles(getElementStyles.call(this)),
newStyle,
- className = that.attr('className');
+ className = that.attr('class');
$.each(classAnimationActions, function(i, action) {
if (value[action]) {
@@ -245,20 +244,26 @@ $.effects.animateClass = function(value, duration, easing, callback) {
}
});
newStyle = filterStyles(getElementStyles.call(this));
- that.attr('className', className);
-
- that.animate(styleDifference(originalStyle, newStyle), duration, easing, function() {
- $.each(classAnimationActions, function(i, action) {
- if (value[action]) { that[action + 'Class'](value[action]); }
- });
- // work around bug in IE by clearing the cssText before setting it
- if (typeof that.attr('style') == 'object') {
- that.attr('style').cssText = '';
- that.attr('style').cssText = originalStyleAttr;
- } else {
- that.attr('style', originalStyleAttr);
+ that.attr('class', className);
+
+ that.animate(styleDifference(originalStyle, newStyle), {
+ queue: false,
+ duration: duration,
+ easing: easing,
+ complete: function() {
+ $.each(classAnimationActions, function(i, action) {
+ if (value[action]) { that[action + 'Class'](value[action]); }
+ });
+ // work around bug in IE by clearing the cssText before setting it
+ if (typeof that.attr('style') == 'object') {
+ that.attr('style').cssText = '';
+ that.attr('style').cssText = originalStyleAttr;
+ } else {
+ that.attr('style', originalStyleAttr);
+ }
+ if (callback) { callback.apply(this, arguments); }
+ $.dequeue( this );
}
- if (callback) { callback.apply(this, arguments); }
});
});
};
@@ -301,7 +306,7 @@ $.fn.extend({
/******************************************************************************/
$.extend($.effects, {
- version: "1.8.2",
+ version: "1.8.17",
// Saves a set of properties in a data storage
save: function(element, set) {
@@ -362,9 +367,16 @@ $.extend($.effects, {
border: 'none',
margin: 0,
padding: 0
- });
+ }),
+ active = document.activeElement;
element.wrap(wrapper);
+
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+
wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
// transfer positioning properties to the wrapper
@@ -382,15 +394,25 @@ $.extend($.effects, {
props[pos] = 'auto';
}
});
- element.css({position: 'relative', top: 0, left: 0 });
+ element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' });
}
return wrapper.css(props).show();
},
removeWrapper: function(element) {
- if (element.parent().is('.ui-effects-wrapper'))
- return element.parent().replaceWith(element);
+ var parent,
+ active = document.activeElement;
+
+ if (element.parent().is('.ui-effects-wrapper')) {
+ parent = element.parent().replaceWith(element);
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+ return parent;
+ }
+
return element;
},
@@ -418,44 +440,72 @@ function _normalizeArguments(effect, options, speed, callback) {
speed = null;
options = {};
}
- if ($.isFunction(speed)) {
- callback = speed;
- speed = null;
- }
- if (typeof options == 'number' || $.fx.speeds[options]) {
+ if (typeof options == 'number' || $.fx.speeds[options]) {
callback = speed;
speed = options;
options = {};
}
+ if ($.isFunction(speed)) {
+ callback = speed;
+ speed = null;
+ }
options = options || {};
speed = speed || options.duration;
speed = $.fx.off ? 0 : typeof speed == 'number'
- ? speed : $.fx.speeds[speed] || $.fx.speeds._default;
+ ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default;
callback = callback || options.complete;
return [effect, options, speed, callback];
}
+function standardSpeed( speed ) {
+ // valid standard speeds
+ if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
+ return true;
+ }
+
+ // invalid strings - treat as "normal" speed
+ if ( typeof speed === "string" && !$.effects[ speed ] ) {
+ return true;
+ }
+
+ return false;
+}
+
$.fn.extend({
effect: function(effect, options, speed, callback) {
var args = _normalizeArguments.apply(this, arguments),
- // TODO: make effects takes actual parameters instead of a hash
+ // TODO: make effects take actual parameters instead of a hash
args2 = {
options: args[1],
duration: args[2],
callback: args[3]
},
+ mode = args2.options.mode,
effectMethod = $.effects[effect];
- return effectMethod && !$.fx.off ? effectMethod.call(this, args2) : this;
+ if ( $.fx.off || !effectMethod ) {
+ // delegate to the original method (e.g., .show()) if possible
+ if ( mode ) {
+ return this[ mode ]( args2.duration, args2.callback );
+ } else {
+ return this.each(function() {
+ if ( args2.callback ) {
+ args2.callback.call( this );
+ }
+ });
+ }
+ }
+
+ return effectMethod.call(this, args2);
},
_show: $.fn.show,
show: function(speed) {
- if (!speed || typeof speed == 'number' || $.fx.speeds[speed]) {
+ if ( standardSpeed( speed ) ) {
return this._show.apply(this, arguments);
} else {
var args = _normalizeArguments.apply(this, arguments);
@@ -466,7 +516,7 @@ $.fn.extend({
_hide: $.fn.hide,
hide: function(speed) {
- if (!speed || typeof speed == 'number' || $.fx.speeds[speed]) {
+ if ( standardSpeed( speed ) ) {
return this._hide.apply(this, arguments);
} else {
var args = _normalizeArguments.apply(this, arguments);
@@ -475,11 +525,10 @@ $.fn.extend({
}
},
- // jQuery core overloads toggle and create _toggle
+ // jQuery core overloads toggle and creates _toggle
__toggle: $.fn.toggle,
toggle: function(speed) {
- if (!speed || typeof speed == 'number' || $.fx.speeds[speed] ||
- typeof speed == 'boolean' || $.isFunction(speed)) {
+ if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
return this.__toggle.apply(this, arguments);
} else {
var args = _normalizeArguments.apply(this, arguments);
diff --git a/resources/jquery.effects/jquery.effects.drop.js b/resources/jquery.effects/jquery.effects.drop.js
index 0059c033..4bbd1087 100644
--- a/resources/jquery.effects/jquery.effects.drop.js
+++ b/resources/jquery.effects/jquery.effects.drop.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Drop 1.8.2
+ * jQuery UI Effects Drop 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Drop
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.drop = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left','opacity'];
+ var el = $(this), props = ['position','top','bottom','left','right','opacity'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.explode.js b/resources/jquery.effects/jquery.effects.explode.js
index 85ba1dcc..2c12fdb4 100644
--- a/resources/jquery.effects/jquery.effects.explode.js
+++ b/resources/jquery.effects/jquery.effects.explode.js
@@ -1,16 +1,16 @@
/*
- * jQuery UI Effects Explode 1.8.2
+ * jQuery UI Effects Explode 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Explode
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.explode = function(o) {
diff --git a/resources/jquery.effects/jquery.effects.fade.js b/resources/jquery.effects/jquery.effects.fade.js
new file mode 100644
index 00000000..bae63661
--- /dev/null
+++ b/resources/jquery.effects/jquery.effects.fade.js
@@ -0,0 +1,32 @@
+/*
+ * jQuery UI Effects Fade 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.fade = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'hide');
+
+ elem.animate({ opacity: mode }, {
+ queue: false,
+ duration: o.duration,
+ easing: o.options.easing,
+ complete: function() {
+ (o.callback && o.callback.apply(this, arguments));
+ elem.dequeue();
+ }
+ });
+ });
+};
+
+})(jQuery);
diff --git a/resources/jquery.effects/jquery.effects.fold.js b/resources/jquery.effects/jquery.effects.fold.js
index 946831bf..8e732739 100644
--- a/resources/jquery.effects/jquery.effects.fold.js
+++ b/resources/jquery.effects/jquery.effects.fold.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Fold 1.8.2
+ * jQuery UI Effects Fold 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Fold
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.fold = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left'];
+ var el = $(this), props = ['position','top','bottom','left','right'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.highlight.js b/resources/jquery.effects/jquery.effects.highlight.js
index 5e03e9ec..306d515b 100644
--- a/resources/jquery.effects/jquery.effects.highlight.js
+++ b/resources/jquery.effects/jquery.effects.highlight.js
@@ -1,16 +1,16 @@
/*
- * jQuery UI Effects Highlight 1.8.2
+ * jQuery UI Effects Highlight 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Highlight
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.highlight = function(o) {
return this.queue(function() {
diff --git a/resources/jquery.effects/jquery.effects.pulsate.js b/resources/jquery.effects/jquery.effects.pulsate.js
index 11c5af13..766690fb 100644
--- a/resources/jquery.effects/jquery.effects.pulsate.js
+++ b/resources/jquery.effects/jquery.effects.pulsate.js
@@ -1,16 +1,16 @@
/*
- * jQuery UI Effects Pulsate 1.8.2
+ * jQuery UI Effects Pulsate 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Pulsate
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.pulsate = function(o) {
return this.queue(function() {
diff --git a/resources/jquery.effects/jquery.effects.scale.js b/resources/jquery.effects/jquery.effects.scale.js
index 1efb781a..4cb73822 100644
--- a/resources/jquery.effects/jquery.effects.scale.js
+++ b/resources/jquery.effects/jquery.effects.scale.js
@@ -1,16 +1,16 @@
/*
- * jQuery UI Effects Scale 1.8.2
+ * jQuery UI Effects Scale 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Scale
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.puff = function(o) {
return this.queue(function() {
@@ -84,8 +84,8 @@ $.effects.size = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left','width','height','overflow','opacity'];
- var props1 = ['position','top','left','overflow','opacity']; // Always restore
+ var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity'];
+ var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore
var props2 = ['width','height','overflow']; // Copy for children
var cProps = ['fontSize'];
var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
diff --git a/resources/jquery.effects/jquery.effects.shake.js b/resources/jquery.effects/jquery.effects.shake.js
index 050894b4..b5a7352e 100644
--- a/resources/jquery.effects/jquery.effects.shake.js
+++ b/resources/jquery.effects/jquery.effects.shake.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Shake 1.8.2
+ * jQuery UI Effects Shake 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Shake
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.shake = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left'];
+ var el = $(this), props = ['position','top','bottom','left','right'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
diff --git a/resources/jquery.effects/jquery.effects.slide.js b/resources/jquery.effects/jquery.effects.slide.js
index d0719a7f..e41869b9 100644
--- a/resources/jquery.effects/jquery.effects.slide.js
+++ b/resources/jquery.effects/jquery.effects.slide.js
@@ -1,23 +1,23 @@
/*
- * jQuery UI Effects Slide 1.8.2
+ * jQuery UI Effects Slide 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Slide
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.slide = function(o) {
return this.queue(function() {
// Create element
- var el = $(this), props = ['position','top','left'];
+ var el = $(this), props = ['position','top','bottom','left','right'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
@@ -29,7 +29,7 @@ $.effects.slide = function(o) {
var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
- if (mode == 'show') el.css(ref, motion == 'pos' ? -distance : distance); // Shift
+ if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift
// Animation
var animation = {};
diff --git a/resources/jquery.effects/jquery.effects.transfer.js b/resources/jquery.effects/jquery.effects.transfer.js
index 4c212ca0..d51b9c43 100644
--- a/resources/jquery.effects/jquery.effects.transfer.js
+++ b/resources/jquery.effects/jquery.effects.transfer.js
@@ -1,16 +1,16 @@
/*
- * jQuery UI Effects Transfer 1.8.2
+ * jQuery UI Effects Transfer 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Transfer
*
* Depends:
* jquery.effects.core.js
*/
-(function($) {
+(function( $, undefined ) {
$.effects.transfer = function(o) {
return this.queue(function() {
diff --git a/resources/jquery.tipsy/jquery.tipsy.css b/resources/jquery.tipsy/jquery.tipsy.css
index 2e504c32..3680fbe2 100644
--- a/resources/jquery.tipsy/jquery.tipsy.css
+++ b/resources/jquery.tipsy/jquery.tipsy.css
@@ -11,7 +11,6 @@
border: solid 1px #a7d7f9;
color: black;
max-width: 15em;
- text-align: left;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
@@ -30,36 +29,36 @@
width: 11px;
height: 6px;
}
-.tipsy-n .tipsy-arrow {
+/* @noflip */ .tipsy-n .tipsy-arrow {
top: 0px;
left: 50%;
margin-left: -5px;
}
-.tipsy-nw .tipsy-arrow {
+/* @noflip */ .tipsy-nw .tipsy-arrow {
top: 1px;
left: 10px;
}
-.tipsy-ne .tipsy-arrow {
+/* @noflip */ .tipsy-ne .tipsy-arrow {
top: 1px;
right: 10px;
}
-.tipsy-s .tipsy-arrow {
+/* @noflip */ .tipsy-s .tipsy-arrow {
bottom: 0px;
left: 50%;
margin-left: -5px;
background-position: bottom left;
}
-.tipsy-sw .tipsy-arrow {
+/* @noflip */ .tipsy-sw .tipsy-arrow {
bottom: 0px;
left: 10px;
background-position: bottom left;
}
-.tipsy-se .tipsy-arrow {
+/* @noflip */ .tipsy-se .tipsy-arrow {
bottom: 0px;
right: 10px;
background-position: bottom left;
}
-.tipsy-e .tipsy-arrow {
+/* @noflip */ .tipsy-e .tipsy-arrow {
top: 50%;
margin-top: -5px;
right: 1px;
@@ -67,7 +66,7 @@
height: 11px;
background-position: top right;
}
-.tipsy-w .tipsy-arrow {
+/* @noflip */ .tipsy-w .tipsy-arrow {
top: 50%;
margin-top: -5px;
left: 0px;
diff --git a/resources/jquery.tipsy/jquery.tipsy.js b/resources/jquery.tipsy/jquery.tipsy.js
index 7c808734..a9fcec8f 100644
--- a/resources/jquery.tipsy/jquery.tipsy.js
+++ b/resources/jquery.tipsy/jquery.tipsy.js
@@ -1,10 +1,17 @@
// tipsy, facebook style tooltips for jquery
-// version 1.0.0a
+// version 1.0.0a*
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
-// releated under the MIT license
+// released under the MIT license
+
+// * This installation of tipsy includes several local modifications to both Javascript and CSS.
+// Please be careful when upgrading.
(function($) {
-
+
+ function maybeCall(thing, ctx) {
+ return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
+ };
+
function fixTitle($ele) {
if ($ele.attr('title') || typeof($ele.attr('original-title')) != 'string') {
$ele.attr('original-title', $ele.attr('title') || '').removeAttr('title');
@@ -71,6 +78,9 @@
}
$tip.css(tp).addClass('tipsy-' + gravity);
+ if (this.options.className) {
+ $tip.addClass(maybeCall(this.options.className, this.$element[0]));
+ }
if (this.options.fade) {
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}, 100);
@@ -174,6 +184,7 @@
};
$.fn.tipsy.defaults = {
+ className: null,
delayIn: 0,
delayOut: 0,
fade: true,
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-af.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-af.js
index 43fbf6cd..0922ef7a 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-af.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-af.js
@@ -1,4 +1,4 @@
-/* Afrikaans initialisation for the jQuery UI date picker plugin. */
+/* Afrikaans initialisation for the jQuery UI date picker plugin. */
/* Written by Renier Pretorius. */
jQuery(function($){
$.datepicker.regional['af'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js
index c799b48d..743a15db 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ar.js
@@ -1,6 +1,5 @@
-/* Arabic Translation for jQuery UI date picker plugin. */
-/* Khaled Al Horani -- koko.dw@gmail.com */
-/* خالد الحوراني -- koko.dw@gmail.com */
+/* Arabic Translation for jQuery UI date picker plugin. */
+/* Khaled Alhourani -- me@khaledalhourani.com */
/* NOTE: monthNames are the original months names and they are the Arabic names, not the new months name فبراير - يناير and there isn't any Arabic roots for these months */
jQuery(function($){
$.datepicker.regional['ar'] = {
@@ -8,15 +7,15 @@ jQuery(function($){
prevText: '<السابق',
nextText: 'التالي>',
currentText: 'اليوم',
- monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران',
+ monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'مايو', 'حزيران',
'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
- monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
- dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'],
- dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
- dayNamesMin: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
+ monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
+ dayNames: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ dayNamesShort: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ dayNamesMin: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
weekHeader: 'أسبوع',
dateFormat: 'dd/mm/yy',
- firstDay: 0,
+ firstDay: 6,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: ''};
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-az.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-az.js
index b5434057..57802a40 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-az.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-az.js
@@ -1,4 +1,4 @@
-/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */
+/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Jamil Najafov (necefov33@gmail.com). */
jQuery(function($) {
$.datepicker.regional['az'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js
index b5113f78..c19d20fb 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-bg.js
@@ -1,4 +1,4 @@
-/* Bulgarian initialisation for the jQuery UI date picker plugin. */
+/* Bulgarian initialisation for the jQuery UI date picker plugin. */
/* Written by Stoyan Kyosev (http://svest.org). */
jQuery(function($){
$.datepicker.regional['bg'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js
index 30ab826b..d4dc8b0e 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-bs.js
@@ -1,4 +1,4 @@
-/* Bosnian i18n for the jQuery UI date picker plugin. */
+/* Bosnian i18n for the jQuery UI date picker plugin. */
/* Written by Kenan Konjo. */
jQuery(function($){
$.datepicker.regional['bs'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js
index c3c07ea6..9805bcdb 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-cs.js
@@ -1,4 +1,4 @@
-/* Czech initialisation for the jQuery UI date picker plugin. */
+/* Czech initialisation for the jQuery UI date picker plugin. */
/* Written by Tomas Muller (tomas@tomas-muller.net). */
jQuery(function($){
$.datepicker.regional['cs'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-da.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-da.js
index 4a99a583..176044e1 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-da.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-da.js
@@ -1,4 +1,4 @@
-/* Danish initialisation for the jQuery UI date picker plugin. */
+/* Danish initialisation for the jQuery UI date picker plugin. */
/* Written by Jan Christensen ( deletestuff@gmail.com). */
jQuery(function($){
$.datepicker.regional['da'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-de.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-de.js
index ac2d516a..166f5371 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-de.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-de.js
@@ -1,4 +1,4 @@
-/* German initialisation for the jQuery UI date picker plugin. */
+/* German initialisation for the jQuery UI date picker plugin. */
/* Written by Milian Wolff (mail@milianw.de). */
jQuery(function($){
$.datepicker.regional['de'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-el.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-el.js
index 9542769d..6d775f99 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-el.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-el.js
@@ -1,4 +1,4 @@
-/* Greek (el) initialisation for the jQuery UI date picker plugin. */
+/* Greek (el) initialisation for the jQuery UI date picker plugin. */
/* Written by Alex Cicovic (http://www.alexcicovic.com) */
jQuery(function($){
$.datepicker.regional['el'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js
index aac7b619..16a096e7 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-en-GB.js
@@ -1,4 +1,4 @@
-/* English/UK initialisation for the jQuery UI date picker plugin. */
+/* English/UK initialisation for the jQuery UI date picker plugin. */
/* Written by Stuart. */
jQuery(function($){
$.datepicker.regional['en-GB'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js
index ba571568..6cabc2c4 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-eo.js
@@ -1,4 +1,4 @@
-/* Esperanto initialisation for the jQuery UI date picker plugin. */
+/* Esperanto initialisation for the jQuery UI date picker plugin. */
/* Written by Olivier M. (olivierweb@ifrance.com). */
jQuery(function($){
$.datepicker.regional['eo'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-et.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-et.js
index f97311f3..92f81f63 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-et.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-et.js
@@ -1,4 +1,4 @@
-/* Estonian initialisation for the jQuery UI date picker plugin. */
+/* Estonian initialisation for the jQuery UI date picker plugin. */
/* Written by Mart Sõmermaa (mrts.pydev at gmail com). */
jQuery(function($){
$.datepicker.regional['et'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js
index 9ba6ee22..bee4bfba 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-eu.js
@@ -1,4 +1,4 @@
-/* Euskarako oinarria 'UI date picker' jquery-ko extentsioarentzat */
+/* Euskarako oinarria 'UI date picker' jquery-ko extentsioarentzat */
/* Karrikas-ek itzulia (karrikas@karrikas.com) */
jQuery(function($){
$.datepicker.regional['eu'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js
index adb3709f..81de4da4 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-fa.js
@@ -1,4 +1,4 @@
-/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */
+/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */
/* Javad Mowlanezhad -- jmowla@gmail.com */
/* Jalali calendar should supported soon! (Its implemented but I have to test it) */
jQuery(function($) {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js
index c1436221..8a6cb99c 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-fo.js
@@ -1,4 +1,4 @@
-/* Faroese initialisation for the jQuery UI date picker plugin */
+/* Faroese initialisation for the jQuery UI date picker plugin */
/* Written by Sverri Mohr Olsen, sverrimo@gmail.com */
jQuery(function($){
$.datepicker.regional['fo'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js
index 38212d54..244eacff 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-fr-CH.js
@@ -1,4 +1,4 @@
-/* Swiss-French initialisation for the jQuery UI date picker plugin. */
+/* Swiss-French initialisation for the jQuery UI date picker plugin. */
/* Written Martin Voelkle (martin.voelkle@e-tc.ch). */
jQuery(function($){
$.datepicker.regional['fr-CH'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js
index 134bda65..7e793639 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-fr.js
@@ -1,23 +1,25 @@
-/* French initialisation for the jQuery UI date picker plugin. */
-/* Written by Keith Wood (kbwood{at}iinet.com.au) and Stéphane Nahmani (sholby@sholby.net). */
+/* French initialisation for the jQuery UI date picker plugin. */
+/* Written by Keith Wood (kbwood{at}iinet.com.au),
+ Stéphane Nahmani (sholby@sholby.net),
+ Stéphane Raimbault <stephane.raimbault@gmail.com> */
jQuery(function($){
$.datepicker.regional['fr'] = {
closeText: 'Fermer',
- prevText: '&#x3c;Préc',
- nextText: 'Suiv&#x3e;',
- currentText: 'Courant',
+ prevText: 'Précédent',
+ nextText: 'Suivant',
+ currentText: 'Aujourd\'hui',
monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin',
'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
- monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun',
- 'Jul','Aoû','Sep','Oct','Nov','Déc'],
+ monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin',
+ 'Juil.','Août','Sept.','Oct.','Nov.','Déc.'],
dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
- dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
- dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'],
- weekHeader: 'Sm',
+ dayNamesShort: ['Dim.','Lun.','Mar.','Mer.','Jeu.','Ven.','Sam.'],
+ dayNamesMin: ['D','L','M','M','J','V','S'],
+ weekHeader: 'Sem.',
dateFormat: 'dd/mm/yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''};
$.datepicker.setDefaults($.datepicker.regional['fr']);
-}); \ No newline at end of file
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js
new file mode 100644
index 00000000..278403e8
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-gl.js
@@ -0,0 +1,23 @@
+/* Galician localization for 'UI date picker' jQuery extension. */
+/* Translated by Jorge Barreiro <yortx.barry@gmail.com>. */
+jQuery(function($){
+ $.datepicker.regional['gl'] = {
+ closeText: 'Pechar',
+ prevText: '&#x3c;Ant',
+ nextText: 'Seg&#x3e;',
+ currentText: 'Hoxe',
+ monthNames: ['Xaneiro','Febreiro','Marzo','Abril','Maio','Xuño',
+ 'Xullo','Agosto','Setembro','Outubro','Novembro','Decembro'],
+ monthNamesShort: ['Xan','Feb','Mar','Abr','Mai','Xuñ',
+ 'Xul','Ago','Set','Out','Nov','Dec'],
+ dayNames: ['Domingo','Luns','Martes','M&eacute;rcores','Xoves','Venres','S&aacute;bado'],
+ dayNamesShort: ['Dom','Lun','Mar','M&eacute;r','Xov','Ven','S&aacute;b'],
+ dayNamesMin: ['Do','Lu','Ma','M&eacute;','Xo','Ve','S&aacute;'],
+ weekHeader: 'Sm',
+ dateFormat: 'dd/mm/yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['gl']);
+}); \ No newline at end of file
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-he.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-he.js
index 3b3dc387..88a78668 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-he.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-he.js
@@ -1,4 +1,4 @@
-/* Hebrew initialisation for the UI Datepicker extension. */
+/* Hebrew initialisation for the UI Datepicker extension. */
/* Written by Amir Hardon (ahardon at gmail dot com). */
jQuery(function($){
$.datepicker.regional['he'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js
index 0285c1aa..1eb3dd92 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-hr.js
@@ -1,4 +1,4 @@
-/* Croatian i18n for the jQuery UI date picker plugin. */
+/* Croatian i18n for the jQuery UI date picker plugin. */
/* Written by Vjekoslav Nesek. */
jQuery(function($){
$.datepicker.regional['hr'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js
index 249e7b0e..b28c268c 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-hu.js
@@ -2,22 +2,22 @@
/* Written by Istvan Karaszi (jquery@spam.raszi.hu). */
jQuery(function($){
$.datepicker.regional['hu'] = {
- closeText: 'bezárás',
- prevText: '&laquo;&nbsp;vissza',
- nextText: 'előre&nbsp;&raquo;',
+ closeText: 'bezár',
+ prevText: 'vissza',
+ nextText: 'előre',
currentText: 'ma',
monthNames: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június',
'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
monthNamesShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún',
'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'],
- dayNames: ['Vasárnap', 'Hétfö', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
+ dayNames: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
dayNamesShort: ['Vas', 'Hét', 'Ked', 'Sze', 'Csü', 'Pén', 'Szo'],
dayNamesMin: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
- weekHeader: 'Hé',
- dateFormat: 'yy-mm-dd',
+ weekHeader: 'Hét',
+ dateFormat: 'yy.mm.dd.',
firstDay: 1,
isRTL: false,
- showMonthAfterYear: false,
+ showMonthAfterYear: true,
yearSuffix: ''};
$.datepicker.setDefaults($.datepicker.regional['hu']);
});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js
index 79cd827c..7eb4268d 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ja.js
@@ -1,4 +1,4 @@
-/* Japanese initialisation for the jQuery UI date picker plugin. */
+/* Japanese initialisation for the jQuery UI date picker plugin. */
/* Written by Kentaro SATO (kentaro@ranvis.com). */
jQuery(function($){
$.datepicker.regional['ja'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js
new file mode 100644
index 00000000..79e3f248
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-kk.js
@@ -0,0 +1,23 @@
+/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */
+/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */
+jQuery(function($){
+ $.datepicker.regional['kk'] = {
+ closeText: 'Жабу',
+ prevText: '&#x3c;Алдыңғы',
+ nextText: 'Келесі&#x3e;',
+ currentText: 'Бүгін',
+ monthNames: ['Қаңтар','Ақпан','Наурыз','Сәуір','Мамыр','Маусым',
+ 'Шілде','Тамыз','Қыркүйек','Қазан','Қараша','Желтоқсан'],
+ monthNamesShort: ['Қаң','Ақп','Нау','Сәу','Мам','Мау',
+ 'Шіл','Там','Қыр','Қаз','Қар','Жел'],
+ dayNames: ['Жексенбі','Дүйсенбі','Сейсенбі','Сәрсенбі','Бейсенбі','Жұма','Сенбі'],
+ dayNamesShort: ['жкс','дсн','ссн','срс','бсн','жма','снб'],
+ dayNamesMin: ['Жк','Дс','Сс','Ср','Бс','Жм','Сн'],
+ weekHeader: 'Не',
+ dateFormat: 'dd.mm.yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['kk']);
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js
new file mode 100644
index 00000000..87c79d59
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-lb.js
@@ -0,0 +1,23 @@
+/* Luxembourgish initialisation for the jQuery UI date picker plugin. */
+/* Written by Michel Weimerskirch <michel@weimerskirch.net> */
+jQuery(function($){
+ $.datepicker.regional['lb'] = {
+ closeText: 'Fäerdeg',
+ prevText: 'Zréck',
+ nextText: 'Weider',
+ currentText: 'Haut',
+ monthNames: ['Januar','Februar','Mäerz','Abrëll','Mee','Juni',
+ 'Juli','August','September','Oktober','November','Dezember'],
+ monthNamesShort: ['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ dayNames: ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch', 'Donneschdeg', 'Freideg', 'Samschdeg'],
+ dayNamesShort: ['Son', 'Méi', 'Dën', 'Mët', 'Don', 'Fre', 'Sam'],
+ dayNamesMin: ['So','Mé','Dë','Më','Do','Fr','Sa'],
+ weekHeader: 'W',
+ dateFormat: 'dd.mm.yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['lb']);
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js
new file mode 100644
index 00000000..554ad20b
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-mk.js
@@ -0,0 +1,23 @@
+/* Macedonian i18n for the jQuery UI date picker plugin. */
+/* Written by Stojce Slavkovski. */
+jQuery(function($){
+ $.datepicker.regional['mk'] = {
+ closeText: 'Затвори',
+ prevText: '&#x3C;',
+ nextText: '&#x3E;',
+ currentText: 'Денес',
+ monthNames: ['Јануари','Фебруари','Март','Април','Мај','Јуни',
+ 'Јули','Август','Септември','Октомври','Ноември','Декември'],
+ monthNamesShort: ['Јан','Феб','Мар','Апр','Мај','Јун',
+ 'Јул','Авг','Сеп','Окт','Ное','Дек'],
+ dayNames: ['Недела','Понеделник','Вторник','Среда','Четврток','Петок','Сабота'],
+ dayNamesShort: ['Нед','Пон','Вто','Сре','Чет','Пет','Саб'],
+ dayNamesMin: ['Не','По','Вт','Ср','Че','Пе','Са'],
+ weekHeader: 'Сед',
+ dateFormat: 'dd/mm/yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['mk']);
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js
new file mode 100644
index 00000000..1e3432c0
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ml.js
@@ -0,0 +1,23 @@
+/* Malayalam (UTF-8) initialisation for the jQuery UI date picker plugin. */
+/* Written by Saji Nediyanchath (saji89@gmail.com). */
+jQuery(function($){
+ $.datepicker.regional['ml'] = {
+ closeText: 'ശരി',
+ prevText: 'മുന്നത്തെ',
+ nextText: 'അടുത്തത് ',
+ currentText: 'ഇന്ന്',
+ monthNames: ['ജനുവരി','ഫെബ്രുവരി','മാര്‍ച്ച്','ഏപ്രില്‍','മേയ്','ജൂണ്‍',
+ 'ജൂലൈ','ആഗസ്റ്റ്','സെപ്റ്റംബര്‍','ഒക്ടോബര്‍','നവംബര്‍','ഡിസംബര്‍'],
+ monthNamesShort: ['ജനു', 'ഫെബ്', 'മാര്‍', 'ഏപ്രി', 'മേയ്', 'ജൂണ്‍',
+ 'ജൂലാ', 'ആഗ', 'സെപ്', 'ഒക്ടോ', 'നവം', 'ഡിസ'],
+ dayNames: ['ഞായര്‍', 'തിങ്കള്‍', 'ചൊവ്വ', 'ബുധന്‍', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
+ dayNamesShort: ['ഞായ', 'തിങ്ക', 'ചൊവ്വ', 'ബുധ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
+ dayNamesMin: ['ഞാ','തി','ചൊ','ബു','വ്യാ','വെ','ശ'],
+ weekHeader: 'ആ',
+ dateFormat: 'dd/mm/yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['ml']);
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js
index 663d6bb2..781fe619 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-nl.js
@@ -1,4 +1,4 @@
-/* Dutch (UTF-8) initialisation for the jQuery UI date picker plugin. */
+/* Dutch (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Mathias Bynens <http://mathiasbynens.be/> */
jQuery(function($){
$.datepicker.regional.nl = {
@@ -8,13 +8,13 @@ jQuery(function($){
currentText: 'Vandaag',
monthNames: ['januari', 'februari', 'maart', 'april', 'mei', 'juni',
'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
- monthNamesShort: ['jan', 'feb', 'maa', 'apr', 'mei', 'jun',
+ monthNamesShort: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
dayNames: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
dayNamesShort: ['zon', 'maa', 'din', 'woe', 'don', 'vri', 'zat'],
dayNamesMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
+ dateFormat: 'dd-mm-yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-no.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-no.js
index 12b2356b..2507043a 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-no.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-no.js
@@ -1,23 +1,23 @@
/* Norwegian initialisation for the jQuery UI date picker plugin. */
/* Written by Naimdjon Takhirov (naimdjon@gmail.com). */
+
jQuery(function($){
- $.datepicker.regional['no'] = {
- closeText: 'Lukk',
- prevText: '&laquo;Forrige',
- nextText: 'Neste&raquo;',
- currentText: 'I dag',
- monthNames: ['Januar','Februar','Mars','April','Mai','Juni',
- 'Juli','August','September','Oktober','November','Desember'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Des'],
- dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'],
- dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'],
- dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'],
- weekHeader: 'Uke',
- dateFormat: 'yy-mm-dd',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['no']);
+ $.datepicker.regional['no'] = {
+ closeText: 'Lukk',
+ prevText: '&laquo;Forrige',
+ nextText: 'Neste&raquo;',
+ currentText: 'I dag',
+ monthNames: ['januar','februar','mars','april','mai','juni','juli','august','september','oktober','november','desember'],
+ monthNamesShort: ['jan','feb','mar','apr','mai','jun','jul','aug','sep','okt','nov','des'],
+ dayNamesShort: ['søn','man','tir','ons','tor','fre','lør'],
+ dayNames: ['søndag','mandag','tirsdag','onsdag','torsdag','fredag','lørdag'],
+ dayNamesMin: ['sø','ma','ti','on','to','fr','lø'],
+ weekHeader: 'Uke',
+ dateFormat: 'dd.mm.yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''
+ };
+ $.datepicker.setDefaults($.datepicker.regional['no']);
});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js
index 38818637..3cc8c796 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-pt-BR.js
@@ -10,9 +10,9 @@ jQuery(function($){
'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun',
'Jul','Ago','Set','Out','Nov','Dez'],
- dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sabado'],
- dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sab'],
- dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','Sab'],
+ dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
+ dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
+ dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
weekHeader: 'Sm',
dateFormat: 'dd/mm/yy',
firstDay: 0,
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js
new file mode 100644
index 00000000..f09f5aeb
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-pt.js
@@ -0,0 +1,22 @@
+/* Portuguese initialisation for the jQuery UI date picker plugin. */
+jQuery(function($){
+ $.datepicker.regional['pt'] = {
+ closeText: 'Fechar',
+ prevText: '&#x3c;Anterior',
+ nextText: 'Seguinte',
+ currentText: 'Hoje',
+ monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho',
+ 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
+ monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun',
+ 'Jul','Ago','Set','Out','Nov','Dez'],
+ dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
+ dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
+ dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
+ weekHeader: 'Sem',
+ dateFormat: 'dd/mm/yy',
+ firstDay: 0,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['pt']);
+}); \ No newline at end of file
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js
new file mode 100644
index 00000000..cf03cd4c
--- /dev/null
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-rm.js
@@ -0,0 +1,21 @@
+/* Romansh initialisation for the jQuery UI date picker plugin. */
+/* Written by Yvonne Gienal (yvonne.gienal@educa.ch). */
+jQuery(function($){
+ $.datepicker.regional['rm'] = {
+ closeText: 'Serrar',
+ prevText: '&#x3c;Suandant',
+ nextText: 'Precedent&#x3e;',
+ currentText: 'Actual',
+ monthNames: ['Schaner','Favrer','Mars','Avrigl','Matg','Zercladur', 'Fanadur','Avust','Settember','October','November','December'],
+ monthNamesShort: ['Scha','Fev','Mar','Avr','Matg','Zer', 'Fan','Avu','Sett','Oct','Nov','Dec'],
+ dayNames: ['Dumengia','Glindesdi','Mardi','Mesemna','Gievgia','Venderdi','Sonda'],
+ dayNamesShort: ['Dum','Gli','Mar','Mes','Gie','Ven','Som'],
+ dayNamesMin: ['Du','Gl','Ma','Me','Gi','Ve','So'],
+ weekHeader: 'emna',
+ dateFormat: 'dd/mm/yy',
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: ''};
+ $.datepicker.setDefaults($.datepicker.regional['rm']);
+});
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js
index 4fe95aea..6b140aff 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ro.js
@@ -1,4 +1,4 @@
-/* Romanian initialisation for the jQuery UI date picker plugin.
+/* Romanian initialisation for the jQuery UI date picker plugin.
*
* Written by Edmond L. (ll_edmond@walla.com)
* and Ionut G. Stan (ionut.g.stan@gmail.com)
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js
index b8091f9e..50a46135 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ru.js
@@ -13,7 +13,7 @@ jQuery(function($){
dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
- weekHeader: 'Не',
+ weekHeader: 'Нед',
dateFormat: 'dd.mm.yy',
firstDay: 1,
isRTL: false,
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js
index 8a6771c1..078d1b0f 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-sk.js
@@ -10,7 +10,7 @@ jQuery(function($){
'Júl','August','September','Október','November','December'],
monthNamesShort: ['Jan','Feb','Mar','Apr','Máj','Jún',
'Júl','Aug','Sep','Okt','Nov','Dec'],
- dayNames: ['Nedel\'a','Pondelok','Utorok','Streda','Štvrtok','Piatok','Sobota'],
+ dayNames: ['Nedeľa','Pondelok','Utorok','Streda','Štvrtok','Piatok','Sobota'],
dayNamesShort: ['Ned','Pon','Uto','Str','Štv','Pia','Sob'],
dayNamesMin: ['Ne','Po','Ut','St','Št','Pia','So'],
weekHeader: 'Ty',
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js
index be84104c..21974c56 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-sq.js
@@ -1,4 +1,4 @@
-/* Albanian initialisation for the jQuery UI date picker plugin. */
+/* Albanian initialisation for the jQuery UI date picker plugin. */
/* Written by Flakron Bytyqi (flakron@gmail.com). */
jQuery(function($){
$.datepicker.regional['sq'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js
index 8f8ea5e6..e7a8683e 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-sr-SR.js
@@ -1,4 +1,4 @@
-/* Serbian i18n for the jQuery UI date picker plugin. */
+/* Serbian i18n for the jQuery UI date picker plugin. */
/* Written by Dejan Dimić. */
jQuery(function($){
$.datepicker.regional['sr-SR'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js
index 49c9b4a3..0bd240e6 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-sr.js
@@ -1,4 +1,4 @@
-/* Serbian i18n for the jQuery UI date picker plugin. */
+/* Serbian i18n for the jQuery UI date picker plugin. */
/* Written by Dejan Dimić. */
jQuery(function($){
$.datepicker.regional['sr'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js
index 8236b62b..e5f549fb 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-sv.js
@@ -1,4 +1,4 @@
-/* Swedish initialisation for the jQuery UI date picker plugin. */
+/* Swedish initialisation for the jQuery UI date picker plugin. */
/* Written by Anders Ekdahl ( anders@nomadiz.se). */
jQuery(function($){
$.datepicker.regional['sv'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js
index 91116d38..40431ed8 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-ta.js
@@ -1,4 +1,4 @@
-/* Tamil (UTF-8) initialisation for the jQuery UI date picker plugin. */
+/* Tamil (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by S A Sureshkumar (saskumar@live.com). */
jQuery(function($){
$.datepicker.regional['ta'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-th.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-th.js
index 978500ab..2e5300cf 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-th.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-th.js
@@ -1,4 +1,4 @@
-/* Thai initialisation for the jQuery UI date picker plugin. */
+/* Thai initialisation for the jQuery UI date picker plugin. */
/* Written by pipo (pipo@sixhead.com). */
jQuery(function($){
$.datepicker.regional['th'] = {
@@ -7,7 +7,7 @@ jQuery(function($){
nextText: 'ถัดไป&nbsp;&raquo;',
currentText: 'วันนี้',
monthNames: ['มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน',
- 'กรกฏาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'],
+ 'กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'],
monthNamesShort: ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.',
'ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'],
dayNames: ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุกร์','เสาร์'],
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js
index 112b40e7..2718f5d1 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-uk.js
@@ -1,5 +1,6 @@
/* Ukrainian (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Maxim Drogobitskiy (maxdao@gmail.com). */
+/* Corrected by Igor Milla (igor.fsp.milla@gmail.com). */
jQuery(function($){
$.datepicker.regional['uk'] = {
closeText: 'Закрити',
@@ -13,7 +14,7 @@ jQuery(function($){
dayNames: ['неділя','понеділок','вівторок','середа','четвер','п’ятниця','субота'],
dayNamesShort: ['нед','пнд','вів','срд','чтв','птн','сбт'],
dayNamesMin: ['Нд','Пн','Вт','Ср','Чт','Пт','Сб'],
- weekHeader: 'Не',
+ weekHeader: 'Тиж',
dateFormat: 'dd/mm/yy',
firstDay: 1,
isRTL: false,
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js
index 9813a59e..1d8f7bbd 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-vi.js
@@ -1,4 +1,4 @@
-/* Vietnamese initialisation for the jQuery UI date picker plugin. */
+/* Vietnamese initialisation for the jQuery UI date picker plugin. */
/* Translated by Le Thanh Huy (lthanhhuy@cit.ctu.edu.vn). */
jQuery(function($){
$.datepicker.regional['vi'] = {
diff --git a/resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js b/resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js
index d211573c..dd51e359 100644
--- a/resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js
+++ b/resources/jquery.ui/i18n/jquery.ui.datepicker-zh-TW.js
@@ -1,4 +1,4 @@
-/* Chinese initialisation for the jQuery UI date picker plugin. */
+/* Chinese initialisation for the jQuery UI date picker plugin. */
/* Written by Ressol (ressol@gmail.com). */
jQuery(function($){
$.datepicker.regional['zh-TW'] = {
diff --git a/resources/jquery.ui/jquery.ui.accordion.js b/resources/jquery.ui/jquery.ui.accordion.js
index 898160a1..8fa8c0b8 100644
--- a/resources/jquery.ui/jquery.ui.accordion.js
+++ b/resources/jquery.ui/jquery.ui.accordion.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Accordion 1.8.11
+ * jQuery UI Accordion 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -518,7 +518,7 @@ $.widget( "ui.accordion", {
});
$.extend( $.ui.accordion, {
- version: "1.8.11",
+ version: "1.8.17",
animations: {
slide: function( options, additions ) {
options = $.extend({
@@ -550,11 +550,11 @@ $.extend( $.ui.accordion, {
// fix width before calculating height of hidden element
var s = options.toShow;
originalWidth = s[0].style.width;
- s.width( parseInt( s.parent().width(), 10 )
- - parseInt( s.css( "paddingLeft" ), 10 )
- - parseInt( s.css( "paddingRight" ), 10 )
- - ( parseInt( s.css( "borderLeftWidth" ), 10 ) || 0 )
- - ( parseInt( s.css( "borderRightWidth" ), 10) || 0 ) );
+ s.width( s.parent().width()
+ - parseFloat( s.css( "paddingLeft" ) )
+ - parseFloat( s.css( "paddingRight" ) )
+ - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 )
+ - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) );
$.each( fxAttrs, function( i, prop ) {
hideProps[ prop ] = "hide";
diff --git a/resources/jquery.ui/jquery.ui.autocomplete.js b/resources/jquery.ui/jquery.ui.autocomplete.js
index 41c13930..48ede20b 100644
--- a/resources/jquery.ui/jquery.ui.autocomplete.js
+++ b/resources/jquery.ui/jquery.ui.autocomplete.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Autocomplete 1.8.14
+ * jQuery UI Autocomplete 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -48,7 +48,7 @@ $.widget( "ui.autocomplete", {
"aria-haspopup": "true"
})
.bind( "keydown.autocomplete", function( event ) {
- if ( self.options.disabled || self.element.attr( "readonly" ) ) {
+ if ( self.options.disabled || self.element.propAttr( "readOnly" ) ) {
return;
}
@@ -215,6 +215,13 @@ $.widget( "ui.autocomplete", {
if ( $.fn.bgiframe ) {
this.menu.element.bgiframe();
}
+ // turning off autocomplete prevents the browser from remembering the
+ // value when navigating through history, so we re-enable autocomplete
+ // if the page is unloaded before the widget is destroyed. #7790
+ self.beforeunloadHandler = function() {
+ self.element.removeAttr( "autocomplete" );
+ };
+ $( window ).bind( "beforeunload", self.beforeunloadHandler );
},
destroy: function() {
@@ -225,6 +232,7 @@ $.widget( "ui.autocomplete", {
.removeAttr( "aria-autocomplete" )
.removeAttr( "aria-haspopup" );
this.menu.element.remove();
+ $( window ).unbind( "beforeunload", this.beforeunloadHandler );
$.Widget.prototype.destroy.call( this );
},
@@ -375,7 +383,9 @@ $.widget( "ui.autocomplete", {
_resizeMenu: function() {
var ul = this.menu.element;
ul.outerWidth( Math.max(
- ul.width( "" ).outerWidth(),
+ // Firefox wraps long text (possibly a rounding bug)
+ // so we add 1px to avoid the wrapping (#7513)
+ ul.width( "" ).outerWidth() + 1,
this.element.outerWidth()
) );
},
diff --git a/resources/jquery.ui/jquery.ui.button.js b/resources/jquery.ui/jquery.ui.button.js
index 9a70a01d..94dce1c3 100644
--- a/resources/jquery.ui/jquery.ui.button.js
+++ b/resources/jquery.ui/jquery.ui.button.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Button 1.8.11
+ * jQuery UI Button 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -13,17 +13,15 @@
*/
(function( $, undefined ) {
-var lastActive,
+var lastActive, startXPos, startYPos, clickDragged,
baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
stateClasses = "ui-state-hover ui-state-active ",
typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
- formResetHandler = function( event ) {
- $( ":ui-button", event.target.form ).each(function() {
- var inst = $( this ).data( "button" );
- setTimeout(function() {
- inst.refresh();
- }, 1 );
- });
+ formResetHandler = function() {
+ var buttons = $( this ).find( ":ui-button" );
+ setTimeout(function() {
+ buttons.button( "refresh" );
+ }, 1 );
},
radioGroup = function( radio ) {
var name = radio.name,
@@ -58,7 +56,7 @@ $.widget( "ui.button", {
.bind( "reset.button", formResetHandler );
if ( typeof this.options.disabled !== "boolean" ) {
- this.options.disabled = this.element.attr( "disabled" );
+ this.options.disabled = this.element.propAttr( "disabled" );
}
this._determineButtonType();
@@ -96,23 +94,54 @@ $.widget( "ui.button", {
}
$( this ).removeClass( hoverClass );
})
+ .bind( "click.button", function( event ) {
+ if ( options.disabled ) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ });
+
+ this.element
.bind( "focus.button", function() {
// no need to check disabled, focus won't be triggered anyway
- $( this ).addClass( focusClass );
+ self.buttonElement.addClass( focusClass );
})
.bind( "blur.button", function() {
- $( this ).removeClass( focusClass );
+ self.buttonElement.removeClass( focusClass );
});
if ( toggleButton ) {
this.element.bind( "change.button", function() {
+ if ( clickDragged ) {
+ return;
+ }
self.refresh();
});
+ // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
+ // prevents issue where button state changes but checkbox/radio checked state
+ // does not in Firefox (see ticket #6970)
+ this.buttonElement
+ .bind( "mousedown.button", function( event ) {
+ if ( options.disabled ) {
+ return;
+ }
+ clickDragged = false;
+ startXPos = event.pageX;
+ startYPos = event.pageY;
+ })
+ .bind( "mouseup.button", function( event ) {
+ if ( options.disabled ) {
+ return;
+ }
+ if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
+ clickDragged = true;
+ }
+ });
}
if ( this.type === "checkbox" ) {
this.buttonElement.bind( "click.button", function() {
- if ( options.disabled ) {
+ if ( options.disabled || clickDragged ) {
return false;
}
$( this ).toggleClass( "ui-state-active" );
@@ -120,11 +149,11 @@ $.widget( "ui.button", {
});
} else if ( this.type === "radio" ) {
this.buttonElement.bind( "click.button", function() {
- if ( options.disabled ) {
+ if ( options.disabled || clickDragged ) {
return false;
}
$( this ).addClass( "ui-state-active" );
- self.buttonElement.attr( "aria-pressed", true );
+ self.buttonElement.attr( "aria-pressed", "true" );
var radio = self.element[ 0 ];
radioGroup( radio )
@@ -133,7 +162,7 @@ $.widget( "ui.button", {
return $( this ).button( "widget" )[ 0 ];
})
.removeClass( "ui-state-active" )
- .attr( "aria-pressed", false );
+ .attr( "aria-pressed", "false" );
});
} else {
this.buttonElement
@@ -179,29 +208,26 @@ $.widget( "ui.button", {
// $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
// be overridden by individual plugins
this._setOption( "disabled", options.disabled );
+ this._resetButton();
},
_determineButtonType: function() {
-
+
if ( this.element.is(":checkbox") ) {
this.type = "checkbox";
+ } else if ( this.element.is(":radio") ) {
+ this.type = "radio";
+ } else if ( this.element.is("input") ) {
+ this.type = "input";
} else {
- if ( this.element.is(":radio") ) {
- this.type = "radio";
- } else {
- if ( this.element.is("input") ) {
- this.type = "input";
- } else {
- this.type = "button";
- }
- }
+ this.type = "button";
}
-
+
if ( this.type === "checkbox" || this.type === "radio" ) {
// we don't search against the document in case the element
// is disconnected from the DOM
var ancestor = this.element.parents().filter(":last"),
- labelSelector = "label[for=" + this.element.attr("id") + "]";
+ labelSelector = "label[for='" + this.element.attr("id") + "']";
this.buttonElement = ancestor.find( labelSelector );
if ( !this.buttonElement.length ) {
ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
@@ -246,10 +272,11 @@ $.widget( "ui.button", {
$.Widget.prototype._setOption.apply( this, arguments );
if ( key === "disabled" ) {
if ( value ) {
- this.element.attr( "disabled", true );
+ this.element.propAttr( "disabled", true );
} else {
- this.element.removeAttr( "disabled" );
+ this.element.propAttr( "disabled", false );
}
+ return;
}
this._resetButton();
},
@@ -264,22 +291,22 @@ $.widget( "ui.button", {
if ( $( this ).is( ":checked" ) ) {
$( this ).button( "widget" )
.addClass( "ui-state-active" )
- .attr( "aria-pressed", true );
+ .attr( "aria-pressed", "true" );
} else {
$( this ).button( "widget" )
.removeClass( "ui-state-active" )
- .attr( "aria-pressed", false );
+ .attr( "aria-pressed", "false" );
}
});
} else if ( this.type === "checkbox" ) {
if ( this.element.is( ":checked" ) ) {
this.buttonElement
.addClass( "ui-state-active" )
- .attr( "aria-pressed", true );
+ .attr( "aria-pressed", "true" );
} else {
this.buttonElement
.removeClass( "ui-state-active" )
- .attr( "aria-pressed", false );
+ .attr( "aria-pressed", "false" );
}
}
},
@@ -292,7 +319,7 @@ $.widget( "ui.button", {
return;
}
var buttonElement = this.buttonElement.removeClass( typeClasses ),
- buttonText = $( "<span></span>" )
+ buttonText = $( "<span></span>", this.element[0].ownerDocument )
.addClass( "ui-button-text" )
.html( this.options.label )
.appendTo( buttonElement.empty() )
@@ -350,6 +377,8 @@ $.widget( "ui.buttonset", {
},
refresh: function() {
+ var rtl = this.element.css( "direction" ) === "rtl";
+
this.buttons = this.element.find( this.options.items )
.filter( ":ui-button" )
.button( "refresh" )
@@ -362,10 +391,10 @@ $.widget( "ui.buttonset", {
})
.removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
.filter( ":first" )
- .addClass( "ui-corner-left" )
+ .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
.end()
.filter( ":last" )
- .addClass( "ui-corner-right" )
+ .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
.end()
.end();
},
diff --git a/resources/jquery.ui/jquery.ui.core.js b/resources/jquery.ui/jquery.ui.core.js
index 4589a47e..6d82ff13 100644
--- a/resources/jquery.ui/jquery.ui.core.js
+++ b/resources/jquery.ui/jquery.ui.core.js
@@ -1,5 +1,5 @@
/*!
- * jQuery UI 1.8.11
+ * jQuery UI 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -18,7 +18,7 @@ if ( $.ui.version ) {
}
$.extend( $.ui, {
- version: "1.8.11",
+ version: "1.8.17",
keyCode: {
ALT: 18,
@@ -58,6 +58,8 @@ $.extend( $.ui, {
// plugins
$.fn.extend({
+ propAttr: $.fn.prop || $.fn.attr,
+
_focus: $.fn.focus,
focus: function( delay, fn ) {
return typeof delay === "number" ?
@@ -174,6 +176,27 @@ $.each( [ "Width", "Height" ], function( i, name ) {
});
// selectors
+function focusable( element, isTabIndexNotNaN ) {
+ var nodeName = element.nodeName.toLowerCase();
+ if ( "area" === nodeName ) {
+ var map = element.parentNode,
+ mapName = map.name,
+ img;
+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
+ return false;
+ }
+ img = $( "img[usemap=#" + mapName + "]" )[0];
+ return !!img && visible( img );
+ }
+ return ( /input|select|textarea|button|object/.test( nodeName )
+ ? !element.disabled
+ : "a" == nodeName
+ ? element.href || isTabIndexNotNaN
+ : isTabIndexNotNaN)
+ // the element and all of its ancestors must be visible
+ && visible( element );
+}
+
function visible( element ) {
return !$( element ).parents().andSelf().filter(function() {
return $.curCSS( this, "visibility" ) === "hidden" ||
@@ -187,30 +210,13 @@ $.extend( $.expr[ ":" ], {
},
focusable: function( element ) {
- var nodeName = element.nodeName.toLowerCase(),
- tabIndex = $.attr( element, "tabindex" );
- if ( "area" === nodeName ) {
- var map = element.parentNode,
- mapName = map.name,
- img;
- if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
- return false;
- }
- img = $( "img[usemap=#" + mapName + "]" )[0];
- return !!img && visible( img );
- }
- return ( /input|select|textarea|button|object/.test( nodeName )
- ? !element.disabled
- : "a" == nodeName
- ? element.href || !isNaN( tabIndex )
- : !isNaN( tabIndex ))
- // the element and all of its ancestors must be visible
- && visible( element );
+ return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
},
tabbable: function( element ) {
- var tabIndex = $.attr( element, "tabindex" );
- return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
+ var tabIndex = $.attr( element, "tabindex" ),
+ isTabIndexNaN = isNaN( tabIndex );
+ return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
}
});
diff --git a/resources/jquery.ui/jquery.ui.datepicker.js b/resources/jquery.ui/jquery.ui.datepicker.js
index cad1d58c..bbf59175 100644
--- a/resources/jquery.ui/jquery.ui.datepicker.js
+++ b/resources/jquery.ui/jquery.ui.datepicker.js
@@ -1,22 +1,22 @@
/*
- * jQuery UI Datepicker 1.8.2
+ * jQuery UI Datepicker 1.8.17
*
- * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker
*
* Depends:
* jquery.ui.core.js
*/
+(function( $, undefined ) {
-(function($) { // hide the namespace
-
-$.extend($.ui, { datepicker: { version: "1.8.2" } });
+$.extend($.ui, { datepicker: { version: "1.8.17" } });
var PROP_NAME = 'datepicker';
var dpuuid = new Date().getTime();
+var instActive;
/* Date picker manager.
Use the singleton instance of this class, $.datepicker, to interact with the date picker.
@@ -105,15 +105,19 @@ function Datepicker() {
altFormat: '', // The date format to use for the alternate field
constrainInput: true, // The input is constrained by the current date format
showButtonPanel: false, // True to show button panel, false to not show it
- autoSize: false // True to size the input for the date format, false to leave as is
+ autoSize: false, // True to size the input for the date format, false to leave as is
+ disabled: false // The initial disabled state
};
$.extend(this._defaults, this.regional['']);
- this.dpDiv = $('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>');
+ this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
}
$.extend(Datepicker.prototype, {
/* Class name added to elements to indicate already configured with a date picker. */
markerClassName: 'hasDatepicker',
+
+ //Keep track of the maximum number of rows displayed (see #7043)
+ maxRows: 4,
/* Debug logging (if enabled). */
log: function () {
@@ -168,13 +172,13 @@ $.extend(Datepicker.prototype, {
/* Create a new instance object. */
_newInst: function(target, inline) {
- var id = target[0].id.replace(/([^A-Za-z0-9_])/g, '\\\\$1'); // escape jQuery meta chars
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
return {id: id, input: target, // associated target
selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
drawMonth: 0, drawYear: 0, // month being drawn
inline: inline, // is datepicker inline or not
dpDiv: (!inline ? this.dpDiv : // presentation div
- $('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))};
+ bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
},
/* Attach the date picker to an input field. */
@@ -194,6 +198,10 @@ $.extend(Datepicker.prototype, {
});
this._autoSize(inst);
$.data(target, PROP_NAME, inst);
+ //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
+ if( inst.settings.disabled ) {
+ this._disableDatepicker( target );
+ }
},
/* Make attachments based on settings. */
@@ -273,6 +281,13 @@ $.extend(Datepicker.prototype, {
this._setDate(inst, this._getDefaultDate(inst), true);
this._updateDatepicker(inst);
this._updateAlternate(inst);
+ //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
+ if( inst.settings.disabled ) {
+ this._disableDatepicker( target );
+ }
+ // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
+ // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
+ inst.dpDiv.css( "display", "block" );
},
/* Pop-up the date picker in a "dialog" box.
@@ -363,6 +378,8 @@ $.extend(Datepicker.prototype, {
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().removeClass('ui-state-disabled');
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
+ removeAttr("disabled");
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); // delete entry
@@ -386,6 +403,8 @@ $.extend(Datepicker.prototype, {
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().addClass('ui-state-disabled');
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
+ attr("disabled", "disabled");
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); // delete entry
@@ -444,10 +463,18 @@ $.extend(Datepicker.prototype, {
this._hideDatepicker();
}
var date = this._getDateDatepicker(target, true);
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
extendRemove(inst.settings, settings);
+ // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
+ if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
+ inst.settings.minDate = this._formatDate(inst, minDate);
+ if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
+ inst.settings.maxDate = this._formatDate(inst, maxDate);
this._attachments($(target), inst);
this._autoSize(inst);
- this._setDateDatepicker(target, date);
+ this._setDate(inst, date);
+ this._updateAlternate(inst);
this._updateDatepicker(inst);
}
},
@@ -500,10 +527,17 @@ $.extend(Datepicker.prototype, {
case 9: $.datepicker._hideDatepicker();
handled = false;
break; // hide on tab out
- case 13: var sel = $('td.' + $.datepicker._dayOverClass, inst.dpDiv).
- add($('td.' + $.datepicker._currentClass, inst.dpDiv));
+ case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
+ $.datepicker._currentClass + ')', inst.dpDiv);
if (sel[0])
$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
+ var onSelect = $.datepicker._get(inst, 'onSelect');
+ if (onSelect) {
+ var dateStr = $.datepicker._formatDate(inst);
+
+ // trigger custom callback
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
+ }
else
$.datepicker._hideDatepicker();
return false; // don't submit the form
@@ -565,7 +599,7 @@ $.extend(Datepicker.prototype, {
if ($.datepicker._get(inst, 'constrainInput')) {
var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
- return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
+ return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
}
},
@@ -591,6 +625,7 @@ $.extend(Datepicker.prototype, {
},
/* Pop-up the date picker for a given input field.
+ If false returned from beforeShow event handler do not show.
@param input element - the input field attached to the date picker or
event - if triggered by focus */
_showDatepicker: function(input) {
@@ -602,9 +637,17 @@ $.extend(Datepicker.prototype, {
var inst = $.datepicker._getInst(input);
if ($.datepicker._curInst && $.datepicker._curInst != inst) {
$.datepicker._curInst.dpDiv.stop(true, true);
+ if ( inst && $.datepicker._datepickerShowing ) {
+ $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
+ }
}
var beforeShow = $.datepicker._get(inst, 'beforeShow');
- extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
+ var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
+ if(beforeShowSettings === false){
+ //false
+ return;
+ }
+ extendRemove(inst.settings, beforeShowSettings);
inst.lastVal = null;
$.datepicker._lastInput = input;
$.datepicker._setDateFromField(inst);
@@ -625,6 +668,8 @@ $.extend(Datepicker.prototype, {
}
var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
$.datepicker._pos = null;
+ //to avoid flashes on Firefox
+ inst.dpDiv.empty();
// determine sizing offscreen
inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
$.datepicker._updateDatepicker(inst);
@@ -638,13 +683,15 @@ $.extend(Datepicker.prototype, {
var showAnim = $.datepicker._get(inst, 'showAnim');
var duration = $.datepicker._get(inst, 'duration');
var postProcess = function() {
- $.datepicker._datepickerShowing = true;
- var borders = $.datepicker._getBorders(inst.dpDiv);
- inst.dpDiv.find('iframe.ui-datepicker-cover'). // IE6- only
- css({left: -borders[0], top: -borders[1],
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !! cover.length ){
+ var borders = $.datepicker._getBorders(inst.dpDiv);
+ cover.css({left: -borders[0], top: -borders[1],
width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
+ }
};
inst.dpDiv.zIndex($(input).zIndex()+1);
+ $.datepicker._datepickerShowing = true;
if ($.effects && $.effects[showAnim])
inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
else
@@ -660,44 +707,41 @@ $.extend(Datepicker.prototype, {
/* Generate the date picker content. */
_updateDatepicker: function(inst) {
var self = this;
+ self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
var borders = $.datepicker._getBorders(inst.dpDiv);
- inst.dpDiv.empty().append(this._generateHTML(inst))
- .find('iframe.ui-datepicker-cover') // IE6- only
- .css({left: -borders[0], top: -borders[1],
- width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
- .end()
- .find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a')
- .bind('mouseout', function(){
- $(this).removeClass('ui-state-hover');
- if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
- if(this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
- })
- .bind('mouseover', function(){
- if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) {
- $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
- $(this).addClass('ui-state-hover');
- if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
- if(this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
- }
- })
- .end()
- .find('.' + this._dayOverClass + ' a')
- .trigger('mouseover')
- .end();
+ instActive = inst; // for delegate hover events
+ inst.dpDiv.empty().append(this._generateHTML(inst));
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
+ cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
+ }
+ inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
var numMonths = this._getNumberOfMonths(inst);
var cols = numMonths[1];
var width = 17;
+ inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
if (cols > 1)
inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
- else
- inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
'Class']('ui-datepicker-multi');
inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
'Class']('ui-datepicker-rtl');
if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
- inst.input.is(':visible') && !inst.input.is(':disabled'))
+ // #6694 - don't focus the input if it's already focused
+ // this breaks the change event in IE
+ inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
inst.input.focus();
+ // deffered render of the years select (to avoid flashes on Firefox)
+ if( inst.yearshtml ){
+ var origyearshtml = inst.yearshtml;
+ setTimeout(function(){
+ //assure that inst.yearshtml didn't change.
+ if( origyearshtml === inst.yearshtml && inst.yearshtml ){
+ inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
+ }
+ origyearshtml = inst.yearshtml = null;
+ }, 0);
+ }
},
/* Retrieve the size of left and top borders for an element.
@@ -737,7 +781,7 @@ $.extend(Datepicker.prototype, {
_findPos: function(obj) {
var inst = this._getInst(obj);
var isRTL = this._get(inst, 'isRTL');
- while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
}
var position = $(obj).offset();
@@ -753,9 +797,10 @@ $.extend(Datepicker.prototype, {
if (this._datepickerShowing) {
var showAnim = this._get(inst, 'showAnim');
var duration = this._get(inst, 'duration');
+ var self = this;
var postProcess = function() {
$.datepicker._tidyDialog(inst);
- this._curInst = null;
+ self._curInst = null;
};
if ($.effects && $.effects[showAnim])
inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
@@ -764,11 +809,11 @@ $.extend(Datepicker.prototype, {
(showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
if (!showAnim)
postProcess();
+ this._datepickerShowing = false;
var onClose = this._get(inst, 'onClose');
if (onClose)
onClose.apply((inst.input ? inst.input[0] : null),
- [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
- this._datepickerShowing = false;
+ [(inst.input ? inst.input.val() : ''), inst]);
this._lastInput = null;
if (this._inDialog) {
this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
@@ -790,12 +835,16 @@ $.extend(Datepicker.prototype, {
_checkExternalClick: function(event) {
if (!$.datepicker._curInst)
return;
- var $target = $(event.target);
- if ($target[0].id != $.datepicker._mainDivId &&
+
+ var $target = $(event.target),
+ inst = $.datepicker._getInst($target[0]);
+
+ if ( ( ( $target[0].id != $.datepicker._mainDivId &&
$target.parents('#' + $.datepicker._mainDivId).length == 0 &&
!$target.hasClass($.datepicker.markerClassName) &&
!$target.hasClass($.datepicker._triggerClass) &&
- $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
+ ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
$.datepicker._hideDatepicker();
},
@@ -835,7 +884,6 @@ $.extend(Datepicker.prototype, {
_selectMonthYear: function(id, select, period) {
var target = $(id);
var inst = this._getInst(target[0]);
- inst._selectingMonthYear = false;
inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
parseInt(select.options[select.selectedIndex].value,10);
@@ -843,15 +891,6 @@ $.extend(Datepicker.prototype, {
this._adjustDate(target);
},
- /* Restore input focus after not changing month/year. */
- _clickMonthYear: function(id) {
- var target = $(id);
- var inst = this._getInst(target[0]);
- if (inst.input && inst._selectingMonthYear && !$.browser.msie)
- inst.input.focus();
- inst._selectingMonthYear = !inst._selectingMonthYear;
- },
-
/* Action for selecting a day. */
_selectDay: function(id, month, year, td) {
var target = $(id);
@@ -948,6 +987,8 @@ $.extend(Datepicker.prototype, {
if (value == '')
return null;
var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
@@ -966,9 +1007,9 @@ $.extend(Datepicker.prototype, {
};
// Extract a number from the string value
var getNumber = function(match) {
- lookAhead(match);
+ var isDoubled = lookAhead(match);
var size = (match == '@' ? 14 : (match == '!' ? 20 :
- (match == 'y' ? 4 : (match == 'o' ? 3 : 2))));
+ (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
var digits = new RegExp('^\\d{1,' + size + '}');
var num = value.substring(iValue).match(digits);
if (!num)
@@ -978,14 +1019,24 @@ $.extend(Datepicker.prototype, {
};
// Extract a name from the string value and convert to an index
var getName = function(match, shortNames, longNames) {
- var names = (lookAhead(match) ? longNames : shortNames);
- for (var i = 0; i < names.length; i++) {
- if (value.substr(iValue, names[i].length) == names[i]) {
- iValue += names[i].length;
- return i + 1;
+ var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
+ return [ [k, v] ];
+ }).sort(function (a, b) {
+ return -(a[1].length - b[1].length);
+ });
+ var index = -1;
+ $.each(names, function (i, pair) {
+ var name = pair[1];
+ if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
+ index = pair[0];
+ iValue += name.length;
+ return false;
}
- }
- throw 'Unknown name at position ' + iValue;
+ });
+ if (index != -1)
+ return index + 1;
+ else
+ throw 'Unknown name at position ' + iValue;
};
// Confirm that a literal character matches the string value
var checkLiteral = function() {
@@ -1042,6 +1093,9 @@ $.extend(Datepicker.prototype, {
checkLiteral();
}
}
+ if (iValue < value.length){
+ throw "Extra/unparsed characters found in date: " + value.substring(iValue);
+ }
if (year == -1)
year = new Date().getFullYear();
else if (year < 100)
@@ -1060,7 +1114,7 @@ $.extend(Datepicker.prototype, {
}
var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
- throw 'Invalid date'; // E.g. 31/02/*
+ throw 'Invalid date'; // E.g. 31/02/00
return date;
},
@@ -1153,7 +1207,7 @@ $.extend(Datepicker.prototype, {
break;
case 'o':
output += formatNumber('o',
- (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3);
+ Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
break;
case 'm':
output += formatNumber('m', date.getMonth() + 1, 2);
@@ -1298,16 +1352,16 @@ $.extend(Datepicker.prototype, {
}
return new Date(year, month, day);
};
- date = (date == null ? defaultDate : (typeof date == 'string' ? offsetString(date) :
- (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : date)));
- date = (date && date.toString() == 'Invalid Date' ? defaultDate : date);
- if (date) {
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+ var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
+ newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
+ if (newDate) {
+ newDate.setHours(0);
+ newDate.setMinutes(0);
+ newDate.setSeconds(0);
+ newDate.setMilliseconds(0);
}
- return this._daylightSavingAdjust(date);
+ return this._daylightSavingAdjust(newDate);
},
/* Handle switch to/from daylight saving.
@@ -1324,13 +1378,13 @@ $.extend(Datepicker.prototype, {
/* Set the date(s) directly. */
_setDate: function(inst, date, noChange) {
- var clear = !(date);
+ var clear = !date;
var origMonth = inst.selectedMonth;
var origYear = inst.selectedYear;
- date = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
- inst.selectedDay = inst.currentDay = date.getDate();
- inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
- inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
+ var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
+ inst.selectedDay = inst.currentDay = newDate.getDate();
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
+ inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
this._notifyChange(inst);
this._adjustInstDate(inst);
@@ -1428,6 +1482,7 @@ $.extend(Datepicker.prototype, {
var html = '';
for (var row = 0; row < numMonths[0]; row++) {
var group = '';
+ this.maxRows = 4;
for (var col = 0; col < numMonths[1]; col++) {
var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
var cornerClass = ' ui-corner-all';
@@ -1462,7 +1517,9 @@ $.extend(Datepicker.prototype, {
if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
- var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
+ var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
+ var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
+ this.maxRows = numRows;
var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
calender += '<tr>';
@@ -1532,7 +1589,6 @@ $.extend(Datepicker.prototype, {
var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
monthHtml += '<select class="ui-datepicker-month" ' +
'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
- 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
'>';
for (var month = 0; month < 12; month++) {
if ((!inMinYear || month >= minDate.getMonth()) &&
@@ -1546,32 +1602,37 @@ $.extend(Datepicker.prototype, {
if (!showMonthAfterYear)
html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
// year selection
- if (secondary || !changeYear)
- html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
- else {
- // determine range of years to display
- var years = this._get(inst, 'yearRange').split(':');
- var thisYear = new Date().getFullYear();
- var determineYear = function(value) {
- var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
- (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
- parseInt(value, 10)));
- return (isNaN(year) ? thisYear : year);
- };
- var year = determineYear(years[0]);
- var endYear = Math.max(year, determineYear(years[1] || ''));
- year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
- endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
- html += '<select class="ui-datepicker-year" ' +
- 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
- 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
- '>';
- for (; year <= endYear; year++) {
- html += '<option value="' + year + '"' +
- (year == drawYear ? ' selected="selected"' : '') +
- '>' + year + '</option>';
+ if ( !inst.yearshtml ) {
+ inst.yearshtml = '';
+ if (secondary || !changeYear)
+ html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
+ else {
+ // determine range of years to display
+ var years = this._get(inst, 'yearRange').split(':');
+ var thisYear = new Date().getFullYear();
+ var determineYear = function(value) {
+ var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
+ (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
+ parseInt(value, 10)));
+ return (isNaN(year) ? thisYear : year);
+ };
+ var year = determineYear(years[0]);
+ var endYear = Math.max(year, determineYear(years[1] || ''));
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
+ inst.yearshtml += '<select class="ui-datepicker-year" ' +
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
+ '>';
+ for (; year <= endYear; year++) {
+ inst.yearshtml += '<option value="' + year + '"' +
+ (year == drawYear ? ' selected="selected"' : '') +
+ '>' + year + '</option>';
+ }
+ inst.yearshtml += '</select>';
+
+ html += inst.yearshtml;
+ inst.yearshtml = null;
}
- html += '</select>';
}
html += this._get(inst, 'yearSuffix');
if (showMonthAfterYear)
@@ -1599,9 +1660,9 @@ $.extend(Datepicker.prototype, {
_restrictMinMax: function(inst, date) {
var minDate = this._getMinMaxDate(inst, 'min');
var maxDate = this._getMinMaxDate(inst, 'max');
- date = (minDate && date < minDate ? minDate : date);
- date = (maxDate && date > maxDate ? maxDate : date);
- return date;
+ var newDate = (minDate && date < minDate ? minDate : date);
+ newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
+ return newDate;
},
/* Notify change of month/year. */
@@ -1625,7 +1686,7 @@ $.extend(Datepicker.prototype, {
/* Find the number of days in a given month. */
_getDaysInMonth: function(year, month) {
- return 32 - new Date(year, month, 32).getDate();
+ return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
},
/* Find the day of the week of the first of a month. */
@@ -1675,6 +1736,33 @@ $.extend(Datepicker.prototype, {
}
});
+/*
+ * Bind hover events for datepicker elements.
+ * Done via delegate so the binding only occurs once in the lifetime of the parent div.
+ * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
+ */
+function bindHover(dpDiv) {
+ var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
+ return dpDiv.bind('mouseout', function(event) {
+ var elem = $( event.target ).closest( selector );
+ if ( !elem.length ) {
+ return;
+ }
+ elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" );
+ })
+ .bind('mouseover', function(event) {
+ var elem = $( event.target ).closest( selector );
+ if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) ||
+ !elem.length ) {
+ return;
+ }
+ elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
+ elem.addClass('ui-state-hover');
+ if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover');
+ if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover');
+ });
+}
+
/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
$.extend(target, props);
@@ -1695,7 +1783,12 @@ function isArray(a) {
Object - settings for attaching new datepicker functionality
@return jQuery object */
$.fn.datepicker = function(options){
-
+
+ /* Verify an empty collection wasn't passed - Fixes #6976 */
+ if ( !this.length ) {
+ return this;
+ }
+
/* Initialise the date picker. */
if (!$.datepicker.initialized) {
$(document).mousedown($.datepicker._checkExternalClick).
@@ -1721,7 +1814,7 @@ $.fn.datepicker = function(options){
$.datepicker = new Datepicker(); // singleton instance
$.datepicker.initialized = false;
$.datepicker.uuid = new Date().getTime();
-$.datepicker.version = "1.8.2";
+$.datepicker.version = "1.8.17";
// Workaround for #4055
// Add another global to avoid noConflict issues with inline event handlers
diff --git a/resources/jquery.ui/jquery.ui.dialog.js b/resources/jquery.ui/jquery.ui.dialog.js
index f0656a2f..33119af2 100644
--- a/resources/jquery.ui/jquery.ui.dialog.js
+++ b/resources/jquery.ui/jquery.ui.dialog.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Dialog 1.8.11
+ * jQuery UI Dialog 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -37,6 +37,18 @@ var uiDialogClasses =
maxWidth: true,
minHeight: true,
minWidth: true
+ },
+ // support for jQuery 1.3.2 - handle common attrFn methods for dialog
+ attrFn = $.attrFn || {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true,
+ click: true
};
$.widget("ui.dialog", {
@@ -98,7 +110,7 @@ $.widget("ui.dialog", {
// setting tabIndex makes the div focusable
// setting outline to 0 prevents a border on focus in Mozilla
.attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
- if (options.closeOnEscape && event.keyCode &&
+ if (options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
event.keyCode === $.ui.keyCode.ESCAPE) {
self.close(event);
@@ -294,7 +306,7 @@ $.widget("ui.dialog", {
//Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
// http://ui.jquery.com/bugs/ticket/3193
- saveScroll = { scrollTop: self.element.attr('scrollTop'), scrollLeft: self.element.attr('scrollLeft') };
+ saveScroll = { scrollTop: self.element.scrollTop(), scrollLeft: self.element.scrollLeft() };
$.ui.dialog.maxZ += 1;
self.uiDialog.css('z-index', $.ui.dialog.maxZ);
self.element.attr(saveScroll);
@@ -317,9 +329,9 @@ $.widget("ui.dialog", {
self.moveToTop(true);
// prevent tabbing out of modal dialogs
- if (options.modal) {
- uiDialog.bind('keypress.ui-dialog', function(event) {
- if (event.keyCode !== $.ui.keyCode.TAB) {
+ if ( options.modal ) {
+ uiDialog.bind( "keydown.ui-dialog", function( event ) {
+ if ( event.keyCode !== $.ui.keyCode.TAB ) {
return;
}
@@ -376,12 +388,21 @@ $.widget("ui.dialog", {
{ click: props, text: name } :
props;
var button = $('<button type="button"></button>')
- .attr( props, true )
- .unbind('click')
.click(function() {
props.click.apply(self.element[0], arguments);
})
.appendTo(uiButtonSet);
+ // can't use .attr( props, true ) with jQuery 1.3.2.
+ $.each( props, function( key, value ) {
+ if ( key === "click" ) {
+ return;
+ }
+ if ( key in attrFn ) {
+ button[ key ]( value );
+ } else {
+ button.attr( key, value );
+ }
+ });
if ($.fn.button) {
button.button();
}
@@ -681,7 +702,7 @@ $.widget("ui.dialog", {
});
$.extend($.ui.dialog, {
- version: "1.8.11",
+ version: "1.8.17",
uuid: 0,
maxZ: 0,
@@ -727,7 +748,7 @@ $.extend($.ui.dialog.overlay, {
// allow closing by pressing the escape key
$(document).bind('keydown.dialog-overlay', function(event) {
- if (dialog.options.closeOnEscape && event.keyCode &&
+ if (dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
event.keyCode === $.ui.keyCode.ESCAPE) {
dialog.close(event);
@@ -802,8 +823,8 @@ $.extend($.ui.dialog.overlay, {
width: function() {
var scrollWidth,
offsetWidth;
- // handle IE 6
- if ($.browser.msie && $.browser.version < 7) {
+ // handle IE
+ if ( $.browser.msie ) {
scrollWidth = Math.max(
document.documentElement.scrollWidth,
document.body.scrollWidth
diff --git a/resources/jquery.ui/jquery.ui.draggable.js b/resources/jquery.ui/jquery.ui.draggable.js
index 5f367616..41aba9d6 100644
--- a/resources/jquery.ui/jquery.ui.draggable.js
+++ b/resources/jquery.ui/jquery.ui.draggable.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Draggable 1.8.11
+ * jQuery UI Draggable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -79,6 +79,18 @@ $.widget("ui.draggable", $.ui.mouse, {
this.handle = this._getHandle(event);
if (!this.handle)
return false;
+
+ if ( o.iframeFix ) {
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+ $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
+ .css({
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+ position: "absolute", opacity: "0.001", zIndex: 1000
+ })
+ .css($(this).offset())
+ .appendTo("body");
+ });
+ }
return true;
@@ -153,6 +165,10 @@ $.widget("ui.draggable", $.ui.mouse, {
this.helper.addClass("ui-draggable-dragging");
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
+ if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
+
return true;
},
@@ -212,6 +228,19 @@ $.widget("ui.draggable", $.ui.mouse, {
return false;
},
+ _mouseUp: function(event) {
+ if (this.options.iframeFix === true) {
+ $("div.ui-draggable-iframeFix").each(function() {
+ this.parentNode.removeChild(this);
+ }); //Remove frame helpers
+ }
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
+ if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
+
+ return $.ui.mouse.prototype._mouseUp.call(this, event);
+ },
+
cancel: function() {
if(this.helper.is(".ui-draggable-dragging")) {
@@ -241,7 +270,7 @@ $.widget("ui.draggable", $.ui.mouse, {
_createHelper: function(event) {
var o = this.options;
- var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
if(!helper.parents('body').length)
helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
@@ -335,23 +364,26 @@ $.widget("ui.draggable", $.ui.mouse, {
var o = this.options;
if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
if(o.containment == 'document' || o.containment == 'window') this.containment = [
- (o.containment == 'document' ? 0 : $(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left,
- (o.containment == 'document' ? 0 : $(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top,
+ o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
+ o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
(o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
(o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
];
if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
- var ce = $(o.containment)[0]; if(!ce) return;
- var co = $(o.containment).offset();
+ var c = $(o.containment);
+ var ce = c[0]; if(!ce) return;
+ var co = c.offset();
var over = ($(ce).css("overflow") != 'hidden');
this.containment = [
- co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
- co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
- co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
- co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
+ (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
+ (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
+ (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
+ (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
];
+ this.relative_container = c;
+
} else if(o.containment.constructor == Array) {
this.containment = o.containment;
}
@@ -393,20 +425,32 @@ $.widget("ui.draggable", $.ui.mouse, {
*/
if(this.originalPosition) { //If we are not dragging yet, we won't check for options
-
- if(this.containment) {
- if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
- if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
- if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
- if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+ var containment;
+ if(this.containment) {
+ if (this.relative_container){
+ var co = this.relative_container.offset();
+ containment = [ this.containment[0] + co.left,
+ this.containment[1] + co.top,
+ this.containment[2] + co.left,
+ this.containment[3] + co.top ];
+ }
+ else {
+ containment = this.containment;
+ }
+
+ if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
+ if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
}
if(o.grid) {
- var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
- pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+ //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
+ var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
+ pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
- var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
- pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
+ pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
}
}
@@ -461,7 +505,7 @@ $.widget("ui.draggable", $.ui.mouse, {
});
$.extend($.ui.draggable, {
- version: "1.8.11"
+ version: "1.8.17"
});
$.ui.plugin.add("draggable", "connectToSortable", {
@@ -546,7 +590,7 @@ $.ui.plugin.add("draggable", "connectToSortable", {
//Now we fake the start of dragging for the sortable instance,
//by cloning the list group item, appending it to the sortable and using it as inst.currentItem
//We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
- this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
+ this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
this.instance.options.helper = function() { return ui.helper[0]; };
@@ -616,24 +660,6 @@ $.ui.plugin.add("draggable", "cursor", {
}
});
-$.ui.plugin.add("draggable", "iframeFix", {
- start: function(event, ui) {
- var o = $(this).data('draggable').options;
- $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
- $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
- .css({
- width: this.offsetWidth+"px", height: this.offsetHeight+"px",
- position: "absolute", opacity: "0.001", zIndex: 1000
- })
- .css($(this).offset())
- .appendTo("body");
- });
- },
- stop: function(event, ui) {
- $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
- }
-});
-
$.ui.plugin.add("draggable", "opacity", {
start: function(event, ui) {
var t = $(ui.helper), o = $(this).data('draggable').options;
diff --git a/resources/jquery.ui/jquery.ui.droppable.js b/resources/jquery.ui/jquery.ui.droppable.js
index 7d6b8975..afce00aa 100644
--- a/resources/jquery.ui/jquery.ui.droppable.js
+++ b/resources/jquery.ui/jquery.ui.droppable.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Droppable 1.8.11
+ * jQuery UI Droppable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -147,7 +147,7 @@ $.widget("ui.droppable", {
});
$.extend($.ui.droppable, {
- version: "1.8.11"
+ version: "1.8.17"
});
$.ui.intersect = function(draggable, droppable, toleranceMode) {
@@ -227,7 +227,7 @@ $.ui.ddmanager = {
if(!this.options) return;
if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
- dropped = dropped || this._drop.call(this, event);
+ dropped = this._drop.call(this, event) || dropped;
if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
this.isout = 1; this.isover = 0;
@@ -238,6 +238,12 @@ $.ui.ddmanager = {
return dropped;
},
+ dragStart: function( draggable, event ) {
+ //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
+ draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() {
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
+ });
+ },
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
@@ -279,6 +285,11 @@ $.ui.ddmanager = {
}
});
+ },
+ dragStop: function( draggable, event ) {
+ draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" );
+ //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
}
};
diff --git a/resources/jquery.ui/jquery.ui.mouse.js b/resources/jquery.ui/jquery.ui.mouse.js
index b8db85ce..6bb65de0 100644
--- a/resources/jquery.ui/jquery.ui.mouse.js
+++ b/resources/jquery.ui/jquery.ui.mouse.js
@@ -1,5 +1,5 @@
/*!
- * jQuery UI Mouse 1.8.11
+ * jQuery UI Mouse 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -12,6 +12,11 @@
*/
(function( $, undefined ) {
+var mouseHandled = false;
+$( document ).mouseup( function( e ) {
+ mouseHandled = false;
+});
+
$.widget("ui.mouse", {
options: {
cancel: ':input,option',
@@ -44,9 +49,7 @@ $.widget("ui.mouse", {
_mouseDown: function(event) {
// don't let more than one widget handle mouseStart
- // TODO: figure out why we have to use originalEvent
- event.originalEvent = event.originalEvent || {};
- if (event.originalEvent.mouseHandled) { return; }
+ if( mouseHandled ) { return };
// we may have missed mouseup (out of window)
(this._mouseStarted && this._mouseUp(event));
@@ -55,7 +58,9 @@ $.widget("ui.mouse", {
var self = this,
btnIsLeft = (event.which == 1),
- elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+ // event.target.nodeName works around a bug in IE 8 with
+ // disabled inputs (#7620)
+ elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
return true;
}
@@ -92,7 +97,8 @@ $.widget("ui.mouse", {
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
event.preventDefault();
- event.originalEvent.mouseHandled = true;
+
+ mouseHandled = true;
return true;
},
diff --git a/resources/jquery.ui/jquery.ui.position.js b/resources/jquery.ui/jquery.ui.position.js
index b66e59ef..8eb30f62 100644
--- a/resources/jquery.ui/jquery.ui.position.js
+++ b/resources/jquery.ui/jquery.ui.position.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Position 1.8.11
+ * jQuery UI Position 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -14,6 +14,7 @@ $.ui = $.ui || {};
var horizontalPositions = /left|center|right/,
verticalPositions = /top|center|bottom/,
center = "center",
+ support = {},
_position = $.fn.position,
_offset = $.fn.offset;
@@ -121,9 +122,11 @@ $.fn.position = function( options ) {
position.top -= elemHeight / 2;
}
- // prevent fractions (see #5280)
- position.left = Math.round( position.left );
- position.top = Math.round( position.top );
+ // prevent fractions if jQuery version doesn't support them (see #5280)
+ if ( !support.fractions ) {
+ position.left = Math.round( position.left );
+ position.top = Math.round( position.top );
+ }
collisionPosition = {
left: position.left - marginLeft,
@@ -249,4 +252,47 @@ if ( !$.offset.setOffset ) {
};
}
+// fraction support test (older versions of jQuery don't support fractions)
+(function () {
+ var body = document.getElementsByTagName( "body" )[ 0 ],
+ div = document.createElement( "div" ),
+ testElement, testElementParent, testElementStyle, offset, offsetTotal;
+
+ //Create a "fake body" for testing based on method used in jQuery.support
+ testElement = document.createElement( body ? "div" : "body" );
+ testElementStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0,
+ background: "none"
+ };
+ if ( body ) {
+ jQuery.extend( testElementStyle, {
+ position: "absolute",
+ left: "-1000px",
+ top: "-1000px"
+ });
+ }
+ for ( var i in testElementStyle ) {
+ testElement.style[ i ] = testElementStyle[ i ];
+ }
+ testElement.appendChild( div );
+ testElementParent = body || document.documentElement;
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
+
+ div.style.cssText = "position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;";
+
+ offset = $( div ).offset( function( _, offset ) {
+ return offset;
+ }).offset();
+
+ testElement.innerHTML = "";
+ testElementParent.removeChild( testElement );
+
+ offsetTotal = offset.top + offset.left + ( body ? 2000 : 0 );
+ support.fractions = offsetTotal > 21 && offsetTotal < 22;
+})();
+
}( jQuery ));
diff --git a/resources/jquery.ui/jquery.ui.progressbar.js b/resources/jquery.ui/jquery.ui.progressbar.js
index c432132a..27bae872 100644
--- a/resources/jquery.ui/jquery.ui.progressbar.js
+++ b/resources/jquery.ui/jquery.ui.progressbar.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Progressbar 1.8.11
+ * jQuery UI Progressbar 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -95,6 +95,7 @@ $.widget( "ui.progressbar", {
}
this.valueDiv
+ .toggle( value > this.min )
.toggleClass( "ui-corner-right", value === this.options.max )
.width( percentage.toFixed(0) + "%" );
this.element.attr( "aria-valuenow", value );
@@ -102,7 +103,7 @@ $.widget( "ui.progressbar", {
});
$.extend( $.ui.progressbar, {
- version: "1.8.11"
+ version: "1.8.17"
});
})( jQuery );
diff --git a/resources/jquery.ui/jquery.ui.resizable.js b/resources/jquery.ui/jquery.ui.resizable.js
index 1d1c906e..52b1fc4f 100644
--- a/resources/jquery.ui/jquery.ui.resizable.js
+++ b/resources/jquery.ui/jquery.ui.resizable.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Resizable 1.8.11
+ * jQuery UI Resizable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -176,10 +176,12 @@ $.widget("ui.resizable", $.ui.mouse, {
$(this.element)
.addClass("ui-resizable-autohide")
.hover(function() {
+ if (o.disabled) return;
$(this).removeClass("ui-resizable-autohide");
self._handles.show();
},
function(){
+ if (o.disabled) return;
if (!self.resizing) {
$(this).addClass("ui-resizable-autohide");
self._handles.hide();
@@ -291,6 +293,8 @@ $.widget("ui.resizable", $.ui.mouse, {
// Calculate the attrs that will be change
var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
+ // Put this in the mouseDrag handler since the user can start pressing shift while resizing
+ this._updateVirtualBoundaries(event.shiftKey);
if (this._aspectRatio || event.shiftKey)
data = this._updateRatio(data, event);
@@ -349,6 +353,32 @@ $.widget("ui.resizable", $.ui.mouse, {
},
+ _updateVirtualBoundaries: function(forceAspectRatio) {
+ var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
+
+ b = {
+ minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
+ maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
+ minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
+ maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
+ };
+
+ if(this._aspectRatio || forceAspectRatio) {
+ // We want to create an enclosing box whose aspect ration is the requested one
+ // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
+ pMinWidth = b.minHeight * this.aspectRatio;
+ pMinHeight = b.minWidth / this.aspectRatio;
+ pMaxWidth = b.maxHeight * this.aspectRatio;
+ pMaxHeight = b.maxWidth / this.aspectRatio;
+
+ if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
+ if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
+ if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
+ if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
+ }
+ this._vBoundaries = b;
+ },
+
_updateCache: function(data) {
var o = this.options;
this.offset = this.helper.offset();
@@ -362,8 +392,8 @@ $.widget("ui.resizable", $.ui.mouse, {
var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
- if (data.height) data.width = (csize.height * this.aspectRatio);
- else if (data.width) data.height = (csize.width / this.aspectRatio);
+ if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
+ else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
if (a == 'sw') {
data.left = cpos.left + (csize.width - data.width);
@@ -379,7 +409,7 @@ $.widget("ui.resizable", $.ui.mouse, {
_respectSize: function(data, event) {
- var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
+ var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
@@ -519,7 +549,7 @@ $.widget("ui.resizable", $.ui.mouse, {
});
$.extend($.ui.resizable, {
- version: "1.8.11"
+ version: "1.8.17"
});
/*
diff --git a/resources/jquery.ui/jquery.ui.selectable.js b/resources/jquery.ui/jquery.ui.selectable.js
index e3b91328..d790ac0d 100644
--- a/resources/jquery.ui/jquery.ui.selectable.js
+++ b/resources/jquery.ui/jquery.ui.selectable.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Selectable 1.8.11
+ * jQuery UI Selectable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -33,6 +33,7 @@ $.widget("ui.selectable", $.ui.mouse, {
var selectees;
this.refresh = function() {
selectees = $(self.options.filter, self.element[0]);
+ selectees.addClass("ui-selectee");
selectees.each(function() {
var $this = $(this);
var pos = $this.offset();
@@ -102,7 +103,7 @@ $.widget("ui.selectable", $.ui.mouse, {
this.selectees.filter('.ui-selected').each(function() {
var selectee = $.data(this, "selectable-item");
selectee.startselected = true;
- if (!event.metaKey) {
+ if (!event.metaKey && !event.ctrlKey) {
selectee.$element.removeClass('ui-selected');
selectee.selected = false;
selectee.$element.addClass('ui-unselecting');
@@ -117,7 +118,7 @@ $.widget("ui.selectable", $.ui.mouse, {
$(event.target).parents().andSelf().each(function() {
var selectee = $.data(this, "selectable-item");
if (selectee) {
- var doSelect = !event.metaKey || !selectee.$element.hasClass('ui-selected');
+ var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
selectee.$element
.removeClass(doSelect ? "ui-unselecting" : "ui-selected")
.addClass(doSelect ? "ui-selecting" : "ui-unselecting");
@@ -187,7 +188,7 @@ $.widget("ui.selectable", $.ui.mouse, {
} else {
// UNSELECT
if (selectee.selecting) {
- if (event.metaKey && selectee.startselected) {
+ if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
selectee.$element.removeClass('ui-selecting');
selectee.selecting = false;
selectee.$element.addClass('ui-selected');
@@ -206,7 +207,7 @@ $.widget("ui.selectable", $.ui.mouse, {
}
}
if (selectee.selected) {
- if (!event.metaKey && !selectee.startselected) {
+ if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
selectee.$element.removeClass('ui-selected');
selectee.selected = false;
@@ -260,7 +261,7 @@ $.widget("ui.selectable", $.ui.mouse, {
});
$.extend($.ui.selectable, {
- version: "1.8.11"
+ version: "1.8.17"
});
})(jQuery);
diff --git a/resources/jquery.ui/jquery.ui.slider.js b/resources/jquery.ui/jquery.ui.slider.js
index f02a922f..53fdc42a 100644
--- a/resources/jquery.ui/jquery.ui.slider.js
+++ b/resources/jquery.ui/jquery.ui.slider.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Slider 1.8.11
+ * jQuery UI Slider 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -36,7 +36,11 @@ $.widget( "ui.slider", $.ui.mouse, {
_create: function() {
var self = this,
- o = this.options;
+ o = this.options,
+ existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
+ handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
+ handleCount = ( o.values && o.values.length ) || 1,
+ handles = [];
this._keySliding = false;
this._mouseSliding = false;
@@ -50,57 +54,35 @@ $.widget( "ui.slider", $.ui.mouse, {
" ui-slider-" + this.orientation +
" ui-widget" +
" ui-widget-content" +
- " ui-corner-all" );
-
- if ( o.disabled ) {
- this.element.addClass( "ui-slider-disabled ui-disabled" );
- }
+ " ui-corner-all" +
+ ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
this.range = $([]);
if ( o.range ) {
if ( o.range === true ) {
- this.range = $( "<div></div>" );
if ( !o.values ) {
o.values = [ this._valueMin(), this._valueMin() ];
}
if ( o.values.length && o.values.length !== 2 ) {
o.values = [ o.values[0], o.values[0] ];
}
- } else {
- this.range = $( "<div></div>" );
- }
-
- this.range
- .appendTo( this.element )
- .addClass( "ui-slider-range" );
-
- if ( o.range === "min" || o.range === "max" ) {
- this.range.addClass( "ui-slider-range-" + o.range );
}
- // note: this isn't the most fittingly semantic framework class for this element,
- // but worked best visually with a variety of themes
- this.range.addClass( "ui-widget-header" );
- }
-
- if ( $( ".ui-slider-handle", this.element ).length === 0 ) {
- $( "<a href='#'></a>" )
+ this.range = $( "<div></div>" )
.appendTo( this.element )
- .addClass( "ui-slider-handle" );
+ .addClass( "ui-slider-range" +
+ // note: this isn't the most fittingly semantic framework class for this element,
+ // but worked best visually with a variety of themes
+ " ui-widget-header" +
+ ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
}
- if ( o.values && o.values.length ) {
- while ( $(".ui-slider-handle", this.element).length < o.values.length ) {
- $( "<a href='#'></a>" )
- .appendTo( this.element )
- .addClass( "ui-slider-handle" );
- }
+ for ( var i = existingHandles.length; i < handleCount; i += 1 ) {
+ handles.push( handle );
}
- this.handles = $( ".ui-slider-handle", this.element )
- .addClass( "ui-state-default" +
- " ui-corner-all" );
+ this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) );
this.handle = this.handles.eq( 0 );
@@ -466,6 +448,7 @@ $.widget( "ui.slider", $.ui.mouse, {
this.options.value = this._trimAlignValue( newValue );
this._refreshValue();
this._change( null, 0 );
+ return;
}
return this._value();
@@ -480,6 +463,7 @@ $.widget( "ui.slider", $.ui.mouse, {
this.options.values[ index ] = this._trimAlignValue( newValue );
this._refreshValue();
this._change( null, index );
+ return;
}
if ( arguments.length ) {
@@ -518,10 +502,10 @@ $.widget( "ui.slider", $.ui.mouse, {
if ( value ) {
this.handles.filter( ".ui-state-focus" ).blur();
this.handles.removeClass( "ui-state-hover" );
- this.handles.attr( "disabled", "disabled" );
+ this.handles.propAttr( "disabled", true );
this.element.addClass( "ui-disabled" );
} else {
- this.handles.removeAttr( "disabled" );
+ this.handles.propAttr( "disabled", false );
this.element.removeClass( "ui-disabled" );
}
break;
@@ -592,7 +576,7 @@ $.widget( "ui.slider", $.ui.mouse, {
return this._valueMax();
}
var step = ( this.options.step > 0 ) ? this.options.step : 1,
- valModStep = (val - this._valueMin()) % step;
+ valModStep = (val - this._valueMin()) % step,
alignValue = val - valModStep;
if ( Math.abs(valModStep) * 2 >= step ) {
@@ -676,7 +660,7 @@ $.widget( "ui.slider", $.ui.mouse, {
});
$.extend( $.ui.slider, {
- version: "1.8.11"
+ version: "1.8.17"
});
}(jQuery));
diff --git a/resources/jquery.ui/jquery.ui.sortable.js b/resources/jquery.ui/jquery.ui.sortable.js
index 1a06dcae..26ba85f0 100644
--- a/resources/jquery.ui/jquery.ui.sortable.js
+++ b/resources/jquery.ui/jquery.ui.sortable.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Sortable 1.8.11
+ * jQuery UI Sortable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -50,7 +50,7 @@ $.widget("ui.sortable", $.ui.mouse, {
this.refresh();
//Let's determine if the items are being displayed horizontally
- this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
+ this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
//Let's determine the parent's offset
this.offset = this.element.offset();
@@ -62,13 +62,11 @@ $.widget("ui.sortable", $.ui.mouse, {
destroy: function() {
this.element
- .removeClass("ui-sortable ui-sortable-disabled")
- .removeData("sortable")
- .unbind(".sortable");
+ .removeClass("ui-sortable ui-sortable-disabled");
this._mouseDestroy();
for ( var i = this.items.length - 1; i >= 0; i-- )
- this.items[i].item.removeData("sortable-item");
+ this.items[i].item.removeData(this.widgetName + "-item");
return this;
},
@@ -86,6 +84,7 @@ $.widget("ui.sortable", $.ui.mouse, {
},
_mouseCapture: function(event, overrideHandle) {
+ var that = this;
if (this.reverting) {
return false;
@@ -98,12 +97,12 @@ $.widget("ui.sortable", $.ui.mouse, {
//Find out if the clicked node (or one of its parents) is a actual item in this.items
var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
- if($.data(this, 'sortable-item') == self) {
+ if($.data(this, that.widgetName + '-item') == self) {
currentItem = $(this);
return false;
}
});
- if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
+ if($.data(event.target, that.widgetName + '-item') == self) currentItem = $(event.target);
if(!currentItem) return false;
if(this.options.handle && !overrideHandle) {
@@ -528,7 +527,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
- var inst = $.data(cur[j], 'sortable');
+ var inst = $.data(cur[j], this.widgetName);
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
}
@@ -550,7 +549,7 @@ $.widget("ui.sortable", $.ui.mouse, {
_removeCurrentsFromItems: function() {
- var list = this.currentItem.find(":data(sortable-item)");
+ var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
for (var i=0; i < this.items.length; i++) {
@@ -576,7 +575,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
- var inst = $.data(cur[j], 'sortable');
+ var inst = $.data(cur[j], this.widgetName);
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
this.containers.push(inst);
@@ -592,7 +591,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
var item = $(_queries[j]);
- item.data('sortable-item', targetData); // Data for target checking (mouse manager)
+ item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
items.push({
item: item,
@@ -615,6 +614,10 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var i = this.items.length - 1; i >= 0; i--){
var item = this.items[i];
+ //We ignore calculating positions of all connected containers when we're not over them
+ if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
+ continue;
+
var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
if (!fast) {
@@ -980,7 +983,7 @@ $.widget("ui.sortable", $.ui.mouse, {
// We first have to update the dom position of the actual currentItem
// Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
- if(!this._noFinalSort && this.currentItem[0].parentNode) this.placeholder.before(this.currentItem);
+ if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
this._noFinalSort = null;
if(this.helper[0] == this.currentItem[0]) {
@@ -1067,7 +1070,7 @@ $.widget("ui.sortable", $.ui.mouse, {
});
$.extend($.ui.sortable, {
- version: "1.8.11"
+ version: "1.8.17"
});
})(jQuery);
diff --git a/resources/jquery.ui/jquery.ui.tabs.js b/resources/jquery.ui/jquery.ui.tabs.js
index 3be7ff49..ff7938ce 100644
--- a/resources/jquery.ui/jquery.ui.tabs.js
+++ b/resources/jquery.ui/jquery.ui.tabs.js
@@ -1,5 +1,5 @@
/*
- * jQuery UI Tabs 1.8.11
+ * jQuery UI Tabs 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -698,7 +698,7 @@ $.widget( "ui.tabs", {
});
$.extend( $.ui.tabs, {
- version: "1.8.11"
+ version: "1.8.17"
});
/*
diff --git a/resources/jquery.ui/jquery.ui.widget.js b/resources/jquery.ui/jquery.ui.widget.js
index b6b1beea..facd77b3 100644
--- a/resources/jquery.ui/jquery.ui.widget.js
+++ b/resources/jquery.ui/jquery.ui.widget.js
@@ -1,5 +1,5 @@
/*!
- * jQuery UI Widget 1.8.11
+ * jQuery UI Widget 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
@@ -14,7 +14,10 @@ if ( $.cleanData ) {
var _cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- $( elem ).triggerHandler( "remove" );
+ try {
+ $( elem ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
}
_cleanData( elems );
};
@@ -25,7 +28,10 @@ if ( $.cleanData ) {
if ( !keepData ) {
if ( !selector || $.filter( selector, [ this ] ).length ) {
$( "*", this ).add( [ this ] ).each(function() {
- $( this ).triggerHandler( "remove" );
+ try {
+ $( this ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
});
}
}
@@ -233,21 +239,25 @@ $.Widget.prototype = {
},
_trigger: function( type, event, data ) {
- var callback = this.options[ type ];
+ var prop, orig,
+ callback = this.options[ type ];
+ data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
- data = data || {};
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[ 0 ];
// copy original event properties over to the new event
- // this would happen if we could call $.event.fix instead of $.Event
- // but we don't have a way to force an event to be fixed multiple times
- if ( event.originalEvent ) {
- for ( var i = $.event.props.length, prop; i; ) {
- prop = $.event.props[ --i ];
- event[ prop ] = event.originalEvent[ prop ];
+ orig = event.originalEvent;
+ if ( orig ) {
+ for ( prop in orig ) {
+ if ( !( prop in event ) ) {
+ event[ prop ] = orig[ prop ];
+ }
}
}
diff --git a/resources/jquery.ui/themes/default/images/ui-anim_basic_16x16.gif b/resources/jquery.ui/themes/default/images/ui-anim_basic_16x16.gif
deleted file mode 100644
index 085ccaec..00000000
--- a/resources/jquery.ui/themes/default/images/ui-anim_basic_16x16.gif
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_55_fbf9ee_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_55_fbf9ee_40x100.png
deleted file mode 100644
index 062f5807..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_55_fbf9ee_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_65_ffffff_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_65_ffffff_40x100.png
deleted file mode 100644
index ac8b229a..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_65_ffffff_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_cccccc_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_75_cccccc_40x100.png
deleted file mode 100644
index 5473afff..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_cccccc_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_dadada_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_75_dadada_40x100.png
deleted file mode 100644
index 7b7b0744..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_dadada_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_e6e6e6_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_75_e6e6e6_40x100.png
deleted file mode 100644
index 5b4ca1a0..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_75_e6e6e6_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_flat_95_fef1ec_40x100.png b/resources/jquery.ui/themes/default/images/ui-bg_flat_95_fef1ec_40x100.png
deleted file mode 100644
index c61aad2e..00000000
--- a/resources/jquery.ui/themes/default/images/ui-bg_flat_95_fef1ec_40x100.png
+++ /dev/null
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 00000000..ad3d6346
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 00000000..42ccba26
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 00000000..5a46b47c
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_75_e6e6e6_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 00000000..86c2baa6
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_75_e6e6e6_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_glass_95_fef1ec_1x400.png b/resources/jquery.ui/themes/default/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 00000000..4443fdc1
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 00000000..7c9fa6c6
--- /dev/null
+++ b/resources/jquery.ui/themes/default/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
index b273ff11..ee039dc0 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
index 84defe6e..45e8928e 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
index 59bd45b9..7ec70d11 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
index 6d02426c..5ba708c3 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png b/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
index 2ab019b7..7930a558 100644
--- a/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
+++ b/resources/jquery.ui/themes/default/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/resources/jquery.ui/themes/default/jquery.ui.accordion.css b/resources/jquery.ui/themes/default/jquery.ui.accordion.css
index 8d8a1a6e..e426c762 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.accordion.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.accordion.css
@@ -1,12 +1,19 @@
-/* Accordion
-----------------------------------*/
+/*
+ * jQuery UI Accordion 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
-/* IE7-/Win - Fix extra vertical space in lists */
-.ui-accordion a { zoom: 1; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
-.ui-accordion .ui-accordion-content-active { display: block; } \ No newline at end of file
+.ui-accordion .ui-accordion-content-active { display: block; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.autocomplete.css b/resources/jquery.ui/themes/default/jquery.ui.autocomplete.css
index f287dbe9..4613938a 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.autocomplete.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.autocomplete.css
@@ -1,13 +1,26 @@
-/* Autocomplete
-----------------------------------*/
+/*
+ * jQuery UI Autocomplete 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
.ui-autocomplete { position: absolute; cursor: default; }
-.ui-autocomplete-loading { background: white /* @embed */ url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
-/* Menu
-----------------------------------*/
+/*
+ * jQuery UI Menu 1.8.17
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
.ui-menu {
list-style:none;
padding: 2px;
diff --git a/resources/jquery.ui/themes/default/jquery.ui.button.css b/resources/jquery.ui/themes/default/jquery.ui.button.css
index 47777a42..a7565af8 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.button.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.button.css
@@ -1,6 +1,12 @@
-/* Button
-----------------------------------*/
-
+/*
+ * jQuery UI Button 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
@@ -11,15 +17,17 @@ button.ui-button-icons-only { width: 3.7em; }
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
-.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
-.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
-.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
@@ -28,8 +36,3 @@ input.ui-button { padding: .4em 1em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
-
-
-
-
-
diff --git a/resources/jquery.ui/themes/default/jquery.ui.core.css b/resources/jquery.ui/themes/default/jquery.ui.core.css
index b3e81930..bde176d2 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.core.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.core.css
@@ -1,20 +1,21 @@
/*
-* jQuery UI CSS Framework
-* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-*/
+ * jQuery UI CSS Framework 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
-.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
-.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
-.ui-helper-clearfix { display: inline-block; }
-/* required comment for clearfix to work in Opera \*/
-* html .ui-helper-clearfix { height:1%; }
-.ui-helper-clearfix { display:block; }
-/* end clearfix */
+.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
+.ui-helper-clearfix:after { clear: both; }
+.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.datepicker.css b/resources/jquery.ui/themes/default/jquery.ui.datepicker.css
index 580a2929..6cb5aefc 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.datepicker.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.datepicker.css
@@ -1,6 +1,13 @@
-/* Datepicker
-----------------------------------*/
-.ui-datepicker { width: 17em; padding: .2em .2em 0; }
+/*
+ * jQuery UI Datepicker 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
@@ -32,20 +39,20 @@
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
-.ui-datepicker-row-break { clear:both; width:100%; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
-/* @noflip */ .ui-datepicker-rtl { direction: rtl; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-group { float:right; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
-/* @noflip */ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
diff --git a/resources/jquery.ui/themes/default/jquery.ui.dialog.css b/resources/jquery.ui/themes/default/jquery.ui.dialog.css
index f8354642..a5914ff1 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.dialog.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.dialog.css
@@ -1,13 +1,21 @@
-/* Dialog
-----------------------------------*/
+/*
+ * jQuery UI Dialog 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
-.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
-.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
-.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
-.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.progressbar.css b/resources/jquery.ui/themes/default/jquery.ui.progressbar.css
index bc0939ec..630fab39 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.progressbar.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.progressbar.css
@@ -1,4 +1,11 @@
-/* Progressbar
-----------------------------------*/
-.ui-progressbar { height:2em; text-align: left; }
+/*
+ * jQuery UI Progressbar 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file
diff --git a/resources/jquery.ui/themes/default/jquery.ui.resizable.css b/resources/jquery.ui/themes/default/jquery.ui.resizable.css
index 366643b5..582bcb31 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.resizable.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.resizable.css
@@ -1,7 +1,14 @@
-/* Resizable
-----------------------------------*/
+/*
+ * jQuery UI Resizable 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
.ui-resizable { position: relative;}
-.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.selectable.css b/resources/jquery.ui/themes/default/jquery.ui.selectable.css
index c5d46ce2..b36b8988 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.selectable.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.selectable.css
@@ -1,3 +1,10 @@
-/* Selectable
-----------------------------------*/
-.ui-selectable-helper { border:1px dotted black }
+/*
+ * jQuery UI Selectable 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.slider.css b/resources/jquery.ui/themes/default/jquery.ui.slider.css
index 07c6f4e5..4cc54810 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.slider.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.slider.css
@@ -1,5 +1,12 @@
-/* Slider
-----------------------------------*/
+/*
+ * jQuery UI Slider 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.tabs.css b/resources/jquery.ui/themes/default/jquery.ui.tabs.css
index 99e16dbd..fb827458 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.tabs.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.tabs.css
@@ -1,5 +1,12 @@
-/* Tabs
-----------------------------------*/
+/*
+ * jQuery UI Tabs 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
diff --git a/resources/jquery.ui/themes/default/jquery.ui.theme.css b/resources/jquery.ui/themes/default/jquery.ui.theme.css
index 7a425f73..d0c93afb 100644
--- a/resources/jquery.ui/themes/default/jquery.ui.theme.css
+++ b/resources/jquery.ui/themes/default/jquery.ui.theme.css
@@ -1,40 +1,43 @@
-
-
/*
-* jQuery UI CSS Framework
-* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=sans-serif&fwDefault=normal&fsDefault=1em&cornerRadius=0&bgColorHeader=cccccc&bgTextureHeader=01_flat.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=01_flat.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=01_flat.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=01_flat.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
-*/
+ * jQuery UI CSS Framework 1.8.17
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/
+ */
/* Component containers
----------------------------------*/
-.ui-widget { font-family: sans-serif; font-size: 1em; }
+.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
.ui-widget .ui-widget { font-size: 1em; }
-.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: sans-serif; font-size: 1em; }
-.ui-widget-content { border: 1px solid #aaaaaa; /* @embed */ background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
-.ui-widget-content a { color: #222222; }
-.ui-widget-header { border: 1px solid #aaaaaa; /* @embed */ background: #cccccc url(images/ui-bg_flat_75_cccccc_40x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
-.ui-widget-header a { color: #222222; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; }
+.ui-widget-content a { color: #222222/*{fcContent}*/; }
+.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; }
+.ui-widget-header a { color: #222222/*{fcHeader}*/; }
/* Interaction states
----------------------------------*/
-.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; /* @embed */ background: #e6e6e6 url(images/ui-bg_flat_75_e6e6e6_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
-.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
-.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; /* @embed */ background: #dadada url(images/ui-bg_flat_75_dadada_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
-.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
-.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; /* @embed */ background: #ffffff url(images/ui-bg_flat_65_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
-.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
-.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; /* @embed */ background: #fbf9ee url(images/ui-bg_flat_55_fbf9ee_40x100.png) 50% 50% repeat-x; color: #363636; }
-.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
-.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; /* @embed */ background: #fef1ec url(images/ui-bg_flat_95_fef1ec_40x100.png) 50% 50% repeat-x; color: #cd0a0a; }
-.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
-.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
@@ -43,12 +46,14 @@
----------------------------------*/
/* states and images */
-.ui-icon { width: 16px; height: 16px; }
-.ui-icon, .ui-widget-content .ui-icon, .ui-widget-header .ui-icon { /* @embed */ background-image: url(images/ui-icons_222222_256x240.png); }
-.ui-state-default .ui-icon { /* @embed */ background-image: url(images/ui-icons_888888_256x240.png); }
-.ui-state-hover .ui-icon, .ui-state-focus .ui-icon, .ui-state-active .ui-icon { /* @embed */ background-image: url(images/ui-icons_454545_256x240.png); }
-.ui-state-highlight .ui-icon { /* @embed */ background-image: url(images/ui-icons_2e83ff_256x240.png); }
-.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { /* @embed */ background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
@@ -232,17 +237,11 @@
----------------------------------*/
/* Corner radius */
-.ui-corner-tl { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; }
-.ui-corner-tr { -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; border-top-right-radius: 0; }
-.ui-corner-bl { -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; }
-.ui-corner-br { -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; }
-.ui-corner-top { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; border-top-right-radius: 0; }
-.ui-corner-bottom { -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; }
-.ui-corner-right { -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; }
-.ui-corner-left { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; }
-.ui-corner-all { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; }
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; -khtml-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; -khtml-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; -khtml-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; -khtml-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
/* Overlays */
-.ui-widget-overlay, .ui-widget-shadow { /* @embed */ background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; }
-.ui-widget-overlay { opacity: .30;filter:Alpha(Opacity=30); }
-.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } \ No newline at end of file
+.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; }
+.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -khtml-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } \ No newline at end of file
diff --git a/resources/jquery.ui/themes/vector/images/button-disabled-green.png b/resources/jquery.ui/themes/vector/images/button-disabled-green.png
index cde520b5..16209389 100644
--- a/resources/jquery.ui/themes/vector/images/button-disabled-green.png
+++ b/resources/jquery.ui/themes/vector/images/button-disabled-green.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-down-green.png b/resources/jquery.ui/themes/vector/images/button-down-green.png
index 1e54a0c4..edfd4275 100644
--- a/resources/jquery.ui/themes/vector/images/button-down-green.png
+++ b/resources/jquery.ui/themes/vector/images/button-down-green.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-off-green.png b/resources/jquery.ui/themes/vector/images/button-off-green.png
index cdf46e7e..c6f5ee4f 100644
--- a/resources/jquery.ui/themes/vector/images/button-off-green.png
+++ b/resources/jquery.ui/themes/vector/images/button-off-green.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/images/button-over-green.png b/resources/jquery.ui/themes/vector/images/button-over-green.png
index 0b8f6ef4..d47eb758 100644
--- a/resources/jquery.ui/themes/vector/images/button-over-green.png
+++ b/resources/jquery.ui/themes/vector/images/button-over-green.png
Binary files differ
diff --git a/resources/jquery.ui/themes/vector/jquery.ui.datepicker.css b/resources/jquery.ui/themes/vector/jquery.ui.datepicker.css
index 81022250..871bf690 100644
--- a/resources/jquery.ui/themes/vector/jquery.ui.datepicker.css
+++ b/resources/jquery.ui/themes/vector/jquery.ui.datepicker.css
@@ -1,6 +1,6 @@
/* Datepicker
----------------------------------*/
-.ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
diff --git a/resources/jquery.ui/themes/vector/jquery.ui.dialog.css b/resources/jquery.ui/themes/vector/jquery.ui.dialog.css
index 2b19fcdd..cd85f14e 100644
--- a/resources/jquery.ui/themes/vector/jquery.ui.dialog.css
+++ b/resources/jquery.ui/themes/vector/jquery.ui.dialog.css
@@ -8,7 +8,7 @@
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
-.ui-dialog .ui-dialog-buttonpane button { float: right; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/* Customizations */
diff --git a/resources/jquery/images/jquery.arrowSteps.divider-ltr.png b/resources/jquery/images/jquery.arrowSteps.divider-ltr.png
new file mode 100644
index 00000000..0de1ae9c
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.divider-ltr.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.divider-rtl.png b/resources/jquery/images/jquery.arrowSteps.divider-rtl.png
new file mode 100644
index 00000000..f3f06519
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.divider-rtl.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.head-ltr.png b/resources/jquery/images/jquery.arrowSteps.head-ltr.png
new file mode 100644
index 00000000..dbcc71b9
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.head-ltr.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.head-rtl.png b/resources/jquery/images/jquery.arrowSteps.head-rtl.png
new file mode 100644
index 00000000..760798bf
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.head-rtl.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.tail-ltr.png b/resources/jquery/images/jquery.arrowSteps.tail-ltr.png
new file mode 100644
index 00000000..61340fa8
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.tail-ltr.png
Binary files differ
diff --git a/resources/jquery/images/jquery.arrowSteps.tail-rtl.png b/resources/jquery/images/jquery.arrowSteps.tail-rtl.png
new file mode 100644
index 00000000..dd2d8e8a
--- /dev/null
+++ b/resources/jquery/images/jquery.arrowSteps.tail-rtl.png
Binary files differ
diff --git a/resources/jquery/images/marker.png b/resources/jquery/images/marker.png
new file mode 100644
index 00000000..3929bbb5
--- /dev/null
+++ b/resources/jquery/images/marker.png
Binary files differ
diff --git a/resources/jquery/images/mask.png b/resources/jquery/images/mask.png
new file mode 100644
index 00000000..b0a4d406
--- /dev/null
+++ b/resources/jquery/images/mask.png
Binary files differ
diff --git a/resources/jquery/images/spinner.gif b/resources/jquery/images/spinner.gif
new file mode 100644
index 00000000..37d3a43d
--- /dev/null
+++ b/resources/jquery/images/spinner.gif
Binary files differ
diff --git a/resources/jquery/images/wheel.png b/resources/jquery/images/wheel.png
new file mode 100644
index 00000000..97b343d9
--- /dev/null
+++ b/resources/jquery/images/wheel.png
Binary files differ
diff --git a/resources/jquery/jquery.arrowSteps.css b/resources/jquery/jquery.arrowSteps.css
new file mode 100644
index 00000000..60461032
--- /dev/null
+++ b/resources/jquery/jquery.arrowSteps.css
@@ -0,0 +1,45 @@
+.arrowSteps {
+ list-style-type: none;
+ list-style-image: none;
+ border: 1px solid #666666;
+ position: relative;
+}
+
+.arrowSteps li {
+ float: left;
+ padding: 0px;
+ margin: 0px;
+ border: 0 none;
+}
+
+.arrowSteps li div {
+ padding: 0.5em;
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.arrowSteps li.arrow div {
+ /* @embed */
+ background: url(images/jquery.arrowSteps.divider-ltr.png) no-repeat right center;
+}
+
+/* applied to the element preceding the highlighted step */
+.arrowSteps li.arrow.tail div {
+ /* @embed */
+ background: url(images/jquery.arrowSteps.tail-ltr.png) no-repeat right center;
+}
+
+/* this applies to all highlighted, including the last */
+.arrowSteps li.head div {
+ /* @embed */
+ background: url(images/jquery.arrowSteps.head-ltr.png) no-repeat left center;
+ font-weight: bold;
+}
+
+/* this applies to all highlighted arrows except the last */
+.arrowSteps li.arrow.head div {
+ /* TODO: eliminate duplication of jquery.arrowSteps.head.png embedding */
+ /* @embed */
+ background: url(images/jquery.arrowSteps.head-ltr.png) no-repeat right center;
+} \ No newline at end of file
diff --git a/resources/jquery/jquery.arrowSteps.js b/resources/jquery/jquery.arrowSteps.js
new file mode 100644
index 00000000..f9637545
--- /dev/null
+++ b/resources/jquery/jquery.arrowSteps.js
@@ -0,0 +1,81 @@
+/**
+ * jQuery arrowSteps plugin
+ * Copyright Neil Kandalgaonkar, 2010
+ *
+ * This work is licensed under the terms of the GNU General Public License,
+ * version 2 or later.
+ * (see http://www.fsf.org/licensing/licenses/gpl.html).
+ * Derivative works and later versions of the code must be free software
+ * licensed under the same or a compatible license.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Show users their progress through a series of steps, via a row of items that fit
+ * together like arrows. One item can be highlighted at a time.
+ *
+ *
+ * SYNOPSIS
+ *
+ * <ul id="robin-hood-daffy">
+ * <li id="guard"><div>Guard!</div></li>
+ * <li id="turn"><div>Turn!</div></li>
+ * <li id="parry"><div>Parry!</div></li>
+ * <li id="dodge"><div>Dodge!</div></li>
+ * <li id="spin"><div>Spin!</div></li>
+ * <li id="ha"><div>Ha!</div></li>
+ * <li id="thrust"><div>Thrust!</div></li>
+ * </ul>
+ *
+ * <script language="javascript"><!--
+ * $( '#robin-hood-daffy' ).arrowSteps();
+ *
+ * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#guard' );
+ * // 'Guard!' is highlighted.
+ *
+ * // ... user completes the 'guard' step ...
+ *
+ * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#turn' );
+ * // 'Turn!' is highlighted.
+ *
+ * //-->
+ * </script>
+ *
+ */
+
+( function( $j ) {
+ $j.fn.arrowSteps = function() {
+ this.addClass( 'arrowSteps' );
+ var $steps = this.find( 'li' );
+
+ var width = parseInt( 100 / $steps.length, 10 );
+ $steps.css( 'width', width + '%' );
+
+ // every step except the last one has an arrow at the right hand side. Also add in the padding
+ // for the calculated arrow width.
+ var arrowWidth = parseInt( this.outerHeight(), 10 );
+ $steps.filter( ':not(:last-child)' ).addClass( 'arrow' )
+ .find( 'div' ).css( 'padding-right', arrowWidth.toString() + 'px' );
+
+ this.data( 'arrowSteps', $steps );
+ return this;
+ };
+
+ $j.fn.arrowStepsHighlight = function( selector ) {
+ var $steps = this.data( 'arrowSteps' );
+ var $previous;
+ $j.each( $steps, function( i, step ) {
+ var $step = $j( step );
+ if ( $step.is( selector ) ) {
+ if ($previous) {
+ $previous.addClass( 'tail' );
+ }
+ $step.addClass( 'head' );
+ } else {
+ $step.removeClass( 'head tail lasthead' );
+ }
+ $previous = $step;
+ } );
+ };
+
+} )( jQuery );
diff --git a/resources/jquery/jquery.autoEllipsis.js b/resources/jquery/jquery.autoEllipsis.js
index 7d726894..9a5fcc9c 100644
--- a/resources/jquery/jquery.autoEllipsis.js
+++ b/resources/jquery/jquery.autoEllipsis.js
@@ -25,7 +25,7 @@ $.fn.autoEllipsis = function( options ) {
$el.text( $el.data( 'autoEllipsis.originalText' ) );
}
}
-
+
// container element - used for measuring against
var $container = $el;
// trimmable text element - only the text within this element will be trimmed
@@ -43,7 +43,7 @@ $.fn.autoEllipsis = function( options ) {
.empty()
.append( $trimmableText );
}
-
+
var text = $container.text();
var trimmableText = $trimmableText.text();
var w = $container.width();
@@ -81,7 +81,7 @@ $.fn.autoEllipsis = function( options ) {
return;
}
}
-
+
if ( $trimmableText.width() + pw > w ) {
switch ( options.position ) {
case 'right':
@@ -136,7 +136,7 @@ $.fn.autoEllipsis = function( options ) {
} else {
cache[text][w][options.position] = $container.html();
}
-
+
} );
};
diff --git a/resources/jquery/jquery.byteLimit.js b/resources/jquery/jquery.byteLimit.js
index c1d26fc4..10411924 100644
--- a/resources/jquery/jquery.byteLimit.js
+++ b/resources/jquery/jquery.byteLimit.js
@@ -6,24 +6,54 @@
( function( $ ) {
/**
- * Enforces a byte limit to a textbox, so that UTF-8 entries are not arbitrarily truncated.
+ * Enforces a byte limit to a textbox, so that UTF-8 entries are counted as well, when, for example,
+ * a databae field has a byte limit rather than a character limit.
+ * Plugin rationale: Browser has native maxlength for number of characters, this plugin exists to
+ * limit number of bytes instead.
+ *
+ * Can be called with a custom limit (to use that limit instead of the maxlength attribute value),
+ * a filter function (in case the limit should apply to something other than the exact input value),
+ * or both. Order of arguments is important!
+ *
+ * @context {jQuery} Instance of jQuery for one or more input elements
+ * @param limit {Number} (optional) Limit to enforce, fallsback to maxLength-attribute,
+ * called with fetched value as argument.
+ * @param fn {Function} (optional) Function to call on the input string before assessing the length
+ * @return {jQuery} The context
*/
- $.fn.byteLimit = function( limit ) {
+ $.fn.byteLimit = function( limit, fn ) {
+ // If the first argument is the function,
+ // set fn to the first argument's value and ignore the second argument.
+ if ( $.isFunction( limit ) ) {
+ fn = limit;
+ limit = undefined;
+ }
- // Default to current attribute value
- if ( limit == null ) {
- limit = this.attr( 'maxLength' );
+ // Default limit to current attribute value
+ if ( limit === undefined ) {
+ limit = this.prop( 'maxLength' );
+ }
- // If passed, update/set attribute value instead
+ // Update/set attribute value, but only if there is no callback set.
+ // If there's a callback set, it's possible that the limit being enforced
+ // is too low (ie. if the callback would return "Foo" for "User:Foo").
+ // Usually this isn't a problem since browsers ignore maxLength when setting
+ // the value property through JavaScript, but Safari 4 violates that rule, so
+ // we have to remove or not set the property if we have a callback.
+ if ( fn == undefined ) {
+ this.prop( 'maxLength', limit );
} else {
- this.attr( 'maxLength', limit );
+ this.removeProp( 'maxLength' );
}
// Nothing passed and/or empty attribute, return without binding an event.
- if ( limit == null ) {
+ if ( limit === undefined ) {
return this;
}
+ // Save function for reference
+ this.data( 'byteLimit-callback', fn );
+
// We've got something, go for it:
return this.keypress( function( e ) {
// First check to see if this is actually a character key
@@ -31,21 +61,22 @@
// Based on key-event info from http://unixpapa.com/js/key.html
// jQuery should also normalize e.which to be consistent cross-browser,
// however the same check is still needed regardless of jQuery.
-
+
// Note: At the moment, for some older opera versions (~< 10.5)
// some special keys won't be recognized (aka left arrow key).
// Backspace will be, so not big issue.
-
+
if ( e.which === 0 || e.charCode === 0 || e.which === 8 ||
e.ctrlKey || e.altKey || e.metaKey )
{
return true; //a special key (backspace, etc) so don't interfere.
}
-
- var len = $.byteLength( this.value );
- // Note that keypress returns a character code point, not a keycode.
- // However, this may not be super reliable depending on how keys come in...
- var charLen = $.byteLength( String.fromCharCode( e.which ) );
+
+ var val = fn !== undefined ? fn( $( this ).val() ): $( this ).val(),
+ len = $.byteLength( val ),
+ // Note that keypress returns a character code point, not a keycode.
+ // However, this may not be super reliable depending on how keys come in...
+ charLen = $.byteLength( String.fromCharCode( e.which ) );
if ( ( len + charLen ) > limit ) {
e.preventDefault();
diff --git a/resources/jquery/jquery.checkboxShiftClick.js b/resources/jquery/jquery.checkboxShiftClick.js
index cfa696d4..0a1d7d7d 100644
--- a/resources/jquery/jquery.checkboxShiftClick.js
+++ b/resources/jquery/jquery.checkboxShiftClick.js
@@ -16,9 +16,9 @@ $.fn.checkboxShiftClick = function( text ) {
if ( prevCheckbox !== null && e.shiftKey ) {
// Check or uncheck this one and all in-between checkboxes
$box.slice(
- Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
- Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
- ).attr( {checked: e.target.checked ? 'checked' : ''} );
+ Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
+ Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
+ ).prop( 'checked', e.target.checked ? true : false );
}
// Either way, update the prevCheckbox variable to the one clicked now
prevCheckbox = e.target;
diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js
index 8082fa7d..ae74a324 100644
--- a/resources/jquery/jquery.client.js
+++ b/resources/jquery/jquery.client.js
@@ -14,7 +14,7 @@
/* Public Methods */
$.client = {
-
+
/**
* Get an object containing information about the client.
*
@@ -37,18 +37,18 @@
}
// Use the cached version if possible
if ( profileCache[nav.userAgent] === undefined ) {
-
+
/* Configuration */
-
+
// Name of browsers or layout engines we don't recognize
var uk = 'unknown';
// Generic version digit
var x = 'x';
// Strings found in user agent strings that need to be conformed
- var wildUserAgents = [ 'Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'];
+ var wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'];
// Translations for conforming user agent strings
var userAgentTranslations = [
- // Tons of browsers lie about being something they are not
+ // 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'],
@@ -86,9 +86,9 @@
var platforms = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'];
// Translations for conforming operating system names
var platformTranslations = [['sunos', 'solaris']];
-
+
/* Methods */
-
+
// Performs multiple replacements on a string
var translate = function( source, translations ) {
for ( var i = 0; i < translations.length; i++ ) {
@@ -96,9 +96,9 @@
}
return source;
};
-
- /* Pre-processing */
-
+
+ /* Pre-processing */
+
var ua = nav.userAgent,
match,
name = uk,
@@ -113,9 +113,9 @@
}
// 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 );
}
@@ -131,9 +131,9 @@
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.match( /safari/ ) && version > 400 ) {
version = '2.0';
@@ -143,9 +143,9 @@
version = ua.match( /version\/([0-9\.]*)/i )[1] || 10;
}
var versionNumber = parseFloat( version, 10 ) || 0.0;
-
+
/* Caching */
-
+
profileCache[nav.userAgent] = {
'name': name,
'layout': layout,
@@ -158,7 +158,7 @@
}
return profileCache[nav.userAgent];
},
-
+
/**
* Checks the current browser against a support map object to determine if the browser has been black-listed or
* not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
diff --git a/resources/jquery/jquery.collapsibleTabs.js b/resources/jquery/jquery.collapsibleTabs.js
index b2399727..1784f86a 100644
--- a/resources/jquery/jquery.collapsibleTabs.js
+++ b/resources/jquery/jquery.collapsibleTabs.js
@@ -7,7 +7,7 @@
if( !this.length ) return this;
//merge options into the defaults
var $settings = $.extend( {}, $.collapsibleTabs.defaults, options );
-
+
this.each( function() {
var $this = $( this );
// add the element to our array of collapsible managers
@@ -20,7 +20,7 @@
$.collapsibleTabs.addData( $( this ) );
} );
} );
-
+
// if we haven't already bound our resize hanlder, bind it now
if( !$.collapsibleTabs.boundEvent ) {
$( window )
@@ -70,15 +70,15 @@
$.collapsibleTabs.instances.each( function() {
var $this = $( this ), data = $.collapsibleTabs.getSettings( $this );
if( data.shifting ) return;
-
+
// if the two navigations are colliding
if( $this.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
-
+
$this.trigger( "beforeTabCollapse" );
// move the element to the dropdown menu
$.collapsibleTabs.moveToCollapsed( $this.children( data.collapsible + ':last' ) );
}
-
+
// if there are still moveable items in the dropdown menu,
// and there is sufficient space to place them in the tab container
if( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0
diff --git a/resources/jquery/jquery.color.js b/resources/jquery/jquery.color.js
index b0419428..8a619b5c 100644
--- a/resources/jquery/jquery.color.js
+++ b/resources/jquery/jquery.color.js
@@ -15,7 +15,7 @@
fx.start = getColor( fx.elem, attr );
fx.end = $.colorUtil.getRGB( fx.end );
}
-
+
fx.elem.style[attr] = 'rgb(' + [
Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
@@ -24,7 +24,7 @@
}
}
);
-
+
function getColor(elem, attr) {
var color;
@@ -33,12 +33,12 @@
// Keep going until we find an element that has color, or we hit the body
if ( color != '' && color != 'transparent' || $.nodeName(elem, 'body') )
- break;
+ break;
attr = 'backgroundColor';
} while ( elem = elem.parentNode );
return $.colorUtil.getRGB(color);
};
-
+
} )( jQuery );
diff --git a/resources/jquery/jquery.cycle.all.js b/resources/jquery/jquery.cycle.all.js
new file mode 100644
index 00000000..d57fb720
--- /dev/null
+++ b/resources/jquery/jquery.cycle.all.js
@@ -0,0 +1,1529 @@
+/*!
+ * jQuery Cycle Plugin (with Transition Definitions)
+ * Examples and documentation at: http://jquery.malsup.com/cycle/
+ * Copyright (c) 2007-2010 M. Alsup
+ * Version: 2.9999 (13-NOV-2011)
+ * Dual licensed under the MIT and GPL licenses.
+ * http://jquery.malsup.com/license.html
+ * Requires: jQuery v1.3.2 or later
+ */
+;(function($, undefined) {
+
+var ver = '2.9999';
+
+// if $.support is not defined (pre jQuery 1.3) add what I need
+if ($.support == undefined) {
+ $.support = {
+ opacity: !($.browser.msie)
+ };
+}
+
+function debug(s) {
+ $.fn.cycle.debug && log(s);
+}
+function log() {
+ window.console && console.log && console.log('[cycle] ' + Array.prototype.join.call(arguments,' '));
+}
+$.expr[':'].paused = function(el) {
+ return el.cyclePause;
+}
+
+
+// the options arg can be...
+// a number - indicates an immediate transition should occur to the given slide index
+// a string - 'pause', 'resume', 'toggle', 'next', 'prev', 'stop', 'destroy' or the name of a transition effect (ie, 'fade', 'zoom', etc)
+// an object - properties to control the slideshow
+//
+// the arg2 arg can be...
+// the name of an fx (only used in conjunction with a numeric value for 'options')
+// the value true (only used in first arg == 'resume') and indicates
+// that the resume should occur immediately (not wait for next timeout)
+
+$.fn.cycle = function(options, arg2) {
+ var o = { s: this.selector, c: this.context };
+
+ // in 1.3+ we can fix mistakes with the ready state
+ if (this.length === 0 && options != 'stop') {
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing slideshow');
+ $(function() {
+ $(o.s,o.c).cycle(options,arg2);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ // iterate the matched nodeset
+ return this.each(function() {
+ var opts = handleArguments(this, options, arg2);
+ if (opts === false)
+ return;
+
+ opts.updateActivePagerLink = opts.updateActivePagerLink || $.fn.cycle.updateActivePagerLink;
+
+ // stop existing slideshow for this container (if there is one)
+ if (this.cycleTimeout)
+ clearTimeout(this.cycleTimeout);
+ this.cycleTimeout = this.cyclePause = 0;
+
+ var $cont = $(this);
+ var $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children();
+ var els = $slides.get();
+
+ var opts2 = buildOptions($cont, $slides, els, opts, o);
+ if (opts2 === false)
+ return;
+
+ if (els.length < 2) {
+ log('terminating; too few slides: ' + els.length);
+ return;
+ }
+
+ var startTime = opts2.continuous ? 10 : getTimeout(els[opts2.currSlide], els[opts2.nextSlide], opts2, !opts2.backwards);
+
+ // if it's an auto slideshow, kick it off
+ if (startTime) {
+ startTime += (opts2.delay || 0);
+ if (startTime < 10)
+ startTime = 10;
+ debug('first timeout: ' + startTime);
+ this.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts.backwards)}, startTime);
+ }
+ });
+};
+
+function triggerPause(cont, byHover, onPager) {
+ var opts = $(cont).data('cycle.opts');
+ var paused = !!cont.cyclePause;
+ if (paused && opts.paused)
+ opts.paused(cont, opts, byHover, onPager);
+ else if (!paused && opts.resumed)
+ opts.resumed(cont, opts, byHover, onPager);
+}
+
+// process the args that were passed to the plugin fn
+function handleArguments(cont, options, arg2) {
+ if (cont.cycleStop == undefined)
+ cont.cycleStop = 0;
+ if (options === undefined || options === null)
+ options = {};
+ if (options.constructor == String) {
+ switch(options) {
+ case 'destroy':
+ case 'stop':
+ var opts = $(cont).data('cycle.opts');
+ if (!opts)
+ return false;
+ cont.cycleStop++; // callbacks look for change
+ if (cont.cycleTimeout)
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ opts.elements && $(opts.elements).stop();
+ $(cont).removeData('cycle.opts');
+ if (options == 'destroy')
+ destroy(opts);
+ return false;
+ case 'toggle':
+ cont.cyclePause = (cont.cyclePause === 1) ? 0 : 1;
+ checkInstantResume(cont.cyclePause, arg2, cont);
+ triggerPause(cont);
+ return false;
+ case 'pause':
+ cont.cyclePause = 1;
+ triggerPause(cont);
+ return false;
+ case 'resume':
+ cont.cyclePause = 0;
+ checkInstantResume(false, arg2, cont);
+ triggerPause(cont);
+ return false;
+ case 'prev':
+ case 'next':
+ var opts = $(cont).data('cycle.opts');
+ if (!opts) {
+ log('options not found, "prev/next" ignored');
+ return false;
+ }
+ $.fn.cycle[options](opts);
+ return false;
+ default:
+ options = { fx: options };
+ };
+ return options;
+ }
+ else if (options.constructor == Number) {
+ // go to the requested slide
+ var num = options;
+ options = $(cont).data('cycle.opts');
+ if (!options) {
+ log('options not found, can not advance slide');
+ return false;
+ }
+ if (num < 0 || num >= options.elements.length) {
+ log('invalid slide index: ' + num);
+ return false;
+ }
+ options.nextSlide = num;
+ if (cont.cycleTimeout) {
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ }
+ if (typeof arg2 == 'string')
+ options.oneTimeFx = arg2;
+ go(options.elements, options, 1, num >= options.currSlide);
+ return false;
+ }
+ return options;
+
+ function checkInstantResume(isPaused, arg2, cont) {
+ if (!isPaused && arg2 === true) { // resume now!
+ var options = $(cont).data('cycle.opts');
+ if (!options) {
+ log('options not found, can not resume');
+ return false;
+ }
+ if (cont.cycleTimeout) {
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ }
+ go(options.elements, options, 1, !options.backwards);
+ }
+ }
+};
+
+function removeFilter(el, opts) {
+ if (!$.support.opacity && opts.cleartype && el.style.filter) {
+ try { el.style.removeAttribute('filter'); }
+ catch(smother) {} // handle old opera versions
+ }
+};
+
+// unbind event handlers
+function destroy(opts) {
+ if (opts.next)
+ $(opts.next).unbind(opts.prevNextEvent);
+ if (opts.prev)
+ $(opts.prev).unbind(opts.prevNextEvent);
+
+ if (opts.pager || opts.pagerAnchorBuilder)
+ $.each(opts.pagerAnchors || [], function() {
+ this.unbind().remove();
+ });
+ opts.pagerAnchors = null;
+ if (opts.destroy) // callback
+ opts.destroy(opts);
+};
+
+// one-time initialization
+function buildOptions($cont, $slides, els, options, o) {
+ var startingSlideSpecified;
+ // support metadata plugin (v1.0 and v2.0)
+ var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {});
+ var meta = $.isFunction($cont.data) ? $cont.data(opts.metaAttr) : null;
+ if (meta)
+ opts = $.extend(opts, meta);
+ if (opts.autostop)
+ opts.countdown = opts.autostopCount || els.length;
+
+ var cont = $cont[0];
+ $cont.data('cycle.opts', opts);
+ opts.$cont = $cont;
+ opts.stopCount = cont.cycleStop;
+ opts.elements = els;
+ opts.before = opts.before ? [opts.before] : [];
+ opts.after = opts.after ? [opts.after] : [];
+
+ // push some after callbacks
+ if (!$.support.opacity && opts.cleartype)
+ opts.after.push(function() { removeFilter(this, opts); });
+ if (opts.continuous)
+ opts.after.push(function() { go(els,opts,0,!opts.backwards); });
+
+ saveOriginalOpts(opts);
+
+ // clearType corrections
+ if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
+ clearTypeFix($slides);
+
+ // container requires non-static position so that slides can be position within
+ if ($cont.css('position') == 'static')
+ $cont.css('position', 'relative');
+ if (opts.width)
+ $cont.width(opts.width);
+ if (opts.height && opts.height != 'auto')
+ $cont.height(opts.height);
+
+ if (opts.startingSlide != undefined) {
+ opts.startingSlide = parseInt(opts.startingSlide,10);
+ if (opts.startingSlide >= els.length || opts.startSlide < 0)
+ opts.startingSlide = 0; // catch bogus input
+ else
+ startingSlideSpecified = true;
+ }
+ else if (opts.backwards)
+ opts.startingSlide = els.length - 1;
+ else
+ opts.startingSlide = 0;
+
+ // if random, mix up the slide array
+ if (opts.random) {
+ opts.randomMap = [];
+ for (var i = 0; i < els.length; i++)
+ opts.randomMap.push(i);
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ if (startingSlideSpecified) {
+ // try to find the specified starting slide and if found set start slide index in the map accordingly
+ for ( var cnt = 0; cnt < els.length; cnt++ ) {
+ if ( opts.startingSlide == opts.randomMap[cnt] ) {
+ opts.randomIndex = cnt;
+ }
+ }
+ }
+ else {
+ opts.randomIndex = 1;
+ opts.startingSlide = opts.randomMap[1];
+ }
+ }
+ else if (opts.startingSlide >= els.length)
+ opts.startingSlide = 0; // catch bogus input
+ opts.currSlide = opts.startingSlide || 0;
+ var first = opts.startingSlide;
+
+ // set position and zIndex on all the slides
+ $slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) {
+ var z;
+ if (opts.backwards)
+ z = first ? i <= first ? els.length + (i-first) : first-i : els.length-i;
+ else
+ z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i;
+ $(this).css('z-index', z)
+ });
+
+ // make sure first slide is visible
+ $(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case
+ removeFilter(els[first], opts);
+
+ // stretch slides
+ if (opts.fit) {
+ if (!opts.aspect) {
+ if (opts.width)
+ $slides.width(opts.width);
+ if (opts.height && opts.height != 'auto')
+ $slides.height(opts.height);
+ } else {
+ $slides.each(function(){
+ var $slide = $(this);
+ var ratio = (opts.aspect === true) ? $slide.width()/$slide.height() : opts.aspect;
+ if( opts.width && $slide.width() != opts.width ) {
+ $slide.width( opts.width );
+ $slide.height( opts.width / ratio );
+ }
+
+ if( opts.height && $slide.height() < opts.height ) {
+ $slide.height( opts.height );
+ $slide.width( opts.height * ratio );
+ }
+ });
+ }
+ }
+
+ if (opts.center && ((!opts.fit) || opts.aspect)) {
+ $slides.each(function(){
+ var $slide = $(this);
+ $slide.css({
+ "margin-left": opts.width ?
+ ((opts.width - $slide.width()) / 2) + "px" :
+ 0,
+ "margin-top": opts.height ?
+ ((opts.height - $slide.height()) / 2) + "px" :
+ 0
+ });
+ });
+ }
+
+ if (opts.center && !opts.fit && !opts.slideResize) {
+ $slides.each(function(){
+ var $slide = $(this);
+ $slide.css({
+ "margin-left": opts.width ? ((opts.width - $slide.width()) / 2) + "px" : 0,
+ "margin-top": opts.height ? ((opts.height - $slide.height()) / 2) + "px" : 0
+ });
+ });
+ }
+
+ // stretch container
+ var reshape = opts.containerResize && !$cont.innerHeight();
+ if (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9
+ var maxw = 0, maxh = 0;
+ for(var j=0; j < els.length; j++) {
+ var $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight();
+ if (!w) w = e.offsetWidth || e.width || $e.attr('width');
+ if (!h) h = e.offsetHeight || e.height || $e.attr('height');
+ maxw = w > maxw ? w : maxw;
+ maxh = h > maxh ? h : maxh;
+ }
+ if (maxw > 0 && maxh > 0)
+ $cont.css({width:maxw+'px',height:maxh+'px'});
+ }
+
+ var pauseFlag = false; // https://github.com/malsup/cycle/issues/44
+ if (opts.pause)
+ $cont.hover(
+ function(){
+ pauseFlag = true;
+ this.cyclePause++;
+ triggerPause(cont, true);
+ },
+ function(){
+ pauseFlag && this.cyclePause--;
+ triggerPause(cont, true);
+ }
+ );
+
+ if (supportMultiTransitions(opts) === false)
+ return false;
+
+ // apparently a lot of people use image slideshows without height/width attributes on the images.
+ // Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that.
+ var requeue = false;
+ options.requeueAttempts = options.requeueAttempts || 0;
+ $slides.each(function() {
+ // try to get height/width of each slide
+ var $el = $(this);
+ this.cycleH = (opts.fit && opts.height) ? opts.height : ($el.height() || this.offsetHeight || this.height || $el.attr('height') || 0);
+ this.cycleW = (opts.fit && opts.width) ? opts.width : ($el.width() || this.offsetWidth || this.width || $el.attr('width') || 0);
+
+ if ( $el.is('img') ) {
+ // sigh.. sniffing, hacking, shrugging... this crappy hack tries to account for what browsers do when
+ // an image is being downloaded and the markup did not include sizing info (height/width attributes);
+ // there seems to be some "default" sizes used in this situation
+ var loadingIE = ($.browser.msie && this.cycleW == 28 && this.cycleH == 30 && !this.complete);
+ var loadingFF = ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete);
+ var loadingOp = ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete);
+ var loadingOther = (this.cycleH == 0 && this.cycleW == 0 && !this.complete);
+ // don't requeue for images that are still loading but have a valid size
+ if (loadingIE || loadingFF || loadingOp || loadingOther) {
+ if (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever
+ log(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH);
+ setTimeout(function() {$(o.s,o.c).cycle(options)}, opts.requeueTimeout);
+ requeue = true;
+ return false; // break each loop
+ }
+ else {
+ log('could not determine size of image: '+this.src, this.cycleW, this.cycleH);
+ }
+ }
+ }
+ return true;
+ });
+
+ if (requeue)
+ return false;
+
+ opts.cssBefore = opts.cssBefore || {};
+ opts.cssAfter = opts.cssAfter || {};
+ opts.cssFirst = opts.cssFirst || {};
+ opts.animIn = opts.animIn || {};
+ opts.animOut = opts.animOut || {};
+
+ $slides.not(':eq('+first+')').css(opts.cssBefore);
+ $($slides[first]).css(opts.cssFirst);
+
+ if (opts.timeout) {
+ opts.timeout = parseInt(opts.timeout,10);
+ // ensure that timeout and speed settings are sane
+ if (opts.speed.constructor == String)
+ opts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed,10);
+ if (!opts.sync)
+ opts.speed = opts.speed / 2;
+
+ var buffer = opts.fx == 'none' ? 0 : opts.fx == 'shuffle' ? 500 : 250;
+ while((opts.timeout - opts.speed) < buffer) // sanitize timeout
+ opts.timeout += opts.speed;
+ }
+ if (opts.easing)
+ opts.easeIn = opts.easeOut = opts.easing;
+ if (!opts.speedIn)
+ opts.speedIn = opts.speed;
+ if (!opts.speedOut)
+ opts.speedOut = opts.speed;
+
+ opts.slideCount = els.length;
+ opts.currSlide = opts.lastSlide = first;
+ if (opts.random) {
+ if (++opts.randomIndex == els.length)
+ opts.randomIndex = 0;
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else if (opts.backwards)
+ opts.nextSlide = opts.startingSlide == 0 ? (els.length-1) : opts.startingSlide-1;
+ else
+ opts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1;
+
+ // run transition init fn
+ if (!opts.multiFx) {
+ var init = $.fn.cycle.transitions[opts.fx];
+ if ($.isFunction(init))
+ init($cont, $slides, opts);
+ else if (opts.fx != 'custom' && !opts.multiFx) {
+ log('unknown transition: ' + opts.fx,'; slideshow terminating');
+ return false;
+ }
+ }
+
+ // fire artificial events
+ var e0 = $slides[first];
+ if (!opts.skipInitializationCallbacks) {
+ if (opts.before.length)
+ opts.before[0].apply(e0, [e0, e0, opts, true]);
+ if (opts.after.length)
+ opts.after[0].apply(e0, [e0, e0, opts, true]);
+ }
+ if (opts.next)
+ $(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,1)});
+ if (opts.prev)
+ $(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,0)});
+ if (opts.pager || opts.pagerAnchorBuilder)
+ buildPager(els,opts);
+
+ exposeAddSlide(opts, els);
+
+ return opts;
+};
+
+// save off original opts so we can restore after clearing state
+function saveOriginalOpts(opts) {
+ opts.original = { before: [], after: [] };
+ opts.original.cssBefore = $.extend({}, opts.cssBefore);
+ opts.original.cssAfter = $.extend({}, opts.cssAfter);
+ opts.original.animIn = $.extend({}, opts.animIn);
+ opts.original.animOut = $.extend({}, opts.animOut);
+ $.each(opts.before, function() { opts.original.before.push(this); });
+ $.each(opts.after, function() { opts.original.after.push(this); });
+};
+
+function supportMultiTransitions(opts) {
+ var i, tx, txs = $.fn.cycle.transitions;
+ // look for multiple effects
+ if (opts.fx.indexOf(',') > 0) {
+ opts.multiFx = true;
+ opts.fxs = opts.fx.replace(/\s*/g,'').split(',');
+ // discard any bogus effect names
+ for (i=0; i < opts.fxs.length; i++) {
+ var fx = opts.fxs[i];
+ tx = txs[fx];
+ if (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) {
+ log('discarding unknown transition: ',fx);
+ opts.fxs.splice(i,1);
+ i--;
+ }
+ }
+ // if we have an empty list then we threw everything away!
+ if (!opts.fxs.length) {
+ log('No valid transitions named; slideshow terminating.');
+ return false;
+ }
+ }
+ else if (opts.fx == 'all') { // auto-gen the list of transitions
+ opts.multiFx = true;
+ opts.fxs = [];
+ for (p in txs) {
+ tx = txs[p];
+ if (txs.hasOwnProperty(p) && $.isFunction(tx))
+ opts.fxs.push(p);
+ }
+ }
+ if (opts.multiFx && opts.randomizeEffects) {
+ // munge the fxs array to make effect selection random
+ var r1 = Math.floor(Math.random() * 20) + 30;
+ for (i = 0; i < r1; i++) {
+ var r2 = Math.floor(Math.random() * opts.fxs.length);
+ opts.fxs.push(opts.fxs.splice(r2,1)[0]);
+ }
+ debug('randomized fx sequence: ',opts.fxs);
+ }
+ return true;
+};
+
+// provide a mechanism for adding slides after the slideshow has started
+function exposeAddSlide(opts, els) {
+ opts.addSlide = function(newSlide, prepend) {
+ var $s = $(newSlide), s = $s[0];
+ if (!opts.autostopCount)
+ opts.countdown++;
+ els[prepend?'unshift':'push'](s);
+ if (opts.els)
+ opts.els[prepend?'unshift':'push'](s); // shuffle needs this
+ opts.slideCount = els.length;
+
+ // add the slide to the random map and resort
+ if (opts.random) {
+ opts.randomMap.push(opts.slideCount-1);
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ }
+
+ $s.css('position','absolute');
+ $s[prepend?'prependTo':'appendTo'](opts.$cont);
+
+ if (prepend) {
+ opts.currSlide++;
+ opts.nextSlide++;
+ }
+
+ if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
+ clearTypeFix($s);
+
+ if (opts.fit && opts.width)
+ $s.width(opts.width);
+ if (opts.fit && opts.height && opts.height != 'auto')
+ $s.height(opts.height);
+ s.cycleH = (opts.fit && opts.height) ? opts.height : $s.height();
+ s.cycleW = (opts.fit && opts.width) ? opts.width : $s.width();
+
+ $s.css(opts.cssBefore);
+
+ if (opts.pager || opts.pagerAnchorBuilder)
+ $.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts);
+
+ if ($.isFunction(opts.onAddSlide))
+ opts.onAddSlide($s);
+ else
+ $s.hide(); // default behavior
+ };
+}
+
+// reset internal state; we do this on every pass in order to support multiple effects
+$.fn.cycle.resetState = function(opts, fx) {
+ fx = fx || opts.fx;
+ opts.before = []; opts.after = [];
+ opts.cssBefore = $.extend({}, opts.original.cssBefore);
+ opts.cssAfter = $.extend({}, opts.original.cssAfter);
+ opts.animIn = $.extend({}, opts.original.animIn);
+ opts.animOut = $.extend({}, opts.original.animOut);
+ opts.fxFn = null;
+ $.each(opts.original.before, function() { opts.before.push(this); });
+ $.each(opts.original.after, function() { opts.after.push(this); });
+
+ // re-init
+ var init = $.fn.cycle.transitions[fx];
+ if ($.isFunction(init))
+ init(opts.$cont, $(opts.elements), opts);
+};
+
+// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt
+function go(els, opts, manual, fwd) {
+ // opts.busy is true if we're in the middle of an animation
+ if (manual && opts.busy && opts.manualTrump) {
+ // let manual transitions requests trump active ones
+ debug('manualTrump in go(), stopping active transition');
+ $(els).stop(true,true);
+ opts.busy = 0;
+ }
+ // don't begin another timeout-based transition if there is one active
+ if (opts.busy) {
+ debug('transition active, ignoring new tx request');
+ return;
+ }
+
+ var p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide];
+
+ // stop cycling if we have an outstanding stop request
+ if (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual)
+ return;
+
+ // check to see if we should stop cycling based on autostop options
+ if (!manual && !p.cyclePause && !opts.bounce &&
+ ((opts.autostop && (--opts.countdown <= 0)) ||
+ (opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) {
+ if (opts.end)
+ opts.end(opts);
+ return;
+ }
+
+ // if slideshow is paused, only transition on a manual trigger
+ var changed = false;
+ if ((manual || !p.cyclePause) && (opts.nextSlide != opts.currSlide)) {
+ changed = true;
+ var fx = opts.fx;
+ // keep trying to get the slide size if we don't have it yet
+ curr.cycleH = curr.cycleH || $(curr).height();
+ curr.cycleW = curr.cycleW || $(curr).width();
+ next.cycleH = next.cycleH || $(next).height();
+ next.cycleW = next.cycleW || $(next).width();
+
+ // support multiple transition types
+ if (opts.multiFx) {
+ if (fwd && (opts.lastFx == undefined || ++opts.lastFx >= opts.fxs.length))
+ opts.lastFx = 0;
+ else if (!fwd && (opts.lastFx == undefined || --opts.lastFx < 0))
+ opts.lastFx = opts.fxs.length - 1;
+ fx = opts.fxs[opts.lastFx];
+ }
+
+ // one-time fx overrides apply to: $('div').cycle(3,'zoom');
+ if (opts.oneTimeFx) {
+ fx = opts.oneTimeFx;
+ opts.oneTimeFx = null;
+ }
+
+ $.fn.cycle.resetState(opts, fx);
+
+ // run the before callbacks
+ if (opts.before.length)
+ $.each(opts.before, function(i,o) {
+ if (p.cycleStop != opts.stopCount) return;
+ o.apply(next, [curr, next, opts, fwd]);
+ });
+
+ // stage the after callacks
+ var after = function() {
+ opts.busy = 0;
+ $.each(opts.after, function(i,o) {
+ if (p.cycleStop != opts.stopCount) return;
+ o.apply(next, [curr, next, opts, fwd]);
+ });
+ if (!p.cycleStop) {
+ // queue next transition
+ queueNext();
+ }
+ };
+
+ debug('tx firing('+fx+'); currSlide: ' + opts.currSlide + '; nextSlide: ' + opts.nextSlide);
+
+ // get ready to perform the transition
+ opts.busy = 1;
+ if (opts.fxFn) // fx function provided?
+ opts.fxFn(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ else if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ?
+ $.fn.cycle[opts.fx](curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ else
+ $.fn.cycle.custom(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ }
+ else {
+ queueNext();
+ }
+
+ if (changed || opts.nextSlide == opts.currSlide) {
+ // calculate the next slide
+ opts.lastSlide = opts.currSlide;
+ if (opts.random) {
+ opts.currSlide = opts.nextSlide;
+ if (++opts.randomIndex == els.length) {
+ opts.randomIndex = 0;
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ }
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ if (opts.nextSlide == opts.currSlide)
+ opts.nextSlide = (opts.currSlide == opts.slideCount - 1) ? 0 : opts.currSlide + 1;
+ }
+ else if (opts.backwards) {
+ var roll = (opts.nextSlide - 1) < 0;
+ if (roll && opts.bounce) {
+ opts.backwards = !opts.backwards;
+ opts.nextSlide = 1;
+ opts.currSlide = 0;
+ }
+ else {
+ opts.nextSlide = roll ? (els.length-1) : opts.nextSlide-1;
+ opts.currSlide = roll ? 0 : opts.nextSlide+1;
+ }
+ }
+ else { // sequence
+ var roll = (opts.nextSlide + 1) == els.length;
+ if (roll && opts.bounce) {
+ opts.backwards = !opts.backwards;
+ opts.nextSlide = els.length-2;
+ opts.currSlide = els.length-1;
+ }
+ else {
+ opts.nextSlide = roll ? 0 : opts.nextSlide+1;
+ opts.currSlide = roll ? els.length-1 : opts.nextSlide-1;
+ }
+ }
+ }
+ if (changed && opts.pager)
+ opts.updateActivePagerLink(opts.pager, opts.currSlide, opts.activePagerClass);
+
+ function queueNext() {
+ // stage the next transition
+ var ms = 0, timeout = opts.timeout;
+ if (opts.timeout && !opts.continuous) {
+ ms = getTimeout(els[opts.currSlide], els[opts.nextSlide], opts, fwd);
+ if (opts.fx == 'shuffle')
+ ms -= opts.speedOut;
+ }
+ else if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic
+ ms = 10;
+ if (ms > 0)
+ p.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.backwards) }, ms);
+ }
+};
+
+// invoked after transition
+$.fn.cycle.updateActivePagerLink = function(pager, currSlide, clsName) {
+ $(pager).each(function() {
+ $(this).children().removeClass(clsName).eq(currSlide).addClass(clsName);
+ });
+};
+
+// calculate timeout value for current transition
+function getTimeout(curr, next, opts, fwd) {
+ if (opts.timeoutFn) {
+ // call user provided calc fn
+ var t = opts.timeoutFn.call(curr,curr,next,opts,fwd);
+ while (opts.fx != 'none' && (t - opts.speed) < 250) // sanitize timeout
+ t += opts.speed;
+ debug('calculated timeout: ' + t + '; speed: ' + opts.speed);
+ if (t !== false)
+ return t;
+ }
+ return opts.timeout;
+};
+
+// expose next/prev function, caller must pass in state
+$.fn.cycle.next = function(opts) { advance(opts,1); };
+$.fn.cycle.prev = function(opts) { advance(opts,0);};
+
+// advance slide forward or back
+function advance(opts, moveForward) {
+ var val = moveForward ? 1 : -1;
+ var els = opts.elements;
+ var p = opts.$cont[0], timeout = p.cycleTimeout;
+ if (timeout) {
+ clearTimeout(timeout);
+ p.cycleTimeout = 0;
+ }
+ if (opts.random && val < 0) {
+ // move back to the previously display slide
+ opts.randomIndex--;
+ if (--opts.randomIndex == -2)
+ opts.randomIndex = els.length-2;
+ else if (opts.randomIndex == -1)
+ opts.randomIndex = els.length-1;
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else if (opts.random) {
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else {
+ opts.nextSlide = opts.currSlide + val;
+ if (opts.nextSlide < 0) {
+ if (opts.nowrap) return false;
+ opts.nextSlide = els.length - 1;
+ }
+ else if (opts.nextSlide >= els.length) {
+ if (opts.nowrap) return false;
+ opts.nextSlide = 0;
+ }
+ }
+
+ var cb = opts.onPrevNextEvent || opts.prevNextClick; // prevNextClick is deprecated
+ if ($.isFunction(cb))
+ cb(val > 0, opts.nextSlide, els[opts.nextSlide]);
+ go(els, opts, 1, moveForward);
+ return false;
+};
+
+function buildPager(els, opts) {
+ var $p = $(opts.pager);
+ $.each(els, function(i,o) {
+ $.fn.cycle.createPagerAnchor(i,o,$p,els,opts);
+ });
+ opts.updateActivePagerLink(opts.pager, opts.startingSlide, opts.activePagerClass);
+};
+
+$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) {
+ var a;
+ if ($.isFunction(opts.pagerAnchorBuilder)) {
+ a = opts.pagerAnchorBuilder(i,el);
+ debug('pagerAnchorBuilder('+i+', el) returned: ' + a);
+ }
+ else
+ a = '<a href="#">'+(i+1)+'</a>';
+
+ if (!a)
+ return;
+ var $a = $(a);
+ // don't reparent if anchor is in the dom
+ if ($a.parents('body').length === 0) {
+ var arr = [];
+ if ($p.length > 1) {
+ $p.each(function() {
+ var $clone = $a.clone(true);
+ $(this).append($clone);
+ arr.push($clone[0]);
+ });
+ $a = $(arr);
+ }
+ else {
+ $a.appendTo($p);
+ }
+ }
+
+ opts.pagerAnchors = opts.pagerAnchors || [];
+ opts.pagerAnchors.push($a);
+
+ var pagerFn = function(e) {
+ e.preventDefault();
+ opts.nextSlide = i;
+ var p = opts.$cont[0], timeout = p.cycleTimeout;
+ if (timeout) {
+ clearTimeout(timeout);
+ p.cycleTimeout = 0;
+ }
+ var cb = opts.onPagerEvent || opts.pagerClick; // pagerClick is deprecated
+ if ($.isFunction(cb))
+ cb(opts.nextSlide, els[opts.nextSlide]);
+ go(els,opts,1,opts.currSlide < i); // trigger the trans
+// return false; // <== allow bubble
+ }
+
+ if ( /mouseenter|mouseover/i.test(opts.pagerEvent) ) {
+ $a.hover(pagerFn, function(){/* no-op */} );
+ }
+ else {
+ $a.bind(opts.pagerEvent, pagerFn);
+ }
+
+ if ( ! /^click/.test(opts.pagerEvent) && !opts.allowPagerClickBubble)
+ $a.bind('click.cycle', function(){return false;}); // suppress click
+
+ var cont = opts.$cont[0];
+ var pauseFlag = false; // https://github.com/malsup/cycle/issues/44
+ if (opts.pauseOnPagerHover) {
+ $a.hover(
+ function() {
+ pauseFlag = true;
+ cont.cyclePause++;
+ triggerPause(cont,true,true);
+ }, function() {
+ pauseFlag && cont.cyclePause--;
+ triggerPause(cont,true,true);
+ }
+ );
+ }
+};
+
+// helper fn to calculate the number of slides between the current and the next
+$.fn.cycle.hopsFromLast = function(opts, fwd) {
+ var hops, l = opts.lastSlide, c = opts.currSlide;
+ if (fwd)
+ hops = c > l ? c - l : opts.slideCount - l;
+ else
+ hops = c < l ? l - c : l + opts.slideCount - c;
+ return hops;
+};
+
+// fix clearType problems in ie6 by setting an explicit bg color
+// (otherwise text slides look horrible during a fade transition)
+function clearTypeFix($slides) {
+ debug('applying clearType background-color hack');
+ function hex(s) {
+ s = parseInt(s,10).toString(16);
+ return s.length < 2 ? '0'+s : s;
+ };
+ function getBg(e) {
+ for ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) {
+ var v = $.css(e,'background-color');
+ if (v && v.indexOf('rgb') >= 0 ) {
+ var rgb = v.match(/\d+/g);
+ return '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
+ }
+ if (v && v != 'transparent')
+ return v;
+ }
+ return '#ffffff';
+ };
+ $slides.each(function() { $(this).css('background-color', getBg(this)); });
+};
+
+// reset common props before the next transition
+$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) {
+ $(opts.elements).not(curr).hide();
+ if (typeof opts.cssBefore.opacity == 'undefined')
+ opts.cssBefore.opacity = 1;
+ opts.cssBefore.display = 'block';
+ if (opts.slideResize && w !== false && next.cycleW > 0)
+ opts.cssBefore.width = next.cycleW;
+ if (opts.slideResize && h !== false && next.cycleH > 0)
+ opts.cssBefore.height = next.cycleH;
+ opts.cssAfter = opts.cssAfter || {};
+ opts.cssAfter.display = 'none';
+ $(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0));
+ $(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1));
+};
+
+// the actual fn for effecting a transition
+$.fn.cycle.custom = function(curr, next, opts, cb, fwd, speedOverride) {
+ var $l = $(curr), $n = $(next);
+ var speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut;
+ $n.css(opts.cssBefore);
+ if (speedOverride) {
+ if (typeof speedOverride == 'number')
+ speedIn = speedOut = speedOverride;
+ else
+ speedIn = speedOut = 1;
+ easeIn = easeOut = null;
+ }
+ var fn = function() {
+ $n.animate(opts.animIn, speedIn, easeIn, function() {
+ cb();
+ });
+ };
+ $l.animate(opts.animOut, speedOut, easeOut, function() {
+ $l.css(opts.cssAfter);
+ if (!opts.sync)
+ fn();
+ });
+ if (opts.sync) fn();
+};
+
+// transition definitions - only fade is defined here, transition pack defines the rest
+$.fn.cycle.transitions = {
+ fade: function($cont, $slides, opts) {
+ $slides.not(':eq('+opts.currSlide+')').css('opacity',0);
+ opts.before.push(function(curr,next,opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.opacity = 0;
+ });
+ opts.animIn = { opacity: 1 };
+ opts.animOut = { opacity: 0 };
+ opts.cssBefore = { top: 0, left: 0 };
+ }
+};
+
+$.fn.cycle.ver = function() { return ver; };
+
+// override these globally if you like (they are all optional)
+$.fn.cycle.defaults = {
+ activePagerClass: 'activeSlide', // class name used for the active pager link
+ after: null, // transition callback (scope set to element that was shown): function(currSlideElement, nextSlideElement, options, forwardFlag)
+ allowPagerClickBubble: false, // allows or prevents click event on pager anchors from bubbling
+ animIn: null, // properties that define how the slide animates in
+ animOut: null, // properties that define how the slide animates out
+ aspect: false, // preserve aspect ratio during fit resizing, cropping if necessary (must be used with fit option)
+ autostop: 0, // true to end slideshow after X transitions (where X == slide count)
+ autostopCount: 0, // number of transitions (optionally used with autostop to define X)
+ backwards: false, // true to start slideshow at last slide and move backwards through the stack
+ before: null, // transition callback (scope set to element to be shown): function(currSlideElement, nextSlideElement, options, forwardFlag)
+ center: null, // set to true to have cycle add top/left margin to each slide (use with width and height options)
+ cleartype: !$.support.opacity, // true if clearType corrections should be applied (for IE)
+ cleartypeNoBg: false, // set to true to disable extra cleartype fixing (leave false to force background color setting on slides)
+ containerResize: 1, // resize container to fit largest slide
+ continuous: 0, // true to start next transition immediately after current one completes
+ cssAfter: null, // properties that defined the state of the slide after transitioning out
+ cssBefore: null, // properties that define the initial state of the slide before transitioning in
+ delay: 0, // additional delay (in ms) for first transition (hint: can be negative)
+ easeIn: null, // easing for "in" transition
+ easeOut: null, // easing for "out" transition
+ easing: null, // easing method for both in and out transitions
+ end: null, // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options)
+ fastOnEvent: 0, // force fast transitions when triggered manually (via pager or prev/next); value == time in ms
+ fit: 0, // force slides to fit container
+ fx: 'fade', // name of transition effect (or comma separated names, ex: 'fade,scrollUp,shuffle')
+ fxFn: null, // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag)
+ height: 'auto', // container height (if the 'fit' option is true, the slides will be set to this height as well)
+ manualTrump: true, // causes manual transition to stop an active transition instead of being ignored
+ metaAttr: 'cycle',// data- attribute that holds the option data for the slideshow
+ next: null, // element, jQuery object, or jQuery selector string for the element to use as event trigger for next slide
+ nowrap: 0, // true to prevent slideshow from wrapping
+ onPagerEvent: null, // callback fn for pager events: function(zeroBasedSlideIndex, slideElement)
+ onPrevNextEvent: null,// callback fn for prev/next events: function(isNext, zeroBasedSlideIndex, slideElement)
+ pager: null, // element, jQuery object, or jQuery selector string for the element to use as pager container
+ pagerAnchorBuilder: null, // callback fn for building anchor links: function(index, DOMelement)
+ pagerEvent: 'click.cycle', // name of event which drives the pager navigation
+ pause: 0, // true to enable "pause on hover"
+ pauseOnPagerHover: 0, // true to pause when hovering over pager link
+ prev: null, // element, jQuery object, or jQuery selector string for the element to use as event trigger for previous slide
+ prevNextEvent:'click.cycle',// event which drives the manual transition to the previous or next slide
+ random: 0, // true for random, false for sequence (not applicable to shuffle fx)
+ randomizeEffects: 1, // valid when multiple effects are used; true to make the effect sequence random
+ requeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded
+ requeueTimeout: 250, // ms delay for requeue
+ rev: 0, // causes animations to transition in reverse (for effects that support it such as scrollHorz/scrollVert/shuffle)
+ shuffle: null, // coords for shuffle animation, ex: { top:15, left: 200 }
+ skipInitializationCallbacks: false, // set to true to disable the first before/after callback that occurs prior to any transition
+ slideExpr: null, // expression for selecting slides (if something other than all children is required)
+ slideResize: 1, // force slide width/height to fixed size before every transition
+ speed: 1000, // speed of the transition (any valid fx speed value)
+ speedIn: null, // speed of the 'in' transition
+ speedOut: null, // speed of the 'out' transition
+ startingSlide: undefined, // zero-based index of the first slide to be displayed
+ sync: 1, // true if in/out transitions should occur simultaneously
+ timeout: 4000, // milliseconds between slide transitions (0 to disable auto advance)
+ timeoutFn: null, // callback for determining per-slide timeout value: function(currSlideElement, nextSlideElement, options, forwardFlag)
+ updateActivePagerLink: null, // callback fn invoked to update the active pager link (adds/removes activePagerClass style)
+ width: null // container width (if the 'fit' option is true, the slides will be set to this width as well)
+};
+
+})(jQuery);
+
+
+/*!
+ * jQuery Cycle Plugin Transition Definitions
+ * This script is a plugin for the jQuery Cycle Plugin
+ * Examples and documentation at: http://malsup.com/jquery/cycle/
+ * Copyright (c) 2007-2010 M. Alsup
+ * Version: 2.73
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+(function($) {
+
+//
+// These functions define slide initialization and properties for the named
+// transitions. To save file size feel free to remove any of these that you
+// don't need.
+//
+$.fn.cycle.transitions.none = function($cont, $slides, opts) {
+ opts.fxFn = function(curr,next,opts,after){
+ $(next).show();
+ $(curr).hide();
+ after();
+ };
+};
+
+// not a cross-fade, fadeout only fades out the top slide
+$.fn.cycle.transitions.fadeout = function($cont, $slides, opts) {
+ $slides.not(':eq('+opts.currSlide+')').css({ display: 'block', 'opacity': 1 });
+ opts.before.push(function(curr,next,opts,w,h,rev) {
+ $(curr).css('zIndex',opts.slideCount + (!rev === true ? 1 : 0));
+ $(next).css('zIndex',opts.slideCount + (!rev === true ? 0 : 1));
+ });
+ opts.animIn.opacity = 1;
+ opts.animOut.opacity = 0;
+ opts.cssBefore.opacity = 1;
+ opts.cssBefore.display = 'block';
+ opts.cssAfter.zIndex = 0;
+};
+
+// scrollUp/Down/Left/Right
+$.fn.cycle.transitions.scrollUp = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var h = $cont.height();
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = 0;
+ opts.cssFirst.top = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = -h;
+};
+$.fn.cycle.transitions.scrollDown = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var h = $cont.height();
+ opts.cssFirst.top = 0;
+ opts.cssBefore.top = -h;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = h;
+};
+$.fn.cycle.transitions.scrollLeft = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var w = $cont.width();
+ opts.cssFirst.left = 0;
+ opts.cssBefore.left = w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = 0-w;
+};
+$.fn.cycle.transitions.scrollRight = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var w = $cont.width();
+ opts.cssFirst.left = 0;
+ opts.cssBefore.left = -w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = w;
+};
+$.fn.cycle.transitions.scrollHorz = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden').width();
+ opts.before.push(function(curr, next, opts, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.left = fwd ? (next.cycleW-1) : (1-next.cycleW);
+ opts.animOut.left = fwd ? -curr.cycleW : curr.cycleW;
+ });
+ opts.cssFirst.left = 0;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.top = 0;
+};
+$.fn.cycle.transitions.scrollVert = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push(function(curr, next, opts, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.top = fwd ? (1-next.cycleH) : (next.cycleH-1);
+ opts.animOut.top = fwd ? curr.cycleH : -curr.cycleH;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.left = 0;
+};
+
+// slideX/slideY
+$.fn.cycle.transitions.slideX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $(opts.elements).not(curr).hide();
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.animIn.width = next.cycleW;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+ opts.animIn.width = 'show';
+ opts.animOut.width = 0;
+};
+$.fn.cycle.transitions.slideY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $(opts.elements).not(curr).hide();
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.animIn.height = next.cycleH;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.height = 0;
+ opts.animIn.height = 'show';
+ opts.animOut.height = 0;
+};
+
+// shuffle
+$.fn.cycle.transitions.shuffle = function($cont, $slides, opts) {
+ var i, w = $cont.css('overflow', 'visible').width();
+ $slides.css({left: 0, top: 0});
+ opts.before.push(function(curr,next,opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ });
+ // only adjust speed once!
+ if (!opts.speedAdjusted) {
+ opts.speed = opts.speed / 2; // shuffle has 2 transitions
+ opts.speedAdjusted = true;
+ }
+ opts.random = 0;
+ opts.shuffle = opts.shuffle || {left:-w, top:15};
+ opts.els = [];
+ for (i=0; i < $slides.length; i++)
+ opts.els.push($slides[i]);
+
+ for (i=0; i < opts.currSlide; i++)
+ opts.els.push(opts.els.shift());
+
+ // custom transition fn (hat tip to Benjamin Sterling for this bit of sweetness!)
+ opts.fxFn = function(curr, next, opts, cb, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ var $el = fwd ? $(curr) : $(next);
+ $(next).css(opts.cssBefore);
+ var count = opts.slideCount;
+ $el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() {
+ var hops = $.fn.cycle.hopsFromLast(opts, fwd);
+ for (var k=0; k < hops; k++)
+ fwd ? opts.els.push(opts.els.shift()) : opts.els.unshift(opts.els.pop());
+ if (fwd) {
+ for (var i=0, len=opts.els.length; i < len; i++)
+ $(opts.els[i]).css('z-index', len-i+count);
+ }
+ else {
+ var z = $(curr).css('z-index');
+ $el.css('z-index', parseInt(z,10)+1+count);
+ }
+ $el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, function() {
+ $(fwd ? this : curr).hide();
+ if (cb) cb();
+ });
+ });
+ };
+ $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
+};
+
+// turnUp/Down/Left/Right
+$.fn.cycle.transitions.turnUp = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.cssBefore.top = next.cycleH;
+ opts.animIn.height = next.cycleH;
+ opts.animOut.width = next.cycleW;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.cssBefore.height = 0;
+ opts.animIn.top = 0;
+ opts.animOut.height = 0;
+};
+$.fn.cycle.transitions.turnDown = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.height = 0;
+ opts.animOut.height = 0;
+};
+$.fn.cycle.transitions.turnLeft = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.cssBefore.left = next.cycleW;
+ opts.animIn.width = next.cycleW;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+ opts.animIn.left = 0;
+ opts.animOut.width = 0;
+};
+$.fn.cycle.transitions.turnRight = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.animIn.width = next.cycleW;
+ opts.animOut.left = curr.cycleW;
+ });
+ $.extend(opts.cssBefore, { top: 0, left: 0, width: 0 });
+ opts.animIn.left = 0;
+ opts.animOut.width = 0;
+};
+
+// zoom
+$.fn.cycle.transitions.zoom = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,false,true);
+ opts.cssBefore.top = next.cycleH/2;
+ opts.cssBefore.left = next.cycleW/2;
+ $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
+ $.extend(opts.animOut, { width: 0, height: 0, top: curr.cycleH/2, left: curr.cycleW/2 });
+ });
+ opts.cssFirst.top = 0;
+ opts.cssFirst.left = 0;
+ opts.cssBefore.width = 0;
+ opts.cssBefore.height = 0;
+};
+
+// fadeZoom
+$.fn.cycle.transitions.fadeZoom = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,false);
+ opts.cssBefore.left = next.cycleW/2;
+ opts.cssBefore.top = next.cycleH/2;
+ $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
+ });
+ opts.cssBefore.width = 0;
+ opts.cssBefore.height = 0;
+ opts.animOut.opacity = 0;
+};
+
+// blindX
+$.fn.cycle.transitions.blindX = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','hidden').width();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.width = next.cycleW;
+ opts.animOut.left = curr.cycleW;
+ });
+ opts.cssBefore.left = w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = w;
+};
+// blindY
+$.fn.cycle.transitions.blindY = function($cont, $slides, opts) {
+ var h = $cont.css('overflow','hidden').height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = h;
+};
+// blindZ
+$.fn.cycle.transitions.blindZ = function($cont, $slides, opts) {
+ var h = $cont.css('overflow','hidden').height();
+ var w = $cont.width();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = w;
+ opts.animIn.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.top = h;
+ opts.animOut.left = w;
+};
+
+// growX - grow horizontally from centered 0 width
+$.fn.cycle.transitions.growX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.cssBefore.left = this.cycleW/2;
+ opts.animIn.left = 0;
+ opts.animIn.width = this.cycleW;
+ opts.animOut.left = 0;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+};
+// growY - grow vertically from centered 0 height
+$.fn.cycle.transitions.growY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.cssBefore.top = this.cycleH/2;
+ opts.animIn.top = 0;
+ opts.animIn.height = this.cycleH;
+ opts.animOut.top = 0;
+ });
+ opts.cssBefore.height = 0;
+ opts.cssBefore.left = 0;
+};
+
+// curtainX - squeeze in both edges horizontally
+$.fn.cycle.transitions.curtainX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true,true);
+ opts.cssBefore.left = next.cycleW/2;
+ opts.animIn.left = 0;
+ opts.animIn.width = this.cycleW;
+ opts.animOut.left = curr.cycleW/2;
+ opts.animOut.width = 0;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+};
+// curtainY - squeeze in both edges vertically
+$.fn.cycle.transitions.curtainY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false,true);
+ opts.cssBefore.top = next.cycleH/2;
+ opts.animIn.top = 0;
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH/2;
+ opts.animOut.height = 0;
+ });
+ opts.cssBefore.height = 0;
+ opts.cssBefore.left = 0;
+};
+
+// cover - curr slide covered by next slide
+$.fn.cycle.transitions.cover = function($cont, $slides, opts) {
+ var d = opts.direction || 'left';
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ if (d == 'right')
+ opts.cssBefore.left = -w;
+ else if (d == 'up')
+ opts.cssBefore.top = h;
+ else if (d == 'down')
+ opts.cssBefore.top = -h;
+ else
+ opts.cssBefore.left = w;
+ });
+ opts.animIn.left = 0;
+ opts.animIn.top = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.left = 0;
+};
+
+// uncover - curr slide moves off next slide
+$.fn.cycle.transitions.uncover = function($cont, $slides, opts) {
+ var d = opts.direction || 'left';
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ if (d == 'right')
+ opts.animOut.left = w;
+ else if (d == 'up')
+ opts.animOut.top = -h;
+ else if (d == 'down')
+ opts.animOut.top = h;
+ else
+ opts.animOut.left = -w;
+ });
+ opts.animIn.left = 0;
+ opts.animIn.top = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.left = 0;
+};
+
+// toss - move top slide and fade away
+$.fn.cycle.transitions.toss = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','visible').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ // provide default toss settings if animOut not provided
+ if (!opts.animOut.left && !opts.animOut.top)
+ $.extend(opts.animOut, { left: w*2, top: -h/2, opacity: 0 });
+ else
+ opts.animOut.opacity = 0;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+};
+
+// wipe - clip animation
+$.fn.cycle.transitions.wipe = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.cssBefore = opts.cssBefore || {};
+ var clip;
+ if (opts.clip) {
+ if (/l2r/.test(opts.clip))
+ clip = 'rect(0px 0px '+h+'px 0px)';
+ else if (/r2l/.test(opts.clip))
+ clip = 'rect(0px '+w+'px '+h+'px '+w+'px)';
+ else if (/t2b/.test(opts.clip))
+ clip = 'rect(0px '+w+'px 0px 0px)';
+ else if (/b2t/.test(opts.clip))
+ clip = 'rect('+h+'px '+w+'px '+h+'px 0px)';
+ else if (/zoom/.test(opts.clip)) {
+ var top = parseInt(h/2,10);
+ var left = parseInt(w/2,10);
+ clip = 'rect('+top+'px '+left+'px '+top+'px '+left+'px)';
+ }
+ }
+
+ opts.cssBefore.clip = opts.cssBefore.clip || clip || 'rect(0px 0px 0px 0px)';
+
+ var d = opts.cssBefore.clip.match(/(\d+)/g);
+ var t = parseInt(d[0],10), r = parseInt(d[1],10), b = parseInt(d[2],10), l = parseInt(d[3],10);
+
+ opts.before.push(function(curr, next, opts) {
+ if (curr == next) return;
+ var $curr = $(curr), $next = $(next);
+ $.fn.cycle.commonReset(curr,next,opts,true,true,false);
+ opts.cssAfter.display = 'block';
+
+ var step = 1, count = parseInt((opts.speedIn / 13),10) - 1;
+ (function f() {
+ var tt = t ? t - parseInt(step * (t/count),10) : 0;
+ var ll = l ? l - parseInt(step * (l/count),10) : 0;
+ var bb = b < h ? b + parseInt(step * ((h-b)/count || 1),10) : h;
+ var rr = r < w ? r + parseInt(step * ((w-r)/count || 1),10) : w;
+ $next.css({ clip: 'rect('+tt+'px '+rr+'px '+bb+'px '+ll+'px)' });
+ (step++ <= count) ? setTimeout(f, 13) : $curr.css('display', 'none');
+ })();
+ });
+ $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
+ opts.animIn = { left: 0 };
+ opts.animOut = { left: 0 };
+};
+
+})(jQuery);
diff --git a/resources/jquery/jquery.expandableField.js b/resources/jquery/jquery.expandableField.js
index ebe29a41..3c65d016 100644
--- a/resources/jquery/jquery.expandableField.js
+++ b/resources/jquery/jquery.expandableField.js
@@ -1,5 +1,5 @@
/**
- * This plugin provides functionallity to expand a text box on focus to double it's current width
+ * This plugin provides functionality to expand a text box on focus to double it's current width
*
* Usage:
*
diff --git a/resources/jquery/jquery.farbtastic.css b/resources/jquery/jquery.farbtastic.css
new file mode 100644
index 00000000..1c6428f8
--- /dev/null
+++ b/resources/jquery/jquery.farbtastic.css
@@ -0,0 +1,54 @@
+/**
+ * Farbtastic Color Picker 1.2
+ * © 2008 Steven Wittens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+.farbtastic {
+ position: relative;
+}
+.farbtastic * {
+ position: absolute;
+ cursor: crosshair;
+}
+.farbtastic, .farbtastic .wheel {
+ width: 195px;
+ height: 195px;
+}
+.farbtastic .color, .farbtastic .overlay {
+ top: 47px;
+ left: 47px;
+ width: 101px;
+ height: 101px;
+}
+.farbtastic .wheel {
+ /* @embed */
+ background: url(images/wheel.png) no-repeat;
+ width: 195px;
+ height: 195px;
+}
+.farbtastic .overlay {
+ /* @embed */
+ background: url(images/mask.png) no-repeat;
+}
+.farbtastic .marker {
+ width: 17px;
+ height: 17px;
+ margin: -8px 0 0 -8px;
+ overflow: hidden;
+ /* @embed */
+ background: url(images/marker.png) no-repeat;
+}
+
diff --git a/resources/jquery/jquery.farbtastic.js b/resources/jquery/jquery.farbtastic.js
new file mode 100644
index 00000000..18810857
--- /dev/null
+++ b/resources/jquery/jquery.farbtastic.js
@@ -0,0 +1,286 @@
+/**
+ * Farbtastic Color Picker 1.2
+ * © 2008 Steven Wittens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+//Adapted to uniform style with jQuery UI widgets and slightly change behavior
+//TODO:
+// - remove duplicated code by replacing it with jquery.colorUtils and modern jQuery
+// - uniform code style
+
+jQuery.fn.farbtastic = function (callback) {
+ $.farbtastic(this, callback);
+ return this;
+};
+
+jQuery.farbtastic = function (container, callback) {
+ var container = $(container).get(0);
+ return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback));
+}
+
+jQuery._farbtastic = function (container, callback) {
+ // Store farbtastic object
+ var fb = this;
+
+ // Insert markup
+ $(container).html('<div class="farbtastic ui-widget-content"><div class="color"></div><div class="wheel"></div><div class="overlay"></div><div class="h-marker marker"></div><div class="sl-marker marker"></div></div>');
+ $(container).addClass('ui-widget');
+ var e = $('.farbtastic', container);
+ fb.wheel = $('.wheel', container).get(0);
+ // Dimensions
+ fb.radius = 84;
+ fb.square = 100;
+ fb.width = 194;
+
+ // Fix background PNGs in IE6
+ if (navigator.appVersion.match(/MSIE [0-6]\./)) {
+ $('*', e).each(function () {
+ if (this.currentStyle.backgroundImage != 'none') {
+ var image = this.currentStyle.backgroundImage;
+ image = this.currentStyle.backgroundImage.substring(5, image.length - 2);
+ $(this).css({
+ 'backgroundImage': 'none',
+ 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
+ });
+ }
+ });
+ }
+
+ /**
+ * Link to the given element(s) or callback.
+ */
+ fb.linkTo = function (callback) {
+ // Unbind previous nodes
+ if (typeof fb.callback == 'object') {
+ $(fb.callback).unbind('keyup', fb.updateValue);
+ }
+
+ // Reset color
+ fb.color = null;
+
+ // Bind callback or elements
+ if (typeof callback == 'function') {
+ fb.callback = callback;
+ }
+ else if (typeof callback == 'object' || typeof callback == 'string') {
+ fb.callback = $(callback);
+ fb.callback.bind('keyup', fb.updateValue);
+ if (fb.callback.get(0).value) {
+ fb.setColor(fb.callback.get(0).value);
+ }
+ }
+ return this;
+ }
+ fb.updateValue = function (event) {
+ if (this.value != fb.color) {
+ fb.setColor(this.value);
+ }
+ }
+
+ /**
+ * Change color with HTML syntax #123456
+ */
+ fb.setColor = function (color) {
+ var rgb = $.colorUtil.getRGB( color );
+ if (fb.color != color && rgb) {
+ rgb = rgb.slice( 0 ); //make a clone
+ //TODO: rewrite code so that this is not needed
+ rgb[0] /= 255;
+ rgb[1] /= 255;
+ rgb[2] /= 255;
+ fb.color = color;
+ fb.rgb = rgb;
+ fb.hsl = fb.RGBToHSL(fb.rgb);
+ fb.updateDisplay();
+ }
+ return this;
+ }
+
+ /**
+ * Change color with HSL triplet [0..1, 0..1, 0..1]
+ */
+ fb.setHSL = function (hsl) {
+ fb.hsl = hsl;
+ fb.rgb = fb.HSLToRGB(hsl);
+ fb.color = fb.pack(fb.rgb);
+ fb.updateDisplay();
+ return this;
+ }
+
+ /////////////////////////////////////////////////////
+
+ /**
+ * Retrieve the coordinates of the given event relative to the center
+ * of the widget.
+ */
+ fb.widgetCoords = function (event) {
+ var ref = $( fb.wheel ).offset();
+ return {
+ x: event.pageX - ref.left - fb.width / 2,
+ y: event.pageY - ref.top - fb.width / 2
+ };
+ }
+
+ /**
+ * Mousedown handler
+ */
+ fb.mousedown = function (event) {
+ // Capture mouse
+ if (!document.dragging) {
+ $(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup);
+ document.dragging = true;
+ }
+
+ // Check which area is being dragged
+ var pos = fb.widgetCoords(event);
+ fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square;
+
+ // Process
+ fb.mousemove(event);
+ return false;
+ }
+
+ /**
+ * Mousemove handler
+ */
+ fb.mousemove = function (event) {
+ // Get coordinates relative to color picker center
+ var pos = fb.widgetCoords(event);
+
+ // Set new HSL parameters
+ if (fb.circleDrag) {
+ var hue = Math.atan2(pos.x, -pos.y) / 6.28;
+ if (hue < 0) hue += 1;
+ fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]);
+ }
+ else {
+ var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5));
+ var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5));
+ fb.setHSL([fb.hsl[0], sat, lum]);
+ }
+ return false;
+ }
+
+ /**
+ * Mouseup handler
+ */
+ fb.mouseup = function () {
+ // Uncapture mouse
+ $(document).unbind('mousemove', fb.mousemove);
+ $(document).unbind('mouseup', fb.mouseup);
+ document.dragging = false;
+ }
+
+ /**
+ * Update the markers and styles
+ */
+ fb.updateDisplay = function () {
+ // Markers
+ var angle = fb.hsl[0] * 6.28;
+ $('.h-marker', e).css({
+ left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px',
+ top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px'
+ });
+
+ $('.sl-marker', e).css({
+ left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px',
+ top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px'
+ });
+
+ // Saturation/Luminance gradient
+ $('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5])));
+
+ // Linked elements or callback
+ if (typeof fb.callback == 'object') {
+ // Set background/foreground color
+ $(fb.callback).css({
+ backgroundColor: fb.color,
+ color: fb.hsl[2] > 0.5 ? '#000' : '#fff'
+ });
+
+ // Change linked value
+ $(fb.callback).each(function() {
+ if ( $( this ).val() != fb.color) {
+ $( this ).val( fb.color ).change();
+ }
+ });
+ }
+ else if (typeof fb.callback == 'function') {
+ fb.callback.call(fb, fb.color);
+ }
+ }
+
+ /* Various color utility functions */
+ fb.pack = function (rgb) {
+ var r = Math.round(rgb[0] * 255);
+ var g = Math.round(rgb[1] * 255);
+ var b = Math.round(rgb[2] * 255);
+ return '#' + (r < 16 ? '0' : '') + r.toString(16) +
+ (g < 16 ? '0' : '') + g.toString(16) +
+ (b < 16 ? '0' : '') + b.toString(16);
+ }
+
+ fb.HSLToRGB = function (hsl) {
+ var m1, m2, r, g, b;
+ var h = hsl[0], s = hsl[1], l = hsl[2];
+ m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s;
+ m1 = l * 2 - m2;
+ return [this.hueToRGB(m1, m2, h+0.33333),
+ this.hueToRGB(m1, m2, h),
+ this.hueToRGB(m1, m2, h-0.33333)];
+ }
+
+ fb.hueToRGB = function (m1, m2, h) {
+ h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
+ if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
+ if (h * 2 < 1) return m2;
+ if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
+ return m1;
+ }
+
+ fb.RGBToHSL = function (rgb) {
+ var min, max, delta, h, s, l;
+ var r = rgb[0], g = rgb[1], b = rgb[2];
+ min = Math.min(r, Math.min(g, b));
+ max = Math.max(r, Math.max(g, b));
+ delta = max - min;
+ l = (min + max) / 2;
+ s = 0;
+ if (l > 0 && l < 1) {
+ s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
+ }
+ h = 0;
+ if (delta > 0) {
+ if (max == r && max != g) h += (g - b) / delta;
+ if (max == g && max != b) h += (2 + (b - r) / delta);
+ if (max == b && max != r) h += (4 + (r - g) / delta);
+ h /= 6;
+ }
+ return [h, s, l];
+ }
+
+ // Install mousedown handler (the others are set on the document on-demand)
+ $('*', e).mousedown(fb.mousedown);
+
+ // Init color
+ fb.setColor('#000000');
+
+ // Set linked elements/callback
+ if (callback) {
+ fb.linkTo(callback);
+ }
+}
diff --git a/resources/jquery/jquery.footHovzer.css b/resources/jquery/jquery.footHovzer.css
new file mode 100644
index 00000000..77d9514c
--- /dev/null
+++ b/resources/jquery/jquery.footHovzer.css
@@ -0,0 +1,6 @@
+#jquery-foot-hovzer {
+ position: fixed;
+ bottom: 0;
+ width: 100%;
+ z-index: 1000;
+}
diff --git a/resources/jquery/jquery.footHovzer.js b/resources/jquery/jquery.footHovzer.js
new file mode 100644
index 00000000..56fc32d4
--- /dev/null
+++ b/resources/jquery/jquery.footHovzer.js
@@ -0,0 +1,45 @@
+/**
+ * Utility to stack stuff in an overlay fixed on the bottom of the page.
+ *
+ * Usage:
+ * <code>
+ * var hovzer = $.getFootHovzer();
+ * hovzer.$.append( $myCollection );
+ * hovzer.update();
+ * </code>
+ *
+ * @author Timo Tijhof, 2012
+ */
+( function ( $ ) {
+ var $hovzer, footHovzer, prevHeight, newHeight;
+
+ function getHovzer() {
+ if ( $hovzer === undefined ) {
+ $hovzer = $( '<div id="jquery-foot-hovzer"></div>' ).appendTo( 'body' );
+ }
+ return $hovzer;
+ }
+
+ footHovzer = {
+ update: function () {
+ 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;
+ }
+ }
+ };
+
+ $.getFootHovzer = function () {
+ footHovzer.$ = getHovzer();
+ return footHovzer;
+ };
+
+}( jQuery ) );
diff --git a/resources/jquery/jquery.form.js b/resources/jquery/jquery.form.js
index 53c078f9..fdcdd15a 100644
--- a/resources/jquery/jquery.form.js
+++ b/resources/jquery/jquery.form.js
@@ -1,6 +1,6 @@
/*!
* jQuery Form Plugin
- * version: 2.52 (07-DEC-2010)
+ * version: 2.84 (12-AUG-2011)
* @requires jQuery v1.3.2 or later
*
* Examples and documentation at: http://malsup.com/jquery/form/
@@ -8,7 +8,7 @@
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
-(function($) {
+;(function($) {
/*
Usage Note:
@@ -49,22 +49,26 @@ $.fn.ajaxSubmit = function(options) {
log('ajaxSubmit: skipping submit process - no element selected');
return this;
}
+
+ var method, action, url, $form = this;
if (typeof options == 'function') {
options = { success: options };
}
- var action = this.attr('action');
- var url = (typeof action === 'string') ? $.trim(action) : '';
+ method = this.attr('method');
+ action = this.attr('action');
+ url = (typeof action === 'string') ? $.trim(action) : '';
+ url = url || window.location.href || '';
if (url) {
// clean url (don't include hash vaue)
url = (url.match(/^([^#]+)/)||[])[1];
}
- url = url || window.location.href || '';
options = $.extend(true, {
url: url,
- type: this.attr('method') || 'GET',
+ success: $.ajaxSettings.success,
+ type: method || 'GET',
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
}, options);
@@ -87,7 +91,7 @@ $.fn.ajaxSubmit = function(options) {
if (options.data) {
options.extraData = options.data;
for (n in options.data) {
- if(options.data[n] instanceof Array) {
+ if( $.isArray(options.data[n]) ) {
for (var k in options.data[n]) {
a.push( { name: n, value: options.data[n][k] } );
}
@@ -123,7 +127,7 @@ $.fn.ajaxSubmit = function(options) {
options.data = q; // data is the query string for 'post'
}
- var $form = this, callbacks = [];
+ var callbacks = [];
if (options.resetForm) {
callbacks.push(function() { $form.resetForm(); });
}
@@ -161,14 +165,20 @@ $.fn.ajaxSubmit = function(options) {
// hack to fix Safari hang (thanks to Tim Molendijk for this)
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
if (options.closeKeepAlive) {
- $.get(options.closeKeepAlive, fileUpload);
+ $.get(options.closeKeepAlive, function() { fileUpload(a); });
}
else {
- fileUpload();
+ fileUpload(a);
}
}
else {
- $.ajax(options);
+ // IE7 massage (see issue 57)
+ if ($.browser.msie && method == 'get') {
+ var ieMeth = $form[0].getAttribute('method');
+ if (typeof ieMeth === 'string')
+ options.type = ieMeth;
+ }
+ $.ajax(options);
}
// fire 'notify' event
@@ -177,8 +187,17 @@ $.fn.ajaxSubmit = function(options) {
// private function for handling file uploads (hat tip to YAHOO!)
- function fileUpload() {
- var form = $form[0];
+ function fileUpload(a) {
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
+ var useProp = !!$.fn.prop;
+
+ if (a) {
+ // ensure that every serialized input is still enabled
+ for (i=0; i < a.length; i++) {
+ el = $(form[a[i].name]);
+ el[ useProp ? 'prop' : 'attr' ]('disabled', false);
+ }
+ }
if ($(':input[name=submit],:input[id=submit]', form).length) {
// if there is an input with a name or id of 'submit' then we won't be
@@ -187,23 +206,25 @@ $.fn.ajaxSubmit = function(options) {
return;
}
- var s = $.extend(true, {}, $.ajaxSettings, options);
+ s = $.extend(true, {}, $.ajaxSettings, options);
s.context = s.context || s;
- var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
- window[fn] = function() {
- var f = $io.data('form-plugin-onload');
- if (f) {
- f();
- window[fn] = undefined;
- try { delete window[fn]; } catch(e){}
- }
- };
- var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" onload="window[\'_\'+this.id]()" />');
- var io = $io[0];
+ id = 'jqFormIO' + (new Date().getTime());
+ if (s.iframeTarget) {
+ $io = $(s.iframeTarget);
+ n = $io.attr('name');
+ if (n == null)
+ $io.attr('name', id);
+ else
+ id = n;
+ }
+ else {
+ $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+ }
+ io = $io[0];
- $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
- var xhr = { // mock object
+ xhr = { // mock object
aborted: 0,
responseText: null,
responseXML: null,
@@ -212,13 +233,19 @@ $.fn.ajaxSubmit = function(options) {
getAllResponseHeaders: function() {},
getResponseHeader: function() {},
setRequestHeader: function() {},
- abort: function() {
+ abort: function(status) {
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
+ log('aborting upload... ' + e);
this.aborted = 1;
$io.attr('src', s.iframeSrc); // abort op in progress
+ xhr.error = e;
+ s.error && s.error.call(s.context, xhr, e, status);
+ g && $.event.trigger("ajaxError", [xhr, s, e]);
+ s.complete && s.complete.call(s.context, xhr, e);
}
};
- var g = s.global;
+ g = s.global;
// trigger ajax global events so that activity/block indicators work like normal
if (g && ! $.active++) {
$.event.trigger("ajaxStart");
@@ -228,7 +255,7 @@ $.fn.ajaxSubmit = function(options) {
}
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
- if (s.global) {
+ if (s.global) {
$.active--;
}
return;
@@ -237,13 +264,10 @@ $.fn.ajaxSubmit = function(options) {
return;
}
- var cbInvoked = false;
- var timedOut = 0;
-
// add submitting element to data if we know it
- var sub = form.clk;
+ sub = form.clk;
if (sub) {
- var n = sub.name;
+ n = sub.name;
if (n && !sub.disabled) {
s.extraData = s.extraData || {};
s.extraData[n] = sub.value;
@@ -253,7 +277,15 @@ $.fn.ajaxSubmit = function(options) {
}
}
}
+
+ var CLIENT_TIMEOUT_ABORT = 1;
+ var SERVER_ABORT = 2;
+ function getDoc(frame) {
+ var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
+ return doc;
+ }
+
// take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() {
// make sure form attrs are set
@@ -261,15 +293,15 @@ $.fn.ajaxSubmit = function(options) {
// update form attrs in IE friendly way
form.setAttribute('target',id);
- if (form.getAttribute('method') != 'POST') {
+ if (!method) {
form.setAttribute('method', 'POST');
}
- if (form.getAttribute('action') != s.url) {
+ if (a != s.url) {
form.setAttribute('action', s.url);
}
// ie borks in some cases when setting encoding
- if (! s.skipEncodingOverride) {
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
$form.attr({
encoding: 'multipart/form-data',
enctype: 'multipart/form-data'
@@ -278,7 +310,23 @@ $.fn.ajaxSubmit = function(options) {
// support timout
if (s.timeout) {
- setTimeout(function() { timedOut = true; cb(); }, s.timeout);
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
+ }
+
+ // look for server aborts
+ function checkState() {
+ try {
+ var state = getDoc(io).readyState;
+ log('state = ' + state);
+ if (state.toLowerCase() == 'uninitialized')
+ setTimeout(checkState,50);
+ }
+ catch(e) {
+ log('Server abort: ' , e, ' (', e.name, ')');
+ cb(SERVER_ABORT);
+ timeoutHandle && clearTimeout(timeoutHandle);
+ timeoutHandle = undefined;
+ }
}
// add "extra" data to form if provided in options
@@ -287,14 +335,17 @@ $.fn.ajaxSubmit = function(options) {
if (s.extraData) {
for (var n in s.extraData) {
extraInputs.push(
- $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
+ $('<input type="hidden" name="'+n+'" />').attr('value',s.extraData[n])
.appendTo(form)[0]);
}
}
- // add iframe to doc and submit the form
- $io.appendTo('body');
- $io.data('form-plugin-onload', cb);
+ if (!s.iframeTarget) {
+ // add iframe to doc and submit the form
+ $io.appendTo('body');
+ io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
+ }
+ setTimeout(checkState,15);
form.submit();
}
finally {
@@ -315,24 +366,42 @@ $.fn.ajaxSubmit = function(options) {
else {
setTimeout(doSubmit, 10); // this lets dom updates render
}
-
- var data, doc, domCheckCount = 50;
- function cb() {
- if (cbInvoked) {
+ var data, doc, domCheckCount = 50, callbackProcessed;
+
+ function cb(e) {
+ if (xhr.aborted || callbackProcessed) {
+ return;
+ }
+ try {
+ doc = getDoc(io);
+ }
+ catch(ex) {
+ log('cannot access response document: ', ex);
+ e = SERVER_ABORT;
+ }
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
+ xhr.abort('timeout');
+ return;
+ }
+ else if (e == SERVER_ABORT && xhr) {
+ xhr.abort('server abort');
return;
}
- $io.removeData('form-plugin-onload');
-
- var ok = true;
+ if (!doc || doc.location.href == s.iframeSrc) {
+ // response not received yet
+ if (!timedOut)
+ return;
+ }
+ io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
+
+ var status = 'success', errMsg;
try {
if (timedOut) {
throw 'timeout';
}
- // extract the server response from the iframe
- doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
-
+
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
log('isXml='+isXml);
if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
@@ -349,76 +418,104 @@ $.fn.ajaxSubmit = function(options) {
}
//log('response detected');
- cbInvoked = true;
- xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null;
+ var docRoot = doc.body ? doc.body : doc.documentElement;
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+ if (isXml)
+ s.dataType = 'xml';
xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType};
return headers[header];
};
-
- var scr = /(json|script)/.test(s.dataType);
+ // support for XHR 'status' & 'statusText' emulation :
+ if (docRoot) {
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
+ }
+
+ var dt = s.dataType || '';
+ var scr = /(json|script|text)/.test(dt.toLowerCase());
if (scr || s.textarea) {
// see if user embedded response in textarea
var ta = doc.getElementsByTagName('textarea')[0];
if (ta) {
xhr.responseText = ta.value;
+ // support for XHR 'status' & 'statusText' emulation :
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
}
else if (scr) {
// account for browsers injecting pre around json response
var pre = doc.getElementsByTagName('pre')[0];
var b = doc.getElementsByTagName('body')[0];
if (pre) {
- xhr.responseText = pre.textContent;
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerHTML;
}
else if (b) {
xhr.responseText = b.innerHTML;
}
- }
+ }
}
else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
xhr.responseXML = toXml(xhr.responseText);
}
- data = $.httpData(xhr, s.dataType);
+
+ try {
+ data = httpData(xhr, s.dataType, s);
+ }
+ catch (e) {
+ status = 'parsererror';
+ xhr.error = errMsg = (e || status);
+ }
}
- catch(e){
- log('error caught:',e);
- ok = false;
- xhr.error = e;
- $.handleError(s, xhr, 'error', e);
+ catch (e) {
+ log('error caught: ',e);
+ status = 'error';
+ xhr.error = errMsg = (e || status);
}
-
+
if (xhr.aborted) {
log('upload aborted');
- ok = false;
+ status = null;
}
+ if (xhr.status) { // we've set xhr.status
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
+ }
+
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
- if (ok) {
- s.success.call(s.context, data, 'success', xhr);
- if (g) {
- $.event.trigger("ajaxSuccess", [xhr, s]);
- }
- }
- if (g) {
- $.event.trigger("ajaxComplete", [xhr, s]);
+ if (status === 'success') {
+ s.success && s.success.call(s.context, data, 'success', xhr);
+ g && $.event.trigger("ajaxSuccess", [xhr, s]);
}
+ else if (status) {
+ if (errMsg == undefined)
+ errMsg = xhr.statusText;
+ s.error && s.error.call(s.context, xhr, status, errMsg);
+ g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
+ }
+
+ g && $.event.trigger("ajaxComplete", [xhr, s]);
+
if (g && ! --$.active) {
$.event.trigger("ajaxStop");
}
- if (s.complete) {
- s.complete.call(s.context, xhr, ok ? 'success' : 'error');
- }
+
+ s.complete && s.complete.call(s.context, xhr, status);
+
+ callbackProcessed = true;
+ if (s.timeout)
+ clearTimeout(timeoutHandle);
// clean up
setTimeout(function() {
- $io.removeData('form-plugin-onload');
- $io.remove();
+ if (!s.iframeTarget)
+ $io.remove();
xhr.responseXML = null;
}, 100);
}
- function toXml(s, doc) {
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
if (window.ActiveXObject) {
doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = 'false';
@@ -427,8 +524,33 @@ $.fn.ajaxSubmit = function(options) {
else {
doc = (new DOMParser()).parseFromString(s, 'text/xml');
}
- return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
- }
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+ };
+ var parseJSON = $.parseJSON || function(s) {
+ return window['eval']('(' + s + ')');
+ };
+
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+
+ var ct = xhr.getResponseHeader('content-type') || '',
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if (xml && data.documentElement.nodeName === 'parsererror') {
+ $.error && $.error('parsererror');
+ }
+ if (s && s.dataFilter) {
+ data = s.dataFilter(data, type);
+ }
+ if (typeof data === 'string') {
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+ data = parseJSON(data);
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+ $.globalEval(data);
+ }
+ }
+ return data;
+ };
}
};
@@ -462,7 +584,7 @@ $.fn.ajaxForm = function(options) {
log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
return this;
}
-
+
return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
e.preventDefault();
@@ -526,7 +648,7 @@ $.fn.formToArray = function(semantic) {
if (!els) {
return a;
}
-
+
var i,j,n,v,el,max,jmax;
for(i=0, max=els.length; i < max; i++) {
el = els[i];
@@ -711,9 +833,10 @@ $.fn.clearForm = function() {
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function() {
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
- if (t == 'text' || t == 'password' || tag == 'textarea') {
+ if (re.test(t) || tag == 'textarea') {
this.value = '';
}
else if (t == 'checkbox' || t == 'radio') {
@@ -775,16 +898,13 @@ $.fn.selected = function(select) {
};
// helper fn for console logging
-// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
- if ($.fn.ajaxSubmit.debug) {
- var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
- if (window.console && window.console.log) {
- window.console.log(msg);
- }
- else if (window.opera && window.opera.postError) {
- window.opera.postError(msg);
- }
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ else if (window.opera && window.opera.postError) {
+ window.opera.postError(msg);
}
};
diff --git a/resources/jquery/jquery.js b/resources/jquery/jquery.js
index 11e6d067..8ccd0ea7 100644
--- a/resources/jquery/jquery.js
+++ b/resources/jquery/jquery.js
@@ -1,5 +1,5 @@
/*!
- * jQuery JavaScript Library v1.6.4
+ * jQuery JavaScript Library v1.7.1
* http://jquery.com/
*
* Copyright 2011, John Resig
@@ -11,7 +11,7 @@
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
- * Date: Mon Sep 12 18:54:48 2011 -0400
+ * Date: Mon Nov 21 21:11:03 2011 -0500
*/
(function( window, undefined ) {
@@ -47,9 +47,6 @@ var jQuery = function( selector, context ) {
trimLeft = /^\s+/,
trimRight = /\s+$/,
- // Check for digits
- rdigit = /\d/,
-
// Match a standalone tag
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
@@ -140,7 +137,7 @@ jQuery.fn = jQuery.prototype = {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
- doc = (context ? context.ownerDocument || context : document);
+ doc = ( context ? context.ownerDocument || context : document );
// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
@@ -157,7 +154,7 @@ jQuery.fn = jQuery.prototype = {
} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
}
return jQuery.merge( this, selector );
@@ -187,7 +184,7 @@ jQuery.fn = jQuery.prototype = {
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
- return (context || rootjQuery).find( selector );
+ return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
@@ -201,7 +198,7 @@ jQuery.fn = jQuery.prototype = {
return rootjQuery.ready( selector );
}
- if (selector.selector !== undefined) {
+ if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
@@ -213,7 +210,7 @@ jQuery.fn = jQuery.prototype = {
selector: "",
// The current version of jQuery being used
- jquery: "1.6.4",
+ jquery: "1.7.1",
// The default length of a jQuery object is 0
length: 0,
@@ -258,7 +255,7 @@ jQuery.fn = jQuery.prototype = {
ret.context = this.context;
if ( name === "find" ) {
- ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
@@ -279,15 +276,16 @@ jQuery.fn = jQuery.prototype = {
jQuery.bindReady();
// Add the callback
- readyList.done( fn );
+ readyList.add( fn );
return this;
},
eq: function( i ) {
+ i = +i;
return i === -1 ?
this.slice( i ) :
- this.slice( i, +i + 1 );
+ this.slice( i, i + 1 );
},
first: function() {
@@ -434,11 +432,11 @@ jQuery.extend({
}
// If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
+ readyList.fireWith( document, [ jQuery ] );
// Trigger any bound ready events
if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ jQuery( document ).trigger( "ready" ).off( "ready" );
}
}
},
@@ -448,7 +446,7 @@ jQuery.extend({
return;
}
- readyList = jQuery._Deferred();
+ readyList = jQuery.Callbacks( "once memory" );
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
@@ -504,8 +502,8 @@ jQuery.extend({
return obj && typeof obj === "object" && "setInterval" in obj;
},
- isNaN: function( obj ) {
- return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
},
type: function( obj ) {
@@ -551,7 +549,7 @@ jQuery.extend({
},
error: function( msg ) {
- throw msg;
+ throw new Error( msg );
},
parseJSON: function( data ) {
@@ -573,7 +571,7 @@ jQuery.extend({
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) {
- return (new Function( "return " + data ))();
+ return ( new Function( "return " + data ) )();
}
jQuery.error( "Invalid JSON: " + data );
@@ -688,8 +686,6 @@ jQuery.extend({
if ( array != null ) {
// The window, strings (and functions) also have 'length'
- // The extra typeof function check is to prevent crashes
- // in Safari 2 (See: #3039)
// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
var type = jQuery.type( array );
@@ -703,18 +699,22 @@ jQuery.extend({
return ret;
},
- inArray: function( elem, array ) {
- if ( !array ) {
- return -1;
- }
+ inArray: function( elem, array, i ) {
+ var len;
- if ( indexOf ) {
- return indexOf.call( array, elem );
- }
+ if ( array ) {
+ if ( indexOf ) {
+ return indexOf.call( array, elem, i );
+ }
+
+ len = array.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in array && array[ i ] === elem ) {
+ return i;
+ }
}
}
@@ -850,7 +850,7 @@ jQuery.extend({
},
now: function() {
- return (new Date()).getTime();
+ return ( new Date() ).getTime();
},
// Use of jQuery.browser is frowned upon.
@@ -957,188 +957,360 @@ return jQuery;
})();
-var // Promise methods
- promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
- // Static reference to slice
- sliceDeferred = [].slice;
+// String to Object flags format cache
+var flagsCache = {};
-jQuery.extend({
- // Create a simple deferred (one callbacks list)
- _Deferred: function() {
- var // callbacks list
- callbacks = [],
- // stored [ context , args ]
- fired,
- // to avoid firing when already doing so
- firing,
- // flag to know if the deferred has been cancelled
- cancelled,
- // the deferred itself
- deferred = {
-
- // done( f1, f2, ...)
- done: function() {
- if ( !cancelled ) {
- var args = arguments,
- i,
- length,
- elem,
- type,
- _fired;
- if ( fired ) {
- _fired = fired;
- fired = 0;
- }
- for ( i = 0, length = args.length; i < length; i++ ) {
- elem = args[ i ];
- type = jQuery.type( elem );
- if ( type === "array" ) {
- deferred.done.apply( deferred, elem );
- } else if ( type === "function" ) {
- callbacks.push( elem );
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+ var object = flagsCache[ flags ] = {},
+ i, length;
+ flags = flags.split( /\s+/ );
+ for ( i = 0, length = flags.length; i < length; i++ ) {
+ object[ flags[i] ] = true;
+ }
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * flags: an optional list of space-separated flags that will change how
+ * the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+ // Convert flags from String-formatted to Object-formatted
+ // (we check in cache first)
+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+ var // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = [],
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Add one or several callbacks to the list
+ add = function( args ) {
+ var i,
+ length,
+ elem,
+ type,
+ actual;
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ // Inspect recursively
+ add( elem );
+ } else if ( type === "function" ) {
+ // Add if not in unique mode and callback is not in
+ if ( !flags.unique || !self.has( elem ) ) {
+ list.push( elem );
+ }
+ }
+ }
+ },
+ // Fire callbacks
+ fire = function( context, args ) {
+ args = args || [];
+ memory = !flags.memory || [ context, args ];
+ firing = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
+ memory = true; // Mark as halted
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( !flags.once ) {
+ if ( stack && stack.length ) {
+ memory = stack.shift();
+ self.fireWith( memory[ 0 ], memory[ 1 ] );
+ }
+ } else if ( memory === true ) {
+ self.disable();
+ } else {
+ list = [];
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ var length = list.length;
+ add( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away, unless previous
+ // firing was halted (stopOnFalse)
+ } else if ( memory && memory !== true ) {
+ firingStart = length;
+ fire( memory[ 0 ], memory[ 1 ] );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ var args = arguments,
+ argIndex = 0,
+ argLength = args.length;
+ for ( ; argIndex < argLength ; argIndex++ ) {
+ for ( var i = 0; i < list.length; i++ ) {
+ if ( args[ argIndex ] === list[ i ] ) {
+ // Handle firingIndex and firingLength
+ if ( firing ) {
+ if ( i <= firingLength ) {
+ firingLength--;
+ if ( i <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ // Remove the element
+ list.splice( i--, 1 );
+ // If we have some unicity property then
+ // we only need to do this once
+ if ( flags.unique ) {
+ break;
+ }
}
}
- if ( _fired ) {
- deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
- }
}
- return this;
- },
-
- // resolve with given context and args
- resolveWith: function( context, args ) {
- if ( !cancelled && !fired && !firing ) {
- // make sure args are available (#8421)
- args = args || [];
- firing = 1;
- try {
- while( callbacks[ 0 ] ) {
- callbacks.shift().apply( context, args );
- }
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ if ( list ) {
+ var i = 0,
+ length = list.length;
+ for ( ; i < length; i++ ) {
+ if ( fn === list[ i ] ) {
+ return true;
}
- finally {
- fired = [ context, args ];
- firing = 0;
+ }
+ }
+ return false;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory || memory === true ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( stack ) {
+ if ( firing ) {
+ if ( !flags.once ) {
+ stack.push( [ context, args ] );
}
+ } else if ( !( flags.once && memory ) ) {
+ fire( context, args );
}
- return this;
- },
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!memory;
+ }
+ };
- // resolve with this as context and given arguments
- resolve: function() {
- deferred.resolveWith( this, arguments );
- return this;
- },
+ return self;
+};
- // Has this deferred been resolved?
- isResolved: function() {
- return !!( firing || fired );
- },
- // Cancel
- cancel: function() {
- cancelled = 1;
- callbacks = [];
- return this;
- }
- };
- return deferred;
- },
- // Full fledged deferred (two callbacks list)
+var // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+
Deferred: function( func ) {
- var deferred = jQuery._Deferred(),
- failDeferred = jQuery._Deferred(),
- promise;
- // Add errorDeferred methods, then and promise
- jQuery.extend( deferred, {
- then: function( doneCallbacks, failCallbacks ) {
- deferred.done( doneCallbacks ).fail( failCallbacks );
- return this;
- },
- always: function() {
- return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ var doneList = jQuery.Callbacks( "once memory" ),
+ failList = jQuery.Callbacks( "once memory" ),
+ progressList = jQuery.Callbacks( "memory" ),
+ state = "pending",
+ lists = {
+ resolve: doneList,
+ reject: failList,
+ notify: progressList
},
- fail: failDeferred.done,
- rejectWith: failDeferred.resolveWith,
- reject: failDeferred.resolve,
- isRejected: failDeferred.isResolved,
- pipe: function( fnDone, fnFail ) {
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( {
- done: [ fnDone, "resolve" ],
- fail: [ fnFail, "reject" ]
- }, function( handler, data ) {
- var fn = data[ 0 ],
- action = data[ 1 ],
- returned;
- if ( jQuery.isFunction( fn ) ) {
- deferred[ handler ](function() {
- returned = fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise().then( newDefer.resolve, newDefer.reject );
- } else {
- newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
- }
- });
- } else {
- deferred[ handler ]( newDefer[ action ] );
+ promise = {
+ done: doneList.add,
+ fail: failList.add,
+ progress: progressList.add,
+
+ state: function() {
+ return state;
+ },
+
+ // Deprecated
+ isResolved: doneList.fired,
+ isRejected: failList.fired,
+
+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+ return this;
+ },
+ always: function() {
+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ return this;
+ },
+ pipe: function( fnDone, fnFail, fnProgress ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ],
+ progress: [ fnProgress, "notify" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ obj = promise;
+ } else {
+ for ( var key in promise ) {
+ obj[ key ] = promise[ key ];
}
- });
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- if ( obj == null ) {
- if ( promise ) {
- return promise;
}
- promise = obj = {};
+ return obj;
}
- var i = promiseMethods.length;
- while( i-- ) {
- obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
- }
- return obj;
- }
- });
- // Make sure only one callback list will be used
- deferred.done( failDeferred.cancel ).fail( deferred.cancel );
- // Unexpose cancel
- delete deferred.cancel;
+ },
+ deferred = promise.promise({}),
+ key;
+
+ for ( key in lists ) {
+ deferred[ key ] = lists[ key ].fire;
+ deferred[ key + "With" ] = lists[ key ].fireWith;
+ }
+
+ // Handle state
+ deferred.done( function() {
+ state = "resolved";
+ }, failList.disable, progressList.lock ).fail( function() {
+ state = "rejected";
+ }, doneList.disable, progressList.lock );
+
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
+
+ // All done!
return deferred;
},
// Deferred helper
when: function( firstParam ) {
- var args = arguments,
+ var args = sliceDeferred.call( arguments, 0 ),
i = 0,
length = args.length,
+ pValues = new Array( length ),
count = length,
+ pCount = length,
deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
firstParam :
- jQuery.Deferred();
+ jQuery.Deferred(),
+ promise = deferred.promise();
function resolveFunc( i ) {
return function( value ) {
args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
if ( !( --count ) ) {
- // Strange bug in FF4:
- // Values changed onto the arguments object sometimes end up as undefined values
- // outside the $.when method. Cloning the object into a fresh array solves the issue
- deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
+ deferred.resolveWith( deferred, args );
}
};
}
+ function progressFunc( i ) {
+ return function( value ) {
+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ deferred.notifyWith( promise, pValues );
+ };
+ }
if ( length > 1 ) {
- for( ; i < length; i++ ) {
- if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
- args[ i ].promise().then( resolveFunc(i), deferred.reject );
+ for ( ; i < length; i++ ) {
+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
} else {
--count;
}
@@ -1149,39 +1321,35 @@ jQuery.extend({
} else if ( deferred !== firstParam ) {
deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
}
- return deferred.promise();
+ return promise;
}
});
+
jQuery.support = (function() {
- var div = document.createElement( "div" ),
- documentElement = document.documentElement,
+ var support,
all,
a,
select,
opt,
input,
marginDiv,
- support,
fragment,
- body,
- testElementParent,
- testElement,
- testElementStyle,
tds,
events,
eventName,
i,
- isSupported;
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
// Preliminary tests
div.setAttribute("className", "t");
div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-
all = div.getElementsByTagName( "*" );
a = div.getElementsByTagName( "a" )[ 0 ];
@@ -1201,11 +1369,11 @@ jQuery.support = (function() {
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
- tbody: !div.getElementsByTagName( "tbody" ).length,
+ tbody: !div.getElementsByTagName("tbody").length,
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName( "link" ).length,
+ htmlSerialize: !!div.getElementsByTagName("link").length,
// Get the style information from getAttribute
// (IE uses .cssText instead)
@@ -1213,12 +1381,12 @@ jQuery.support = (function() {
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
- hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.55$/.test( a.style.opacity ),
+ opacity: /^0.55/.test( a.style.opacity ),
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
@@ -1236,6 +1404,13 @@ jQuery.support = (function() {
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
getSetAttribute: div.className !== "t",
+ // Tests for enctype support on a form(#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
// Will be defined later
submitBubbles: true,
changeBubbles: true,
@@ -1273,7 +1448,7 @@ jQuery.support = (function() {
div.cloneNode( true ).fireEvent( "onclick" );
}
- // Check if a radio maintains it's value
+ // Check if a radio maintains its value
// after being appended to the DOM
input = document.createElement("input");
input.value = "t";
@@ -1283,82 +1458,18 @@ jQuery.support = (function() {
input.setAttribute("checked", "checked");
div.appendChild( input );
fragment = document.createDocumentFragment();
- fragment.appendChild( div.firstChild );
+ fragment.appendChild( div.lastChild );
// WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
- div.innerHTML = "";
-
- // Figure out if the W3C box model works as expected
- div.style.width = div.style.paddingLeft = "1px";
-
- body = document.getElementsByTagName( "body" )[ 0 ];
- // We use our own, invisible, body unless the body is already present
- // in which case we use a div (#9239)
- testElement = document.createElement( body ? "div" : "body" );
- testElementStyle = {
- visibility: "hidden",
- width: 0,
- height: 0,
- border: 0,
- margin: 0,
- background: "none"
- };
- if ( body ) {
- jQuery.extend( testElementStyle, {
- position: "absolute",
- left: "-1000px",
- top: "-1000px"
- });
- }
- for ( i in testElementStyle ) {
- testElement.style[ i ] = testElementStyle[ i ];
- }
- testElement.appendChild( div );
- testElementParent = body || documentElement;
- testElementParent.insertBefore( testElement, testElementParent.firstChild );
-
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
support.appendChecked = input.checked;
- support.boxModel = div.offsetWidth === 2;
-
- if ( "zoom" in div.style ) {
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- // (IE < 8 does this)
- div.style.display = "inline";
- div.style.zoom = 1;
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
-
- // Check if elements with layout shrink-wrap their children
- // (IE 6 does this)
- div.style.display = "";
- div.innerHTML = "<div style='width:4px;'></div>";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
- }
-
- div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
- tds = div.getElementsByTagName( "td" );
+ fragment.removeChild( input );
+ fragment.appendChild( div );
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- // (only IE 8 fails this test)
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
-
- // Check if empty table cells still have offsetWidth/Height
- // (IE < 8 fail this test)
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
div.innerHTML = "";
// Check if div with explicit width and no margin-right incorrectly
@@ -1366,21 +1477,18 @@ jQuery.support = (function() {
// info see bug #3333
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ if ( window.getComputedStyle ) {
marginDiv = document.createElement( "div" );
marginDiv.style.width = "0";
marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
div.appendChild( marginDiv );
support.reliableMarginRight =
- ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
}
- // Remove the body element we added
- testElement.innerHTML = "";
- testElementParent.removeChild( testElement );
-
// Technique from Juriy Zaytsev
- // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
@@ -1390,7 +1498,7 @@ jQuery.support = (function() {
submit: 1,
change: 1,
focusin: 1
- } ) {
+ }) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
@@ -1401,15 +1509,111 @@ jQuery.support = (function() {
}
}
- // Null connected elements to avoid leaks in IE
- testElement = fragment = select = opt = body = marginDiv = div = input = null;
+ fragment.removeChild( div );
+
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = marginDiv = div = input = null;
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, outer, inner, table, td, offsetSupport,
+ conMarginTop, ptlm, vb, style, html,
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ conMarginTop = 1;
+ ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";
+ vb = "visibility:hidden;border:0;";
+ style = "style='" + ptlm + "border:5px solid #000;padding:0;'";
+ html = "<div " + style + "><div></div></div>" +
+ "<table " + style + " cellpadding='0' cellspacing='0'>" +
+ "<tr><td></td></tr></table>";
+
+ container = document.createElement("div");
+ container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Figure out if the W3C box model works as expected
+ div.innerHTML = "";
+ div.style.width = div.style.paddingLeft = "1px";
+ jQuery.boxModel = support.boxModel = div.offsetWidth === 2;
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.style.cssText = ptlm + vb;
+ div.innerHTML = html;
+
+ outer = div.firstChild;
+ inner = outer.firstChild;
+ td = outer.nextSibling.firstChild.firstChild;
+
+ offsetSupport = {
+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+ };
+
+ inner.style.position = "fixed";
+ inner.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+ inner.style.position = inner.style.top = "";
+
+ outer.style.overflow = "hidden";
+ outer.style.position = "relative";
+
+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+ body.removeChild( container );
+ div = container = null;
+
+ jQuery.extend( support, offsetSupport );
+ });
return support;
})();
-// Keep track of boxModel
-jQuery.boxModel = jQuery.support.boxModel;
-
@@ -1437,7 +1641,6 @@ jQuery.extend({
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
-
return !!elem && !isEmptyDataObject( elem );
},
@@ -1446,7 +1649,7 @@ jQuery.extend({
return;
}
- var thisCache, ret,
+ var privateCache, thisCache, ret,
internalKey = jQuery.expando,
getByName = typeof name === "string",
@@ -1460,11 +1663,12 @@ jQuery.extend({
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+ isEvents = name === "events";
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
- if ( (!id || (pvt && id && (cache[ id ] && !cache[ id ][ internalKey ]))) && getByName && data === undefined ) {
+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
@@ -1472,18 +1676,17 @@ jQuery.extend({
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
- elem[ jQuery.expando ] = id = ++jQuery.uuid;
+ elem[ internalKey ] = id = ++jQuery.uuid;
} else {
- id = jQuery.expando;
+ id = internalKey;
}
}
if ( !cache[ id ] ) {
cache[ id ] = {};
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
if ( !isNode ) {
cache[ id ].toJSON = jQuery.noop;
}
@@ -1493,34 +1696,33 @@ jQuery.extend({
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
- cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
+ cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
- cache[ id ] = jQuery.extend(cache[ id ], name);
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
- thisCache = cache[ id ];
+ privateCache = thisCache = cache[ id ];
- // Internal jQuery data is stored in a separate object inside the object's data
+ // jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
- // data
- if ( pvt ) {
- if ( !thisCache[ internalKey ] ) {
- thisCache[ internalKey ] = {};
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
}
- thisCache = thisCache[ internalKey ];
+ thisCache = thisCache.data;
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
- // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
- // not attempt to inspect the internal events object using jQuery.data, as this
- // internal data object is undocumented and subject to change.
- if ( name === "events" && !thisCache[name] ) {
- return thisCache[ internalKey ] && thisCache[ internalKey ].events;
+ // Users should not attempt to inspect the internal events object using jQuery.data,
+ // it is undocumented and subject to change. But does anyone listen? No.
+ if ( isEvents && !thisCache[ name ] ) {
+ return privateCache.events;
}
// Check for both converted-to-camel and non-converted data property names
@@ -1548,7 +1750,7 @@ jQuery.extend({
return;
}
- var thisCache,
+ var thisCache, i, l,
// Reference to internal data cache key
internalKey = jQuery.expando,
@@ -1559,7 +1761,7 @@ jQuery.extend({
cache = isNode ? jQuery.cache : elem,
// See jQuery.data for more information
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+ id = isNode ? elem[ internalKey ] : internalKey;
// If there is already no cache entry for this object, there is no
// purpose in continuing
@@ -1569,28 +1771,43 @@ jQuery.extend({
if ( name ) {
- thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
if ( thisCache ) {
- // Support interoperable removal of hyphenated or camelcased keys
- if ( !thisCache[ name ] ) {
- name = jQuery.camelCase( name );
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
}
- delete thisCache[ name ];
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
- if ( !isEmptyDataObject(thisCache) ) {
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
}
}
}
// See jQuery.data for more information
- if ( pvt ) {
- delete cache[ id ][ internalKey ];
+ if ( !pvt ) {
+ delete cache[ id ].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
@@ -1599,8 +1816,6 @@ jQuery.extend({
}
}
- var internalCache = cache[ id ][ internalKey ];
-
// Browsers that fail expando deletion also refuse to delete expandos on
// the window, but it will allow it on all other JS objects; other browsers
// don't care
@@ -1611,32 +1826,18 @@ jQuery.extend({
cache[ id ] = null;
}
- // We destroyed the entire user cache at once because it's faster than
- // iterating through each key, but we need to continue to persist internal
- // data if it existed
- if ( internalCache ) {
- cache[ id ] = {};
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
-
- cache[ id ][ internalKey ] = internalCache;
-
- // Otherwise, we need to eliminate the expando on the node to avoid
+ // We destroyed the cache and need to eliminate the expando on the node to avoid
// false lookups in the cache for entries that no longer exist
- } else if ( isNode ) {
+ if ( isNode ) {
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
if ( jQuery.support.deleteExpando ) {
- delete elem[ jQuery.expando ];
+ delete elem[ internalKey ];
} else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
+ elem.removeAttribute( internalKey );
} else {
- elem[ jQuery.expando ] = null;
+ elem[ internalKey ] = null;
}
}
},
@@ -1662,14 +1863,15 @@ jQuery.extend({
jQuery.fn.extend({
data: function( key, value ) {
- var data = null;
+ var parts, attr, name,
+ data = null;
if ( typeof key === "undefined" ) {
if ( this.length ) {
data = jQuery.data( this[0] );
- if ( this[0].nodeType === 1 ) {
- var attr = this[0].attributes, name;
+ if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
+ attr = this[0].attributes;
for ( var i = 0, l = attr.length; i < l; i++ ) {
name = attr[i].name;
@@ -1679,6 +1881,7 @@ jQuery.fn.extend({
dataAttr( this[0], name, data[ name ] );
}
}
+ jQuery._data( this[0], "parsedAttrs", true );
}
}
@@ -1690,7 +1893,7 @@ jQuery.fn.extend({
});
}
- var parts = key.split(".");
+ parts = key.split(".");
parts[1] = parts[1] ? "." + parts[1] : "";
if ( value === undefined ) {
@@ -1708,12 +1911,12 @@ jQuery.fn.extend({
} else {
return this.each(function() {
- var $this = jQuery( this ),
+ var self = jQuery( this ),
args = [ parts[0], value ];
- $this.triggerHandler( "setData" + parts[1] + "!", args );
+ self.triggerHandler( "setData" + parts[1] + "!", args );
jQuery.data( this, key, value );
- $this.triggerHandler( "changeData" + parts[1] + "!", args );
+ self.triggerHandler( "changeData" + parts[1] + "!", args );
});
}
},
@@ -1739,7 +1942,7 @@ function dataAttr( elem, key, data ) {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
- !jQuery.isNaN( data ) ? parseFloat( data ) :
+ jQuery.isNumeric( data ) ? parseFloat( data ) :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
@@ -1755,11 +1958,14 @@ function dataAttr( elem, key, data ) {
return data;
}
-// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
-// property to be considered empty objects; this property always exists in
-// order to make sure JSON.stringify does not expose internal metadata
+// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
for ( var name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
if ( name !== "toJSON" ) {
return false;
}
@@ -1775,17 +1981,17 @@ function handleQueueMarkDefer( elem, type, src ) {
var deferDataKey = type + "defer",
queueDataKey = type + "queue",
markDataKey = type + "mark",
- defer = jQuery.data( elem, deferDataKey, undefined, true );
+ defer = jQuery._data( elem, deferDataKey );
if ( defer &&
- ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
- ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
// Give room for hard-coded callbacks to fire first
// and eventually mark/queue something else on the element
setTimeout( function() {
- if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
- !jQuery.data( elem, markDataKey, undefined, true ) ) {
+ if ( !jQuery._data( elem, queueDataKey ) &&
+ !jQuery._data( elem, markDataKey ) ) {
jQuery.removeData( elem, deferDataKey, true );
- defer.resolve();
+ defer.fire();
}
}, 0 );
}
@@ -1795,8 +2001,8 @@ jQuery.extend({
_mark: function( elem, type ) {
if ( elem ) {
- type = (type || "fx") + "mark";
- jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
+ type = ( type || "fx" ) + "mark";
+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
}
},
@@ -1809,9 +2015,9 @@ jQuery.extend({
if ( elem ) {
type = type || "fx";
var key = type + "mark",
- count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
if ( count ) {
- jQuery.data( elem, key, count, true );
+ jQuery._data( elem, key, count );
} else {
jQuery.removeData( elem, key, true );
handleQueueMarkDefer( elem, type, "mark" );
@@ -1820,13 +2026,15 @@ jQuery.extend({
},
queue: function( elem, type, data ) {
+ var q;
if ( elem ) {
- type = (type || "fx") + "queue";
- var q = jQuery.data( elem, type, undefined, true );
+ type = ( type || "fx" ) + "queue";
+ q = jQuery._data( elem, type );
+
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
- q = jQuery.data( elem, type, jQuery.makeArray(data), true );
+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
@@ -1840,7 +2048,7 @@ jQuery.extend({
var queue = jQuery.queue( elem, type ),
fn = queue.shift(),
- defer;
+ hooks = {};
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
@@ -1851,16 +2059,17 @@ jQuery.extend({
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
- queue.unshift("inprogress");
+ queue.unshift( "inprogress" );
}
- fn.call(elem, function() {
- jQuery.dequeue(elem, type);
- });
+ jQuery._data( elem, type + ".run", hooks );
+ fn.call( elem, function() {
+ jQuery.dequeue( elem, type );
+ }, hooks );
}
if ( !queue.length ) {
- jQuery.removeData( elem, type + "queue", true );
+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
handleQueueMarkDefer( elem, type, "queue" );
}
}
@@ -1892,14 +2101,14 @@ jQuery.fn.extend({
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
- time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
- return this.queue( type, function() {
- var elem = this;
- setTimeout(function() {
- jQuery.dequeue( elem, type );
- }, time );
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
});
},
clearQueue: function( type ) {
@@ -1930,9 +2139,9 @@ jQuery.fn.extend({
if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
- jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
count++;
- tmp.done( resolve );
+ tmp.add( resolve );
}
}
resolve();
@@ -1950,7 +2159,8 @@ var rclass = /[\n\t\r]/g,
rfocusable = /^(?:button|input|object|select|textarea)$/i,
rclickable = /^a(?:rea)?$/i,
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- nodeHook, boolHook;
+ getSetAttribute = jQuery.support.getSetAttribute,
+ nodeHook, boolHook, fixSpecified;
jQuery.fn.extend({
attr: function( name, value ) {
@@ -1962,11 +2172,11 @@ jQuery.fn.extend({
jQuery.removeAttr( this, name );
});
},
-
+
prop: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.prop );
},
-
+
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
@@ -2025,7 +2235,7 @@ jQuery.fn.extend({
}
if ( (value && typeof value === "string") || value === undefined ) {
- classNames = (value || "").split( rspace );
+ classNames = ( value || "" ).split( rspace );
for ( i = 0, l = this.length; i < l; i++ ) {
elem = this[ i ];
@@ -2086,8 +2296,10 @@ jQuery.fn.extend({
},
hasClass: function( selector ) {
- var className = " " + selector + " ";
- for ( var i = 0, l = this.length; i < l; i++ ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
return true;
}
@@ -2097,9 +2309,9 @@ jQuery.fn.extend({
},
val: function( value ) {
- var hooks, ret,
+ var hooks, ret, isFunction,
elem = this[0];
-
+
if ( !arguments.length ) {
if ( elem ) {
hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
@@ -2110,17 +2322,17 @@ jQuery.fn.extend({
ret = elem.value;
- return typeof ret === "string" ?
+ return typeof ret === "string" ?
// handle most common string cases
- ret.replace(rreturn, "") :
+ ret.replace(rreturn, "") :
// handle cases where value is null/undef or number
ret == null ? "" : ret;
}
- return undefined;
+ return;
}
- var isFunction = jQuery.isFunction( value );
+ isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
var self = jQuery(this), val;
@@ -2168,7 +2380,7 @@ jQuery.extend({
},
select: {
get: function( elem ) {
- var value,
+ var value, i, max, option,
index = elem.selectedIndex,
values = [],
options = elem.options,
@@ -2180,8 +2392,10 @@ jQuery.extend({
}
// Loop through all the selected options
- for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
- var option = options[ i ];
+ i = one ? index : 0;
+ max = one ? index + 1 : options.length;
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
// Don't return options that are disabled or in a disabled optgroup
if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
@@ -2233,18 +2447,14 @@ jQuery.extend({
height: true,
offset: true
},
-
- attrFix: {
- // Always normalize to ensure hook usage
- tabindex: "tabIndex"
- },
-
+
attr: function( elem, name, value, pass ) {
- var nType = elem.nodeType;
-
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
+ return;
}
if ( pass && name in jQuery.attrFn ) {
@@ -2252,36 +2462,24 @@ jQuery.extend({
}
// Fallback to prop when attributes are not supported
- if ( !("getAttribute" in elem) ) {
+ if ( typeof elem.getAttribute === "undefined" ) {
return jQuery.prop( elem, name, value );
}
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
- // Normalize the name if needed
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
if ( notxml ) {
- name = jQuery.attrFix[ name ] || name;
-
- hooks = jQuery.attrHooks[ name ];
-
- if ( !hooks ) {
- // Use boolHook for boolean attributes
- if ( rboolean.test( name ) ) {
- hooks = boolHook;
-
- // Use nodeHook if available( IE6/7 )
- } else if ( nodeHook ) {
- hooks = nodeHook;
- }
- }
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
- return undefined;
+ return;
} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
@@ -2305,17 +2503,29 @@ jQuery.extend({
}
},
- removeAttr: function( elem, name ) {
- var propName;
- if ( elem.nodeType === 1 ) {
- name = jQuery.attrFix[ name ] || name;
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, l,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+ attrNames = value.toLowerCase().split( rspace );
+ l = attrNames.length;
+
+ for ( ; i < l; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
- jQuery.attr( elem, name, "" );
- elem.removeAttribute( name );
+ // See #9699 for explanation of this approach (setting first, then removal)
+ jQuery.attr( elem, name, "" );
+ elem.removeAttribute( getSetAttribute ? name : propName );
- // Set corresponding property to false for boolean attributes
- if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
- elem[ propName ] = false;
+ // Set corresponding property to false for boolean attributes
+ if ( rboolean.test( name ) && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
}
}
},
@@ -2374,17 +2584,17 @@ jQuery.extend({
frameborder: "frameBorder",
contenteditable: "contentEditable"
},
-
+
prop: function( elem, name, value ) {
- var nType = elem.nodeType;
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
+ return;
}
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
@@ -2397,7 +2607,7 @@ jQuery.extend({
return ret;
} else {
- return (elem[ name ] = value);
+ return ( elem[ name ] = value );
}
} else {
@@ -2409,7 +2619,7 @@ jQuery.extend({
}
}
},
-
+
propHooks: {
tabIndex: {
get: function( elem ) {
@@ -2427,16 +2637,17 @@ jQuery.extend({
}
});
-// Add the tabindex propHook to attrHooks for back-compat
-jQuery.attrHooks.tabIndex = jQuery.propHooks.tabIndex;
+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
// Fall back to attribute presence where some booleans are not supported
- var attrNode;
- return jQuery.prop( elem, name ) === true || ( attrNode = elem.getAttributeNode( name ) ) && attrNode.nodeValue !== false ?
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
name.toLowerCase() :
undefined;
},
@@ -2461,16 +2672,20 @@ boolHook = {
};
// IE6/7 do not support getting/setting some attributes with get/setAttribute
-if ( !jQuery.support.getSetAttribute ) {
-
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true
+ };
+
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = jQuery.valHooks.button = {
get: function( elem, name ) {
var ret;
ret = elem.getAttributeNode( name );
- // Return undefined if nodeValue is empty string
- return ret && ret.nodeValue !== "" ?
+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
ret.nodeValue :
undefined;
},
@@ -2481,10 +2696,13 @@ if ( !jQuery.support.getSetAttribute ) {
ret = document.createAttribute( name );
elem.setAttributeNode( ret );
}
- return (ret.nodeValue = value + "");
+ return ( ret.nodeValue = value + "" );
}
};
+ // Apply the nodeHook to tabindex
+ jQuery.attrHooks.tabindex.set = nodeHook.set;
+
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
@@ -2497,6 +2715,18 @@ if ( !jQuery.support.getSetAttribute ) {
}
});
});
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
}
@@ -2520,7 +2750,7 @@ if ( !jQuery.support.style ) {
return elem.style.cssText.toLowerCase() || undefined;
},
set: function( elem, value ) {
- return (elem.style.cssText = "" + value);
+ return ( elem.style.cssText = "" + value );
}
};
}
@@ -2545,6 +2775,11 @@ if ( !jQuery.support.optSelected ) {
});
}
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
// Radios and checkboxes getter/setter
if ( !jQuery.support.checkOn ) {
jQuery.each([ "radio", "checkbox" ], function() {
@@ -2560,7 +2795,7 @@ jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
- return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
}
}
});
@@ -2569,116 +2804,118 @@ jQuery.each([ "radio", "checkbox" ], function() {
-var rnamespaces = /\.(.*)$/,
- rformElems = /^(?:textarea|input|select)$/i,
- rperiod = /\./g,
- rspaces = / /g,
- rescape = /[^\w\s.|`]/g,
- fcleanup = function( nm ) {
- return nm.replace(rescape, "\\$&");
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+ rhoverHack = /\bhover(\.\S+)?\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+ quickParse = function( selector ) {
+ var quick = rquickIs.exec( selector );
+ if ( quick ) {
+ // 0 1 2 3
+ // [ _, tag, id, class ]
+ quick[1] = ( quick[1] || "" ).toLowerCase();
+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
+ }
+ return quick;
+ },
+ quickIs = function( elem, m ) {
+ var attrs = elem.attributes || {};
+ return (
+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+ (!m[2] || (attrs.id || {}).value === m[2]) &&
+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+ );
+ },
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
};
/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code originated from
- * Dean Edwards' addEvent library.
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
*/
jQuery.event = {
- // Bind an event to an element
- // Original by Dean Edwards
- add: function( elem, types, handler, data ) {
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, quick, handlers, special;
- if ( handler === false ) {
- handler = returnFalse;
- } else if ( !handler ) {
- // Fixes bug #7229. Fix recommended by jdalton
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
return;
}
- var handleObjIn, handleObj;
-
+ // Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
}
- // Make sure that the function being executed has a unique ID
+ // Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
- // Init the element's event structure
- var elemData = jQuery._data( elem );
-
- // If no elemData is found then we must be trying to bind to one of the
- // banned noData elements
- if ( !elemData ) {
- return;
- }
-
- var events = elemData.events,
- eventHandle = elemData.handle;
-
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
if ( !events ) {
elemData.events = events = {};
}
-
+ eventHandle = elemData.handle;
if ( !eventHandle ) {
elemData.handle = eventHandle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
}
- // Add elem as a property of the handle function
- // This is to prevent a memory leak with non-native events in IE.
- eventHandle.elem = elem;
-
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
- types = types.split(" ");
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
- var type, i = 0, namespaces;
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
- while ( (type = types[ i++ ]) ) {
- handleObj = handleObjIn ?
- jQuery.extend({}, handleObjIn) :
- { handler: handler, data: data };
-
- // Namespaced event handlers
- if ( type.indexOf(".") > -1 ) {
- namespaces = type.split(".");
- type = namespaces.shift();
- handleObj.namespace = namespaces.slice(0).sort().join(".");
-
- } else {
- namespaces = [];
- handleObj.namespace = "";
- }
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
- handleObj.type = type;
- if ( !handleObj.guid ) {
- handleObj.guid = handler.guid;
- }
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
- // Get the current list of functions bound to this event
- var handlers = events[ type ],
- special = jQuery.event.special[ type ] || {};
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
- // Init the event handler queue
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ quick: quickParse( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
if ( !handlers ) {
handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
- // Check for a special event handler
- // Only use addEventListener/attachEvent if the special
- // events handler returns false
+ // Only use addEventListener/attachEvent if the special events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
@@ -2698,10 +2935,14 @@ jQuery.event = {
}
}
- // Add the function to the element's handler list
- handlers.push( handleObj );
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
- // Keep track of which events have been used, for event optimization
+ // Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
}
@@ -2712,129 +2953,80 @@ jQuery.event = {
global: {},
// Detach an event or set of events from an element
- remove: function( elem, types, handler, pos ) {
- // don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- if ( handler === false ) {
- handler = returnFalse;
- }
+ remove: function( elem, types, handler, selector, mappedTypes ) {
- var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
- events = elemData && elemData.events;
+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ t, tns, type, origType, namespaces, origCount,
+ j, events, special, handle, eventType, handleObj;
- if ( !elemData || !events ) {
+ if ( !elemData || !(events = elemData.events) ) {
return;
}
- // types is actually an event object here
- if ( types && types.type ) {
- handler = types.handler;
- types = types.type;
- }
-
- // Unbind all events for the element
- if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
- types = types || "";
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
- for ( type in events ) {
- jQuery.event.remove( elem, type + types );
- }
-
- return;
- }
-
- // Handle multiple events separated by a space
- // jQuery(...).unbind("mouseover mouseout", fn);
- types = types.split(" ");
-
- while ( (type = types[ i++ ]) ) {
- origType = type;
- handleObj = null;
- all = type.indexOf(".") < 0;
- namespaces = [];
-
- if ( !all ) {
- // Namespaced event handlers
- namespaces = type.split(".");
- type = namespaces.shift();
-
- namespace = new RegExp("(^|\\.)" +
- jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- eventType = events[ type ];
-
- if ( !eventType ) {
- continue;
- }
-
- if ( !handler ) {
- for ( j = 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( all || namespace.test( handleObj.namespace ) ) {
- jQuery.event.remove( elem, origType, handleObj.handler, j );
- eventType.splice( j--, 1 );
- }
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
}
-
continue;
}
special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
- for ( j = pos || 0; j < eventType.length; j++ ) {
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
- if ( handler.guid === handleObj.guid ) {
- // remove the given handler for the given type
- if ( all || namespace.test( handleObj.namespace ) ) {
- if ( pos == null ) {
- eventType.splice( j--, 1 );
- }
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
}
-
- if ( pos != null ) {
- break;
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
}
}
}
- // remove generic event handler if no more handlers exist
- if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
- ret = null;
delete events[ type ];
}
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
- var handle = elemData.handle;
+ handle = elemData.handle;
if ( handle ) {
handle.elem = null;
}
- delete elemData.events;
- delete elemData.handle;
-
- if ( jQuery.isEmptyObject( elemData ) ) {
- jQuery.removeData( elem, undefined, true );
- }
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, [ "events", "handle" ], true );
}
},
-
+
// Events that are safe to short-circuit if no handlers are attached.
// Native DOM events should not be added, they may have inline handlers.
customEvent: {
@@ -2844,18 +3036,28 @@ jQuery.event = {
},
trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
// Event object or event type
var type = event.type || event,
namespaces = [],
- exclusive;
+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
- if ( type.indexOf("!") >= 0 ) {
+ if ( type.indexOf( "!" ) >= 0 ) {
// Exclusive events trigger only for the exact event (no namespaces)
type = type.slice(0, -1);
exclusive = true;
}
- if ( type.indexOf(".") >= 0 ) {
+ if ( type.indexOf( "." ) >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split(".");
type = namespaces.shift();
@@ -2877,230 +3079,299 @@ jQuery.event = {
new jQuery.Event( type );
event.type = type;
+ event.isTrigger = true;
event.exclusive = exclusive;
- event.namespace = namespaces.join(".");
- event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
-
- // triggerHandler() and global events don't bubble or run the default action
- if ( onlyHandlers || !elem ) {
- event.preventDefault();
- event.stopPropagation();
- }
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
// Handle a global trigger
if ( !elem ) {
+
// TODO: Stop taunting the data cache; remove global events and always attach to document
- jQuery.each( jQuery.cache, function() {
- // internalKey variable is just used to make it easier to find
- // and potentially change this stuff later; currently it just
- // points to jQuery.expando
- var internalKey = jQuery.expando,
- internalCache = this[ internalKey ];
- if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
- jQuery.event.trigger( event, data, internalCache.handle.elem );
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
}
- });
- return;
- }
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ }
return;
}
// Clean up the event in case it is being reused
event.result = undefined;
- event.target = elem;
+ if ( !event.target ) {
+ event.target = elem;
+ }
// Clone any incoming data and prepend the event, creating the handler arg list
data = data != null ? jQuery.makeArray( data ) : [];
data.unshift( event );
- var cur = elem,
- // IE doesn't like method names with a colon (#3533, #8272)
- ontype = type.indexOf(":") < 0 ? "on" + type : "";
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
- // Fire event on the current element, then bubble up the DOM tree
- do {
- var handle = jQuery._data( cur, "handle" );
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ old = null;
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
- event.currentTarget = cur;
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old && old === elem.ownerDocument ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
-
- // Trigger an inline bound script
- if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
- event.result = false;
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
-
- // Bubble up to document, then to window
- cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
- } while ( cur && !event.isPropagationStopped() );
+ }
+ event.type = type;
// If nobody prevented the default action, do it now
- if ( !event.isDefaultPrevented() ) {
- var old,
- special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
- if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
// Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction)() check here because IE6/7 fails that test.
- // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
- try {
- if ( ontype && elem[ type ] ) {
- // Don't re-trigger an onFOO event when we call its FOO() method
- old = elem[ ontype ];
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
- if ( old ) {
- elem[ ontype ] = null;
- }
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
- jQuery.event.triggered = type;
- elem[ type ]();
+ if ( old ) {
+ elem[ ontype ] = null;
}
- } catch ( ieError ) {}
- if ( old ) {
- elem[ ontype ] = old;
- }
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
- jQuery.event.triggered = undefined;
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
}
}
-
+
return event.result;
},
- handle: function( event ) {
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event || window.event );
- // Snapshot the handlers list since a called handler may add/remove events.
- var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
+
+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = [].slice.call( arguments, 0 ),
run_all = !event.exclusive && !event.namespace,
- args = Array.prototype.slice.call( arguments, 0 );
+ handlerQueue = [],
+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
- // Use the fix-ed Event rather than the (read-only) native event
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
- event.currentTarget = this;
-
- for ( var j = 0, l = handlers.length; j < l; j++ ) {
- var handleObj = handlers[ j ];
-
- // Triggered event must 1) be non-exclusive and have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event.
- if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
- // Pass in a reference to the handler function itself
- // So that we can later remove it
- event.handler = handleObj.handler;
- event.data = handleObj.data;
- event.handleObj = handleObj;
-
- var ret = handleObj.handler.apply( this, args );
-
- if ( ret !== undefined ) {
- event.result = ret;
- if ( ret === false ) {
- event.preventDefault();
- event.stopPropagation();
+ event.delegateTarget = this;
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) {
+
+ // Pregenerate a single jQuery object for reuse with .is()
+ jqcur = jQuery(this);
+ jqcur.context = this.ownerDocument || this;
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+ selMatch = {};
+ matches = [];
+ jqcur[0] = cur;
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = (
+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+ );
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
}
}
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
- if ( event.isImmediatePropagationStopped() ) {
- break;
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
}
}
}
+
return event.result;
},
- props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
- // store a copy of the original event object
- // and "clone" to set read-only properties
- var originalEvent = event;
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
event = jQuery.Event( originalEvent );
- for ( var i = this.props.length, prop; i; ) {
- prop = this.props[ --i ];
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
event[ prop ] = originalEvent[ prop ];
}
- // Fix target property, if necessary
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
if ( !event.target ) {
- // Fixes #1925 where srcElement might not be defined either
- event.target = event.srcElement || document;
+ event.target = originalEvent.srcElement || document;
}
- // check if target is a textnode (safari)
+ // Target should not be a text node (#504, Safari)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
- // Add relatedTarget, if necessary
- if ( !event.relatedTarget && event.fromElement ) {
- event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
- }
-
- // Calculate pageX/Y if missing and clientX/Y available
- if ( event.pageX == null && event.clientX != null ) {
- var eventDocument = event.target.ownerDocument || document,
- doc = eventDocument.documentElement,
- body = eventDocument.body;
-
- event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
- event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
- }
-
- // Add which for key events
- if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
- event.which = event.charCode != null ? event.charCode : event.keyCode;
- }
-
- // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
- if ( !event.metaKey && event.ctrlKey ) {
+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+ if ( event.metaKey === undefined ) {
event.metaKey = event.ctrlKey;
}
- // Add which for click: 1 === left; 2 === middle; 3 === right
- // Note: button is not normalized, so don't use it
- if ( !event.which && event.button !== undefined ) {
- event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
- }
-
- return event;
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
},
- // Deprecated, use jQuery.guid instead
- guid: 1E8,
-
- // Deprecated, use jQuery.proxy instead
- proxy: jQuery.proxy,
-
special: {
ready: {
// Make sure the ready event is setup
- setup: jQuery.bindReady,
- teardown: jQuery.noop
+ setup: jQuery.bindReady
},
- live: {
- add: function( handleObj ) {
- jQuery.event.add( this,
- liveConvert( handleObj.origType, handleObj.selector ),
- jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
- },
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
- remove: function( handleObj ) {
- jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
- }
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
},
beforeunload: {
@@ -3117,9 +3388,35 @@ jQuery.event = {
}
}
}
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
}
};
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
@@ -3134,7 +3431,7 @@ jQuery.removeEvent = document.removeEventListener ?
jQuery.Event = function( src, props ) {
// Allow instantiation without the 'new' keyword
- if ( !this.preventDefault ) {
+ if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
@@ -3145,8 +3442,8 @@ jQuery.Event = function( src, props ) {
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
- src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
// Event type
} else {
@@ -3158,9 +3455,8 @@ jQuery.Event = function( src, props ) {
jQuery.extend( this, props );
}
- // timeStamp is buggy for some events on Firefox(#3843)
- // So we won't rely on the native value
- this.timeStamp = jQuery.now();
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
// Mark it as fixed
this[ jQuery.expando ] = true;
@@ -3216,216 +3512,130 @@ jQuery.Event.prototype = {
isImmediatePropagationStopped: returnFalse
};
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function( event ) {
-
- // Check if mouse(over|out) are still within the same parent element
- var related = event.relatedTarget,
- inside = false,
- eventType = event.type;
-
- event.type = event.data;
-
- if ( related !== this ) {
-
- if ( related ) {
- inside = jQuery.contains( this, related );
- }
-
- if ( !inside ) {
-
- jQuery.event.handle.apply( this, arguments );
-
- event.type = eventType;
- }
- }
-},
-
-// In case of event delegation, we only need to rename the event.type,
-// liveHandler will take care of the rest.
-delegate = function( event ) {
- event.type = event.data;
- jQuery.event.handle.apply( this, arguments );
-};
-
-// Create mouseenter and mouseleave events
+// Create mouseenter/leave events using mouseover/out and event-time checks
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
- setup: function( data ) {
- jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
- },
- teardown: function( data ) {
- jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector,
+ ret;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
}
};
});
-// submit delegation
+// IE submit delegation
if ( !jQuery.support.submitBubbles ) {
jQuery.event.special.submit = {
- setup: function( data, namespaces ) {
- if ( !jQuery.nodeName( this, "form" ) ) {
- jQuery.event.add(this, "click.specialSubmit", function( e ) {
- // Avoid triggering error on non-existent type attribute in IE VML (#7071)
- var elem = e.target,
- type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : "";
-
- if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
- trigger( "submit", this, arguments );
- }
- });
-
- jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
- var elem = e.target,
- type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : "";
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
- if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
- trigger( "submit", this, arguments );
- }
- });
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !form._submit_attached ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ });
+ form._submit_attached = true;
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
- } else {
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
- },
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialSubmit" );
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
}
};
-
}
-// change delegation, happens here so we have bind.
+// IE change delegation and checkbox/radio fix
if ( !jQuery.support.changeBubbles ) {
- var changeFilters,
-
- getVal = function( elem ) {
- var type = jQuery.nodeName( elem, "input" ) ? elem.type : "",
- val = elem.value;
-
- if ( type === "radio" || type === "checkbox" ) {
- val = elem.checked;
-
- } else if ( type === "select-multiple" ) {
- val = elem.selectedIndex > -1 ?
- jQuery.map( elem.options, function( elem ) {
- return elem.selected;
- }).join("-") :
- "";
-
- } else if ( jQuery.nodeName( elem, "select" ) ) {
- val = elem.selectedIndex;
- }
-
- return val;
- },
-
- testChange = function testChange( e ) {
- var elem = e.target, data, val;
-
- if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
- return;
- }
-
- data = jQuery._data( elem, "_change_data" );
- val = getVal(elem);
-
- // the current data will be also retrieved by beforeactivate
- if ( e.type !== "focusout" || elem.type !== "radio" ) {
- jQuery._data( elem, "_change_data", val );
- }
-
- if ( data === undefined || val === data ) {
- return;
- }
-
- if ( data != null || val ) {
- e.type = "change";
- e.liveFired = undefined;
- jQuery.event.trigger( e, arguments[1], elem );
- }
- };
-
jQuery.event.special.change = {
- filters: {
- focusout: testChange,
- beforedeactivate: testChange,
+ setup: function() {
- click: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
-
- if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
- testChange.call( this, e );
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ jQuery.event.simulate( "change", this, event, true );
+ }
+ });
}
- },
-
- // Change has to be called before submit
- // Keydown will be called before keypress, which is used in submit-event delegation
- keydown: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
- if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
- (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
- type === "select-multiple" ) {
- testChange.call( this, e );
+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ elem._change_attached = true;
}
- },
-
- // Beforeactivate happens also before the previous element is blurred
- // with this event you can't trigger a change event, but you can store
- // information
- beforeactivate: function( e ) {
- var elem = e.target;
- jQuery._data( elem, "_change_data", getVal(elem) );
- }
+ });
},
- setup: function( data, namespaces ) {
- if ( this.type === "file" ) {
- return false;
- }
+ handle: function( event ) {
+ var elem = event.target;
- for ( var type in changeFilters ) {
- jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
}
-
- return rformElems.test( this.nodeName );
},
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialChange" );
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
return rformElems.test( this.nodeName );
}
};
-
- changeFilters = jQuery.event.special.change.filters;
-
- // Handle when the input is .focus()'d
- changeFilters.focus = changeFilters.beforeactivate;
-}
-
-function trigger( type, elem, args ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
- // Don't pass args or remember liveFired; they apply to the donor event.
- var event = jQuery.extend( {}, args[ 0 ] );
- event.type = type;
- event.originalEvent = {};
- event.liveFired = undefined;
- jQuery.event.handle.call( elem, event );
- if ( event.isDefaultPrevented() ) {
- args[ 0 ].preventDefault();
- }
}
// Create "bubbling" focus and blur events
@@ -3433,7 +3643,10 @@ if ( !jQuery.support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
// Attach a single capturing handler while someone wants focusin/focusout
- var attaches = 0;
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
jQuery.event.special[ fix ] = {
setup: function() {
@@ -3447,89 +3660,120 @@ if ( !jQuery.support.focusinBubbles ) {
}
}
};
-
- function handler( donor ) {
- // Donor event is always a native one; fix it and switch its type.
- // Let focusin/out handler cancel the donor focus/blur event.
- var e = jQuery.event.fix( donor );
- e.type = fix;
- e.originalEvent = {};
- jQuery.event.trigger( e, null, e.target );
- if ( e.isDefaultPrevented() ) {
- donor.preventDefault();
- }
- }
});
}
-jQuery.each(["bind", "one"], function( i, name ) {
- jQuery.fn[ name ] = function( type, data, fn ) {
- var handler;
+jQuery.fn.extend({
- // Handle object literals
- if ( typeof type === "object" ) {
- for ( var key in type ) {
- this[ name ](key, data, type[key], fn);
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+ // ( types-Object, data )
+ data = selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
}
return this;
}
- if ( arguments.length === 2 || data === false ) {
- fn = data;
- data = undefined;
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
}
- if ( name === "one" ) {
- handler = function( event ) {
- jQuery( this ).unbind( event, handler );
- return fn.apply( this, arguments );
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
};
- handler.guid = fn.guid || jQuery.guid++;
- } else {
- handler = fn;
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
-
- if ( type === "unload" && name !== "one" ) {
- this.one( type, data, fn );
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.add( this[i], type, handler, data );
- }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on.call( this, types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ var handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
}
-
- return this;
- };
-});
-
-jQuery.fn.extend({
- unbind: function( type, fn ) {
- // Handle object literals
- if ( typeof type === "object" && !type.preventDefault ) {
- for ( var key in type ) {
- this.unbind(key, type[key]);
- }
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.remove( this[i], type, fn );
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( var type in types ) {
+ this.off( type, selector, types[ type ] );
}
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
}
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
},
delegate: function( selector, types, data, fn ) {
- return this.live( types, data, fn, selector );
+ return this.on( types, selector, data, fn );
},
-
undelegate: function( selector, types, fn ) {
- if ( arguments.length === 0 ) {
- return this.unbind( "live" );
-
- } else {
- return this.die( types, null, fn, selector );
- }
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
},
trigger: function( type, data ) {
@@ -3537,7 +3781,6 @@ jQuery.fn.extend({
jQuery.event.trigger( type, data, this );
});
},
-
triggerHandler: function( type, data ) {
if ( this[0] ) {
return jQuery.event.trigger( type, data, this[0], true );
@@ -3551,8 +3794,8 @@ jQuery.fn.extend({
i = 0,
toggler = function( event ) {
// Figure out which function to execute
- var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
- jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
// Make sure that clicks stop
event.preventDefault();
@@ -3575,178 +3818,9 @@ jQuery.fn.extend({
}
});
-var liveMap = {
- focus: "focusin",
- blur: "focusout",
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-};
-
-jQuery.each(["live", "die"], function( i, name ) {
- jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
- var type, i = 0, match, namespaces, preType,
- selector = origSelector || this.selector,
- context = origSelector ? this : jQuery( this.context );
-
- if ( typeof types === "object" && !types.preventDefault ) {
- for ( var key in types ) {
- context[ name ]( key, data, types[key], selector );
- }
-
- return this;
- }
-
- if ( name === "die" && !types &&
- origSelector && origSelector.charAt(0) === "." ) {
-
- context.unbind( origSelector );
-
- return this;
- }
-
- if ( data === false || jQuery.isFunction( data ) ) {
- fn = data || returnFalse;
- data = undefined;
- }
-
- types = (types || "").split(" ");
-
- while ( (type = types[ i++ ]) != null ) {
- match = rnamespaces.exec( type );
- namespaces = "";
-
- if ( match ) {
- namespaces = match[0];
- type = type.replace( rnamespaces, "" );
- }
-
- if ( type === "hover" ) {
- types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
- continue;
- }
-
- preType = type;
-
- if ( liveMap[ type ] ) {
- types.push( liveMap[ type ] + namespaces );
- type = type + namespaces;
-
- } else {
- type = (liveMap[ type ] || type) + namespaces;
- }
-
- if ( name === "live" ) {
- // bind live handler
- for ( var j = 0, l = context.length; j < l; j++ ) {
- jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
- { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
- }
-
- } else {
- // unbind live handler
- context.unbind( "live." + liveConvert( type, selector ), fn );
- }
- }
-
- return this;
- };
-});
-
-function liveHandler( event ) {
- var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
- elems = [],
- selectors = [],
- events = jQuery._data( this, "events" );
-
- // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
- if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
- return;
- }
-
- if ( event.namespace ) {
- namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- event.liveFired = this;
-
- var live = events.live.slice(0);
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
- selectors.push( handleObj.selector );
-
- } else {
- live.splice( j--, 1 );
- }
- }
-
- match = jQuery( event.target ).closest( selectors, event.currentTarget );
-
- for ( i = 0, l = match.length; i < l; i++ ) {
- close = match[i];
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
- elem = close.elem;
- related = null;
-
- // Those two events require additional checking
- if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
- event.type = handleObj.preType;
- related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
-
- // Make sure not to accidentally match a child element with the same selector
- if ( related && jQuery.contains( elem, related ) ) {
- related = elem;
- }
- }
-
- if ( !related || related !== elem ) {
- elems.push({ elem: elem, handleObj: handleObj, level: close.level });
- }
- }
- }
- }
-
- for ( i = 0, l = elems.length; i < l; i++ ) {
- match = elems[i];
-
- if ( maxLevel && match.level > maxLevel ) {
- break;
- }
-
- event.currentTarget = match.elem;
- event.data = match.handleObj.data;
- event.handleObj = match.handleObj;
-
- ret = match.handleObj.origHandler.apply( match.elem, arguments );
-
- if ( ret === false || event.isPropagationStopped() ) {
- maxLevel = match.level;
-
- if ( ret === false ) {
- stop = false;
- }
- if ( event.isImmediatePropagationStopped() ) {
- break;
- }
- }
- }
-
- return stop;
-}
-
-function liveConvert( type, selector ) {
- return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
-}
-
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
- "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
@@ -3756,13 +3830,21 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
}
return arguments.length > 0 ?
- this.bind( name, data, fn ) :
+ this.on( name, null, data, fn ) :
this.trigger( name );
};
if ( jQuery.attrFn ) {
jQuery.attrFn[ name ] = true;
}
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
});
@@ -3776,11 +3858,13 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache" + (Math.random() + '').replace('.', ''),
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
baseHasDuplicate = true,
rBackslash = /\\/g,
+ rReturn = /\r\n/g,
rNonWord = /\W/;
// Here we check if the JavaScript engine is using some sort of
@@ -3832,7 +3916,7 @@ var Sizzle = function( selector, context, results, seed ) {
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- set = posProcess( parts[0] + parts[1], context );
+ set = posProcess( parts[0] + parts[1], context, seed );
} else {
set = Expr.relative[ parts[0] ] ?
@@ -3846,7 +3930,7 @@ var Sizzle = function( selector, context, results, seed ) {
selector += parts.shift();
}
- set = posProcess( selector, set );
+ set = posProcess( selector, set, seed );
}
}
@@ -3965,18 +4049,17 @@ Sizzle.matchesSelector = function( node, expr ) {
};
Sizzle.find = function( expr, context, isXML ) {
- var set;
+ var set, i, len, match, type, left;
if ( !expr ) {
return [];
}
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var match,
- type = Expr.order[i];
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
- var left = match[1];
+ left = match[1];
match.splice( 1, 1 );
if ( left.substr( left.length - 1 ) !== "\\" ) {
@@ -4002,17 +4085,18 @@ Sizzle.find = function( expr, context, isXML ) {
Sizzle.filter = function( expr, set, inplace, not ) {
var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
old = expr,
result = [],
curLoop = set,
isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
+ for ( type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- var found, item,
- filter = Expr.filter[ type ],
- left = match[1];
+ filter = Expr.filter[ type ];
+ left = match[1];
anyFound = false;
@@ -4038,10 +4122,10 @@ Sizzle.filter = function( expr, set, inplace, not ) {
}
if ( match ) {
- for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
- var pass = not ^ !!found;
+ pass = not ^ found;
if ( inplace && found != null ) {
if ( pass ) {
@@ -4092,7 +4176,46 @@ Sizzle.filter = function( expr, set, inplace, not ) {
};
Sizzle.error = function( msg ) {
- throw "Syntax error, unrecognized expression: " + msg;
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
};
var Expr = Sizzle.selectors = {
@@ -4482,7 +4605,7 @@ var Expr = Sizzle.selectors = {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
@@ -4501,7 +4624,10 @@ var Expr = Sizzle.selectors = {
},
CHILD: function( elem, match ) {
- var type = match[1],
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ type = match[1],
node = elem;
switch ( type ) {
@@ -4529,18 +4655,18 @@ var Expr = Sizzle.selectors = {
return true;
case "nth":
- var first = match[2],
- last = match[3];
+ first = match[2];
+ last = match[3];
if ( first === 1 && last === 0 ) {
return true;
}
- var doneName = match[0],
- parent = elem.parentNode;
+ doneName = match[0];
+ parent = elem.parentNode;
- if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
- var count = 0;
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
@@ -4548,10 +4674,10 @@ var Expr = Sizzle.selectors = {
}
}
- parent.sizcache = doneName;
+ parent[ expando ] = doneName;
}
- var diff = elem.nodeIndex - last;
+ diff = elem.nodeIndex - last;
if ( first === 0 ) {
return diff === 0;
@@ -4567,7 +4693,7 @@ var Expr = Sizzle.selectors = {
},
TAG: function( elem, match ) {
- return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
},
CLASS: function( elem, match ) {
@@ -4577,7 +4703,9 @@ var Expr = Sizzle.selectors = {
ATTR: function( elem, match ) {
var name = match[1],
- result = Expr.attrHandle[ name ] ?
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
@@ -4588,6 +4716,8 @@ var Expr = Sizzle.selectors = {
return result == null ?
type === "!=" :
+ !type && Sizzle.attr ?
+ result != null :
type === "=" ?
value === check :
type === "*=" ?
@@ -4768,26 +4898,6 @@ if ( document.documentElement.compareDocumentPosition ) {
};
}
-// Utility function for retreiving the text value of an array of DOM nodes
-Sizzle.getText = function( elems ) {
- var ret = "", elem;
-
- for ( var i = 0; elems[i]; i++ ) {
- elem = elems[i];
-
- // Get the text from text nodes and CDATA nodes
- if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
- ret += elem.nodeValue;
-
- // Traverse everything else, except comment nodes
- } else if ( elem.nodeType !== 8 ) {
- ret += Sizzle.getText( elem.childNodes );
- }
- }
-
- return ret;
-};
-
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
@@ -5065,13 +5175,13 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
elem = elem[dir];
while ( elem ) {
- if ( elem.sizcache === doneName ) {
+ if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 && !isXML ){
- elem.sizcache = doneName;
+ elem[ expando ] = doneName;
elem.sizset = i;
}
@@ -5098,14 +5208,14 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
elem = elem[dir];
while ( elem ) {
- if ( elem.sizcache === doneName ) {
+ if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 ) {
if ( !isXML ) {
- elem.sizcache = doneName;
+ elem[ expando ] = doneName;
elem.sizset = i;
}
@@ -5153,7 +5263,7 @@ Sizzle.isXML = function( elem ) {
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
-var posProcess = function( selector, context ) {
+var posProcess = function( selector, context, seed ) {
var match,
tmpSet = [],
later = "",
@@ -5169,13 +5279,16 @@ var posProcess = function( selector, context ) {
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet );
+ Sizzle( selector, root[i], tmpSet, seed );
}
return Sizzle.filter( later, tmpSet );
};
// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
@@ -5261,43 +5374,33 @@ jQuery.fn.extend({
},
is: function( selector ) {
- return !!selector && ( typeof selector === "string" ?
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ POS.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
},
closest: function( selectors, context ) {
var ret = [], i, l, cur = this[0];
- // Array
+ // Array (deprecated as of jQuery 1.7)
if ( jQuery.isArray( selectors ) ) {
- var match, selector,
- matches = {},
- level = 1;
-
- if ( cur && selectors.length ) {
- for ( i = 0, l = selectors.length; i < l; i++ ) {
- selector = selectors[i];
-
- if ( !matches[ selector ] ) {
- matches[ selector ] = POS.test( selector ) ?
- jQuery( selector, context || this.context ) :
- selector;
- }
- }
+ var level = 1;
- while ( cur && cur.ownerDocument && cur !== context ) {
- for ( selector in matches ) {
- match = matches[ selector ];
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( i = 0; i < selectors.length; i++ ) {
- if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
- ret.push({ selector: selector, elem: cur, level: level });
- }
+ if ( jQuery( cur ).is( selectors[ i ] ) ) {
+ ret.push({ selector: selectors[ i ], elem: cur, level: level });
}
-
- cur = cur.parentNode;
- level++;
}
+
+ cur = cur.parentNode;
+ level++;
}
return ret;
@@ -5414,12 +5517,7 @@ jQuery.each({
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until ),
- // The variable 'args' was introduced in
- // https://github.com/jquery/jquery/commit/52a0238
- // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
- // http://code.google.com/p/v8/issues/detail?id=1050
- args = slice.call(arguments);
+ var ret = jQuery.map( this, fn, until );
if ( !runtil.test( name ) ) {
selector = until;
@@ -5435,7 +5533,7 @@ jQuery.each({
ret = ret.reverse();
}
- return this.pushStack( ret, name, args.join(",") );
+ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
};
});
@@ -5504,7 +5602,7 @@ function winnow( elements, qualifier, keep ) {
} else if ( qualifier.nodeType ) {
return jQuery.grep(elements, function( elem, i ) {
- return (elem === qualifier) === keep;
+ return ( elem === qualifier ) === keep;
});
} else if ( typeof qualifier === "string" ) {
@@ -5520,20 +5618,38 @@ function winnow( elements, qualifier, keep ) {
}
return jQuery.grep(elements, function( elem, i ) {
- return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
});
}
-var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
rleadingWhitespace = /^\s+/,
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style)/i,
rnocache = /<(?:script|object|embed|option|style)/i,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")", "i"),
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /\/(java|ecma)script/i,
@@ -5547,7 +5663,8 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ]
- };
+ },
+ safeFragment = createSafeFragment( document );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
@@ -5625,8 +5742,10 @@ jQuery.fn.extend({
},
wrap: function( html ) {
- return this.each(function() {
- jQuery( this ).wrapAll( html );
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
});
},
@@ -5660,7 +5779,7 @@ jQuery.fn.extend({
this.parentNode.insertBefore( elem, this );
});
} else if ( arguments.length ) {
- var set = jQuery(arguments[0]);
+ var set = jQuery.clean( arguments );
set.push.apply( set, this.toArray() );
return this.pushStack( set, "before", arguments );
}
@@ -5673,7 +5792,7 @@ jQuery.fn.extend({
});
} else if ( arguments.length ) {
var set = this.pushStack( this, "after", arguments );
- set.push.apply( set, jQuery(arguments[0]).toArray() );
+ set.push.apply( set, jQuery.clean(arguments) );
return set;
}
},
@@ -5728,7 +5847,7 @@ jQuery.fn.extend({
null;
// See if we can take a shortcut and just use innerHTML
- } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
@@ -5854,7 +5973,7 @@ jQuery.fn.extend({
// in certain situations (Bug #8070).
// Fragments from the fragment cache must always be cloned and never used
// in place.
- results.cacheable || (l > 1 && i < lastIndex) ?
+ results.cacheable || ( l > 1 && i < lastIndex ) ?
jQuery.clone( fragment, true, true ) :
fragment
);
@@ -5883,27 +6002,26 @@ function cloneCopyEvent( src, dest ) {
return;
}
- var internalKey = jQuery.expando,
- oldData = jQuery.data( src ),
- curData = jQuery.data( dest, oldData );
-
- // Switch to use the internal data object, if it exists, for the next
- // stage of data copying
- if ( (oldData = oldData[ internalKey ]) ) {
- var events = oldData.events;
- curData = curData[ internalKey ] = jQuery.extend({}, oldData);
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
- if ( events ) {
- delete curData.handle;
- curData.events = {};
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
- for ( var type in events ) {
- for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
- jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
- }
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
}
}
}
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
}
function cloneFixAttributes( src, dest ) {
@@ -5965,16 +6083,17 @@ function cloneFixAttributes( src, dest ) {
}
jQuery.buildFragment = function( args, nodes, scripts ) {
- var fragment, cacheable, cacheresults, doc;
-
- // nodes may contain either an explicit document object,
- // a jQuery collection or context object.
- // If nodes[0] contains a valid object to assign to doc
- if ( nodes && nodes[0] ) {
- doc = nodes[0].ownerDocument || nodes[0];
- }
+ var fragment, cacheable, cacheresults, doc,
+ first = args[ 0 ];
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
- // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
if ( !doc.createDocumentFragment ) {
@@ -5985,12 +6104,15 @@ jQuery.buildFragment = function( args, nodes, scripts ) {
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
- if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
- args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
cacheable = true;
- cacheresults = jQuery.fragments[ args[0] ];
+ cacheresults = jQuery.fragments[ first ];
if ( cacheresults && cacheresults !== 1 ) {
fragment = cacheresults;
}
@@ -6002,7 +6124,7 @@ jQuery.buildFragment = function( args, nodes, scripts ) {
}
if ( cacheable ) {
- jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ jQuery.fragments[ first ] = cacheresults ? fragment : 1;
}
return { fragment: fragment, cacheable: cacheable };
@@ -6028,7 +6150,7 @@ jQuery.each({
} else {
for ( var i = 0, l = insert.length; i < l; i++ ) {
- var elems = (i > 0 ? this.clone(true) : this).get();
+ var elems = ( i > 0 ? this.clone(true) : this ).get();
jQuery( insert[i] )[ original ]( elems );
ret = ret.concat( elems );
}
@@ -6039,10 +6161,10 @@ jQuery.each({
});
function getAll( elem ) {
- if ( "getElementsByTagName" in elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
return elem.getElementsByTagName( "*" );
- } else if ( "querySelectorAll" in elem ) {
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
return elem.querySelectorAll( "*" );
} else {
@@ -6058,19 +6180,33 @@ function fixDefaultChecked( elem ) {
}
// Finds all inputs and passes them to fixDefaultChecked
function findInputs( elem ) {
- if ( jQuery.nodeName( elem, "input" ) ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "input" ) {
fixDefaultChecked( elem );
- } else if ( "getElementsByTagName" in elem ) {
+ // Skip scripts, get other children
+ } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
}
}
+// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
+function shimCloneNode( elem ) {
+ var div = document.createElement( "div" );
+ safeFragment.appendChild( div );
+
+ div.innerHTML = elem.outerHTML;
+ return div.firstChild;
+}
+
jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
- var clone = elem.cloneNode(true),
- srcElements,
- destElements,
- i;
+ var srcElements,
+ destElements,
+ i,
+ // IE<=8 does not properly clone detached, unknown element nodes
+ clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ?
+ elem.cloneNode( true ) :
+ shimCloneNode( elem );
if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
@@ -6082,8 +6218,7 @@ jQuery.extend({
cloneFixAttributes( elem, clone );
- // Using Sizzle here is crazy slow, so we use getElementsByTagName
- // instead
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
srcElements = getAll( elem );
destElements = getAll( clone );
@@ -6148,11 +6283,20 @@ jQuery.extend({
elem = elem.replace(rxhtmlTag, "<$1></$2>");
// Trim whitespace, otherwise indexOf won't work as expected
- var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div");
+ // Append wrapper element to unknown element safe doc fragment
+ if ( context === document ) {
+ // Use the fragment we've already created for this document
+ safeFragment.appendChild( div );
+ } else {
+ // Use a fragment created with the owner document
+ createSafeFragment( context ).appendChild( div );
+ }
+
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
@@ -6233,7 +6377,9 @@ jQuery.extend({
},
cleanData: function( elems ) {
- var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
+ var data, id,
+ cache = jQuery.cache,
+ special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
@@ -6244,7 +6390,7 @@ jQuery.extend({
id = elem[ jQuery.expando ];
if ( id ) {
- data = cache[ id ] && cache[ id ][ internalKey ];
+ data = cache[ id ];
if ( data && data.events ) {
for ( var type in data.events ) {
@@ -6506,7 +6652,7 @@ if ( !jQuery.support.opacity ) {
set: function( elem, value ) {
var style = elem.style,
currentStyle = elem.currentStyle,
- opacity = jQuery.isNaN( value ) ? "" : "alpha(opacity=" + value * 100 + ")",
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
filter = currentStyle && currentStyle.filter || style.filter || "";
// IE has trouble with opacity if it does not have layout
@@ -6563,11 +6709,8 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
name = name.replace( rupper, "-$1" ).toLowerCase();
- if ( !(defaultView = elem.ownerDocument.defaultView) ) {
- return undefined;
- }
-
- if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ if ( (defaultView = elem.ownerDocument.defaultView) &&
+ (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
ret = computedStyle.getPropertyValue( name );
if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
ret = jQuery.style( elem, name );
@@ -6580,25 +6723,32 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
if ( document.documentElement.currentStyle ) {
currentStyle = function( elem, name ) {
- var left,
+ var left, rsLeft, uncomputed,
ret = elem.currentStyle && elem.currentStyle[ name ],
- rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
style = elem.style;
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret === null && style && (uncomputed = style[ name ]) ) {
+ ret = uncomputed;
+ }
+
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+
// Remember the original values
left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
elem.runtimeStyle.left = elem.currentStyle.left;
}
- style.left = name === "fontSize" ? "1em" : (ret || 0);
+ style.left = name === "fontSize" ? "1em" : ( ret || 0 );
ret = style.pixelLeft + "px";
// Revert the changed values
@@ -6618,20 +6768,22 @@ function getWH( elem, name, extra ) {
// Start with offset property
var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
- which = name === "width" ? cssWidth : cssHeight;
+ which = name === "width" ? cssWidth : cssHeight,
+ i = 0,
+ len = which.length;
if ( val > 0 ) {
if ( extra !== "border" ) {
- jQuery.each( which, function() {
+ for ( ; i < len; i++ ) {
if ( !extra ) {
- val -= parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
+ val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
}
if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
+ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
} else {
- val -= parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
+ val -= parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
}
- });
+ }
}
return val + "px";
@@ -6647,15 +6799,15 @@ function getWH( elem, name, extra ) {
// Add padding, border, margin
if ( extra ) {
- jQuery.each( which, function() {
- val += parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
+ for ( ; i < len; i++ ) {
+ val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
if ( extra !== "padding" ) {
- val += parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
+ val += parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
}
if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
+ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
}
- });
+ }
}
return val + "px";
@@ -6666,7 +6818,7 @@ if ( jQuery.expr && jQuery.expr.filters ) {
var width = elem.offsetWidth,
height = elem.offsetHeight;
- return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
+ return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
@@ -6720,7 +6872,7 @@ var r20 = /%20/g,
// Document location segments
ajaxLocParts,
-
+
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
allTypes = ["*/"] + ["*"];
@@ -6759,7 +6911,7 @@ function addToPrefiltersOrTransports( structure ) {
placeBefore;
// For each dataType in the dataTypeExpression
- for(; i < length; i++ ) {
+ for ( ; i < length; i++ ) {
dataType = dataTypes[ i ];
// We control if we're asked to add before
// any existing element
@@ -6790,7 +6942,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX
executeOnly = ( structure === prefilters ),
selection;
- for(; i < length && ( executeOnly || !selection ); i++ ) {
+ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
selection = list[ i ]( options, originalOptions, jqXHR );
// If we got redirected to another dataType
// we try there if executing only and not done already
@@ -6821,7 +6973,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX
function ajaxExtend( target, src ) {
var key, deep,
flatOptions = jQuery.ajaxSettings.flatOptions || {};
- for( key in src ) {
+ for ( key in src ) {
if ( src[ key ] !== undefined ) {
( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
}
@@ -6938,7 +7090,7 @@ jQuery.fn.extend({
// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
jQuery.fn[ o ] = function( f ){
- return this.bind( o, f );
+ return this.on( o, f );
};
});
@@ -7080,7 +7232,7 @@ jQuery.extend({
jQuery( callbackContext ) : jQuery.event,
// Deferreds
deferred = jQuery.Deferred(),
- completeDeferred = jQuery._Deferred(),
+ completeDeferred = jQuery.Callbacks( "once memory" ),
// Status-dependent callbacks
statusCode = s.statusCode || {},
// ifModified key
@@ -7230,7 +7382,7 @@ jQuery.extend({
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
- if( !statusText || status ) {
+ if ( !statusText || status ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
@@ -7259,7 +7411,7 @@ jQuery.extend({
}
// Complete
- completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
@@ -7274,14 +7426,14 @@ jQuery.extend({
deferred.promise( jqXHR );
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
- jqXHR.complete = completeDeferred.done;
+ jqXHR.complete = completeDeferred.add;
// Status-dependent callbacks
jqXHR.statusCode = function( map ) {
if ( map ) {
var tmp;
if ( state < 2 ) {
- for( tmp in map ) {
+ for ( tmp in map ) {
statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
}
} else {
@@ -7358,7 +7510,7 @@ jQuery.extend({
ret = s.url.replace( rts, "$1_=" + ts );
// if nothing was replaced, add timestamp to the end
- s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
}
}
@@ -7432,7 +7584,7 @@ jQuery.extend({
done( -1, e );
// Simply rethrow otherwise
} else {
- jQuery.error( e );
+ throw e;
}
}
}
@@ -7536,7 +7688,7 @@ function ajaxHandleResponses( s, jqXHR, responses ) {
firstDataType;
// Fill responseXXX fields
- for( type in responseFields ) {
+ for ( type in responseFields ) {
if ( type in responses ) {
jqXHR[ responseFields[type] ] = responses[ type ];
}
@@ -7615,13 +7767,13 @@ function ajaxConvert( s, response ) {
conv2;
// For each dataType in the chain
- for( i = 1; i < length; i++ ) {
+ for ( i = 1; i < length; i++ ) {
// Create converters map
// with lowercased keys
if ( i === 1 ) {
- for( key in s.converters ) {
- if( typeof key === "string" ) {
+ for ( key in s.converters ) {
+ if ( typeof key === "string" ) {
converters[ key.toLowerCase() ] = s.converters[ key ];
}
}
@@ -7632,7 +7784,7 @@ function ajaxConvert( s, response ) {
current = dataTypes[ i ];
// If current is auto dataType, update it to prev
- if( current === "*" ) {
+ if ( current === "*" ) {
current = prev;
// If no auto and dataTypes are actually different
} else if ( prev !== "*" && prev !== current ) {
@@ -7644,7 +7796,7 @@ function ajaxConvert( s, response ) {
// If there is no direct converter, search transitively
if ( !conv ) {
conv2 = undefined;
- for( conv1 in converters ) {
+ for ( conv1 in converters ) {
tmp = conv1.split( " " );
if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
conv2 = converters[ tmp[1] + " " + current ];
@@ -8083,11 +8235,11 @@ jQuery.fn.extend({
var elem, display;
if ( speed || speed === 0 ) {
- return this.animate( genFx("show", 3), speed, easing, callback);
+ return this.animate( genFx("show", 3), speed, easing, callback );
} else {
for ( var i = 0, j = this.length; i < j; i++ ) {
- elem = this[i];
+ elem = this[ i ];
if ( elem.style ) {
display = elem.style.display;
@@ -8101,8 +8253,8 @@ jQuery.fn.extend({
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
- if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
- jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
+ if ( display === "" && jQuery.css(elem, "display") === "none" ) {
+ jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
}
}
@@ -8110,13 +8262,13 @@ jQuery.fn.extend({
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( i = 0; i < j; i++ ) {
- elem = this[i];
+ elem = this[ i ];
if ( elem.style ) {
display = elem.style.display;
if ( display === "" || display === "none" ) {
- elem.style.display = jQuery._data(elem, "olddisplay") || "";
+ elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
}
}
}
@@ -8130,12 +8282,17 @@ jQuery.fn.extend({
return this.animate( genFx("hide", 3), speed, easing, callback);
} else {
- for ( var i = 0, j = this.length; i < j; i++ ) {
- if ( this[i].style ) {
- var display = jQuery.css( this[i], "display" );
+ var elem, display,
+ i = 0,
+ j = this.length;
+
+ for ( ; i < j; i++ ) {
+ elem = this[i];
+ if ( elem.style ) {
+ display = jQuery.css( elem, "display" );
- if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
- jQuery._data( this[i], "olddisplay", display );
+ if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
+ jQuery._data( elem, "olddisplay", display );
}
}
}
@@ -8180,7 +8337,7 @@ jQuery.fn.extend({
},
animate: function( prop, speed, easing, callback ) {
- var optall = jQuery.speed(speed, easing, callback);
+ var optall = jQuery.speed( speed, easing, callback );
if ( jQuery.isEmptyObject( prop ) ) {
return this.each( optall.complete, [ false ] );
@@ -8189,7 +8346,7 @@ jQuery.fn.extend({
// Do not change referenced properties as per-property easing will be lost
prop = jQuery.extend( {}, prop );
- return this[ optall.queue === false ? "each" : "queue" ](function() {
+ function doAnimation() {
// XXX 'this' does not always have a nodeName when running the
// test suite
@@ -8200,9 +8357,9 @@ jQuery.fn.extend({
var opt = jQuery.extend( {}, optall ),
isElement = this.nodeType === 1,
hidden = isElement && jQuery(this).is(":hidden"),
- name, val, p,
- display, e,
- parts, start, end, unit;
+ name, val, p, e,
+ parts, start, end, unit,
+ method;
// will store per property easing and be used to determine when an animation is complete
opt.animatedProperties = {};
@@ -8238,25 +8395,17 @@ jQuery.fn.extend({
opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
// Set display property to inline-block for height/width
- // animations on inline elements that are having width/height
- // animated
+ // animations on inline elements that are having width/height animated
if ( jQuery.css( this, "display" ) === "inline" &&
jQuery.css( this, "float" ) === "none" ) {
- if ( !jQuery.support.inlineBlockNeedsLayout ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
this.style.display = "inline-block";
} else {
- display = defaultDisplay( this.nodeName );
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if ( display === "inline" ) {
- this.style.display = "inline-block";
-
- } else {
- this.style.display = "inline";
- this.style.zoom = 1;
- }
+ this.style.zoom = 1;
}
}
}
@@ -8270,8 +8419,17 @@ jQuery.fn.extend({
e = new jQuery.fx( this, opt, p );
val = prop[ p ];
- if ( rfxtypes.test(val) ) {
- e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
+ if ( rfxtypes.test( val ) ) {
+
+ // Tracks whether to show or hide based on private
+ // data attached to the element
+ method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
+ if ( method ) {
+ jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
+ e[ method ]();
+ } else {
+ e[ val ]();
+ }
} else {
parts = rfxnum.exec( val );
@@ -8284,7 +8442,7 @@ jQuery.fn.extend({
// We need to compute starting value
if ( unit !== "px" ) {
jQuery.style( this, p, (end || 1) + unit);
- start = ((end || 1) / e.cur()) * start;
+ start = ( (end || 1) / e.cur() ) * start;
jQuery.style( this, p, start + unit);
}
@@ -8303,39 +8461,71 @@ jQuery.fn.extend({
// For JS strict compliance
return true;
- });
+ }
+
+ return optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
},
- stop: function( clearQueue, gotoEnd ) {
- if ( clearQueue ) {
- this.queue([]);
+ stop: function( type, clearQueue, gotoEnd ) {
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
}
- this.each(function() {
- var timers = jQuery.timers,
- i = timers.length;
+ return this.each(function() {
+ var index,
+ hadTimers = false,
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
// clear marker counters if we know they won't be
if ( !gotoEnd ) {
jQuery._unmark( true, this );
}
- while ( i-- ) {
- if ( timers[i].elem === this ) {
- if (gotoEnd) {
- // force the next step to be the last
- timers[i](true);
- }
- timers.splice(i, 1);
+ function stopQueue( elem, data, index ) {
+ var hooks = data[ index ];
+ jQuery.removeData( elem, index, true );
+ hooks.stop( gotoEnd );
+ }
+
+ if ( type == null ) {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
+ stopQueue( this, data, index );
+ }
}
+ } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
+ stopQueue( this, data, index );
}
- });
- // start the next in the queue if the last step wasn't forced
- if ( !gotoEnd ) {
- this.dequeue();
- }
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ if ( gotoEnd ) {
- return this;
+ // force the next step to be the last
+ timers[ index ]( true );
+ } else {
+ timers[ index ].saveState();
+ }
+ hadTimers = true;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( !( gotoEnd && hadTimers ) ) {
+ jQuery.dequeue( this, type );
+ }
+ });
}
});
@@ -8354,7 +8544,7 @@ function clearFxNow() {
function genFx( type, num ) {
var obj = {};
- jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
obj[ this ] = type;
});
@@ -8363,9 +8553,9 @@ function genFx( type, num ) {
// Generate shortcuts for custom animations
jQuery.each({
- slideDown: genFx("show", 1),
- slideUp: genFx("hide", 1),
- slideToggle: genFx("toggle", 1),
+ slideDown: genFx( "show", 1 ),
+ slideUp: genFx( "hide", 1 ),
+ slideToggle: genFx( "toggle", 1 ),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
@@ -8377,25 +8567,31 @@ jQuery.each({
jQuery.extend({
speed: function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
};
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
// Queueing
opt.old = opt.complete;
+
opt.complete = function( noUnmark ) {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
}
- if ( opt.queue !== false ) {
- jQuery.dequeue( this );
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
} else if ( noUnmark !== false ) {
jQuery._unmark( this );
}
@@ -8409,7 +8605,7 @@ jQuery.extend({
return firstNum + diff * p;
},
swing: function( p, n, firstNum, diff ) {
- return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;
}
},
@@ -8432,12 +8628,12 @@ jQuery.fx.prototype = {
this.options.step.call( this.elem, this.now, this );
}
- (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+ ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
},
// Get the current size
cur: function() {
- if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
return this.elem[ this.prop ];
}
@@ -8455,17 +8651,22 @@ jQuery.fx.prototype = {
fx = jQuery.fx;
this.startTime = fxNow || createFxNow();
- this.start = from;
this.end = to;
- this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
- this.now = this.start;
+ this.now = this.start = from;
this.pos = this.state = 0;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
function t( gotoEnd ) {
- return self.step(gotoEnd);
+ return self.step( gotoEnd );
}
+ t.queue = this.options.queue;
t.elem = this.elem;
+ t.saveState = function() {
+ if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.start );
+ }
+ };
if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval( fx.tick, fx.interval );
@@ -8474,14 +8675,20 @@ jQuery.fx.prototype = {
// Simple 'show' function
show: function() {
+ var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+
// Remember where we started, so that we can go back to it later
- this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
this.options.show = true;
// Begin the animation
- // Make sure that we start at a small width/height to avoid any
- // flash of content
- this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+ // Make sure that we start at a small width/height to avoid any flash of content
+ if ( dataShow !== undefined ) {
+ // This show is picking up where a previous hide or show left off
+ this.custom( this.cur(), dataShow );
+ } else {
+ this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+ }
// Start by showing the element
jQuery( this.elem ).show();
@@ -8490,20 +8697,20 @@ jQuery.fx.prototype = {
// Simple 'hide' function
hide: function() {
// Remember where we started, so that we can go back to it later
- this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
this.options.hide = true;
// Begin the animation
- this.custom(this.cur(), 0);
+ this.custom( this.cur(), 0 );
},
// Each step of an animation
step: function( gotoEnd ) {
- var t = fxNow || createFxNow(),
+ var p, n, complete,
+ t = fxNow || createFxNow(),
done = true,
elem = this.elem,
- options = this.options,
- i, n;
+ options = this.options;
if ( gotoEnd || t >= options.duration + this.startTime ) {
this.now = this.end;
@@ -8512,8 +8719,8 @@ jQuery.fx.prototype = {
options.animatedProperties[ this.prop ] = true;
- for ( i in options.animatedProperties ) {
- if ( options.animatedProperties[i] !== true ) {
+ for ( p in options.animatedProperties ) {
+ if ( options.animatedProperties[ p ] !== true ) {
done = false;
}
}
@@ -8522,25 +8729,36 @@ jQuery.fx.prototype = {
// Reset the overflow
if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
- jQuery.each( [ "", "X", "Y" ], function (index, value) {
- elem.style[ "overflow" + value ] = options.overflow[index];
+ jQuery.each( [ "", "X", "Y" ], function( index, value ) {
+ elem.style[ "overflow" + value ] = options.overflow[ index ];
});
}
// Hide the element if the "hide" operation was done
if ( options.hide ) {
- jQuery(elem).hide();
+ jQuery( elem ).hide();
}
// Reset the properties, if the item has been hidden or shown
if ( options.hide || options.show ) {
- for ( var p in options.animatedProperties ) {
- jQuery.style( elem, p, options.orig[p] );
+ for ( p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[ p ] );
+ jQuery.removeData( elem, "fxshow" + p, true );
+ // Toggle data is no longer needed
+ jQuery.removeData( elem, "toggle" + p, true );
}
}
// Execute the complete function
- options.complete.call( elem );
+ // in the event that the complete function throws an exception
+ // we must ensure it won't be called twice. #5684
+
+ complete = options.complete;
+ if ( complete ) {
+
+ options.complete = false;
+ complete.call( elem );
+ }
}
return false;
@@ -8554,8 +8772,8 @@ jQuery.fx.prototype = {
this.state = n / options.duration;
// Perform the easing function, defaults to swing
- this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration );
- this.now = this.start + ((this.end - this.start) * this.pos);
+ this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ( (this.end - this.start) * this.pos );
}
// Perform the next step of the animation
this.update();
@@ -8567,9 +8785,15 @@ jQuery.fx.prototype = {
jQuery.extend( jQuery.fx, {
tick: function() {
- for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
- if ( !timers[i]() ) {
- timers.splice(i--, 1);
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
}
}
@@ -8599,7 +8823,7 @@ jQuery.extend( jQuery.fx, {
_default: function( fx ) {
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
- fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
} else {
fx.elem[ fx.prop ] = fx.now;
}
@@ -8607,6 +8831,14 @@ jQuery.extend( jQuery.fx, {
}
});
+// Adds width/height step functions
+// Do not set anything below 0
+jQuery.each([ "width", "height" ], function( i, prop ) {
+ jQuery.fx.step[ prop ] = function( fx ) {
+ jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
+ };
+});
+
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
@@ -8623,7 +8855,6 @@ function defaultDisplay( nodeName ) {
var body = document.body,
elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
display = elem.css( "display" );
-
elem.remove();
// If the simple way fails,
@@ -8651,7 +8882,6 @@ function defaultDisplay( nodeName ) {
iframeDoc.body.appendChild( elem );
display = jQuery.css( elem, "display" );
-
body.removeChild( iframe );
}
@@ -8728,8 +8958,6 @@ if ( "getBoundingClientRect" in document.documentElement ) {
return jQuery.offset.bodyOffset( elem );
}
- jQuery.offset.initialize();
-
var computedStyle,
offsetParent = elem.offsetParent,
prevOffsetParent = elem,
@@ -8742,7 +8970,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
left = elem.offsetLeft;
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
- if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
break;
}
@@ -8754,7 +8982,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
top += elem.offsetTop;
left += elem.offsetLeft;
- if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
@@ -8763,7 +8991,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
offsetParent = elem.offsetParent;
}
- if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
@@ -8776,7 +9004,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
left += body.offsetLeft;
}
- if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
top += Math.max( docElem.scrollTop, body.scrollTop );
left += Math.max( docElem.scrollLeft, body.scrollLeft );
}
@@ -8786,46 +9014,12 @@ if ( "getBoundingClientRect" in document.documentElement ) {
}
jQuery.offset = {
- initialize: function() {
- var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
- html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
-
- jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
-
- container.innerHTML = html;
- body.insertBefore( container, body.firstChild );
- innerDiv = container.firstChild;
- checkDiv = innerDiv.firstChild;
- td = innerDiv.nextSibling.firstChild.firstChild;
-
- this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
- this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
-
- checkDiv.style.position = "fixed";
- checkDiv.style.top = "20px";
-
- // safari subtracts parent border width here which is 5px
- this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
- checkDiv.style.position = checkDiv.style.top = "";
-
- innerDiv.style.overflow = "hidden";
- innerDiv.style.position = "relative";
-
- this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
-
- this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
-
- body.removeChild( container );
- jQuery.offset.initialize = jQuery.noop;
- },
bodyOffset: function( body ) {
var top = body.offsetTop,
left = body.offsetLeft;
- jQuery.offset.initialize();
-
- if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
}
@@ -8845,7 +9039,7 @@ jQuery.offset = {
curOffset = curElem.offset(),
curCSSTop = jQuery.css( elem, "top" ),
curCSSLeft = jQuery.css( elem, "left" ),
- calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
props = {}, curPosition = {}, curTop, curLeft;
// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
@@ -8862,11 +9056,11 @@ jQuery.offset = {
options = options.call( elem, i, curOffset );
}
- if (options.top != null) {
- props.top = (options.top - curOffset.top) + curTop;
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
}
- if (options.left != null) {
- props.left = (options.left - curOffset.left) + curLeft;
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
}
if ( "using" in options ) {
@@ -8879,6 +9073,7 @@ jQuery.offset = {
jQuery.fn.extend({
+
position: function() {
if ( !this[0] ) {
return null;
@@ -8981,16 +9176,20 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
// innerHeight and innerWidth
jQuery.fn[ "inner" + name ] = function() {
var elem = this[0];
- return elem && elem.style ?
+ return elem ?
+ elem.style ?
parseFloat( jQuery.css( elem, type, "padding" ) ) :
+ this[ type ]() :
null;
};
// outerHeight and outerWidth
jQuery.fn[ "outer" + name ] = function( margin ) {
var elem = this[0];
- return elem && elem.style ?
+ return elem ?
+ elem.style ?
parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
+ this[ type ]() :
null;
};
@@ -9030,7 +9229,7 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
var orig = jQuery.css( elem, type ),
ret = parseFloat( orig );
- return jQuery.isNaN( ret ) ? orig : ret;
+ return jQuery.isNumeric( ret ) ? ret : orig;
// Set the width or height on the element (default to pixels if value is unitless)
} else {
@@ -9041,6 +9240,27 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
});
+
+
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
-})(window);
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+
+
+})( window );
diff --git a/resources/jquery/jquery.makeCollapsible.js b/resources/jquery/jquery.makeCollapsible.js
index a84f405c..7a1897ce 100644
--- a/resources/jquery/jquery.makeCollapsible.js
+++ b/resources/jquery/jquery.makeCollapsible.js
@@ -38,13 +38,13 @@ $.fn.makeCollapsible = function() {
if ( typeof $defaultToggle == 'undefined' ) {
$defaultToggle = null;
}
- if ( $defaultToggle !== null && !($defaultToggle instanceof jQuery) ) {
+ if ( $defaultToggle !== null && !($defaultToggle instanceof $) ) {
// is optional (may be undefined), but if defined it must be an instance of jQuery.
// If it's not, abort right away.
// After this $defaultToggle is either null or a valid jQuery instance.
return;
}
-
+
var $containers = null;
if ( action == 'collapse' ) {
@@ -55,17 +55,17 @@ $.fn.makeCollapsible = function() {
// Slide doens't work with tables, but fade does as of jQuery 1.1.3
// http://stackoverflow.com/questions/467336#920480
$containers = $collapsible.find( '>tbody>tr' );
- if ( $defaultToggle ) {
+ if ( $defaultToggle ) {
// Exclude tablerow containing togglelink
$containers.not( $defaultToggle.closest( 'tr' ) ).stop(true, true).fadeOut();
} else {
- if ( instantHide ) {
+ if ( instantHide ) {
$containers.hide();
} else {
$containers.stop( true, true ).fadeOut();
}
}
-
+
} else if ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) {
$containers = $collapsible.find( '> li' );
if ( $defaultToggle ) {
@@ -78,10 +78,10 @@ $.fn.makeCollapsible = function() {
$containers.stop( true, true ).slideUp();
}
}
-
+
} else { // <div>, <p> etc.
var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
-
+
// If a collapsible-content is defined, collapse it
if ( $collapsibleContent.length ) {
if ( instantHide ) {
@@ -102,7 +102,7 @@ $.fn.makeCollapsible = function() {
}
} else {
-
+
// Expand the element
if ( $collapsible.is( 'table' ) ) {
$containers = $collapsible.find( '>tbody>tr' );
@@ -112,7 +112,7 @@ $.fn.makeCollapsible = function() {
} else {
$containers.stop(true, true).fadeIn();
}
-
+
} else if ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) {
$containers = $collapsible.find( '> li' );
if ( $defaultToggle ) {
@@ -121,10 +121,10 @@ $.fn.makeCollapsible = function() {
} else {
$containers.stop( true, true ).slideDown();
}
-
+
} else { // <div>, <p> etc.
var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
-
+
// If a collapsible-content is defined, collapse it
if ( $collapsibleContent.length ) {
$collapsibleContent.slideDown();
@@ -147,7 +147,7 @@ $.fn.makeCollapsible = function() {
$collapsible = $that.closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
e.preventDefault();
e.stopPropagation();
-
+
// It's expanded right now
if ( !$that.hasClass( 'mw-collapsible-toggle-collapsed' ) ) {
// Change link to "Show"
@@ -182,7 +182,7 @@ $.fn.makeCollapsible = function() {
}
e.preventDefault();
e.stopPropagation();
-
+
// It's expanded right now
if ( !$that.hasClass( 'mw-collapsible-toggle-collapsed' ) ) {
// Change toggle to collapsed
@@ -210,7 +210,7 @@ $.fn.makeCollapsible = function() {
var action = $collapsible.hasClass( 'mw-collapsed' ) ? 'expand' : 'collapse';
$collapsible.toggleClass( 'mw-collapsed' );
toggleElement( $collapsible, action, $that );
-
+
};
// Use custom text or default ?
@@ -239,7 +239,7 @@ $.fn.makeCollapsible = function() {
} else {
$that.addClass( 'mw-made-collapsible' );
}
-
+
// Check if this element has a custom position for the toggle link
// (ie. outside the container or deeper inside the tree)
// Then: Locate the custom toggle link(s) and bind them
@@ -248,7 +248,7 @@ $.fn.makeCollapsible = function() {
var thatId = $that.attr( 'id' ),
$customTogglers = $( '.' + thatId.replace( 'mw-customcollapsible', 'mw-customtoggle' ) );
mw.log( _fn + 'Found custom collapsible: #' + thatId );
-
+
// Double check that there is actually a customtoggle link
if ( $customTogglers.length ) {
$customTogglers.bind( 'click.mw-collapse', function( e ) {
@@ -257,7 +257,7 @@ $.fn.makeCollapsible = function() {
} else {
mw.log( _fn + '#' + thatId + ': Missing toggler!' );
}
-
+
// Initial state
if ( $that.hasClass( 'mw-collapsed' ) ) {
$that.removeClass( 'mw-collapsed' );
@@ -265,7 +265,7 @@ $.fn.makeCollapsible = function() {
}
// If this is not a custom case, do the default:
- // Wrap the contents add the toggle link
+ // Wrap the contents add the toggle link
} else {
// Elements are treated differently
@@ -273,7 +273,7 @@ $.fn.makeCollapsible = function() {
// The toggle-link will be in one the the cells (td or th) of the first row
var $firstRowCells = $( 'tr:first th, tr:first td', that ),
$toggle = $firstRowCells.find( '> .mw-collapsible-toggle' );
-
+
// If theres no toggle link, add it to the last cell
if ( !$toggle.length ) {
$firstRowCells.eq(-1).prepend( $toggleLink );
@@ -282,19 +282,19 @@ $.fn.makeCollapsible = function() {
toggleLinkPremade( $toggle, e );
} );
}
-
+
} else if ( $that.is( 'ul' ) || $that.is( 'ol' ) ) {
// The toggle-link will be in the first list-item
var $firstItem = $( 'li:first', $that),
$toggle = $firstItem.find( '> .mw-collapsible-toggle' );
-
+
// If theres no toggle link, add it
if ( !$toggle.length ) {
// Make sure the numeral order doesn't get messed up, force the first (soon to be second) item
// to be "1". Except if the value-attribute is already used.
// If no value was set WebKit returns "", Mozilla returns '-1', others return null or undefined.
var firstval = $firstItem.attr( 'value' );
- if ( firstval === undefined || !firstval || firstval == '-1' ) {
+ if ( firstval === undefined || !firstval || firstval == '-1' ) {
$firstItem.attr( 'value', '1' );
}
$that.prepend( $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent() );
@@ -303,9 +303,9 @@ $.fn.makeCollapsible = function() {
toggleLinkPremade( $toggle, e );
} );
}
-
+
} else { // <div>, <p> etc.
-
+
// The toggle-link will be the first child of the element
var $toggle = $that.find( '> .mw-collapsible-toggle' );
@@ -313,7 +313,7 @@ $.fn.makeCollapsible = function() {
if ( !$that.find( '> .mw-collapsible-content' ).length ) {
$that.wrapInner( '<div class="mw-collapsible-content"></div>' );
}
-
+
// If theres no toggle link, add it
if ( !$toggle.length ) {
$that.prepend( $toggleLink );
@@ -336,4 +336,4 @@ $.fn.makeCollapsible = function() {
}
} );
};
-} )( jQuery, mediaWiki ); \ No newline at end of file
+} )( jQuery, mediaWiki );
diff --git a/resources/jquery/jquery.messageBox.js b/resources/jquery/jquery.messageBox.js
index a69fca59..690fedd2 100644
--- a/resources/jquery/jquery.messageBox.js
+++ b/resources/jquery/jquery.messageBox.js
@@ -11,7 +11,7 @@
* @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
* @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
*/
-( function( $, mw ) {
+( function( $ ) {
// @return jQuery object of the message box
$.messageBoxNew = function( options ) {
options = $.extend( {
@@ -19,16 +19,16 @@ $.messageBoxNew = function( options ) {
'parent': 'body', // jQuery/CSS selector
'insert': 'prepend' // 'prepend' or 'append'
}, options );
- var $curBox = $( '#'+ options.id );
+ var $curBox = $( '#' + options.id );
// Only create a new box if it doesn't exist already
- if ( $curBox.size() > 0 ) {
+ if ( $curBox.length > 0 ) {
if ( $curBox.hasClass( 'js-messagebox' ) ) {
return $curBox;
} else {
return $curBox.addClass( 'js-messagebox' );
}
} else {
- var $newBox = $( '<div/>', {
+ var $newBox = $( '<div>', {
'id': options.id,
'class': 'js-messagebox',
'css': {
@@ -58,13 +58,13 @@ $.messageBox = function( options ) {
'group': 'default',
'replace': false, // if true replaces any previous message in this group
'target': 'js-messagebox'
- }, options );
+ }, options );
var $target = $.messageBoxNew( { id: options.target } );
var groupID = options.target + '-' + options.group;
var $group = $( '#' + groupID );
// Create group container if not existant
- if ( $group.size() < 1 ) {
- $group = $( '<div/>', {
+ if ( $group.length < 1 ) {
+ $group = $( '<div>', {
'id': groupID,
'class': 'js-messagebox-group'
});
@@ -79,12 +79,12 @@ $.messageBox = function( options ) {
$group.hide();
} else {
// Actual message addition
- $group.prepend( $( '<p/>' ).append( options.message ) ).show();
+ $group.prepend( $( '<p>' ).append( options.message ) ).show();
$target.slideDown();
}
// If the last visible group was just hidden, slide the entire box up
// Othere wise slideDown (if already visible nothing will happen)
- if ( $target.find( '> *:visible' ).size() === 0 ) {
+ if ( $target.find( '> *:visible' ).length === 0 ) {
// to avoid a sudden dissapearance of the last group followed by
// a slide up of only the outline, show it for a second
$group.show();
@@ -95,4 +95,4 @@ $.messageBox = function( options ) {
}
return $group;
};
-} )( jQuery, mediaWiki ); \ No newline at end of file
+} )( jQuery );
diff --git a/resources/jquery/jquery.mockjax.js b/resources/jquery/jquery.mockjax.js
new file mode 100644
index 00000000..5f6e1306
--- /dev/null
+++ b/resources/jquery/jquery.mockjax.js
@@ -0,0 +1,382 @@
+/*!
+ * MockJax - jQuery Plugin to Mock Ajax requests
+ *
+ * Version: 1.4.0
+ * Released: 2011-02-04
+ * Source: http://github.com/appendto/jquery-mockjax
+ * Docs: http://enterprisejquery.com/2010/07/mock-your-ajax-requests-with-mockjax-for-rapid-development
+ * Plugin: mockjax
+ * Author: Jonathan Sharp (http://jdsharp.com)
+ * License: MIT,GPL
+ *
+ * Copyright (c) 2010 appendTo LLC.
+ * Dual licensed under the MIT or GPL licenses.
+ * http://appendto.com/open-source-licenses
+ */
+(function($) {
+ var _ajax = $.ajax,
+ mockHandlers = [];
+
+ function parseXML(xml) {
+ if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
+ DOMParser = function() { };
+ DOMParser.prototype.parseFromString = function( xmlString ) {
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML( xmlString );
+ return doc;
+ };
+ }
+
+ try {
+ var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
+ if ( $.isXMLDoc( xmlDoc ) ) {
+ var err = $('parsererror', xmlDoc);
+ if ( err.length == 1 ) {
+ throw('Error: ' + $(xmlDoc).text() );
+ }
+ } else {
+ throw('Unable to parse XML');
+ }
+ } catch( e ) {
+ var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
+ $(document).trigger('xmlParseError', [ msg ]);
+ return undefined;
+ }
+ return xmlDoc;
+ }
+
+ $.extend({
+ ajax: function(origSettings) {
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
+ mock = false;
+ // Iterate over our mock handlers (in registration order) until we find
+ // one that is willing to intercept the request
+ $.each(mockHandlers, function(k, v) {
+ if ( !mockHandlers[k] ) {
+ return;
+ }
+ var m = null;
+ // If the mock was registered with a function, let the function decide if we
+ // want to mock this request
+ if ( $.isFunction(mockHandlers[k]) ) {
+ m = mockHandlers[k](s);
+ } else {
+ m = mockHandlers[k];
+ // Inspect the URL of the request and check if the mock handler's url
+ // matches the url for this ajax request
+ if ( $.isFunction(m.url.test) ) {
+ // The user provided a regex for the url, test it
+ if ( !m.url.test( s.url ) ) {
+ m = null;
+ }
+ } else {
+ // Look for a simple wildcard '*' or a direct URL match
+ var star = m.url.indexOf('*');
+ if ( ( m.url != '*' && m.url != s.url && star == -1 ) ||
+ ( star > -1 && m.url.substr(0, star) != s.url.substr(0, star) ) ) {
+ // The url we tested did not match the wildcard *
+ m = null;
+ }
+ }
+ if ( m ) {
+ // Inspect the data submitted in the request (either POST body or GET query string)
+ if ( m.data && s.data ) {
+ var identical = false;
+ // Deep inspect the identity of the objects
+ (function ident(mock, live) {
+ // Test for situations where the data is a querystring (not an object)
+ if (typeof live === 'string') {
+ // Querystring may be a regex
+ identical = $.isFunction( mock.test ) ? mock.test(live) : mock == live;
+ return identical;
+ }
+ $.each(mock, function(k, v) {
+ if ( live[k] === undefined ) {
+ identical = false;
+ return false;
+ } else {
+ identical = true;
+ if ( typeof live[k] == 'object' ) {
+ return ident(mock[k], live[k]);
+ } else {
+ if ( $.isFunction( mock[k].test ) ) {
+ identical = mock[k].test(live[k]);
+ } else {
+ identical = ( mock[k] == live[k] );
+ }
+ return identical;
+ }
+ }
+ });
+ })(m.data, s.data);
+ // They're not identical, do not mock this request
+ if ( identical == false ) {
+ m = null;
+ }
+ }
+ // Inspect the request type
+ if ( m && m.type && m.type != s.type ) {
+ // The request type doesn't match (GET vs. POST)
+ m = null;
+ }
+ }
+ }
+ if ( m ) {
+ mock = true;
+
+ // Handle console logging
+ var c = $.extend({}, $.mockjaxSettings, m);
+ if ( c.log && $.isFunction(c.log) ) {
+ c.log('MOCK ' + s.type.toUpperCase() + ': ' + s.url, $.extend({}, s));
+ }
+
+ var jsre = /=\?(&|$)/, jsc = (new Date()).getTime();
+
+ // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
+ // because there isn't an easy hook for the cross domain script tag of jsonp
+ if ( s.dataType === "jsonp" ) {
+ if ( s.type.toUpperCase() === "GET" ) {
+ if ( !jsre.test( s.url ) ) {
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ }
+ } else if ( !s.data || !jsre.test(s.data) ) {
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ }
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data ) {
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ }
+
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+
+ try {
+ delete window[ jsonp ];
+ } catch(e) {}
+
+ if ( head ) {
+ head.removeChild( script );
+ }
+ };
+ }
+
+ var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+ parts = rurl.exec( s.url ),
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+ // Test if we are going to create a script tag (if so, intercept & mock)
+ if ( s.dataType === "script" && s.type.toUpperCase() === "GET" && remote ) {
+ // Synthesize the mock request for adding a script tag
+ var callbackContext = origSettings && origSettings.context || s;
+
+ function success() {
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success ) {
+ s.success.call( callbackContext, ( m.response ? m.response.toString() : m.responseText || ''), status, {} );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ trigger( "ajaxSuccess", [{}, s] );
+ }
+ }
+
+ function complete() {
+ // Process result
+ if ( s.complete ) {
+ s.complete.call( callbackContext, {} , status );
+ }
+
+ // The request was completed
+ if ( s.global ) {
+ trigger( "ajaxComplete", [{}, s] );
+ }
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+
+ function trigger(type, args) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+ }
+
+ if ( m.response && $.isFunction(m.response) ) {
+ m.response(origSettings);
+ } else {
+ $.globalEval(m.responseText);
+ }
+ success();
+ complete();
+ return false;
+ }
+ mock = _ajax.call($, $.extend(true, {}, origSettings, {
+ // Mock the XHR object
+ xhr: function() {
+ // Extend with our default mockjax settings
+ m = $.extend({}, $.mockjaxSettings, m);
+
+ if ( m.contentType ) {
+ m.headers['content-type'] = m.contentType;
+ }
+
+ // Return our mock xhr object
+ return {
+ status: m.status,
+ readyState: 1,
+ open: function() { },
+ send: function() {
+ // This is a substitute for < 1.4 which lacks $.proxy
+ var process = (function(that) {
+ return function() {
+ return (function() {
+ // The request has returned
+ this.status = m.status;
+ this.readyState = 4;
+
+ // We have an executable function, call it to give
+ // the mock handler a chance to update it's data
+ if ( $.isFunction(m.response) ) {
+ m.response(origSettings);
+ }
+ // Copy over our mock to our xhr object before passing control back to
+ // jQuery's onreadystatechange callback
+ if ( s.dataType == 'json' && ( typeof m.responseText == 'object' ) ) {
+ this.responseText = JSON.stringify(m.responseText);
+ } else if ( s.dataType == 'xml' ) {
+ if ( typeof m.responseXML == 'string' ) {
+ this.responseXML = parseXML(m.responseXML);
+ } else {
+ this.responseXML = m.responseXML;
+ }
+ } else {
+ this.responseText = m.responseText;
+ }
+ // jQuery < 1.4 doesn't have onreadystate change for xhr
+ if ( $.isFunction(this.onreadystatechange) ) {
+ this.onreadystatechange( m.isTimeout ? 'timeout' : undefined );
+ }
+ }).apply(that);
+ };
+ })(this);
+
+ if ( m.proxy ) {
+ // We're proxying this request and loading in an external file instead
+ _ajax({
+ global: false,
+ url: m.proxy,
+ type: m.proxyType,
+ data: m.data,
+ dataType: s.dataType,
+ complete: function(xhr, txt) {
+ m.responseXML = xhr.responseXML;
+ m.responseText = xhr.responseText;
+ this.responseTimer = setTimeout(process, m.responseTime || 0);
+ }
+ });
+ } else {
+ // type == 'POST' || 'GET' || 'DELETE'
+ if ( s.async === false ) {
+ // TODO: Blocking delay
+ process();
+ } else {
+ this.responseTimer = setTimeout(process, m.responseTime || 50);
+ }
+ }
+ },
+ abort: function() {
+ clearTimeout(this.responseTimer);
+ },
+ setRequestHeader: function() { },
+ getResponseHeader: function(header) {
+ // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
+ if ( m.headers && m.headers[header] ) {
+ // Return arbitrary headers
+ return m.headers[header];
+ } else if ( header.toLowerCase() == 'last-modified' ) {
+ return m.lastModified || (new Date()).toString();
+ } else if ( header.toLowerCase() == 'etag' ) {
+ return m.etag || '';
+ } else if ( header.toLowerCase() == 'content-type' ) {
+ return m.contentType || 'text/plain';
+ }
+ },
+ getAllResponseHeaders: function() {
+ var headers = '';
+ $.each(m.headers, function(k, v) {
+ headers += k + ': ' + v + "\n";
+ });
+ return headers;
+ }
+ };
+ }
+ }));
+ return false;
+ }
+ });
+ // We don't have a mock request, trigger a normal request
+ if ( !mock ) {
+ return _ajax.apply($, arguments);
+ } else {
+ return mock;
+ }
+ }
+ });
+
+ $.mockjaxSettings = {
+ //url: null,
+ //type: 'GET',
+ log: function(msg) {
+ window['console'] && window.console.log && window.console.log(msg);
+ },
+ status: 200,
+ responseTime: 500,
+ isTimeout: false,
+ contentType: 'text/plain',
+ response: '',
+ responseText: '',
+ responseXML: '',
+ proxy: '',
+ proxyType: 'GET',
+
+ lastModified: null,
+ etag: '',
+ headers: {
+ etag: 'IJF@H#@923uf8023hFO@I#H#',
+ 'content-type' : 'text/plain'
+ }
+ };
+
+ $.mockjax = function(settings) {
+ var i = mockHandlers.length;
+ mockHandlers[i] = settings;
+ return i;
+ };
+ $.mockjaxClear = function(i) {
+ if ( arguments.length == 1 ) {
+ mockHandlers[i] = null;
+ } else {
+ mockHandlers = [];
+ }
+ };
+})(jQuery);
diff --git a/resources/jquery/jquery.mw-jump.js b/resources/jquery/jquery.mw-jump.js
new file mode 100644
index 00000000..36b6690c
--- /dev/null
+++ b/resources/jquery/jquery.mw-jump.js
@@ -0,0 +1,15 @@
+/**
+ * JavaScript to show jump links to motor-impaired users when they are focused.
+ */
+jQuery( function( $ ) {
+
+ $('.mw-jump').delegate( 'a', 'focus blur', function( e ) {
+ // Confusingly jQuery leaves e.type as "focusout" for delegated blur events
+ if ( e.type === "blur" || e.type === "focusout" ) {
+ $( this ).closest( '.mw-jump' ).css({ height: '0' });
+ } else {
+ $( this ).closest( '.mw-jump' ).css({ height: 'auto' });
+ }
+ } );
+
+} );
diff --git a/resources/jquery/jquery.mwExtension.js b/resources/jquery/jquery.mwExtension.js
new file mode 100644
index 00000000..023ea7f3
--- /dev/null
+++ b/resources/jquery/jquery.mwExtension.js
@@ -0,0 +1,121 @@
+/*
+ * JavaScript backwards-compatibility alternatives and other convenience functions
+ */
+( function( $ ) {
+
+ $.extend({
+ trimLeft: function( str ) {
+ return str === null ? '' : str.toString().replace( /^\s+/, '' );
+ },
+ trimRight: function( str ) {
+ return str === null ?
+ '' : str.toString().replace( /\s+$/, '' );
+ },
+ ucFirst: function( str ) {
+ return str.charAt( 0 ).toUpperCase() + str.substr( 1 );
+ },
+ escapeRE: function( str ) {
+ return str.replace ( /([\\{}()|.?*+\-^$\[\]])/g, "\\$1" );
+ },
+ isDomElement: function( el ) {
+ return !!el && !!el.nodeType;
+ },
+ isEmpty: function( v ) {
+ if ( v === '' || v === 0 || v === '0' || v === null
+ || v === false || v === undefined )
+ {
+ return true;
+ }
+ // the for-loop could potentially contain prototypes
+ // to avoid that we check it's length first
+ if ( v.length === 0 ) {
+ return true;
+ }
+ if ( typeof v === 'object' ) {
+ for ( var key in v ) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ },
+ compareArray: function( arrThis, arrAgainst ) {
+ if ( arrThis.length != arrAgainst.length ) {
+ return false;
+ }
+ for ( var i = 0; i < arrThis.length; i++ ) {
+ if ( $.isArray( arrThis[i] ) ) {
+ if ( !$.compareArray( arrThis[i], arrAgainst[i] ) ) {
+ return false;
+ }
+ } else if ( arrThis[i] !== arrAgainst[i] ) {
+ return false;
+ }
+ }
+ return true;
+ },
+ compareObject: function( objectA, objectB ) {
+
+ // Do a simple check if the types match
+ if ( typeof objectA == typeof objectB ) {
+
+ // Only loop over the contents if it really is an object
+ if ( typeof objectA == 'object' ) {
+ // If they are aliases of the same object (ie. mw and mediaWiki) return now
+ if ( objectA === objectB ) {
+ return true;
+ } else {
+ var prop;
+ // Iterate over each property
+ for ( prop in objectA ) {
+ // Check if this property is also present in the other object
+ if ( prop in objectB ) {
+ // Compare the types of the properties
+ var type = typeof objectA[prop];
+ if ( type == typeof objectB[prop] ) {
+ // Recursively check objects inside this one
+ switch ( type ) {
+ case 'object' :
+ if ( !$.compareObject( objectA[prop], objectB[prop] ) ) {
+ return false;
+ }
+ break;
+ case 'function' :
+ // Functions need to be strings to compare them properly
+ if ( objectA[prop].toString() !== objectB[prop].toString() ) {
+ return false;
+ }
+ break;
+ default:
+ // Strings, numbers
+ if ( objectA[prop] !== objectB[prop] ) {
+ return false;
+ }
+ break;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ // Check for properties in B but not in A
+ // This is about 15% faster (tested in Safari 5 and Firefox 3.6)
+ // ...than incrementing a count variable in the above and below loops
+ // See also: http://www.mediawiki.org/wiki/ResourceLoader/Default_modules/compareObject_test#Results
+ for ( prop in objectB ) {
+ if ( !( prop in objectA ) ) {
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+ }
+ });
+
+} )( jQuery );
diff --git a/resources/jquery/jquery.mwPrototypes.js b/resources/jquery/jquery.mwPrototypes.js
deleted file mode 100644
index e26a6be6..00000000
--- a/resources/jquery/jquery.mwPrototypes.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * JavaScript backwards-compatibility alternatives and other convenience functions
- */
-
-jQuery.extend({
- trimLeft : function( str ) {
- return str === null ? '' : str.toString().replace( /^\s+/, '' );
- },
- trimRight : function( str ) {
- return str === null ?
- '' : str.toString().replace( /\s+$/, '' );
- },
- ucFirst : function( str ) {
- return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 );
- },
- escapeRE : function( str ) {
- 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 || typeof v === 'undefined' )
- {
- return true;
- }
- // the for-loop could potentially contain prototypes
- // to avoid that we check it's length first
- if ( v.length === 0 ) {
- return true;
- }
- if ( typeof v === 'object' ) {
- for ( key in v ) {
- return false;
- }
- return true;
- }
- return false;
- },
- compareArray : function( arrThis, arrAgainst ) {
- if ( arrThis.length != arrAgainst.length ) {
- return false;
- }
- for ( var i = 0; i < arrThis.length; i++ ) {
- if ( arrThis[i] instanceof Array ) {
- if ( !$.compareArray( arrThis[i], arrAgainst[i] ) ) {
- return false;
- }
- } else if ( arrThis[i] !== arrAgainst[i] ) {
- return false;
- }
- }
- return true;
- },
- compareObject : function( objectA, objectB ) {
-
- // Do a simple check if the types match
- if ( typeof objectA == typeof objectB ) {
-
- // Only loop over the contents if it really is an object
- if ( typeof objectA == 'object' ) {
- // If they are aliases of the same object (ie. mw and mediaWiki) return now
- if ( objectA === objectB ) {
- return true;
- } else {
- var prop;
- // Iterate over each property
- for ( prop in objectA ) {
- // Check if this property is also present in the other object
- if ( prop in objectB ) {
- // Compare the types of the properties
- var type = typeof objectA[prop];
- if ( type == typeof objectB[prop] ) {
- // Recursively check objects inside this one
- switch ( type ) {
- case 'object' :
- if ( !$.compareObject( objectA[prop], objectB[prop] ) ) {
- return false;
- }
- break;
- case 'function' :
- // Functions need to be strings to compare them properly
- if ( objectA[prop].toString() !== objectB[prop].toString() ) {
- return false;
- }
- break;
- default:
- // Strings, numbers
- if ( objectA[prop] !== objectB[prop] ) {
- return false;
- }
- break;
- }
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
- // Check for properties in B but not in A
- // This is about 15% faster (tested in Safari 5 and Firefox 3.6)
- // ...than incrementing a count variable in the above and below loops
- // See also: http://www.mediawiki.org/wiki/ResourceLoader/Default_modules/compareObject_test#Results
- for ( prop in objectB ) {
- if ( !( prop in objectA ) ) {
- return false;
- }
- }
- }
- }
- } else {
- return false;
- }
- return true;
- }
-});
-
diff --git a/resources/jquery/jquery.placeholder.js b/resources/jquery/jquery.placeholder.js
index 1bb69f00..8310cef9 100644
--- a/resources/jquery/jquery.placeholder.js
+++ b/resources/jquery/jquery.placeholder.js
@@ -46,7 +46,7 @@ $.fn.placeholder = function() {
// text somewhere in the middle of the placeholder string,
// we want to set the contents of the search box to the
// dropped text.
-
+
// IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
// Firefox fails gracefully with an empty string, IE barfs with an error
try {
@@ -56,7 +56,7 @@ $.fn.placeholder = function() {
// Got an exception, so use the IE way
this.value = e.originalEvent.dataTransfer.getData( 'text' );
}
-
+
// On Firefox, drop fires after the dropped text has been inserted,
// but on IE it fires before. If we don't prevent the default action,
// IE will insert the dropped text twice.
diff --git a/resources/jquery/jquery.qunit.completenessTest.js b/resources/jquery/jquery.qunit.completenessTest.js
index 2244237c..5abb8691 100644
--- a/resources/jquery/jquery.qunit.completenessTest.js
+++ b/resources/jquery/jquery.qunit.completenessTest.js
@@ -2,10 +2,10 @@
* jQuery QUnit CompletenessTest 0.3
*
* Tests the completeness of test suites for object oriented javascript
- * libraries. Written to be used in enviroments with jQuery and QUnit.
+ * libraries. Written to be used in environments with jQuery and QUnit.
* Requires jQuery 1.5.2 or higher.
*
- * Globals: jQuery, $, QUnit, console.log
+ * Globals: jQuery, QUnit, console.log
*
* Built for and tested with:
* - Safari 5
@@ -13,11 +13,7 @@
*
* @author Timo Tijhof, 2011
*/
-(function(){
-
-/* Private members */
-var TYPE_SIMPLEFUNC = 101;
-var TYPE_OBJCONSTRFUNC = 100;
+( function( $ ) {
/**
* CompletenessTest
@@ -159,7 +155,7 @@ CompletenessTest.fn = CompletenessTest.prototype = {
$.each( currVar.prototype, function( key, value ) {
if ( key === 'constructor' ) return;
- // Clone and brake reference to parentPathArray
+ // Clone and break reference to parentPathArray
var tmpPathArray = $.extend( [], parentPathArray );
tmpPathArray.push( 'prototype' ); tmpPathArray.push( key );
@@ -174,11 +170,9 @@ CompletenessTest.fn = CompletenessTest.prototype = {
if ( !currVar.prototype || $.isEmptyObject( currVar.prototype ) ) {
// Inject check
- that.injectCheck( masterVariable, parentPathArray, function(){
-
+ that.injectCheck( masterVariable, parentPathArray, function() {
that.methodCallTracker[ parentPathArray.join( '.' ) ] = true;
-
- }, TYPE_SIMPLEFUNC );
+ } );
// We don't support checking object constructors yet...
} else {
@@ -187,7 +181,7 @@ CompletenessTest.fn = CompletenessTest.prototype = {
$.each( currVar.prototype, function( key, value ) {
if ( key === 'constructor' ) return;
- // Clone and brake reference to parentPathArray
+ // Clone and break reference to parentPathArray
var tmpPathArray = $.extend( [], parentPathArray );
tmpPathArray.push( 'prototype' ); tmpPathArray.push( key );
@@ -197,12 +191,12 @@ CompletenessTest.fn = CompletenessTest.prototype = {
}
- // Recursively. After all, this *is* the completness test
+ // Recursively. After all, this *is* the completeness test
} else if ( type === 'object' ) {
$.each( currVar, function( key, value ) {
- // Clone and brake reference to parentPathArray
+ // Clone and break reference to parentPathArray
var tmpPathArray = $.extend( [], parentPathArray );
tmpPathArray.push( key );
@@ -211,8 +205,6 @@ CompletenessTest.fn = CompletenessTest.prototype = {
} );
}
-
- return 'End of checkTests';
},
/**
@@ -220,13 +212,13 @@ CompletenessTest.fn = CompletenessTest.prototype = {
*
* Checks if the given method name (ie. 'my.foo.bar')
* was called during the test suite (as far as the tracker knows).
- * If so it adds it to missingTests.
+ * If not it adds it to missingTests.
*
* @param fnName {String}
* @return {Boolean}
*/
hasTest: function( fnName ) {
- if ( !(fnName in this.methodCallTracker) ) {
+ if ( !( fnName in this.methodCallTracker ) ) {
this.missingTests[fnName] = true;
return false;
}
@@ -248,14 +240,14 @@ CompletenessTest.fn = CompletenessTest.prototype = {
curr = masterVariable,
lastMember;
- $.each( objectPathArray, function(i, memberName){
+ $.each( objectPathArray, function( i, memberName ) {
prev = curr;
curr = prev[memberName];
lastMember = memberName;
});
// Objects are by reference, members (unless objects) are not.
- prev[lastMember] = function(){
+ prev[lastMember] = function() {
injectFn();
return curr.apply( this, arguments );
};
@@ -264,4 +256,4 @@ CompletenessTest.fn = CompletenessTest.prototype = {
window.CompletenessTest = CompletenessTest;
-})();
+} )( jQuery );
diff --git a/resources/jquery/jquery.qunit.css b/resources/jquery/jquery.qunit.css
index fa9156f9..bcecc4c0 100644
--- a/resources/jquery/jquery.qunit.css
+++ b/resources/jquery/jquery.qunit.css
@@ -1,9 +1,9 @@
/**
- * QUnit - A JavaScript Unit Testing Framework
- *
+ * QUnit v1.2.0 - A JavaScript Unit Testing Framework
+ *
* http://docs.jquery.com/QUnit
*
- * Copyright (c) 2011 John Resig, Jörn Zaefferer
+ * Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
@@ -11,7 +11,7 @@
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
- font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
@@ -37,7 +37,7 @@
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
-
+
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
@@ -105,13 +105,13 @@
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
-
+
background-color: #fff;
-
+
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
-
+
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
@@ -174,7 +174,7 @@
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
-
+
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
@@ -186,6 +186,7 @@
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
+ white-space: pre;
}
#qunit-tests > li:last-child {
@@ -222,4 +223,4 @@
position: absolute;
top: -10000px;
left: -10000px;
-} \ No newline at end of file
+}
diff --git a/resources/jquery/jquery.qunit.js b/resources/jquery/jquery.qunit.js
index 1405e797..6d2a8a7b 100644
--- a/resources/jquery/jquery.qunit.js
+++ b/resources/jquery/jquery.qunit.js
@@ -1,6 +1,6 @@
/**
- * QUnit - A JavaScript Unit Testing Framework
- *
+ * QUnit v1.2.0 - A JavaScript Unit Testing Framework
+ *
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
@@ -15,13 +15,15 @@ var defined = {
sessionStorage: (function() {
try {
return !!sessionStorage.getItem;
- } catch(e){
+ } catch(e) {
return false;
}
- })()
+ })()
};
-var testId = 0;
+var testId = 0,
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty;
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
this.name = name;
@@ -48,7 +50,7 @@ Test.prototype = {
setup: function() {
if (this.module != config.previousModule) {
if ( config.previousModule ) {
- QUnit.moduleDone( {
+ runLoggingCallbacks('moduleDone', QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
@@ -57,7 +59,7 @@ Test.prototype = {
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
- QUnit.moduleStart( {
+ runLoggingCallbacks( 'moduleStart', QUnit, {
name: this.module
} );
}
@@ -71,14 +73,15 @@ Test.prototype = {
extend(this.testEnvironment, this.testEnvironmentArg);
}
- QUnit.testStart( {
- name: this.testName
- } );
+ runLoggingCallbacks( 'testStart', QUnit, {
+ name: this.testName,
+ module: this.module
+ });
// allow utility functions to access the current test environment
// TODO why??
QUnit.current_testEnvironment = this.testEnvironment;
-
+
try {
if ( !config.pollution ) {
saveGlobal();
@@ -90,6 +93,7 @@ Test.prototype = {
}
},
run: function() {
+ config.current = this;
if ( this.async ) {
QUnit.stop();
}
@@ -108,23 +112,25 @@ Test.prototype = {
// Restart the tests if they're blocking
if ( config.blocking ) {
- start();
+ QUnit.start();
}
}
},
teardown: function() {
+ config.current = this;
try {
- checkPollution();
this.testEnvironment.teardown.call(this.testEnvironment);
+ checkPollution();
} catch(e) {
QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
}
},
finish: function() {
- if ( this.expected && this.expected != this.assertions.length ) {
+ config.current = this;
+ if ( this.expected != null && this.expected != this.assertions.length ) {
QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
}
-
+
var good = 0, bad = 0,
tests = id("qunit-tests");
@@ -132,7 +138,7 @@ Test.prototype = {
config.moduleStats.all += this.assertions.length;
if ( tests ) {
- var ol = document.createElement("ol");
+ var ol = document.createElement("ol");
for ( var i = 0; i < this.assertions.length; i++ ) {
var assertion = this.assertions[i];
@@ -166,17 +172,17 @@ Test.prototype = {
var b = document.createElement("strong");
b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
-
+
var a = document.createElement("a");
a.innerHTML = "Rerun";
a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
-
+
addEvent(b, "click", function() {
var next = b.nextSibling.nextSibling,
display = next.style.display;
next.style.display = display === "none" ? "block" : "none";
});
-
+
addEvent(b, "dblclick", function(e) {
var target = e && e.target ? e.target : window.event.srcElement;
if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
@@ -210,14 +216,15 @@ Test.prototype = {
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
}
- QUnit.testDone( {
+ runLoggingCallbacks( 'testDone', QUnit, {
name: this.testName,
+ module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length
} );
},
-
+
queue: function() {
var test = this;
synchronize(function() {
@@ -243,10 +250,10 @@ Test.prototype = {
if (bad) {
run();
} else {
- synchronize(run);
+ synchronize(run, true);
};
}
-
+
};
var QUnit = {
@@ -260,12 +267,12 @@ var QUnit = {
asyncTest: function(testName, expected, callback) {
if ( arguments.length === 2 ) {
callback = expected;
- expected = 0;
+ expected = null;
}
QUnit.test(testName, expected, callback, true);
},
-
+
test: function(testName, expected, callback, async) {
var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
@@ -275,7 +282,7 @@ var QUnit = {
}
// is 2nd argument a testEnvironment?
if ( expected && typeof expected === 'object') {
- testEnvironmentArg = expected;
+ testEnvironmentArg = expected;
expected = null;
}
@@ -286,13 +293,13 @@ var QUnit = {
if ( !validTest(config.currentModule + ": " + testName) ) {
return;
}
-
+
var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
test.module = config.currentModule;
test.moduleTestEnvironment = config.currentModuleTestEnviroment;
test.queue();
},
-
+
/**
* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
*/
@@ -310,8 +317,8 @@ var QUnit = {
result: a,
message: msg
};
- msg = escapeHtml(msg);
- QUnit.log(details);
+ msg = escapeInnerText(msg);
+ runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({
result: a,
message: msg
@@ -337,7 +344,7 @@ var QUnit = {
notEqual: function(actual, expected, message) {
QUnit.push(expected != actual, actual, expected, message);
},
-
+
deepEqual: function(actual, expected, message) {
QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
},
@@ -356,39 +363,39 @@ var QUnit = {
raises: function(block, expected, message) {
var actual, ok = false;
-
+
if (typeof expected === 'string') {
message = expected;
expected = null;
}
-
+
try {
block();
} catch (e) {
actual = e;
}
-
+
if (actual) {
// we don't want to validate thrown error
if (!expected) {
ok = true;
- // expected is a regexp
+ // expected is a regexp
} else if (QUnit.objectType(expected) === "regexp") {
ok = expected.test(actual);
- // expected is a constructor
+ // expected is a constructor
} else if (actual instanceof expected) {
ok = true;
- // expected is a validation function which returns true is validation passed
+ // expected is a validation function which returns true is validation passed
} else if (expected.call({}, actual) === true) {
ok = true;
}
}
-
+
QUnit.ok(ok, message);
},
- start: function() {
- config.semaphore--;
+ start: function(count) {
+ config.semaphore -= count || 1;
if (config.semaphore > 0) {
// don't start until equal number of stop-calls
return;
@@ -400,33 +407,46 @@ var QUnit = {
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
window.setTimeout(function() {
+ if (config.semaphore > 0) {
+ return;
+ }
if ( config.timeout ) {
clearTimeout(config.timeout);
}
config.blocking = false;
- process();
+ process(true);
}, 13);
} else {
config.blocking = false;
- process();
+ process(true);
}
},
-
- stop: function(timeout) {
- config.semaphore++;
+
+ stop: function(count) {
+ config.semaphore += count || 1;
config.blocking = true;
- if ( timeout && defined.setTimeout ) {
+ if ( config.testTimeout && defined.setTimeout ) {
clearTimeout(config.timeout);
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
QUnit.start();
- }, timeout);
+ }, config.testTimeout);
}
}
};
+//We want access to the constructor's prototype
+(function() {
+ function F(){};
+ F.prototype = QUnit;
+ QUnit = new F();
+ //Make F QUnit's constructor so that we can add to the prototype later
+ QUnit.constructor = F;
+})();
+
// Backwards compatibility, deprecated
QUnit.equals = QUnit.equal;
QUnit.same = QUnit.deepEqual;
@@ -438,13 +458,28 @@ var config = {
// block until document ready
blocking: true,
-
+
+ // when enabled, show only failing tests
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
+ hidepassed: false,
+
// by default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
reorder: true,
- noglobals: false,
- notrycatch: false
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ urlConfig: ['noglobals', 'notrycatch'],
+
+ //logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
};
// Load paramaters
@@ -462,9 +497,6 @@ var config = {
// allow just a key to turn on a flag, e.g., test.html?noglobals
current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
urlParams[ current[ 0 ] ] = current[ 1 ];
- if ( current[ 0 ] in config ) {
- config[ current[ 0 ] ] = current[ 1 ];
- }
}
}
@@ -519,7 +551,7 @@ extend(QUnit, {
if ( result ) {
result.parentNode.removeChild( result );
}
-
+
if ( tests ) {
result = document.createElement( "p" );
result.id = "qunit-testresult";
@@ -528,10 +560,10 @@ extend(QUnit, {
result.innerHTML = 'Running...<br/>&nbsp;';
}
},
-
+
/**
* Resets the test setup. Useful for tests that modify the DOM.
- *
+ *
* If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
*/
reset: function() {
@@ -544,7 +576,7 @@ extend(QUnit, {
}
}
},
-
+
/**
* Trigger an event on an element.
*
@@ -564,12 +596,12 @@ extend(QUnit, {
elem.fireEvent("on"+type);
}
},
-
+
// Safe object type checking
is: function( type, obj ) {
return QUnit.objectType( obj ) == type;
},
-
+
objectType: function( obj ) {
if (typeof obj === "undefined") {
return "undefined";
@@ -580,8 +612,7 @@ extend(QUnit, {
return "null";
}
- var type = Object.prototype.toString.call( obj )
- .match(/^\[object\s(.*)\]$/)[1] || '';
+ var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
switch (type) {
case 'Number':
@@ -603,7 +634,7 @@ extend(QUnit, {
}
return undefined;
},
-
+
push: function(result, actual, expected, message) {
var details = {
result: result,
@@ -611,11 +642,11 @@ extend(QUnit, {
actual: actual,
expected: expected
};
-
- message = escapeHtml(message) || (result ? "okay" : "failed");
+
+ message = escapeInnerText(message) || (result ? "okay" : "failed");
message = '<span class="test-message">' + message + "</span>";
- expected = escapeHtml(QUnit.jsDump.parse(expected));
- actual = escapeHtml(QUnit.jsDump.parse(actual));
+ expected = escapeInnerText(QUnit.jsDump.parse(expected));
+ actual = escapeInnerText(QUnit.jsDump.parse(actual));
var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
if (actual != expected) {
output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
@@ -625,54 +656,66 @@ extend(QUnit, {
var source = sourceFromStacktrace();
if (source) {
details.source = source;
- output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
+ output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
}
}
output += "</table>";
-
- QUnit.log(details);
-
+
+ runLoggingCallbacks( 'log', QUnit, details );
+
config.current.assertions.push({
result: !!result,
message: output
});
},
-
+
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var querystring = "?",
key;
for ( key in params ) {
+ if ( !hasOwn.call( params, key ) ) {
+ continue;
+ }
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
return window.location.pathname + querystring.slice( 0, -1 );
},
-
+
+ extend: extend,
+ id: id,
+ addEvent: addEvent
+});
+
+//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
+//Doing this allows us to tell if the following methods have been overwritten on the actual
+//QUnit object, which is a deprecated way of using the callbacks.
+extend(QUnit.constructor.prototype, {
// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
- begin: function() {},
+ begin: registerLoggingCallback('begin'),
// done: { failed, passed, total, runtime }
- done: function() {},
+ done: registerLoggingCallback('done'),
// log: { result, actual, expected, message }
- log: function() {},
+ log: registerLoggingCallback('log'),
// testStart: { name }
- testStart: function() {},
+ testStart: registerLoggingCallback('testStart'),
// testDone: { name, failed, passed, total }
- testDone: function() {},
+ testDone: registerLoggingCallback('testDone'),
// moduleStart: { name }
- moduleStart: function() {},
+ moduleStart: registerLoggingCallback('moduleStart'),
// moduleDone: { name, failed, passed, total }
- moduleDone: function() {}
+ moduleDone: registerLoggingCallback('moduleDone')
});
if ( typeof document === "undefined" || document.readyState === "complete" ) {
config.autorun = true;
}
-addEvent(window, "load", function() {
- QUnit.begin({});
-
+QUnit.load = function() {
+ runLoggingCallbacks( 'begin', QUnit, {} );
+
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
QUnit.init();
@@ -680,22 +723,26 @@ addEvent(window, "load", function() {
config.blocking = false;
+ var urlConfigHtml = '', len = config.urlConfig.length;
+ for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
+ config[val] = QUnit.urlParams[val];
+ urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
+ }
+
var userAgent = id("qunit-userAgent");
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
var banner = id("qunit-header");
if ( banner ) {
- banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' +
- '<label><input name="noglobals" type="checkbox"' + ( config.noglobals ? ' checked="checked"' : '' ) + '>noglobals</label>' +
- '<label><input name="notrycatch" type="checkbox"' + ( config.notrycatch ? ' checked="checked"' : '' ) + '>notrycatch</label>';
+ banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
addEvent( banner, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
}
-
+
var toolbar = id("qunit-testrunner-toolbar");
if ( toolbar ) {
var filter = document.createElement("input");
@@ -711,13 +758,13 @@ addEvent(window, "load", function() {
}
if ( defined.sessionStorage ) {
if (filter.checked) {
- sessionStorage.setItem("qunit-filter-passed-tests", "true");
+ sessionStorage.setItem("qunit-filter-passed-tests", "true");
} else {
sessionStorage.removeItem("qunit-filter-passed-tests");
}
}
});
- if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
filter.checked = true;
var ol = document.getElementById("qunit-tests");
ol.className = ol.className + " hidepass";
@@ -738,14 +785,27 @@ addEvent(window, "load", function() {
if (config.autostart) {
QUnit.start();
}
-});
+};
+
+addEvent(window, "load", QUnit.load);
+
+// addEvent(window, "error") gives us a useless event object
+window.onerror = function( message, file, line ) {
+ if ( QUnit.config.current ) {
+ ok( false, message + ", " + file + ":" + line );
+ } else {
+ test( "global failure", function() {
+ ok( false, message + ", " + file + ":" + line );
+ });
+ }
+};
function done() {
config.autorun = true;
// Log the last module results
if ( config.currentModule ) {
- QUnit.moduleDone( {
+ runLoggingCallbacks( 'moduleDone', QUnit, {
name: config.currentModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
@@ -774,13 +834,22 @@ function done() {
banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
}
- if ( tests ) {
+ if ( tests ) {
id( "qunit-testresult" ).innerHTML = html;
}
- QUnit.done( {
+ if ( config.altertitle && typeof document !== "undefined" && document.title ) {
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ (config.stats.bad ? "\u2716" : "\u2714"),
+ document.title.replace(/^[\u2714\u2716] /i, "")
+ ].join(" ");
+ }
+
+ runLoggingCallbacks( 'done', QUnit, {
failed: config.stats.bad,
- passed: passed,
+ passed: passed,
total: config.stats.all,
runtime: runtime
} );
@@ -794,7 +863,7 @@ function validTest( name ) {
return true;
}
- not = filter.charAt( 0 ) === "!";
+ var not = filter.charAt( 0 ) === "!";
if ( not ) {
filter = filter.slice( 1 );
}
@@ -822,20 +891,22 @@ function sourceFromStacktrace() {
} else if (e.stack) {
// Firefox, Chrome
return e.stack.split("\n")[4];
+ } else if (e.sourceURL) {
+ // Safari, PhantomJS
+ // TODO sourceURL points at the 'throw new Error' line above, useless
+ //return e.sourceURL + ":" + e.line;
}
}
}
-function escapeHtml(s) {
+function escapeInnerText(s) {
if (!s) {
return "";
}
s = s + "";
- return s.replace(/[\&"<>\\]/g, function(s) {
+ return s.replace(/[\&<>]/g, function(s) {
switch(s) {
case "&": return "&amp;";
- case "\\": return "\\\\";
- case '"': return '\"';
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
@@ -843,35 +914,42 @@ function escapeHtml(s) {
});
}
-function synchronize( callback ) {
+function synchronize( callback, last ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
- process();
+ process(last);
}
}
-function process() {
- var start = (new Date()).getTime();
+function process( last ) {
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
while ( config.queue.length && !config.blocking ) {
- if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
- window.setTimeout( process, 13 );
+ window.setTimeout( function(){
+ process( last );
+ }, 13 );
break;
}
}
- if (!config.blocking && !config.queue.length) {
- done();
- }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
}
function saveGlobal() {
config.pollution = [];
-
+
if ( config.noglobals ) {
for ( var key in window ) {
+ if ( !hasOwn.call( window, key ) ) {
+ continue;
+ }
config.pollution.push( key );
}
}
@@ -880,7 +958,7 @@ function saveGlobal() {
function checkPollution( name ) {
var old = config.pollution;
saveGlobal();
-
+
var newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
@@ -922,7 +1000,9 @@ function extend(a, b) {
for ( var prop in b ) {
if ( b[prop] === undefined ) {
delete a[prop];
- } else {
+
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
+ } else if ( prop !== "constructor" || a !== window ) {
a[prop] = b[prop];
}
}
@@ -945,176 +1025,216 @@ function id(name) {
document.getElementById( name );
}
+function registerLoggingCallback(key){
+ return function(callback){
+ config[key].push( callback );
+ };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks(key, scope, args) {
+ //debugger;
+ var callbacks;
+ if ( QUnit.hasOwnProperty(key) ) {
+ QUnit[key].call(scope, args);
+ } else {
+ callbacks = config[key];
+ for( var i = 0; i < callbacks.length; i++ ) {
+ callbacks[i].call( scope, args );
+ }
+ }
+}
+
// Test for equality any JavaScript type.
-// Discussions and reference: http://philrathe.com/articles/equiv
-// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {
- var innerEquiv; // the real equiv function
- var callers = []; // stack to decide between skip/abort functions
- var parents = []; // stack to avoiding loops from circular referencing
-
- // Call the o related callback with the given arguments.
- function bindCallbacks(o, callbacks, args) {
- var prop = QUnit.objectType(o);
- if (prop) {
- if (QUnit.objectType(callbacks[prop]) === "function") {
- return callbacks[prop].apply(callbacks, args);
- } else {
- return callbacks[prop]; // or undefined
- }
- }
- }
-
- var callbacks = function () {
-
- // for string, boolean, number and null
- function useStrictEquality(b, a) {
- if (b instanceof a.constructor || a instanceof b.constructor) {
- // to catch short annotaion VS 'new' annotation of a declaration
- // e.g. var i = 1;
- // var j = new Number(1);
- return a == b;
- } else {
- return a === b;
- }
- }
-
- return {
- "string": useStrictEquality,
- "boolean": useStrictEquality,
- "number": useStrictEquality,
- "null": useStrictEquality,
- "undefined": useStrictEquality,
-
- "nan": function (b) {
- return isNaN(b);
- },
-
- "date": function (b, a) {
- return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
- },
-
- "regexp": function (b, a) {
- return QUnit.objectType(b) === "regexp" &&
- a.source === b.source && // the regex itself
- a.global === b.global && // and its modifers (gmi) ...
- a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline;
- },
-
- // - skip when the property is a method of an instance (OOP)
- // - abort otherwise,
- // initial === would have catch identical references anyway
- "function": function () {
- var caller = callers[callers.length - 1];
- return caller !== Object &&
- typeof caller !== "undefined";
- },
-
- "array": function (b, a) {
- var i, j, loop;
- var len;
-
- // b could be an object literal here
- if ( ! (QUnit.objectType(b) === "array")) {
- return false;
- }
-
- len = a.length;
- if (len !== b.length) { // safe and faster
- return false;
- }
-
- //track reference to avoid circular references
- parents.push(a);
- for (i = 0; i < len; i++) {
- loop = false;
- for(j=0;j<parents.length;j++){
- if(parents[j] === a[i]){
- loop = true;//dont rewalk array
- }
- }
- if (!loop && ! innerEquiv(a[i], b[i])) {
- parents.pop();
- return false;
- }
- }
- parents.pop();
- return true;
- },
-
- "object": function (b, a) {
- var i, j, loop;
- var eq = true; // unless we can proove it
- var aProperties = [], bProperties = []; // collection of strings
-
- // comparing constructors is more strict than using instanceof
- if ( a.constructor !== b.constructor) {
- return false;
- }
+ var innerEquiv; // the real equiv function
+ var callers = []; // stack to decide between skip/abort functions
+ var parents = []; // stack to avoiding loops from circular referencing
- // stack constructor before traversing properties
- callers.push(a.constructor);
- //track reference to avoid circular references
- parents.push(a);
-
- for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
- loop = false;
- for(j=0;j<parents.length;j++){
- if(parents[j] === a[i])
- loop = true; //don't go down the same path twice
- }
- aProperties.push(i); // collect a's properties
-
- if (!loop && ! innerEquiv(a[i], b[i])) {
- eq = false;
- break;
- }
- }
+ // Call the o related callback with the given arguments.
+ function bindCallbacks(o, callbacks, args) {
+ var prop = QUnit.objectType(o);
+ if (prop) {
+ if (QUnit.objectType(callbacks[prop]) === "function") {
+ return callbacks[prop].apply(callbacks, args);
+ } else {
+ return callbacks[prop]; // or undefined
+ }
+ }
+ }
- callers.pop(); // unstack, we are done
- parents.pop();
+ var getProto = Object.getPrototypeOf || function (obj) {
+ return obj.__proto__;
+ };
- for (i in b) {
- bProperties.push(i); // collect b's properties
- }
+ var callbacks = function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality(b, a) {
+ if (b instanceof a.constructor || a instanceof b.constructor) {
+ // to catch short annotaion VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string" : useStrictEquality,
+ "boolean" : useStrictEquality,
+ "number" : useStrictEquality,
+ "null" : useStrictEquality,
+ "undefined" : useStrictEquality,
+
+ "nan" : function(b) {
+ return isNaN(b);
+ },
+
+ "date" : function(b, a) {
+ return QUnit.objectType(b) === "date"
+ && a.valueOf() === b.valueOf();
+ },
+
+ "regexp" : function(b, a) {
+ return QUnit.objectType(b) === "regexp"
+ && a.source === b.source && // the regex itself
+ a.global === b.global && // and its modifers
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase
+ && a.multiline === b.multiline;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function" : function() {
+ var caller = callers[callers.length - 1];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array" : function(b, a) {
+ var i, j, loop;
+ var len;
+
+ // b could be an object literal here
+ if (!(QUnit.objectType(b) === "array")) {
+ return false;
+ }
+
+ len = a.length;
+ if (len !== b.length) { // safe and faster
+ return false;
+ }
- // Ensures identical properties name
- return eq && innerEquiv(aProperties.sort(), bProperties.sort());
- }
- };
- }();
-
- innerEquiv = function () { // can take multiple arguments
- var args = Array.prototype.slice.apply(arguments);
- if (args.length < 2) {
- return true; // end transition
- }
-
- return (function (a, b) {
- if (a === b) {
- return true; // catch the most you can
- } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
- return false; // don't lose time with error prone cases
- } else {
- return bindCallbacks(a, callbacks, [b, a]);
- }
-
- // apply transition with (1..n) arguments
- })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
- };
-
- return innerEquiv;
+ // track reference to avoid circular references
+ parents.push(a);
+ for (i = 0; i < len; i++) {
+ loop = false;
+ for (j = 0; j < parents.length; j++) {
+ if (parents[j] === a[i]) {
+ loop = true;// dont rewalk array
+ }
+ }
+ if (!loop && !innerEquiv(a[i], b[i])) {
+ parents.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ return true;
+ },
+
+ "object" : function(b, a) {
+ var i, j, loop;
+ var eq = true; // unless we can proove it
+ var aProperties = [], bProperties = []; // collection of
+ // strings
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if (a.constructor !== b.constructor) {
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if (!((getProto(a) === null && getProto(b) === Object.prototype) ||
+ (getProto(b) === null && getProto(a) === Object.prototype)))
+ {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push(a.constructor);
+ // track reference to avoid circular references
+ parents.push(a);
+
+ for (i in a) { // be strict: don't ensures hasOwnProperty
+ // and go deep
+ loop = false;
+ for (j = 0; j < parents.length; j++) {
+ if (parents[j] === a[i])
+ loop = true; // don't go down the same path
+ // twice
+ }
+ aProperties.push(i); // collect a's properties
+
+ if (!loop && !innerEquiv(a[i], b[i])) {
+ eq = false;
+ break;
+ }
+ }
+
+ callers.pop(); // unstack, we are done
+ parents.pop();
+
+ for (i in b) {
+ bProperties.push(i); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq
+ && innerEquiv(aProperties.sort(), bProperties
+ .sort());
+ }
+ };
+ }();
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = Array.prototype.slice.apply(arguments);
+ if (args.length < 2) {
+ return true; // end transition
+ }
+
+ return (function(a, b) {
+ if (a === b) {
+ return true; // catch the most you can
+ } else if (a === null || b === null || typeof a === "undefined"
+ || typeof b === "undefined"
+ || QUnit.objectType(a) !== QUnit.objectType(b)) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [ b, a ]);
+ }
+
+ // apply transition with (1..n) arguments
+ })(args[0], args[1])
+ && arguments.callee.apply(this, args.splice(1,
+ args.length - 1));
+ };
+
+ return innerEquiv;
}();
/**
- * jsDump
- * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
- * Date: 5/15/2008
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
* @projectDescription Advanced and extensible data dumping for Javascript.
* @version 1.0.0
* @author Ariel Flesler
@@ -1125,7 +1245,7 @@ QUnit.jsDump = (function() {
return '"' + str.toString().replace(/"/g, '\\"') + '"';
};
function literal( o ) {
- return o + '';
+ return o + '';
};
function join( pre, arr, post ) {
var s = jsDump.separator(),
@@ -1137,25 +1257,35 @@ QUnit.jsDump = (function() {
return pre + post;
return [ pre, inner + arr, base + post ].join(s);
};
- function array( arr ) {
- var i = arr.length, ret = Array(i);
+ function array( arr, stack ) {
+ var i = arr.length, ret = Array(i);
this.up();
while ( i-- )
- ret[i] = this.parse( arr[i] );
+ ret[i] = this.parse( arr[i] , undefined , stack);
this.down();
return join( '[', ret, ']' );
};
-
+
var reName = /^function (\w+)/;
-
+
var jsDump = {
- parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
- var parser = this.parsers[ type || this.typeOf(obj) ];
- type = typeof parser;
-
- return type == 'function' ? parser.call( this, obj ) :
- type == 'string' ? parser :
- this.parsers.error;
+ parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
+ stack = stack || [ ];
+ var parser = this.parsers[ type || this.typeOf(obj) ];
+ type = typeof parser;
+ var inStack = inArray(obj, stack);
+ if (inStack != -1) {
+ return 'recursion('+(inStack - stack.length)+')';
+ }
+ //else
+ if (type == 'function') {
+ stack.push(obj);
+ var res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ // else
+ return (type == 'string') ? parser : this.parsers.error;
},
typeOf:function( obj ) {
var type;
@@ -1175,7 +1305,12 @@ QUnit.jsDump = (function() {
type = "document";
} else if (obj.nodeType) {
type = "node";
- } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+ ) {
type = "array";
} else {
type = typeof obj;
@@ -1203,7 +1338,7 @@ QUnit.jsDump = (function() {
this.parsers[name] = parser;
},
// The next 3 are exposed so you can use them
- quote:quote,
+ quote:quote,
literal:literal,
join:join,
//
@@ -1222,28 +1357,30 @@ QUnit.jsDump = (function() {
if ( name )
ret += ' ' + name;
ret += '(';
-
+
ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
},
array: array,
nodelist: array,
arguments: array,
- object:function( map ) {
+ object:function( map, stack ) {
var ret = [ ];
QUnit.jsDump.up();
- for ( var key in map )
- ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
+ for ( var key in map ) {
+ var val = map[key];
+ ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
+ }
QUnit.jsDump.down();
return join( '{', ret, '}' );
},
node:function( node ) {
var open = QUnit.jsDump.HTML ? '&lt;' : '<',
close = QUnit.jsDump.HTML ? '&gt;' : '>';
-
+
var tag = node.nodeName.toLowerCase(),
ret = open + tag;
-
+
for ( var a in QUnit.jsDump.DOMAttrs ) {
var val = node[QUnit.jsDump.DOMAttrs[a]];
if ( val )
@@ -1253,8 +1390,8 @@ QUnit.jsDump = (function() {
},
functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
var l = fn.length;
- if ( !l ) return '';
-
+ if ( !l ) return '';
+
var args = Array(l);
while ( l-- )
args[l] = String.fromCharCode(97+l);//97 is 'a'
@@ -1302,6 +1439,21 @@ function getText( elems ) {
return ret;
};
+//from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/)
@@ -1311,35 +1463,38 @@ function getText( elems ) {
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
- *
+ *
* Usage: QUnit.diff(expected, actual)
- *
+ *
* QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
*/
QUnit.diff = (function() {
- function diff(o, n){
- var ns = new Object();
- var os = new Object();
-
+ function diff(o, n) {
+ var ns = {};
+ var os = {};
+
for (var i = 0; i < n.length; i++) {
- if (ns[n[i]] == null)
+ if (ns[n[i]] == null)
ns[n[i]] = {
- rows: new Array(),
+ rows: [],
o: null
};
ns[n[i]].rows.push(i);
}
-
+
for (var i = 0; i < o.length; i++) {
- if (os[o[i]] == null)
+ if (os[o[i]] == null)
os[o[i]] = {
- rows: new Array(),
+ rows: [],
n: null
};
os[o[i]].rows.push(i);
}
-
+
for (var i in ns) {
+ if ( !hasOwn.call( ns, i ) ) {
+ continue;
+ }
if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
n[ns[i].rows[0]] = {
text: n[ns[i].rows[0]],
@@ -1351,7 +1506,7 @@ QUnit.diff = (function() {
};
}
}
-
+
for (var i = 0; i < n.length - 1; i++) {
if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
n[i + 1] == o[n[i].row + 1]) {
@@ -1365,7 +1520,7 @@ QUnit.diff = (function() {
};
}
}
-
+
for (var i = n.length - 1; i > 0; i--) {
if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
n[i - 1] == o[n[i].row - 1]) {
@@ -1379,20 +1534,20 @@ QUnit.diff = (function() {
};
}
}
-
+
return {
o: o,
n: n
};
}
-
- return function(o, n){
+
+ return function(o, n) {
o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
var str = "";
-
+
var oSpace = o.match(/\s+/g);
if (oSpace == null) {
oSpace = [" "];
@@ -1407,7 +1562,7 @@ QUnit.diff = (function() {
else {
nSpace.push(" ");
}
-
+
if (out.n.length == 0) {
for (var i = 0; i < out.o.length; i++) {
str += '<del>' + out.o[i] + oSpace[i] + "</del>";
@@ -1419,14 +1574,14 @@ QUnit.diff = (function() {
str += '<del>' + out.o[n] + oSpace[n] + "</del>";
}
}
-
+
for (var i = 0; i < out.n.length; i++) {
if (out.n[i].text == null) {
str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
}
else {
var pre = "";
-
+
for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
}
@@ -1434,9 +1589,9 @@ QUnit.diff = (function() {
}
}
}
-
+
return str;
};
})();
-})(this); \ No newline at end of file
+})(this);
diff --git a/resources/jquery/jquery.spinner.css b/resources/jquery/jquery.spinner.css
new file mode 100644
index 00000000..150a51b4
--- /dev/null
+++ b/resources/jquery/jquery.spinner.css
@@ -0,0 +1,12 @@
+.mw-spinner {
+ /* @embed */
+ background: transparent url(images/spinner.gif);
+ height: 20px;
+ width: 20px;
+ display: inline-block;
+ vertical-align: middle;
+
+ /* IE < 8 Hacks */
+ zoom: 1;
+ *display: inline;
+} \ No newline at end of file
diff --git a/resources/jquery/jquery.spinner.js b/resources/jquery/jquery.spinner.js
new file mode 100644
index 00000000..87e45382
--- /dev/null
+++ b/resources/jquery/jquery.spinner.js
@@ -0,0 +1,44 @@
+/**
+ * jQuery spinner
+ *
+ * Simple jQuery plugin to create, inject and remove spinners.
+ */
+( function( $ ) {
+
+$.extend( {
+ /**
+ * Creates a spinner element.
+ *
+ * @param id {String} id of the spinner
+ * @return {jQuery} spinner
+ */
+ createSpinner: function( id ) {
+ return $( '<div>' ).attr( {
+ id: 'mw-spinner-' + id,
+ 'class': 'mw-spinner',
+ title: '...'
+ } );
+ },
+
+ /**
+ * Removes a spinner element.
+ *
+ * @param id {String}
+ * @return {jQuery} spinner
+ */
+ removeSpinner: function( id ) {
+ return $( '#mw-spinner-' + id ).remove();
+ }
+} );
+
+/**
+ * Injects a spinner after the elements in the jQuery collection.
+ *
+ * @param id String id of the spinner
+ * @return {jQuery}
+ */
+$.fn.injectSpinner = function( id ) {
+ return this.after( $.createSpinner( id ) );
+};
+
+} )( jQuery );
diff --git a/resources/jquery/jquery.suggestions.css b/resources/jquery/jquery.suggestions.css
index 74094c53..3cbdad22 100644
--- a/resources/jquery/jquery.suggestions.css
+++ b/resources/jquery/jquery.suggestions.css
@@ -3,9 +3,9 @@
.suggestions {
overflow: hidden;
position: absolute;
- top: 0px;
- left: 0px;
- width: 0px;
+ top: 0;
+ left: 0;
+ width: 0;
border: none;
z-index: 1099;
padding: 0;
@@ -17,7 +17,7 @@ html > body .suggestions {
}
.suggestions-special {
position: relative;
- background-color: Window;
+ background-color: white;
font-size: 0.8em;
cursor: pointer;
border: solid 1px #aaaaaa;
@@ -30,7 +30,6 @@ html > body .suggestions {
}
.suggestions-results {
background-color: white;
- background-color: Window;
font-size: 0.8em;
cursor: pointer;
border: solid 1px #aaaaaa;
@@ -39,7 +38,6 @@ html > body .suggestions {
}
.suggestions-result {
color: black;
- color: WindowText;
margin: 0;
line-height: 1.5em;
padding: 0.01em 0.25em;
@@ -47,9 +45,7 @@ html > body .suggestions {
}
.suggestions-result-current {
background-color: #4C59A6;
- background-color: Highlight;
color: white;
- color: HighlightText;
}
.suggestions-special .special-label {
font-size: 0.8em;
@@ -67,7 +63,6 @@ html > body .suggestions {
.suggestions-result-current .special-label,
.suggestions-result-current .special-query {
color: white;
- color: HighlightText;
}
.autoellipsis-matched,
.highlight {
diff --git a/resources/jquery/jquery.suggestions.js b/resources/jquery/jquery.suggestions.js
index 90cf53b2..55c30010 100644
--- a/resources/jquery/jquery.suggestions.js
+++ b/resources/jquery/jquery.suggestions.js
@@ -4,36 +4,36 @@
* Usage:
*
* Set options:
- * $('#textbox').suggestions( { option1: value1, option2: value2 } );
- * $('#textbox').suggestions( option, value );
+ * $( '#textbox' ).suggestions( { option1: value1, option2: value2 } );
+ * $( '#textbox' ).suggestions( option, value );
* Get option:
- * value = $('#textbox').suggestions( option );
+ * value = $( '#textbox' ).suggestions( option );
* Initialize:
- * $('#textbox').suggestions();
+ * $( '#textbox' ).suggestions();
*
* Options:
*
* fetch(query): Callback that should fetch suggestions and set the suggestions property. Executed in the context of the
- * textbox
- * Type: Function
+ * textbox
+ * Type: Function
* cancel: Callback function to call when any pending asynchronous suggestions fetches should be canceled.
- * Executed in the context of the textbox
+ * 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)
+ * Type: jQuery Object, Default: $(this)
* suggestions: Suggestions to display
- * Type: Array of strings
+ * Type: Array of strings
* maxRows: Maximum number of suggestions to display at one time
- * Type: Number, Range: 1 - 100, Default: 7
+ * 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
+ * Type: Number, Range: 0 - 1200, Default: 120
* 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
+ * 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
* positionFromLeft: Whether to position the suggestion box with the left attribute or the right
@@ -52,7 +52,7 @@ $.suggestions = {
if ( context.data.timerID != null ) {
clearTimeout( context.data.timerID );
}
- if ( typeof context.config.cancel == 'function' ) {
+ if ( $.isFunction( context.config.cancel ) ) {
context.config.cancel.call( context.data.$textbox );
}
},
@@ -66,7 +66,7 @@ $.suggestions = {
},
/**
* Ask the user-specified callback for new suggestions. Any previous delayed 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
+ * 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
*/
@@ -74,12 +74,12 @@ $.suggestions = {
// Only fetch if the value in the textbox changed and is not empty
// if the textbox is empty then clear the result div, but leave other settings intouched
function maybeFetch() {
- if ( context.data.$textbox.val().length == 0 ) {
+ if ( context.data.$textbox.val().length === 0 ) {
context.data.$container.hide();
context.data.prevText = '';
} else if ( context.data.$textbox.val() !== context.data.prevText ) {
- if ( typeof context.config.fetch == 'function' ) {
- context.data.prevText = context.data.$textbox.val();
+ if ( typeof context.config.fetch === 'function' ) {
+ context.data.prevText = context.data.$textbox.val();
context.config.fetch.call( context.data.$textbox, context.data.$textbox.val() );
}
}
@@ -99,7 +99,7 @@ $.suggestions = {
},
special: function( context ) {
// Allow custom rendering - but otherwise don't do any rendering
- if ( typeof context.config.special.render == 'function' ) {
+ if ( typeof context.config.special.render === 'function' ) {
// Wait for the browser to update the value
setTimeout( function() {
// Render special
@@ -126,8 +126,8 @@ $.suggestions = {
case 'suggestions':
context.config[property] = value;
// Update suggestions
- if ( typeof context.data !== 'undefined' ) {
- if ( context.data.$textbox.val().length == 0 ) {
+ if ( context.data !== undefined ) {
+ if ( context.data.$textbox.val().length === 0 ) {
// Hide the div when no suggestion exist
context.data.$container.hide();
} else {
@@ -135,17 +135,17 @@ $.suggestions = {
context.data.$container.show();
// Update the size and position of the list
var newCSS = {
- 'top': context.config.$region.offset().top + context.config.$region.outerHeight(),
- 'bottom': 'auto',
- 'width': context.config.$region.outerWidth(),
- 'height': 'auto'
+ top: context.config.$region.offset().top + context.config.$region.outerHeight(),
+ bottom: 'auto',
+ width: context.config.$region.outerWidth(),
+ height: 'auto'
};
if ( context.config.positionFromLeft ) {
- newCSS['left'] = context.config.$region.offset().left;
- newCSS['right'] = 'auto';
+ newCSS.left = context.config.$region.offset().left;
+ newCSS.right = 'auto';
} else {
- newCSS['left'] = 'auto';
- newCSS['right'] = $( 'body' ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
+ newCSS.left = 'auto';
+ newCSS.right = $( 'body' ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
}
context.data.$container.css( newCSS );
var $results = context.data.$container.children( '.suggestions-results' );
@@ -155,7 +155,7 @@ $.suggestions = {
var matchedText = null;
for ( var i = 0; i < context.config.suggestions.length; i++ ) {
var text = context.config.suggestions[i];
- var $result = $( '<div />' )
+ var $result = $( '<div>' )
.addClass( 'suggestions-result' )
.attr( 'rel', i )
.data( 'text', context.config.suggestions[i] )
@@ -167,18 +167,18 @@ $.suggestions = {
} )
.appendTo( $results );
// Allow custom rendering
- if ( typeof context.config.result.render == 'function' ) {
+ if ( typeof context.config.result.render === 'function' ) {
context.config.result.render.call( $result, context.config.suggestions[i] );
} else {
// Add <span> with text
if( context.config.highlightInput ) {
matchedText = context.data.prevText;
}
- $result.append( $( '<span />' )
+ $result.append( $( '<span>' )
.css( 'whiteSpace', 'nowrap' )
.text( text )
);
-
+
// Widen results box if needed
// New width is only calculated here, applied later
var $span = $result.children( 'span' );
@@ -223,25 +223,25 @@ $.suggestions = {
highlight: function( context, result, updateTextbox ) {
var selected = context.data.$container.find( '.suggestions-result-current' );
if ( !result.get || selected.get( 0 ) != result.get( 0 ) ) {
- if ( result == 'prev' ) {
+ if ( result === 'prev' ) {
if( selected.is( '.suggestions-special' ) ) {
- result = context.data.$container.find( '.suggestions-result:last' )
+ result = context.data.$container.find( '.suggestions-result:last' );
} else {
result = selected.prev();
- if ( selected.length == 0 ) {
+ if ( selected.length === 0 ) {
// we are at the beginning, so lets jump to the last item
- if ( context.data.$container.find( '.suggestions-special' ).html() != "" ) {
+ if ( context.data.$container.find( '.suggestions-special' ).html() !== '' ) {
result = context.data.$container.find( '.suggestions-special' );
} else {
result = context.data.$container.find( '.suggestions-results div:last' );
}
}
}
- } else if ( result == 'next' ) {
- if ( selected.length == 0 ) {
+ } else if ( result === 'next' ) {
+ if ( selected.length === 0 ) {
// No item selected, go to the first one
result = context.data.$container.find( '.suggestions-results div:first' );
- if ( result.length == 0 && context.data.$container.find( '.suggestions-special' ).html() != "" ) {
+ if ( result.length === 0 && context.data.$container.find( '.suggestions-special' ).html() !== '' ) {
// No suggestion exists, go to the special one directly
result = context.data.$container.find( '.suggestions-special' );
}
@@ -250,8 +250,8 @@ $.suggestions = {
if ( selected.is( '.suggestions-special' ) ) {
result = $( [] );
} else if (
- result.length == 0 &&
- context.data.$container.find( '.suggestions-special' ).html() != ""
+ result.length === 0 &&
+ context.data.$container.find( '.suggestions-special' ).html() !== ''
) {
// We were at the last item, jump to the specials!
result = context.data.$container.find( '.suggestions-special' );
@@ -262,7 +262,7 @@ $.suggestions = {
result.addClass( 'suggestions-result-current' );
}
if ( updateTextbox ) {
- if ( result.length == 0 || result.is( '.suggestions-special' ) ) {
+ if ( result.length === 0 || result.is( '.suggestions-special' ) ) {
$.suggestions.restore( context );
} else {
context.data.$textbox.val( result.data( 'text' ) );
@@ -278,8 +278,8 @@ $.suggestions = {
* @param key Integer Code of key pressed
*/
keypress: function( e, context, key ) {
- var wasVisible = context.data.$container.is( ':visible' );
- var preventDefault = false;
+ var wasVisible = context.data.$container.is( ':visible' ),
+ preventDefault = false;
switch ( key ) {
// Arrow down
case 40:
@@ -312,17 +312,17 @@ $.suggestions = {
context.data.$container.hide();
preventDefault = wasVisible;
selected = context.data.$container.find( '.suggestions-result-current' );
- if ( selected.size() == 0 || context.data.selectedWithMouse ) {
- // if nothing is selected OR if something was selected with the mouse,
+ if ( selected.length === 0 || context.data.selectedWithMouse ) {
+ // if nothing is selected OR if something was selected with the mouse,
// cancel any current requests and submit the form
$.suggestions.cancel( context );
context.config.$region.closest( 'form' ).submit();
} else if ( selected.is( '.suggestions-special' ) ) {
- if ( typeof context.config.special.select == 'function' ) {
+ if ( typeof context.config.special.select === 'function' ) {
context.config.special.select.call( selected, context.data.$textbox );
}
} else {
- if ( typeof context.config.result.select == 'function' ) {
+ if ( typeof context.config.result.select === 'function' ) {
$.suggestions.highlight( context, selected, true );
context.config.result.select.call( selected, context.data.$textbox );
} else {
@@ -341,17 +341,17 @@ $.suggestions = {
}
};
$.fn.suggestions = function() {
-
+
// Multi-context fields
var returnValue = null;
var args = arguments;
-
+
$(this).each( function() {
/* Construction / Loading */
-
+
var context = $(this).data( 'suggestions-context' );
- if ( typeof context == 'undefined' || context == null ) {
+ if ( context === undefined || context === null ) {
context = {
config: {
'fetch' : function() {},
@@ -369,17 +369,17 @@ $.fn.suggestions = function() {
}
};
}
-
+
/* API */
-
+
// Handle various calling styles
if ( args.length > 0 ) {
- if ( typeof args[0] == 'object' ) {
+ if ( typeof args[0] === 'object' ) {
// Apply set of properties
for ( var key in args[0] ) {
$.suggestions.configure( context, key, args[0][key] );
}
- } else if ( typeof args[0] == 'string' ) {
+ } else if ( typeof args[0] === 'string' ) {
if ( args.length > 1 ) {
// Set property values
$.suggestions.configure( context, args[0], args[1] );
@@ -389,10 +389,10 @@ $.fn.suggestions = function() {
}
}
}
-
+
/* Initialization */
-
- if ( typeof context.data == 'undefined' ) {
+
+ if ( context.data === undefined ) {
context.data = {
// ID of running timer
'timerID': null,
@@ -407,23 +407,23 @@ $.fn.suggestions = function() {
};
// Setup the css for positioning the results box
var newCSS = {
- 'top': Math.round( context.data.$textbox.offset().top + context.data.$textbox.outerHeight() ),
- 'width': context.data.$textbox.outerWidth(),
- 'display': 'none'
+ top: Math.round( context.data.$textbox.offset().top + context.data.$textbox.outerHeight() ),
+ width: context.data.$textbox.outerWidth(),
+ display: 'none'
};
if ( context.config.positionFromLeft ) {
- newCSS['left'] = context.config.$region.offset().left;
- newCSS['right'] = 'auto';
+ newCSS.left = context.config.$region.offset().left;
+ newCSS.right = 'auto';
} else {
- newCSS['left'] = 'auto';
- newCSS['right'] = $( 'body' ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
+ newCSS.left = 'auto';
+ newCSS.right = $( 'body' ).width() - ( context.config.$region.offset().left + context.config.$region.outerWidth() );
}
-
- context.data.$container = $( '<div />' )
+
+ context.data.$container = $( '<div>' )
.css( newCSS )
.addClass( 'suggestions' )
.append(
- $( '<div />' ).addClass( 'suggestions-results' )
+ $( '<div>' ).addClass( 'suggestions-results' )
// Can't use click() because the container div is hidden when the textbox loses focus. Instead,
// listen for a mousedown followed by a mouseup on the same div
.mousedown( function( e ) {
@@ -438,14 +438,14 @@ $.fn.suggestions = function() {
}
$.suggestions.highlight( context, $result, true );
context.data.$container.hide();
- if ( typeof context.config.result.select == 'function' ) {
+ if ( typeof context.config.result.select === 'function' ) {
context.config.result.select.call( $result, context.data.$textbox );
}
context.data.$textbox.focus();
} )
)
.append(
- $( '<div />' ).addClass( 'suggestions-special' )
+ $( '<div>' ).addClass( 'suggestions-special' )
// Can't use click() because the container div is hidden when the textbox loses focus. Instead,
// listen for a mousedown followed by a mouseup on the same div
.mousedown( function( e ) {
@@ -459,7 +459,7 @@ $.fn.suggestions = function() {
return;
}
context.data.$container.hide();
- if ( typeof context.config.special.select == 'function' ) {
+ if ( typeof context.config.special.select === 'function' ) {
context.config.special.select.call( $special, context.data.$textbox );
}
context.data.$textbox.focus();
@@ -477,9 +477,9 @@ $.fn.suggestions = function() {
.attr( 'autocomplete', 'off')
.keydown( function( e ) {
// Store key pressed to handle later
- context.data.keypressed = ( e.keyCode == undefined ) ? e.which : e.keyCode;
+ context.data.keypressed = ( e.keyCode === undefined ) ? e.which : e.keyCode;
context.data.keypressedCount = 0;
-
+
switch ( context.data.keypressed ) {
// This preventDefault logic is duplicated from
// $.suggestions.keypress(), which sucks
@@ -503,7 +503,7 @@ $.fn.suggestions = function() {
.keyup( function( e ) {
// Some browsers won't throw keypress() for arrow keys. If we got a keydown and a keyup without a
// keypress in between, solve it
- if ( context.data.keypressedCount == 0 ) {
+ if ( context.data.keypressedCount === 0 ) {
$.suggestions.keypress( e, context, context.data.keypressed );
}
} )
diff --git a/resources/jquery/jquery.tabIndex.js b/resources/jquery/jquery.tabIndex.js
index 33e06047..75731d7c 100644
--- a/resources/jquery/jquery.tabIndex.js
+++ b/resources/jquery/jquery.tabIndex.js
@@ -4,13 +4,13 @@
( function( $ ) {
/**
* Finds the lowerst tabindex in use within a selection
- *
+ *
* @return number Lowest tabindex on the page
*/
$.fn.firstTabIndex = function() {
var minTabIndex = null;
$(this).find( '[tabindex]' ).each( function() {
- var tabIndex = parseInt( $(this).attr( 'tabindex' ), 10 );
+ 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.
@@ -29,13 +29,13 @@ $.fn.firstTabIndex = function() {
/**
* Finds the highest tabindex in use within a selection
- *
+ *
* @return number Highest tabindex on the page
*/
$.fn.lastTabIndex = function() {
var maxTabIndex = null;
$(this).find( '[tabindex]' ).each( function() {
- var tabIndex = parseInt( $(this).attr( 'tabindex' ), 10 );
+ var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
// Initial value
if ( maxTabIndex === null ) {
diff --git a/resources/jquery/jquery.tablesorter.css b/resources/jquery/jquery.tablesorter.css
index 87719810..a88acc09 100644
--- a/resources/jquery/jquery.tablesorter.css
+++ b/resources/jquery/jquery.tablesorter.css
@@ -1,17 +1,17 @@
/* Table Sorting */
table.jquery-tablesorter th.headerSort {
/* @embed */
- background-image: url(images/sort_both.gif) !important;
+ background-image: url(images/sort_both.gif);
cursor: pointer;
- background-repeat: no-repeat !important;
- background-position: center right !important;
- padding-right: 21px !important;
+ background-repeat: no-repeat;
+ background-position: center right;
+ padding-right: 21px;
}
table.jquery-tablesorter th.headerSortUp {
/* @embed */
- background-image: url(images/sort_up.gif) !important;
+ background-image: url(images/sort_up.gif);
}
table.jquery-tablesorter th.headerSortDown {
/* @embed */
- background-image: url(images/sort_down.gif) !important;
+ background-image: url(images/sort_down.gif);
}
diff --git a/resources/jquery/jquery.tablesorter.js b/resources/jquery/jquery.tablesorter.js
index 5af27cc4..ea86b64e 100644
--- a/resources/jquery/jquery.tablesorter.js
+++ b/resources/jquery/jquery.tablesorter.js
@@ -8,8 +8,9 @@
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
- * @depends on mw.config (wgDigitTransformTable, wgMonthNames, wgMonthNamesShort,
+ * Depends on mw.config (wgDigitTransformTable, wgMonthNames, wgMonthNamesShort,
* wgDefaultDateFormat, wgContentLanguage)
+ * Uses 'tableSorterCollation' in mw.config (if available)
*/
/**
*
@@ -240,7 +241,7 @@
}
$thead.append( this );
} );
- $table.children('tbody').before( $thead );
+ $table.find(' > tbody:first').before( $thead );
}
if( !$table.get(0).tFoot ) {
var $tfoot = $( '<tfoot>' );
@@ -259,9 +260,9 @@
var maxSeen = 0,
longest,
realCellIndex = 0,
- $tableHeaders = $( 'thead:eq(0) tr', table );
+ $tableHeaders = $( 'thead:eq(0) > tr', table );
if ( $tableHeaders.length > 1 ) {
- $tableHeaders.each(function() {
+ $tableHeaders.each( function() {
if ( this.cells.length > maxSeen ) {
maxSeen = this.cells.length;
longest = this;
@@ -594,7 +595,7 @@
if ( firstTime ) {
firstTime = false;
-
+
// Legacy fix of .sortbottoms
// Wrap them inside inside a tfoot (because that's what they actually want to be) &
// and put the <tfoot> at the end of the <table>
@@ -604,10 +605,10 @@
if ( $tfoot.length ) {
$tfoot.eq(0).prepend( $sortbottoms );
} else {
- $table.append( $( '<tfoot>' ).append( $sortbottoms ) )
+ $table.append( $( '<tfoot>' ).append( $sortbottoms ) );
}
}
-
+
explodeRowspans( $table );
// try to auto detect column type, and store in tables config
table.config.parsers = buildParserCache( table, $headers );
diff --git a/resources/jquery/jquery.textSelection.js b/resources/jquery/jquery.textSelection.js
index f3d1a146..91b6e75d 100644
--- a/resources/jquery/jquery.textSelection.js
+++ b/resources/jquery/jquery.textSelection.js
@@ -87,7 +87,7 @@ getSelection: function() {
encapsulateSelection: function( options ) {
return this.each( function() {
var pre = options.pre, post = options.post;
-
+
/**
* Check if the selected text is the same as the insert text
*/
@@ -110,10 +110,10 @@ encapsulateSelection: function( options ) {
}
}
}
-
+
/**
* Do the splitlines stuff.
- *
+ *
* Wrap each line of the selected text with pre and post
*/
function doSplitLines( selText, pre, post ) {
@@ -127,12 +127,62 @@ encapsulateSelection: function( options ) {
}
return insertText;
}
-
+
var isSample = false;
if ( this.style.display == 'none' ) {
// Do nothing
+ } else if ( document.selection && document.selection.createRange ) {
+ // IE
+
+ // Note that IE9 will trigger the next section unless we check this first.
+ // See bug 35201.
+
+ activateElementOnIE( this );
+ if ( context ) {
+ context.fn.restoreCursorAndScrollTop();
+ }
+ if ( options.selectionStart !== undefined ) {
+ $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
+ }
+
+ var selText = $(this).textSelection( 'getSelection' );
+ var scrollTop = this.scrollTop;
+ var range = document.selection.createRange();
+
+ checkSelectedText();
+ var insertText = pre + selText + post;
+ if ( options.splitlines ) {
+ insertText = doSplitLines( selText, pre, post );
+ }
+ if ( options.ownline && range.moveStart ) {
+ var range2 = document.selection.createRange();
+ range2.collapse();
+ range2.moveStart( 'character', -1 );
+ // FIXME: Which check is correct?
+ if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
+ insertText = "\n" + insertText;
+ pre += "\n";
+ }
+ var range3 = document.selection.createRange();
+ range3.collapse( false );
+ range3.moveEnd( 'character', 1 );
+ if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
+ insertText += "\n";
+ post += "\n";
+ }
+ }
+
+ range.text = insertText;
+ if ( isSample && options.selectPeri && range.moveStart ) {
+ range.moveStart( 'character', - post.length - selText.length );
+ range.moveEnd( 'character', - post.length );
+ }
+ range.select();
+ // Restore the scroll position
+ this.scrollTop = scrollTop;
} else if ( this.selectionStart || this.selectionStart == '0' ) {
// Mozilla/Opera
+
$(this).focus();
if ( options.selectionStart !== undefined ) {
$(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
@@ -143,7 +193,6 @@ encapsulateSelection: function( options ) {
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
checkSelectedText();
-
if ( options.selectionStart !== undefined
&& endPos - startPos != options.selectionEnd - options.selectionStart )
{
@@ -158,11 +207,13 @@ encapsulateSelection: function( options ) {
insertText = doSplitLines( selText, pre, post );
}
if ( options.ownline ) {
- if ( startPos != 0 && this.value.charAt( startPos - 1 ) != "\n" ) {
+ if ( startPos != 0 && this.value.charAt( startPos - 1 ) != "\n" && this.value.charAt( startPos - 1 ) != "\r" ) {
insertText = "\n" + insertText;
+ pre += "\n";
}
- if ( this.value.charAt( endPos ) != "\n" ) {
+ if ( this.value.charAt( endPos ) != "\n" && this.value.charAt( endPos ) != "\r" ) {
insertText += "\n";
+ post += "\n";
}
}
this.value = this.value.substring( 0, startPos ) + insertText +
@@ -181,49 +232,6 @@ encapsulateSelection: function( options ) {
this.selectionStart = startPos + insertText.length;
this.selectionEnd = this.selectionStart;
}
- } else if ( document.selection && document.selection.createRange ) {
- // IE
- activateElementOnIE( this );
- if ( context ) {
- context.fn.restoreCursorAndScrollTop();
- }
- if ( options.selectionStart !== undefined ) {
- $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
- }
-
- var selText = $(this).textSelection( 'getSelection' );
- var scrollTop = this.scrollTop;
- var range = document.selection.createRange();
-
- checkSelectedText();
- var insertText = pre + selText + post;
- if ( options.splitlines ) {
- insertText = doSplitLines( selText, pre, post );
- }
- if ( options.ownline && range.moveStart ) {
- var range2 = document.selection.createRange();
- range2.collapse();
- range2.moveStart( 'character', -1 );
- // FIXME: Which check is correct?
- if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
- insertText = "\n" + insertText;
- }
- var range3 = document.selection.createRange();
- range3.collapse( false );
- range3.moveEnd( 'character', 1 );
- if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
- insertText += "\n";
- }
- }
-
- range.text = insertText;
- if ( isSample && options.selectPeri && range.moveStart ) {
- range.moveStart( 'character', - post.length - selText.length );
- range.moveEnd( 'character', - post.length );
- }
- range.select();
- // Restore the scroll position
- this.scrollTop = scrollTop;
}
$(this).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
options.replace, options.spitlines ] );
@@ -349,10 +357,10 @@ setSelection: function( options ) {
var length = this.value.length;
// IE doesn't count \n when computing the offset, so we won't either
var newLines = this.value.match( /\n/g );
- if ( newLines) length = length - newLines.length;
+ if ( newLines ) length = length - newLines.length;
selection.moveStart( 'character', options.start );
selection.moveEnd( 'character', -length + options.end );
-
+
// This line can cause an error under certain circumstances (textarea empty, no selection)
// Silence that error
try {
@@ -503,14 +511,14 @@ scrollToCaretPosition: function( options ) {
}
var context = $(this).data( 'wikiEditor-context' );
var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined';
-
+
// IE selection restore voodoo
var needSave = false;
if ( hasIframe && context.savedSelection !== null ) {
context.fn.restoreSelection();
needSave = true;
}
- retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
+ var retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
if ( hasIframe && needSave ) {
context.fn.saveSelection();
}
diff --git a/resources/jquery/jquery.validate.js b/resources/jquery/jquery.validate.js
new file mode 100644
index 00000000..72296a61
--- /dev/null
+++ b/resources/jquery/jquery.validate.js
@@ -0,0 +1,1166 @@
+/**
+ * jQuery Validation Plugin 1.8.1
+ *
+ * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
+ * http://docs.jquery.com/Plugins/Validation
+ *
+ * Copyright (c) 2006 - 2011 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+(function($) {
+
+$.extend($.fn, {
+ // http://docs.jquery.com/Plugins/Validation/validate
+ validate: function( options ) {
+
+ // if nothing is selected, return nothing; can't chain anyway
+ if (!this.length) {
+ options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
+ return;
+ }
+
+ // check if a validator for this form was already created
+ var validator = $.data(this[0], 'validator');
+ if ( validator ) {
+ return validator;
+ }
+
+ validator = new $.validator( options, this[0] );
+ $.data(this[0], 'validator', validator);
+
+ if ( validator.settings.onsubmit ) {
+
+ // allow suppresing validation by adding a cancel class to the submit button
+ this.find("input, button").filter(".cancel").click(function() {
+ validator.cancelSubmit = true;
+ });
+
+ // when a submitHandler is used, capture the submitting button
+ if (validator.settings.submitHandler) {
+ this.find("input, button").filter(":submit").click(function() {
+ validator.submitButton = this;
+ });
+ }
+
+ // validate the form on submit
+ this.submit( function( event ) {
+ if ( validator.settings.debug )
+ // prevent form submit to be able to see console output
+ event.preventDefault();
+
+ function handle() {
+ if ( validator.settings.submitHandler ) {
+ if (validator.submitButton) {
+ // insert a hidden input as a replacement for the missing submit button
+ var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
+ }
+ validator.settings.submitHandler.call( validator, validator.currentForm );
+ if (validator.submitButton) {
+ // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
+ hidden.remove();
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // prevent submit for invalid forms or custom submit handlers
+ if ( validator.cancelSubmit ) {
+ validator.cancelSubmit = false;
+ return handle();
+ }
+ if ( validator.form() ) {
+ if ( validator.pendingRequest ) {
+ validator.formSubmitted = true;
+ return false;
+ }
+ return handle();
+ } else {
+ validator.focusInvalid();
+ return false;
+ }
+ });
+ }
+
+ return validator;
+ },
+ // http://docs.jquery.com/Plugins/Validation/valid
+ valid: function() {
+ if ( $(this[0]).is('form')) {
+ return this.validate().form();
+ } else {
+ var valid = true;
+ var validator = $(this[0].form).validate();
+ this.each(function() {
+ valid &= validator.element(this);
+ });
+ return valid;
+ }
+ },
+ // attributes: space seperated list of attributes to retrieve and remove
+ removeAttrs: function(attributes) {
+ var result = {},
+ $element = this;
+ $.each(attributes.split(/\s/), function(index, value) {
+ result[value] = $element.attr(value);
+ $element.removeAttr(value);
+ });
+ return result;
+ },
+ // http://docs.jquery.com/Plugins/Validation/rules
+ rules: function(command, argument) {
+ var element = this[0];
+
+ if (command) {
+ var settings = $.data(element.form, 'validator').settings;
+ var staticRules = settings.rules;
+ var existingRules = $.validator.staticRules(element);
+ switch(command) {
+ case "add":
+ $.extend(existingRules, $.validator.normalizeRule(argument));
+ staticRules[element.name] = existingRules;
+ if (argument.messages)
+ settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
+ break;
+ case "remove":
+ if (!argument) {
+ delete staticRules[element.name];
+ return existingRules;
+ }
+ var filtered = {};
+ $.each(argument.split(/\s/), function(index, method) {
+ filtered[method] = existingRules[method];
+ delete existingRules[method];
+ });
+ return filtered;
+ }
+ }
+
+ var data = $.validator.normalizeRules(
+ $.extend(
+ {},
+ $.validator.metadataRules(element),
+ $.validator.classRules(element),
+ $.validator.attributeRules(element),
+ $.validator.staticRules(element)
+ ), element);
+
+ // make sure required is at front
+ if (data.required) {
+ var param = data.required;
+ delete data.required;
+ data = $.extend({required: param}, data);
+ }
+
+ return data;
+ }
+});
+
+// Custom selectors
+$.extend($.expr[":"], {
+ // http://docs.jquery.com/Plugins/Validation/blank
+ blank: function(a) {return !$.trim("" + a.value);},
+ // http://docs.jquery.com/Plugins/Validation/filled
+ filled: function(a) {return !!$.trim("" + a.value);},
+ // http://docs.jquery.com/Plugins/Validation/unchecked
+ unchecked: function(a) {return !a.checked;}
+});
+
+// constructor for validator
+$.validator = function( options, form ) {
+ this.settings = $.extend( true, {}, $.validator.defaults, options );
+ this.currentForm = form;
+ this.init();
+};
+
+$.validator.format = function(source, params) {
+ if ( arguments.length == 1 )
+ return function() {
+ var args = $.makeArray(arguments);
+ args.unshift(source);
+ return $.validator.format.apply( this, args );
+ };
+ if ( arguments.length > 2 && params.constructor != Array ) {
+ params = $.makeArray(arguments).slice(1);
+ }
+ if ( params.constructor != Array ) {
+ params = [ params ];
+ }
+ $.each(params, function(i, n) {
+ source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
+ });
+ return source;
+};
+
+$.extend($.validator, {
+
+ defaults: {
+ messages: {},
+ groups: {},
+ rules: {},
+ errorClass: "error",
+ validClass: "valid",
+ errorElement: "label",
+ focusInvalid: true,
+ errorContainer: $( [] ),
+ errorLabelContainer: $( [] ),
+ onsubmit: true,
+ ignore: [],
+ ignoreTitle: false,
+ onfocusin: function(element) {
+ this.lastActive = element;
+
+ // hide error label and remove error class on focus if enabled
+ if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
+ this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
+ this.addWrapper(this.errorsFor(element)).hide();
+ }
+ },
+ onfocusout: function(element) {
+ if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
+ this.element(element);
+ }
+ },
+ onkeyup: function(element) {
+ if ( element.name in this.submitted || element == this.lastElement ) {
+ this.element(element);
+ }
+ },
+ onclick: function(element) {
+ // click on selects, radiobuttons and checkboxes
+ if ( element.name in this.submitted )
+ this.element(element);
+ // or option elements, check parent select in that case
+ else if (element.parentNode.name in this.submitted)
+ this.element(element.parentNode);
+ },
+ highlight: function(element, errorClass, validClass) {
+ if (element.type === 'radio') {
+ this.findByName(element.name).addClass(errorClass).removeClass(validClass);
+ } else {
+ $(element).addClass(errorClass).removeClass(validClass);
+ }
+ },
+ unhighlight: function(element, errorClass, validClass) {
+ if (element.type === 'radio') {
+ this.findByName(element.name).removeClass(errorClass).addClass(validClass);
+ } else {
+ $(element).removeClass(errorClass).addClass(validClass);
+ }
+ }
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
+ setDefaults: function(settings) {
+ $.extend( $.validator.defaults, settings );
+ },
+
+ messages: {
+ required: "This field is required.",
+ remote: "Please fix this field.",
+ email: "Please enter a valid email address.",
+ url: "Please enter a valid URL.",
+ date: "Please enter a valid date.",
+ dateISO: "Please enter a valid date (ISO).",
+ number: "Please enter a valid number.",
+ digits: "Please enter only digits.",
+ creditcard: "Please enter a valid credit card number.",
+ equalTo: "Please enter the same value again.",
+ accept: "Please enter a value with a valid extension.",
+ maxlength: $.validator.format("Please enter no more than {0} characters."),
+ minlength: $.validator.format("Please enter at least {0} characters."),
+ rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
+ range: $.validator.format("Please enter a value between {0} and {1}."),
+ max: $.validator.format("Please enter a value less than or equal to {0}."),
+ min: $.validator.format("Please enter a value greater than or equal to {0}.")
+ },
+
+ autoCreateRanges: false,
+
+ prototype: {
+
+ init: function() {
+ this.labelContainer = $(this.settings.errorLabelContainer);
+ this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
+ this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
+ this.submitted = {};
+ this.valueCache = {};
+ this.pendingRequest = 0;
+ this.pending = {};
+ this.invalid = {};
+ this.reset();
+
+ var groups = (this.groups = {});
+ $.each(this.settings.groups, function(key, value) {
+ $.each(value.split(/\s/), function(index, name) {
+ groups[name] = key;
+ });
+ });
+ var rules = this.settings.rules;
+ $.each(rules, function(key, value) {
+ rules[key] = $.validator.normalizeRule(value);
+ });
+
+ function delegate(event) {
+ var validator = $.data(this[0].form, "validator"),
+ eventType = "on" + event.type.replace(/^validate/, "");
+ validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] );
+ }
+ $(this.currentForm)
+ .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
+ .validateDelegate(":radio, :checkbox, select, option", "click", delegate);
+
+ if (this.settings.invalidHandler)
+ $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/form
+ form: function() {
+ this.checkForm();
+ $.extend(this.submitted, this.errorMap);
+ this.invalid = $.extend({}, this.errorMap);
+ if (!this.valid())
+ $(this.currentForm).triggerHandler("invalid-form", [this]);
+ this.showErrors();
+ return this.valid();
+ },
+
+ checkForm: function() {
+ this.prepareForm();
+ for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
+ this.check( elements[i] );
+ }
+ return this.valid();
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/element
+ element: function( element ) {
+ element = this.clean( element );
+ this.lastElement = element;
+ this.prepareElement( element );
+ this.currentElements = $(element);
+ var result = this.check( element );
+ if ( result ) {
+ delete this.invalid[element.name];
+ } else {
+ this.invalid[element.name] = true;
+ }
+ if ( !this.numberOfInvalids() ) {
+ // Hide error containers on last error
+ this.toHide = this.toHide.add( this.containers );
+ }
+ this.showErrors();
+ return result;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
+ showErrors: function(errors) {
+ if(errors) {
+ // add items to error list and map
+ $.extend( this.errorMap, errors );
+ this.errorList = [];
+ for ( var name in errors ) {
+ this.errorList.push({
+ message: errors[name],
+ element: this.findByName(name)[0]
+ });
+ }
+ // remove items from success list
+ this.successList = $.grep( this.successList, function(element) {
+ return !(element.name in errors);
+ });
+ }
+ this.settings.showErrors
+ ? this.settings.showErrors.call( this, this.errorMap, this.errorList )
+ : this.defaultShowErrors();
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
+ resetForm: function() {
+ if ( $.fn.resetForm )
+ $( this.currentForm ).resetForm();
+ this.submitted = {};
+ this.prepareForm();
+ this.hideErrors();
+ this.elements().removeClass( this.settings.errorClass );
+ },
+
+ numberOfInvalids: function() {
+ return this.objectLength(this.invalid);
+ },
+
+ objectLength: function( obj ) {
+ var count = 0;
+ for ( var i in obj )
+ count++;
+ return count;
+ },
+
+ hideErrors: function() {
+ this.addWrapper( this.toHide ).hide();
+ },
+
+ valid: function() {
+ return this.size() == 0;
+ },
+
+ size: function() {
+ return this.errorList.length;
+ },
+
+ focusInvalid: function() {
+ if( this.settings.focusInvalid ) {
+ try {
+ $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
+ .filter(":visible")
+ .focus()
+ // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
+ .trigger("focusin");
+ } catch(e) {
+ // ignore IE throwing errors when focusing hidden elements
+ }
+ }
+ },
+
+ findLastActive: function() {
+ var lastActive = this.lastActive;
+ return lastActive && $.grep(this.errorList, function(n) {
+ return n.element.name == lastActive.name;
+ }).length == 1 && lastActive;
+ },
+
+ elements: function() {
+ var validator = this,
+ rulesCache = {};
+
+ // select all valid inputs inside the form (no submit or reset buttons)
+ return $(this.currentForm)
+ .find("input, select, textarea")
+ .not(":submit, :reset, :image, [disabled]")
+ .not( this.settings.ignore )
+ .filter(function() {
+ !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
+
+ // select only the first element for each name, and only those with rules specified
+ if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
+ return false;
+
+ rulesCache[this.name] = true;
+ return true;
+ });
+ },
+
+ clean: function( selector ) {
+ return $( selector )[0];
+ },
+
+ errors: function() {
+ return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
+ },
+
+ reset: function() {
+ this.successList = [];
+ this.errorList = [];
+ this.errorMap = {};
+ this.toShow = $([]);
+ this.toHide = $([]);
+ this.currentElements = $([]);
+ },
+
+ prepareForm: function() {
+ this.reset();
+ this.toHide = this.errors().add( this.containers );
+ },
+
+ prepareElement: function( element ) {
+ this.reset();
+ this.toHide = this.errorsFor(element);
+ },
+
+ check: function( element ) {
+ element = this.clean( element );
+
+ // if radio/checkbox, validate first element in group instead
+ if (this.checkable(element)) {
+ element = this.findByName( element.name ).not(this.settings.ignore)[0];
+ }
+
+ var rules = $(element).rules();
+ var dependencyMismatch = false;
+ for (var method in rules ) {
+ var rule = { method: method, parameters: rules[method] };
+ try {
+ var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
+
+ // if a method indicates that the field is optional and therefore valid,
+ // don't mark it as valid when there are no other rules
+ if ( result == "dependency-mismatch" ) {
+ dependencyMismatch = true;
+ continue;
+ }
+ dependencyMismatch = false;
+
+ if ( result == "pending" ) {
+ this.toHide = this.toHide.not( this.errorsFor(element) );
+ return;
+ }
+
+ if( !result ) {
+ this.formatAndAdd( element, rule );
+ return false;
+ }
+ } catch(e) {
+ this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
+ + ", check the '" + rule.method + "' method", e);
+ throw e;
+ }
+ }
+ if (dependencyMismatch)
+ return;
+ if ( this.objectLength(rules) )
+ this.successList.push(element);
+ return true;
+ },
+
+ // return the custom message for the given element and validation method
+ // specified in the element's "messages" metadata
+ customMetaMessage: function(element, method) {
+ if (!$.metadata)
+ return;
+
+ var meta = this.settings.meta
+ ? $(element).metadata()[this.settings.meta]
+ : $(element).metadata();
+
+ return meta && meta.messages && meta.messages[method];
+ },
+
+ // return the custom message for the given element name and validation method
+ customMessage: function( name, method ) {
+ var m = this.settings.messages[name];
+ return m && (m.constructor == String
+ ? m
+ : m[method]);
+ },
+
+ // return the first defined argument, allowing empty strings
+ findDefined: function() {
+ for(var i = 0; i < arguments.length; i++) {
+ if (arguments[i] !== undefined)
+ return arguments[i];
+ }
+ return undefined;
+ },
+
+ defaultMessage: function( element, method) {
+ return this.findDefined(
+ this.customMessage( element.name, method ),
+ this.customMetaMessage( element, method ),
+ // title is never undefined, so handle empty string as undefined
+ !this.settings.ignoreTitle && element.title || undefined,
+ $.validator.messages[method],
+ "<strong>Warning: No message defined for " + element.name + "</strong>"
+ );
+ },
+
+ formatAndAdd: function( element, rule ) {
+ var message = this.defaultMessage( element, rule.method ),
+ theregex = /\$?\{(\d+)\}/g;
+ if ( typeof message == "function" ) {
+ message = message.call(this, rule.parameters, element);
+ } else if (theregex.test(message)) {
+ message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
+ }
+ this.errorList.push({
+ message: message,
+ element: element
+ });
+
+ this.errorMap[element.name] = message;
+ this.submitted[element.name] = message;
+ },
+
+ addWrapper: function(toToggle) {
+ if ( this.settings.wrapper )
+ toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
+ return toToggle;
+ },
+
+ defaultShowErrors: function() {
+ for ( var i = 0; this.errorList[i]; i++ ) {
+ var error = this.errorList[i];
+ this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
+ this.showLabel( error.element, error.message );
+ }
+ if( this.errorList.length ) {
+ this.toShow = this.toShow.add( this.containers );
+ }
+ if (this.settings.success) {
+ for ( var i = 0; this.successList[i]; i++ ) {
+ this.showLabel( this.successList[i] );
+ }
+ }
+ if (this.settings.unhighlight) {
+ for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
+ this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
+ }
+ }
+ this.toHide = this.toHide.not( this.toShow );
+ this.hideErrors();
+ this.addWrapper( this.toShow ).show();
+ },
+
+ validElements: function() {
+ return this.currentElements.not(this.invalidElements());
+ },
+
+ invalidElements: function() {
+ return $(this.errorList).map(function() {
+ return this.element;
+ });
+ },
+
+ showLabel: function(element, message) {
+ var label = this.errorsFor( element );
+ if ( label.length ) {
+ // refresh error/success class
+ label.removeClass().addClass( this.settings.errorClass );
+
+ // check if we have a generated label, replace the message then
+ label.attr("generated") && label.html(message);
+ } else {
+ // create label
+ label = $("<" + this.settings.errorElement + "/>")
+ .attr({"for": this.idOrName(element), generated: true})
+ .addClass(this.settings.errorClass)
+ .html(message || "");
+ if ( this.settings.wrapper ) {
+ // make sure the element is visible, even in IE
+ // actually showing the wrapped element is handled elsewhere
+ label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
+ }
+ if ( !this.labelContainer.append(label).length )
+ this.settings.errorPlacement
+ ? this.settings.errorPlacement(label, $(element) )
+ : label.insertAfter(element);
+ }
+ if ( !message && this.settings.success ) {
+ label.text("");
+ typeof this.settings.success == "string"
+ ? label.addClass( this.settings.success )
+ : this.settings.success( label );
+ }
+ this.toShow = this.toShow.add(label);
+ },
+
+ errorsFor: function(element) {
+ var name = this.idOrName(element);
+ return this.errors().filter(function() {
+ return $(this).attr('for') == name;
+ });
+ },
+
+ idOrName: function(element) {
+ return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
+ },
+
+ checkable: function( element ) {
+ return /radio|checkbox/i.test(element.type);
+ },
+
+ findByName: function( name ) {
+ // select by name and filter by form for performance over form.find("[name=...]")
+ var form = this.currentForm;
+ return $(document.getElementsByName(name)).map(function(index, element) {
+ return element.form == form && element.name == name && element || null;
+ });
+ },
+
+ getLength: function(value, element) {
+ switch( element.nodeName.toLowerCase() ) {
+ case 'select':
+ return $("option:selected", element).length;
+ case 'input':
+ if( this.checkable( element) )
+ return this.findByName(element.name).filter(':checked').length;
+ }
+ return value.length;
+ },
+
+ depend: function(param, element) {
+ return this.dependTypes[typeof param]
+ ? this.dependTypes[typeof param](param, element)
+ : true;
+ },
+
+ dependTypes: {
+ "boolean": function(param, element) {
+ return param;
+ },
+ "string": function(param, element) {
+ return !!$(param, element.form).length;
+ },
+ "function": function(param, element) {
+ return param(element);
+ }
+ },
+
+ optional: function(element) {
+ return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
+ },
+
+ startRequest: function(element) {
+ if (!this.pending[element.name]) {
+ this.pendingRequest++;
+ this.pending[element.name] = true;
+ }
+ },
+
+ stopRequest: function(element, valid) {
+ this.pendingRequest--;
+ // sometimes synchronization fails, make sure pendingRequest is never < 0
+ if (this.pendingRequest < 0)
+ this.pendingRequest = 0;
+ delete this.pending[element.name];
+ if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
+ $(this.currentForm).submit();
+ this.formSubmitted = false;
+ } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
+ $(this.currentForm).triggerHandler("invalid-form", [this]);
+ this.formSubmitted = false;
+ }
+ },
+
+ previousValue: function(element) {
+ return $.data(element, "previousValue") || $.data(element, "previousValue", {
+ old: null,
+ valid: true,
+ message: this.defaultMessage( element, "remote" )
+ });
+ }
+
+ },
+
+ classRuleSettings: {
+ required: {required: true},
+ email: {email: true},
+ url: {url: true},
+ date: {date: true},
+ dateISO: {dateISO: true},
+ dateDE: {dateDE: true},
+ number: {number: true},
+ numberDE: {numberDE: true},
+ digits: {digits: true},
+ creditcard: {creditcard: true}
+ },
+
+ addClassRules: function(className, rules) {
+ className.constructor == String ?
+ this.classRuleSettings[className] = rules :
+ $.extend(this.classRuleSettings, className);
+ },
+
+ classRules: function(element) {
+ var rules = {};
+ var classes = $(element).attr('class');
+ classes && $.each(classes.split(' '), function() {
+ if (this in $.validator.classRuleSettings) {
+ $.extend(rules, $.validator.classRuleSettings[this]);
+ }
+ });
+ return rules;
+ },
+
+ attributeRules: function(element) {
+ var rules = {};
+ var $element = $(element);
+
+ for (var method in $.validator.methods) {
+ var value = $element.attr(method);
+ if (value) {
+ rules[method] = value;
+ }
+ }
+
+ // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
+ if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
+ delete rules.maxlength;
+ }
+
+ return rules;
+ },
+
+ metadataRules: function(element) {
+ if (!$.metadata) return {};
+
+ var meta = $.data(element.form, 'validator').settings.meta;
+ return meta ?
+ $(element).metadata()[meta] :
+ $(element).metadata();
+ },
+
+ staticRules: function(element) {
+ var rules = {};
+ var validator = $.data(element.form, 'validator');
+ if (validator.settings.rules) {
+ rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
+ }
+ return rules;
+ },
+
+ normalizeRules: function(rules, element) {
+ // handle dependency check
+ $.each(rules, function(prop, val) {
+ // ignore rule when param is explicitly false, eg. required:false
+ if (val === false) {
+ delete rules[prop];
+ return;
+ }
+ if (val.param || val.depends) {
+ var keepRule = true;
+ switch (typeof val.depends) {
+ case "string":
+ keepRule = !!$(val.depends, element.form).length;
+ break;
+ case "function":
+ keepRule = val.depends.call(element, element);
+ break;
+ }
+ if (keepRule) {
+ rules[prop] = val.param !== undefined ? val.param : true;
+ } else {
+ delete rules[prop];
+ }
+ }
+ });
+
+ // evaluate parameters
+ $.each(rules, function(rule, parameter) {
+ rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
+ });
+
+ // clean number parameters
+ $.each(['minlength', 'maxlength', 'min', 'max'], function() {
+ if (rules[this]) {
+ rules[this] = Number(rules[this]);
+ }
+ });
+ $.each(['rangelength', 'range'], function() {
+ if (rules[this]) {
+ rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
+ }
+ });
+
+ if ($.validator.autoCreateRanges) {
+ // auto-create ranges
+ if (rules.min && rules.max) {
+ rules.range = [rules.min, rules.max];
+ delete rules.min;
+ delete rules.max;
+ }
+ if (rules.minlength && rules.maxlength) {
+ rules.rangelength = [rules.minlength, rules.maxlength];
+ delete rules.minlength;
+ delete rules.maxlength;
+ }
+ }
+
+ // To support custom messages in metadata ignore rule methods titled "messages"
+ if (rules.messages) {
+ delete rules.messages;
+ }
+
+ return rules;
+ },
+
+ // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
+ normalizeRule: function(data) {
+ if( typeof data == "string" ) {
+ var transformed = {};
+ $.each(data.split(/\s/), function() {
+ transformed[this] = true;
+ });
+ data = transformed;
+ }
+ return data;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
+ addMethod: function(name, method, message) {
+ $.validator.methods[name] = method;
+ $.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
+ if (method.length < 3) {
+ $.validator.addClassRules(name, $.validator.normalizeRule(name));
+ }
+ },
+
+ methods: {
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/required
+ required: function(value, element, param) {
+ // check if dependency is met
+ if ( !this.depend(param, element) )
+ return "dependency-mismatch";
+ switch( element.nodeName.toLowerCase() ) {
+ case 'select':
+ // could be an array for select-multiple or a string, both are fine this way
+ var val = $(element).val();
+ return val && val.length > 0;
+ case 'input':
+ if ( this.checkable(element) )
+ return this.getLength(value, element) > 0;
+ default:
+ return $.trim(value).length > 0;
+ }
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/remote
+ remote: function(value, element, param) {
+ if ( this.optional(element) )
+ return "dependency-mismatch";
+
+ var previous = this.previousValue(element);
+ if (!this.settings.messages[element.name] )
+ this.settings.messages[element.name] = {};
+ previous.originalMessage = this.settings.messages[element.name].remote;
+ this.settings.messages[element.name].remote = previous.message;
+
+ param = typeof param == "string" && {url:param} || param;
+
+ if ( this.pending[element.name] ) {
+ return "pending";
+ }
+ if ( previous.old === value ) {
+ return previous.valid;
+ }
+
+ previous.old = value;
+ var validator = this;
+ this.startRequest(element);
+ var data = {};
+ data[element.name] = value;
+ $.ajax($.extend(true, {
+ url: param,
+ mode: "abort",
+ port: "validate" + element.name,
+ dataType: "json",
+ data: data,
+ success: function(response) {
+ validator.settings.messages[element.name].remote = previous.originalMessage;
+ var valid = response === true;
+ if ( valid ) {
+ var submitted = validator.formSubmitted;
+ validator.prepareElement(element);
+ validator.formSubmitted = submitted;
+ validator.successList.push(element);
+ validator.showErrors();
+ } else {
+ var errors = {};
+ var message = response || validator.defaultMessage( element, "remote" );
+ errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
+ validator.showErrors(errors);
+ }
+ previous.valid = valid;
+ validator.stopRequest(element, valid);
+ }
+ }, param));
+ return "pending";
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/minlength
+ minlength: function(value, element, param) {
+ return this.optional(element) || this.getLength($.trim(value), element) >= param;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
+ maxlength: function(value, element, param) {
+ return this.optional(element) || this.getLength($.trim(value), element) <= param;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
+ rangelength: function(value, element, param) {
+ var length = this.getLength($.trim(value), element);
+ return this.optional(element) || ( length >= param[0] && length <= param[1] );
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/min
+ min: function( value, element, param ) {
+ return this.optional(element) || value >= param;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/max
+ max: function( value, element, param ) {
+ return this.optional(element) || value <= param;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/range
+ range: function( value, element, param ) {
+ return this.optional(element) || ( value >= param[0] && value <= param[1] );
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/email
+ email: function(value, element) {
+ // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
+ return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/url
+ url: function(value, element) {
+ // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
+ return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/date
+ date: function(value, element) {
+ return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
+ dateISO: function(value, element) {
+ return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/number
+ number: function(value, element) {
+ return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/digits
+ digits: function(value, element) {
+ return this.optional(element) || /^\d+$/.test(value);
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
+ // based on http://en.wikipedia.org/wiki/Luhn
+ creditcard: function(value, element) {
+ if ( this.optional(element) )
+ return "dependency-mismatch";
+ // accept only digits and dashes
+ if (/[^0-9-]+/.test(value))
+ return false;
+ var nCheck = 0,
+ nDigit = 0,
+ bEven = false;
+
+ value = value.replace(/\D/g, "");
+
+ for (var n = value.length - 1; n >= 0; n--) {
+ var cDigit = value.charAt(n);
+ var nDigit = parseInt(cDigit, 10);
+ if (bEven) {
+ if ((nDigit *= 2) > 9)
+ nDigit -= 9;
+ }
+ nCheck += nDigit;
+ bEven = !bEven;
+ }
+
+ return (nCheck % 10) == 0;
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/accept
+ accept: function(value, element, param) {
+ param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
+ return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
+ },
+
+ // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
+ equalTo: function(value, element, param) {
+ // bind to the blur event of the target in order to revalidate whenever the target field is updated
+ // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
+ var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
+ $(element).valid();
+ });
+ return value == target.val();
+ }
+
+ }
+
+});
+
+// deprecated, use $.validator.format instead
+$.format = $.validator.format;
+
+})(jQuery);
+
+// ajax mode: abort
+// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
+// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
+;(function($) {
+ var pendingRequests = {};
+ // Use a prefilter if available (1.5+)
+ if ( $.ajaxPrefilter ) {
+ $.ajaxPrefilter(function(settings, _, xhr) {
+ var port = settings.port;
+ if (settings.mode == "abort") {
+ if ( pendingRequests[port] ) {
+ pendingRequests[port].abort();
+ }
+ pendingRequests[port] = xhr;
+ }
+ });
+ } else {
+ // Proxy ajax
+ var ajax = $.ajax;
+ $.ajax = function(settings) {
+ var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
+ port = ( "port" in settings ? settings : $.ajaxSettings ).port;
+ if (mode == "abort") {
+ if ( pendingRequests[port] ) {
+ pendingRequests[port].abort();
+ }
+ return (pendingRequests[port] = ajax.apply(this, arguments));
+ }
+ return ajax.apply(this, arguments);
+ };
+ }
+})(jQuery);
+
+// provides cross-browser focusin and focusout events
+// IE has native support, in other browsers, use event caputuring (neither bubbles)
+
+// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
+// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
+;(function($) {
+ // only implement if not provided by jQuery core (since 1.4)
+ // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
+ if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
+ $.each({
+ focus: 'focusin',
+ blur: 'focusout'
+ }, function( original, fix ){
+ $.event.special[fix] = {
+ setup:function() {
+ this.addEventListener( original, handler, true );
+ },
+ teardown:function() {
+ this.removeEventListener( original, handler, true );
+ },
+ handler: function(e) {
+ arguments[0] = $.event.fix(e);
+ arguments[0].type = fix;
+ return $.event.handle.apply(this, arguments);
+ }
+ };
+ function handler(e) {
+ e = $.event.fix(e);
+ e.type = fix;
+ return $.event.handle.call(this, e);
+ }
+ });
+ };
+ $.extend($.fn, {
+ validateDelegate: function(delegate, type, handler) {
+ return this.bind(type, function(event) {
+ var target = $(event.target);
+ if (target.is(delegate)) {
+ return handler.apply(target, arguments);
+ }
+ });
+ }
+ });
+})(jQuery);
diff --git a/resources/jquery/jquery.xmldom.js b/resources/jquery/jquery.xmldom.js
new file mode 100644
index 00000000..85d0083d
--- /dev/null
+++ b/resources/jquery/jquery.xmldom.js
@@ -0,0 +1,46 @@
+/*!
+ * jQuery xmlDOM Plugin v1.0
+ * http://outwestmedia.com/jquery-plugins/xmldom/
+ *
+ * Released: 2009-04-06
+ * Version: 1.0
+ *
+ * Copyright (c) 2009 Jonathan Sharp, Out West Media LLC.
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ */
+(function($) {
+ // IE DOMParser wrapper
+ if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
+ DOMParser = function() { };
+ DOMParser.prototype.parseFromString = function( xmlString ) {
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML( xmlString );
+ return doc;
+ };
+ }
+
+ $.xmlDOM = function(xml, onErrorFn) {
+ try {
+ var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
+ if ( $.isXMLDoc( xmlDoc ) ) {
+ var err = $('parsererror', xmlDoc);
+ if ( err.length == 1 ) {
+ throw('Error: ' + $(xmlDoc).text() );
+ }
+ } else {
+ throw('Unable to parse XML');
+ }
+ } catch( e ) {
+ var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
+ if ( $.isFunction( onErrorFn ) ) {
+ onErrorFn( msg );
+ } else {
+ $(document).trigger('xmlParseError', [ msg ]);
+ }
+ return $([]);
+ }
+ return $( xmlDoc );
+ };
+})(jQuery); \ No newline at end of file
diff --git a/resources/mediawiki.action/mediawiki.action.edit.js b/resources/mediawiki.action/mediawiki.action.edit.js
index b121d34f..e685ca94 100644
--- a/resources/mediawiki.action/mediawiki.action.edit.js
+++ b/resources/mediawiki.action/mediawiki.action.edit.js
@@ -1,101 +1,134 @@
-(function( $ ) {
- // currentFocus is used to determine where to insert tags
- var currentFocused = $( '#wpTextbox1' );
-
- mw.toolbar = {
- $toolbar : $( '#toolbar' ),
- buttons : [],
- // If you want to add buttons, use
- // mw.toolbar.addButton( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText );
- addButton : function() {
- this.buttons.push( [].slice.call( arguments ) );
+( function ( $, mw ) {
+ var isReady, toolbar, currentFocused;
+
+ isReady = false;
+
+ toolbar = {
+ $toolbar: false,
+ buttons: [],
+ /**
+ * If you want to add buttons, use
+ * mw.toolbar.addButton( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText );
+ */
+ addButton: function () {
+ if ( isReady ) {
+ toolbar.insertButton.apply( toolbar, arguments );
+ } else {
+ toolbar.buttons.push( [].slice.call( arguments ) );
+ }
},
- insertButton : function( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
+ insertButton: function ( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
var image = $('<img>', {
- width : 23,
- height : 22,
- src : imageFile,
- alt : speedTip,
- title : speedTip,
- id : imageId || '',
+ width : 23,
+ height: 22,
+ src : imageFile,
+ alt : speedTip,
+ title : speedTip,
+ id : imageId || '',
'class': 'mw-toolbar-editbutton'
- } ).click( function() {
+ } ).click( function () {
mw.toolbar.insertTags( tagOpen, tagClose, sampleText, selectText );
return false;
} );
- this.$toolbar.append( image );
+ toolbar.$toolbar.append( image );
return true;
},
- // apply tagOpen/tagClose to selection in textarea,
- // use sampleText instead of selection if there is none
- insertTags : function( tagOpen, tagClose, sampleText, selectText) {
- if ( currentFocused.length ) {
+ /**
+ * apply tagOpen/tagClose to selection in textarea,
+ * use sampleText instead of selection if there is none.
+ */
+ insertTags: function ( tagOpen, tagClose, sampleText, selectText ) {
+ if ( currentFocused && currentFocused.length ) {
currentFocused.textSelection(
- 'encapsulateSelection', { 'pre': tagOpen, 'peri': sampleText, 'post': tagClose }
+ 'encapsulateSelection', {
+ 'pre': tagOpen,
+ 'peri': sampleText,
+ 'post': tagClose
+ }
);
}
- },
- init : function() {
- // Legacy
- // Merge buttons from mwCustomEditButtons
- var buttons = [].concat( this.buttons, window.mwCustomEditButtons );
- for ( var i = 0; i < buttons.length; i++ ) {
- if ( $.isArray( buttons[i] ) ) {
- // Passes our button array as arguments
- mw.toolbar.insertButton.apply( this, buttons[i] );
- } else {
- // Legacy mwCustomEditButtons is an object
- var c = buttons[i];
- mw.toolbar.insertButton( c.imageFile, c.speedTip, c.tagOpen, c.tagClose, c.sampleText, c.imageId, c.selectText );
- }
+ },
+
+ // For backwards compatibility
+ init: function () {}
+ };
+
+ // Legacy (for compatibility with the code previously in skins/common.edit.js)
+ window.addButton = toolbar.addButton;
+ window.insertTags = toolbar.insertTags;
+
+ // Explose publicly
+ mw.toolbar = toolbar;
+
+ $( document ).ready( function () {
+ var buttons, i, c, iframe;
+
+ // currentFocus is used to determine where to insert tags
+ currentFocused = $( '#wpTextbox1' );
+
+ // Populate the selector cache for $toolbar
+ toolbar.$toolbar = $( '#toolbar' );
+
+ // Legacy: Merge buttons from mwCustomEditButtons
+ buttons = [].concat( toolbar.buttons, window.mwCustomEditButtons );
+ for ( i = 0; i < buttons.length; i++ ) {
+ if ( $.isArray( buttons[i] ) ) {
+ // Passes our button array as arguments
+ toolbar.insertButton.apply( toolbar, buttons[i] );
+ } else {
+ // Legacy mwCustomEditButtons is an object
+ c = buttons[i];
+ toolbar.insertButton( c.imageFile, c.speedTip, c.tagOpen,
+ c.tagClose, c.sampleText, c.imageId, c.selectText );
}
- return true;
}
- };
- //Legacy
- window.addButton = mw.toolbar.addButton;
- window.insertTags = mw.toolbar.insertTags;
+ // This causes further calls to addButton to go to insertion directly
+ // instead of to the toolbar.buttons queue.
+ // It is important that this is after the one and only loop through
+ // the the toolbar.buttons queue
+ isReady = true;
+
+ // Make sure edit summary does not exceed byte limit
+ $( '#wpSummary' ).byteLimit( 250 );
- //make sure edit summary does not exceed byte limit
- $( '#wpSummary' ).byteLimit( 250 );
-
- $( document ).ready( function() {
/**
* Restore the edit box scroll state following a preview operation,
* and set up a form submission handler to remember this state
*/
- var scrollEditBox = function() {
- var editBox = document.getElementById( 'wpTextbox1' );
- var scrollTop = document.getElementById( 'wpScrolltop' );
- var $editForm = $( '#editform' );
- if( $editForm.length && editBox && scrollTop ) {
- if( scrollTop.value ) {
+ ( function scrollEditBox() {
+ var editBox, scrollTop, $editForm;
+
+ editBox = document.getElementById( 'wpTextbox1' );
+ scrollTop = document.getElementById( 'wpScrolltop' );
+ $editForm = $( '#editform' );
+ if ( $editForm.length && editBox && scrollTop ) {
+ if ( scrollTop.value ) {
editBox.scrollTop = scrollTop.value;
}
- $editForm.submit( function() {
+ $editForm.submit( function () {
scrollTop.value = editBox.scrollTop;
});
}
- };
- scrollEditBox();
-
- // Create button bar
- mw.toolbar.init();
-
- $( 'textarea, input:text' ).focus( function() {
+ }() );
+
+ $( 'textarea, input:text' ).focus( function () {
currentFocused = $(this);
});
// HACK: make currentFocused work with the usability iframe
// With proper focus detection support (HTML 5!) this'll be much cleaner
- var iframe = $( '.wikiEditor-ui-text iframe' );
+ iframe = $( '.wikiEditor-ui-text iframe' );
if ( iframe.length > 0 ) {
$( iframe.get( 0 ).contentWindow.document )
- .add( iframe.get( 0 ).contentWindow.document.body ) // for IE
- .focus( function() { currentFocused = iframe; } );
+ // for IE
+ .add( iframe.get( 0 ).contentWindow.document.body )
+ .focus( function () {
+ currentFocused = iframe;
+ } );
}
});
-})(jQuery);
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/mediawiki.action/mediawiki.action.history.js b/resources/mediawiki.action/mediawiki.action.history.js
index 1b5b3a00..76b0e6cd 100644
--- a/resources/mediawiki.action/mediawiki.action.history.js
+++ b/resources/mediawiki.action/mediawiki.action.history.js
@@ -1,53 +1,126 @@
-/*
+/**
* JavaScript for History action
*/
-jQuery( function( $ ) {
- var $lis = $( 'ul#pagehistory li' );
- var updateDiffRadios = function() {
+jQuery( document ).ready( function ( $ ) {
+ var $historyCompareForm = $( '#mw-history-compare' ),
+ $historySubmitter,
+ $lis = $( '#pagehistory > li' );
+
+ /**
+ * @context {Element} input
+ * @param e {jQuery.Event}
+ */
+ function updateDiffRadios() {
var diffLi = false, // the li where the diff radio is checked
oldLi = false; // the li where the oldid radio is checked
if ( !$lis.length ) {
return true;
}
- $lis.removeClass( 'selected' );
- $lis.each( function() {
- var $this = $(this);
- var $inputs = $this.find( 'input[type="radio"]' );
- if ( $inputs.length !== 2 ) {
+
+ $lis
+ .removeClass( 'selected' )
+ .each( function () {
+ var $li = $(this),
+ $inputs = $li.find( 'input[type="radio"]' ),
+ $oldidRadio = $inputs.filter( '[name="oldid"]' ).eq(0),
+ $diffRadio = $inputs.filter( '[name="diff"]' ).eq(0);
+
+ if ( !$oldidRadio.length || !$diffRadio.length ) {
return true;
}
- // this row has a checked radio button
- if ( $inputs.get(0).checked ) {
+ if ( $oldidRadio.prop( 'checked' ) ) {
oldLi = true;
- $this.addClass( 'selected' );
- $inputs.eq(0).css( 'visibility', 'visible' );
- $inputs.eq(1).css( 'visibility', 'hidden' );
- } else if ( $inputs.get(1).checked ) {
+ $li.addClass( 'selected' );
+ $oldidRadio.css( 'visibility', 'visible' );
+ $diffRadio.css( 'visibility', 'hidden' );
+
+ } else if ( $diffRadio.prop( 'checked' ) ) {
diffLi = true;
- $this.addClass( 'selected' );
- $inputs.eq(0).css( 'visibility', 'hidden' );
- $inputs.eq(1).css( 'visibility', 'visible' );
+ $li.addClass( 'selected' );
+ $oldidRadio.css( 'visibility', 'hidden' );
+ $diffRadio.css( 'visibility', 'visible' );
+
+ // This list item has neither checked
} else {
- // no radio is checked in this row
+ // We're below the selected radios
if ( diffLi && oldLi ) {
- // We're below the selected radios
- $inputs.eq(0).css( 'visibility', 'visible' );
- $inputs.eq(1).css( 'visibility', 'hidden' );
- } else if ( diffLi ) {
- // We're between the selected radios
- $inputs.css( 'visibility', 'visible' );
+ $oldidRadio.css( 'visibility', 'visible' );
+ $diffRadio.css( 'visibility', 'hidden' );
+
+ // We're between the selected radios
+ } else if ( diffLi ) {
+ $diffRadio.css( 'visibility', 'visible' );
+ $oldidRadio.css( 'visibility', 'visible' );
+
+ // We're above the selected radios
} else {
- // We're above the selected radios
- $inputs.eq(1).css( 'visibility', 'visible' );
- $inputs.eq(0).css( 'visibility', 'hidden' );
+ $diffRadio.css( 'visibility', 'visible' );
+ $oldidRadio.css( 'visibility', 'hidden' );
}
}
});
+
return true;
- };
+ }
+
+ $lis.find( 'input[name="diff"], input[name="oldid"]' ).click( updateDiffRadios );
- $( '#pagehistory li input[name="diff"], #pagehistory li input[name="oldid"]' ).click( updateDiffRadios );
+ // Set initial state
updateDiffRadios();
-}); \ No newline at end of file
+
+
+ // Prettify url output for HistoryAction submissions,
+ // to cover up action=historysubmit construction.
+
+ // Ideally we'd use e.target instead of $historySubmitter, but e.target points
+ // to the form element for submit actions, so.
+ $historyCompareForm.find( '.historysubmit' ).click( function () {
+ $historySubmitter = $(this);
+ } );
+
+ // On submit we clone the form element, remove unneeded fields in the clone
+ // that pollute the query parameter with stuff from the other "use case",
+ // and then submit the clone.
+ // Without the cloning we'd be changing the real form, which is slower, could make
+ // the page look broken for a second in slow browsers and might show the form broken
+ // again when coming back from a "next" page.
+ $historyCompareForm.submit( function ( e ) {
+ var $copyForm, $copyRadios, $copyAction;
+
+ if ( $historySubmitter ) {
+ $copyForm = $historyCompareForm.clone();
+ $copyRadios = $copyForm.find( '#pagehistory > li' ).find( 'input[name="diff"], input[name="oldid"]' );
+ $copyAction = $copyForm.find( '> [name="action"]');
+
+ // Remove action=historysubmit and ids[..]=..
+ if ( $historySubmitter.hasClass( 'mw-history-compareselectedversions-button' ) ) {
+ $copyAction.remove();
+ $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' ) ) {
+ $copyRadios.remove();
+ $copyAction.val( $historySubmitter.attr( 'name' ) );
+ $copyForm.find( ':submit' ).remove();
+ }
+
+ // IE7 doesn't do submission from an off-DOM clone, so insert hidden into document first
+ // Also remove potentially conflicting id attributes that we don't need anyway
+ $copyForm
+ .css( 'display', 'none' )
+ .find('[id]')
+ .removeAttr('id')
+ .end()
+ .insertAfter( $historyCompareForm )
+ .submit();
+
+ e.preventDefault();
+ return false; // Because the submit is special, return false as well.
+ }
+
+ // Continue natural browser handling other wise
+ return true;
+ } );
+} );
diff --git a/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js b/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js
new file mode 100644
index 00000000..b1d906f6
--- /dev/null
+++ b/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js
@@ -0,0 +1,12 @@
+/**
+ * This module enables double-click-to-edit functionality
+ */
+jQuery( document ).ready( function( $ ) {
+ var url = $( '#ca-edit a' ).attr( 'href' );
+ if ( url ) {
+ mw.util.$content.dblclick( function( e ) {
+ e.preventDefault();
+ window.location = url;
+ } );
+ }
+} );
diff --git a/resources/mediawiki.action/mediawiki.action.view.metadata.js b/resources/mediawiki.action/mediawiki.action.view.metadata.js
index 378dd155..b791cabd 100644
--- a/resources/mediawiki.action/mediawiki.action.view.metadata.js
+++ b/resources/mediawiki.action/mediawiki.action.view.metadata.js
@@ -14,7 +14,7 @@ jQuery( document ).ready( function( $ ) {
return;
}
- var $row = $( '<tr></tr>' );
+ var $row = $( '<tr class="mw-metadata-show-hide-extended"></tr>' );
var $col = $( '<td colspan="2"></td>' );
var $link = $( '<a></a>', {
diff --git a/resources/mediawiki.action/mediawiki.action.watch.ajax.js b/resources/mediawiki.action/mediawiki.action.watch.ajax.js
index 93aa29c9..f5f09f52 100644
--- a/resources/mediawiki.action/mediawiki.action.watch.ajax.js
+++ b/resources/mediawiki.action/mediawiki.action.watch.ajax.js
@@ -1,174 +1,155 @@
/**
* Animate watch/unwatch links to use asynchronous API requests to
- * watch pages, rather than clicking on links. Requires jQuery.
+ * watch pages, rather than navigating to a different URI.
*/
-( function( $ ) {
-var $links;
-
-var setLinkText = function( $link, action ) {
- if ( action == 'watch' || action == 'unwatch' ) {
- // save the accesskey from the title
- var keyCommand = $link.attr( 'title' ).match( /\[.*?\]$/ ) ? $link.attr( 'title' ).match( /\[.*?\]$/ )[0] : '';
- $link.attr( 'title', mw.msg( 'tooltip-ca-' + action ) + ' ' + keyCommand );
- }
- if ( $link.data( 'icon' ) ) {
- $link.attr( 'alt', mw.msg( action ) );
- if ( action == 'watching' || action == 'unwatching' ) {
+( function ( $, mw, undefined ) {
+
+/**
+ * The name of the page to watch or unwatch.
+ */
+var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
+
+/**
+ * Update the link text, link href attribute and (if applicable)
+ * "loading" class.
+ *
+ * @param $link {jQuery} Anchor tag of (un)watch link
+ * @param action {String} One of 'watch', 'unwatch'.
+ * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'.
+ */
+function updateWatchLink( $link, action, state ) {
+ // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
+ var msgKey = state === 'loading' ? action + 'ing' : action,
+ accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ),
+ $li = $link.closest( 'li' );
+
+ $link
+ .text( mw.msg( msgKey ) )
+ .attr( 'title', mw.msg( 'tooltip-ca-' + action ) +
+ ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
+ )
+ .attr( 'href', mw.util.wikiScript() + '?' + $.param({
+ title: title,
+ action: action
+ })
+ );
+
+ // Special case for vector icon
+ if ( $li.hasClass( 'icon' ) ) {
+ if ( state === 'loading' ) {
$link.addClass( 'loading' );
} else {
$link.removeClass( 'loading' );
}
- } else {
- $link.html( mw.msg( action ) );
}
-};
-
-var errorHandler = function( $link ) {
-
- // Reset link text to whatever it was before we switching it to the '(un)watch'+ing message.
- setLinkText( $link, $link.data( 'action' ) );
-
- // Format error message
- var cleanTitle = mw.config.get( 'wgPageName' ).replace( /_/g, ' ' );
- var link = mw.html.element(
- 'a', {
- 'href': mw.util.wikiGetlink( mw.config.get( 'wgPageName' ) ),
- 'title': cleanTitle
- }, cleanTitle
- );
- var msg = mw.msg( 'watcherrortext', link );
-
- // Report to user about the error
- mw.util.jsMessage( msg, 'watch' );
-};
+}
/**
- * Process the result of the API watch action.
- *
- * @param response Data object from API request.
- * @param $link jQuery object of the watch link.
- * @return Boolean true on success, false otherwise.
+ * @todo This should be moved somewhere more accessible.
+ * @param url {String}
+ * @return {String} The extracted action, defaults to 'view'.
*/
-var processResult = function( response, $link ) {
-
- if ( ( 'error' in response ) || !response.watch ) {
- errorHandler( $link );
- return false;
+function mwUriGetAction( url ) {
+ var actionPaths = mw.config.get( 'wgActionPaths' ),
+ key, parts, m, action;
+
+ // @todo: Does MediaWiki give action path or query param
+ // precedence ? If the former, move this to the bottom
+ action = mw.util.getParamValue( 'action', url );
+ if ( action !== null ) {
+ return action;
}
- var watchResponse = response.watch;
-
- // To ensure we set the same status for all watch links with the
- // same target we trigger a custom event on *all* watch links.
- if ( watchResponse.watched !== undefined ) {
- $links.trigger( 'mw-ajaxwatch', [watchResponse.title, 'watch', $link] );
- } else if ( watchResponse.unwatched !== undefined ) {
- $links.trigger( 'mw-ajaxwatch', [watchResponse.title, 'unwatch', $link] );
- } else {
- // Either we got an error code or it just plain broke.
- window.location.href = $link[0].href;
- return false;
+ for ( key in actionPaths ) {
+ if ( actionPaths.hasOwnProperty( key ) ) {
+ parts = actionPaths[key].split( '$1' );
+ for ( i = 0; i < parts.length; i += 1 ) {
+ parts[i] = $.escapeRE( parts[i] );
+ }
+ m = new RegExp( parts.join( '(.+)' ) ).exec( url );
+ if ( m && m[1] ) {
+ return key;
+ }
+
+ }
}
- mw.util.jsMessage( watchResponse.message, 'watch' );
-
- // Bug 12395 - update the watch checkbox on edit pages when the
- // page is watched or unwatched via the tab.
- if ( watchResponse.watched !== undefined ) {
- $( '#wpWatchthis' ).attr( 'checked', 'checked' );
- } else {
- $( '#wpWatchthis' ).removeAttr( 'checked' );
- }
- return true;
-};
+ return 'view';
+}
$( document ).ready( function() {
- $links = $( '.mw-watchlink a, a.mw-watchlink' );
- // BC with older skins
- $links = $links
- .add( '#ca-watch a, #ca-unwatch a, a#mw-unwatch-link1, ' +
- 'a#mw-unwatch-link2, a#mw-watch-link2, a#mw-watch-link1' );
- // allowing people to add inline animated links is a little scary
- $links = $links.filter( ':not( #bodyContent *, #content * )' );
+ var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
+ '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
+ '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
- $links.each( function() {
- var $link = $( this );
- var link = this;
- $link
- .data( 'icon', $link.closest( 'li' ).hasClass( 'icon' ) )
- .data( 'action', mw.util.getParamValue( 'action', link.href ) == 'unwatch' ? 'unwatch' : 'watch' );
- var title = mw.util.getParamValue( 'title', link.href );
- $link.data( 'target', title.replace( /_/g, ' ' ) );
- });
+ // Allowing people to add inline animated links is a little scary
+ $links = $links.filter( ':not( #bodyContent *, #content * )' );
- $links.click( function( event ) {
- var $link = $( this );
+ $links.click( function( e ) {
+ var $link, api,
+ action = mwUriGetAction( this.href );
- if ( !mw.config.get( 'wgEnableWriteAPI' ) ) {
- // Lazy initialization so we don't toss up
- // ActiveX warnings on initial page load
- // for IE 6 users with security settings.
- $links.unbind( 'click' );
+ if ( action !== 'watch' && action !== 'unwatch' ) {
+ // Could not extract target action from link url,
+ // let native browsing handle it further
return true;
}
-
- setLinkText( $link, $link.data( 'action' ) + 'ing' );
-
- var reqData = {
- 'action': 'watch',
- 'format': 'json',
- 'title': $link.data( 'target' ),
- 'token': mw.user.tokens.get( 'watchToken' ),
- // API return contains a localized data.watch.message string.
- 'uselang': mw.config.get( 'wgUserLanguage' )
- };
-
- if ( $link.data( 'action' ) == 'unwatch' ) {
- reqData.unwatch = '';
- }
-
- $.ajax({
- url: mw.util.wikiScript( 'api' ),
- dataType: 'json',
- type: 'POST',
- data: reqData,
- success: function( data, textStatus, xhr ) {
- processResult( data, $link );
+ e.preventDefault();
+ e.stopPropagation();
+
+ $link = $( this );
+
+ updateWatchLink( $link, action, 'loading' );
+
+ api = new mw.Api();
+ api[action](
+ title,
+ // Success
+ function( watchResponse ) {
+ var otherAction = action === 'watch' ? 'unwatch' : 'watch',
+ $li = $link.closest( 'li' );
+
+ mw.util.jsMessage( watchResponse.message, 'ajaxwatch' );
+
+ // Set link to opposite
+ updateWatchLink( $link, otherAction );
+
+ // Most common ID style
+ if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) {
+ $li.prop( 'id', 'ca-' + otherAction );
+ }
+
+ // Bug 12395 - update the watch checkbox on edit pages when the
+ // page is watched or unwatched via the tab.
+ if ( watchResponse.watched !== undefined ) {
+ $( '#wpWatchthis' ).prop( 'checked', true );
+ } else {
+ $( '#wpWatchthis' ).removeProp( 'checked' );
+ }
},
- error: function(){
- processResult( {}, $link );
- }
- });
+ // Error
+ function(){
+
+ // Reset link to non-loading mode
+ updateWatchLink( $link, action );
+
+ // Format error message
+ var cleanTitle = title.replace( /_/g, ' ' );
+ var link = mw.html.element(
+ 'a', {
+ 'href': mw.util.wikiGetlink( title ),
+ 'title': cleanTitle
+ }, cleanTitle
+ );
+ var html = mw.msg( 'watcherrortext', link );
+
+ // Report to user about the error
+ mw.util.jsMessage( html, 'ajaxwatch' );
- return false;
- });
-
- // When a request returns, a custom event 'mw-ajaxwatch' is triggered
- // on *all* watch links, so they can be updated if necessary
- $links.bind( 'mw-ajaxwatch', function( event, target, action, $link ) {
- var foo = $link.data( 'target' );
- if ( $link.data( 'target' ) == target ) {
- var otheraction = action == 'watch'
- ? 'unwatch'
- : 'watch';
-
- $link.data( 'action', otheraction );
- setLinkText( $link, otheraction );
- $link.attr( 'href',
- mw.config.get( 'wgScript' )
- + '?title=' + mw.util.wikiUrlencode( mw.config.get( 'wgPageName' ) )
- + '&action=' + otheraction
- );
- if ( $link.closest( 'li' ).attr( 'id' ) == 'ca-' + action ) {
- $link.closest( 'li' ).attr( 'id', 'ca-' + otheraction );
- // update the link text with the new message
- $link.text( mw.msg( otheraction ) );
}
- }
-
- return false;
+ );
});
});
-})( jQuery );
+})( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.category.js b/resources/mediawiki.api/mediawiki.api.category.js
new file mode 100644
index 00000000..c8c18e69
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.category.js
@@ -0,0 +1,105 @@
+/**
+ * Additional mw.Api methods to assist with API calls related to categories.
+ */
+
+( function( $, mw, undefined ) {
+
+ $.extend( mw.Api.prototype, {
+ /**
+ * Determine if a category exists.
+ * @param title {mw.Title}
+ * @param success {Function} callback to pass boolean of category's existence
+ * @param err {Function} optional callback to run if api error
+ * @return ajax call object
+ */
+ isCategory: function( title, success, err ) {
+ var params = {
+ prop: 'categoryinfo',
+ titles: title.toString()
+ },
+ ok = function( data ) {
+ var exists = false;
+ if ( data.query && data.query.pages ) {
+ $.each( data.query.pages, function( id, page ) {
+ if ( page.categoryinfo ) {
+ exists = true;
+ }
+ } );
+ }
+ success( exists );
+ };
+
+ return this.get( params, { ok: ok, err: err } );
+ },
+
+ /**
+ * Get a list of categories that match a certain prefix.
+ * e.g. given "Foo", return "Food", "Foolish people", "Foosball tables" ...
+ * @param prefix {String} prefix to match
+ * @param success {Function} callback to pass matched categories to
+ * @param err {Function} optional callback to run if api error
+ * @return {jqXHR}
+ */
+ getCategoriesByPrefix: function( prefix, success, err ) {
+
+ // fetch with allpages to only get categories that have a corresponding description page.
+ var params = {
+ 'list': 'allpages',
+ 'apprefix': prefix,
+ 'apnamespace': mw.config.get('wgNamespaceIds').category
+ };
+
+ var ok = function( data ) {
+ var texts = [];
+ if ( data.query && data.query.allpages ) {
+ $.each( data.query.allpages, function( i, category ) {
+ texts.push( new mw.Title( category.title ).getNameText() );
+ } );
+ }
+ success( texts );
+ };
+
+ return this.get( params, { ok: ok, err: err } );
+ },
+
+
+ /**
+ * Get the categories that a particular page on the wiki belongs to
+ * @param title {mw.Title}
+ * @param success {Function} callback to pass categories to (or false, if title not found)
+ * @param err {Function} optional callback to run if api error
+ * @param async {Boolean} optional asynchronousness (default = true = async)
+ * @return {jqXHR}
+ */
+ getCategories: function( title, success, err, async ) {
+ var params, ok;
+ params = {
+ prop: 'categories',
+ titles: title.toString()
+ };
+ if ( async === undefined ) {
+ async = true;
+ }
+ ok = function( data ) {
+ var ret = false;
+ if ( data.query && data.query.pages ) {
+ $.each( data.query.pages, function( id, page ) {
+ if ( page.categories ) {
+ if ( typeof ret !== 'object' ) {
+ ret = [];
+ }
+ $.each( page.categories, function( i, cat ) {
+ ret.push( new mw.Title( cat.title ) );
+ } );
+ }
+ } );
+ }
+ success( ret );
+ };
+
+ return this.get( params, { ok: ok, err: err, async: async } );
+ }
+
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.edit.js b/resources/mediawiki.api/mediawiki.api.edit.js
new file mode 100644
index 00000000..a9d488a8
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.edit.js
@@ -0,0 +1,119 @@
+/**
+ * Additional mw.Api methods to assist with API calls related to editing wiki pages.
+ */
+
+( function( $, mw, undefined ) {
+
+ // Cache token so we don't have to keep fetching new ones for every single request.
+ var cachedToken = null;
+
+ $.extend( mw.Api.prototype, {
+
+ /**
+ * Post to API with edit token. If we have no token, get one and try to post.
+ * If we have a cached token try using that, and if it fails, blank out the
+ * cached token and start over.
+ *
+ * @param params {Object} API parameters
+ * @param ok {Function} callback for success
+ * @param err {Function} [optional] error callback
+ * @return {jqXHR}
+ */
+ postWithEditToken: function( params, ok, err ) {
+ var api = this, useTokenToPost, getTokenIfBad;
+ if ( cachedToken === null ) {
+ // We don't have a valid cached token, so get a fresh one and try posting.
+ // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
+ // an infinite loop. If this fresh token is bad, something else is very wrong.
+ useTokenToPost = function( token ) {
+ params.token = token;
+ api.post( params, ok, err );
+ };
+ return api.getEditToken( useTokenToPost, err );
+ } else {
+ // We do have a token, but it might be expired. So if it is 'bad' then
+ // start over with a new token.
+ params.token = cachedToken;
+ getTokenIfBad = function( code, result ) {
+ if ( code === 'badtoken' ) {
+ cachedToken = null; // force a new token
+ api.postWithEditToken( params, ok, err );
+ } else {
+ err( code, result );
+ }
+ };
+ return api.post( params, { ok : ok, err : getTokenIfBad });
+ }
+ },
+
+ /**
+ * Api helper to grab an edit token
+ *
+ * token callback has signature ( String token )
+ * error callback has signature ( String code, Object results, XmlHttpRequest xhr, Exception exception )
+ * Note that xhr and exception are only available for 'http_*' errors
+ * code may be any http_* error code (see mw.Api), or 'token_missing'
+ *
+ * @param tokenCallback {Function} received token callback
+ * @param err {Function} error callback
+ * @return {jqXHR}
+ */
+ getEditToken: function( tokenCallback, err ) {
+ var parameters = {
+ prop: 'info',
+ intoken: 'edit',
+ // we need some kind of dummy page to get a token from. This will return a response
+ // complaining that the page is missing, but we should also get an edit token
+ titles: 'DummyPageForEditToken'
+ },
+ ok = function( data ) {
+ var token;
+ $.each( data.query.pages, function( i, page ) {
+ if ( page.edittoken ) {
+ token = page.edittoken;
+ return false;
+ }
+ } );
+ if ( token !== undefined ) {
+ cachedToken = token;
+ tokenCallback( token );
+ } else {
+ err( 'token-missing', data );
+ }
+ },
+ ajaxOptions = {
+ ok: ok,
+ err: err,
+ // Due to the API assuming we're logged out if we pass the callback-parameter,
+ // we have to disable jQuery's callback system, and instead parse JSON string,
+ // by setting 'jsonp' to false.
+ jsonp: false
+ };
+
+ return this.get( parameters, ajaxOptions );
+ },
+
+ /**
+ * Create a new section of the page.
+ * @param title {mw.Title|String} target page
+ * @param header {String}
+ * @param message {String} wikitext message
+ * @param ok {Function} success handler
+ * @param err {Function} error handler
+ * @return {jqXHR}
+ */
+ newSection: function( title, header, message, ok, err ) {
+ var params = {
+ action: 'edit',
+ section: 'new',
+ format: 'json',
+ title: title.toString(),
+ summary: header,
+ text: message
+ };
+ return this.postWithEditToken( params, ok, err );
+ }
+
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.js b/resources/mediawiki.api/mediawiki.api.js
new file mode 100644
index 00000000..225093b3
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.js
@@ -0,0 +1,224 @@
+/* mw.Api objects represent the API of a particular MediaWiki server. */
+
+( function( $, mw, undefined ) {
+
+ /**
+ * @var defaultOptions {Object}
+ * We allow people to omit these default parameters from API requests
+ * there is very customizable error handling here, on a per-call basis
+ * wondering, would it be simpler to make it easy to clone the api object,
+ * change error handling, and use that instead?
+ */
+ var defaultOptions = {
+
+ // Query parameters for API requests
+ parameters: {
+ action: 'query',
+ format: 'json'
+ },
+
+ // Ajax options for jQuery.ajax()
+ ajax: {
+ url: mw.util.wikiScript( 'api' ),
+
+ ok: function() {},
+
+ // caller can supply handlers for http transport error or api errors
+ err: function( code, result ) {
+ mw.log( 'mw.Api error: ' + code, 'debug' );
+ },
+
+ timeout: 30000, // 30 seconds
+
+ dataType: 'json'
+ }
+ };
+
+ /**
+ * Constructor to create an object to interact with the API of a particular MediaWiki server.
+ *
+ * @todo Share API objects with exact same config.
+ * @example
+ * <code>
+ * var api = new mw.Api();
+ * api.get( {
+ * action: 'query',
+ * meta: 'userinfo'
+ * }, {
+ * ok: function () { console.log( arguments ); }
+ * } );
+ * </code>
+ *
+ * @constructor
+ * @param options {Object} See defaultOptions documentation above. Ajax options can also be
+ * overridden for each individual request to jQuery.ajax() later on.
+ */
+ mw.Api = function( options ) {
+
+ if ( options === undefined ) {
+ options = {};
+ }
+
+ // Force toString if we got a mw.Uri object
+ if ( options.ajax && options.ajax.url !== undefined ) {
+ options.ajax.url = String( options.ajax.url );
+ }
+
+ options.parameters = $.extend( {}, defaultOptions.parameters, options.parameters );
+ options.ajax = $.extend( {}, defaultOptions.ajax, options.ajax );
+
+ this.defaults = options;
+ };
+
+ mw.Api.prototype = {
+
+ /**
+ * For api queries, in simple cases the caller just passes a success callback.
+ * In complex cases they pass an object with a success property as callback and
+ * probably other options.
+ * Normalize the argument so that it's always the latter case.
+ *
+ * @param {Object|Function} An object contaning one or more of options.ajax,
+ * or just a success function (options.ajax.ok).
+ * @return {Object} Normalized ajax options.
+ */
+ normalizeAjaxOptions: function( arg ) {
+ var opt = arg;
+ if ( typeof arg === 'function' ) {
+ opt = { 'ok': arg };
+ }
+ if ( !opt.ok ) {
+ throw new Error( 'ajax options must include ok callback' );
+ }
+ return opt;
+ },
+
+ /**
+ * Perform API get request
+ *
+ * @param {Object} request parameters
+ * @param {Object|Function} ajax options, or just a success function
+ * @return {jqXHR}
+ */
+ get: function( parameters, ajaxOptions ) {
+ ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
+ ajaxOptions.type = 'GET';
+ return this.ajax( parameters, ajaxOptions );
+ },
+
+ /**
+ * Perform API post request
+ * @todo Post actions for nonlocal will need proxy
+ *
+ * @param {Object} request parameters
+ * @param {Object|Function} ajax options, or just a success function
+ * @return {jqXHR}
+ */
+ post: function( parameters, ajaxOptions ) {
+ ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
+ ajaxOptions.type = 'POST';
+ return this.ajax( parameters, ajaxOptions );
+ },
+
+ /**
+ * Perform the API call.
+ *
+ * @param {Object} request parameters
+ * @param {Object} ajax options
+ * @return {jqXHR}
+ */
+ ajax: function( parameters, ajaxOptions ) {
+ parameters = $.extend( {}, this.defaults.parameters, parameters );
+ ajaxOptions = $.extend( {}, this.defaults.ajax, ajaxOptions );
+
+ // Some deployed MediaWiki >= 1.17 forbid periods in URLs, due to an IE XSS bug
+ // So let's escape them here. See bug #28235
+ // This works because jQuery accepts data as a query string or as an Object
+ ajaxOptions.data = $.param( parameters ).replace( /\./g, '%2E' );
+
+ ajaxOptions.error = function( xhr, textStatus, exception ) {
+ ajaxOptions.err( 'http', {
+ xhr: xhr,
+ textStatus: textStatus,
+ exception: exception
+ } );
+ };
+
+ // Success just means 200 OK; also check for output and API errors
+ ajaxOptions.success = function( result ) {
+ if ( result === undefined || result === null || result === '' ) {
+ ajaxOptions.err( 'ok-but-empty',
+ 'OK response but empty result (check HTTP headers?)' );
+ } else if ( result.error ) {
+ var code = result.error.code === undefined ? 'unknown' : result.error.code;
+ ajaxOptions.err( code, result );
+ } else {
+ ajaxOptions.ok( result );
+ }
+ };
+
+ return $.ajax( ajaxOptions );
+ }
+
+ };
+
+ /**
+ * @var {Array} List of errors we might receive from the API.
+ * For now, this just documents our expectation that there should be similar messages
+ * available.
+ */
+ mw.Api.errors = [
+ // occurs when POST aborted
+ // jQuery 1.4 can't distinguish abort or lost connection from 200 OK + empty result
+ 'ok-but-empty',
+
+ // timeout
+ 'timeout',
+
+ // really a warning, but we treat it like an error
+ 'duplicate',
+ 'duplicate-archive',
+
+ // upload succeeded, but no image info.
+ // this is probably impossible, but might as well check for it
+ 'noimageinfo',
+ // remote errors, defined in API
+ 'uploaddisabled',
+ 'nomodule',
+ 'mustbeposted',
+ 'badaccess-groups',
+ 'stashfailed',
+ 'missingresult',
+ 'missingparam',
+ 'invalid-file-key',
+ 'copyuploaddisabled',
+ 'mustbeloggedin',
+ 'empty-file',
+ 'file-too-large',
+ 'filetype-missing',
+ 'filetype-banned',
+ 'filename-tooshort',
+ 'illegal-filename',
+ 'verification-error',
+ 'hookaborted',
+ 'unknown-error',
+ 'internal-error',
+ 'overwrite',
+ 'badtoken',
+ 'fetchfileerror',
+ 'fileexists-shared-forbidden',
+ 'invalidtitle',
+ 'notloggedin'
+ ];
+
+ /**
+ * @var {Array} List of warnings we might receive from the API.
+ * For now, this just documents our expectation that there should be similar messages
+ * available.
+ */
+ mw.Api.warnings = [
+ 'duplicate',
+ 'exists'
+ ];
+
+})( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.parse.js b/resources/mediawiki.api/mediawiki.api.parse.js
new file mode 100644
index 00000000..1cc68f29
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.parse.js
@@ -0,0 +1,31 @@
+/**
+ * Additional mw.Api methods to assist with API calls related to parsing wikitext.
+ */
+
+( function( $, mw ) {
+
+ $.extend( mw.Api.prototype, {
+ /**
+ * Convinience method for 'action=parse'. Parses wikitext into HTML.
+ *
+ * @param wikiText {String}
+ * @param success {Function} callback to which to pass success HTML
+ * @param err {Function} callback if error (optional)
+ * @return {jqXHR}
+ */
+ parse: function( wikiText, success, err ) {
+ var params = {
+ text: wikiText,
+ action: 'parse'
+ },
+ ok = function( data ) {
+ if ( data.parse && data.parse.text && data.parse.text['*'] ) {
+ success( data.parse.text['*'] );
+ }
+ };
+ return this.get( params, { ok: ok, err: err } );
+ }
+
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.titleblacklist.js b/resources/mediawiki.api/mediawiki.api.titleblacklist.js
new file mode 100644
index 00000000..5435945b
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.titleblacklist.js
@@ -0,0 +1,51 @@
+/**
+ * Additional mw.Api methods to assist with API calls to the API module of the TitleBlacklist extension.
+ */
+
+( function( $, mw, undefined ) {
+
+ $.extend( mw.Api.prototype, {
+ /**
+ * Convinience method for 'action=titleblacklist'.
+ * Note: This action is not provided by MediaWiki core, but as part of the TitleBlacklist extension.
+ *
+ * @param title {mw.Title}
+ * @param success {Function} Called on successfull request. First argument is false if title wasn't blacklisted,
+ * object with 'reason', 'line' and 'message' properties if title was blacklisted.
+ * @param err {Function} optional callback to run if api error
+ * @return {jqXHR}
+ */
+ isBlacklisted: function( title, success, err ) {
+ var params = {
+ action: 'titleblacklist',
+ tbaction: 'create',
+ tbtitle: title.toString()
+ },
+ ok = function( data ) {
+ var result;
+
+ // this fails open (if nothing valid is returned by the api, allows the title)
+ // also fails open when the API is not present, which will be most of the time
+ // as this API module is part of the TitleBlacklist extension.
+ if ( data.titleblacklist && data.titleblacklist.result && data.titleblacklist.result === 'blacklisted') {
+ if ( data.titleblacklist.reason ) {
+ result = {
+ reason: data.titleblacklist.reason,
+ line: data.titleblacklist.line,
+ message: data.titleblacklist.message
+ };
+ } else {
+ mw.log('mw.Api.titleblacklist::isBlacklisted> no reason data for blacklisted title', 'debug');
+ result = { reason: 'Blacklisted, but no reason supplied', line: 'Unknown', message: null };
+ }
+ success( result );
+ } else {
+ success ( false );
+ }
+ };
+
+ return this.get( params, { ok: ok, err: err } );
+ }
+
+ } );
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.api/mediawiki.api.watch.js b/resources/mediawiki.api/mediawiki.api.watch.js
new file mode 100644
index 00000000..3f2525ad
--- /dev/null
+++ b/resources/mediawiki.api/mediawiki.api.watch.js
@@ -0,0 +1,58 @@
+/**
+ * Additional mw.Api methods to assist with (un)watching wiki pages.
+ * @since 1.19
+ */
+( function( $, mw ) {
+
+ $.extend( mw.Api.prototype, {
+ /**
+ * Convinience method for 'action=watch'.
+ *
+ * @param page {String|mw.Title} Full page name or instance of mw.Title
+ * @param success {Function} callback to which the watch object will be passed
+ * watch object contains 'title' (full page name), 'watched' (boolean) and
+ * 'message' (parsed HTML of the 'addedwatchtext' message).
+ * @param err {Function} callback if error (optional)
+ * @return {jqXHR}
+ */
+ watch: function( page, success, err ) {
+ var params, ok;
+ params = {
+ action: 'watch',
+ title: String( page ),
+ token: mw.user.tokens.get( 'watchToken' ),
+ uselang: mw.config.get( 'wgUserLanguage' )
+ };
+ ok = function( data ) {
+ success( data.watch );
+ };
+ return this.post( params, { ok: ok, err: err } );
+ },
+ /**
+ * Convinience method for 'action=watch&unwatch='.
+ *
+ * @param page {String|mw.Title} Full page name or instance of mw.Title
+ * @param success {Function} callback to which the watch object will be passed
+ * watch object contains 'title' (full page name), 'unwatched' (boolean) and
+ * 'message' (parsed HTML of the 'removedwatchtext' message).
+ * @param err {Function} callback if error (optional)
+ * @return {jqXHR}
+ */
+ unwatch: function( page, success, err ) {
+ var params, ok;
+ params = {
+ action: 'watch',
+ unwatch: 1,
+ title: String( page ),
+ token: mw.user.tokens.get( 'watchToken' ),
+ uselang: mw.config.get( 'wgUserLanguage' )
+ };
+ ok = function( data ) {
+ success( data.watch );
+ };
+ return this.post( params, { ok: ok, err: err } );
+ }
+
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.language/languages/cs.js b/resources/mediawiki.language/languages/cs.js
index 0b77909d..04dda99d 100644
--- a/resources/mediawiki.language/languages/cs.js
+++ b/resources/mediawiki.language/languages/cs.js
@@ -7,12 +7,10 @@ mediaWiki.language.convertPlural = function( count, forms ) {
switch ( count ) {
case 1:
return forms[0];
- break;
case 2:
case 3:
case 4:
return forms[1];
- break;
default:
return forms[2];
}
diff --git a/resources/mediawiki.language/languages/lt.js b/resources/mediawiki.language/languages/lt.js
index ee0a609b..04964aff 100644
--- a/resources/mediawiki.language/languages/lt.js
+++ b/resources/mediawiki.language/languages/lt.js
@@ -3,6 +3,7 @@
*/
mediaWiki.language.convertPlural = function( count, forms ) {
+ // if the number is not mentioned in message, then use $form[0] for singular and $form[1] for plural or zero
if ( forms.length == 2 ) {
return count == 1 ? forms[0] : forms[1];
}
@@ -11,7 +12,7 @@ mediaWiki.language.convertPlural = function( count, forms ) {
return forms[0];
}
if ( count % 10 >= 2 && ( count % 100 < 10 || count % 100 >= 20 ) ) {
- return forms[1];
+ return forms[1];
}
return forms[2];
};
diff --git a/resources/mediawiki.language/languages/mk.js b/resources/mediawiki.language/languages/mk.js
index 5105025c..c89bbac5 100644
--- a/resources/mediawiki.language/languages/mk.js
+++ b/resources/mediawiki.language/languages/mk.js
@@ -4,5 +4,5 @@
mediaWiki.language.convertPlural = function( count, forms ) {
forms = mediaWiki.language.preConvertPlural( forms, 2 );
- return ( count % 10 === 1 ) ? forms[0] : forms[1];
+ return ( ( count % 10 === 1 ) && ( count % 100 !== 11 ) ) ? forms[0] : forms[1];
};
diff --git a/resources/mediawiki.language/languages/se.js b/resources/mediawiki.language/languages/se.js
index 51ebaf25..e1ae5b9a 100644
--- a/resources/mediawiki.language/languages/se.js
+++ b/resources/mediawiki.language/languages/se.js
@@ -3,15 +3,15 @@
*/
mediaWiki.language.convertPlural = function( count, forms ) {
- if ( count == 0 ) {
- return '';
+ if ( !forms || forms.length === 0 ) {
+ return '';
}
forms = mediaWiki.language.preConvertPlural( forms, 3 );
if ( count == 1 ) {
- return forms[1];
+ return forms[0];
}
if ( count == 2 ) {
- return forms[2];
+ return forms[1];
}
- return ''
+ return forms[2];
};
diff --git a/resources/mediawiki.language/languages/sma.js b/resources/mediawiki.language/languages/sma.js
index d3ccf625..8163c42c 100644
--- a/resources/mediawiki.language/languages/sma.js
+++ b/resources/mediawiki.language/languages/sma.js
@@ -3,12 +3,15 @@
*/
mediaWiki.language.convertPlural = function( count, forms ) {
- forms = mediaWiki.language.preConvertPlural( forms, 4 );
+ if ( !forms || forms.length === 0 ) {
+ return '';
+ }
+ forms = mediaWiki.language.preConvertPlural( forms, 3 );
if ( count == 1 ) {
- return forms[1];
+ return forms[0];
}
if ( count == 2 ) {
- return forms[2];
+ return forms[1];
}
- return forms[3];
+ return forms[2];
};
diff --git a/resources/mediawiki.language/mediawiki.language.js b/resources/mediawiki.language/mediawiki.language.js
index fa7aa8d5..4abfa4ba 100644
--- a/resources/mediawiki.language/mediawiki.language.js
+++ b/resources/mediawiki.language/mediawiki.language.js
@@ -22,7 +22,7 @@ mw.language = {
'procPLURAL': function( template ) {
if ( template.title && template.parameters && mw.language.convertPlural ) {
// Check if we have forms to replace
- if ( template.parameters.length == 0 ) {
+ if ( template.parameters.length === 0 ) {
return '';
}
// Restore the count into a Number ( if it got converted earlier )
@@ -44,7 +44,7 @@ mw.language = {
* @return string Correct form for quantifier in this language
*/
'convertPlural': function( count, forms ){
- if ( !forms || forms.length == 0 ) {
+ if ( !forms || forms.length === 0 ) {
return '';
}
return ( parseInt( count, 10 ) == 1 ) ? forms[0] : forms[1];
@@ -96,6 +96,32 @@ mw.language = {
}
return integer ? parseInt( convertedNumber, 10 ) : convertedNumber;
},
+ /**
+ * Provides an alternative text depending on specified gender.
+ * Usage {{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.
+ *
+ * @param gender string male, female, or anything else for neutral.
+ * @param forms array List of gender forms
+ *
+ * @return string
+ */
+ 'gender': function( gender, forms ) {
+ if ( !forms || forms.length === 0 ) {
+ return '';
+ }
+ forms = mw.language.preConvertPlural( forms, 2 );
+ if ( gender === 'male' ) {
+ return forms[0];
+ }
+ if ( gender === 'female' ) {
+ return forms[1];
+ }
+ return ( forms.length === 3 ) ? forms[2] : forms[0];
+ },
+
// Digit Transform Table, populated by language classes where applicable
'digitTransformTable': null
};
diff --git a/resources/mediawiki.page/images/AJAXCategorySprite.png b/resources/mediawiki.page/images/AJAXCategorySprite.png
deleted file mode 100644
index d5f9cf48..00000000
--- a/resources/mediawiki.page/images/AJAXCategorySprite.png
+++ /dev/null
Binary files differ
diff --git a/resources/mediawiki.page/mediawiki.page.ajaxCategories.css b/resources/mediawiki.page/mediawiki.page.ajaxCategories.css
deleted file mode 100644
index dd991aaf..00000000
--- a/resources/mediawiki.page/mediawiki.page.ajaxCategories.css
+++ /dev/null
@@ -1,64 +0,0 @@
-.mw-addcategory-prompt {
- display: inline;
-}
-
-.mw-addcategory-prompt input {
- margin-left: 0.5em;
- margin-right: 0.5em;
-}
-
-.mw-remove-category {
- padding: 2px 8px;
- display:inline;
-}
-.mw-removed-category {
- text-decoration: line-through;
-}
-#catlinks:hover .icon {
- opacity: 1;
-}
-
-.mw-ajax-addcategory {
- padding-left: 30px;
- margin-right: 1em;
- cursor: pointer;
-}
-#catlinks .icon {
- cursor: pointer;
- padding: 1px 8px;
- margin: 0;
- background: url('images/AJAXCategorySprite.png') 0 0 no-repeat;
- opacity: 0.5;
-}
-#catlinks .icon-parent {
- cursor: pointer;
-}
-#catlinks .icon-parent:hover .icon {
- background-position-y: -16px;
-}
-#catlinks .no-text {
-}
-#catlinks .icon-close {
- background-position: 0 0;
-}
-#catlinks .icon-edit {
- background-position: -16px 0;
-}
-#catlinks .icon-tick {
- background-position: -32px 0;
-}
-#catlinks .icon-add {
- background-position: -64px 0;
-}
-#catlinks .icon-close:hover {
- background-position: 0 -16px;
-}
-#catlinks .icon-edit:hover {
- background-position: -16px -16px;
-}
-#catlinks .icon-tick:hover {
- background-position: -32px -16px;
-}
-#catlinks .icon-add:hover {
- background-position: -64px -16px;
-} \ No newline at end of file
diff --git a/resources/mediawiki.page/mediawiki.page.ready.js b/resources/mediawiki.page/mediawiki.page.ready.js
index eba5db11..370c3a19 100644
--- a/resources/mediawiki.page/mediawiki.page.ready.js
+++ b/resources/mediawiki.page/mediawiki.page.ready.js
@@ -5,7 +5,7 @@ jQuery( document ).ready( function( $ ) {
$( 'input[placeholder]' ).placeholder();
}
- /* Enable makeCollapse */
+ /* Enable makeCollapsible */
$( '.mw-collapsible' ).makeCollapsible();
/* Lazy load jquery.tablesorter */
diff --git a/resources/mediawiki.page/mediawiki.page.startup.js b/resources/mediawiki.page/mediawiki.page.startup.js
index 8af2fad9..a5541ef9 100644
--- a/resources/mediawiki.page/mediawiki.page.startup.js
+++ b/resources/mediawiki.page/mediawiki.page.startup.js
@@ -1,10 +1,18 @@
-( function( $ ) {
+( function ( $ ) {
- /* Client profile classes for <html> */
- /* Allows for easy hiding/showing of JS or no-JS-specific UI elements */
+ mw.page = {};
+ // Client profile classes for <html>
+ // Allows for easy hiding/showing of JS or no-JS-specific UI elements
$( 'html' )
.addClass('client-js' )
.removeClass( 'client-nojs' );
+ // Initialize utilities as soon as the document is ready (mw.util.$content,
+ // messageBoxNew, profile, tooltip access keys, Table of contents toggle, ..).
+ // Enqueued into domready from here instead of mediawiki.page.ready to ensure that it gets enqueued
+ // before other modules hook into document ready, so that mw.util.$content (defined by mw.util.init),
+ // is defined for them.
+ $( mw.util.init );
+
} )( jQuery );
diff --git a/resources/mediawiki.special/mediawiki.special.preferences.css b/resources/mediawiki.special/mediawiki.special.changeemail.css
index 8f628a95..3d53e8db 100644
--- a/resources/mediawiki.special/mediawiki.special.preferences.css
+++ b/resources/mediawiki.special/mediawiki.special.changeemail.css
@@ -1,14 +1,10 @@
#mw-emailaddress-validity {
padding: 2px 1em;
}
-body.ltr #mw-emailaddress-validity {
+#mw-emailaddress-validity {
border-bottom-right-radius: 0.8em;
border-top-right-radius: 0.8em;
}
-body.rtl #mw-emailaddress-validity {
- border-bottom-left-radius: 0.8em;
- border-top-left-radius: 0.8em;
-}
#mw-emailaddress-validity.valid {
border: 1px solid #80FF80;
background-color: #C0FFC0;
diff --git a/resources/mediawiki.special/mediawiki.special.changeemail.js b/resources/mediawiki.special/mediawiki.special.changeemail.js
new file mode 100644
index 00000000..6b4ed81d
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.changeemail.js
@@ -0,0 +1,40 @@
+/*
+ * JavaScript for Special:ChangeEmail
+ */
+( function( $, mw ) {
+
+/**
+ * Given an email validity status (true, false, null) update the label CSS class
+ */
+var updateMailValidityLabel = function( mail ) {
+ var isValid = mw.util.validateEmail( mail ),
+ $label = $( '#mw-emailaddress-validity' );
+
+ // We allow empty address
+ if( isValid === null ) {
+ $label.text( '' ).removeClass( 'valid invalid' );
+
+ // Valid
+ } else if ( isValid ) {
+ $label.text( mw.msg( 'email-address-validity-valid' ) ).addClass( 'valid' ).removeClass( 'invalid' );
+
+ // Not valid
+ } else {
+ $label.text( mw.msg( 'email-address-validity-invalid' ) ).addClass( 'invalid' ).removeClass( 'valid' );
+ }
+};
+
+// Lame tip to let user know if its email is valid. See bug 22449
+// Only bind once for 'blur' so that the user can fill it in without errors
+// After that look at every keypress for direct feedback if it was invalid onblur
+$( '#wpNewEmail' ).one( 'blur', function() {
+ if ( $( '#mw-emailaddress-validity' ).length === 0 ) {
+ $(this).after( '<label for="wpNewEmail" id="mw-emailaddress-validity"></label>' );
+ }
+ updateMailValidityLabel( $(this).val() );
+ $(this).keyup( function() {
+ updateMailValidityLabel( $(this).val() );
+ } );
+} );
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki.special/mediawiki.special.changeslist.css b/resources/mediawiki.special/mediawiki.special.changeslist.css
index cb4d2156..42afbcd7 100644
--- a/resources/mediawiki.special/mediawiki.special.changeslist.css
+++ b/resources/mediawiki.special/mediawiki.special.changeslist.css
@@ -7,7 +7,8 @@ table.mw-enhanced-rc {
border-spacing: 0;
}
-table.mw-enhanced-rc th, table.mw-enhanced-rc td {
+table.mw-enhanced-rc th,
+table.mw-enhanced-rc td {
padding: 0;
vertical-align: top;
}
@@ -30,8 +31,10 @@ table.mw-enhanced-rc td.mw-enhanced-rc-nested {
float: none;
}
-/* If JS is disabled, the arrow is still needed
- for spacing, but ideally shouldn't be shown */
+/**
+ * If JS is disabled, the arrow is still needed
+ * for spacing, but ideally shouldn't be shown
+ */
.mw-enhanced-rc .mw-rc-openarrow {
visibility: hidden;
}
diff --git a/resources/mediawiki.special/mediawiki.special.javaScriptTest.js b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js
new file mode 100644
index 00000000..d413f602
--- /dev/null
+++ b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js
@@ -0,0 +1,33 @@
+/*
+ * JavaScript for Special:JavaScriptTest
+ */
+jQuery( document ).ready( function( $ ) {
+
+ // Create useskin dropdown menu and reload onchange to the selected skin
+ // (only if a framework was found, not on error pages).
+ $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function() {
+
+ var $html = $( '<p><label for="useskin">'
+ + mw.message( 'javascripttest-pagetext-skins' ).escaped()
+ + ' '
+ + '</label></p>' ),
+ select = '<select name="useskin" id="useskin">';
+
+ // Build <select> further
+ $.each( mw.config.get( 'wgAvailableSkins' ), function( id ) {
+ select += '<option value="' + id + '"'
+ + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
+ + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
+ } );
+ select += '</select>';
+
+ // Bind onchange event handler and append to form
+ $html.append(
+ $( select ).change( function() {
+ window.location = QUnit.url( { useskin: $(this).val() } );
+ } )
+ );
+
+ return $html;
+ } );
+} );
diff --git a/resources/mediawiki.special/mediawiki.special.movePage.js b/resources/mediawiki.special/mediawiki.special.movePage.js
index 2f94cc06..68c2ed07 100644
--- a/resources/mediawiki.special/mediawiki.special.movePage.js
+++ b/resources/mediawiki.special/mediawiki.special.movePage.js
@@ -1,5 +1,5 @@
/* JavaScript for Special:MovePage */
jQuery( function( $ ) {
- $( '#wpReason' ).byteLimit();
+ $( '#wpReason, #wpNewTitleMain' ).byteLimit();
});
diff --git a/resources/mediawiki.special/mediawiki.special.preferences.js b/resources/mediawiki.special/mediawiki.special.preferences.js
index 2e07e7f1..b7200826 100644
--- a/resources/mediawiki.special/mediawiki.special.preferences.js
+++ b/resources/mediawiki.special/mediawiki.special.preferences.js
@@ -22,7 +22,7 @@ $legends.each( function( i, legend ) {
$legend.parent().show();
}
var ident = $legend.parent().attr( 'id' );
-
+
var $li = $( '<li/>', {
'class' : ( i === 0 ) ? 'selected' : null
});
@@ -37,7 +37,7 @@ $legends.each( function( i, legend ) {
var scrollTop = $(window).scrollTop();
window.location.hash = $(this).attr('href');
$(window).scrollTop(scrollTop);
-
+
$preftoc.find( 'li' ).removeClass( 'selected' );
$(this).parent().addClass( 'selected' );
$( '#preferences > fieldset' ).hide();
@@ -58,41 +58,6 @@ $( function() {
}
} );
-/**
- * Given an email validity status (true, false, null) update the label CSS class
- */
-var updateMailValidityLabel = function( mail ) {
- var isValid = mw.util.validateEmail( mail ),
- $label = $( '#mw-emailaddress-validity' );
-
- // We allow empty address
- if( isValid === null ) {
- $label.text( '' ).removeClass( 'valid invalid' );
-
- // Valid
- } else if ( isValid ) {
- $label.text( mw.msg( 'email-address-validity-valid' ) ).addClass( 'valid' ).removeClass( 'invalid' );
-
- // Not valid
- } else {
- $label.text( mw.msg( 'email-address-validity-invalid' ) ).addClass( 'invalid' ).removeClass( 'valid' );
- }
-};
-
-// Lame tip to let user know if its email is valid. See bug 22449
-// Only bind once for 'blur' so that the user can fill it in without errors
-// After that look at every keypress for direct feedback if it was invalid onblur
-$( '#mw-input-wpemailaddress' ).one( 'blur', function() {
- if ( $( '#mw-emailaddress-validity' ).length === 0 ) {
- $(this).after( '<label for="mw-input-wpemailaddress" id="mw-emailaddress-validity"></label>' );
- }
- updateMailValidityLabel( $(this).val() );
- $(this).keyup( function() {
- updateMailValidityLabel( $(this).val() );
- } );
-} );
-
-
/**
* Timezone functions.
@@ -145,7 +110,7 @@ var updateTimezoneSelection = function() {
$tzTextbox.val( minutesToHours( minuteDiff ) );
$tzSelect.val( 'other' );
$tzTextbox.get( 0 ).disabled = false;
- } else if ( type == 'other' ) {
+ } else if ( type == 'other' ) {
// Grab data from the textbox, parse it.
minuteDiff = hoursToMinutes( $tzTextbox.val() );
} else {
@@ -172,4 +137,4 @@ if ( $tzSelect.length && $tzTextbox.length ) {
$tzTextbox.blur( function() { updateTimezoneSelection(); } );
updateTimezoneSelection();
}
-} )( jQuery, mediaWiki );
+} )( jQuery, mediaWiki ); \ No newline at end of file
diff --git a/resources/mediawiki.special/mediawiki.special.recentchanges.js b/resources/mediawiki.special/mediawiki.special.recentchanges.js
index 7e284fbd..3d520f5e 100644
--- a/resources/mediawiki.special/mediawiki.special.recentchanges.js
+++ b/resources/mediawiki.special/mediawiki.special.recentchanges.js
@@ -9,24 +9,24 @@
var $select = null;
var rc = mw.special.recentchanges = {
-
+
/**
* Handler to disable/enable the namespace selector checkboxes when the
* special 'all' namespace is selected/unselected respectively.
*/
- updateCheckboxes: function() {
+ updateCheckboxes: function() {
// The option element for the 'all' namespace has an empty value
var isAllNS = ('' === $select.find('option:selected').val() );
// Iterates over checkboxes and propagate the selected option
$.each( checkboxes, function( i, id ) {
- $( '#' + id ).attr( 'disabled', isAllNS );
+ $( '#' + id ).prop( 'disabled', isAllNS );
});
},
init: function() {
// Populate
- $select = $( '#namespace' );
+ $select = $( '#namespace' );
// Bind to change event, and trigger once to set the initial state of the checkboxes.
$select.change( rc.updateCheckboxes ).change();
diff --git a/resources/mediawiki.special/mediawiki.special.search.css b/resources/mediawiki.special/mediawiki.special.search.css
index 89d55b0b..914e47e3 100644
--- a/resources/mediawiki.special/mediawiki.special.search.css
+++ b/resources/mediawiki.special/mediawiki.special.search.css
@@ -1,10 +1,10 @@
/**
- * Fixes sister projects box moving down the extract
+ * Fixes sister projects box moving down the extract
* of the first result (bug #16886).
- * It only happens when the window is small and
- * This changes slightly the layout for big screens
- * where there was space for the extracts and the
- * sister projects and thus it showed like in any
+ * It only happens when the window is small and
+ * This changes slightly the layout for big screens
+ * where there was space for the extracts and the
+ * sister projects and thus it showed like in any
* other browser.
*
* This will only affect IE 7 and lower
diff --git a/resources/mediawiki.special/mediawiki.special.search.js b/resources/mediawiki.special/mediawiki.special.search.js
index bac27fc6..8865d04c 100644
--- a/resources/mediawiki.special/mediawiki.special.search.js
+++ b/resources/mediawiki.special/mediawiki.special.search.js
@@ -22,7 +22,7 @@ var headerLinks = $('.search-types a');
$('#searchText, #powerSearchText').change(function() {
var searchterm = $(this).val();
headerLinks.each( function() {
- var parts = this.href.split( 'search=' );
+ var parts = $(this).attr('href').split( 'search=' );
var lastpart = '';
var prefix = 'search=';
if( parts.length > 1 && parts[1].indexOf('&') >= 0 ) {
@@ -34,4 +34,4 @@ $('#searchText, #powerSearchText').change(function() {
});
}).trigger('change');
-} ); \ No newline at end of file
+} );
diff --git a/resources/mediawiki.special/mediawiki.special.upload.js b/resources/mediawiki.special/mediawiki.special.upload.js
index 4a8622f5..85b3f3f5 100644
--- a/resources/mediawiki.special/mediawiki.special.upload.js
+++ b/resources/mediawiki.special/mediawiki.special.upload.js
@@ -25,7 +25,7 @@ jQuery( function( $ ) {
* @return boolean
*/
function fileIsPreviewable( file ) {
- var known = ['image/png', 'image/gif', 'image/jpeg'],
+ var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'],
tooHuge = 10 * 1024 * 1024;
return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge;
}
@@ -46,27 +46,22 @@ jQuery( function( $ ) {
var previewSize = 180,
thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
'<div class="thumbinner">' +
- '<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>' +
+ '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' +
'<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
'</div>' +
'</div>' );
thumb.find( '.filename' ).text( file.name ).end()
.find( '.fileinfo' ).text( prettySize( file.size ) ).end();
-
- var ctx = thumb.find( 'canvas' )[0].getContext( '2d' ),
- spinner = new Image();
- spinner.onload = function() {
- ctx.drawImage( spinner, (previewSize - spinner.width) / 2,
- (previewSize - spinner.height) / 2 );
- };
- spinner.src = mw.config.get( 'wgScriptPath' ) + '/skins/common/images/spinner.gif';
+
+ var $canvas = $('<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>'),
+ ctx = $canvas[0].getContext( '2d' );
$( '#mw-htmlform-source' ).parent().prepend( thumb );
var meta;
fetchPreview( file, function( dataURL ) {
var img = new Image(),
rotation = 0;
-
+
if ( meta && meta.tiff && meta.tiff.Orientation ) {
rotation = (360 - function () {
// See includes/media/Bitmap.php
@@ -82,7 +77,7 @@ jQuery( function( $ ) {
}
}() ) % 360;
}
-
+
img.onload = function() {
var width, height, x, y, dx, dy, logicalWidth, logicalHeight;
// Fit the image within the previewSizexpreviewSize box
@@ -98,7 +93,7 @@ jQuery( function( $ ) {
dy = (180 - height) / 2;
switch ( rotation ) {
// If a rotation is applied, the direction of the axis
- // changes as well. You can derive the values below by
+ // changes as well. You can derive the values below by
// drawing on paper an axis system, rotate it and see
// where the positive axis direction is
case 0:
@@ -108,7 +103,7 @@ jQuery( function( $ ) {
logicalHeight = img.height;
break;
case 90:
-
+
x = dx;
y = dy - previewSize;
logicalWidth = img.height;
@@ -127,11 +122,12 @@ jQuery( function( $ ) {
logicalHeight = img.width;
break;
}
-
+
ctx.clearRect( 0, 0, 180, 180 );
ctx.rotate( rotation / 180 * Math.PI );
ctx.drawImage( img, x, y, width, height );
-
+ thumb.find('.mw-small-spinner').replaceWith($canvas);
+
// Image size
var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
', ' + prettySize( file.size );
@@ -160,20 +156,33 @@ jQuery( function( $ ) {
*/
function fetchPreview( file, callback, callbackBinary ) {
var reader = new FileReader();
- reader.onload = function() {
- if ( callbackBinary ) {
- callbackBinary( reader.result );
- reader.onload = function() {
- callback( reader.result );
- };
- reader.readAsDataURL( file );
- } else {
- callback( reader.result );
- }
- };
if ( callbackBinary ) {
+ // To fetch JPEG metadata we need a binary string; start there.
+ // todo:
+ reader.onload = function() {
+ callbackBinary( reader.result );
+
+ // Now run back through the regular code path.
+ fetchPreview(file, callback );
+ };
reader.readAsBinaryString( file );
+ } else if ('URL' in window && 'createObjectURL' in window.URL) {
+ // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en/DOM/window.URL.createObjectURL>
+ // WebKit has it in a namespace for now but that's ok. ;)
+ //
+ // Lifetime of this URL is until document close, which is fine
+ // for Special:Upload -- if this code gets used on longer-running
+ // pages, add a revokeObjectURL() when it's no longer needed.
+ //
+ // Prefer this over readAsDataURL for Firefox 7 due to bug reading
+ // some SVG files from data URIs <https://bugzilla.mozilla.org/show_bug.cgi?id=694165>
+ callback(window.URL.createObjectURL(file));
} else {
+ // This ends up decoding the file to base-64 and back again, which
+ // feels horribly inefficient.
+ reader.onload = function() {
+ callback( reader.result );
+ };
reader.readAsDataURL( file );
}
}
@@ -200,7 +209,7 @@ jQuery( function( $ ) {
function clearPreview() {
$( '#mw-upload-thumbnail' ).remove();
}
-
+
/**
* Check if the file does not exceed the maximum size
*/
@@ -213,18 +222,18 @@ jQuery( function( $ ) {
return sizes['*'];
}
$( '.mw-upload-source-error' ).remove();
-
- var maxSize = getMaxUploadSize( 'file' );
+
+ var maxSize = getMaxUploadSize( 'file' );
if ( file.size > maxSize ) {
- var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
+ var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
$( '#wpUploadFile' ).after( error );
return false;
}
return true;
}
-
-
+
+
/**
* Initialization
*/
@@ -235,11 +244,11 @@ jQuery( function( $ ) {
if ( this.files && this.files.length ) {
// Note: would need to be updated to handle multiple files.
var file = this.files[0];
-
+
if ( !checkMaxUploadSize( file ) ) {
return;
}
-
+
if ( fileIsPreviewable( file ) ) {
showPreview( file );
}
@@ -261,9 +270,9 @@ jQuery( function ( $ ) {
$( '.mw-upload-source-error' ).remove();
if ( this.checked ) {
// Disable all inputs
- $( 'input[name!="wpSourceType"]', rows ).attr( 'disabled', true );
+ $( 'input[name!="wpSourceType"]', rows ).prop( 'disabled', 'disabled' );
// Re-enable the current one
- $( 'input', currentRow ).attr( 'disabled', false );
+ $( 'input', currentRow ).prop( 'disabled', false );
}
};
}() );
diff --git a/resources/mediawiki/mediawiki.Uri.js b/resources/mediawiki/mediawiki.Uri.js
index 7ff8dda4..26fdfa9e 100644
--- a/resources/mediawiki/mediawiki.Uri.js
+++ b/resources/mediawiki/mediawiki.Uri.js
@@ -56,7 +56,7 @@
*
*/
-( function( $ ) {
+( function( $, mw ) {
/**
* Function that's useful when constructing the URI string -- we frequently encounter the pattern of
@@ -89,172 +89,213 @@
'host', // www.test.com
'port', // 81
'path', // /dir/dir.2/index.htm
- 'query', // q1=0&&test1&test2=value (will become { q1: 0, test1: '', test2: 'value' } )
+ 'query', // q1=0&&test1&test2=value (will become { q1: '0', test1: '', test2: 'value' } )
'fragment' // top
];
- /**
- * Constructs URI object. Throws error if arguments are illegal/impossible, or otherwise don't parse.
- * @constructor
- * @param {!Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone). Object must have non-blank 'protocol', 'host', and 'path' properties.
- * @param {Boolean} strict mode (when parsing a string)
- */
- mw.Uri = function( uri, strictMode ) {
- strictMode = !!strictMode;
- if ( uri !== undefined && uri !== null || uri !== '' ) {
- if ( typeof uri === 'string' ) {
- this._parse( uri, strictMode );
- } else if ( typeof uri === 'object' ) {
- var _this = this;
- $.each( properties, function( i, property ) {
- _this[property] = uri[property];
- } );
- if ( this.query === undefined ) {
- this.query = {};
- }
- }
- }
- if ( !( this.protocol && this.host && this.path ) ) {
- throw new Error( 'Bad constructor arguments' );
- }
- };
/**
- * Standard encodeURIComponent, with extra stuff to make all browsers work similarly and more compliant with RFC 3986
- * Similar to rawurlencode from PHP and our JS library mw.util.rawurlencode, but we also replace space with a +
- * @param {String} string
- * @return {String} encoded for URI
+ * We use a factory to inject a document location, for relative URLs, including protocol-relative URLs.
+ * so the library is still testable & purely functional.
*/
- mw.Uri.encode = function( s ) {
- return encodeURIComponent( s )
- .replace( /!/g, '%21').replace( /'/g, '%27').replace( /\(/g, '%28')
- .replace( /\)/g, '%29').replace( /\*/g, '%2A')
- .replace( /%20/g, '+' );
- };
-
- /**
- * Standard decodeURIComponent, with '+' to space
- * @param {String} string encoded for URI
- * @return {String} decoded string
- */
- mw.Uri.decode = function( s ) {
- return decodeURIComponent( s ).replace( /\+/g, ' ' );
- };
-
- mw.Uri.prototype = {
+ mw.UriRelative = function( documentLocation ) {
/**
- * Parse a string and set our properties accordingly.
- * @param {String} URI
- * @param {Boolean} strictness
- * @return {Boolean} success
+ * Constructs URI object. Throws error if arguments are illegal/impossible, or otherwise don't parse.
+ * @constructor
+ * @param {Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone).
+ * Object must have non-blank 'protocol', 'host', and 'path' properties.
+ * @param {Object|Boolean} Object with options, or (backwards compatibility) a boolean for strictMode
+ * - strictMode {Boolean} Trigger strict mode parsing of the url. Default: false
+ * - overrideKeys {Boolean} Wether to let duplicate query parameters override eachother (true) or automagically
+ * convert to an array (false, default).
*/
- _parse: function( str, strictMode ) {
- var matches = parser[ strictMode ? 'strict' : 'loose' ].exec( str );
- var uri = this;
- $.each( properties, function( i, property ) {
- uri[ property ] = matches[ i+1 ];
- } );
+ function Uri( uri, options ) {
+ options = typeof options === 'object' ? options : { strictMode: !!options };
+ options = $.extend( {
+ strictMode: false,
+ overrideKeys: false
+ }, options );
- // uri.query starts out as the query string; we will parse it into key-val pairs then make
- // that object the "query" property.
- // we overwrite query in uri way to make cloning easier, it can use the same list of properties.
- var q = {};
- // using replace to iterate over a string
- if ( uri.query ) {
- uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ($0, $1, $2, $3) {
- if ( $1 ) {
- var k = mw.Uri.decode( $1 );
- var v = ( $2 === '' || $2 === undefined ) ? null : mw.Uri.decode( $3 );
- if ( typeof q[ k ] === 'string' ) {
- q[ k ] = [ q[ k ] ];
- }
- if ( typeof q[ k ] === 'object' ) {
- q[ k ].push( v );
- } else {
- q[ k ] = v;
- }
+ if ( uri !== undefined && uri !== null || uri !== '' ) {
+ if ( typeof uri === 'string' ) {
+ this._parse( uri, options );
+ } else if ( typeof uri === 'object' ) {
+ var _this = this;
+ $.each( properties, function( i, property ) {
+ _this[property] = uri[property];
+ } );
+ if ( this.query === undefined ) {
+ this.query = {};
}
- } );
+ }
}
- this.query = q;
- },
- /**
- * Returns user and password portion of a URI.
- * @return {String}
- */
- getUserInfo: function() {
- return cat( '', this.user, cat( ':', this.password, '' ) );
- },
+ // protocol-relative URLs
+ if ( !this.protocol ) {
+ this.protocol = defaultProtocol;
+ }
- /**
- * Gets host and port portion of a URI.
- * @return {String}
- */
- getHostPort: function() {
- return this.host + cat( ':', this.port, '' );
- },
+ if ( !( this.protocol && this.host && this.path ) ) {
+ throw new Error( 'Bad constructor arguments' );
+ }
+ }
/**
- * Returns the userInfo and host and port portion of the URI.
- * In most real-world URLs, this is simply the hostname, but it is more general.
- * @return {String}
+ * Standard encodeURIComponent, with extra stuff to make all browsers work similarly and more compliant with RFC 3986
+ * Similar to rawurlencode from PHP and our JS library mw.util.rawurlencode, but we also replace space with a +
+ * @param {String} string
+ * @return {String} encoded for URI
*/
- getAuthority: function() {
- return cat( '', this.getUserInfo(), '@' ) + this.getHostPort();
- },
+ Uri.encode = function( s ) {
+ return encodeURIComponent( s )
+ .replace( /!/g, '%21').replace( /'/g, '%27').replace( /\(/g, '%28')
+ .replace( /\)/g, '%29').replace( /\*/g, '%2A')
+ .replace( /%20/g, '+' );
+ };
/**
- * Returns the query arguments of the URL, encoded into a string
- * Does not preserve the order of arguments passed into the URI. Does handle escaping.
- * @return {String}
+ * Standard decodeURIComponent, with '+' to space
+ * @param {String} string encoded for URI
+ * @return {String} decoded string
*/
- getQueryString: function() {
- var args = [];
- $.each( this.query, function( key, val ) {
- var k = mw.Uri.encode( key );
- var vals = val === null ? [ null ] : $.makeArray( val );
- $.each( vals, function( i, v ) {
- args.push( k + ( v === null ? '' : '=' + mw.Uri.encode( v ) ) );
+ Uri.decode = function( s ) {
+ return decodeURIComponent( s.replace( /\+/g, '%20' ) );
+ };
+
+ Uri.prototype = {
+
+ /**
+ * Parse a string and set our properties accordingly.
+ * @param {String} URI
+ * @param {Object} options
+ * @return {Boolean} success
+ */
+ _parse: function( str, options ) {
+ var matches = parser[ options.strictMode ? 'strict' : 'loose' ].exec( str );
+ var uri = this;
+ $.each( properties, function( i, property ) {
+ uri[ property ] = matches[ i+1 ];
} );
- } );
- return args.join( '&' );
- },
- /**
- * Returns everything after the authority section of the URI
- * @return {String}
- */
- getRelativePath: function() {
- return this.path + cat( '?', this.getQueryString(), '', true ) + cat( '#', this.fragment, '' );
- },
+ // uri.query starts out as the query string; we will parse it into key-val pairs then make
+ // that object the "query" property.
+ // we overwrite query in uri way to make cloning easier, it can use the same list of properties.
+ var q = {};
+ // using replace to iterate over a string
+ if ( uri.query ) {
+ uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ($0, $1, $2, $3) {
+ if ( $1 ) {
+ var k = Uri.decode( $1 );
+ var v = ( $2 === '' || $2 === undefined ) ? null : Uri.decode( $3 );
- /**
- * Gets the entire URI string. May not be precisely the same as input due to order of query arguments.
- * @return {String} the URI string
- */
- toString: function() {
- return this.protocol + '://' + this.getAuthority() + this.getRelativePath();
- },
+ // If overrideKeys, always (re)set top level value.
+ // If not overrideKeys but this key wasn't set before, then we set it as well.
+ if ( options.overrideKeys || q[ k ] === undefined ) {
+ q[ k ] = v;
- /**
- * Clone this URI
- * @return {Object} new URI object with same properties
- */
- clone: function() {
- return new mw.Uri( this );
- },
+ // Use arrays if overrideKeys is false and key was already seen before
+ } else {
+ // Once before, still a string, turn into an array
+ if ( typeof q[ k ] === 'string' ) {
+ q[ k ] = [ q[ k ] ];
+ }
+ // Add to the array
+ if ( $.isArray( q[ k ] ) ) {
+ q[ k ].push( v );
+ }
+ }
+ }
+ } );
+ }
+ this.query = q;
+ },
- /**
- * Extend the query -- supply query parameters to override or add to ours
- * @param {Object} query parameters in key-val form to override or add
- * @return {Object} this URI object
- */
- extend: function( parameters ) {
- $.extend( this.query, parameters );
- return this;
- }
+ /**
+ * Returns user and password portion of a URI.
+ * @return {String}
+ */
+ getUserInfo: function() {
+ return cat( '', this.user, cat( ':', this.password, '' ) );
+ },
+
+ /**
+ * Gets host and port portion of a URI.
+ * @return {String}
+ */
+ getHostPort: function() {
+ return this.host + cat( ':', this.port, '' );
+ },
+
+ /**
+ * Returns the userInfo and host and port portion of the URI.
+ * In most real-world URLs, this is simply the hostname, but it is more general.
+ * @return {String}
+ */
+ getAuthority: function() {
+ return cat( '', this.getUserInfo(), '@' ) + this.getHostPort();
+ },
+
+ /**
+ * Returns the query arguments of the URL, encoded into a string
+ * Does not preserve the order of arguments passed into the URI. Does handle escaping.
+ * @return {String}
+ */
+ getQueryString: function() {
+ var args = [];
+ $.each( this.query, function( key, val ) {
+ var k = Uri.encode( key );
+ var vals = val === null ? [ null ] : $.makeArray( val );
+ $.each( vals, function( i, v ) {
+ args.push( k + ( v === null ? '' : '=' + Uri.encode( v ) ) );
+ } );
+ } );
+ return args.join( '&' );
+ },
+
+ /**
+ * Returns everything after the authority section of the URI
+ * @return {String}
+ */
+ getRelativePath: function() {
+ return this.path + cat( '?', this.getQueryString(), '', true ) + cat( '#', this.fragment, '' );
+ },
+
+ /**
+ * Gets the entire URI string. May not be precisely the same as input due to order of query arguments.
+ * @return {String} the URI string
+ */
+ toString: function() {
+ return this.protocol + '://' + this.getAuthority() + this.getRelativePath();
+ },
+
+ /**
+ * Clone this URI
+ * @return {Object} new URI object with same properties
+ */
+ clone: function() {
+ return new Uri( this );
+ },
+
+ /**
+ * Extend the query -- supply query parameters to override or add to ours
+ * @param {Object} query parameters in key-val form to override or add
+ * @return {Object} this URI object
+ */
+ extend: function( parameters ) {
+ $.extend( this.query, parameters );
+ return this;
+ }
+ };
+
+ var defaultProtocol = ( new Uri( documentLocation ) ).protocol;
+
+ return Uri;
};
-} )( jQuery );
+ // if we are running in a browser, inject the current document location, for relative URLs
+ if ( document && document.location && document.location.href ) {
+ mw.Uri = mw.UriRelative( document.location.href );
+ }
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki/mediawiki.debug.css b/resources/mediawiki/mediawiki.debug.css
new file mode 100644
index 00000000..923d4a47
--- /dev/null
+++ b/resources/mediawiki/mediawiki.debug.css
@@ -0,0 +1,185 @@
+.mw-debug {
+ width: 100%;
+ text-align: left;
+ background-color: #eee;
+ border-top: 1px solid #aaa;
+}
+
+.mw-debug pre {
+ font-family: Monaco, "Consolas", "Lucida Console", "Courier New", monospace;
+ font-size: 11px;
+ padding: 0;
+ margin: 0;
+ background: none;
+ border: none;
+}
+
+.mw-debug table {
+ border-spacing: 0;
+ width: 100%;
+ table-layout: fixed;
+}
+
+.mw-debug table tr {
+ background-color: #fff;
+}
+
+.mw-debug table tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+
+.mw-debug table td, .mw-debug table th {
+ padding: 4px 10px;
+}
+
+.mw-debug table td {
+ border-bottom: 1px solid #eee;
+ word-wrap: break-word;
+}
+
+.mw-debug table td.nr {
+ text-align: right;
+}
+
+.mw-debug table td span.stats {
+ color: #808080;
+}
+
+.mw-debug ul {
+ margin: 0;
+ list-style: none;
+}
+
+.mw-debug li {
+ padding: 4px 0;
+ width: 100%;
+}
+
+.mw-debug-bits {
+ text-align: center;
+ border-bottom: 1px solid #aaa;
+}
+
+.mw-debug-bit {
+ display: inline-block;
+ padding: 10px 5px;
+ font-size: 13px;
+ /* IE-hack for display: inline-block */
+ zoom: 1;
+ *display:inline;
+}
+
+.mw-debug-panelink {
+ background-color: #eee;
+ border-right: 1px solid #ccc;
+}
+
+.mw-debug-panelink:first-child {
+ border-left: 1px solid #ccc;
+}
+
+.mw-debug-panelink:hover {
+ background-color: #fefefe;
+ cursor: pointer;
+}
+.mw-debug-panelink.current {
+ background-color: #dedede;
+
+}
+a.mw-debug-panelabel,
+a.mw-debug-panelabel:visited {
+ color: #000;
+}
+
+.mw-debug-pane {
+ height: 300px;
+ overflow: scroll;
+ display: none;
+ font-size: 11px;
+ background-color: #e1eff2;
+ box-sizing: border-box;
+}
+
+#mw-debug-pane-debuglog,
+#mw-debug-pane-request {
+ padding: 20px;
+}
+
+#mw-debug-pane-request table {
+ width: 100%;
+ margin: 10px 0 30px;
+}
+
+#mw-debug-pane-request tr,
+#mw-debug-pane-request th,
+#mw-debug-pane-request td,
+#mw-debug-pane-request table {
+ border: 1px solid #D0DBB3;
+ border-collapse: collapse;
+ margin: 0;
+}
+
+#mw-debug-pane-request th,
+#mw-debug-pane-request td {
+ font-size: 12px;
+ padding: 8px 10px;
+}
+
+#mw-debug-pane-request th {
+ background-color: #F1F7E2;
+ font-weight: bold;
+}
+
+#mw-debug-pane-request td {
+ background-color: white;
+}
+
+#mw-debug-console tr td:first-child {
+ font-weight: bold;
+ vertical-align: top;
+}
+
+#mw-debug-console tr td:last-child {
+ vertical-align: top;
+}
+
+.mw-debug-console-log {
+ background-color: #add8e6;
+}
+
+.mw-debug-console-warn {
+ background-color: #ffa07a;
+}
+
+.mw-debug-console-deprecated {
+ background-color: #ffb6c1;
+}
+
+.mw-debug-backtrace {
+ padding: 5px 10px;
+ margin: 5px;
+ background-color: #dedede;
+}
+
+.mw-debug-backtrace span {
+ font-weight: bold;
+ color: #111;
+}
+
+.mw-debug-backtrace ul {
+ padding-left: 10px;
+}
+
+.mw-debug-backtrace li {
+ width: auto;
+ padding: 0;
+ color: #333;
+ font-size: 10px;
+ margin-bottom: 0;
+ line-height: 1em;
+}
+
+/* Cheapo hack to hide the first 3 lines of the backtrace */
+.mw-debug-backtrace li:nth-child(-n+3) {
+ display: none;
+}
diff --git a/resources/mediawiki/mediawiki.debug.init.js b/resources/mediawiki/mediawiki.debug.init.js
new file mode 100644
index 00000000..0f85e80d
--- /dev/null
+++ b/resources/mediawiki/mediawiki.debug.init.js
@@ -0,0 +1,3 @@
+jQuery( function () {
+ mediaWiki.Debug.init();
+} );
diff --git a/resources/mediawiki/mediawiki.debug.js b/resources/mediawiki/mediawiki.debug.js
new file mode 100644
index 00000000..a2bfbcbe
--- /dev/null
+++ b/resources/mediawiki/mediawiki.debug.js
@@ -0,0 +1,351 @@
+/**
+ * JavaScript for the new debug toolbar, enabled through $wgDebugToolbar.
+ *
+ * @author John Du Hart
+ * @since 1.19
+ */
+
+( function ( $, mw, undefined ) {
+"use strict";
+
+ var hovzer = $.getFootHovzer();
+
+ var debug = mw.Debug = {
+ /**
+ * Toolbar container element
+ *
+ * @var {jQuery}
+ */
+ $container: null,
+
+ /**
+ * Object containing data for the debug toolbar
+ *
+ * @var {Object}
+ */
+ data: {},
+
+ /**
+ * Initializes the debugging pane.
+ * Shouldn't be called before the document is ready
+ * (since it binds to elements on the page).
+ *
+ * @param {Object} data, defaults to 'debugInfo' from mw.config
+ */
+ init: function ( data ) {
+
+ this.data = data || mw.config.get( 'debugInfo' );
+ this.buildHtml();
+
+ // Insert the container into the DOM
+ hovzer.$.append( this.$container );
+ hovzer.update();
+
+ $( '.mw-debug-panelink' ).click( this.switchPane );
+ },
+
+ /**
+ * Switches between panes
+ *
+ * @todo Store cookie for last pane open
+ * @context {Element}
+ * @param {jQuery.Event} e
+ */
+ switchPane: function ( e ) {
+ var currentPaneId = debug.$container.data( 'currentPane' ),
+ requestedPaneId = $(this).prop( 'id' ).substr( 9 ),
+ $currentPane = $( '#mw-debug-pane-' + currentPaneId ),
+ $requestedPane = $( '#mw-debug-pane-' + requestedPaneId ),
+ hovDone = false;
+
+ function updateHov() {
+ if ( !hovDone ) {
+ hovzer.update();
+ hovDone = true;
+ }
+ }
+
+ // Skip hash fragment handling. Prevents screen from jumping.
+ e.preventDefault();
+
+ $( this ).addClass( 'current ');
+ $( '.mw-debug-panelink' ).not( this ).removeClass( 'current ');
+
+ // Hide the current pane
+ if ( requestedPaneId === currentPaneId ) {
+ $currentPane.slideUp( updateHov );
+ debug.$container.data( 'currentPane', null );
+ return;
+ }
+
+ debug.$container.data( 'currentPane', requestedPaneId );
+
+ if ( currentPaneId === undefined || currentPaneId === null ) {
+ $requestedPane.slideDown( updateHov );
+ } else {
+ $currentPane.hide();
+ $requestedPane.show();
+ updateHov();
+ }
+ },
+
+ /**
+ * Constructs the HTML for the debugging toolbar
+ */
+ buildHtml: function () {
+ var $container, $bits, panes, id;
+
+ $container = $( '<div id="mw-debug-toolbar" class="mw-debug"></div>' );
+
+ $bits = $( '<div class="mw-debug-bits"></div>' );
+
+ /**
+ * Returns a jQuery element for a debug-bit div
+ *
+ * @param id
+ * @return {jQuery}
+ */
+ function bitDiv( id ) {
+ return $( '<div>' ).attr({
+ id: 'mw-debug-' + id,
+ 'class': 'mw-debug-bit'
+ })
+ .appendTo( $bits );
+ }
+
+ /**
+ * Returns a jQuery element for a pane link
+ *
+ * @param id
+ * @param text
+ * @return {jQuery}
+ */
+ function paneLabel( id, text ) {
+ return $( '<a>' )
+ .attr({
+ 'class': 'mw-debug-panelabel',
+ href: '#mw-debug-pane-' + id
+ })
+ .text( text );
+ }
+
+ /**
+ * Returns a jQuery element for a debug-bit div with a for a pane link
+ *
+ * @param id CSS id snippet. Will be prefixed with 'mw-debug-'
+ * @param text Text to show
+ * @param count Optional count to show
+ * @return {jQuery}
+ */
+ function paneTriggerBitDiv( id, text, count ) {
+ if( count ) {
+ text = text + ' (' + count + ')';
+ }
+ return $( '<div>' ).attr({
+ id: 'mw-debug-' + id,
+ 'class': 'mw-debug-bit mw-debug-panelink'
+ })
+ .append( paneLabel( id, text ) )
+ .appendTo( $bits );
+ }
+
+ paneTriggerBitDiv( 'console', 'Console', this.data.log.length );
+
+ paneTriggerBitDiv( 'querylist', 'Queries', this.data.queries.length );
+
+ paneTriggerBitDiv( 'debuglog', 'Debug log', this.data.debugLog.length );
+
+ paneTriggerBitDiv( 'request', 'Request' );
+
+ paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length );
+
+ bitDiv( 'mwversion' )
+ .append( $( '<a href="//www.mediawiki.org/"></a>' ).text( 'MediaWiki' ) )
+ .append( ': ' + this.data.mwVersion );
+
+ bitDiv( 'phpversion' )
+ .append( $( '<a href="//www.php.net/"></a>' ).text( 'PHP' ) )
+ .append( ': ' + this.data.phpVersion );
+
+ bitDiv( 'time' )
+ .text( 'Time: ' + this.data.time.toFixed( 5 ) );
+
+ bitDiv( 'memory' )
+ .text( 'Memory: ' + this.data.memory )
+ .append( $( '<span title="Peak usage"></span>' ).text( ' (' + this.data.memoryPeak + ')' ) );
+
+
+ $bits.appendTo( $container );
+
+ panes = {
+ console: this.buildConsoleTable(),
+ querylist: this.buildQueryTable(),
+ debuglog: this.buildDebugLogTable(),
+ request: this.buildRequestPane(),
+ includes: this.buildIncludesPane()
+ };
+
+ for ( id in panes ) {
+ if ( !panes.hasOwnProperty( id ) ) {
+ continue;
+ }
+
+ $( '<div>' )
+ .attr({
+ 'class': 'mw-debug-pane',
+ id: 'mw-debug-pane-' + id
+ })
+ .append( panes[id] )
+ .appendTo( $container );
+ }
+
+ this.$container = $container;
+ },
+
+ /**
+ * Builds the console panel
+ */
+ buildConsoleTable: function () {
+ var $table, entryTypeText, i, length, entry;
+
+ $table = $( '<table id="mw-debug-console">' );
+
+ $('<colgroup>').css( 'width', /*padding=*/20 + ( 10*/*fontSize*/11 ) ).appendTo( $table );
+ $('<colgroup>').appendTo( $table );
+ $('<colgroup>').css( 'width', 350 ).appendTo( $table );
+
+
+ entryTypeText = function( entryType ) {
+ switch ( entryType ) {
+ case 'log':
+ return 'Log';
+ case 'warn':
+ return 'Warning';
+ case 'deprecated':
+ return 'Deprecated';
+ default:
+ return 'Unknown';
+ }
+ };
+
+ for ( i = 0, length = this.data.log.length; i < length; i += 1 ) {
+ entry = this.data.log[i];
+ entry.typeText = entryTypeText( entry.type );
+
+ $( '<tr>' )
+ .append( $( '<td>' )
+ .text( entry.typeText )
+ .attr( 'class', 'mw-debug-console-' + entry.type )
+ )
+ .append( $( '<td>' ).html( entry.msg ) )
+ .append( $( '<td>' ).text( entry.caller ) )
+ .appendTo( $table );
+ }
+
+ return $table;
+ },
+
+ /**
+ * Query list pane
+ */
+ buildQueryTable: function () {
+ var $table, i, length, query;
+
+ $table = $( '<table id="mw-debug-querylist"></table>' );
+
+ $( '<tr>' )
+ .append( $('<th>#</th>').css( 'width', '4em' ) )
+ .append( $('<th>SQL</th>') )
+ .append( $('<th>Time</th>').css( 'width', '8em' ) )
+ .append( $('<th>Call</th>').css( 'width', '18em' ) )
+ .appendTo( $table );
+
+ for ( i = 0, length = this.data.queries.length; i < length; i += 1 ) {
+ query = this.data.queries[i];
+
+ $( '<tr>' )
+ .append( $( '<td>' ).text( i + 1 ) )
+ .append( $( '<td>' ).text( query.sql ) )
+ .append( $( '<td class="stats">' ).text( ( query.time * 1000 ).toFixed( 4 ) + 'ms' ) )
+ .append( $( '<td>' ).text( query['function'] ) )
+ .appendTo( $table );
+ }
+
+
+ return $table;
+ },
+
+ /**
+ * Legacy debug log pane
+ */
+ buildDebugLogTable: function () {
+ var $list, i, length, line;
+ $list = $( '<ul>' );
+
+ for ( i = 0, length = this.data.debugLog.length; i < length; i += 1 ) {
+ line = this.data.debugLog[i];
+ $( '<li>' )
+ .html( mw.html.escape( line ).replace( /\n/g, "<br />\n" ) )
+ .appendTo( $list );
+ }
+
+ return $list;
+ },
+
+ /**
+ * Request information pane
+ */
+ buildRequestPane: function () {
+
+ function buildTable( title, data ) {
+ var $unit, $table, key;
+
+ $unit = $( '<div>' ).append( $( '<h2>' ).text( title ) );
+
+ $table = $( '<table>' ).appendTo( $unit );
+
+ $( '<tr>' )
+ .html( '<th>Key</th><th>Value</th>' )
+ .appendTo( $table );
+
+ for ( key in data ) {
+ if ( !data.hasOwnProperty( key ) ) {
+ continue;
+ }
+
+ $( '<tr>' )
+ .append( $( '<th>' ).text( key ) )
+ .append( $( '<td>' ).text( data[key] ) )
+ .appendTo( $table );
+ }
+
+ return $unit;
+ }
+
+ return $( '<div>' )
+ .text( this.data.request.method + ' ' + this.data.request.url )
+ .append( buildTable( 'Headers', this.data.request.headers ) )
+ .append( buildTable( 'Parameters', this.data.request.params ) );
+ },
+
+ /**
+ * Included files pane
+ */
+ buildIncludesPane: function () {
+ var $table, i, length, file;
+
+ $table = $( '<table>' );
+
+ for ( i = 0, length = this.data.includes.length; i < length; i += 1 ) {
+ file = this.data.includes[i];
+ $( '<tr>' )
+ .append( $( '<td>' ).text( file.name ) )
+ .append( $( '<td class="nr">' ).text( file.size ) )
+ .appendTo( $table );
+ }
+
+ return $table;
+ }
+ };
+
+} )( jQuery, mediaWiki );
diff --git a/resources/mediawiki/mediawiki.feedback.css b/resources/mediawiki/mediawiki.feedback.css
new file mode 100644
index 00000000..6bd47bb2
--- /dev/null
+++ b/resources/mediawiki/mediawiki.feedback.css
@@ -0,0 +1,9 @@
+.feedback-spinner {
+ display: inline-block;
+ zoom: 1;
+ *display: inline; /* IE7 and below */
+ /* @embed */
+ background: url(mediawiki.feedback.spinner.gif);
+ width: 18px;
+ height: 18px;
+}
diff --git a/resources/mediawiki/mediawiki.feedback.js b/resources/mediawiki/mediawiki.feedback.js
new file mode 100644
index 00000000..9a4a7298
--- /dev/null
+++ b/resources/mediawiki/mediawiki.feedback.js
@@ -0,0 +1,242 @@
+/**
+ * mediawiki.Feedback
+ *
+ * @author Ryan Kaldari, 2010
+ * @author Neil Kandalgaonkar, 2010-11
+ * @since 1.19
+ *
+ * This is a way of getting simple feedback from users. It's useful
+ * for testing new features -- users can give you feedback without
+ * the difficulty of opening a whole new talk page. For this reason,
+ * it also tends to collect a wider range of both positive and negative
+ * comments. However you do need to tend to the feedback page. It will
+ * get long relatively quickly, and you often get multiple messages
+ * reporting the same issue.
+ *
+ * It takes the form of thing on your page which, when clicked, opens a small
+ * dialog box. Submitting that dialog box appends its contents to a
+ * wiki page that you specify, as a new section.
+ *
+ * Not compatible with LiquidThreads.
+ *
+ * Minimal example in how to use it:
+ *
+ * var feedback = new mw.Feedback();
+ * $( '#myButton' ).click( function() { feedback.launch(); } );
+ *
+ * You can also launch the feedback form with a prefilled subject and body.
+ * See the docs for the launch() method.
+ */
+( function( mw, $, undefined ) {
+ /**
+ * Thingy for collecting user feedback on a wiki page
+ * @param {Array} options -- optional, all properties optional.
+ * api: {mw.Api} if omitted, will just create a standard API
+ * title: {mw.Title} the title of the page where you collect feedback. Defaults to "Feedback".
+ * dialogTitleMessageKey: {String} message key for the title of the dialog box
+ * bugsLink: {mw.Uri|String} url where bugs can be posted
+ * bugsListLink: {mw.Uri|String} url where bugs can be listed
+ */
+ mw.Feedback = function( options ) {
+ if ( options === undefined ) {
+ options = {};
+ }
+
+ if ( options.api === undefined ) {
+ options.api = new mw.Api();
+ }
+
+ if ( options.title === undefined ) {
+ options.title = new mw.Title( 'Feedback' );
+ }
+
+ if ( options.dialogTitleMessageKey === undefined ) {
+ options.dialogTitleMessageKey = 'feedback-submit';
+ }
+
+ if ( options.bugsLink === undefined ) {
+ options.bugsLink = '//bugzilla.wikimedia.org/enter_bug.cgi';
+ }
+
+ if ( options.bugsListLink === undefined ) {
+ options.bugsListLink = '//bugzilla.wikimedia.org/query.cgi';
+ }
+
+ $.extend( this, options );
+ this.setup();
+ };
+
+ mw.Feedback.prototype = {
+ setup: function() {
+ var _this = this;
+
+ var $feedbackPageLink = $( '<a></a>' )
+ .attr( { 'href': _this.title.getUrl(), 'target': '_blank' } )
+ .css( { 'white-space': 'nowrap' } );
+
+ var $bugNoteLink = $( '<a></a>' ).attr( { 'href': '#' } ).click( function() { _this.displayBugs(); } );
+
+ var $bugsListLink = $( '<a></a>' ).attr( { 'href': _this.bugsListLink, 'target': '_blank' } );
+
+ this.$dialog =
+ $( '<div style="position:relative;"></div>' ).append(
+ $( '<div class="feedback-mode feedback-form"></div>' ).append(
+ $( '<small></small>' ).append(
+ $( '<p></p>' ).msg(
+ 'feedback-bugornote',
+ $bugNoteLink,
+ _this.title.getNameText(),
+ $feedbackPageLink.clone()
+ )
+ ),
+ $( '<div style="margin-top:1em;"></div>' ).append(
+ mw.msg( 'feedback-subject' ),
+ $( '<br/>' ),
+ $( '<input type="text" class="feedback-subject" name="subject" maxlength="60" style="width:99%;"/>' )
+ ),
+ $( '<div style="margin-top:0.4em;"></div>' ).append(
+ mw.msg( 'feedback-message' ),
+ $( '<br/>' ),
+ $( '<textarea name="message" class="feedback-message" style="width:99%;" rows="5" cols="60"></textarea>' )
+ )
+ ),
+ $( '<div class="feedback-mode feedback-bugs"></div>' ).append(
+ $( '<p>' ).msg( 'feedback-bugcheck', $bugsListLink )
+ ),
+ $( '<div class="feedback-mode feedback-submitting" style="text-align:center;margin:3em 0;"></div>' ).append(
+ mw.msg( 'feedback-adding' ),
+ $( '<br/>' ),
+ $( '<span class="feedback-spinner"></span>' )
+ ),
+ $( '<div class="feedback-mode feedback-thanks" style="text-align:center;margin:1em"></div>' ).msg(
+ 'feedback-thanks', _this.title.getNameText(), $feedbackPageLink.clone()
+ ),
+ $( '<div class="feedback-mode feedback-error" style="position:relative;"></div>' ).append(
+ $( '<div class="feedback-error-msg style="color:#990000;margin-top:0.4em;"></div>' )
+ )
+ );
+
+ // undo some damage from dialog css
+ this.$dialog.find( 'a' ).css( { 'color': '#0645ad' } );
+
+ this.$dialog.dialog({
+ width: 500,
+ autoOpen: false,
+ title: mw.msg( this.dialogTitleMessageKey ),
+ modal: true,
+ buttons: _this.buttons
+ });
+
+ this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get(0);
+ this.messageInput = this.$dialog.find( 'textarea.feedback-message' ).get(0);
+
+ },
+
+ display: function( s ) {
+ this.$dialog.dialog( { buttons:{} } ); // hide the buttons
+ this.$dialog.find( '.feedback-mode' ).hide(); // hide everything
+ this.$dialog.find( '.feedback-' + s ).show(); // show the desired div
+ },
+
+ displaySubmitting: function() {
+ this.display( 'submitting' );
+ },
+
+ displayBugs: function() {
+ var _this = this;
+ this.display( 'bugs' );
+ var bugsButtons = {};
+ bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function() { window.open( _this.bugsLink, '_blank' ); };
+ bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); };
+ this.$dialog.dialog( { buttons: bugsButtons } );
+ },
+
+ displayThanks: function() {
+ var _this = this;
+ this.display( 'thanks' );
+ var closeButton = {};
+ closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); };
+ this.$dialog.dialog( { buttons: closeButton } );
+ },
+
+ /**
+ * Display the feedback form
+ * @param {Object} optional prefilled contents for the feedback form. Object with properties:
+ * subject: {String}
+ * message: {String}
+ */
+ displayForm: function( contents ) {
+ var _this = this;
+ this.subjectInput.value = (contents && contents.subject) ? contents.subject : '';
+ this.messageInput.value = (contents && contents.message) ? contents.message : '';
+
+ this.display( 'form' );
+
+ // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
+ var formButtons = {};
+ formButtons[ mw.msg( 'feedback-submit' ) ] = function() { _this.submit(); };
+ formButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); };
+ this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back
+ },
+
+ displayError: function( message ) {
+ var _this = this;
+ this.display( 'error' );
+ this.$dialog.find( '.feedback-error-msg' ).msg( message );
+ var closeButton = {};
+ closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); };
+ this.$dialog.dialog( { buttons: closeButton } );
+ },
+
+ cancel: function() {
+ this.$dialog.dialog( 'close' );
+ },
+
+ submit: function() {
+ var _this = this;
+
+ // get the values to submit
+ var subject = this.subjectInput.value;
+
+ var message = "<small>User agent: " + navigator.userAgent + "</small>\n\n"
+ + this.messageInput.value;
+ if ( message.indexOf( '~~~' ) == -1 ) {
+ message += " ~~~~";
+ }
+
+ this.displaySubmitting();
+
+ var ok = function( result ) {
+ if ( result.edit !== undefined ) {
+ if ( result.edit.result === 'Success' ) {
+ _this.displayThanks();
+ } else {
+ _this.displayError( 'feedback-error1' ); // unknown API result
+ }
+ } else {
+ _this.displayError( 'feedback-error2' ); // edit failed
+ }
+ };
+
+ var err = function( code, info ) {
+ _this.displayError( 'feedback-error3' ); // ajax request failed
+ };
+
+ this.api.newSection( this.title, subject, message, ok, err );
+ }, // close submit button function
+
+ /**
+ * Modify the display form, and then open it, focusing interface on the subject.
+ * @param {Object} optional prefilled contents for the feedback form. Object with properties:
+ * subject: {String}
+ * message: {String}
+ */
+ launch: function( contents ) {
+ this.displayForm( contents );
+ this.$dialog.dialog( 'open' );
+ this.subjectInput.focus();
+ }
+
+ };
+
+} )( window.mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.feedback.spinner.gif b/resources/mediawiki/mediawiki.feedback.spinner.gif
new file mode 100644
index 00000000..aed0ea41
--- /dev/null
+++ b/resources/mediawiki/mediawiki.feedback.spinner.gif
Binary files differ
diff --git a/resources/mediawiki/mediawiki.htmlform.js b/resources/mediawiki/mediawiki.htmlform.js
index 1a6acd6f..17a02cf4 100644
--- a/resources/mediawiki/mediawiki.htmlform.js
+++ b/resources/mediawiki/mediawiki.htmlform.js
@@ -1,7 +1,7 @@
/**
* Utility functions for jazzing up HTMLForm elements
*/
-( function( $ ) {
+( function( $ ) {
/**
* jQuery plugin to fade or snap to visible state.
@@ -10,7 +10,7 @@
* @return jQuery
*/
$.fn.goIn = function( instantToggle ) {
- if ( instantToggle !== undefined && instantToggle === true ) {
+ if ( instantToggle === true ) {
return $(this).show();
}
return $(this).stop( true, true ).fadeIn();
@@ -23,7 +23,7 @@ $.fn.goIn = function( instantToggle ) {
* @return jQuery
*/
$.fn.goOut = function( instantToggle ) {
- if ( instantToggle !== undefined && instantToggle === true ) {
+ if ( instantToggle === true ) {
return $(this).hide();
}
return $(this).stop( true, true ).fadeOut();
diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js
new file mode 100644
index 00000000..6c00bd15
--- /dev/null
+++ b/resources/mediawiki/mediawiki.jqueryMsg.js
@@ -0,0 +1,685 @@
+/**
+ * Experimental advanced wikitext parser-emitter.
+ * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
+ *
+ * @author neilk@wikimedia.org
+ */
+
+( function( mw, $, undefined ) {
+
+ mw.jqueryMsg = {};
+
+ /**
+ * Given parser options, return a function that parses a key and replacements, returning jQuery object
+ * @param {Object} parser options
+ * @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery}
+ */
+ function getFailableParserFn( options ) {
+ var parser = new mw.jqueryMsg.parser( options );
+ /**
+ * Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes.
+ * If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into
+ * the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it.
+ *
+ * @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements.
+ * @return {jQuery}
+ */
+ return function( args ) {
+ var key = args[0];
+ var argsArray = $.isArray( args[1] ) ? args[1] : $.makeArray( args ).slice( 1 );
+ var escapedArgsArray = $.map( argsArray, function( arg ) {
+ return typeof arg === 'string' ? mw.html.escape( arg ) : arg;
+ } );
+ try {
+ return parser.parse( key, escapedArgsArray );
+ } catch ( e ) {
+ return $( '<span></span>' ).append( key + ': ' + e.message );
+ }
+ };
+ }
+
+ /**
+ * Class method.
+ * Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements).
+ * e.g.
+ * window.gM = mediaWiki.parser.getMessageFunction( options );
+ * $( 'p#headline' ).html( gM( 'hello-user', username ) );
+ *
+ * Like the old gM() function this returns only strings, so it destroys any bindings. If you want to preserve bindings use the
+ * jQuery plugin version instead. This is only included for backwards compatibility with gM().
+ *
+ * @param {Array} parser options
+ * @return {Function} function suitable for assigning to window.gM
+ */
+ mw.jqueryMsg.getMessageFunction = function( options ) {
+ var failableParserFn = getFailableParserFn( options );
+ /**
+ * N.B. replacements are variadic arguments or an array in second parameter. In other words:
+ * somefunction(a, b, c, d)
+ * is equivalent to
+ * somefunction(a, [b, c, d])
+ *
+ * @param {String} message key
+ * @param {Array} optional replacements (can also specify variadically)
+ * @return {String} rendered HTML as string
+ */
+ return function( /* key, replacements */ ) {
+ return failableParserFn( arguments ).html();
+ };
+ };
+
+ /**
+ * Class method.
+ * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to
+ * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links.
+ * e.g.
+ * $.fn.msg = mediaWiki.parser.getJqueryPlugin( options );
+ * var userlink = $( '<a>' ).click( function() { alert( "hello!!") } );
+ * $( 'p#headline' ).msg( 'hello-user', userlink );
+ *
+ * @param {Array} parser options
+ * @return {Function} function suitable for assigning to jQuery plugin, such as $.fn.msg
+ */
+ mw.jqueryMsg.getPlugin = function( options ) {
+ var failableParserFn = getFailableParserFn( options );
+ /**
+ * N.B. replacements are variadic arguments or an array in second parameter. In other words:
+ * somefunction(a, b, c, d)
+ * is equivalent to
+ * somefunction(a, [b, c, d])
+ *
+ * We append to 'this', which in a jQuery plugin context will be the selected elements.
+ * @param {String} message key
+ * @param {Array} optional replacements (can also specify variadically)
+ * @return {jQuery} this
+ */
+ return function( /* key, replacements */ ) {
+ var $target = this.empty();
+ $.each( failableParserFn( arguments ).contents(), function( i, node ) {
+ $target.append( node );
+ } );
+ return $target;
+ };
+ };
+
+ var parserDefaults = {
+ 'magic' : {},
+ 'messages' : mw.messages,
+ 'language' : mw.language
+ };
+
+ /**
+ * The parser itself.
+ * Describes an object, whose primary duty is to .parse() message keys.
+ * @param {Array} options
+ */
+ mw.jqueryMsg.parser = function( options ) {
+ this.settings = $.extend( {}, parserDefaults, options );
+ this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic );
+ };
+
+ mw.jqueryMsg.parser.prototype = {
+
+ // cache, map of mediaWiki message key to the AST of the message. In most cases, the message is a string so this is identical.
+ // (This is why we would like to move this functionality server-side).
+ astCache: {},
+
+ /**
+ * Where the magic happens.
+ * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery
+ * If an error is thrown, returns original key, and logs the error
+ * @param {String} message key
+ * @param {Array} replacements for $1, $2... $n
+ * @return {jQuery}
+ */
+ parse: function( key, replacements ) {
+ return this.emitter.emit( this.getAst( key ), replacements );
+ },
+
+ /**
+ * Fetch the message string associated with a key, return parsed structure. Memoized.
+ * Note that we pass '[' + key + ']' back for a missing message here.
+ * @param {String} key
+ * @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
+ */
+ getAst: function( key ) {
+ if ( this.astCache[ key ] === undefined ) {
+ var wikiText = this.settings.messages.get( key );
+ if ( typeof wikiText !== 'string' ) {
+ wikiText = "\\[" + key + "\\]";
+ }
+ this.astCache[ key ] = this.wikiTextToAst( wikiText );
+ }
+ return this.astCache[ key ];
+ },
+
+ /*
+ * Parses the input wikiText into an abstract syntax tree, essentially an s-expression.
+ *
+ * CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already.
+ * n.b. We want to move this functionality to the server. Nothing here is required to be on the client.
+ *
+ * @param {String} message string wikitext
+ * @throws Error
+ * @return {Mixed} abstract syntax tree
+ */
+ wikiTextToAst: function( input ) {
+
+ // Indicates current position in input as we parse through it.
+ // Shared among all parsing functions below.
+ var pos = 0;
+
+ // =========================================================
+ // parsing combinators - could be a library on its own
+ // =========================================================
+
+
+ // Try parsers until one works, if none work return null
+ function choice( ps ) {
+ return function() {
+ for ( var i = 0; i < ps.length; i++ ) {
+ var result = ps[i]();
+ if ( result !== null ) {
+ return result;
+ }
+ }
+ return null;
+ };
+ }
+
+ // try several ps in a row, all must succeed or return null
+ // this is the only eager one
+ function sequence( ps ) {
+ var originalPos = pos;
+ var result = [];
+ for ( var i = 0; i < ps.length; i++ ) {
+ var res = ps[i]();
+ if ( res === null ) {
+ pos = originalPos;
+ return null;
+ }
+ result.push( res );
+ }
+ return result;
+ }
+
+ // run the same parser over and over until it fails.
+ // must succeed a minimum of n times or return null
+ function nOrMore( n, p ) {
+ return function() {
+ var originalPos = pos;
+ var result = [];
+ var parsed = p();
+ while ( parsed !== null ) {
+ result.push( parsed );
+ parsed = p();
+ }
+ if ( result.length < n ) {
+ pos = originalPos;
+ return null;
+ }
+ return result;
+ };
+ }
+
+ // There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null.
+ // But using this as a combinator seems to cause problems when combined with nOrMore().
+ // May be some scoping issue
+ function transform( p, fn ) {
+ return function() {
+ var result = p();
+ return result === null ? null : fn( result );
+ };
+ }
+
+ // Helpers -- just make ps out of simpler JS builtin types
+
+ function makeStringParser( s ) {
+ var len = s.length;
+ return function() {
+ var result = null;
+ if ( input.substr( pos, len ) === s ) {
+ result = s;
+ pos += len;
+ }
+ return result;
+ };
+ }
+
+ function makeRegexParser( regex ) {
+ return function() {
+ var matches = input.substr( pos ).match( regex );
+ if ( matches === null ) {
+ return null;
+ }
+ pos += matches[0].length;
+ return matches[0];
+ };
+ }
+
+
+ /**
+ * ===================================================================
+ * General patterns above this line -- wikitext specific parsers below
+ * ===================================================================
+ */
+
+ // Parsing functions follow. All parsing functions work like this:
+ // They don't accept any arguments.
+ // Instead, they just operate non destructively on the string 'input'
+ // As they can consume parts of the string, they advance the shared variable pos,
+ // and return tokens (or whatever else they want to return).
+
+ // some things are defined as closures and other things as ordinary functions
+ // converting everything to a closure makes it a lot harder to debug... errors pop up
+ // but some debuggers can't tell you exactly where they come from. Also the mutually
+ // recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
+ // This may be because, to save code, memoization was removed
+
+
+ var regularLiteral = makeRegexParser( /^[^{}[\]$\\]/ );
+ var regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
+ var regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
+
+ var backslash = makeStringParser( "\\" );
+ var anyCharacter = makeRegexParser( /^./ );
+
+ function escapedLiteral() {
+ var result = sequence( [
+ backslash,
+ anyCharacter
+ ] );
+ return result === null ? null : result[1];
+ }
+
+ var escapedOrLiteralWithoutSpace = choice( [
+ escapedLiteral,
+ regularLiteralWithoutSpace
+ ] );
+
+ var escapedOrLiteralWithoutBar = choice( [
+ escapedLiteral,
+ regularLiteralWithoutBar
+ ] );
+
+ var escapedOrRegularLiteral = choice( [
+ escapedLiteral,
+ regularLiteral
+ ] );
+
+ // Used to define "literals" without spaces, in space-delimited situations
+ function literalWithoutSpace() {
+ var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
+ return result === null ? null : result.join('');
+ }
+
+ // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
+ // it is not a literal in the parameter
+ function literalWithoutBar() {
+ var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
+ return result === null ? null : result.join('');
+ }
+
+ function literal() {
+ var result = nOrMore( 1, escapedOrRegularLiteral )();
+ return result === null ? null : result.join('');
+ }
+
+ var whitespace = makeRegexParser( /^\s+/ );
+ var dollar = makeStringParser( '$' );
+ var digits = makeRegexParser( /^\d+/ );
+
+ function replacement() {
+ var result = sequence( [
+ dollar,
+ digits
+ ] );
+ if ( result === null ) {
+ return null;
+ }
+ return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
+ }
+
+
+ var openExtlink = makeStringParser( '[' );
+ var closeExtlink = makeStringParser( ']' );
+
+ // this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed
+ function extlink() {
+ var result = null;
+ var parsedResult = sequence( [
+ openExtlink,
+ nonWhitespaceExpression,
+ whitespace,
+ expression,
+ closeExtlink
+ ] );
+ if ( parsedResult !== null ) {
+ result = [ 'LINK', parsedResult[1], parsedResult[3] ];
+ }
+ return result;
+ }
+
+ var openLink = makeStringParser( '[[' );
+ var closeLink = makeStringParser( ']]' );
+
+ function link() {
+ var result = null;
+ var parsedResult = sequence( [
+ openLink,
+ expression,
+ closeLink
+ ] );
+ if ( parsedResult !== null ) {
+ result = [ 'WLINK', parsedResult[1] ];
+ }
+ return result;
+ }
+
+ var templateName = transform(
+ // see $wgLegalTitleChars
+ // not allowing : due to the need to catch "PLURAL:$1"
+ makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+-]+/ ),
+ function( result ) { return result.toString(); }
+ );
+
+ function templateParam() {
+ var result = sequence( [
+ pipe,
+ nOrMore( 0, paramExpression )
+ ] );
+ if ( result === null ) {
+ return null;
+ }
+ var expr = result[1];
+ // use a "CONCAT" operator if there are multiple nodes, otherwise return the first node, raw.
+ return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0];
+ }
+
+ var pipe = makeStringParser( '|' );
+
+ function templateWithReplacement() {
+ var result = sequence( [
+ templateName,
+ colon,
+ replacement
+ ] );
+ return result === null ? null : [ result[0], result[2] ];
+ }
+
+ var colon = makeStringParser(':');
+
+ var templateContents = choice( [
+ function() {
+ var res = sequence( [
+ templateWithReplacement,
+ nOrMore( 0, templateParam )
+ ] );
+ return res === null ? null : res[0].concat( res[1] );
+ },
+ function() {
+ var res = sequence( [
+ templateName,
+ nOrMore( 0, templateParam )
+ ] );
+ if ( res === null ) {
+ return null;
+ }
+ return [ res[0] ].concat( res[1] );
+ }
+ ] );
+
+ var openTemplate = makeStringParser('{{');
+ var closeTemplate = makeStringParser('}}');
+
+ function template() {
+ var result = sequence( [
+ openTemplate,
+ templateContents,
+ closeTemplate
+ ] );
+ return result === null ? null : result[1];
+ }
+
+ var nonWhitespaceExpression = choice( [
+ template,
+ link,
+ extlink,
+ replacement,
+ literalWithoutSpace
+ ] );
+
+ var paramExpression = choice( [
+ template,
+ link,
+ extlink,
+ replacement,
+ literalWithoutBar
+ ] );
+
+ var expression = choice( [
+ template,
+ link,
+ extlink,
+ replacement,
+ literal
+ ] );
+
+ function start() {
+ var result = nOrMore( 0, expression )();
+ if ( result === null ) {
+ return null;
+ }
+ return [ "CONCAT" ].concat( result );
+ }
+
+ // everything above this point is supposed to be stateless/static, but
+ // I am deferring the work of turning it into prototypes & objects. It's quite fast enough
+
+ // finally let's do some actual work...
+
+ var result = start();
+
+ /*
+ * For success, the p must have gotten to the end of the input
+ * and returned a non-null.
+ * n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
+ */
+ if (result === null || pos !== input.length) {
+ throw new Error( "Parse error at position " + pos.toString() + " in input: " + input );
+ }
+ return result;
+ }
+
+ };
+
+ /**
+ * htmlEmitter - object which primarily exists to emit HTML from parser ASTs
+ */
+ mw.jqueryMsg.htmlEmitter = function( language, magic ) {
+ this.language = language;
+ var _this = this;
+
+ $.each( magic, function( key, val ) {
+ _this[ key.toLowerCase() ] = function() { return val; };
+ } );
+
+ /**
+ * (We put this method definition here, and not in prototype, to make sure it's not overwritten by any magic.)
+ * Walk entire node structure, applying replacements and template functions when appropriate
+ * @param {Mixed} abstract syntax tree (top node or subnode)
+ * @param {Array} replacements for $1, $2, ... $n
+ * @return {Mixed} single-string node or array of nodes suitable for jQuery appending
+ */
+ this.emit = function( node, replacements ) {
+ var ret = null;
+ var _this = this;
+ switch( typeof node ) {
+ case 'string':
+ case 'number':
+ ret = node;
+ break;
+ case 'object': // node is an array of nodes
+ var subnodes = $.map( node.slice( 1 ), function( n ) {
+ return _this.emit( n, replacements );
+ } );
+ var operation = node[0].toLowerCase();
+ if ( typeof _this[operation] === 'function' ) {
+ ret = _this[ operation ]( subnodes, replacements );
+ } else {
+ throw new Error( 'unknown operation "' + operation + '"' );
+ }
+ break;
+ case 'undefined':
+ // Parsing the empty string (as an entire expression, or as a paramExpression in a template) results in undefined
+ // Perhaps a more clever parser can detect this, and return the empty string? Or is that useful information?
+ // The logical thing is probably to return the empty string here when we encounter undefined.
+ ret = '';
+ break;
+ default:
+ throw new Error( 'unexpected type in AST: ' + typeof node );
+ }
+ return ret;
+ };
+
+ };
+
+ // For everything in input that follows double-open-curly braces, there should be an equivalent parser
+ // function. For instance {{PLURAL ... }} will be processed by 'plural'.
+ // If you have 'magic words' then configure the parser to have them upon creation.
+ //
+ // An emitter method takes the parent node, the array of subnodes and the array of replacements (the values that $1, $2... should translate to).
+ // Note: all such functions must be pure, with the exception of referring to other pure functions via this.language (convertPlural and so on)
+ mw.jqueryMsg.htmlEmitter.prototype = {
+
+ /**
+ * Parsing has been applied depth-first we can assume that all nodes here are single nodes
+ * Must return a single node to parents -- a jQuery with synthetic span
+ * However, unwrap any other synthetic spans in our children and pass them upwards
+ * @param {Array} nodes - mixed, some single nodes, some arrays of nodes
+ * @return {jQuery}
+ */
+ concat: function( nodes ) {
+ var span = $( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
+ $.each( nodes, function( i, node ) {
+ if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) {
+ $.each( node.contents(), function( j, childNode ) {
+ span.append( childNode );
+ } );
+ } else {
+ // strings, integers, anything else
+ span.append( node );
+ }
+ } );
+ return span;
+ },
+
+ /**
+ * Return replacement of correct index, or string if unavailable.
+ * Note that we expect the parsed parameter to be zero-based. i.e. $1 should have become [ 0 ].
+ * if the specified parameter is not found return the same string
+ * (e.g. "$99" -> parameter 98 -> not found -> return "$99" )
+ * TODO throw error if nodes.length > 1 ?
+ * @param {Array} of one element, integer, n >= 0
+ * @return {String} replacement
+ */
+ replace: function( nodes, replacements ) {
+ var index = parseInt( nodes[0], 10 );
+ return index < replacements.length ? replacements[index] : '$' + ( index + 1 );
+ },
+
+ /**
+ * Transform wiki-link
+ * TODO unimplemented
+ */
+ wlink: function( nodes ) {
+ return "unimplemented";
+ },
+
+ /**
+ * Transform parsed structure into external link
+ * If the href is a jQuery object, treat it as "enclosing" the link text.
+ * ... function, treat it as the click handler
+ * ... string, treat it as a URI
+ * TODO: throw an error if nodes.length > 2 ?
+ * @param {Array} of two elements, {jQuery|Function|String} and {String}
+ * @return {jQuery}
+ */
+ link: function( nodes ) {
+ var arg = nodes[0];
+ var contents = nodes[1];
+ var $el;
+ if ( arg instanceof jQuery ) {
+ $el = arg;
+ } else {
+ $el = $( '<a>' );
+ if ( typeof arg === 'function' ) {
+ $el.click( arg ).attr( 'href', '#' );
+ } else {
+ $el.attr( 'href', arg.toString() );
+ }
+ }
+ $el.append( contents );
+ return $el;
+ },
+
+ /**
+ * Transform parsed structure into pluralization
+ * n.b. The first node may be a non-integer (for instance, a string representing an Arabic number).
+ * So convert it back with the current language's convertNumber.
+ * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ]
+ * @return {String} selected pluralized form according to current language
+ */
+ plural: function( nodes ) {
+ var count = parseInt( this.language.convertNumber( nodes[0], true ), 10 );
+ var forms = nodes.slice(1);
+ return forms.length ? this.language.convertPlural( count, forms ) : '';
+ },
+
+ /**
+ * Transform parsed structure into gender
+ * Usage {{gender:[gender| mw.user object ] | masculine|feminine|neutral}}.
+ * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ]
+ * @return {String} selected gender form according to current language
+ */
+ gender: function( nodes ) {
+ var gender;
+ if ( nodes[0] && nodes[0].options instanceof mw.Map ){
+ gender = nodes[0].options.get( 'gender' );
+ } else {
+ gender = nodes[0];
+ }
+ var forms = nodes.slice(1);
+ return this.language.gender( gender, forms );
+ }
+
+ };
+
+ // TODO figure out a way to make magic work with common globals like wgSiteName, without requiring init from library users...
+ // var options = { magic: { 'SITENAME' : mw.config.get( 'wgSiteName' ) } };
+
+ // deprecated! don't rely on gM existing.
+ // the window.gM ought not to be required - or if required, not required here. But moving it to extensions breaks it (?!)
+ // Need to fix plugin so it could do attributes as well, then will be okay to remove this.
+ window.gM = mw.jqueryMsg.getMessageFunction();
+
+ $.fn.msg = mw.jqueryMsg.getPlugin();
+
+ // Replace the default message parser with jqueryMsg
+ var oldParser = mw.Message.prototype.parser;
+ mw.Message.prototype.parser = function() {
+ // TODO: should we cache the message function so we don't create a new one every time? Benchmark this maybe?
+ // Caching is somewhat problematic, because we do need different message functions for different maps, so
+ // we'd have to cache the parser as a member of this.map, which sounds a bit ugly.
+
+ // Do not use mw.jqueryMsg unless required
+ if ( this.map.get( this.key ).indexOf( '{{' ) < 0 ) {
+ // Fall back to mw.msg's simple parser
+ return oldParser.apply( this );
+ }
+
+ var messageFunction = mw.jqueryMsg.getMessageFunction( { 'messages': this.map } );
+ return messageFunction( this.key, this.parameters );
+ };
+
+} )( mediaWiki, jQuery );
diff --git a/resources/mediawiki/mediawiki.jqueryMsg.peg b/resources/mediawiki/mediawiki.jqueryMsg.peg
new file mode 100644
index 00000000..74c57e4b
--- /dev/null
+++ b/resources/mediawiki/mediawiki.jqueryMsg.peg
@@ -0,0 +1,76 @@
+/* PEG grammar for a subset of wikitext, useful in the MediaWiki frontend */
+
+start
+ = e:expression* { return e.length > 1 ? [ "CONCAT" ].concat(e) : e[0]; }
+
+expression
+ = template
+ / link
+ / extlink
+ / replacement
+ / literal
+
+paramExpression
+ = template
+ / link
+ / extlink
+ / replacement
+ / literalWithoutBar
+
+template
+ = "{{" t:templateContents "}}" { return t; }
+
+templateContents
+ = twr:templateWithReplacement p:templateParam* { return twr.concat(p) }
+ / t:templateName p:templateParam* { return p.length ? [ t, p ] : [ t ] }
+
+templateWithReplacement
+ = t:templateName ":" r:replacement { return [ t, r ] }
+
+templateParam
+ = "|" e:paramExpression* { return e.length > 1 ? [ "CONCAT" ].concat(e) : e[0]; }
+
+templateName
+ = tn:[A-Za-z_]+ { return tn.join('').toUpperCase() }
+
+link
+ = "[[" w:expression "]]" { return [ 'WLINK', w ]; }
+
+extlink
+ = "[" url:url whitespace text:expression "]" { return [ 'LINK', url, text ] }
+
+url
+ = url:[^ ]+ { return url.join(''); }
+
+whitespace
+ = [ ]+
+
+replacement
+ = '$' digits:digits { return [ 'REPLACE', parseInt( digits, 10 ) - 1 ] }
+
+digits
+ = [0-9]+
+
+literal
+ = lit:escapedOrRegularLiteral+ { return lit.join(''); }
+
+literalWithoutBar
+ = lit:escapedOrLiteralWithoutBar+ { return lit.join(''); }
+
+escapedOrRegularLiteral
+ = escapedLiteral
+ / regularLiteral
+
+escapedOrLiteralWithoutBar
+ = escapedLiteral
+ / regularLiteralWithoutBar
+
+escapedLiteral
+ = "\\" escaped:. { return escaped; }
+
+regularLiteral
+ = [^{}\[\]$\\]
+
+regularLiteralWithoutBar
+ = [^{}\[\]$\\|]
+
diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js
index aca59e4e..121d5399 100644
--- a/resources/mediawiki/mediawiki.js
+++ b/resources/mediawiki/mediawiki.js
@@ -2,16 +2,12 @@
* Core MediaWiki JavaScript Library
*/
-// Attach to window
-window.mediaWiki = new ( function( $ ) {
+var mw = ( function ( $, undefined ) {
+"use strict";
/* Private Members */
- /**
- * @var object List of messages that have been requested to be loaded.
- */
- var messageQueue = {};
-
+ var hasOwn = Object.prototype.hasOwnProperty;
/* Object constructors */
/**
@@ -21,87 +17,95 @@ window.mediaWiki = new ( function( $ ) {
* that allow both single and multiple variables at once.
*
* @param global boolean Whether to store the values in the global window
- * object or a exclusively in the object property 'values'.
+ * object or a exclusively in the object property 'values'.
* @return Map
*/
function Map( global ) {
- this.values = ( global === true ) ? window : {};
+ this.values = global === true ? window : {};
return this;
}
- /**
- * Get the value of one or multiple a keys.
- *
- * If called with no arguments, all values will be returned.
- *
- * @param selection mixed String key or array of keys to get values for.
- * @param fallback mixed Value to use in case key(s) do not exist (optional).
- * @return mixed If selection was a string returns the value or null,
- * If selection was an array, returns an object of key/values (value is null if not found),
- * If selection was not passed or invalid, will return the 'values' object member (be careful as
- * objects are always passed by reference in JavaScript!).
- * @return Values as a string or object, null if invalid/inexistant.
- */
- Map.prototype.get = function( selection, fallback ) {
- if ( $.isArray( selection ) ) {
- selection = $.makeArray( selection );
- var results = {};
- for ( var i = 0; i < selection.length; i++ ) {
- results[selection[i]] = this.get( selection[i], fallback );
- }
- return results;
- } else if ( typeof selection === 'string' ) {
- if ( this.values[selection] === undefined ) {
- if ( fallback !== undefined ) {
- return fallback;
+ Map.prototype = {
+ /**
+ * Get the value of one or multiple a keys.
+ *
+ * If called with no arguments, all values will be returned.
+ *
+ * @param selection mixed String key or array of keys to get values for.
+ * @param fallback mixed Value to use in case key(s) do not exist (optional).
+ * @return mixed If selection was a string returns the value or null,
+ * If selection was an array, returns an object of key/values (value is null if not found),
+ * If selection was not passed or invalid, will return the 'values' object member (be careful as
+ * objects are always passed by reference in JavaScript!).
+ * @return Values as a string or object, null if invalid/inexistant.
+ */
+ get: function ( selection, fallback ) {
+ var results, i;
+
+ if ( $.isArray( selection ) ) {
+ selection = $.makeArray( selection );
+ results = {};
+ for ( i = 0; i < selection.length; i += 1 ) {
+ results[selection[i]] = this.get( selection[i], fallback );
}
- return null;
+ return results;
+ } else if ( typeof selection === 'string' ) {
+ if ( this.values[selection] === undefined ) {
+ if ( fallback !== undefined ) {
+ return fallback;
+ }
+ return null;
+ }
+ return this.values[selection];
}
- return this.values[selection];
- }
- if ( selection === undefined ) {
- return this.values;
- } else {
- return null; // invalid selection key
- }
- };
+ if ( selection === undefined ) {
+ return this.values;
+ } else {
+ return null; // invalid selection key
+ }
+ },
- /**
- * Sets one or multiple key/value pairs.
- *
- * @param selection mixed String key or array of keys to set values for.
- * @param value mixed Value to set (optional, only in use when key is a string)
- * @return bool This returns true on success, false on failure.
- */
- Map.prototype.set = function( selection, value ) {
- if ( $.isPlainObject( selection ) ) {
- for ( var s in selection ) {
- this.values[s] = selection[s];
+ /**
+ * Sets one or multiple key/value pairs.
+ *
+ * @param selection {mixed} String key or array of keys to set values for.
+ * @param value {mixed} Value to set (optional, only in use when key is a string)
+ * @return {Boolean} This returns true on success, false on failure.
+ */
+ set: function ( selection, value ) {
+ var s;
+
+ if ( $.isPlainObject( selection ) ) {
+ for ( s in selection ) {
+ this.values[s] = selection[s];
+ }
+ return true;
+ } else if ( typeof selection === 'string' && value !== undefined ) {
+ this.values[selection] = value;
+ return true;
}
- return true;
- } else if ( typeof selection === 'string' && value !== undefined ) {
- this.values[selection] = value;
- return true;
- }
- return false;
- };
+ return false;
+ },
- /**
- * Checks if one or multiple keys exist.
- *
- * @param selection mixed String key or array of keys to check
- * @return boolean Existence of key(s)
- */
- Map.prototype.exists = function( selection ) {
- if ( typeof selection === 'object' ) {
- for ( var s = 0; s < selection.length; s++ ) {
- if ( !( selection[s] in this.values ) ) {
- return false;
+ /**
+ * Checks if one or multiple keys exist.
+ *
+ * @param selection {mixed} String key or array of keys to check
+ * @return {Boolean} Existence of key(s)
+ */
+ exists: function ( selection ) {
+ var s;
+
+ if ( $.isArray( selection ) ) {
+ for ( s = 0; s < selection.length; s += 1 ) {
+ if ( this.values[selection[s]] === undefined ) {
+ return false;
+ }
}
+ return true;
+ } else {
+ return this.values[selection] !== undefined;
}
- return true;
- } else {
- return selection in this.values;
}
};
@@ -117,418 +121,630 @@ window.mediaWiki = new ( function( $ ) {
* @return Message
*/
function Message( map, key, parameters ) {
- this.format = 'parse';
+ this.format = 'plain';
this.map = map;
this.key = key;
this.parameters = parameters === undefined ? [] : $.makeArray( parameters );
return this;
}
- /**
- * Appends (does not replace) parameters for replacement to the .parameters property.
- *
- * @param parameters Array
- * @return Message
- */
- Message.prototype.params = function( parameters ) {
- for ( var i = 0; i < parameters.length; i++ ) {
- this.parameters.push( parameters[i] );
- }
- return this;
- };
-
- /**
- * Converts message object to it's string form based on the state of format.
- *
- * @return string Message as a string in the current form or <key> if key does not exist.
- */
- Message.prototype.toString = function() {
- if ( !this.map.exists( this.key ) ) {
- // Use <key> as text if key does not exist
- if ( this.format !== 'plain' ) {
- // format 'escape' and 'parse' need to have the brackets and key html escaped
- return mw.html.escape( '<' + this.key + '>' );
+ Message.prototype = {
+ /**
+ * Simple message parser, does $N replacement and nothing else.
+ * This may be overridden to provide a more complex message parser.
+ *
+ * This function will not be called for nonexistent messages.
+ */
+ parser: function() {
+ var parameters = this.parameters;
+ return this.map.get( this.key ).replace( /\$(\d+)/g, function ( str, match ) {
+ var index = parseInt( match, 10 ) - 1;
+ return parameters[index] !== undefined ? parameters[index] : '$' + match;
+ } );
+ },
+
+ /**
+ * Appends (does not replace) parameters for replacement to the .parameters property.
+ *
+ * @param parameters Array
+ * @return Message
+ */
+ params: function ( parameters ) {
+ var i;
+ for ( i = 0; i < parameters.length; i += 1 ) {
+ this.parameters.push( parameters[i] );
}
- return '<' + this.key + '>';
- }
- var text = this.map.get( this.key );
- var parameters = this.parameters;
- text = text.replace( /\$(\d+)/g, function( string, match ) {
- var index = parseInt( match, 10 ) - 1;
- return index in parameters ? parameters[index] : '$' + match;
- } );
-
- if ( this.format === 'plain' ) {
- return text;
- }
- if ( this.format === 'escaped' ) {
- // According to Message.php this needs {{-transformation, which is
- // still todo
- return mw.html.escape( text );
- }
-
- /* This should be fixed up when we have a parser
- if ( this.format === 'parse' && 'language' in mediaWiki ) {
- text = mw.language.parse( text );
- }
- */
- return text;
- };
-
- /**
- * Changes format to parse and converts message to string
- *
- * @return {string} String form of parsed message
- */
- Message.prototype.parse = function() {
- this.format = 'parse';
- return this.toString();
- };
-
- /**
- * Changes format to plain and converts message to string
- *
- * @return {string} String form of plain message
- */
- Message.prototype.plain = function() {
- this.format = 'plain';
- return this.toString();
- };
-
- /**
- * Changes the format to html escaped and converts message to string
- *
- * @return {string} String form of html escaped message
- */
- Message.prototype.escaped = function() {
- this.format = 'escaped';
- return this.toString();
- };
-
- /**
- * Checks if message exists
- *
- * @return {string} String form of parsed message
- */
- Message.prototype.exists = function() {
- return this.map.exists( this.key );
- };
+ return this;
+ },
- /* Public Members */
+ /**
+ * Converts message object to it's string form based on the state of format.
+ *
+ * @return string Message as a string in the current form or <key> if key does not exist.
+ */
+ toString: function() {
+ var text;
+
+ if ( !this.exists() ) {
+ // Use <key> as text if key does not exist
+ if ( this.format !== 'plain' ) {
+ // format 'escape' and 'parse' need to have the brackets and key html escaped
+ return mw.html.escape( '<' + this.key + '>' );
+ }
+ return '<' + this.key + '>';
+ }
- /*
- * Dummy function which in debug mode can be replaced with a function that
- * emulates console.log in console-less environments.
- */
- this.log = function() { };
+ if ( this.format === 'plain' ) {
+ // @todo FIXME: Although not applicable to core Message,
+ // Plugins like jQueryMsg should be able to distinguish
+ // between 'plain' (only variable replacement and plural/gender)
+ // and actually parsing wikitext to HTML.
+ text = this.parser();
+ }
- /**
- * @var constructor Make the Map-class publicly available.
- */
- this.Map = Map;
+ if ( this.format === 'escaped' ) {
+ text = this.parser();
+ text = mw.html.escape( text );
+ }
+
+ if ( this.format === 'parse' ) {
+ text = this.parser();
+ }
- /**
- * List of configuration values
- *
- * Dummy placeholder. Initiated in startUp module as a new instance of mw.Map().
- * If $wgLegacyJavaScriptGlobals is true, this Map will have its values
- * in the global window object.
- */
- this.config = null;
+ return text;
+ },
- /**
- * @var object
- *
- * Empty object that plugins can be installed in.
- */
- this.libs = {};
+ /**
+ * Changes format to parse and converts message to string
+ *
+ * @return {string} String form of parsed message
+ */
+ parse: function() {
+ this.format = 'parse';
+ return this.toString();
+ },
- /*
- * Localization system
- */
- this.messages = new this.Map();
+ /**
+ * Changes format to plain and converts message to string
+ *
+ * @return {string} String form of plain message
+ */
+ plain: function() {
+ this.format = 'plain';
+ return this.toString();
+ },
- /* Public Methods */
+ /**
+ * Changes the format to html escaped and converts message to string
+ *
+ * @return {string} String form of html escaped message
+ */
+ escaped: function() {
+ this.format = 'escaped';
+ return this.toString();
+ },
- /**
- * Gets a message object, similar to wfMessage()
- *
- * @param key string Key of message to get
- * @param parameter_1 mixed First argument in a list of variadic arguments,
- * each a parameter for $N replacement in messages.
- * @return Message
- */
- this.message = function( key, parameter_1 /* [, parameter_2] */ ) {
- var parameters;
- // Support variadic arguments
- if ( parameter_1 !== undefined ) {
- parameters = $.makeArray( arguments );
- parameters.shift();
- } else {
- parameters = [];
+ /**
+ * Checks if message exists
+ *
+ * @return {string} String form of parsed message
+ */
+ exists: function() {
+ return this.map.exists( this.key );
}
- return new Message( mw.messages, key, parameters );
};
- /**
- * Gets a message string, similar to wfMsg()
- *
- * @param key string Key of message to get
- * @param parameters mixed First argument in a list of variadic arguments,
- * each a parameter for $N replacement in messages.
- * @return String.
- */
- this.msg = function( key, parameters ) {
- return mw.message.apply( mw.message, arguments ).toString();
- };
+ return {
+ /* Public Members */
- /**
- * Client-side module loader which integrates with the MediaWiki ResourceLoader
- */
- this.loader = new ( function() {
-
- /* Private Members */
+ /**
+ * Dummy function which in debug mode can be replaced with a function that
+ * emulates console.log in console-less environments.
+ */
+ log: function() { },
+
+ /**
+ * @var constructor Make the Map constructor publicly available.
+ */
+ Map: Map,
/**
- * Mapping of registered modules
+ * @var constructor Make the Message constructor publicly available.
+ */
+ Message: Message,
+
+ /**
+ * List of configuration values
*
- * The jquery module is pre-registered, because it must have already
- * been provided for this object to have been built, and in debug mode
- * jquery would have been provided through a unique loader request,
- * making it impossible to hold back registration of jquery until after
- * mediawiki.
+ * Dummy placeholder. Initiated in startUp module as a new instance of mw.Map().
+ * If $wgLegacyJavaScriptGlobals is true, this Map will have its values
+ * in the global window object.
+ */
+ config: null,
+
+ /**
+ * @var object
*
- * Format:
- * {
- * 'moduleName': {
- * 'dependencies': ['required module', 'required module', ...], (or) function() {}
- * 'state': 'registered', 'loading', 'loaded', 'ready', or 'error'
- * 'script': function() {},
- * 'style': 'css code string',
- * 'messages': { 'key': 'value' },
- * 'version': ############## (unix timestamp)
- * }
- * }
+ * Empty object that plugins can be installed in.
*/
- var registry = {};
- // List of modules which will be loaded as when ready
- var batch = [];
- // List of modules to be loaded
- var queue = [];
- // List of callback functions waiting for modules to be ready to be called
- var jobs = [];
- // Flag inidicating that document ready has occured
- var ready = false;
- // Selector cache for the marker element. Use getMarker() to get/use the marker!
- var $marker = null;
-
- /* Private Methods */
-
- function getMarker(){
- // Cached ?
- if ( $marker ) {
- return $marker;
+ libs: {},
+
+ /* Extension points */
+
+ legacy: {},
+
+ /**
+ * Localization system
+ */
+ messages: new Map(),
+
+ /* Public Methods */
+
+ /**
+ * Gets a message object, similar to wfMessage()
+ *
+ * @param key string Key of message to get
+ * @param parameter_1 mixed First argument in a list of variadic arguments,
+ * each a parameter for $N replacement in messages.
+ * @return Message
+ */
+ message: function ( key, parameter_1 /* [, parameter_2] */ ) {
+ var parameters;
+ // Support variadic arguments
+ if ( parameter_1 !== undefined ) {
+ parameters = $.makeArray( arguments );
+ parameters.shift();
} else {
- $marker = $( 'meta[name="ResourceLoaderDynamicStyles"]' );
- if ( $marker.length ) {
+ parameters = [];
+ }
+ return new Message( mw.messages, key, parameters );
+ },
+
+ /**
+ * Gets a message string, similar to wfMsg()
+ *
+ * @param key string Key of message to get
+ * @param parameters mixed First argument in a list of variadic arguments,
+ * each a parameter for $N replacement in messages.
+ * @return String.
+ */
+ msg: function ( key, parameters ) {
+ return mw.message.apply( mw.message, arguments ).toString();
+ },
+
+ /**
+ * Client-side module loader which integrates with the MediaWiki ResourceLoader
+ */
+ loader: ( function() {
+
+ /* Private Members */
+
+ /**
+ * Mapping of registered modules
+ *
+ * The jquery module is pre-registered, because it must have already
+ * been provided for this object to have been built, and in debug mode
+ * jquery would have been provided through a unique loader request,
+ * making it impossible to hold back registration of jquery until after
+ * mediawiki.
+ *
+ * For exact details on support for script, style and messages, look at
+ * mw.loader.implement.
+ *
+ * Format:
+ * {
+ * 'moduleName': {
+ * 'version': ############## (unix timestamp),
+ * 'dependencies': ['required.foo', 'bar.also', ...], (or) function() {}
+ * 'group': 'somegroup', (or) null,
+ * 'source': 'local', 'someforeignwiki', (or) null
+ * 'state': 'registered', 'loading', 'loaded', 'ready', 'error' or 'missing'
+ * 'script': ...,
+ * 'style': ...,
+ * 'messages': { 'key': 'value' },
+ * }
+ * }
+ */
+ var registry = {},
+ /**
+ * Mapping of sources, keyed by source-id, values are objects.
+ * Format:
+ * {
+ * 'sourceId': {
+ * 'loadScript': 'http://foo.bar/w/load.php'
+ * }
+ * }
+ */
+ sources = {},
+ // List of modules which will be loaded as when ready
+ batch = [],
+ // List of modules to be loaded
+ queue = [],
+ // List of callback functions waiting for modules to be ready to be called
+ jobs = [],
+ // Flag indicating that document ready has occured
+ ready = false,
+ // Selector cache for the marker element. Use getMarker() to get/use the marker!
+ $marker = null;
+
+ /* Cache document ready status */
+
+ $(document).ready( function () {
+ ready = true;
+ } );
+
+ /* Private methods */
+
+ function getMarker() {
+ // Cached ?
+ if ( $marker ) {
+ return $marker;
+ } else {
+ $marker = $( 'meta[name="ResourceLoaderDynamicStyles"]' );
+ if ( $marker.length ) {
+ return $marker;
+ }
+ mw.log( 'getMarker> No <meta name="ResourceLoaderDynamicStyles"> found, inserting dynamically.' );
+ $marker = $( '<meta>' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' );
return $marker;
}
- mw.log( 'getMarker> No <meta name="ResourceLoaderDynamicStyles"> found, inserting dynamically.' );
- $marker = $( '<meta>' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' );
- return $marker;
- }
- }
-
- function compare( a, b ) {
- if ( a.length != b.length ) {
- return false;
}
- for ( var i = 0; i < b.length; i++ ) {
- if ( $.isArray( a[i] ) ) {
- if ( !compare( a[i], b[i] ) ) {
- return false;
+
+ function addInlineCSS( css, media ) {
+ var $style = getMarker().prev(),
+ $newStyle,
+ attrs = { 'type': 'text/css', 'media': media };
+ if ( $style.is( 'style' ) && $style.data( 'ResourceLoaderDynamicStyleTag' ) === true ) {
+ // There's already a dynamic <style> tag present, append to it
+ // This recycling of <style> tags is for bug 31676 (can't have
+ // more than 32 <style> tags in IE)
+
+ // Also, calling .append() on a <style> tag explodes with a JS error in IE,
+ // so if the .append() fails we fall back to building a new <style> tag and
+ // replacing the existing one
+ try {
+ // Do cdata sanitization on the provided CSS, and prepend a double newline
+ css = $( mw.html.element( 'style', {}, new mw.html.Cdata( "\n\n" + css ) ) ).html();
+ $style.append( css );
+ } catch ( e ) {
+ // Generate a new tag with the combined CSS
+ css = $style.html() + "\n\n" + css;
+ $newStyle = $( mw.html.element( 'style', attrs, new mw.html.Cdata( css ) ) )
+ .data( 'ResourceLoaderDynamicStyleTag', true );
+ // Prevent a flash of unstyled content by inserting the new tag
+ // before removing the old one
+ $style.after( $newStyle );
+ $style.remove();
}
+ } else {
+ // Create a new <style> tag and insert it
+ $style = $( mw.html.element( 'style', attrs, new mw.html.Cdata( css ) ) );
+ $style.data( 'ResourceLoaderDynamicStyleTag', true );
+ getMarker().before( $style );
}
- if ( a[i] !== b[i] ) {
+ }
+
+ function compare( a, b ) {
+ var i;
+ if ( a.length !== b.length ) {
return false;
}
+ for ( i = 0; i < b.length; i += 1 ) {
+ if ( $.isArray( a[i] ) ) {
+ if ( !compare( a[i], b[i] ) ) {
+ return false;
+ }
+ }
+ if ( a[i] !== b[i] ) {
+ return false;
+ }
+ }
+ return true;
}
- return true;
- }
-
- /**
- * Generates an ISO8601 "basic" string from a UNIX timestamp
- */
- function formatVersionNumber( timestamp ) {
- function pad( a, b, c ) {
- return [a < 10 ? '0' + a : a, b < 10 ? '0' + b : b, c < 10 ? '0' + c : c].join( '' );
- }
- var d = new Date();
- d.setTime( timestamp * 1000 );
- return [
- pad( d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate() ), 'T',
- pad( d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() ), 'Z'
- ].join( '' );
- }
-
- /**
- * Recursively resolves dependencies and detects circular references
- */
- function recurse( module, resolved, unresolved ) {
- if ( registry[module] === undefined ) {
- throw new Error( 'Unknown dependency: ' + module );
+
+ /**
+ * Generates an ISO8601 "basic" string from a UNIX timestamp
+ */
+ function formatVersionNumber( timestamp ) {
+ var pad = function ( a, b, c ) {
+ return [a < 10 ? '0' + a : a, b < 10 ? '0' + b : b, c < 10 ? '0' + c : c].join( '' );
+ },
+ d = new Date();
+ d.setTime( timestamp * 1000 );
+ return [
+ pad( d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate() ), 'T',
+ pad( d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() ), 'Z'
+ ].join( '' );
}
- // Resolves dynamic loader function and replaces it with its own results
- if ( $.isFunction( registry[module].dependencies ) ) {
- registry[module].dependencies = registry[module].dependencies();
- // Ensures the module's dependencies are always in an array
- if ( typeof registry[module].dependencies !== 'object' ) {
- registry[module].dependencies = [registry[module].dependencies];
+
+ /**
+ * Recursively resolves dependencies and detects circular references
+ */
+ function recurse( module, resolved, unresolved ) {
+ var n, deps, len;
+
+ if ( registry[module] === undefined ) {
+ throw new Error( 'Unknown dependency: ' + module );
}
- }
- // Tracks down dependencies
- for ( var n = 0; n < registry[module].dependencies.length; n++ ) {
- if ( $.inArray( registry[module].dependencies[n], resolved ) === -1 ) {
- if ( $.inArray( registry[module].dependencies[n], unresolved ) !== -1 ) {
- throw new Error(
- 'Circular reference detected: ' + module +
- ' -> ' + registry[module].dependencies[n]
- );
+ // Resolves dynamic loader function and replaces it with its own results
+ if ( $.isFunction( registry[module].dependencies ) ) {
+ registry[module].dependencies = registry[module].dependencies();
+ // Ensures the module's dependencies are always in an array
+ if ( typeof registry[module].dependencies !== 'object' ) {
+ registry[module].dependencies = [registry[module].dependencies];
}
- recurse( registry[module].dependencies[n], resolved, unresolved );
}
- }
- resolved[resolved.length] = module;
- unresolved.splice( $.inArray( module, unresolved ), 1 );
- }
+ // Tracks down dependencies
+ deps = registry[module].dependencies;
+ len = deps.length;
+ for ( n = 0; n < len; n += 1 ) {
+ if ( $.inArray( deps[n], resolved ) === -1 ) {
+ if ( $.inArray( deps[n], unresolved ) !== -1 ) {
+ throw new Error(
+ 'Circular reference detected: ' + module +
+ ' -> ' + deps[n]
+ );
+ }
- /**
- * Gets a list of module names that a module depends on in their proper dependency order
- *
- * @param module string module name or array of string module names
- * @return list of dependencies
- * @throws Error if circular reference is detected
- */
- function resolve( module ) {
- // Allow calling with an array of module names
- if ( typeof module === 'object' ) {
- var modules = [];
- for ( var m = 0; m < module.length; m++ ) {
- var dependencies = resolve( module[m] );
- for ( var n = 0; n < dependencies.length; n++ ) {
- modules[modules.length] = dependencies[n];
+ // Add to unresolved
+ unresolved[unresolved.length] = module;
+ recurse( deps[n], resolved, unresolved );
+ // module is at the end of unresolved
+ unresolved.pop();
}
}
- return modules;
- } else if ( typeof module === 'string' ) {
- // Undefined modules have no dependencies
- if ( !( module in registry ) ) {
- return [];
- }
- var resolved = [];
- recurse( module, resolved, [] );
- return resolved;
+ resolved[resolved.length] = module;
}
- throw new Error( 'Invalid module argument: ' + module );
- }
-
- /**
- * Narrows a list of module names down to those matching a specific
- * state. Possible states are 'undefined', 'registered', 'loading',
- * 'loaded', or 'ready'
- *
- * @param states string or array of strings of module states to filter by
- * @param modules array list of module names to filter (optional, all modules
- * will be used by default)
- * @return array list of filtered module names
- */
- function filter( states, modules ) {
- // Allow states to be given as a string
- if ( typeof states === 'string' ) {
- states = [states];
+
+ /**
+ * Gets a list of module names that a module depends on in their proper dependency order
+ *
+ * @param module string module name or array of string module names
+ * @return list of dependencies, including 'module'.
+ * @throws Error if circular reference is detected
+ */
+ function resolve( module ) {
+ var modules, m, deps, n, resolved;
+
+ // Allow calling with an array of module names
+ if ( $.isArray( module ) ) {
+ modules = [];
+ for ( m = 0; m < module.length; m += 1 ) {
+ deps = resolve( module[m] );
+ for ( n = 0; n < deps.length; n += 1 ) {
+ modules[modules.length] = deps[n];
+ }
+ }
+ return modules;
+ } else if ( typeof module === 'string' ) {
+ resolved = [];
+ recurse( module, resolved, [] );
+ return resolved;
+ }
+ throw new Error( 'Invalid module argument: ' + module );
}
- // If called without a list of modules, build and use a list of all modules
- var list = [], module;
- if ( modules === undefined ) {
- modules = [];
- for ( module in registry ) {
- modules[modules.length] = module;
+
+ /**
+ * Narrows a list of module names down to those matching a specific
+ * state (see comment on top of this scope for a list of valid states).
+ * One can also filter for 'unregistered', which will return the
+ * modules names that don't have a registry entry.
+ *
+ * @param states string or array of strings of module states to filter by
+ * @param modules array list of module names to filter (optional, by default the entire
+ * registry is used)
+ * @return array list of filtered module names
+ */
+ function filter( states, modules ) {
+ var list, module, s, m;
+
+ // Allow states to be given as a string
+ if ( typeof states === 'string' ) {
+ states = [states];
+ }
+ // If called without a list of modules, build and use a list of all modules
+ list = [];
+ if ( modules === undefined ) {
+ modules = [];
+ for ( module in registry ) {
+ modules[modules.length] = module;
+ }
}
+ // Build a list of modules which are in one of the specified states
+ for ( s = 0; s < states.length; s += 1 ) {
+ for ( m = 0; m < modules.length; m += 1 ) {
+ if ( registry[modules[m]] === undefined ) {
+ // Module does not exist
+ if ( states[s] === 'unregistered' ) {
+ // OK, undefined
+ list[list.length] = modules[m];
+ }
+ } else {
+ // Module exists, check state
+ if ( registry[modules[m]].state === states[s] ) {
+ // OK, correct state
+ list[list.length] = modules[m];
+ }
+ }
+ }
+ }
+ return list;
}
- // Build a list of modules which are in one of the specified states
- for ( var s = 0; s < states.length; s++ ) {
- for ( var m = 0; m < modules.length; m++ ) {
- if ( registry[modules[m]] === undefined ) {
- // Module does not exist
- if ( states[s] == 'undefined' ) {
- // OK, undefined
- list[list.length] = modules[m];
+
+ /**
+ * Automatically executes jobs and modules which are pending with satistifed dependencies.
+ *
+ * This is used when dependencies are satisfied, such as when a module is executed.
+ */
+ function handlePending( module ) {
+ var j, r;
+
+ try {
+ // Run jobs whose dependencies have just been met
+ for ( j = 0; j < jobs.length; j += 1 ) {
+ if ( compare(
+ filter( 'ready', jobs[j].dependencies ),
+ jobs[j].dependencies ) )
+ {
+ var callback = jobs[j].ready;
+ jobs.splice( j, 1 );
+ j -= 1;
+ if ( $.isFunction( callback ) ) {
+ callback();
+ }
}
- } else {
- // Module exists, check state
- if ( registry[modules[m]].state === states[s] ) {
- // OK, correct state
- list[list.length] = modules[m];
+ }
+ // Execute modules whose dependencies have just been met
+ for ( r in registry ) {
+ if ( registry[r].state === 'loaded' ) {
+ if ( compare(
+ filter( ['ready'], registry[r].dependencies ),
+ registry[r].dependencies ) )
+ {
+ execute( r );
+ }
+ }
+ }
+ } catch ( e ) {
+ // Run error callbacks of jobs affected by this condition
+ for ( j = 0; j < jobs.length; j += 1 ) {
+ if ( $.inArray( module, jobs[j].dependencies ) !== -1 ) {
+ if ( $.isFunction( jobs[j].error ) ) {
+ jobs[j].error( e, module );
+ }
+ jobs.splice( j, 1 );
+ j -= 1;
}
}
+ throw e;
}
}
- return list;
- }
-
- /**
- * Executes a loaded module, making it ready to use
- *
- * @param module string module name to execute
- */
- function execute( module, callback ) {
- var _fn = 'mw.loader::execute> ';
- if ( registry[module] === undefined ) {
- throw new Error( 'Module has not been registered yet: ' + module );
- } else if ( registry[module].state === 'registered' ) {
- throw new Error( 'Module has not been requested from the server yet: ' + module );
- } else if ( registry[module].state === 'loading' ) {
- throw new Error( 'Module has not completed loading yet: ' + module );
- } else if ( registry[module].state === 'ready' ) {
- throw new Error( 'Module has already been loaded: ' + module );
+
+ /**
+ * Adds a script tag to the DOM, either using document.write or low-level DOM manipulation,
+ * depending on whether document-ready has occured yet and whether we are in async mode.
+ *
+ * @param src String: URL to script, will be used as the src attribute in the script tag
+ * @param callback Function: Optional callback which will be run when the script is done
+ */
+ function addScript( src, callback, async ) {
+ var done = false, script, head;
+ if ( ready || async ) {
+ // jQuery's getScript method is NOT better than doing this the old-fashioned way
+ // because jQuery will eval the script's code, and errors will not have sane
+ // line numbers.
+ script = document.createElement( 'script' );
+ script.setAttribute( 'src', src );
+ script.setAttribute( 'type', 'text/javascript' );
+ if ( $.isFunction( callback ) ) {
+ // Attach handlers for all browsers (based on jQuery.ajax)
+ script.onload = script.onreadystatechange = function() {
+
+ if (
+ !done
+ && (
+ !script.readyState
+ || /loaded|complete/.test( script.readyState )
+ )
+ ) {
+
+ done = true;
+
+ callback();
+
+ // Handle memory leak in IE. This seems to fail in
+ // IE7 sometimes (Permission Denied error when
+ // accessing script.parentNode) so wrap it in
+ // a try catch.
+ try {
+ script.onload = script.onreadystatechange = null;
+ if ( script.parentNode ) {
+ script.parentNode.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+ } catch ( e ) { }
+ }
+ };
+ }
+
+ if ( window.opera ) {
+ // Appending to the <head> blocks rendering completely in Opera,
+ // so append to the <body> after document ready. This means the
+ // scripts only start loading after the document has been rendered,
+ // but so be it. Opera users don't deserve faster web pages if their
+ // browser makes it impossible
+ $( function() { document.body.appendChild( script ); } );
+ } else {
+ // IE-safe way of getting the <head> . document.documentElement.head doesn't
+ // work in scripts that run in the <head>
+ head = document.getElementsByTagName( 'head' )[0];
+ ( document.body || head ).appendChild( script );
+ }
+ } else {
+ document.write( mw.html.element(
+ 'script', { 'type': 'text/javascript', 'src': src }, ''
+ ) );
+ if ( $.isFunction( callback ) ) {
+ // Document.write is synchronous, so this is called when it's done
+ // FIXME: that's a lie. doc.write isn't actually synchronous
+ callback();
+ }
+ }
}
- // Add styles
- if ( $.isPlainObject( registry[module].style ) ) {
- for ( var media in registry[module].style ) {
- var style = registry[module].style[media];
- if ( $.isArray( style ) ) {
- for ( var i = 0; i < style.length; i++ ) {
- getMarker().before( mw.html.element( 'link', {
- 'type': 'text/css',
- 'media': media,
- 'rel': 'stylesheet',
- 'href': style[i]
- } ) );
+
+ /**
+ * Executes a loaded module, making it ready to use
+ *
+ * @param module string module name to execute
+ */
+ function execute( module, callback ) {
+ var style, media, i, script, markModuleReady, nestedAddScript;
+
+ if ( registry[module] === undefined ) {
+ throw new Error( 'Module has not been registered yet: ' + module );
+ } else if ( registry[module].state === 'registered' ) {
+ throw new Error( 'Module has not been requested from the server yet: ' + module );
+ } else if ( registry[module].state === 'loading' ) {
+ throw new Error( 'Module has not completed loading yet: ' + module );
+ } else if ( registry[module].state === 'ready' ) {
+ throw new Error( 'Module has already been loaded: ' + module );
+ }
+
+ // Add styles
+ if ( $.isPlainObject( registry[module].style ) ) {
+ for ( media in registry[module].style ) {
+ style = registry[module].style[media];
+ if ( $.isArray( style ) ) {
+ for ( i = 0; i < style.length; i += 1 ) {
+ getMarker().before( mw.html.element( 'link', {
+ 'type': 'text/css',
+ 'media': media,
+ 'rel': 'stylesheet',
+ 'href': style[i]
+ } ) );
+ }
+ } else if ( typeof style === 'string' ) {
+ addInlineCSS( style, media );
}
- } else if ( typeof style === 'string' ) {
- getMarker().before( mw.html.element(
- 'style',
- { 'type': 'text/css', 'media': media },
- new mw.html.Cdata( style )
- ) );
}
}
- }
- // Add localizations to message system
- if ( $.isPlainObject( registry[module].messages ) ) {
- mw.messages.set( registry[module].messages );
- }
- // Execute script
- try {
- var script = registry[module].script,
+ // Add localizations to message system
+ if ( $.isPlainObject( registry[module].messages ) ) {
+ mw.messages.set( registry[module].messages );
+ }
+ // Execute script
+ try {
+ script = registry[module].script;
markModuleReady = function() {
registry[module].state = 'ready';
handlePending( module );
if ( $.isFunction( callback ) ) {
callback();
}
- },
- nestedAddScript = function( arr, callback, i ) {
+ };
+ nestedAddScript = function ( arr, callback, async, i ) {
// Recursively call addScript() in its own callback
// for each element of arr.
if ( i >= arr.length ) {
@@ -536,666 +752,702 @@ window.mediaWiki = new ( function( $ ) {
callback();
return;
}
-
+
addScript( arr[i], function() {
- nestedAddScript( arr, callback, i + 1 );
- } );
+ nestedAddScript( arr, callback, async, i + 1 );
+ }, async );
};
-
- if ( $.isArray( script ) ) {
- registry[module].state = 'loading';
- nestedAddScript( script, markModuleReady, 0 );
- } else if ( $.isFunction( script ) ) {
- script( jQuery );
- markModuleReady();
- }
- } catch ( e ) {
- // This needs to NOT use mw.log because these errors are common in production mode
- // and not in debug mode, such as when a symbol that should be global isn't exported
- if ( window.console && typeof window.console.log === 'function' ) {
- console.log( _fn + 'Exception thrown by ' + module + ': ' + e.message );
+
+ if ( $.isArray( script ) ) {
+ registry[module].state = 'loading';
+ nestedAddScript( script, markModuleReady, registry[module].async, 0 );
+ } else if ( $.isFunction( script ) ) {
+ script( $ );
+ markModuleReady();
+ }
+ } catch ( e ) {
+ // This needs to NOT use mw.log because these errors are common in production mode
+ // and not in debug mode, such as when a symbol that should be global isn't exported
+ if ( window.console && typeof window.console.log === 'function' ) {
+ console.log( 'mw.loader::execute> Exception thrown by ' + module + ': ' + e.message );
+ }
+ registry[module].state = 'error';
}
- registry[module].state = 'error';
- throw e;
}
- }
-
- /**
- * Automatically executes jobs and modules which are pending with satistifed dependencies.
- *
- * This is used when dependencies are satisfied, such as when a module is executed.
- */
- function handlePending( module ) {
- try {
- // Run jobs who's dependencies have just been met
- for ( var j = 0; j < jobs.length; j++ ) {
- if ( compare(
- filter( 'ready', jobs[j].dependencies ),
- jobs[j].dependencies ) )
- {
- if ( $.isFunction( jobs[j].ready ) ) {
- jobs[j].ready();
+
+ /**
+ * Adds a dependencies to the queue with optional callbacks to be run
+ * when the dependencies are ready or fail
+ *
+ * @param dependencies string module name or array of string module names
+ * @param ready function callback to execute when all dependencies are ready
+ * @param error function callback to execute when any dependency fails
+ * @param async (optional) If true, load modules asynchronously even if
+ * document ready has not yet occurred
+ */
+ function request( dependencies, ready, error, async ) {
+ var regItemDeps, regItemDepLen, n;
+
+ // Allow calling by single module name
+ if ( typeof dependencies === 'string' ) {
+ dependencies = [dependencies];
+ if ( registry[dependencies[0]] !== undefined ) {
+ // Cache repetitively accessed deep level object member
+ regItemDeps = registry[dependencies[0]].dependencies;
+ // Cache to avoid looped access to length property
+ regItemDepLen = regItemDeps.length;
+ for ( n = 0; n < regItemDepLen; n += 1 ) {
+ dependencies[dependencies.length] = regItemDeps[n];
}
- jobs.splice( j, 1 );
- j--;
}
}
- // Execute modules who's dependencies have just been met
- for ( var r in registry ) {
- if ( registry[r].state == 'loaded' ) {
- if ( compare(
- filter( ['ready'], registry[r].dependencies ),
- registry[r].dependencies ) )
- {
- execute( r );
- }
- }
+ // Add ready and error callbacks if they were given
+ if ( arguments.length > 1 ) {
+ jobs[jobs.length] = {
+ 'dependencies': filter(
+ ['registered', 'loading', 'loaded'],
+ dependencies
+ ),
+ 'ready': ready,
+ 'error': error
+ };
}
- } catch ( e ) {
- // Run error callbacks of jobs affected by this condition
- for ( var j = 0; j < jobs.length; j++ ) {
- if ( $.inArray( module, jobs[j].dependencies ) !== -1 ) {
- if ( $.isFunction( jobs[j].error ) ) {
- jobs[j].error();
+ // Queue up any dependencies that are registered
+ dependencies = filter( ['registered'], dependencies );
+ for ( n = 0; n < dependencies.length; n += 1 ) {
+ if ( $.inArray( dependencies[n], queue ) === -1 ) {
+ queue[queue.length] = dependencies[n];
+ if ( async ) {
+ // Mark this module as async in the registry
+ registry[dependencies[n]].async = true;
}
- jobs.splice( j, 1 );
- j--;
}
}
+ // Work the queue
+ mw.loader.work();
}
- }
-
- /**
- * Adds a dependencies to the queue with optional callbacks to be run
- * when the dependencies are ready or fail
- *
- * @param dependencies string module name or array of string module names
- * @param ready function callback to execute when all dependencies are ready
- * @param error function callback to execute when any dependency fails
- */
- function request( dependencies, ready, error ) {
- // Allow calling by single module name
- if ( typeof dependencies === 'string' ) {
- dependencies = [dependencies];
- if ( dependencies[0] in registry ) {
- for ( var n = 0; n < registry[dependencies[0]].dependencies.length; n++ ) {
- dependencies[dependencies.length] =
- registry[dependencies[0]].dependencies[n];
+
+ function sortQuery(o) {
+ var sorted = {}, key, a = [];
+ for ( key in o ) {
+ if ( hasOwn.call( o, key ) ) {
+ a.push( key );
}
}
- }
- // Add ready and error callbacks if they were given
- if ( arguments.length > 1 ) {
- jobs[jobs.length] = {
- 'dependencies': filter(
- ['undefined', 'registered', 'loading', 'loaded'],
- dependencies ),
- 'ready': ready,
- 'error': error
- };
- }
- // Queue up any dependencies that are undefined or registered
- dependencies = filter( ['undefined', 'registered'], dependencies );
- for ( var n = 0; n < dependencies.length; n++ ) {
- if ( $.inArray( dependencies[n], queue ) === -1 ) {
- queue[queue.length] = dependencies[n];
+ a.sort();
+ for ( key = 0; key < a.length; key += 1 ) {
+ sorted[a[key]] = o[a[key]];
}
+ return sorted;
}
- // Work the queue
- mw.loader.work();
- }
-
- function sortQuery(o) {
- var sorted = {}, key, a = [];
- for ( key in o ) {
- if ( o.hasOwnProperty( key ) ) {
- a.push( key );
+
+ /**
+ * Converts a module map of the form { foo: [ 'bar', 'baz' ], bar: [ 'baz, 'quux' ] }
+ * to a query string of the form foo.bar,baz|bar.baz,quux
+ */
+ function buildModulesString( moduleMap ) {
+ var arr = [], p, prefix;
+ for ( prefix in moduleMap ) {
+ p = prefix === '' ? '' : prefix + '.';
+ arr.push( p + moduleMap[prefix].join( ',' ) );
}
+ return arr.join( '|' );
}
- a.sort();
- for ( key = 0; key < a.length; key++ ) {
- sorted[a[key]] = o[a[key]];
- }
- return sorted;
- }
-
- /**
- * Converts a module map of the form { foo: [ 'bar', 'baz' ], bar: [ 'baz, 'quux' ] }
- * to a query string of the form foo.bar,baz|bar.baz,quux
- */
- function buildModulesString( moduleMap ) {
- var arr = [];
- for ( var prefix in moduleMap ) {
- var p = prefix === '' ? '' : prefix + '.';
- arr.push( p + moduleMap[prefix].join( ',' ) );
+
+ /**
+ * Asynchronously append a script tag to the end of the body
+ * that invokes load.php
+ * @param moduleMap {Object}: Module map, see buildModulesString()
+ * @param currReqBase {Object}: Object with other parameters (other than 'modules') to use in the request
+ * @param sourceLoadScript {String}: URL of load.php
+ * @param async {Boolean}: If true, use an asynchrounous request even if document ready has not yet occurred
+ */
+ function doRequest( moduleMap, currReqBase, sourceLoadScript, async ) {
+ var request = $.extend(
+ { 'modules': buildModulesString( moduleMap ) },
+ currReqBase
+ );
+ request = sortQuery( request );
+ // Asynchronously append a script tag to the end of the body
+ // Append &* to avoid triggering the IE6 extension check
+ addScript( sourceLoadScript + '?' + $.param( request ) + '&*', null, async );
}
- return arr.join( '|' );
- }
-
- /**
- * Adds a script tag to the body, either using document.write or low-level DOM manipulation,
- * depending on whether document-ready has occured yet.
- *
- * @param src String: URL to script, will be used as the src attribute in the script tag
- * @param callback Function: Optional callback which will be run when the script is done
- */
- function addScript( src, callback ) {
- if ( ready ) {
- // jQuery's getScript method is NOT better than doing this the old-fashioned way
- // because jQuery will eval the script's code, and errors will not have sane
- // line numbers.
- var script = document.createElement( 'script' );
- script.setAttribute( 'src', src );
- script.setAttribute( 'type', 'text/javascript' );
- if ( $.isFunction( callback ) ) {
- var done = false;
- // Attach handlers for all browsers -- this is based on jQuery.getScript
- script.onload = script.onreadystatechange = function() {
- if (
- !done
- && (
- !this.readyState
- || this.readyState === 'loaded'
- || this.readyState === 'complete'
- )
- ) {
- done = true;
- callback();
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
- if ( script.parentNode ) {
- script.parentNode.removeChild( script );
+
+ /* Public Methods */
+ return {
+ /**
+ * Requests dependencies from server, loading and executing when things when ready.
+ */
+ work: function () {
+ var reqBase, splits, maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
+ source, group, g, i, modules, maxVersion, sourceLoadScript,
+ currReqBase, currReqBaseLength, moduleMap, l,
+ lastDotIndex, prefix, suffix, bytesAdded, async;
+
+ // Build a list of request parameters common to all requests.
+ reqBase = {
+ skin: mw.config.get( 'skin' ),
+ lang: mw.config.get( 'wgUserLanguage' ),
+ debug: mw.config.get( 'debug' )
+ };
+ // Split module batch by source and by group.
+ splits = {};
+ maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', -1 );
+
+ // Appends a list of modules from the queue to the batch
+ for ( q = 0; q < queue.length; q += 1 ) {
+ // Only request modules which are registered
+ if ( registry[queue[q]] !== undefined && registry[queue[q]].state === 'registered' ) {
+ // Prevent duplicate entries
+ if ( $.inArray( queue[q], batch ) === -1 ) {
+ batch[batch.length] = queue[q];
+ // Mark registered modules as loading
+ registry[queue[q]].state = 'loading';
+ }
+ }
+ }
+ // Early exit if there's nothing to load...
+ if ( !batch.length ) {
+ return;
+ }
+
+ // The queue has been processed into the batch, clear up the queue.
+ queue = [];
+
+ // Always order modules alphabetically to help reduce cache
+ // misses for otherwise identical content.
+ batch.sort();
+
+ // Split batch by source and by group.
+ for ( b = 0; b < batch.length; b += 1 ) {
+ bSource = registry[batch[b]].source;
+ bGroup = registry[batch[b]].group;
+ if ( splits[bSource] === undefined ) {
+ splits[bSource] = {};
+ }
+ if ( splits[bSource][bGroup] === undefined ) {
+ splits[bSource][bGroup] = [];
+ }
+ bSourceGroup = splits[bSource][bGroup];
+ bSourceGroup[bSourceGroup.length] = batch[b];
+ }
+
+ // Clear the batch - this MUST happen before we append any
+ // script elements to the body or it's possible that a script
+ // will be locally cached, instantly load, and work the batch
+ // again, all before we've cleared it causing each request to
+ // include modules which are already loaded.
+ batch = [];
+
+ for ( source in splits ) {
+
+ sourceLoadScript = sources[source].loadScript;
+
+ for ( group in splits[source] ) {
+
+ // Cache access to currently selected list of
+ // modules for this group from this source.
+ modules = splits[source][group];
+
+ // Calculate the highest timestamp
+ maxVersion = 0;
+ for ( g = 0; g < modules.length; g += 1 ) {
+ if ( registry[modules[g]].version > maxVersion ) {
+ maxVersion = registry[modules[g]].version;
+ }
+ }
+
+ currReqBase = $.extend( { 'version': formatVersionNumber( maxVersion ) }, reqBase );
+ currReqBaseLength = $.param( currReqBase ).length;
+ async = true;
+ // We may need to split up the request to honor the query string length limit,
+ // so build it piece by piece.
+ l = currReqBaseLength + 9; // '&modules='.length == 9
+
+ moduleMap = {}; // { prefix: [ suffixes ] }
+
+ for ( i = 0; i < modules.length; i += 1 ) {
+ // Determine how many bytes this module would add to the query string
+ lastDotIndex = modules[i].lastIndexOf( '.' );
+ // Note that these substr() calls work even if lastDotIndex == -1
+ prefix = modules[i].substr( 0, lastDotIndex );
+ suffix = modules[i].substr( lastDotIndex + 1 );
+ bytesAdded = moduleMap[prefix] !== undefined
+ ? suffix.length + 3 // '%2C'.length == 3
+ : modules[i].length + 3; // '%7C'.length == 3
+
+ // If the request would become too long, create a new one,
+ // but don't create empty requests
+ if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) {
+ // This request would become too long, create a new one
+ // and fire off the old one
+ doRequest( moduleMap, currReqBase, sourceLoadScript, async );
+ moduleMap = {};
+ async = true;
+ l = currReqBaseLength + 9;
+ }
+ if ( moduleMap[prefix] === undefined ) {
+ moduleMap[prefix] = [];
+ }
+ moduleMap[prefix].push( suffix );
+ if ( !registry[modules[i]].async ) {
+ // If this module is blocking, make the entire request blocking
+ // This is slightly suboptimal, but in practice mixing of blocking
+ // and async modules will only occur in debug mode.
+ async = false;
+ }
+ l += bytesAdded;
+ }
+ // If there's anything left in moduleMap, request that too
+ if ( !$.isEmptyObject( moduleMap ) ) {
+ doRequest( moduleMap, currReqBase, sourceLoadScript, async );
}
}
+ }
+ },
+
+ /**
+ * Register a source.
+ *
+ * @param id {String}: Short lowercase a-Z string representing a source, only used internally.
+ * @param props {Object}: Object containing only the loadScript property which is a url to
+ * the load.php location of the source.
+ * @return {Boolean}
+ */
+ addSource: function ( id, props ) {
+ var source;
+ // Allow multiple additions
+ if ( typeof id === 'object' ) {
+ for ( source in id ) {
+ mw.loader.addSource( source, id[source] );
+ }
+ return true;
+ }
+
+ if ( sources[id] !== undefined ) {
+ throw new Error( 'source already registered: ' + id );
+ }
+
+ sources[id] = props;
+
+ return true;
+ },
+
+ /**
+ * Registers a module, letting the system know about it and its
+ * properties. Startup modules contain calls to this function.
+ *
+ * @param module {String}: Module name
+ * @param version {Number}: Module version number as a timestamp (falls backs to 0)
+ * @param dependencies {String|Array|Function}: One string or array of strings of module
+ * names on which this module depends, or a function that returns that array.
+ * @param group {String}: Group which the module is in (optional, defaults to null)
+ * @param source {String}: Name of the source. Defaults to local.
+ */
+ register: function ( module, version, dependencies, group, source ) {
+ var m;
+ // Allow multiple registration
+ if ( typeof module === 'object' ) {
+ for ( m = 0; m < module.length; m += 1 ) {
+ // module is an array of module names
+ if ( typeof module[m] === 'string' ) {
+ mw.loader.register( module[m] );
+ // module is an array of arrays
+ } else if ( typeof module[m] === 'object' ) {
+ mw.loader.register.apply( mw.loader, module[m] );
+ }
+ }
+ return;
+ }
+ // Validate input
+ if ( typeof module !== 'string' ) {
+ throw new Error( 'module must be a string, not a ' + typeof module );
+ }
+ if ( registry[module] !== undefined ) {
+ throw new Error( 'module already registered: ' + module );
+ }
+ // List the module as registered
+ registry[module] = {
+ 'version': version !== undefined ? parseInt( version, 10 ) : 0,
+ 'dependencies': [],
+ 'group': typeof group === 'string' ? group : null,
+ 'source': typeof source === 'string' ? source: 'local',
+ 'state': 'registered'
};
- }
- document.body.appendChild( script );
- } else {
- document.write( mw.html.element(
- 'script', { 'type': 'text/javascript', 'src': src }, ''
- ) );
- if ( $.isFunction( callback ) ) {
- // Document.write is synchronous, so this is called when it's done
- callback();
- }
- }
- }
-
- /* Public Methods */
-
- /**
- * Requests dependencies from server, loading and executing when things when ready.
- */
- this.work = function() {
- // Appends a list of modules to the batch
- for ( var q = 0; q < queue.length; q++ ) {
- // Only request modules which are undefined or registered
- if ( !( queue[q] in registry ) || registry[queue[q]].state == 'registered' ) {
- // Prevent duplicate entries
- if ( $.inArray( queue[q], batch ) === -1 ) {
- batch[batch.length] = queue[q];
- // Mark registered modules as loading
- if ( queue[q] in registry ) {
- registry[queue[q]].state = 'loading';
+ if ( typeof dependencies === 'string' ) {
+ // Allow dependencies to be given as a single module name
+ registry[module].dependencies = [dependencies];
+ } else if ( typeof dependencies === 'object' || $.isFunction( dependencies ) ) {
+ // Allow dependencies to be given as an array of module names
+ // or a function which returns an array
+ registry[module].dependencies = dependencies;
+ }
+ },
+
+ /**
+ * Implements a module, giving the system a course of action to take
+ * upon loading. Results of a request for one or more modules contain
+ * calls to this function.
+ *
+ * All arguments are required.
+ *
+ * @param module String: Name of module
+ * @param script Mixed: Function of module code or String of URL to be used as the src
+ * attribute when adding a script element to the body
+ * @param style Object: Object of CSS strings keyed by media-type or Object of lists of URLs
+ * keyed by media-type
+ * @param msgs Object: List of key/value pairs to be passed through mw.messages.set
+ */
+ implement: function ( module, script, style, msgs ) {
+ // Validate input
+ if ( typeof module !== 'string' ) {
+ throw new Error( 'module must be a string, not a ' + typeof module );
+ }
+ if ( !$.isFunction( script ) && !$.isArray( script ) ) {
+ throw new Error( 'script must be a function or an array, not a ' + typeof script );
+ }
+ if ( !$.isPlainObject( style ) ) {
+ throw new Error( 'style must be an object, not a ' + typeof style );
+ }
+ if ( !$.isPlainObject( msgs ) ) {
+ throw new Error( 'msgs must be an object, not a ' + typeof msgs );
+ }
+ // Automatically register module
+ if ( registry[module] === undefined ) {
+ mw.loader.register( module );
+ }
+ // Check for duplicate implementation
+ if ( registry[module] !== undefined && registry[module].script !== undefined ) {
+ throw new Error( 'module already implemented: ' + module );
+ }
+ // Mark module as loaded
+ registry[module].state = 'loaded';
+ // Attach components
+ registry[module].script = script;
+ registry[module].style = style;
+ registry[module].messages = msgs;
+ // Execute or queue callback
+ if ( compare(
+ filter( ['ready'], registry[module].dependencies ),
+ registry[module].dependencies ) )
+ {
+ execute( module );
+ }
+ },
+
+ /**
+ * Executes a function as soon as one or more required modules are ready
+ *
+ * @param dependencies {String|Array} Module name or array of modules names the callback
+ * dependends on to be ready before executing
+ * @param ready {Function} callback to execute when all dependencies are ready (optional)
+ * @param error {Function} callback to execute when if dependencies have a errors (optional)
+ */
+ using: function ( dependencies, ready, error ) {
+ var tod = typeof dependencies;
+ // Validate input
+ if ( tod !== 'object' && tod !== 'string' ) {
+ throw new Error( 'dependencies must be a string or an array, not a ' + tod );
+ }
+ // Allow calling with a single dependency as a string
+ if ( tod === 'string' ) {
+ dependencies = [dependencies];
+ }
+ // Resolve entire dependency map
+ dependencies = resolve( dependencies );
+ // If all dependencies are met, execute ready immediately
+ if ( compare( filter( ['ready'], dependencies ), dependencies ) ) {
+ if ( $.isFunction( ready ) ) {
+ ready();
}
}
- }
- }
- // Early exit if there's nothing to load
- if ( !batch.length ) {
- return;
- }
- // Clean up the queue
- queue = [];
- // Always order modules alphabetically to help reduce cache
- // misses for otherwise identical content
- batch.sort();
- // Build a list of request parameters
- var base = {
- 'skin': mw.config.get( 'skin' ),
- 'lang': mw.config.get( 'wgUserLanguage' ),
- 'debug': mw.config.get( 'debug' )
- };
- // Extend request parameters with a list of modules in the batch
- var requests = [];
- // Split into groups
- var groups = {};
- for ( var b = 0; b < batch.length; b++ ) {
- var group = registry[batch[b]].group;
- if ( !( group in groups ) ) {
- groups[group] = [];
- }
- groups[group][groups[group].length] = batch[b];
- }
- for ( var group in groups ) {
- // Calculate the highest timestamp
- var version = 0;
- for ( var g = 0; g < groups[group].length; g++ ) {
- if ( registry[groups[group][g]].version > version ) {
- version = registry[groups[group][g]].version;
+ // If any dependencies have errors execute error immediately
+ else if ( filter( ['error'], dependencies ).length ) {
+ if ( $.isFunction( error ) ) {
+ error( new Error( 'one or more dependencies have state "error"' ),
+ dependencies );
+ }
}
- }
- var reqBase = $.extend( { 'version': formatVersionNumber( version ) }, base );
- var reqBaseLength = $.param( reqBase ).length;
- var reqs = [];
- var limit = mw.config.get( 'wgResourceLoaderMaxQueryLength', -1 );
- // We may need to split up the request to honor the query string length limit
- // So build it piece by piece
- var l = reqBaseLength + 9; // '&modules='.length == 9
- var r = 0;
- reqs[0] = {}; // { prefix: [ suffixes ] }
- for ( var i = 0; i < groups[group].length; i++ ) {
- // Determine how many bytes this module would add to the query string
- var lastDotIndex = groups[group][i].lastIndexOf( '.' );
- // Note that these substr() calls work even if lastDotIndex == -1
- var prefix = groups[group][i].substr( 0, lastDotIndex );
- var suffix = groups[group][i].substr( lastDotIndex + 1 );
- var bytesAdded = prefix in reqs[r] ?
- suffix.length + 3 : // '%2C'.length == 3
- groups[group][i].length + 3; // '%7C'.length == 3
-
- // If the request would become too long, create a new one,
- // but don't create empty requests
- if ( limit > 0 && reqs[r] != {} && l + bytesAdded > limit ) {
- // This request would become too long, create a new one
- r++;
- reqs[r] = {};
- l = reqBaseLength + 9;
+ // Since some dependencies are not yet ready, queue up a request
+ else {
+ request( dependencies, ready, error );
}
- if ( !( prefix in reqs[r] ) ) {
- reqs[r][prefix] = [];
+ },
+
+ /**
+ * Loads an external script or one or more modules for future use
+ *
+ * @param modules {mixed} Either the name of a module, array of modules,
+ * or a URL of an external script or style
+ * @param type {String} mime-type to use if calling with a URL of an
+ * external script or style; acceptable values are "text/css" and
+ * "text/javascript"; if no type is provided, text/javascript is assumed.
+ * @param async {Boolean} (optional) If true, load modules asynchronously
+ * even if document ready has not yet occurred. If false (default),
+ * block before document ready and load async after. If not set, true will
+ * be assumed if loading a URL, and false will be assumed otherwise.
+ */
+ load: function ( modules, type, async ) {
+ var filtered, m;
+
+ // Validate input
+ if ( typeof modules !== 'object' && typeof modules !== 'string' ) {
+ throw new Error( 'modules must be a string or an array, not a ' + typeof modules );
}
- reqs[r][prefix].push( suffix );
- l += bytesAdded;
- }
- for ( var r = 0; r < reqs.length; r++ ) {
- requests[requests.length] = $.extend(
- { 'modules': buildModulesString( reqs[r] ) }, reqBase
- );
- }
- }
- // Clear the batch - this MUST happen before we append the
- // script element to the body or it's possible that the script
- // will be locally cached, instantly load, and work the batch
- // again, all before we've cleared it causing each request to
- // include modules which are already loaded
- batch = [];
- // Asynchronously append a script tag to the end of the body
- for ( var r = 0; r < requests.length; r++ ) {
- requests[r] = sortQuery( requests[r] );
- // Append &* to avoid triggering the IE6 extension check
- var src = mw.config.get( 'wgLoadScript' ) + '?' + $.param( requests[r] ) + '&*';
- addScript( src );
- }
- };
-
- /**
- * Registers a module, letting the system know about it and its
- * dependencies. loader.js files contain calls to this function.
- */
- this.register = function( module, version, dependencies, group ) {
- // Allow multiple registration
- if ( typeof module === 'object' ) {
- for ( var m = 0; m < module.length; m++ ) {
- if ( typeof module[m] === 'string' ) {
- mw.loader.register( module[m] );
- } else if ( typeof module[m] === 'object' ) {
- mw.loader.register.apply( mw.loader, module[m] );
+ // Allow calling with an external url or single dependency as a string
+ if ( typeof modules === 'string' ) {
+ // Support adding arbitrary external scripts
+ if ( /^(https?:)?\/\//.test( modules ) ) {
+ if ( async === undefined ) {
+ // Assume async for bug 34542
+ async = true;
+ }
+ if ( type === 'text/css' ) {
+ $( 'head' ).append( $( '<link>', {
+ rel: 'stylesheet',
+ type: 'text/css',
+ href: modules
+ } ) );
+ return;
+ } else if ( type === 'text/javascript' || type === undefined ) {
+ addScript( modules, null, async );
+ return;
+ }
+ // Unknown type
+ throw new Error( 'invalid type for external url, must be text/css or text/javascript. not ' + type );
+ }
+ // Called with single module
+ modules = [modules];
}
- }
- return;
- }
- // Validate input
- if ( typeof module !== 'string' ) {
- throw new Error( 'module must be a string, not a ' + typeof module );
- }
- if ( registry[module] !== undefined ) {
- throw new Error( 'module already implemented: ' + module );
- }
- // List the module as registered
- registry[module] = {
- 'state': 'registered',
- 'group': typeof group === 'string' ? group : null,
- 'dependencies': [],
- 'version': version !== undefined ? parseInt( version, 10 ) : 0
- };
- if ( typeof dependencies === 'string' ) {
- // Allow dependencies to be given as a single module name
- registry[module].dependencies = [dependencies];
- } else if ( typeof dependencies === 'object' || $.isFunction( dependencies ) ) {
- // Allow dependencies to be given as an array of module names
- // or a function which returns an array
- registry[module].dependencies = dependencies;
- }
- };
- /**
- * Implements a module, giving the system a course of action to take
- * upon loading. Results of a request for one or more modules contain
- * calls to this function.
- *
- * All arguments are required.
- *
- * @param module String: Name of module
- * @param script Mixed: Function of module code or String of URL to be used as the src
- * attribute when adding a script element to the body
- * @param style Object: Object of CSS strings keyed by media-type or Object of lists of URLs
- * keyed by media-type
- * @param msgs Object: List of key/value pairs to be passed through mw.messages.set
- */
- this.implement = function( module, script, style, msgs ) {
- // Validate input
- if ( typeof module !== 'string' ) {
- throw new Error( 'module must be a string, not a ' + typeof module );
- }
- if ( !$.isFunction( script ) && !$.isArray( script ) ) {
- throw new Error( 'script must be a function or an array, not a ' + typeof script );
- }
- if ( !$.isPlainObject( style ) ) {
- throw new Error( 'style must be an object, not a ' + typeof style );
- }
- if ( !$.isPlainObject( msgs ) ) {
- throw new Error( 'msgs must be an object, not a ' + typeof msgs );
- }
- // Automatically register module
- if ( registry[module] === undefined ) {
- mw.loader.register( module );
- }
- // Check for duplicate implementation
- if ( registry[module] !== undefined && registry[module].script !== undefined ) {
- throw new Error( 'module already implemeneted: ' + module );
- }
- // Mark module as loaded
- registry[module].state = 'loaded';
- // Attach components
- registry[module].script = script;
- registry[module].style = style;
- registry[module].messages = msgs;
- // Execute or queue callback
- if ( compare(
- filter( ['ready'], registry[module].dependencies ),
- registry[module].dependencies ) )
- {
- execute( module );
- } else {
- request( module );
- }
- };
-
- /**
- * Executes a function as soon as one or more required modules are ready
- *
- * @param dependencies string or array of strings of modules names the callback
- * dependencies to be ready before
- * executing
- * @param ready function callback to execute when all dependencies are ready (optional)
- * @param error function callback to execute when if dependencies have a errors (optional)
- */
- this.using = function( dependencies, ready, error ) {
- // Validate input
- if ( typeof dependencies !== 'object' && typeof dependencies !== 'string' ) {
- throw new Error( 'dependencies must be a string or an array, not a ' +
- typeof dependencies );
- }
- // Allow calling with a single dependency as a string
- if ( typeof dependencies === 'string' ) {
- dependencies = [dependencies];
- }
- // Resolve entire dependency map
- dependencies = resolve( dependencies );
- // If all dependencies are met, execute ready immediately
- if ( compare( filter( ['ready'], dependencies ), dependencies ) ) {
- if ( $.isFunction( ready ) ) {
- ready();
- }
- }
- // If any dependencies have errors execute error immediately
- else if ( filter( ['error'], dependencies ).length ) {
- if ( $.isFunction( error ) ) {
- error();
- }
- }
- // Since some dependencies are not yet ready, queue up a request
- else {
- request( dependencies, ready, error );
- }
- };
+ // Filter out undefined modules, otherwise resolve() will throw
+ // an exception for trying to load an undefined module.
+ // Undefined modules are acceptable here in load(), because load() takes
+ // an array of unrelated modules, whereas the modules passed to
+ // using() are related and must all be loaded.
+ for ( filtered = [], m = 0; m < modules.length; m += 1 ) {
+ if ( registry[modules[m]] !== undefined ) {
+ filtered[filtered.length] = modules[m];
+ }
+ }
- /**
- * Loads an external script or one or more modules for future use
- *
- * @param modules mixed either the name of a module, array of modules,
- * or a URL of an external script or style
- * @param type string mime-type to use if calling with a URL of an
- * external script or style; acceptable values are "text/css" and
- * "text/javascript"; if no type is provided, text/javascript is
- * assumed
- */
- this.load = function( modules, type ) {
- // Validate input
- if ( typeof modules !== 'object' && typeof modules !== 'string' ) {
- throw new Error( 'modules must be a string or an array, not a ' +
- typeof modules );
- }
- // Allow calling with an external script or single dependency as a string
- if ( typeof modules === 'string' ) {
- // Support adding arbitrary external scripts
- if ( modules.substr( 0, 7 ) === 'http://' || modules.substr( 0, 8 ) === 'https://' || modules.substr( 0, 2 ) === '//' ) {
- if ( type === 'text/css' ) {
- $( 'head' ).append( $( '<link />', {
- rel: 'stylesheet',
- type: 'text/css',
- href: modules
- } ) );
- return true;
- } else if ( type === 'text/javascript' || type === undefined ) {
- addScript( modules );
- return true;
+ // Resolve entire dependency map
+ filtered = resolve( filtered );
+ // If all modules are ready, nothing dependency be done
+ if ( compare( filter( ['ready'], filtered ), filtered ) ) {
+ return;
}
- // Unknown type
- return false;
+ // If any modules have errors
+ else if ( filter( ['error'], filtered ).length ) {
+ return;
+ }
+ // Since some modules are not yet ready, queue up a request
+ else {
+ request( filtered, null, null, async );
+ return;
+ }
+ },
+
+ /**
+ * Changes the state of a module
+ *
+ * @param module {String|Object} module name or object of module name/state pairs
+ * @param state {String} state name
+ */
+ state: function ( module, state ) {
+ var m;
+ if ( typeof module === 'object' ) {
+ for ( m in module ) {
+ mw.loader.state( m, module[m] );
+ }
+ return;
+ }
+ if ( registry[module] === undefined ) {
+ mw.loader.register( module );
+ }
+ registry[module].state = state;
+ },
+
+ /**
+ * Gets the version of a module
+ *
+ * @param module string name of module to get version for
+ */
+ getVersion: function ( module ) {
+ if ( registry[module] !== undefined && registry[module].version !== undefined ) {
+ return formatVersionNumber( registry[module].version );
+ }
+ return null;
+ },
+
+ /**
+ * @deprecated since 1.18 use mw.loader.getVersion() instead
+ */
+ version: function () {
+ return mw.loader.getVersion.apply( mw.loader, arguments );
+ },
+
+ /**
+ * Gets the state of a module
+ *
+ * @param module string name of module to get state for
+ */
+ getState: function ( module ) {
+ if ( registry[module] !== undefined && registry[module].state !== undefined ) {
+ return registry[module].state;
+ }
+ return null;
+ },
+
+ /**
+ * Get names of all registered modules.
+ *
+ * @return {Array}
+ */
+ getModuleNames: function () {
+ return $.map( registry, function ( i, key ) {
+ return key;
+ } );
+ },
+
+ /**
+ * For backwards-compatibility with Squid-cached pages. Loads mw.user
+ */
+ go: function () {
+ mw.loader.load( 'mediawiki.user' );
}
- // Called with single module
- modules = [modules];
- }
- // Resolve entire dependency map
- modules = resolve( modules );
- // If all modules are ready, nothing dependency be done
- if ( compare( filter( ['ready'], modules ), modules ) ) {
- return true;
- }
- // If any modules have errors return false
- else if ( filter( ['error'], modules ).length ) {
- return false;
- }
- // Since some modules are not yet ready, queue up a request
- else {
- request( modules );
- return true;
- }
- };
-
- /**
- * Changes the state of a module
- *
- * @param module string module name or object of module name/state pairs
- * @param state string state name
- */
- this.state = function( module, state ) {
- if ( typeof module === 'object' ) {
- for ( var m in module ) {
- mw.loader.state( m, module[m] );
+ };
+ }() ),
+
+ /** HTML construction helper functions */
+ html: ( function () {
+ function escapeCallback( s ) {
+ switch ( s ) {
+ case "'":
+ return '&#039;';
+ case '"':
+ return '&quot;';
+ case '<':
+ return '&lt;';
+ case '>':
+ return '&gt;';
+ case '&':
+ return '&amp;';
}
- return;
}
- if ( !( module in registry ) ) {
- mw.loader.register( module );
- }
- registry[module].state = state;
- };
-
- /**
- * Gets the version of a module
- *
- * @param module string name of module to get version for
- */
- this.getVersion = function( module ) {
- if ( module in registry && 'version' in registry[module] ) {
- return formatVersionNumber( registry[module].version );
- }
- return null;
- };
- /**
- * @deprecated use mw.loader.getVersion() instead
- */
- this.version = function() {
- return mediaWiki.loader.getVersion.apply( mediaWiki.loader, arguments );
- };
- /**
- * Gets the state of a module
- *
- * @param module string name of module to get state for
- */
- this.getState = function( module ) {
- if ( module in registry && 'state' in registry[module] ) {
- return registry[module].state;
- }
- return null;
- };
+ return {
+ /**
+ * Escape a string for HTML. Converts special characters to HTML entities.
+ * @param s The string to escape
+ */
+ escape: function ( s ) {
+ return s.replace( /['"<>&]/g, escapeCallback );
+ },
- /**
- * For backwards-compatibility with Squid-cached pages. Loads mw.user
- */
- this.go = function() { mw.loader.load( 'mediawiki.user' ); };
-
- /* Cache document ready status */
-
- $(document).ready( function() { ready = true; } );
- } )();
-
- /** HTML construction helper functions */
- this.html = new ( function () {
- var escapeCallback = function( s ) {
- switch ( s ) {
- case "'":
- return '&#039;';
- case '"':
- return '&quot;';
- case '<':
- return '&lt;';
- case '>':
- return '&gt;';
- case '&':
- return '&amp;';
- }
- };
-
- /**
- * Escape a string for HTML. Converts special characters to HTML entities.
- * @param s The string to escape
- */
- this.escape = function( s ) {
- return s.replace( /['"<>&]/g, escapeCallback );
- };
-
- /**
- * Wrapper object for raw HTML passed to mw.html.element().
- */
- this.Raw = function( value ) {
- this.value = value;
- };
-
- /**
- * Wrapper object for CDATA element contents passed to mw.html.element()
- */
- this.Cdata = function( value ) {
- this.value = value;
- };
-
- /**
- * Create an HTML element string, with safe escaping.
- *
- * @param name The tag name.
- * @param attrs An object with members mapping element names to values
- * @param contents The contents of the element. May be either:
- * - string: The string is escaped.
- * - null or undefined: The short closing form is used, e.g. <br/>.
- * - this.Raw: The value attribute is included without escaping.
- * - this.Cdata: The value attribute is included, and an exception is
- * thrown if it contains an illegal ETAGO delimiter.
- * See http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.3.2
- *
- * Example:
- * var h = mw.html;
- * return h.element( 'div', {},
- * new h.Raw( h.element( 'img', {src: '<'} ) ) );
- * Returns <div><img src="&lt;"/></div>
- */
- this.element = function( name, attrs, contents ) {
- var v, s = '<' + name;
- for ( var attrName in attrs ) {
- v = attrs[attrName];
- // Convert name=true, to name=name
- if ( v === true ) {
- v = attrName;
- // Skip name=false
- } else if ( v === false ) {
- continue;
- }
- s += ' ' + attrName + '="' + this.escape( '' + v ) + '"';
- }
- if ( contents === undefined || contents === null ) {
- // Self close tag
- s += '/>';
- return s;
- }
- // Regular open tag
- s += '>';
- switch ( typeof contents ) {
- case 'string':
- // Escaped
- s += this.escape( contents );
- break;
- case 'number':
- case 'boolean':
- // Convert to string
- s += '' + contents;
- break;
- default:
- if ( contents instanceof this.Raw ) {
- // Raw HTML inclusion
- s += contents.value;
- } else if ( contents instanceof this.Cdata ) {
- // CDATA
- if ( /<\/[a-zA-z]/.test( contents.value ) ) {
- throw new Error( 'mw.html.element: Illegal end tag found in CDATA' );
+ /**
+ * Wrapper object for raw HTML passed to mw.html.element().
+ * @constructor
+ */
+ Raw: function ( value ) {
+ this.value = value;
+ },
+
+ /**
+ * Wrapper object for CDATA element contents passed to mw.html.element()
+ * @constructor
+ */
+ Cdata: function ( value ) {
+ this.value = value;
+ },
+
+ /**
+ * Create an HTML element string, with safe escaping.
+ *
+ * @param name The tag name.
+ * @param attrs An object with members mapping element names to values
+ * @param contents The contents of the element. May be either:
+ * - string: The string is escaped.
+ * - null or undefined: The short closing form is used, e.g. <br/>.
+ * - this.Raw: The value attribute is included without escaping.
+ * - this.Cdata: The value attribute is included, and an exception is
+ * thrown if it contains an illegal ETAGO delimiter.
+ * See http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.3.2
+ *
+ * Example:
+ * var h = mw.html;
+ * return h.element( 'div', {},
+ * new h.Raw( h.element( 'img', {src: '<'} ) ) );
+ * Returns <div><img src="&lt;"/></div>
+ */
+ element: function ( name, attrs, contents ) {
+ var v, attrName, s = '<' + name;
+
+ for ( attrName in attrs ) {
+ v = attrs[attrName];
+ // Convert name=true, to name=name
+ if ( v === true ) {
+ v = attrName;
+ // Skip name=false
+ } else if ( v === false ) {
+ continue;
}
- s += contents.value;
- } else {
- throw new Error( 'mw.html.element: Invalid type of contents' );
+ s += ' ' + attrName + '="' + this.escape( String( v ) ) + '"';
}
- }
- s += '</' + name + '>';
- return s;
- };
- } )();
-
- /* Extension points */
-
- this.legacy = {};
+ if ( contents === undefined || contents === null ) {
+ // Self close tag
+ s += '/>';
+ return s;
+ }
+ // Regular open tag
+ s += '>';
+ switch ( typeof contents ) {
+ case 'string':
+ // Escaped
+ s += this.escape( contents );
+ break;
+ case 'number':
+ case 'boolean':
+ // Convert to string
+ s += String( contents );
+ break;
+ default:
+ if ( contents instanceof this.Raw ) {
+ // Raw HTML inclusion
+ s += contents.value;
+ } else if ( contents instanceof this.Cdata ) {
+ // CDATA
+ if ( /<\/[a-zA-z]/.test( contents.value ) ) {
+ throw new Error( 'mw.html.element: Illegal end tag found in CDATA' );
+ }
+ s += contents.value;
+ } else {
+ throw new Error( 'mw.html.element: Invalid type of contents' );
+ }
+ }
+ s += '</' + name + '>';
+ return s;
+ }
+ };
+ })(),
-} )( jQuery );
+ // Skeleton user object. mediawiki.user.js extends this
+ user: {
+ options: new Map(),
+ tokens: new Map()
+ }
+ };
+
+})( jQuery );
// Alias $j to jQuery for backwards compatibility
window.$j = jQuery;
-// Global alias
-window.mw = mediaWiki;
-
-/* Auto-register from pre-loaded startup scripts */
+// Attach to window and globally alias
+window.mw = window.mediaWiki = mw;
-if ( jQuery.isFunction( startUp ) ) {
+// Auto-register from pre-loaded startup scripts
+if ( typeof startUp !== 'undefined' && jQuery.isFunction( startUp ) ) {
startUp();
- delete startUp;
+ startUp = undefined;
}
diff --git a/resources/mediawiki/mediawiki.log.js b/resources/mediawiki/mediawiki.log.js
index 38f3411f..ad4c73df 100644
--- a/resources/mediawiki/mediawiki.log.js
+++ b/resources/mediawiki/mediawiki.log.js
@@ -1,67 +1,70 @@
-/*
- * Implementation for mediaWiki.log stub
+/**
+ * Logger for MediaWiki javascript.
+ * Implements the stub left by the main 'mediawiki' module.
+ *
+ * @author Michael Dale <mdale@wikimedia.org>
+ * @author Trevor Parscal <tparscal@wikimedia.org>
*/
-(function( $ ) {
+( function ( $ ) {
/**
- * Log output to the console.
+ * Logs a message to the console.
*
- * In the case that the browser does not have a console available, one is created by appending a
- * <div> element to the bottom of the body and then appending a <div> element to that for each
- * message.
+ * In the case the browser does not have a console API, a console is created on-the-fly by appending
+ * a <div id="mw-log-console"> element to the bottom of the body and then appending this and future
+ * messages to that, instead of the console.
*
- * @author Michael Dale <mdale@wikimedia.org>
- * @author Trevor Parscal <tparscal@wikimedia.org>
- * @param logmsg string Message to output to console.
+ * @param {String} First in list of variadic messages to output to console.
*/
- mw.log = function( logmsg ) {
- // Allow log messages to use a configured prefix to identify the source window (ie. frame)
- if ( mw.config.exists( 'mw.log.prefix' ) ) {
- logmsg = mw.config.get( 'mw.log.prefix' ) + '> ' + logmsg;
- }
+ mw.log = function( /* logmsg, logmsg, */ ) {
+ // Turn arguments into an array
+ var args = Array.prototype.slice.call( arguments ),
+ // Allow log messages to use a configured prefix to identify the source window (ie. frame)
+ prefix = mw.config.exists( 'mw.log.prefix' ) ? mw.config.get( 'mw.log.prefix' ) + '> ' : '';
+
// Try to use an existing console
if ( window.console !== undefined && $.isFunction( window.console.log ) ) {
- window.console.log( logmsg );
- } else {
- // Set timestamp
- var d = new Date();
- var time = ( d.getHours() < 10 ? '0' + d.getHours() : d.getHours() ) +
+ args.unshift( prefix );
+ window.console.log.apply( window.console, args );
+ return;
+ }
+
+ // If there is no console, use our own log box
+ mw.loader.using( 'jquery.footHovzer', function () {
+
+ var d = new Date(),
+ // Create HH:MM:SS.MIL timestamp
+ time = ( d.getHours() < 10 ? '0' + d.getHours() : d.getHours() ) +
':' + ( d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes() ) +
':' + ( d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds() ) +
- '.' + ( d.getMilliseconds() < 10 ? '00' + d.getMilliseconds() : ( d.getMilliseconds() < 100 ? '0' + d.getMilliseconds() : d.getMilliseconds() ) );
- // Show a log box for console-less browsers
- var $log = $( '#mw-log-console' );
+ '.' + ( d.getMilliseconds() < 10 ? '00' + d.getMilliseconds() : ( d.getMilliseconds() < 100 ? '0' + d.getMilliseconds() : d.getMilliseconds() ) ),
+ $log = $( '#mw-log-console' );
+
if ( !$log.length ) {
- $log = $( '<div id="mw-log-console"></div>' )
- .css( {
- 'position': 'fixed',
- 'overflow': 'auto',
- 'z-index': 500,
- 'bottom': '0px',
- 'left': '0px',
- 'right': '0px',
- 'height': '150px',
- 'background-color': 'white',
- 'border-top': 'solid 2px #ADADAD'
+ $log = $( '<div id="mw-log-console"></div>' ).css( {
+ overflow: 'auto',
+ height: '150px',
+ backgroundColor: 'white',
+ borderTop: 'solid 2px #ADADAD'
} );
- $( 'body' )
- .css( 'padding-bottom', '150px' ) // don't hide anything
- .append( $log );
+ var hovzer = $.getFootHovzer();
+ hovzer.$.append( $log );
+ hovzer.update();
}
$log.append(
$( '<div></div>' )
.css( {
- 'border-bottom': 'solid 1px #DDDDDD',
- 'font-size': 'small',
- 'font-family': 'monospace',
- 'white-space': 'pre-wrap',
- 'padding': '0.125em 0.25em'
+ borderBottom: 'solid 1px #DDDDDD',
+ fontSize: 'small',
+ fontFamily: 'monospace',
+ whiteSpace: 'pre-wrap',
+ padding: '0.125em 0.25em'
} )
- .text( logmsg )
- .prepend( '<span style="float:right">[' + time + ']</span>' )
+ .text( prefix + args.join( ', ' ) )
+ .prepend( '<span style="float: right;">[' + time + ']</span>' )
);
- }
+ } );
};
-})(jQuery);
+})( jQuery );
diff --git a/resources/mediawiki/mediawiki.user.js b/resources/mediawiki/mediawiki.user.js
index b0176cf4..7f881b0e 100644
--- a/resources/mediawiki/mediawiki.user.js
+++ b/resources/mediawiki/mediawiki.user.js
@@ -1,5 +1,5 @@
/*
- * Implementation for mediaWiki.log stub
+ * Implementation for mediaWiki.user
*/
(function( $ ) {
@@ -7,7 +7,7 @@
/**
* User object
*/
- function User() {
+ function User( options, tokens ) {
/* Private Members */
@@ -15,9 +15,9 @@
/* Public Members */
- this.options = new mw.Map();
+ this.options = options || new mw.Map();
- this.tokens = new mw.Map();
+ this.tokens = tokens || new mw.Map();
/* Public Methods */
@@ -176,6 +176,8 @@
};
}
- mw.user = new User();
+ // Extend the skeleton mw.user from mediawiki.js
+ // This is kind of ugly but we're stuck with this for b/c reasons
+ mw.user = new User( mw.user.options, mw.user.tokens );
})(jQuery); \ No newline at end of file
diff --git a/resources/mediawiki/mediawiki.util.js b/resources/mediawiki/mediawiki.util.js
index 59727b3d..0a95d102 100644
--- a/resources/mediawiki/mediawiki.util.js
+++ b/resources/mediawiki/mediawiki.util.js
@@ -1,109 +1,112 @@
/**
- * Utilities
+ * Implements mediaWiki.util library
*/
-( function( $ ) {
+( function ( $, mw ) {
+ "use strict";
// Local cache and alias
- var util = mw.util = {
+ var util = {
- /* Initialisation */
/**
- * @var boolean Wether or not already initialised
+ * Initialisation
+ * (don't call before document ready)
*/
- 'initialised' : false,
- 'init' : function() {
- if ( this.initialised === false ) {
- this.initialised = true;
-
- // Folllowing the initialisation after the DOM is ready
- $(document).ready( function() {
-
- /* Set up $.messageBox */
- $.messageBoxNew( {
- 'id': 'mw-js-message',
- 'parent': '#content'
- } );
+ init: function () {
+ var profile, $tocTitle, $tocToggleLink, hideTocCookie;
- // Shortcut to client profile return
- var profile = $.client.profile();
-
- /* Set tooltipAccessKeyPrefix */
-
- // Opera on any platform
- if ( profile.name == 'opera' ) {
- util.tooltipAccessKeyPrefix = 'shift-esc-';
-
- // Chrome on any platform
- } else if ( profile.name == 'chrome' ) {
- // Chrome on Mac or Chrome on other platform ?
- util.tooltipAccessKeyPrefix = ( profile.platform == 'mac'
- ? 'ctrl-option-' : 'alt-' );
-
- // Non-Windows Safari with webkit_version > 526
- } else if ( profile.platform !== 'win'
- && profile.name == 'safari'
- && profile.layoutVersion > 526 ) {
- util.tooltipAccessKeyPrefix = 'ctrl-alt-';
-
- // Safari/Konqueror on any platform, or any browser on Mac
- // (but not Safari on Windows)
- } else if ( !( profile.platform == 'win' && profile.name == 'safari' )
- && ( profile.name == 'safari'
- || profile.platform == 'mac'
- || profile.name == 'konqueror' ) ) {
- util.tooltipAccessKeyPrefix = 'ctrl-';
-
- // Firefox 2.x and later
- } else if ( profile.name == 'firefox' && profile.versionBase > '1' ) {
- util.tooltipAccessKeyPrefix = 'alt-shift-';
- }
+ /* Set up $.messageBox */
+ $.messageBoxNew( {
+ id: 'mw-js-message',
+ parent: '#content'
+ } );
- /* Fill $content var */
- if ( $( '#bodyContent' ).length ) {
- // Vector, Monobook, Chick etc.
- util.$content = $( '#bodyContent' );
+ /* Set tooltipAccessKeyPrefix */
+ profile = $.client.profile();
+
+ // Opera on any platform
+ if ( profile.name === 'opera' ) {
+ util.tooltipAccessKeyPrefix = 'shift-esc-';
+
+ // Chrome on any platform
+ } else if ( profile.name === 'chrome' ) {
+
+ util.tooltipAccessKeyPrefix = (
+ profile.platform === 'mac'
+ // Chrome on Mac
+ ? 'ctrl-option-'
+ : profile.platform === 'win'
+ // Chrome on Windows
+ // (both alt- and alt-shift work, but alt-f triggers Chrome wrench menu
+ // which alt-shift-f does not)
+ ? 'alt-shift-'
+ // Chrome on other (Ubuntu?)
+ : 'alt-'
+ );
- } else if ( $( '#mw_contentholder' ).length ) {
- // Modern
- util.$content = $( '#mw_contentholder' );
+ // Non-Windows Safari with webkit_version > 526
+ } else if ( profile.platform !== 'win'
+ && profile.name === 'safari'
+ && profile.layoutVersion > 526 ) {
+ util.tooltipAccessKeyPrefix = 'ctrl-alt-';
+
+ // Safari/Konqueror on any platform, or any browser on Mac
+ // (but not Safari on Windows)
+ } else if ( !( profile.platform === 'win' && profile.name === 'safari' )
+ && ( profile.name === 'safari'
+ || profile.platform === 'mac'
+ || profile.name === 'konqueror' ) ) {
+ util.tooltipAccessKeyPrefix = 'ctrl-';
+
+ // Firefox 2.x and later
+ } else if ( profile.name === 'firefox' && profile.versionBase > '1' ) {
+ util.tooltipAccessKeyPrefix = 'alt-shift-';
+ }
- } else if ( $( '#article' ).length ) {
- // Standard, CologneBlue
- util.$content = $( '#article' );
+ /* Fill $content var */
+ if ( $( '#bodyContent' ).length ) {
+ // Vector, Monobook, Chick etc.
+ util.$content = $( '#bodyContent' );
- } else {
- // #content is present on almost all if not all skins. Most skins (the above cases)
- // have #content too, but as an outer wrapper instead of the article text container.
- // The skins that don't have an outer wrapper do have #content for everything
- // so it's a good fallback
- util.$content = $( '#content' );
- }
+ } else if ( $( '#mw_contentholder' ).length ) {
+ // Modern
+ util.$content = $( '#mw_contentholder' );
- /* Table of Contents toggle */
- var $tocContainer = $( '#toc' ),
- $tocTitle = $( '#toctitle' ),
- $tocToggleLink = $( '#togglelink' );
- // Only add it if there is a TOC and there is no toggle added already
- if ( $tocContainer.size() && $tocTitle.size() && !$tocToggleLink.size() ) {
- var hideTocCookie = $.cookie( 'mw_hidetoc' );
- $tocToggleLink = $( '<a href="#" class="internal" id="togglelink"></a>' )
- .text( mw.msg( 'hidetoc' ) )
- .click( function(e){
- e.preventDefault();
- util.toggleToc( $(this) );
- } );
- $tocTitle.append( $tocToggleLink.wrap( '<span class="toctoggle"></span>' ).parent().prepend( '&nbsp;[' ).append( ']&nbsp;' ) );
-
- if ( hideTocCookie == '1' ) {
- // Cookie says user want toc hidden
- $tocToggleLink.click();
- }
- }
- } );
+ } else if ( $( '#article' ).length ) {
+ // Standard, CologneBlue
+ util.$content = $( '#article' );
- return true;
+ } else {
+ // #content is present on almost all if not all skins. Most skins (the above cases)
+ // have #content too, but as an outer wrapper instead of the article text container.
+ // The skins that don't have an outer wrapper do have #content for everything
+ // so it's a good fallback
+ util.$content = $( '#content' );
+ }
+
+ // Table of contents toggle
+ $tocTitle = $( '#toctitle' );
+ $tocToggleLink = $( '#togglelink' );
+ // Only add it if there is a TOC and there is no toggle added already
+ if ( $( '#toc' ).length && $tocTitle.length && !$tocToggleLink.length ) {
+ hideTocCookie = $.cookie( 'mw_hidetoc' );
+ $tocToggleLink = $( '<a href="#" class="internal" id="togglelink"></a>' )
+ .text( mw.msg( 'hidetoc' ) )
+ .click( function ( e ) {
+ e.preventDefault();
+ util.toggleToc( $(this) );
+ } );
+ $tocTitle.append(
+ $tocToggleLink
+ .wrap( '<span class="toctoggle"></span>' )
+ .parent()
+ .prepend( '&nbsp;[' )
+ .append( ']&nbsp;' )
+ );
+
+ if ( hideTocCookie === '1' ) {
+ util.toggleToc( $tocToggleLink );
+ }
}
- return false;
},
/* Main body */
@@ -113,8 +116,8 @@
*
* @param str string String to be encoded
*/
- 'rawurlencode' : function( str ) {
- str = ( str + '' ).toString();
+ rawurlencode: function ( str ) {
+ str = String( str );
return encodeURIComponent( str )
.replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' )
.replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' );
@@ -127,20 +130,20 @@
*
* @param str string String to be encoded
*/
- 'wikiUrlencode' : function( str ) {
- return this.rawurlencode( str )
+ wikiUrlencode: function ( str ) {
+ return util.rawurlencode( str )
.replace( /%20/g, '_' ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' );
},
/**
* Get the link to a page name (relative to wgServer)
*
- * @param str string Page name to get the link for.
- * @return string Location for a page with name of 'str' or boolean false on error.
+ * @param str String: Page name to get the link for.
+ * @return String: Location for a page with name of 'str' or boolean false on error.
*/
- 'wikiGetlink' : function( str ) {
+ wikiGetlink: function ( str ) {
return mw.config.get( 'wgArticlePath' ).replace( '$1',
- this.wikiUrlencode( str || mw.config.get( 'wgPageName' ) ) );
+ util.wikiUrlencode( typeof str === 'string' ? str : mw.config.get( 'wgPageName' ) ) );
},
/**
@@ -150,8 +153,9 @@
* @param str string Name of script (eg. 'api'), defaults to 'index'
* @return string Address to script (eg. '/w/api.php' )
*/
- 'wikiScript' : function( str ) {
- return mw.config.get( 'wgScriptPath' ) + '/' + ( str || 'index' ) + mw.config.get( 'wgScriptExtension' );
+ wikiScript: function ( str ) {
+ return mw.config.get( 'wgScriptPath' ) + '/' + ( str || 'index' ) +
+ mw.config.get( 'wgScriptExtension' );
},
/**
@@ -160,7 +164,7 @@
* @param text string CSS to be appended
* @return CSSStyleSheet
*/
- 'addCSS' : function( text ) {
+ addCSS: function ( text ) {
var s = document.createElement( 'style' );
s.type = 'text/css';
s.rel = 'stylesheet';
@@ -169,7 +173,8 @@
if ( s.styleSheet ) {
s.styleSheet.cssText = text; // IE
} else {
- s.appendChild( document.createTextNode( text + '' ) ); // Safari sometimes borks on null
+ // Safari sometimes borks on null
+ s.appendChild( document.createTextNode( String( text ) ) );
}
return s.sheet || s;
},
@@ -183,12 +188,12 @@
* @return mixed Boolean visibility of the toc (true if it's visible)
* or Null if there was no table of contents.
*/
- 'toggleToc' : function( $toggleLink, callback ) {
+ toggleToc: function ( $toggleLink, callback ) {
var $tocList = $( '#toc ul:first' );
// This function shouldn't be called if there's no TOC,
// but just in case...
- if ( $tocList.size() ) {
+ if ( $tocList.length ) {
if ( $tocList.is( ':hidden' ) ) {
$tocList.slideDown( 'fast', callback );
$toggleLink.text( mw.msg( 'hidetoc' ) );
@@ -221,11 +226,11 @@
* @param url string URL to search through (optional)
* @return mixed Parameter value or null.
*/
- 'getParamValue' : function( param, url ) {
- url = url ? url : document.location.href;
+ getParamValue: function ( param, url ) {
+ url = url || document.location.href;
// Get last match, stop at hash
- var re = new RegExp( '^[^#]*[&?]' + $.escapeRE( param ) + '=([^&#]*)' );
- var m = re.exec( url );
+ var re = new RegExp( '^[^#]*[&?]' + $.escapeRE( param ) + '=([^&#]*)' ),
+ m = re.exec( url );
if ( m && m.length > 1 ) {
// Beware that decodeURIComponent is not required to understand '+'
// by spec, as encodeURIComponent does not produce it.
@@ -239,13 +244,13 @@
* Access key prefix. Will be re-defined based on browser/operating system
* detection in mw.util.init().
*/
- 'tooltipAccessKeyPrefix' : 'alt-',
+ tooltipAccessKeyPrefix: 'alt-',
/**
* @var RegExp
* Regex to match accesskey tooltips.
*/
- 'tooltipAccessKeyRegexp': /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
+ tooltipAccessKeyRegexp: /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
/**
* Add the appropriate prefix to the accesskey shown in the tooltip.
@@ -253,36 +258,25 @@
* otherwise, all the nodes that will probably have accesskeys by
* default are updated.
*
- * @param nodeList {Array|jQuery} (optional) A jQuery object, or array of elements to update.
+ * @param $nodes {Array|jQuery} [optional] A jQuery object, or array
+ * of elements to update.
*/
- 'updateTooltipAccessKeys' : function( nodeList ) {
- var $nodes;
- if ( !nodeList ) {
-
- // Rather than scanning all links, just the elements that
- // contain the relevant links
- this.updateTooltipAccessKeys(
- $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a' ) );
-
- // these are rare enough that no such optimization is needed
- this.updateTooltipAccessKeys( $( 'input' ) );
- this.updateTooltipAccessKeys( $( 'label' ) );
-
- return;
-
- } else if ( nodeList instanceof jQuery ) {
- $nodes = nodeList;
- } else {
- $nodes = $( nodeList );
+ updateTooltipAccessKeys: function ( $nodes ) {
+ if ( !$nodes ) {
+ // Rather than going into a loop of all anchor tags, limit to few elements that
+ // contain the relevant anchor tags.
+ // Input and label are rare enough that no such optimization is needed
+ $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label' );
+ } else if ( !( $nodes instanceof $ ) ) {
+ $nodes = $( $nodes );
}
- $nodes.each( function ( i ) {
- var tip = $(this).attr( 'title' );
- if ( !!tip && util.tooltipAccessKeyRegexp.exec( tip ) ) {
- tip = tip.replace( util.tooltipAccessKeyRegexp,
- '[' + util.tooltipAccessKeyPrefix + "$5]" );
- $(this).attr( 'title', tip );
+ $nodes.attr( 'title', function ( i, val ) {
+ if ( val && util.tooltipAccessKeyRegexp.exec( val ) ) {
+ return val.replace( util.tooltipAccessKeyRegexp,
+ '[' + util.tooltipAccessKeyPrefix + '$5]' );
}
+ return val;
} );
},
@@ -291,7 +285,7 @@
* A jQuery object that refers to the page-content element
* Populated by init().
*/
- '$content' : null,
+ $content: null,
/**
* Add a link to a portlet menu on the page, such as:
@@ -328,14 +322,15 @@
* @return mixed The DOM Node of the added item (a ListItem or Anchor element,
* depending on the skin) or null if no element was added to the document.
*/
- 'addPortletLink' : function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
+ addPortletLink: function ( portlet, href, text, id, tooltip, accesskey, nextnode ) {
+ var $item, $link, $portlet, $ul;
// Check if there's atleast 3 arguments to prevent a TypeError
if ( arguments.length < 3 ) {
return null;
}
// Setup the anchor tag
- var $link = $( '<a></a>' ).attr( 'href', href ).text( text );
+ $link = $( '<a>' ).attr( 'href', href ).text( text );
if ( tooltip ) {
$link.attr( 'title', tooltip );
}
@@ -343,22 +338,22 @@
// Some skins don't have any portlets
// just add it to the bottom of their 'sidebar' element as a fallback
switch ( mw.config.get( 'skin' ) ) {
- case 'standard' :
- case 'cologneblue' :
- $( '#quickbar' ).append( $link.after( '<br />' ) );
+ case 'standard':
+ case 'cologneblue':
+ $( '#quickbar' ).append( $link.after( '<br/>' ) );
return $link[0];
- case 'nostalgia' :
- $( '#searchform' ).before( $link).before( ' &#124; ' );
+ case 'nostalgia':
+ $( '#searchform' ).before( $link ).before( ' &#124; ' );
return $link[0];
- default : // Skins like chick, modern, monobook, myskin, simple, vector...
+ default: // Skins like chick, modern, monobook, myskin, simple, vector...
// Select the specified portlet
- var $portlet = $( '#' + portlet );
+ $portlet = $( '#' + portlet );
if ( $portlet.length === 0 ) {
return null;
}
// Select the first (most likely only) unordered list inside the portlet
- var $ul = $portlet.find( 'ul' );
+ $ul = $portlet.find( 'ul' );
// If it didn't have an unordered list yet, create it
if ( $ul.length === 0 ) {
@@ -383,7 +378,6 @@
// Wrap the anchor tag in a list item (and a span if $portlet is a Vector tab)
// and back up the selector to the list item
- var $item;
if ( $portlet.hasClass( 'vectorTabs' ) ) {
$item = $link.wrap( '<li><span></span></li>' ).parent().parent();
} else {
@@ -400,19 +394,18 @@
$link.attr( 'title', tooltip );
}
if ( accesskey && tooltip ) {
- this.updateTooltipAccessKeys( $link );
+ util.updateTooltipAccessKeys( $link );
}
// Where to put our node ?
- // - nextnode is a DOM element (before MW 1.17, in wikibits.js, this was the only option)
- if ( nextnode && nextnode.parentNode == $ul[0] ) {
+ // - nextnode is a DOM element (was the only option before MW 1.17, in wikibits.js)
+ if ( nextnode && nextnode.parentNode === $ul[0] ) {
$(nextnode).before( $item );
// - nextnode is a CSS selector for jQuery
- } else if ( typeof nextnode == 'string' && $ul.find( nextnode ).length !== 0 ) {
+ } else if ( typeof nextnode === 'string' && $ul.find( nextnode ).length !== 0 ) {
$ul.find( nextnode ).eq( 0 ).before( $item );
-
// If the jQuery selector isn't found within the <ul>,
// or if nextnode was invalid or not passed at all,
// then just append it at the end of the <ul> (this is the default behaviour)
@@ -430,15 +423,13 @@
* something, replacing any previous message.
* Calling with no arguments, with an empty string or null will hide the message
*
- * @param message mixed The DOM-element or HTML-string to be put inside the message box.
- * @param className string Used in adding a class; should be different for each call
+ * @param message {mixed} The DOM-element, jQuery object or HTML-string to be put inside the message box.
+ * @param className {String} Used in adding a class; should be different for each call
* to allow CSS/JS to hide different boxes. null = no class used.
- * @return boolean True on success, false on failure.
+ * @return {Boolean} True on success, false on failure.
*/
- 'jsMessage' : function( message, className ) {
-
+ jsMessage: function ( message, className ) {
if ( !arguments.length || message === '' || message === null ) {
-
$( '#mw-js-message' ).empty().hide();
return true; // Emptying and hiding message is intended behaviour, return true
@@ -448,7 +439,7 @@
// an mw-js-message div to start with.
var $messageDiv = $( '#mw-js-message' );
if ( !$messageDiv.length ) {
- $messageDiv = $( '<div id="mw-js-message">' );
+ $messageDiv = $( '<div id="mw-js-message"></div>' );
if ( util.$content.parent().length ) {
util.$content.parent().prepend( $messageDiv );
} else {
@@ -457,12 +448,12 @@
}
if ( className ) {
- $messageDiv.attr( 'class', 'mw-js-message-' + className );
+ $messageDiv.prop( 'class', 'mw-js-message-' + className );
}
if ( typeof message === 'object' ) {
$messageDiv.empty();
- $messageDiv.append( message ); // Append new content
+ $messageDiv.append( message );
} else {
$messageDiv.html( message );
}
@@ -483,8 +474,10 @@
* @return mixed Null if mailtxt was an empty string, otherwise true/false
* is determined by validation.
*/
- 'validateEmail' : function( mailtxt ) {
- if( mailtxt === '' ) {
+ validateEmail: function ( mailtxt ) {
+ var rfc5322_atext, rfc1034_ldh_str, HTML5_email_regexp;
+
+ if ( mailtxt === '' ) {
return null;
}
@@ -500,7 +493,7 @@
*/
/**
- * First, define the RFC 5322 'atext' which is pretty easy :
+ * First, define the RFC 5322 'atext' which is pretty easy:
* atext = ALPHA / DIGIT / ; Printable US-ASCII
"!" / "#" / ; characters not including
"$" / "%" / ; specials. Used for atoms.
@@ -513,7 +506,7 @@
"|" / "}" /
"~"
*/
- var rfc5322_atext = "a-z0-9!#$%&'*+\\-/=?^_`{|}~",
+ rfc5322_atext = "a-z0-9!#$%&'*+\\-/=?^_`{|}~";
/**
* Next define the RFC 1034 'ldh-str'
@@ -524,29 +517,29 @@
* <let-dig-hyp> ::= <let-dig> | "-"
* <let-dig> ::= <letter> | <digit>
*/
- rfc1034_ldh_str = "a-z0-9\\-",
-
- HTML5_email_regexp = new RegExp(
- // start of string
- '^'
- +
- // User part which is liberal :p
- '[' + rfc5322_atext + '\\.]+'
- +
- // 'at'
- '@'
- +
- // Domain first part
- '[' + rfc1034_ldh_str + ']+'
- +
- // Optional second part and following are separated by a dot
- '(?:\\.[' + rfc1034_ldh_str + ']+)*'
- +
- // End of string
- '$',
- // RegExp is case insensitive
- 'i'
- );
+ rfc1034_ldh_str = "a-z0-9\\-";
+
+ HTML5_email_regexp = new RegExp(
+ // start of string
+ '^'
+ +
+ // User part which is liberal :p
+ '[' + rfc5322_atext + '\\.]+'
+ +
+ // 'at'
+ '@'
+ +
+ // Domain first part
+ '[' + rfc1034_ldh_str + ']+'
+ +
+ // Optional second part and following are separated by a dot
+ '(?:\\.[' + rfc1034_ldh_str + ']+)*'
+ +
+ // End of string
+ '$',
+ // RegExp is case insensitive
+ 'i'
+ );
return (null !== mailtxt.match( HTML5_email_regexp ) );
},
@@ -557,12 +550,18 @@
* @param allowBlock boolean
* @return boolean
*/
- 'isIPv4Address' : function( address, allowBlock ) {
- var block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
- var RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
- var RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
- return typeof address === 'string' && address.search( new RegExp( '^' + RE_IP_ADD + block + '$' ) ) != -1;
+ isIPv4Address: function ( address, allowBlock ) {
+ if ( typeof address !== 'string' ) {
+ return false;
+ }
+
+ var block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '',
+ RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])',
+ RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
+
+ return address.search( new RegExp( '^' + RE_IP_ADD + block + '$' ) ) !== -1;
},
+
/**
* Note: borrows from IP::isIPv6
*
@@ -570,12 +569,13 @@
* @param allowBlock boolean
* @return boolean
*/
- 'isIPv6Address' : function( address, allowBlock ) {
+ isIPv6Address: function ( address, allowBlock ) {
if ( typeof address !== 'string' ) {
return false;
}
- var block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '';
- var RE_IPV6_ADD =
+
+ var block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '',
+ RE_IPV6_ADD =
'(?:' + // starts with "::" (including "::")
':(?::|(?::' + '[0-9A-Fa-f]{1,4}' + '){1,7})' +
'|' + // ends with "::" (except "::")
@@ -583,17 +583,19 @@
'|' + // contains no "::"
'[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){7}' +
')';
- if ( address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) != -1 ) {
+
+ if ( address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) !== -1 ) {
return true;
}
+
RE_IPV6_ADD = // contains one "::" in the middle (single '::' check below)
'[0-9A-Fa-f]{1,4}' + '(?:::?' + '[0-9A-Fa-f]{1,4}' + '){1,6}';
- return address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) != -1
- && address.search( /::/ ) != -1 && address.search( /::.*::/ ) == -1;
- }
+ return address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) !== -1
+ && address.search( /::/ ) !== -1 && address.search( /::.*::/ ) === -1;
+ }
};
- util.init();
+ mw.util = util;
-} )( jQuery );
+} )( jQuery, mediaWiki );