summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/UploadWizard/resources/mw.Escaper.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/UploadWizard/resources/mw.Escaper.js')
-rw-r--r--www/wiki/extensions/UploadWizard/resources/mw.Escaper.js151
1 files changed, 151 insertions, 0 deletions
diff --git a/www/wiki/extensions/UploadWizard/resources/mw.Escaper.js b/www/wiki/extensions/UploadWizard/resources/mw.Escaper.js
new file mode 100644
index 00000000..e57d42af
--- /dev/null
+++ b/www/wiki/extensions/UploadWizard/resources/mw.Escaper.js
@@ -0,0 +1,151 @@
+( function ( mw, OO ) {
+ mw.Escaper = {
+ /**
+ * Escapes wikitext for use inside {{templates}}.
+ *
+ * @param {string} wikitext
+ * @return {string}
+ */
+ escapeForTemplate: function ( wikitext ) {
+ return this.escapePipes( wikitext );
+ },
+
+ /**
+ * Escapes pipe characters, which could be problematic when the content is
+ * inserted in a template.
+ *
+ * @param {string} wikitext
+ * @return {string}
+ */
+ escapePipes: function ( wikitext ) {
+ var extractedTemplates, extractedLinks;
+
+ // Pipes (`|`) must be escaped because we'll be inserting this
+ // content into a templates & pipes would mess up the syntax.
+ // First, urlencode pipes inside links:
+ wikitext = wikitext.replace( /\bhttps?:\/\/[^\s]+/g, function ( match ) {
+ return match.replace( /\|/g, '%7C' );
+ } );
+
+ // Second, pipes can be valid inside other templates or links in
+ // wikitext, so we'll first extract those from the content, then
+ // replace the pipes, then restore the original (extracted) content:
+ extractedTemplates = this.extractTemplates( wikitext );
+ extractedLinks = this.extractLinks( extractedTemplates[ 0 ] );
+ wikitext = extractedLinks[ 0 ].replace( /\|/g, '{{!}}' );
+ return this.restoreExtracts( wikitext, $.extend( extractedTemplates[ 1 ], extractedLinks[ 1 ] ) );
+ },
+
+ /**
+ * Extract all {{templates}} from wikitext, replacing them with
+ * placeholder content in the form of {{1}}, {{2}}.
+ *
+ * Nested templates will safely be extracted by first replacing inner
+ * templates, then moving outwards, ensuring we don't get closing
+ * bracket mismatches.
+ *
+ * Restoring the content is as simple as feeding the returned content &
+ * replacements back into this.restoreExtracts.
+ *
+ * @param {string} wikitext
+ * @return {array} [{string} wikitext, {Object} replacements]
+ */
+ extractTemplates: function ( wikitext ) {
+ var extracts = {},
+ previousExtracts = {},
+ extracted = wikitext,
+ // the regex explained:
+ // * `[^\{]`: character can not be {
+ // * `\{(?!\{)`: or if it is, it can't be followed by another {
+ // this excludes template opening brackets: {{
+ // * `\{\{[0-9]+\}\}`: unless it's a complete {{[0-9]+}}
+ // sequence, generated by an earlier run of this regex
+ regex = /\{\{([^{]|\{(?!\{)|\{\{[0-9]+\}\})*?\}\}/g,
+ callback = function ( match ) {
+ var replacement = '{{' + Object.keys( extracts ).length + '}}';
+
+ // safeguard for not replacing already-replaced matches
+ // this makes sure that when real content contains something
+ // like {{1}}, it will still be replaced, while {{1}}
+ // generated by this code can be recognized & ignored
+ if ( match in previousExtracts ) {
+ return match;
+ }
+
+ extracts[ replacement ] = match;
+ return replacement;
+ };
+
+ do {
+ wikitext = extracted;
+ previousExtracts = OO.copy( extracts );
+ extracted = wikitext.replace( regex, callback );
+ } while ( wikitext !== extracted );
+
+ return [ wikitext, extracts ];
+ },
+
+ /**
+ * Extract all [[links]] from wikitext, replacing them with placeholder
+ * content in the form of [[1]], [[2]].
+ *
+ * Restoring the content is as simple as feeding the returned content &
+ * replacements back into this.restoreExtracts.
+ *
+ * @param {string} wikitext
+ * @return {array} [{string} wikitext, {Object} replacements]
+ */
+ extractLinks: function ( wikitext ) {
+ var extracts = {};
+
+ wikitext = wikitext.replace( /\[\[.*?\]\]/g, function ( match ) {
+ var replacement = '[[' + Object.keys( extracts ).length + ']]';
+ extracts[ replacement ] = match;
+ return replacement;
+ } );
+
+ return [ wikitext, extracts ];
+ },
+
+ /**
+ * Restores content that was extracted from wikitext.
+ *
+ * @param {string} wikitext
+ * @param {Object} replacements
+ * @return {string}
+ */
+ restoreExtracts: function ( wikitext, replacements ) {
+ // turn search keys into a regular expression, allowing us to match
+ // all of them at once
+ var searchValues = Object.keys( replacements ).map( mw.RegExp.escape ),
+ searchRegex = new RegExp( '(' + searchValues.join( '|' ) + ')', 'g' ),
+ callback = function ( match ) {
+ var replacement = replacements[ match ];
+
+ // we matched something that has no replacement, must be valid
+ // user input that just happens to look like on of the
+ // replacement values
+ if ( replacement === undefined ) {
+ return match;
+ }
+
+ // if we find the replacement itself matches a search value, we
+ // also don't want to go recursive: nesting doesn't work like
+ // that, it's just a coincidence where user input happens to
+ // look just like a replacement value (e.g. `{{1}}`)
+ if ( replacement in replacements ) {
+ return replacement;
+ }
+
+ // we must not replace this one again, to avoid getting stuck in
+ // endless recursion
+ delete replacements[ match ];
+
+ // go recursive, there may be more replacements nested down there
+ return this.restoreExtracts( replacement, replacements );
+ }.bind( this );
+
+ return wikitext.replace( searchRegex, callback );
+ }
+ };
+}( mediaWiki, OO ) );