Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.

Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;

Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.
(function () {
		var cleanSpaces = function (v) {
			return v.replace(/(?:[^\S\r\n]|_| )+/g, ' ').replace(/’/g, '\'').trim();
		};
		var getNum1 = function (num) {
			if (num) {
				num = num.slice(0, 1).toUpperCase() + num.slice(1);
				if (num === 'Ière' || num === 'Iere') {
					return 'Ire';
				}
			}
			return num;
		};
		var getNum2 = function (i, er) {
			i = i.toUpperCase();
			er = er.toLowerCase();
			if (er === 'ere' || er === 'ère') {
				er = 're';
			}
			if (i !== 'I') {
				console.error('Expected "I", got "', i ,'"');
			}
			if (er !== 'er' && er !== 're') {
				console.error('Expected "er" or "re", got "', er ,'"');
			}
			return i + er;
		};
  // Above CXL/140, it starts getting sparse.
  var romLimit = 140;
  var wrongRomModelsBelowCXL = ['L', 'LV', 'C', 'CIII' /*c*/, 'CIV', 'CV', 'CX', 'CXXXIX' /*c*/];
  var existingRomModelsAboveCXL = ['CXL', 'CXLII', 'CXLIX', 'CLVI', 'CLVIII', 'CLX', 'CLXI', 'CLXII', 'CLXV', 'CLXXII', 'CLXXIV', 'CLXXV', 'CLXXIX', 'CLXXXII', 'CLXXXV', 'CXCI', 'CXCVI', 'CC', 'CCXII', 'CCXLVIII', 'CCLV', 'CCLXI', 'CCLXIII', 'CCXCIV', 'CCXCVI', 'CCC', 'CD', 'CDXXVIII', 'D', 'DC', 'MIX', 'MCIII', 'MD', 'MDCCXX', 'MDCCCXLIII', 'MCMLXXXVIII', 'MMVII'];
  var getRomValue = function (rom) {
    rom = rom.toUpperCase();
    var order = ['M', 'D', 'C', 'L', 'X', 'V', 'I'];
    var minIndex = 0;
    var value = 0;
    for (var i = 0, l = rom.length; i < l; i++) {
      var char = rom[i];
      if (char === 'M' && minIndex <= 0) {
        value += 1000;
      } else if (char === 'D' && minIndex <= 1) {
        value += 500;
        minIndex = 2;
      } else if (char === 'C' && minIndex <= 2) {
        if (rom[i+1] === 'M') {
          value += 900;
          i++;
          minIndex = 3;
        } else if (rom[i+1] === 'D') {
          value += 400;
          i++;
          minIndex = 3;
        } else {
          value += 100;
          minIndex = 2;
        }
      } else if (char === 'L' && minIndex <= 3) {
        value += 50;
        minIndex = 4;
      } else if (char === 'X' && minIndex <= 4) {
        if (rom[i+1] === 'C') {
          value += 90;
          i++;
          minIndex = 5;
        } else if (rom[i+1] === 'L') {
          value += 40;
          i++;
          minIndex = 5;
        } else {
          value += 10;
          minIndex = 4;
        }
      } else if (char === 'V' && minIndex <= 5) {
        value += 5;
        minIndex = 6;
      } else if (char === 'I' && minIndex <= 6) {
        if (rom[i+1] === 'X') {
          value += 9;
          i++;
          minIndex = 7;
        } else if (rom[i+1] === 'V') {
          value += 4;
          i++;
          minIndex = 7;
        } else {
          value += 1;
          minIndex = 6;
        }
      } else {
        throw new Error('Unable to parse rom num ' + rom);
      }
    }
    return value;
  };
  var getRomMinModel = function (rom) {
    return '{{rom-min|' + rom + '|' + getRomValue(rom) + '}}'
  };
  var getRomMajModel = function (rom) {
    return '{{rom-maj|' + rom + '|' + getRomValue(rom) + '}}'
  };
  var getRomModel = function (rom) {
    if (rom === 'Ier' || rom === 'Ire') {
      return '{{' + rom + '}}';
    }
    if (getRomValue(rom) < romLimit) {
      if (wrongRomModelsBelowCXL.indexOf(rom) === -1) {
        return '{{' + rom + '}}';
      }
    }
    if (existingRomModelsAboveCXL.indexOf(rom) > -1) {
      return '{{' + rom + '}}';
    }
    
    return getRomMajModel(rom);
  };
		var knownNamesRaw = /Albert (?:Ier|II)|Alexandre VII|Alphonse Ier|Arnoul (?:Ier|II|III)|Baldéric II|Baudouin (?:II|III|VII)|Charles (?:Ier|II|III|IX|V)|Clément IX|Clovis Ier|Conrad (?:Ier|II)|Constantin Ier|Édouard Ier|Ferdinand Ier|Frédéric Ier|Gérard Ier|Guillaume (?:Ier|II|III|IV)|Henri (?:Ier|II|III|IV)|Héribert Ier|Innocent X|Jacques Ier|Léopold (?:Ier|II|III)|Louis (?:Ier|II|XI|XII|XIII|XIV|XV|XVI)|Otton (?:Ier|II|III)|Philippe (?:Ier|V)|Raoul Ier|Régnier (?:III|V)|Robert (?:Ier|II|III)|Roger II|Théodose Ier|Thierry III/.source;
		var knownNames = RegExp('^(?:' + knownNamesRaw + ')$');
		var replaceOptions = [{
      // [[Foo II Bar]]
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)([^\|\]]+?)(?:[^\S\r\n]|_|&nbsp;)+(I(?:er|re)|[IVXLCDM]+)(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\|\]]*?))?\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var before = cleanSpaces(match[1] || '');
				var num = match[2];
				var comma = match[3] || '';
				var after = cleanSpaces(match[4] || '');
				console.log(before, num, comma, after);
				return [match[0], before, num, comma, after];
			},
			replacements: function (_, before, num, comma, after) {
				var res = [];
				var beforeLastWord = before.substr(before.lastIndexOf(' ') + 1);
				var beforeWords = before.substr(0, before.lastIndexOf(' '));
				if (after) {
				  res.push('{{souverain3|' + before + ' ' + num + comma + ' ' + after + '}}');
				}
				if (!comma && !after) {
					res.push('{{souverain2|' + before + ' ' + num + '}}');
				}
				if (knownNames.test(before + ' ' + num)) {
					res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|{{' + before + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
				} else {
					res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|{{souverain-|' + before + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
				}
				if (beforeWords) {
					if (knownNames.test(beforeLastWord + ' ' + num)) {
						res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{' + beforeLastWord + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
					} else {
						res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{souverain-|' + beforeLastWord + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
					}
				}
				if (!beforeWords && !comma && !after) {
					res.push('{{lnobr rom|' +  before + ' ' + num + '}}');
				}
				res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{nobr rom|' + beforeLastWord + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
				try {
					res.push('[[' + before + ' ' + num + comma + (after ? ' ' + after : '') + '|' + before + ' ' + getRomModel(num) + comma + (after ? ' ' + after : '') + ']]');
				} catch(e) {}
				return res;
			}
		}, {
      // [[Foo II Bar|Foo II Baz]]
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)([^\|\]]+?)(?:[^\S\r\n]|_|&nbsp;)+(I(?:er|re)|[IVXLCDM]+)(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\|\]]*?))?\|([^\|\]]+?)(?:[^\S\r\n]|_|&nbsp;)+(?:<abbr>)?(?:(I(?:ere?|ère|re)|[IVXLCDM]+)|(I)(?:<sup>(ere?|ère|re)<\/sup>|\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?([eE]re?|[èÈ]re|[rR]e)[^\S\r\n]*\}\})|\{\{[^\S\r\n]*([Ii](?:ere?|ère|re)|[IVXLCDM]+)[^\S\r\n]*\}\})(?:<\/abbr>)?(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\|\]]*?))?\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var linkBefore = cleanSpaces(match[1] || '');
				var linkNum = match[2];
				var linkComma = match[3] || '';
				var linkAfter = cleanSpaces(match[4] || '');
				var textBefore = cleanSpaces(match[5] || '');
				var textNum = getNum1(match[6] || match[10]);
				if (match[7]) {
					textNum = getNum2(match[7], match[8] || match[9]);
				}
				var textComma = match[11] || '';
				var textAfter = cleanSpaces(match[12] || '');
				console.log(linkBefore, linkNum, linkComma, linkAfter, textBefore, textNum, textComma, textAfter);
				return [match[0], linkBefore, linkNum, linkComma, linkAfter, textBefore, textNum, textComma, textAfter];
    		},
    		replacements: function (_, linkBefore, linkNum, linkComma, linkAfter, textBefore, textNum, textComma, textAfter) {
				var res = [];
				var textAfterNoQuotes = textAfter.replace(/^dite? /, '');
				var italicRe = /^''(.*?)''$/;
				var quotesRe = /^« (.*?) »$/;
				if (italicRe.test(textAfterNoQuotes)) {
					textAfterNoQuotes = textAfterNoQuotes.replace(italicRe, '$1');
				} else if (quotesRe.test(textAfterNoQuotes)) {
					textAfterNoQuotes = textAfterNoQuotes.replace(quotesRe, '$1');
				}
				var text = textBefore + ' ' + textNum + textComma + (textAfter ? ' ' + textAfter : '');
				var link = linkBefore + ' ' + linkNum + linkComma + (linkAfter ? ' ' + linkAfter : '');
				if (linkAfter && textAfter && linkBefore === textBefore && linkNum === textNum && (
					(linkComma === textComma && linkAfter === textAfter) || (linkAfter === textAfterNoQuotes && !linkComma)
				)) {
					// souverain3
					res.push('{{souverain3|' + text + '}}');
				}
				if (linkBefore === textBefore && linkNum === textNum && !textComma && !textAfter) {
					// souverain2
					res.push('{{souverain2|' + link + '}}');
				}
				if (linkBefore === textBefore && linkNum === textNum && textAfter && linkAfter !== textAfter) {
					// souverain2 cplt
					res.push('{{souverain2|' + link + '|' + (textComma ? textComma + ' ' : '') + textAfter + '}}');
				}
				if (linkBefore !== textBefore || linkNum !== textNum) {
					// souverain rewritten
					res.push('{{souverain2|' + link + '|' + textBefore + ' ' + textNum + textComma + (textAfter ? ' ' + textAfter : '') + '}}');
				}
        
				var textBeforeLastWord = textBefore.substr(textBefore.lastIndexOf(' ') + 1);
				var textBeforeWords = textBefore.substr(0, textBefore.lastIndexOf(' '));
				if (knownNames.test(textBefore + ' ' + textNum)) {
					res.push('[[' + link + '|{{' + textBefore + ' ' + textNum + '}}' + textComma + (textAfter ? ' ' + textAfter : '') + ']]');
				} else {
					res.push('[[' + link + '|{{souverain-|' + textBefore + ' ' + textNum + '}}' + textComma + (textAfter ? ' ' + textAfter : '') + ']]');
				}
				if (textBeforeWords) {
					if (knownNames.test(textBeforeLastWord + ' ' + textNum)) {
						res.push('[[' + link + '|' + (textBeforeWords ? textBeforeWords + ' ' : '') + '{{' + textBeforeLastWord + ' ' + textNum + '}}' + textComma + (textAfter ? ' ' + textAfter : '') + ']]');
					} else {
						res.push('[[' + link + '|' + (textBeforeWords ? textBeforeWords + ' ' : '') + '{{souverain-|' + textBeforeLastWord + ' ' + textNum + '}}' + textComma + (textAfter ? ' ' + textAfter : '') + ']]');
					}
				}
				if (!textBeforeWords) {
					if (link === text) {
						res.push('{{lnobr rom|' + textBefore + ' ' + textNum + textComma + (textAfter ? ' ' + textAfter : '') + '}}');
					} else {
						res.push('{{lnobr rom|' + link + '|' +  textBefore + ' ' + textNum + textComma + (textAfter ? ' ' + textAfter : '') + '}}');
					}
				}
				res.push('[[' + link + '|' + (textBeforeWords ? textBeforeWords + ' ' : '') + '{{nobr rom|' + textBeforeLastWord + ' ' + textNum + '}}' + (textComma || '') + (textAfter ? ' ' + textAfter : '') + ']]');
				try {
					res.push('[[' + link + '|' + textBefore + ' ' + getRomModel(textNum) + textComma + (textAfter ? ' ' + textAfter : '') + ']]');
				} catch(e) {}

				return res;
			}
		}, {
      // [[texte|Foo II Bar]] -> {{souverain2|texte|Foo II Bar}}
      // [[texte|Foo II Bar]] -> [[texte|{{Foo II}} Bar]]
      // [[texte|Foo II Bar]] -> [[texte|{{souverain-|Foo II}} Bar]]
      // [[texte|Foo II Bar]] -> [[texte|{{nobr rom|Foo II}} Bar]]
      // [[texte|Foo II Bar]] -> [[texte|Foo {{II}} Bar]]
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)([^\|\]]+?)\|([^\|\]]+?)(?:[^\S\r\n]|_|&nbsp;)+(?:<abbr>)?(?:(I(?:ere?|ère|re)|[IVXLCDM]+)|(I)(?:<sup>(ere?|ère|re)<\/sup>|\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?([eE]re?|[èÈ]re|[rR]e)[^\S\r\n]*\}\})|\{\{[^\S\r\n]*([Ii](?:ere?|ère|re)|[IVXLCDM]+)[^\S\r\n]*\}\})(?:<\/abbr>)?(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\|\]]*?))?\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var link = cleanSpaces(match[1] || '');
				var before = cleanSpaces(match[2] || '');
				var num = getNum1(match[3] || match[7]);
				if (match[4]) {
					num = getNum2(match[4], match[5] || match[6]);
				}
				var comma = match[8] || '';
				var after = cleanSpaces(match[9] || '');
				console.log(link, before, num, comma, after);
				return [match[0], link, before, num, comma, after];
			},
			replacements: function (_, link, before, num, comma, after) {
				var res = [];
				var beforeLastWord = before.substr(before.lastIndexOf(' ') + 1);
				var beforeWords = before.substr(0, before.lastIndexOf(' '));
				res.push('{{souverain2|' + link + '|' + before + ' ' + num + comma + (after ? ' ' + after : '') + '}}');
				if (knownNames.test(before + ' ' + num)) {
					res.push('[[' + link + '|{{' + before + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
				} else {
					res.push('[[' + link + '|{{souverain-|' + before + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
				}
				if (beforeWords) {
					if (knownNames.test(beforeLastWord + ' ' + num)) {
						res.push('[[' + link + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{' + beforeLastWord + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
					} else {
						res.push('[[' + link + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{souverain-|' + beforeLastWord + ' ' + num + '}}' + comma + (after ? ' ' + after : '') + ']]');
					}
				}
				if (!beforeWords) {
					res.push('{{lnobr rom|' + link + '|' +  before + ' ' + num + comma + (after ? ' ' + after : '') + '}}');
				}
				res.push('[[' + link + '|' + (beforeWords ? beforeWords + ' ' : '') + '{{nobr rom|' + beforeLastWord + ' ' + num + '}}' + (comma || '') + (after ? ' ' + after : '') + ']]');
				try {
					res.push('[[' + link + '|' + before + ' ' + getRomModel(num) + comma + (after ? ' ' + after : '') + ']]');
				} catch(e) {}
				return res;
			}
		}, {
			// souverain2
      // FIXME Should not be needed
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)[^\S\r\n]*([^\[\]\|\n\r]+?)(?:[^\S\r\n]|_|&nbsp;)+((I)(er|re)|[IVXLCDM]+)(?:(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\[\]\|\n\r]+?))?[^\S\r\n]*\|[^\S\r\n]*(?:\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)|\{\{[^\S\r\n]*(?:nobr(?: rom(?:ains)?)?|souverain-)[^\S\r\n]*\|[^\S\r\n]*\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)[^\S\r\n]*\}\}))?[^\S\r\n]*\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var name = match[1];
				var num = match[2];
				var comma = match[5] || '';
				var suffix = cleanSpaces(match[6] || '');
				return [name, num, comma, suffix];
			},
			replacements: function (name, num, comma, suffix) {
				return [
					'{{souverain2|' + name + ' ' + num + comma + (suffix ? ' ' + suffix : '') + '}}'
				];
			}
		}, {
			// souverain3
      // FIXME Should not be needed
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)[^\S\r\n]*([^\[\]\|\n\r]+?)(?:[^\S\r\n]|_|&nbsp;)+((I)(er|re)|[IVXLCDM]+)(?:[^\S\r\n]|_|&nbsp;)+([^\[\]\|\n\r]+?)(?:[^\S\r\n]*\|[^\S\r\n]*(?:\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)(,?(?:[^\S\r\n]+|&nbsp;)dite?)?(?:[^\S\r\n]+|&nbsp;)('')?\5\7|\{\{[^\S\r\n]*(?:nobr(?: rom(?:ains)?)?|souverain-)[^\S\r\n]*\|[^\S\r\n]*\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)[^\S\r\n]*\}\}(,?(?:[^\S\r\n]+|&nbsp;)dite?)?(?:[^\S\r\n]+|&nbsp;)('')?\5\9))?[^\S\r\n]*\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var name = match[1];
				var num = match[2];
				var suffix = cleanSpaces(match[5]);
				var dit = match[6] || match[8] || '';
				var quote = match[7] || match[9] || '';
				return [name, num, dit, quote, suffix];
			},
			replacements: function (name, num, dit, quote, suffix) {
				return [
					'{{souverain3|' + name + ' ' + num + dit + ' ' + quote + suffix + quote + '}}'
				];
			}
		}, {
			// souverain2 cplt
      // FIXME Should not be needed
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)[^\S\r\n]*([^\[\]\|\n\r]+?)(?:[^\S\r\n]|_|&nbsp;)+((I)(er|re)|[IVXLCDM]+)(?:(,)?(?:[^\S\r\n]|_|&nbsp;)+([^\[\]\|\n\r]+?))?[^\S\r\n]*\|[^\S\r\n]*(?:(?:\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)|\{\{[^\S\r\n]*(?:nobr(?: rom(?:ains)?)?|souverain-)[^\S\r\n]*\|[^\S\r\n]*\1(?:[^\S\r\n]+|&nbsp;)(?:\2|\{\{[^\S\r\n]*\2[^\S\r\n]*\}\}|\3\{\{[^\S\r\n]*\4[^\S\r\n]*\}\}|\3<sup>\4<\/sup>)[^\S\r\n]*\}\})(,)?(?:[^\S\r\n]+|&nbsp;)([^\[\]\|\n\r]+?))[^\S\r\n]*\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var name = match[1];
				var num = match[2];
				var comma = match[5] || '';
				var suffix = cleanSpaces(match[6] || '');
        		var newComma = match[7] || '';
				var newSuffix = cleanSpaces(match[8]);
				return [name, num, comma, suffix, newComma, newSuffix];
			},
			replacements: function (name, num, comma, suffix, newComma, newSuffix) {
				var res = [];
				if (suffix && comma + ' ' + suffix === newComma + ' ' + newSuffix) {
					res.push('{{souverain3|' + name + ' ' + num + comma + ' ' + suffix + '}}');
				} else {
					res.push('{{souverain2|' + name + ' ' + num + comma + (suffix ? ' ' + suffix : '') + '|' + (newComma ? newComma + ' ' : '') + newSuffix + '}}');
				}
				return res;
			}
		}, {
			// souverain2 num
      // FIXME Should not be needed
			regex: /\[\[(?!(?:[fF]ichier|[fF]ile|[iI]mage|[cC]atégorie):)[^\S\r\n]*([^\[\]\|\n\r]+?)(?:[^\S\r\n]|_|&nbsp;)+((?:I(?:er|re)|[IVXLCDM]+)(?:(?:[^\S\r\n]|_|&nbsp;)+[^\[\]\|\n\r]+?)?)[^\S\r\n]*\|[^\S\r\n]*(?:\1(?:[^\S\r\n]+|&nbsp;)(?:(Ier|Ire|[IVXLCDM]+)|\{\{[^\S\r\n]*(Ier|Ire|[IVXLCDM]+)[^\S\r\n]*\}\}|(I)\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?(er|re)[^\S\r\n]*\}\})|\{\{[^\S\r\n]*(?:nobr(?: rom(?:ains)?)?|souverain-)[^\S\r\n]*\|[^\S\r\n]*\1(?:[^\S\r\n]+|&nbsp;)(?:(Ier|Ire|[IVXLCDM]+)|\{\{[^\S\r\n]*(Ier|Ire|[IVXLCDM]+)[^\S\r\n]*\}\})[^\S\r\n]*\}\})[^\S\r\n]*\]\]/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var name = match[1];
				var numSuffix = cleanSpaces(match[2]);
				var newNum = match[5] ? getNum2(match[5], match[6]) : getNum1(match[3] || match[4] || match[7] || match[8]);
				return [name, numSuffix, newNum];
			},
			replacements: function (name, numSuffix, newNum) {
				return [
					'{{souverain2|' + name + ' ' + numSuffix + '|' + name + ' ' + newNum + '}}'
				];
			}
		}, {
      // {{nobr|Foo {{II}}}} -> {{souverain-|Foo II}}
      // {{nobr rom|Foo {{II}}}} -> {{souverain-|Foo II}}
      // {{nobr|Foo {{II}}}} -> {{nobr rom|Foo II}}
			regex: /\{\{[^\S\r\n]*nobr(?: rom(?:ains)?)?[^\S\r\n]*\|[^\S\r\n]*(?![lL]ivre|[cC]hapitre|[tT]ome|[vV]olume)([^\[\]\|\n\r]+?)(?:[^\S\r\n]+|&nbsp;)(?:(Ier|Ire|[IVXLCDM]+)|\{\{[^\S\r\n]*(Ier|Ire|[IVXLCDM]+)[^\S\r\n]*\}\})[^\S\r\n]*\}\}/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var name = match[1];
				var num = getNum1(match[2] || match[3]);
				return [name, num];
			},
			replacements: function (name, num) {
				var res = [];
				if (knownNames.test(name + ' ' + num)) {
					res.push('{{' + name + ' ' + num + '}}');
				}
				res.push('{{souverain-|' + name + ' ' + num + '}}');
				res.push('{{nobr rom|' + name + ' ' + num + '}}');
				return res;
			}
		}, /*{
			// {{souverain2|Foo II|Foo II}} -> {{souverain2|Foo II}}
			// {{souverain2|Foo II Bar|Foo II Baz}} -> {{souverain2|Foo II Bar|Baz}}
			// {{souverain2|Foo II Bar|Foo II Bar}} -> {{souverain3|Foo II Bar}}
			// {{souverain2|Foo II Bar|Bar}} -> {{souverain3|Foo II Bar}}
			regex: /\{\{[^\S\r\n]*souverain2[^\S\r\n]*\|[^\S\r\n]*([^\[\]\|\n\r]+?)(?:[^\S\r\n]+|&nbsp;)(Ier|Ire|[IVXLCDM]+)(,?[^\S\r\n]+[^\[\]\|\n\r]+)?[^\S\r\n]*\|[^\S\r\n]*([^\[\]\|\n\r]+?)(?:(?:[^\S\r\n]+|&nbsp;)(Ier|Ire|[IVXLCDM]+)(,?[^\S\r\n]+[^\[\]\|\n\r]+)?)?[^\S\r\n]*\}\}/g,
    		cats: ['dyn', 'rom'],
    		replacements: function (_, before1, num1, after1, before2, num2, after2) {
				var res = [];
				if (before1 === before2 && num1 === num2) {
					if (after1 && after1 === after2) {
						res.push('{{souverain3|' + before1 + ' ' + num1 + after1 + '}}');
					} else if (after2) {
						res.push('{{souverain2|' + before1 + ' ' + num1 + (after1 || '') + '|' + cleanSpaces(after2) + '}}');
					} else {
						res.push('{{souverain2|' + before1 + ' ' + num1 + (after1 || '') + '}}');
					}
				}
				if (!num2 && cleanSpaces(after1) === cleanSpaces(before2)) {
					res.push('{{souverain3|' + before1 + ' ' + num1 + after1 + '}}');
				}
				return res;
    		}
		},*/ {
      // Foo II -> {{souverain-|Foo II}}
      // Handles Fichier: too, only with vignette.
			//regex: /(^|\[\[(?:[fF]ichier|[fF]ile|[iI]mage):[^\|\]]+(?:\|(?![^\S\r\n]*(?:vignette|thumb|\{\{[^\S\r\n]*[sS]ouverain[23\-])[^\S\r\n]*\|)[^\|\]]*)*\|(?:vignette|thumb)(?:\|(?![^\S\r\n]*(?:vignette|thumb|\{\{[^\S\r\n]*[sS]ouverain[23\-])[^\S\r\n]*\|)[^\|\]]*)*\||\{\{(?![^\S\r\n]*(?:[sS]ouverain[23\-]|[nN]obr(?: rom(?:ains)?)?)[^\S\r\n]*\|)[^\|\{\}\r\n]+\|[^\S\r\n]*|\|\|[^\S\r\n]*|[^\|\{\[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])([àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w](?:-?[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])*)(?:[^\S\r\n]+|&nbsp;)(?:(I)(?:\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?([eE]re?|[èÈ]re|[rR]e)[^\S\r\n]*\}\}|<sup>(ere?|ère|re)<\/sup>)|(Iere?|Ière|Ire|[IVXLCDM]+)|\{\{[^\S\r\n]*([iI]ere?|[iI]re|[IVXLCDM]+)[^\S\r\n]*\}\})($|(?!\{\{!\}\})[^àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])/g,
			regex: /(^|\[\[(?:[fF]ichier|[fF]ile|[iI]mage):[^\|\]]+(?:\|[^\|\]]*)*?\|(?:vignette|thumb)(?:\|[^\|\]]*)*?\||\{\{(?![^\S\r\n]*(?:[sS]ouverain[23\-]|[nN]obr(?: rom(?:ains)?)?)[^\S\r\n]*\|)[^\|\{\}\r\n]+\|[^\S\r\n]*|\|\|[^\S\r\n]*|[^\|\{\[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])([àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w](?:[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])*)(?:[^\S\r\n]+|&nbsp;)(?:(I)(?:\{\{[^\S\r\n]*(?:[eE]xp[^\S\r\n]*\|[^\S\r\n]*)?([eE]re?|[èÈ]re|[rR]e)[^\S\r\n]*\}\}|<sup>(ere?|ère|re)<\/sup>)|(Iere?|Ière|Ire|[IVXLCDM]+)|\{\{[^\S\r\n]*([iI]ere?|[iI]re|[IVXLCDM]+)[^\S\r\n]*\}\})($|[^àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])/g,
      cats: ['dyn', 'rom'],
			processMatch: function (match) {
				var before = match[1];
				var name = match[2];
				var num = match[3] ? getNum2(match[3], match[4] || match[5]) : getNum1(match[6] || match[7]);
				var after = match[8];
				return [before, name, num, after];
			},
			replacements: function (before, name, num, after) {
				var res = [];
				if (knownNames.test(name + ' ' + num)) {
					res.push(before + '{{' + name + ' ' + num + '}}' + after);
				}
				res.push(before + '{{souverain-|' + name + ' ' + num + '}}' + after);
				res.push(before + '{{nobr rom|' + name + ' ' + num + '}}' + after);
				try {
					res.push(before + name + ' ' + getRomModel(num) + after);
				} catch(e) {}
				return res;
			}
		}, {
			// Nom dyn.
			regex: RegExp('\\{\\{souverain-\\|(' + knownNamesRaw + ')\\}\\}', 'g'),
      cats: ['dyn', 'rom'],
			replacements: function (_, name) {
				return [
					'{{' + name + '}}'
				];
			}
		}, {
      regex: /(^|[^\S\r\n]|[\[\(\.\-])([IVXLCDM]+)([.,\)\]\s]|$)|(=)([IVXLCDM]+)([\-,\|\}\s])/g,
      cats: ['rom'],
      processMatch: function (match) {
		var before = match[1] || match[4];
		var num = match[2] || match[5];
		var after = match[3] || match[6];
		return [match[0], before, num, after];
	},
      replacements: function (_, before, num, after) {
      	var res = [];
      	var value;
      	var model;
      	var majModel;
      	try {
      		value = getRomValue(num);
      		model = getRomModel(num);
      		majModel = getRomMajModel(num);
      	} catch(e) {
      		return res;
      	}
      	if (value >= romLimit) {
      		res.push(before + majModel + after);
      		if (model !== majModel) {
      			res.push(before + model + after);
      		}
      	} else {
      		res.push(before + model + after);
      		if (model !== majModel) {
      			res.push(before + majModel + after);
      		}
      	}
        return res;
      }
    }, {
      regex: /(§|\b(?:[cC]h(?:ap)?\.?|[cC]ol\.?|[fF]asc\.?|[fF]r\.?|[lL]iv\.?|[nN]os?|[nN]\.?|[nN][°º]|[nN]um\.?|[pP]p?\.?|[pP]l\.?|[tT]\.?|[vV]ol\.?))(?:[^\S\r\n]|&nbsp;)*(?:(\d+(?:[–\-]\d+)?\b)|(?:([IVXLCDM]+)\b|\{\{([IVXLCDM]+)\}\})(?:[–\-](?:([IVXLCDM]+)\b|\{\{([IVXLCDM]+)\}\}))?)/g,
      cats: ['abbr'],
      processMatch: function (match) {
      	var abbr = match[1];
      	var num = match[2];
      	var numRom = match[3] || match[4];
      	var numRom2 = match[5] || match[6];
      	return [match[0], abbr, num, numRom, numRom2];
      },
      replacements: function (_, abbr, num, numRom, numRom2) {
        var res = [];
        var finalNum = num;
        if (numRom) {
        	try {
        		numRom = getRomModel(numRom);
        	} catch(e) {}
        	finalNum = numRom;
        	
	        if (numRom2) {
	        	try {
	        		numRom2 = getRomModel(numRom2);
	        	} catch(e) {}
	        	finalNum += '-' + numRom2;
	        }
        }
        var abbrPoint = abbr;
        if (abbrPoint.substr(-1) !== '.') {
          abbrPoint += '.';
        }
        var abbrLower = abbr.toLowerCase();
        var abbrLowerPoint = abbrPoint.toLowerCase();
        
        if (abbrLowerPoint === 'ch.') {
        	abbrLowerPoint = 'chap.';
        }
        
        if (abbrLowerPoint === 'chap.'
        	|| abbrLowerPoint === 'col.'
        	|| abbrLowerPoint === 'fasc.'
        	|| abbrLowerPoint === 'fr.'
        	|| abbrLowerPoint === 'liv.'
        	|| abbrLowerPoint === 'n.'
        	|| abbrLowerPoint === 'p.'
        	|| abbrLowerPoint === 'pl.'
        	|| abbrLowerPoint === 'pp.'
        	|| abbrLowerPoint === 't.'
        	|| abbrLowerPoint === 'vol.') {
        		if (abbrLowerPoint === 'pp.') {
        			res.push('{{p.|' + finalNum + '}}');
        			res.push('{{p.}}' + finalNum);
        		}
          res.push('{{' + abbrLowerPoint + '|' + finalNum + '}}');
          res.push('{{' + abbrLowerPoint + '}}' + finalNum);
        }
        if (abbr === '§') {
          res.push('{{§|' + finalNum + '}}');
            res.push('{{§}}' + finalNum);
        }
        if (abbrLowerPoint === 'n.'
        	|| abbrLower === 'no'
        	|| abbrLower === 'nos'
        	|| abbrLower === 'n°'
        	|| abbrLower === 'nº'
        	|| abbrLowerPoint === 'num.') {
          if (abbr === 'No' || abbr === 'N°' || abbrPoint === 'Num.') {
            res.push('{{N°maj|' + finalNum + '}}');
            res.push('{{N°maj}}' + finalNum);
          }
          if (abbr === 'Nos' || abbr === 'N°' || abbrPoint === 'Num.') {
            res.push('{{Numéros maj|' + finalNum + '}}');
            res.push('{{Numéros maj}}' + finalNum);
          }
          if (abbrLowerPoint === 'n.' || abbrLower === 'no' || abbrLower === 'n°' || abbrLower === 'nº' || abbrPoint === 'num.') {
            res.push('{{n°|' + finalNum + '}}');
          	res.push('{{n°}}' + finalNum);
          }
          if (abbrLower === 'nos' || abbrLower === 'n°' || abbrLower === 'nº' || abbrPoint === 'num.') {
            res.push('{{nos|' + finalNum + '}}');
          	res.push('{{nos}}' + finalNum);
          }
        }
        return res;
      }
    }, {
      regex: /('')?(?!(?:[oO]pcit|[oO]p cit|[oO]p\.cit\.|[oO]p\. cit\.)\}\})([oO]p(?:us|t(?:ion)?)?\.?[^\S\r\n]*[cC]it(?:ée?)?\.?)\1/g,
      cats: ['abbr'],
      replacements: function (_, quotes, expr) {
        return [
          '{{op. cit.}}'
        ];
      }
    }, {
      regex: /('')?([iI]bid(?!\.?\}\})\.?)\1/g,
      cats: ['abbr'],
      replacements: function (_, quotes, expr) {
        return [
          '{{ibid.}}'
        ];
      }
    }, {
      regex: /\b(coll\b\.?)(?!\})/g,
      cats: ['abbr'],
      replacements: function (_) {
        return [
          '{{coll.}}'
        ];
      }
    }, {
    	regex: /\b[cC]f\b\.?/g,
    	cats: ['abbr'],
    	replacements: function (_) {
    		var res = [];
    		if (_[0] === 'C') {
    			res.push('{{Cf.|maj}}');
    		}
    		res.push('{{cf.}}');
    		return res;
    	}
    }, {
    	regex: /\bi\. ?e\./g,
    	cats: ['abbr'],
    	replacements: function (_) {
    		return [
    			'{{càd}}'
			];
    	}
    }, {
      regex: /(?:\b([IVX]+)(?:er?|\{\{er?\}\}|<sup>er?<\/sup>)?|\{\{([IVX]+)(?:er?)?\}\})(?:([-–]|[^\S\r\n]+(?:[-–]|au|et)[^\S\r\n]+)(?:([IVX]+)(?:er?|\{\{er?\}\}|<sup>er?<\/sup>)?|\{\{([IVX]+)(?:er?)?\}\}))?(?:[^\S\r\n]|&nbsp;)+siècle(s)?/g,
      cats: ['rom', 'date'],
      processMatch: function (match) {
      	var s1 = match[1] || match[2];
      	var sep = cleanSpaces(match[3] || '');
      	var s2 = match[4] || match[5] || '';
      	var plural = match[6] || '';
      	return [match[0], s1, sep, s2, plural];
      },
      replacements: function (_, s1, sep, s2, plural) {
        var res = [];
        if (sep && s2) {
          if (sep === '–' || sep === '—') {
            sep = '-';
          }
          if (sep === 'et') {
            res.push('{{s2-|' + s1 + '|' + s2 + '}}');
          }
          res.push('{{sp-|' + s1 + '|' + sep + '|' + s2 + (plural ? '|s' : '') + '}}');
        } else if (!plural) {
          res.push('{{s-|' + s1 + '}}');
        }
        return res;
      }
    }, {
      regex: /(?:(?:\{\{(1)er\}\}|\b(1)\{\{er\}\}|\b(1er|\d+)) +)?(?:\b(janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)\b|(\bprintemps\b|\Bété\B|\bautomne\b|\bhiver\b))(?: +(\d+)\b)?/g,
      cats: ['date'],
      processMatch: function (match) {
        var day = match[1] || match[2] || match[3] || '';
        if (day === '1') {
        	day = '1er';
        }
        var month = match[4] || '';
        var season = match[5] || '';
        var year = match[6] || '';
        return [day, month, season, year];
      },
      replacements: function (day, month, season, year) {
      	var res = [];
        if (month) {
        	res.push('{{date-|' + (day ? day + ' ' : '') + month + (year ? ' ' + year : '') + '}}');
        } else if (season && year) {
        	res.push('{{date-|' + season + ' ' + year + '}}');
        }
        return res;
      }
    }, {
      regex: /\[\[(?:[^\]\|]+\|)?(?:(?:\{\{(1er)\}\}|\b(1er|\d+)) +)?\b(janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)\]\](?: +\[\[(\d+)\]\])?/g,
      cats: ['date'],
      processMatch: function (match) {
        var day = match[1] || match[2] || '';
        var month = match[3];
        var year = match[4] || '';
        return [match[0], day, month, year];
      },
      replacements: function (_, day, month, year) {
        return [
        	'{{date|' + (day ? day + ' ' : '') + month + (year ? ' ' + year : '') + '}}',
        	'{{nobr|' + _ + '}}'
        ];
      }
    }, {
    	regex: /(\d+)°/g,
    	cats: ['abbr'],
    	replacements: function (_, n) {
    		var res = [];
    		if (+n >= 1 && +n <= 4) {
    			res.push('{{' + n + 'o}}');
    		}
    		res.push(n + '{{o}}');
    		return res;
    	}
    }, {
    	// 25 p. => 25{{nb p.}}
    	regex: /\b(\d+)[^\S\r\n]+(p|vol)\b\.?/g,
    	cats: ['punc'],
    	replacements: function (_, num, pvol) {
    		var res = [];
    		if (num !== '1') {
    			res.push(num + '{{nb ' + pvol + '.}}');
    		}
    		res.push('{{nb ' + pvol + '.|' + num + '}}');
    		return res;
    	}
    }, {
    	regex: /(?:\b(\d+(?:[^\S\r\n]+\d+)*(?:[.,]\d+)?)|\{\{formatnum:(\d+(?:\.\d+)?)\}\})[^\S\r\n]+((?!(?:[qQ]ue|[qQ]ui|[qQ]uand|[pP]our|[dD]u|[aA]u|[pP]ar|[eE]nviron|[aA]vec|[lL]es?|[lL]a|[uU]ne?|[eE]n|[vV]ers|[dD]es?|[dD]ès|[àÀ]|[eE]ntre|[dD]epuis|[pP]endant|[eE]t|[oO]u|[aA]vant|[aA]près|\d+)[^\|\{\[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]+(?=[^\|\[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])|\[\[(?:[^\]\|]+\|)?[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]+\]\][àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]*)/g,
    	cats: ['punc'],
    	replacements: function (_, num, formatnum, w) {
    		var res = [];
    		num = num || formatnum;
    		var numCompact = num.replace(/\s/g, '').replace(/\./g, ',');
    		var numInt = parseInt(numCompact, 10);
    		if (String(numInt).length < 4) {
    			res.push('{{nobr|' + numCompact + ' ' + w + '}}');
    		}
    		res.push('{{nb|' + numCompact + ' ' + w + '}}');
    		res.push('{{unité|' + numCompact + ' ' + w + '}}');
    		res.push('{{formatnum:' + numCompact.replace(/,/g, '.') + '}} ' + w);
    		return res;
    	}
    }, {
    	regex: /\[\[([àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]+)[^\S\r\n]+(\d+)\]\]/g,
    	cats: ['punc'],
    	replacements: function (_, w, num) {
    		var res = [];
    		res.push('{{lnobr|' + w + ' ' + num + '}}');
    		res.push('[[{{nobr|' + w + ' ' + num + '}}]]');
    		return res;
    	}
    }, {
    	regex: /(^|[^àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w])((?!(?:[qQ]ue|[qQ]ui|[qQ]uand|[pP]our|[dD]u|[aA]u|[pP]ar|[eE]nviron|[aA]vec|[lL]es?|[lL]a|[uU]ne?|[eE]n|[vV]ers|[dD]es?|[dD]ès|[àÀ]|[eE]ntre|[dD]epuis|[pP]endant|[eE]t|[oO]u|[aA]vant|[aA]près|\d+)[^\S\r\n])[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]+)[^\S\r\n]+(\d+\b|\[\[(?:[^\]\|]+\|)?\d+\]\])/g,
    	cats: ['punc'],
    	replacements: function (_, before, w, num) {
    		var res = [];
    		res.push(before + '{{nobr|' + w + ' ' + num + '}}');
    		return res;
    	}
    }, {
    	regex: /(?:\b(\d+|[IVX]+)(?:\{\{(e|è|èmes?|ers?|res?|ere|ères?)\}\}|<sup>(e|è|èmes?|ers?|res?|ere|ères?)<\/sup>|(e|è|èmes?|ers?|res?|ere|ères?|n?de?))|\{\{((?:\d+|[IVX]+)(?:e|è|èmes?|ers?|res?|ere|ères?|n?de?))\}\})(?:[^\S\r\n]+|&nbsp;)([àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]+(?=[^\|\{\[àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w\-])|\[\[(?:[^\]\|]+\|)?[^\]]+\]\][àâäāãăáåæÀÂÄĀÃĂÁÅÆçÇéèêëěēếęÉÈÊËĚĒẾḥḤîïíīÎÏÍĪḵḴłŁñÑôöóòōøœÔÖÓÒŌØŒřŘšșŠȘŚûüúūùÛÜÚŪÙʿ\w]*)/g,
    	cats: ['abbr'],
    	processMatch: function (match) {
    		var n = match[1];
    		var er = match[2] || match[3] || match[4];
    		var nvalid = match[5];
    		var w = match[6];
    		if (n) {
    			er = er.toLowerCase();
    			if (er === 'è' || er === 'ème') {
    				er = 'e';
    			}
    			if (er === 'ere' || er === 'ère') {
    				er = 're';
    			}
    			if (er === 'ères') {
    				er = 'res';
    			}
    			if (er === 'nd') {
    				er = 'd';
    			}
    			if (er === 'nde') {
    				er = 'de';
    			}
    			nvalid = n + er;
    		}
    		return [match[0], nvalid, n, w];
    	},
    	replacements: function (_, n, numOriginal, w) {
    		var res = [];
    		res.push('{{' + n + '|' + w + '}}');
    		res.push('{{' + n + '}} ' + w);
    		if (/^[IVX]+$/.test(numOriginal)) {
    			res.push('{{s mini-|' + numOriginal + '}} ' + w);
    		}
    		return res;
    	}
    }, {
    	regex: /ː/g,
    	cats: ['punc'],
    	replacements: function (_) {
    		return [':'];
    	}
    }, {
    	regex: /ǃ/g,
    	cats: ['punc'],
    	replacements: function (_) {
    		return ['!'];
    	}
    }, {
    	regex: /\.\.\./g,
    	cats: ['punc'],
    	replacements: function (_) {
    		return ['…'];
    	}
    }, {
    	regex: /\bv\.\s*/g,
    	cats: ['abbr'],
    	replacements: function (_) {
    		return ['{{v.}}'];
    	}
    }, {
    	regex: /\{\{(\s*)[dD]ébut dynastie(\s*[|}])/g,
    	cats: ['dyn'],
    	replacements: function (_, sp, close) {
    		return ['{{' + sp + 'Succession/Début' + close];
    	}
    }, {
    	regex: /\{\{(\s*)[fF]in dynastie(\s*[|}])/g,
    	cats: ['dyn'],
    	replacements: function (_, sp, close) {
    		return ['{{' + sp + 'Succession/Fin' + close];
    	}
    }, {
    	regex: /\{\{(\s*)[iI]nsérer dynastie(\s*\|)/g,
    	cats: ['dyn'],
    	replacements: function (_, sp, close) {
    		return ['{{' + sp + 'Succession/Ligne' + close];
    	}
    }, {
    	regex: / & /g,
    	cats: ['abbr'],
    	replacements: function (_) {
    		return [' et '];
    	}
    }, {
    	regex: / ?† ?/g,
    	cats: ['abbr'],
    	replacements: function (_) {
    		return [
    			'mort ',
    			'mort en ',
    			'mort le ',
    			'morte ',
    			'morte en ',
    			'morte le ',
    			'-'
			];
    	}
    }, {
    	regex: /(^|[^\d–\-])(\d+)([–\-])(\d+)(?![\d–\-])/g,
    	cats: ['punc'],
    	replacements: function (_, before, num1, sep, num2) {
    		var res = [];
    		if (sep === '–') {
    			res.push(before + num1 + '-' + num2);
    		}
    		if (+num2 < +num1) {
    			num2 = num1.slice(0, -num2.length) + num2;
    			res.unshift(before + num1 + '-' + num2);
    		}
    		return res;
    	}
    }];
		
		var wait = 50;
		function waitForCodeMirror(cb) {
			var $elt = $('.CodeMirror');
			if ($elt && $elt[0] && $elt[0].CodeMirror) {
				cb();
			} else {
				setTimeout(function () {
					waitForCodeMirror(cb);
				}, wait = Math.min(2000, wait * 2));
			}
		}
		function initReplaceTools(opts) {
			var $summaryLabel = $('#wpSummaryLabel');
			var $container = $('#golmoteReplaceTools');
			if ($container.length) {
				$container.empty();
			} else {
				$container = $('<div id="golmoteReplaceTools"/>').insertBefore($summaryLabel);
			}
      var $controlsContainer = $('<div class="oo-ui-layout oo-ui-horizontalLayout"/>').appendTo($container);
			var $prevButtonContainer = $('<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-buttonInputWidget"/>').appendTo($controlsContainer);
			var $prevButton = $('<button type="button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">Prev</button>').appendTo($prevButtonContainer);
			var $nextButtonContainer = $('<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-buttonInputWidget"/>').appendTo($controlsContainer);
			var $nextButton = $('<button type="button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">Next</button>').appendTo($nextButtonContainer);
      var getActiveCategory = function () {
        return $controlsContainer.find('input:checked').val();
      };
      var getCategories = function () {
        var cats = [];
        for (var i = 0, l = opts.length; i < l; i++) {
          var optCats = opts[i].cats;
          if (optCats) {
            for (var j = 0, m = optCats.length; j < m; j++) {
              var optCat = optCats[j];
              if (cats.indexOf(optCat) === -1) {
                cats.push(optCat);
              }
            }
          }
        }
        cats.push('all');
        return cats;
      };
      var buildCategories = function () {
        var cats = getCategories();
        for (var i = 0, l = cats.length; i < l; i++) {
          var $label = $('<label class="oo-ui-widget" />').appendTo($controlsContainer);
          var $input = $('<input class="oo-ui-inputWidget" type="radio" name="golmoteReplaceToolsCat" />').val(cats[i]).appendTo($label);
          if (i === 0) {
            $input.prop('checked', true);
          }
          $('<span/>').text(cats[i]).appendTo($label);
        }
      };
      buildCategories();
      var $matchContainer = $('<p/>').appendTo($container);
			var $match = $('<code />').css('display', 'none').appendTo($matchContainer);
			var $replacements = $('<div />').appendTo($container);
			var buildReplacements = function (match, processedMatch, availableReplacements) {
				$replacements.empty();
				availableReplacements.forEach(function(previewText, i) {
					if (previewText === match[0]) {
						return;
					}
					var $replacement = $('<p />').appendTo($replacements);
					var $btnContainer = $('<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-buttonInputWidget"/>').appendTo($replacement);
					var $btn = $('<button type="button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">OK</button>').appendTo($btnContainer);
					var $preview = $('<code />').appendTo($replacement);
					$preview.text(previewText);
					$btn.on('click', function () { s.replace(i); });
				});
			};
			var codemirrorInstance = $('.CodeMirror')[0].CodeMirror;
			var initSearch = function () {
				var re = opts.regex;
				var lastIndexStack = [];
				var lastIndex = 0;
				var lastMatch = null;
				var lastMatchOptIdx = -1;
				var reset = function () {
					lastIndex = 0;
					lastIndexStack = [];
					lastMatch = null;
					lastMatchOptIdx = -1;
					codemirrorInstance.setCursor(0, 0);
				};
				var prev = function () {
					lastIndexStack.pop();
					lastIndex = lastIndexStack[lastIndexStack.length - 1] || 0;
					var res = search(true);
					lastIndex = lastMatch ? (lastMatch.index + 1) : 0;
					console.log('next search will begin at', lastIndex, lastIndexStack);
					return res;
				};
				var next = function () {
					var res = search(false);
					lastIndexStack.push(lastIndex);
					lastIndex = lastMatch ? (lastMatch.index + 1) : 0;
					//console.log(res);
					console.log('next search will begin at', lastIndex, lastIndexStack);
					return res;
				};
				var selectMatch = function (match) {
					var content = codemirrorInstance.getValue();
					var nlMatch;
					var before = content.substr(0, match.index);
					var occ = match[0];
					var beforeLine = (nlMatch = before.match(/\n/g)) ? nlMatch.length : 0;
					var beforeCh = before.substr(before.lastIndexOf('\n') + 1).length;
					var occLine = beforeLine + ((nlMatch = occ.match(/\n/g)) ? nlMatch.length : 0);
					var occCh = occLine > beforeLine ? occ.substr(occ.lastIndexOf('\n') + 1).length : beforeCh + occ.length;
					codemirrorInstance.focus();
					codemirrorInstance.setSelection({line:beforeLine,ch:beforeCh},{line:occLine,ch:occCh});
				};
				var search = function (backwards) {
					lastMatch = null;
					lastMatchOptIdx = -1;
					var idx = null;
					var res;
          var cat = getActiveCategory();
          //console.log(cat);
					for (var i = 0, l = opts.length; i < l; i++) {
						var opt = opts[i];
            if (cat !== 'all' && (!opt.cats || opt.cats.indexOf(cat) === -1)) {
              // Skip opt if it doesn't match active cat
              continue;
            }
						res = _search(opts[i]);
						if (res) {
							if (idx === null || res.index < idx) {
								lastMatch = res;
								lastMatchOptIdx = i;
								idx = res.index;
							}
						}
					}
					if (lastMatch) {
						var processedMatch = opts[lastMatchOptIdx].processMatch ? opts[lastMatchOptIdx].processMatch(lastMatch) : lastMatch;
						var availableReplacements = opts[lastMatchOptIdx].replacements.apply(null, processedMatch);
						if (availableReplacements.length) {
							selectMatch(lastMatch);
							$match.css('display', '').text(lastMatch[0]);
							try {
								buildReplacements(lastMatch, processedMatch, availableReplacements);
							} catch(e) { console.error(e); }
						} else {
							if (backwards) {
								prev();
							} else {
								lastIndex = lastMatch.index + 1;
								console.log('next search will begin at', lastIndex, lastIndexStack);
								next();
							}
						}
					} else {
						console.log('Found nothing, reset');
						reset();
						$match.css('display', 'none');
						$replacements.empty();
					}
					return res;
				};
				var _search = function (opt) {
					var re = opt.regex;
					var content = codemirrorInstance.getValue();
					re.lastIndex = lastIndex;
					console.log('search from', lastIndex);
					var match = re.exec(content);
					if (match) {
						return match;
					}
					return false;
				};
				var replace = function (replacementIndex) {
					var opt = opts[lastMatchOptIdx];
					var processedMatch = opt.processMatch ? opt.processMatch(lastMatch) : lastMatch;
					var availableReplacements = opt.replacements.apply(null, processedMatch);
					var replaceValue = availableReplacements[replacementIndex];
					if (!replaceValue || !lastMatch) {
						return false;
					}
					codemirrorInstance.replaceSelection(replaceValue);
					lastIndex = lastMatch ? lastMatch.index + 1 : 0;
					console.log('next search will begin at', lastIndex, lastIndexStack);
					next();
				};
				return {
					reset: reset,
					prev: prev,
					next: next,
					replace: replace
				};
			};
			var s = initSearch();
			$prevButton.on('click', function () { s.prev(); });
			$nextButton.on('click', function () { s.next(); });
		}
		if (location.href.match(/&action=(?:edit|submit)/)) {
			$(function () {
				waitForCodeMirror(function () {
          initReplaceTools(replaceOptions);
        });
			});
		}
	}());