'ResourceLoaderForeignApiModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderForeignApiModule.php',
'ResourceLoaderImage' => __DIR__ . '/includes/resourceloader/ResourceLoaderImage.php',
'ResourceLoaderImageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderImageModule.php',
- 'ResourceLoaderJqueryMsgModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderJqueryMsgModule.php',
'ResourceLoaderLanguageDataModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageDataModule.php',
- 'ResourceLoaderLanguageNamesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageNamesModule.php',
'ResourceLoaderLessVarFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLessVarFileModule.php',
- 'ResourceLoaderMediaWikiUtilModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderMediaWikiUtilModule.php',
'ResourceLoaderModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderModule.php',
'ResourceLoaderOOUIFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIFileModule.php',
'ResourceLoaderOOUIImageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIImageModule.php',
'ResourceLoaderSiteModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSiteModule.php',
'ResourceLoaderSiteStylesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSiteStylesModule.php',
'ResourceLoaderSkinModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSkinModule.php',
- 'ResourceLoaderSpecialCharacterDataModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSpecialCharacterDataModule.php',
'ResourceLoaderStartUpModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderStartUpModule.php',
- 'ResourceLoaderUploadDialogModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUploadDialogModule.php',
'ResourceLoaderUserDefaultsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserDefaultsModule.php',
'ResourceLoaderUserModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserModule.php',
'ResourceLoaderUserOptionsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserOptionsModule.php',
+++ /dev/null
-<?php
-/**
- * ResourceLoader module for mediawiki.jqueryMsg that provides generated data.
- *
- * 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * ResourceLoader module for mediawiki.jqueryMsg and its generated data
- */
-class ResourceLoaderJqueryMsgModule extends ResourceLoaderFileModule {
-
- /**
- * @param ResourceLoaderContext $context
- * @return string JavaScript code
- */
- public function getScript( ResourceLoaderContext $context ) {
- $fileScript = parent::getScript( $context );
-
- $tagData = Sanitizer::getRecognizedTagData();
- $allowedHtmlElements = array_merge(
- array_keys( $tagData['htmlpairs'] ),
- array_diff(
- array_keys( $tagData['htmlsingle'] ),
- array_keys( $tagData['htmlsingleonly'] )
- )
- );
-
- $magicWords = [
- 'SITENAME' => $this->getConfig()->get( 'Sitename' ),
- ];
- Hooks::run( 'ResourceLoaderJqueryMsgModuleMagicWords', [ $context, &$magicWords ] );
-
- $parserDefaults = [
- 'allowedHtmlElements' => $allowedHtmlElements,
- 'magic' => $magicWords,
- ];
-
- $setDataScript = Xml::encodeJsCall( 'mw.jqueryMsg.setParserDefaults', [
- $parserDefaults,
- // Pass deep=true because mediawiki.jqueryMsg.js contains
- // page-specific magic words that must not be overwritten.
- true,
- ] );
-
- return $fileScript . $setDataScript;
- }
-
- /**
- * @param ResourceLoaderContext $context
- * @return array
- */
- public function getScriptURLsForDebug( ResourceLoaderContext $context ) {
- // Bypass file module urls
- return ResourceLoaderModule::getScriptURLsForDebug( $context );
- }
-
- /**
- * @return bool
- */
- public function enableModuleContentVersion() {
- return true;
- }
-}
+++ /dev/null
-<?php
-/**
- * ResourceLoader module for providing language names.
- *
- * By default these names will be autonyms however other extensions may
- * provided language names in the context language (e.g. cldr extension)
- *
- * 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Ed Sanders
- * @author Trevor Parscal
- */
-
-/**
- * ResourceLoader module for populating language specific data.
- */
-class ResourceLoaderLanguageNamesModule extends ResourceLoaderFileModule {
-
- protected $targets = [ 'desktop', 'mobile' ];
-
- /**
- * @param ResourceLoaderContext $context
- * @return array
- */
- protected function getData( ResourceLoaderContext $context ) {
- return Language::fetchLanguageNames(
- $context->getLanguage(),
- 'all'
- );
- }
-
- /**
- * @param ResourceLoaderContext $context
- * @return string JavaScript code
- */
- public function getScript( ResourceLoaderContext $context ) {
- return Xml::encodeJsCall(
- 'mw.language.setData',
- [
- $context->getLanguage(),
- 'languageNames',
- $this->getData( $context )
- ],
- ResourceLoader::inDebugMode()
- );
- }
-
- /**
- * @param ResourceLoaderContext|null $context
- * @return array
- */
- public function getDependencies( ResourceLoaderContext $context = null ) {
- return [ 'mediawiki.language' ];
- }
-
- /**
- * @return bool
- */
- public function enableModuleContentVersion() {
- return true;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * ResourceLoader mediawiki.util module
- *
- * 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * ResourceLoader module for mediawiki.util
- *
- * @since 1.30
- */
-class ResourceLoaderMediaWikiUtilModule extends ResourceLoaderFileModule {
- /**
- * @inheritDoc
- */
- public function getScript( ResourceLoaderContext $context ) {
- return ResourceLoader::makeConfigSetScript(
- [ 'wgFragmentMode' => $this->getConfig()->get( 'FragmentMode' ) ]
- )
- . "\n"
- . parent::getScript( $context );
- }
-
- /**
- * @inheritDoc
- */
- public function supportsURLLoading() {
- return false;
- }
-
- /**
- * @inheritDoc
- */
- public function enableModuleContentVersion() {
- return true;
- }
-}
+++ /dev/null
-<?php
-/**
- * ResourceLoader module for populating special characters data for some
- * editing extensions to use.
- *
- * 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * ResourceLoader module for populating special characters data for some
- * editing extensions to use.
- */
-class ResourceLoaderSpecialCharacterDataModule extends ResourceLoaderModule {
- private $path = "resources/src/mediawiki.language/specialcharacters.json";
- protected $targets = [ 'desktop', 'mobile' ];
-
- /**
- * Get all the dynamic data.
- *
- * @return array
- */
- protected function getData() {
- global $IP;
- return json_decode( file_get_contents( "$IP/{$this->path}" ) );
- }
-
- /**
- * @param ResourceLoaderContext $context
- * @return string JavaScript code
- */
- public function getScript( ResourceLoaderContext $context ) {
- return Xml::encodeJsCall(
- 'mw.language.setSpecialCharacters',
- [
- $this->getData()
- ],
- ResourceLoader::inDebugMode()
- );
- }
-
- /**
- * @return bool
- */
- public function enableModuleContentVersion() {
- return true;
- }
-
- /**
- * @param ResourceLoaderContext|null $context
- * @return array
- */
- public function getDependencies( ResourceLoaderContext $context = null ) {
- return [ 'mediawiki.language' ];
- }
-
- /**
- * @return array
- */
- public function getMessages() {
- return [
- 'special-characters-group-latin',
- 'special-characters-group-latinextended',
- 'special-characters-group-ipa',
- 'special-characters-group-symbols',
- 'special-characters-group-greek',
- 'special-characters-group-greekextended',
- 'special-characters-group-cyrillic',
- 'special-characters-group-arabic',
- 'special-characters-group-arabicextended',
- 'special-characters-group-persian',
- 'special-characters-group-hebrew',
- 'special-characters-group-bangla',
- 'special-characters-group-tamil',
- 'special-characters-group-telugu',
- 'special-characters-group-sinhala',
- 'special-characters-group-devanagari',
- 'special-characters-group-gujarati',
- 'special-characters-group-thai',
- 'special-characters-group-lao',
- 'special-characters-group-khmer',
- 'special-characters-group-canadianaboriginal',
- 'special-characters-title-endash',
- 'special-characters-title-emdash',
- 'special-characters-title-minus'
- ];
- }
-}
+++ /dev/null
-<?php
-/**
- * ResourceLoader module for the upload dialog configuration data.
- *
- * 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * ResourceLoader module for the upload dialog configuration data.
- *
- * @since 1.27
- */
-class ResourceLoaderUploadDialogModule extends ResourceLoaderModule {
-
- protected $targets = [ 'desktop', 'mobile' ];
-
- /**
- * @param ResourceLoaderContext $context
- * @return string JavaScript code
- */
- public function getScript( ResourceLoaderContext $context ) {
- $config = $context->getResourceLoader()->getConfig();
- return ResourceLoader::makeConfigSetScript( [
- 'wgUploadDialog' => $config->get( 'UploadDialog' ),
- ] );
- }
-
- /**
- * @return bool
- */
- public function enableModuleContentVersion() {
- return true;
- }
-}
die( 'Not an entry point.' );
}
+global $wgResourceBasePath;
+
return [
/**
'upload-foreign-cant-upload',
]
],
- 'mediawiki.ForeignStructuredUpload.config' => [
- 'class' => ResourceLoaderUploadDialogModule::class,
- ],
'mediawiki.ForeignStructuredUpload' => [
- 'scripts' => 'resources/src/mediawiki.ForeignStructuredUpload.js',
+ 'localBasePath' => "$IP/resources/src",
+ 'remoteBasePath' => "$wgResourceBasePath/resources/src",
+ 'packageFiles' => [
+ 'mediawiki.ForeignStructuredUpload.js',
+ 'config.json' => [ 'config' => [ 'UploadDialog' ] ],
+ ],
'dependencies' => [
'mediawiki.ForeignUpload',
- 'mediawiki.ForeignStructuredUpload.config',
],
'messages' => [
'upload-foreign-cant-load-config',
]
],
'mediawiki.util' => [
- 'class' => ResourceLoaderMediaWikiUtilModule::class,
- 'scripts' => 'resources/src/mediawiki.util.js',
+ 'localBasePath' => "$IP/resources/src",
+ 'remoteBasePath' => "$wgResourceBasePath/resources/src",
+ 'packageFiles' => [
+ 'mediawiki.util.js',
+ 'config.json' => [ 'config' => [ 'FragmentMode' ] ],
+ ],
'dependencies' => [
'jquery.accessKeyLabel',
'mediawiki.RegExp',
],
'mediawiki.jqueryMsg' => [
- // Add data for mediawiki.jqueryMsg, such as allowed tags
- 'class' => ResourceLoaderJqueryMsgModule::class,
- 'scripts' => 'resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js',
+ 'localBasePath' => "$IP/resources/src/mediawiki.jqueryMsg",
+ 'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.jqueryMsg",
+ 'packageFiles' => [
+ 'mediawiki.jqueryMsg.js',
+ 'parserDefaults.json' => [ 'callback' => function ( ResourceLoaderContext $context ) {
+ $tagData = Sanitizer::getRecognizedTagData();
+ $allowedHtmlElements = array_merge(
+ array_keys( $tagData['htmlpairs'] ),
+ array_diff(
+ array_keys( $tagData['htmlsingle'] ),
+ array_keys( $tagData['htmlsingleonly'] )
+ )
+ );
+
+ $magicWords = [
+ 'SITENAME' => $context->getConfig()->get( 'Sitename' ),
+ ];
+ Hooks::run( 'ResourceLoaderJqueryMsgModuleMagicWords', [ $context, &$magicWords ] );
+
+ return [
+ 'allowedHtmlElements' => $allowedHtmlElements,
+ 'magic' => $magicWords,
+ ];
+ } ],
+ ],
'dependencies' => [
'mediawiki.util',
'mediawiki.language',
)
],
- 'mediawiki.language.names' => [ 'class' => ResourceLoaderLanguageNamesModule::class ],
+ 'mediawiki.language.names' => [
+ 'localBasePath' => "$IP/resources/src/mediawiki.language",
+ 'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.language",
+ 'packageFiles' => [
+ 'mediawiki.language.names.js',
+ 'names.json' => [ 'callback' => function ( ResourceLoaderContext $context ) {
+ return Language::fetchLanguageNames( $context->getLanguage(), 'all' );
+ } ],
+ ],
+ 'dependencies' => 'mediawiki.language',
+ 'targets' => [ 'desktop', 'mobile' ],
+ ],
'mediawiki.language.specialCharacters' => [
- 'class' => ResourceLoaderSpecialCharacterDataModule::class
+ 'localBasePath' => "$IP/resources/src/mediawiki.language",
+ 'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.language",
+ 'packageFiles' => [
+ 'mediawiki.language.specialCharacters.js',
+ 'specialcharacters.json'
+ ],
+ 'dependencies' => 'mediawiki.language',
+ 'targets' => [ 'desktop', 'mobile' ],
+ 'messages' => [
+ 'special-characters-group-latin',
+ 'special-characters-group-latinextended',
+ 'special-characters-group-ipa',
+ 'special-characters-group-symbols',
+ 'special-characters-group-greek',
+ 'special-characters-group-greekextended',
+ 'special-characters-group-cyrillic',
+ 'special-characters-group-arabic',
+ 'special-characters-group-arabicextended',
+ 'special-characters-group-persian',
+ 'special-characters-group-hebrew',
+ 'special-characters-group-bangla',
+ 'special-characters-group-tamil',
+ 'special-characters-group-telugu',
+ 'special-characters-group-sinhala',
+ 'special-characters-group-devanagari',
+ 'special-characters-group-gujarati',
+ 'special-characters-group-thai',
+ 'special-characters-group-lao',
+ 'special-characters-group-khmer',
+ 'special-characters-group-canadianaboriginal',
+ 'special-characters-title-endash',
+ 'special-characters-title-emdash',
+ 'special-characters-title-minus'
+ ]
],
/* MediaWiki Libs */
// Config for uploads to local wiki.
// Can be overridden with foreign wiki config when #loadConfig is called.
- this.config = mw.config.get( 'wgUploadDialog' );
+ this.config = require( './config.json' ).UploadDialog;
mw.ForeignUpload.call( this, target, apiconfig );
}
var oldParser,
slice = Array.prototype.slice,
parserDefaults = {
+ // Magic words and their expansions. Server-side data is added to this below.
magic: {
PAGENAME: mw.config.get( 'wgPageName' ),
PAGENAMEE: mw.util.wikiUrlencode( mw.config.get( 'wgPageName' ) )
},
// Whitelist for allowed HTML elements in wikitext.
// Self-closing tags are not currently supported.
- // Can be populated via setParserDefaults().
+ // Filled in with server-side data below
allowedHtmlElements: [],
// Key tag name, value allowed attributes for that tag.
// See Sanitizer::setupAttributeWhitelist
format: 'parse'
};
+ // Add in server-side data (allowedHtmlElements and magic words)
+ $.extend( true, parserDefaults, require( './parserDefaults.json' ) );
+
/**
* Wrapper around jQuery append that converts all non-objects to TextNode so append will not
* convert what it detects as an htmlString to an element.
--- /dev/null
+( function () {
+ var names = require( './names.json' );
+ mw.language.setData( mw.config.get( 'wgUserLanguage' ), 'languageData', names );
+}() );
--- /dev/null
+( function () {
+ var specialCharacters = require( './specialcharacters.json' );
+ mw.language.setSpecialCharacters( specialCharacters );
+ module.exports = specialCharacters;
+}() );
( function () {
'use strict';
- var util;
+ var util,
+ config = require( './config.json' ),
+ origConfig = config;
/**
* Encode the string like PHP's rawurlencode
/* Main body */
+ setOptionsForTest: function ( opts ) {
+ if ( !window.QUnit ) {
+ throw new Error( 'Modifying options not allowed outside unit tests' );
+ }
+ config = $.extend( {}, config, opts );
+ },
+
+ resetOptionsForTest: function () {
+ if ( !window.QUnit ) {
+ throw new Error( 'Resetting options not allowed outside unit tests' );
+ }
+ config = origConfig;
+ },
+
/**
* Encode the string like PHP's rawurlencode
*
* @return {string} Encoded string
*/
escapeIdForAttribute: function ( str ) {
- var mode = mw.config.get( 'wgFragmentMode' )[ 0 ];
+ var mode = config.FragmentMode[ 0 ];
return escapeIdInternal( str, mode );
},
* @return {string} Encoded string
*/
escapeIdForLink: function ( str ) {
- var mode = mw.config.get( 'wgFragmentMode' )[ 0 ];
+ var mode = config.FragmentMode[ 0 ];
return escapeIdInternal( str, mode );
},
'SkinModule (FileModule subclass)' => [ true,
[ 'class' => ResourceLoaderSkinModule::class, 'scripts' => 'example.js' ]
],
- 'JqueryMsgModule (FileModule subclass)' => [ true, [
- 'class' => ResourceLoaderJqueryMsgModule::class,
- 'scripts' => 'example.js',
- ] ],
'WikiModule' => [ false, [
'class' => ResourceLoaderWikiModule::class,
'scripts' => [ 'MediaWiki:Example.js' ],
},
teardown: function () {
$.fn.updateTooltipAccessKeys.setTestMode( false );
+ mw.util.resetOptionsForTest();
},
messages: {
// Used by accessKeyLabel in test for addPortletLink
// Distant future: no legacy fallbacks
[ allNew, text, html5Encoded ]
].forEach( function ( testCase ) {
- mw.config.set( 'wgFragmentMode', testCase[ 0 ] );
+ mw.util.setOptionsForTest( { FragmentMode: testCase[ 0 ] } );
assert.strictEqual( util.escapeIdForAttribute( testCase[ 1 ] ), testCase[ 2 ] );
} );
// Distant future: no legacy fallbacks
[ allNew, text, html5Encoded ]
].forEach( function ( testCase ) {
- mw.config.set( 'wgFragmentMode', testCase[ 0 ] );
+ mw.util.setOptionsForTest( { FragmentMode: testCase[ 0 ] } );
assert.strictEqual( util.escapeIdForLink( testCase[ 1 ] ), testCase[ 2 ] );
} );
href = util.getUrl( '#Fragment', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?action=edit#Fragment', 'empty title with query string and fragment' );
- mw.config.set( 'wgFragmentMode', [ 'legacy' ] );
+ mw.util.setOptionsForTest( { FragmentMode: [ 'legacy' ] } );
href = util.getUrl( 'Foo:Sandbox \xC4#Fragment \xC4', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?title=Foo:Sandbox_%C3%84&action=edit#Fragment_.C3.84', 'title with query string, fragment, and special characters' );
- mw.config.set( 'wgFragmentMode', [ 'html5' ] );
+ mw.util.setOptionsForTest( { FragmentMode: [ 'html5' ] } );
href = util.getUrl( 'Foo:Sandbox \xC4#Fragment \xC4', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?title=Foo:Sandbox_%C3%84&action=edit#Fragment_Ä', 'title with query string, fragment, and special characters' );
href = util.getUrl( 'Foo:%23#Fragment', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?title=Foo:%2523&action=edit#Fragment', 'title containing %23 (#), fragment, and a query string' );
- mw.config.set( 'wgFragmentMode', [ 'legacy' ] );
+ mw.util.setOptionsForTest( { FragmentMode: [ 'legacy' ] } );
href = util.getUrl( '#+&=:;@$-_.!*/[]<>\'§', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?action=edit#.2B.26.3D:.3B.40.24-_..21.2A.2F.5B.5D.3C.3E.27.C2.A7', 'fragment with various characters' );
- mw.config.set( 'wgFragmentMode', [ 'html5' ] );
+ mw.util.setOptionsForTest( { FragmentMode: [ 'html5' ] } );
href = util.getUrl( '#+&=:;@$-_.!*/[]<>\'§', { action: 'edit' } );
assert.strictEqual( href, '/w/index.php?action=edit#+&=:;@$-_.!*/[]<>\'§', 'fragment with various characters' );
} );