Poznámka: Po uložení musíte vyprázdnit mezipaměť vašeho prohlížeče, jinak změny neuvidíte.

//<nowiki>
//"use strict";

// ==UserScript==
// @name           Wire
// @namespace      http://jsvoboda.info/wire/
// @description    Nastroj pro upravy textu orientovany na Wikipedii
// @include        http://cs.wikipedia.org/*
// @include        http://cs.wikisource.org/*
// @include        http://cs.wiktionary.org/*
// @include        http://cs.wikiquote.org/*
// @include        http://cs.wikibooks.org/*
// @include        http://cs.wikiversity.org/*
// @include        http://commons.wikimedia.org/*
// @grant          none
// ==/UserScript==

var isFirefox = (navigator.userAgent.match(/Firefox/i) != null);
var con = function() {};
//var con = function() { if (isFirefox && window.console) { console.info.apply(this, arguments); } };

// depending on editor, brace matching and/or code folding may not work (because of some complex regexes)
var wire = wire || {
  prgName: 'Wire',
  version: '0.24',
  debugLevel: 0,
  envir: '',
  winpos: [],

  numofmatches: 0,
  numofnematches: 0,
  dbgcount: 0,

  txtArea: null,
  dbgArea: null,
  menuHook: null,
  exampleHook: null,

  menu_hook_id: 'wire_hook',
  menu_button_id: 'wire_button',

  menuOpened: false,

  hideTimer: null,

  init: function(versionSuffix) {
    if (document.getElementById(this.menu_hook_id)) {
      return;
    }

    if (versionSuffix) {
      this.version += versionSuffix;
    }

    this.bindDOM();
    if (!this.txtArea) {
      this.debug('cannot bind to DOM');
      return;
    }

    if (this.versionField) {
      this.versionField.value = this.version;
    }

    if (this.exampleHook) { // testovaci ovladaci panel, nelze pouzit pod Greasemonkey
      this.initExamples();
    }

    this.initMenu();

    this.txtArea.focus();
  },

  debug: function(txt) {
    if (this.debugLevel) alert(this.prgName + ': ' + txt);
  },

  setEnvir: function() {
    // unused now
    // Greasemonkey
    // if (window.GM_addStyle && (typeof window.GM_addStyle == "function")) { ... }

    envir = 'gm'; try { tmp = GM_registerMenuCommand; }
    catch (err) { envir = ''; }

    return envir;
  },

  bindDOM: function() {
    this.txtArea = document.getElementById('wpTextbox1');
    this.dbgArea = document.getElementById('dbgbox');
    this.menuHook = document.getElementById('toolbar') || document.getElementById('firstHeading');
    this.exampleHook = document.getElementById('examplebar');
    this.versionField = document.getElementById('wireVersionField');
  },

  profilingOn: function() {
    var t = 'profilingChbox';
    return document.getElementById(t) && document.getElementById(t).checked;
  },

  tracingOn: function() {
    var t = 'tracingChbox';
    return document.getElementById(t) && document.getElementById(t).checked;
  },

  // *** core application logic follows ***

  // joblist is program
  // tin and tout are for regression testing
  programs: {
    'interpunkce' : {
      'joblist': [['ipcz0'],['condwiki', 'ipcz'],['compress_hspace']],
      'shortdesc': 'Opravit interpunkci',
      'longdesc': 'Oprava mezerování v okolí interpunkčních znamének',
      'tin': 'A,b.C.\n,,q"\nX , y ,z.\n11:30\nBylo to roku 1888 ( nebo 1889 ), pripadne(dle Wagnera)1891.\n(Tak.) ("Tak.") \n',
      'tout': 'A, b. C.\n\"q\"\nX, y, z.\n11:30\nBylo to roku 1888 (nebo 1889), pripadne (dle Wagnera) 1891.\n(Tak.) ("Tak.") \n'
    },
    'ipcz0' : {
      'joblist': [['ipcz0']],
      'shortdesc': 'Sjednotit uvozovky',
      'longdesc': 'Odstranit různé náhrady uvozovek',
      'tin': 'A,b.C.\n,,q"\nX , y ,z.\n11:30\n'
    },
    'typocz1' : {
      'openGrp': 1,
      'joblist': [['condwiki', 'quotecz1'],['condwiki', 'dashcz']],
      'shortdesc': 'Typografie 1 („...“ 1–2)',
      'longdesc': 'Uvozovky první úrovně a pomlčky',
      'tin': '"a" [["b"]] [["c"|"d"]] {{"e"}}\na-b\nc - d\n1-2\n[[1848]]-[[1849]]\n"1-2"\n"{{ "" {{ "" }} "" }}"\n',
      'tout': '„a“ [["b"]] [["c"|„d“]] {{"e"}}\na-b\nc – d\n1–2\n[[1848]]–[[1849]]\n„1–2“\n„{{ "" {{ "" }} "" }}“\n'
    },
    'typocz2a' : {
      'joblist': [['condwiki', 'quotecz2']],
      'shortdesc': 'Typografie 2 (‚...‘)',
      'longdesc': 'Uvozovky druhé úrovně (‚...‘)'
    },
    'typocz2g' : {
      'joblist': [['condwiki', 'quotecz2g']],
      'shortdesc': 'Typografie 2 (»...«)',
      'longdesc': 'Uvozovky druhé úrovně (»...«)'
    },
    'compress_hspace' : {
      'openGrp': 1,
      'joblist': [['compress_hspace']],
      'shortdesc': 'Stažení skupin H mezer',
      'longdesc': 'Stažení více mezer v jednu, ne však na začátku řádku',
      'tin': '  a  b  c\n  a  b  c\n',
      'tout': '  a b c\n  a b c\n'
    },
    'ltrim' : {
      'joblist': [['ltrim']],
      'shortdesc': 'Zrušení počátečních H mezer',
      'tin': ' a \n b \n'
    },
    'rtrim' : {
      'joblist': [['rtrim']],
      'shortdesc': 'Zrušení koncových H mezer',
      'tin': ' a \n b \n'
    },
    'compress_vspace' : {
      'openGrp': 1,
      'joblist': [['compress_vspace']],
      'shortdesc': 'Stažení skupin V mezer',
      'longdesc': 'Stažení více vertikálních mezer v jednu',
      'tin': 'a\nb\n\na\n\n\na'
    },
    'remove_vspace' : {
      'joblist': [['remove_vspace']],
      'shortdesc': 'Úplné zrušení V mezer',
      'tin': 'a\nb\n\na\n\n\na'
    },
    'wikilist' : {
      'openGrp': 1,
      'joblist': [['wikilist']],
      'shortdesc': 'Vložit * na začátku řádku',
      'tin': 'a \nb \n'
    },
    'wikiheading' : {
      'joblist': [['wikiheading']],
      'shortdesc': 'Doplnit mezery okolo nadpisů'
    },
    'vlnka-nbsp' : {
      'joblist': [['condwiki', 'vlnka-nbsp']],
      'shortdesc': 'Vložit nezlomitelné mezery',
      'longdesc': 'Vkládání nezlomitelné mezery před neslabičné předložky',
      'tin': 'U nás i u vás, stůjme při sobě. Z [[vodík]]u se vyrábí voda.'
    },
    'crlf1' : {
      'openGrp': 1,
      'joblist': [['crlf1']],
      'shortdesc': 'Zdvojit osamocené konce řádků'
    },
    'br_p' : {
      'joblist': [['br_p']],
      'shortdesc': 'BR převést na konec řádku'
    },
    'br_pp' : {
      'joblist': [['br_pp']],
      'shortdesc': 'BR převést na dvojí konec řádku'
    },
    'br_delete' : {
      'joblist': [['br_delete']],
      'shortdesc': 'BR odstranit'
    },
    // speciality
    'nkc-strucny' : {
      'openGrp': 1,
      'joblist': [['nkc-strucny']],
      'shortdesc': 'NKC stručný záznam',
      'longdesc': 'NKC stručný záznam, převod na text (ne zcela dokonalý)'
    },
    'geo' : {
      'joblist': [['geo']],
      'shortdesc': 'Šablona Geo',
      'longdesc': 'Konverze formátu GPS souřadnic z mapy.cz a atlas.cz',
      'tin': 'Loc: 49°34\'22.689\"N, 16°52\'37.81\"E\n49°34\'27.01\"N; 16°52\'29.17\"E\n',
      'tout': 'Loc:{{Geo dms|49|34|22.689|N|s|16|52|37.81|E|v|type:landmark_region:CZ}}\n{{Geo dms|49|34|27.01|N|s|16|52|29.17|E|v|type:landmark_region:CZ}}\n'
    },
    'location' : {
      'joblist': [['location']],
      'shortdesc': 'Šablona Location',
      'longdesc': 'Konverze formátu GPS souřadnic z mapy.cz a atlas.cz'
    },
    'citation' : {
      'joblist': [['citation']],
      'shortdesc': 'Převod citačních šablon'
    },
    'nbsp_to_entities' : {
      'joblist': [['nbsp_to_entities']],
      'shortdesc': 'Převod nezlomitelných mezer ze znaků (160) na entity (&nbsp;)',
      'tin': 'a b\n',
      'tout': 'a&nbsp;b\n'
    }
    /*
    'units' : {
      'joblist': [['condwiki', 'units'],['compress_hspace']],
      'longdesc': 'Oprava mezerování v okolí jednotek (poměrně hrubý nástroj)',
      'tin': '10cm a 20mm\n',
      'tout': '10 cm a 20 mm\n'
    },
    'break_coam' : {
      'joblist': [['break_coam']],
      'shortdesc': 'Šablona České obce a města',
      'longdesc': 'Rozdělí infobox "České obce a města" na novou dvojici "Infobox sídlo" a "IBOM"',
      'tin': '...\n...\n{{Infobox české obce a města\n| název = Malý Bor\n| status = obec\n| kraj = [[Plzeňský kraj|Plzeňský]]\n| okres = Klatovy\n| ob.roz.půs = [[Horažďovice]]\n| pov.ob = [[Horažďovice]]\n| země = [[Čechy]]\n| výměra = 15,07\n| obyvatelé = 565 <small>(28.&nbsp;8.&nbsp;2006)</small>\n| zem.šíř = 49° 19’ 46’’ s. š.\n| zem.dél = 13° 38’ 26’’\n| PSČ = 341&nbsp;01\n| početzsj = 4\n| početč = 4\n| početk = 4\n| adresa = Malý&nbsp;Bor&nbsp;146<br />34101 Horažďovice\n| web =\n| úř.web =\n| email = ou@mbor.jz.cz\n| starosta = Radovan Faltys| foto =\n| popisek.foto =\n| znak =\n| vlajka = Image:Maly bor prapor.gif\n| mapa =\n| NUTS5 = CZ0322 556629\n| NUTS4 = CZ0322\n| NUTS3 = CZ032\n| nad.výš = 442\n}}\n+++\n+++'
    },
    */
  },

  // brace matching and/or code folding may not work here
  machines: {
    // *** Custom machines
    'break_coam' : [
      ["_", 'callfn', 'wirespec.breakCoam']
    ],
    'citation' : [
      ["_", 'callfn', 'wirespec.convertAllCitations']
    ],

    // *** Global replacement machines
    'nbsp_to_entities' : [
      ["_", 'global'],
      [/ /, "&nbsp;", ""]
    ],
    'remove_vspace' : [
      ["_", 'global'],
      [/(\r\n){1,}/, "\r\n", ""],
      [/(\n){1,}/, "\n", ""]
    ],
    'compress_vspace' : [
      ["_", 'global'],
      [/(\r\n){2,}/, "\r\n\r\n", ""],
      [/(\n){2,}/, "\n\n", ""]
    ],
    'ltrim' : [
      ["_", 'global'],
      [/^[ \t]+/, "", "m"]
    ],
    'rtrim' : [
      ["_", 'global'],
      [/[ \t]+$/, "", "m"]
    ],
    'wikilist' : [
      ["_", 'global'],
      [/\r\n/, "\n", ""],  // fix IE behavior
      [/^[ \t]*(.)/, "* $1", "m"]
    ],
    'wikiheading' : [
      ["_", 'global'],
      [/^(={2,})(.*?)(={2,}) *$/, function(m, p1, p2, p3) {
        return p1 + (p2.charAt(0) == ' ' ? '' : ' ') + p2 + (p2.charAt(p2.length - 1) == ' ' ? '' : ' ') + p3;
      }, "m"]
    ],
    'nkc-strucny' : [
      ["_", 'global'],
      [/^ *(.*) \/ (.*) ; (.*) -- (.*) -- (.*) -- (.*)$/, "$2: ''$1''. $3 $4 $5", "m"]
    ],

    // *** Condition machines
    'condwiki' : [  // format Wikipedie
      // {cond: 1, id: "conserve URL", pat: /(ftp|http|https):\/\/[^][\s<>{}|"']*/}, // simple but...
      // http://blog.mattheworiordan.com/post/13174566389/url-regular-expression-for-links-with-or-without-the
      {cond: null, id: "conserve URL", pat: /((((ftp|http|https):(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/},
      {cond: 1, id: "conserve file link", pat: /(File|Soubor):.*?\|/},
      {cond: 1, id: "conserve", pat: ['<code>', /<code>[\s\S]*?<\/code>/]},
      {cond: 1, id: "conserve", pat: ['<del>', /<del>[\s\S]*?<\/del>/]},
      {cond: 1, id: "conserve", pat: ['<ins>', /<ins>[\s\S]*?<\/ins>/]},
      {cond: 1, id: "conserve", pat: ['<kbd>', /<kbd>[\s\S]*?<\/kbd>/]},
      {cond: 1, id: "conserve", pat: ['<math>', /<math>[\s\S]*?<\/math>/]},
      {cond: 1, id: "conserve", pat: ['<nowiki>', /<nowiki>[\s\S]*?<\/nowiki>/]},
      {cond: 1, id: "conserve", pat: ['<pre>', /<pre>[\s\S]*?<\/pre>/]},
      {cond: 1, id: "conserve", pat: ['<samp>', /<samp>[\s\S]*?<\/samp>/]},
      {cond: 1, id: "conserve", pat: ['<sub>', /<sub>[\s\S]*?<\/sub>/]},
      {cond: 1, id: "conserve", pat: ['<sup>', /<sup>[\s\S]*?<\/sup>/]},
      {cond: 1, id: "conserve", pat: ['<timeline>', /<timeline>[\s\S]*?<\/timeline>/]},
      {cond: 1, id: "conserve", pat: ['<tt>', /<tt>[\s\S]*?<\/tt>/]},
      {cond: 1, id: "conserve", pat: ['<var>', /<var>[\s\S]*?<\/var>/]},

      {id: "wikitable begin", pat: "{|",  open: "{|"},
      {id: "wikitable end",   pat: "|}", close: "{|"},

      {id: "template begin",  pat: "{{",  open: "{{"},
      {id: "template end",    pat: "}}", close: "{{"},

      {id: "internal link begin", cond: 1, tran: 103, pat: "[["},
      {id: "internal link end", cond: 103, tran: 1, pat: "]]"},
      {id: "internal link break", cond: 103, tran: 1, pat: "|"},

      {id: "external link begin", cond: 1, tran: 104, pat: "["},
      {id: "external link end", cond: 104, tran: 1, pat: "]"},
      {id: "external link break", cond: 104, tran: 1, pat: " "},

      {id: "(pseudo)html entity begin", cond: 1, tran: 201, pat: "<"},
      {id: "(pseudo)html entity end", cond: 201, tran: 1, pat: ">"},
      {id: "(pseudo)html entity begin", cond: 2, tran: 202, pat: "<"},
      {id: "(pseudo)html entity end", cond: 202, tran: 2, pat: ">"},
      {id: "(pseudo)html entity begin", cond: 3, tran: 203, pat: "<"},
      {id: "(pseudo)html entity end", cond: 203, tran: 3, pat: ">"}
    ],

    // *** Replacement machines
    'crlf1' : [
      {cond: 1, pat: /\n+/, repl: '\n\n'}
    ],
    'br_p' : [
      {cond: 1, pat: /<\/?br *\/?>/, repl: '\n'}
    ],
    'br_pp' : [
      {cond: 1, pat: /<\/?br *\/?>/, repl: '\n\n'}
    ],
    'br_delete' : [
      {cond: 1, pat: /<\/?br *\/?>/, repl: ''}
    ],
    'ipcz0' : [
      // konverze ruznych nahrad uvozovek
      {cond: 1, pat: ',,', repl: '"'},
      {cond: 1, pat: '´´', repl: '"'},
      {cond: 1, pat: '„', repl: '"'},
      {cond: 1, pat: '“', repl: '"'},
      {cond: 1, pat: '”', repl: '"'}
    ],

    'ipcz' : [
      // pokusi se opravit chybnou interpunkci; muze vytvorit serie mezer
      {id: "zachovani otviraci zavorky",
        cond: 1, pat: /(^|\n)\(/},
      {id: "zachovani ciselnych udaju",
        cond: 1, pat: /\d[:,]\d/},
      {id: "zachovani tri tecek",
        cond: 1, pat: ["...", /\.{3,}/], repl: '...'},
      {id: "zachovani tri tecek po mezere",
        cond: 1, pat: [" ...", / \.{3,}/], repl: ' ...'},
      {id: "stazeni dvou tecek v jednu",
        cond: 1, pat: "..", repl: '.'},
      {id: "mezerovani kolem krat",
        cond: 1, pat: /(\d) ?[xX] ?(\d)/, repl: function(){return RegExp.$1 + "×" + RegExp.$2;}},
      {id: "mezery okolo leve zavorky",
        cond: 1, pat: ['(', /\( */], repl: ' ('},
      {id: "mezery okolo prave zavorky - pred interpunkci",
        cond: 1, pat: / *\)([.,:;!?]+)/, repl: function(){return ")" + RegExp.$1 + " ";}},
      {id: "mezery okolo prave zavorky - jinak",
        cond: 1, pat: / *\)/, repl: ') '},
      {id: "mezera za dvojteckou uvozujici primou rec",
        cond: 1, pat: /:"/, repl: ': "'},
      {id: "stazeni mezery pred interpunkci a jeji vlozeni za interpunkci",
        cond: 1, pat: / ([.,:;!?]+)(?!"|\))/, repl: function(){return RegExp.$1 + " ";}},
      {id: "vlozeni mezery za interpunkci",
        cond: 1, pat: /([.,:;!?]+)(?!"|\))([^\s])/, repl: function(){return RegExp.$1 + " " + RegExp.$2;}}
    ],

    'units' : [  // mezerovani kolem jednotek (pomerne hruby nastroj)
      {cond: 1, pat: /(\d+)(mm|cm|let)\b/, repl: function(){return RegExp.$1 + "&nbsp;" + RegExp.$2;}}
    ],

    'quotecz1' : [  // ceske uvozovky 1. urovne
      {cond: 1, tran: 2, pat: '"', repl: "„"},  // \u201E
      {cond: 2, tran: 1, pat: '"', repl: "“"}  // \u201C
    ],
    'quotecz2' : [  // ceske uvozovky 2. urovne (apostrofy)
      {cond: 0, tran: 2, pat: "„"},  // \u201E
      {cond: 0, tran: 1, pat: "“"},  // \u201C
      {cond: 0, tran: 0, pat: ["''", /'{2,}/]},  // zachovat skupiny apostrofu
      {cond: 2, tran: 3, pat: "'", repl: "‚"},
      {cond: 3, tran: 2, pat: "'", repl: "‘"}
    ],
    'quotecz2g' : [  // ceske uvozovky 2. urovne (guillemets)
      {cond: 0, tran: 2, pat: "„"},  // \u201E
      {cond: 0, tran: 1, pat: "“"},  // \u201C
      {cond: 0, tran: 0, pat: ["''", /'{2,}/]},  // zachovat skupiny apostrofu
      {cond: 2, tran: 3, pat: "'", repl: "»"},
      {cond: 3, tran: 2, pat: "'", repl: "«"}
    ],
    'dashcz' : [  // pomlcky
      {cond: 0, tran: 0, pat: /\d{4}-\d{1,2}-\d{1,2}/},
        // vyjimka: u dat zachovat spojovnik
      {cond: 0, tran: 0, pat: ['IS', /IS[BS]N:? [-\d]*/]},
        // vyjimka: u ISBN/ISSN zachovat spojovnik
      {cond: 1, tran: 0, pat: / - /, repl: " – "},
        // nahradit spojovnik pomlckou
      {cond: 1, tran: 0, pat: /(\d)-(\d)/, repl: function(){return RegExp.$1 + "–" + RegExp.$2;}},
        // nahradit spojovnik pomlckou mezi cisly
      {cond: 103, tran: 0, pat: /(\d)\]\]-\[\[(\d)/, repl: function(){return RegExp.$1 + "]]–[[" + RegExp.$2;}}
        // nahradit spojovnik pomlckou mezi cisly, ktere jsou odkazy (typicky letopocty)
    ],
    'compress_hspace' : [  // stahne mezery a tabulatory, ne vsak na novem radku
      {cond:1, pat: /[\r\n][ \t]+/},
      {cond:1, pat: /[ \t]+/, repl: function(pos){return (pos>0 ? " " : null);}}
    ],
    'geo' : [  // Konverze formátu GPS souřadnic z mapy.cz a atlas.cz
      {cond:1, pat: / *(\d+)°(\d+)'([\d.]+)"N[,;] *(\d+)°(\d+)'([\d.]+)"E */,
        repl: function(){return "{{Geo dms|" + RegExp.$1 + "|" + RegExp.$2 + "|" + RegExp.$3 + "|N|s|" + RegExp.$4 + "|" + RegExp.$5 + "|" + RegExp.$6 + "|E|v|type:landmark_region:CZ}}";}
      }
    ],
    'location' : [  // Konverze formátu GPS souřadnic z mapy.cz a atlas.cz
      {cond:1, pat: / *(\d+)°(\d+)'([\d.]+)"N[,;] *(\d+)°(\d+)'([\d.]+)"E */,
        repl: function(){return "{{Location|" + RegExp.$1 + "|" + RegExp.$2 + "|" + RegExp.$3 + "|N|" + RegExp.$4 + "|" + RegExp.$5 + "|" + RegExp.$6 + "|E|type:landmark_region:CZ}}";}
      }
    ],
    'vlnka-nbsp' : [  // vlnkovani predlozek HTML entitou
      {cond:1, pat: /([AIiOoUuKkSsVvZz])( +| *\s)/,
        repl: function(pos,buf){return (pos==0||!wire.isWikiWordChar(buf,pos-1) ? RegExp.$1 + "&nbsp;" : null);}
      }
    ]
  },

  // ***

  addGlobalStyle: function(doc, rules) {
    var i;

    sheet = doc.styleSheets[0];
    if (!sheet) {
      alert('sheet not found');
    }
    for (i in rules) {
      rule = rules[i];
      // alert(i);
      if (sheet.rules) {
        // IE
        sheet.addRule(rule[0], rule[1]);
      }
      else if (sheet.cssRules) {
        // NN
        // raises Security error
        sheet.insertRule(rule[0] + '{' + rule[1] + '}', sheet.cssRules.length);
      }
    }
  },

  runTests: function() {
    var result;
    var str = "";
    for (name in this.programs) {
      if (this.programs[name]['tin'] && this.programs[name]['tout']) {
        str += this.programs[name]['shortdesc'] + ": ";
        result = (this.callProg(name, this.programs[name]['tin'], 0) == this.programs[name]['tout']);
        if (result) {
          str += "OK";
        }
        else {
          str += "FAILED";
        }
        str += "\n";
      }
    }
    alert(str);
  },

  addHandler: function(element, attach, handler) {
    if (window.addEventListener) {  // NN6+
      element.addEventListener(attach, handler, false);
    } else if (window.attachEvent) {  // IE5+
      element.attachEvent('on' + attach, handler);
    }
    else {
      alert("Error in function addHandler");
    }
  },

  initMenu: function() {
    // tlacitko otevirajici menu
    var span = document.createElement('span');

    var a = document.createElement('a');
    a.id = this.menu_button_id;
    a.style.cursor = 'pointer';
    a.style.background = '#4f4';
    this.addHandler(a, 'click', function(e){wire.openMenu();});
    // this.addHandler(a, 'dblclick', function(e){wire.openWindoid();});

    a.appendChild(document.createTextNode(this.prgName + ' ' + this.version  + (typeof GM_info != 'undefined' ? ' (GM)' : '')));

    var ch = document.createElement('input');
    ch.setAttribute('type', 'checkbox');
    // ch.setAttribute('checked', '1');
    // ch.id = 'useSans';
    this.addHandler(ch, 'click', function(e){
      if (this.checked) {
        wire.txtArea.style.fontFamily = "Arial, Helvetica, sans-serif";
        wire.txtArea.style.fontSize = "10pt";
      }
      else {
        wire.txtArea.style.fontFamily = "Courier New";
        wire.txtArea.style.fontSize = "10pt";
      }
    });

    span.appendChild(a);
    span.appendChild(ch);
    this.menuHook.insertBefore(span, this.menuHook.childNodes[0]);

    // absolutne polohovany hook pro zaveseni menu
    var hook0 = document.createElement('div');
    hook0.style.position = 'absolute';

    var hook = document.createElement('div');
    hook.id = this.menu_hook_id;
    hook.style.position = 'absolute';
    hook.style.width = '400px';
    hook.style.zIndex = '1020';
    hook.style.backgroud = 'white';
    hook.style.height = '1px';
    hook.style.top = '1px';
    hook.style.margin = '1px';

    hook0.appendChild(hook);

    this.txtArea.parentNode.insertBefore(hook0, this.txtArea);
  },

  openMenu: function() {
    if (this.menuOpened) {
      this.closeMenu();
    }
    else {
      this.menuOpened = true;
      div1 = document.createElement('div');
      div1.style.position = "relative";
      div1.style.zIndex = "1020";
      div1.style.border = "1px dotted gray";
      div1.style.fontSize = "12px";
      div1.style.lineHeight = "1.2em";

      // skryti menu po mouseout, otestovano ve FF, ale nekdy nefungovalo v user scriptu GM
      this.addHandler(div1, 'mouseout', function(e) {
        this.hideTimer = setTimeout("wire.closeMenu()", 100); // even 0 is possible
      });
      this.addHandler(div1, 'mouseover', function(e) {
        clearTimeout(this.hideTimer);
      });

      for (name in this.programs) {
        p1 = document.createElement('a');
        p1.style.display = "block";
        p1.style.textDecoration = "none";
        p1.style.color = "black";
        p1.style.background = "white";
        p1.style.padding = "1px 0 1px 0.5em";
        p1.style.cursor = "pointer";
        if (this.programs[name]['openGrp']) {
          p1.style.borderTop = "1px dotted black";
        }
        p1.setAttribute('boundProg', name);
        this.addHandler(p1, 'click', this.menuClick);
        this.addHandler(p1, 'mouseover', function(e){ if(this.style)this.style.background = "#4f4"; });
        this.addHandler(p1, 'mouseout', function(e){ if(this.style)this.style.background = "white"; });

        txt1 = document.createTextNode(this.programs[name]['shortdesc']);
        p1.appendChild(txt1);

        div1.appendChild(p1);
      }
      document.getElementById(this.menu_hook_id).appendChild(div1);
      // this.addHandler(document, 'click', function(e){ this.closeMenu(); });
    }
  },

  closeMenu: function() {
    menu_hook = document.getElementById(this.menu_hook_id);
    var kid = menu_hook.firstChild;
    while (kid) {
      menu_hook.removeChild(kid);
      kid = menu_hook.firstChild;
    }
    this.menuOpened = false;
  },

  menuClick: function(evt) {
    evt = (evt) ? evt : (window.event) ? window.event : "";
    if (evt) {
      var elem = (evt.target) ? evt.target : evt.srcElement;
      prog = elem.getAttribute('boundProg');
      wire.callProg(prog, null, wire.profilingOn());
      wire.closeMenu();
    }
    else {
      alert("Error in function menuClick");
    }
  },

  stopBubble: function(e) {
    // If an event object is provided, then this is a non-IE browser
    if ( e ) {
      // and therefore it supports the W3C stopPropagation() method
      e.stopPropagation();
    }
    else {
      // Otherwise, we need to use the Internet Explorer way of cancelling event bubbling
      window.event.cancelBubble = true;
    }
  },

  initExamples: function() {
    var tbody = document.createElement('tbody');
    var tr, td, elem, longdesc;

    for (name in this.programs) {
      tr = document.createElement('tr');

      td = document.createElement('td');
      if (this.programs[name]['tin']) {
        elem = document.createElement('a');
        elem.href = "javascript:wire.insertExample('" + name + "')";
        elem.appendChild(document.createTextNode("příklad"));
        td.appendChild(elem);
      }
      tr.appendChild(td);

      elem = document.createElement('a');
      elem.href = "javascript:wire.callProg('" + name + "',null,wire.profilingOn())";
      elem.appendChild(document.createTextNode(this.programs[name]['shortdesc']));
      td = document.createElement('td');
      td.appendChild(elem);
      tr.appendChild(td);

      longdesc = (this.programs[name]['longdesc'] ? this.programs[name]['longdesc'] : '');
      elem = document.createElement('span');
      elem.appendChild(document.createTextNode(longdesc));
      td = document.createElement('td');
      td.appendChild(elem);
      tr.appendChild(td);

      tbody.appendChild(tr);
    }

    var table = document.createElement('table');
    table.appendChild(tbody);
    this.exampleHook.appendChild(table);

    // ***

    var par = document.createElement('p');
    {
      var ch = document.createElement('input');
      ch.setAttribute('type', 'checkbox');
      ch.id = 'profilingChbox';
      par.appendChild(ch);
      par.appendChild(document.createTextNode('profiling '));

      ch = document.createElement('input');
      ch.setAttribute('type', 'checkbox');
      ch.id = 'tracingChbox';
      par.appendChild(ch);
      par.appendChild(document.createTextNode('tracing '));
    }
    this.exampleHook.appendChild(par);
  },

  insertExample: function(program) {
    this.txtArea.value = this.programs[program]['tin'];
  },

  prepareRegExp: function(source) {
    var re = new RegExp("" + source + "|", "g");
    // 'g' proto, abychom mohli vyuzit re.lastIndex
    // prazdna alternativa kvuli optimalizaci
    return re;
  },

  callProg: function(program, inputstr, profiling) {
    var joblist = this.programs[program]['joblist'];
    var i, j, k;
    var buf, re, machine, raw, compiled, rnum;

    // document.getElementById(this.menu_button_id).innerHTML = this.prgName + " ...";
    // for(var i = 0; i < 200000000; i++) ;

    if (inputstr) {
      buf = inputstr;
    }
    else {
      this.winpos = [];
      buf = this.getTextOrSelection(this.txtArea);
    }

    var d1 = new Date();

    // cyklus 1. urovne - pres ulohy
    for (i in joblist) {

      machine = this.machines[joblist[i][0]];

      //con(typeof machine);

      if (machine[0][0] == '_') {
        switch (machine[0][1]) {
          case 'callfn':
            // call custom function
            buf = eval(machine[0][2]).call(null, buf);
          break;
          case 'global':
            // global replacement
            for (j = 1; j < machine.length; j++) {
              if (machine[j]) {  // fixes IE6 behavior when comma is used after last array element
                re = new RegExp(machine[j][0].source, "g" + machine[j][2]);
                buf = buf.replace(re, machine[j][1]);
              }
            }
          break;
          default:
            alert("Unsupported option " + machine[0][1]);
          break;
        }
      }
      else {
        // standard processing (soutezeni pravidel)
        var rules = [];

        // cyklus 2. urovne - pres stroje
        for (j in joblist[i]) {
          machine = this.machines[joblist[i][j]];

          // cyklus 3. urovne - pres pravidla
          for (k in machine) {
            raw = machine[k];
            compiled = new Array();
            rnum = rules.length;

            if (raw.id) {
              compiled.id = raw.id;
            }
            else {
              compiled.id = '#' + (rnum + 1);
            }

            if (raw.cond) {
              if (typeof raw.cond == 'number') {
                // cislo
                compiled.condFrom = raw.cond;
                compiled.condTo = raw.cond;
              }
              else if (typeof raw.cond == 'object') {
                // interval
                compiled.condFrom = raw.cond[0];
                compiled.condTo = raw.cond[1];
              }
              else alert("Chyba raw1");
            }
            else {
              compiled.condFrom = 0;
            }

            if (raw.tran) {
              compiled.tran = raw.tran;
            }
            else {
              compiled.tran = 0;
            }

            // vzor
            compiled.pat = new Array();
            if (raw.pat === null) {
              compiled.pat.alwaysMatch = true;
            }
            else if (typeof raw.pat == 'string') {
              if (raw.pat.length == 0)
                compiled.pat.alwaysMatch = true;
              else
                compiled.pat.str = raw.pat;
            }
            else if (raw.pat.source) {  // should be regex
              compiled.pat.re = this.prepareRegExp(raw.pat.source);
            }
            else if (typeof raw.pat == 'object') {  // array
              if (raw.pat.length == 2) {
                compiled.pat.strPrefix = raw.pat[0];
                compiled.pat.re = this.prepareRegExp(raw.pat[1].source);
              }
              else {
                alert ("Unexpected array length in fun callProg: " + raw.pat.length);
                return;
              }
            }
            else {
              alert ("Unknown type in fun callProg: " + typeof raw.pat);
              return;
            }

            // nahrada
            if (typeof raw.repl != 'undefined') {
              compiled.repl = raw.repl;
            }
            else {
              compiled.repl = null;
            }

            if (raw.open) {
              compiled.open = raw.open;
            }

            if (raw.close) {
              compiled.close = raw.close;
            }

            // uctovani
            compiled.num_of_chs = 0;
            compiled.num_of_apps = 0;

            rules[rnum] = compiled;
          }

        }

        // apply rules
        if (profiling) {
          this.applyRules(rules, buf, true);  // bez vystupu
        }
        else {
          buf = this.applyRules(rules, buf);
        }
      }
    }

    var d2 = new Date();
    var milliseconds = d2.getTime() - d1.getTime();
    var seconds = (milliseconds * 0.001).toFixed(3);

    // document.getElementById(this.menu_button_id).innerHTML = this.prgName + ' ' + this.version;

    if (this.profilingOn()) {
      if (document.getElementById('wpSummary')) {
        this.dbgcount++;
        var tmp = "";
        if (joblist.length != 1)
          tmp = "Nelze zmerit vykon (vice jobu v davce)!!!";
        else
          tmp = "" + this.dbgcount + ": "
            + "t = " + seconds + " s/"
            + "" + this.txtArea.value.length + "B/"
            + "" + rules.length + "R/"
            + "V = " + (milliseconds / (rules.length * this.txtArea.value.length)).toFixed(3) + "/"
              // + " ms/B/R;"
            + "NM = " + this.numofmatches + "/" + this.numofnematches
          ;

        document.getElementById('wpSummary').value = tmp;

        if (document.getElementById('dbgbox'))
          document.getElementById('dbgbox').value = program + ": " + tmp + "\n"
            + document.getElementById('dbgbox').value
          ;
      }
    }

    if (inputstr) {
      return buf;
    }
    else {
      this.putTextOrSelection(this.txtArea, buf);
      return;
    }
  },

  applyRules: function(rules, buf, noOutput) {
    var i, mo, repl, skip, rule, changes;
    var pos = 0;
    var state = 1;
    var mainlen = buf.length;
    var outstr = "";
    var effects;

    var globalStackCounter = 0;
    var stackCounter = {};
    var stackStateMemory = {};

    var stack = [];
    var stackPtr = 0;
    var stackPush = function(elem) {
      stack[stackPtr++] = elem;
      return true;
    };
    var stackPop = function() {
      return stack[--stackPtr];
    };
    var stackTop = function() {
      return (stackPtr == 0 ? null : stack[stackPtr - 1]);
    };
    var stackEmpty = function() {
      return (stackPtr == 0);
    };

    // init stackCounter

    for (i in rules) {
      rule = rules[i];
      if (rule.open) {
        stackCounter[rule.open] = 0;
        stackStateMemory[rule.open] = 0;
      }
    }

    while (pos < mainlen) {

      repl = buf.charAt(pos);

      skip = 1;

      for (i in rules) {
        rule = rules[i];

        if (rule.condFrom == 0 || (rule.condFrom <= state && rule.condTo >= state)) {

          if (rule.pat.strPrefix) {
            if (rule.pat.strPrefix != buf.substr(pos, rule.pat.strPrefix.length)) {
              continue;
            }
          }

          if (rule.pat.alwaysMatch) {
            mo = [""];
          }
          else if (rule.pat.str) {
            if (rule.pat.str == buf.substr(pos, rule.pat.str.length)) {
              mo = [rule.pat.str];
            }
            else {
              mo = false;
            }
          }
          else {
            rule.pat.re.lastIndex = pos;
            mo = rule.pat.re.exec(buf);  // toto spotrebuje nejvice casu
          }

          if (mo) {

            if (rule.pat.alwaysMatch || mo[0].length > 0) {  // if pattern matched (matches against empty strings are allowed)

              if (rule.open) {
                effects = (stackCounter[rule.open] == 0);
                con(stackCounter[rule.open], 'open', rule.open);
                ++stackCounter[rule.open];
                ++globalStackCounter;
                stackPush(rule.open);
                con(stackCounter[rule.open]);
                if (effects) {
                  stackStateMemory[rule.open] = state;
                }
              }
              else if (rule.close) {
                con(stackCounter[rule.close]);
                --stackCounter[rule.close];
                --globalStackCounter;
                con('close', rule.close, stackCounter[rule.close]);
                effects = (stackCounter[rule.close] == 0 && stackPop() == rule.close);
                if (effects) {
                  state = stackStateMemory[rule.close];
                }
              }
              else {
                // MODIFIED 17.3.2018
                //effects = true;
                effects = (globalStackCounter == 0); // pokud jsme zanoreni, ZADNA pravidla se neaplikuji (pokud to bude potreba, lze zavest vyjimky)
              }

              //con('effects', effects );

              if (!noOutput && this.tracingOn()) {
                outstr += "[" + rule.id + ":" + mo[0] + (effects ? "" : "[NO_EFFECT]") + "]";
              }

              if (effects) {
                // change state
                if (rule.tran != 0) {
                  state = rule.tran;
                }

                // skip pattern in buf
                skip = mo[0].length;

                // prepare replacement
                if (rule.repl === null) {
                  repl = null;
                }
                else if (typeof rule.repl == 'string') {
                  repl = rule.repl;
                }
                else if (typeof rule.repl == 'function') {
                  repl = rule.repl.call(null, pos, buf);
                }
                else {
                  alert(typeof rule.repl);
                  alert("Program corrupted (2)!");
                  return;
                }

                if (repl === null) {
                  repl = mo[0];
                }

                // do accounting
                if (repl == mo[0]) {
                  changes = 0;
                }
                else {
                  changes = 1;
                }
                rule.num_of_chs = rule.num_of_chs + changes;
                rule.num_of_apps = rule.num_of_apps + 1;
              }

              con('state', state);

              // ignore other rules
              break;
            }
          }
        }
      }

      pos = pos + skip;
      if (!noOutput && state > 0) {
        outstr += repl;
      }

    }

    return outstr;
  },

  specialOutput: function(mode) {
    var i;
    var txt = this.txtArea.value;
    var str = "";

    if (mode == 1) {
      for (i = 0; i < txt.length; i++) {
        switch (txt.charAt(i)) {
          case '\r': str += '\\r'; break;
          case '\n': str += '\\n'; break;
          case '\t': str += '\\t'; break;
          case '\\': str += '\\\\'; break;
          case '\"': str += '\\"'; break;
          case "\'": str += "\\'"; break;
          default: str += txt.charAt(i); break;
        }
      }
    }
    else if (mode == 2) {
      for (i = 0; i < txt.length; i++) {
        str += ""
          + "["
          + (txt.charCodeAt(i) > 32 ? txt.charAt(i) : ".")
          + "/"
          + txt.charCodeAt(i)
          + "] "
        ;
      }
    }
    else if (mode == 3) {
      str = "" + txt.length + " znaku";
    }

    this.dbgArea.value = str;
  },

  dbg: function(str) {
    // output to dbgArea
    this.dbgArea.value += str;
  },

  isWordChar: function(txt, i) {
    // should work for Unicode
    return (txt.charAt(i) >= 'A' && txt.charAt(i) <= 'Z')
      || (txt.charAt(i) >= 'a' && txt.charAt(i) <= 'z')
      || (txt.charCodeAt(i) >= 0xC0 && txt.charCodeAt(i) < 0x2000)
    ;
  },

  isWikiWordChar: function(txt, i) {
    return this.isWordChar(txt, i)
      || txt.charAt(i) == '[' || txt.charAt(i) == ']'
    ;
  },

  // next 2 functions borrowed from wikibits.js (MediaWiki JavaScript support functions)
  getTextOrSelection: function(txtArea) {
    var selText;
    if (document.selection && document.selection.createRange) {  // IE/Opera
      // save window scroll position
      if (document.documentElement && document.documentElement.scrollTop) {
        this.winpos.winScroll = document.documentElement.scrollTop;
      }
      else if (document.body) {
        this.winpos.winScroll = document.body.scrollTop;
      }

      // get current selection
      txtArea.focus();
      this.winpos.range = document.selection.createRange();
      selText = this.winpos.range.text;
    }
    else if (txtArea.selectionStart || txtArea.selectionStart == '0') { // Mozilla
      // save textarea scroll position
      this.winpos.textScroll = txtArea.scrollTop;

      // get current selection
      txtArea.focus();
      this.winpos.startPos = txtArea.selectionStart;
      this.winpos.endPos = txtArea.selectionEnd;
      selText = txtArea.value.substring(this.winpos.startPos, this.winpos.endPos);
    }

    if (selText) {
      this.winpos.usingRange = true;
    }
    else {
      this.winpos.usingRange = false;
      selText = txtArea.value;
    }

    return selText;
  },

  putTextOrSelection: function(txtArea, selText) {
    if (document.selection && document.selection.createRange) {  // IE/Opera

      // insert text
      if (this.winpos.usingRange) {
        this.winpos.range.text = selText;
        // mark inserted text as new selection
        if (this.winpos.range.moveStart) {
          this.winpos.range.moveStart('character', -selText.length);
        }
        this.winpos.range.select();
      }
      else {
        txtArea.value = selText;
      }

      // restore window scroll position
      if (document.documentElement && document.documentElement.scrollTop) {
        document.documentElement.scrollTop = this.winpos.winScroll;
      }
      else if (document.body) {
        document.body.scrollTop = this.winpos.winScroll;
      }
    }
    else if (txtArea.selectionStart || txtArea.selectionStart == '0') { // Mozilla

      // insert text
      if (this.winpos.usingRange) {
        txtArea.value = txtArea.value.substring(0, this.winpos.startPos)
          + selText
          + txtArea.value.substring(this.winpos.endPos, txtArea.value.length)
        ;
        // mark inserted text as new selection
        txtArea.selectionStart = this.winpos.startPos;
        txtArea.selectionEnd = this.winpos.startPos + selText.length;
      }
      else {
        txtArea.value = selText;
      }

      // restore textarea scroll position
      txtArea.scrollTop = this.winpos.textScroll;
    }
  }
};  // end of wire object


var wirespec = {

  citationMetaDict : {
    'citation' : 'Cit',
    'cite book' : 'Citace monografie',
    'cite conference' : 'Citace sborníku',
    'cite journal' : 'Citace periodika',
    'cite web' : 'Citace elektronické monografie',
    'cite news' : 'Citace periodika',
    '~' : '~'
  },

  citationSupplyParams : {
    'Citace periodika' : { 'jazyk' : 'anglicky' },
    'Citace elektronického periodika' : { 'jazyk' : 'anglicky' },
    'Citace elektronické monografie' : { 'jazyk' : 'anglicky' },
    '~' : '~'
  },

  citationDict : {
    'access-date' : 'datum přístupu',
    'accessdate' : 'datum přístupu',
    'archive-url' : 'url archivu',
    'archiveurl' : 'url archivu',
    'archive-date' : 'datum archivace',
    'archivedate' : 'datum archivace',
    'author' : 'autor',
    'author1' : 'autor',
    'author2' : 'autor2',
    'author3' : 'autor3',
    'author4' : 'autor4',
    'authorlink' : 'odkaz na autora',
    'authorlink1' : 'odkaz na autora',
    'authorlink2' : 'odkaz na autora2',
    'authorlink3' : 'odkaz na autora3',
    'authorlink4' : 'odkaz na autora4',
    'booktitle' : 'sborník',
    'chapter' : 'kapitola',
    'coauthors' : 'spoluautoři',
    'date' : 'datum vydání',
    'day' : 'den',
    'deadurl' : 'deadurl',
    'doi' : 'doi',
    'editor' : 'sestavitel',
    'first' : 'jméno',
    'first1' : 'jméno',
    'first2' : 'jméno2',
    'first3' : 'jméno3',
    'first4' : 'jméno4',
    'format' : 'formát',
    'id' : 'id',
    'isbn' : 'isbn',
    'issue' : 'číslo',
    'journal' : 'periodikum',
    'language' : 'jazyk',
    'last' : 'příjmení',
    'last1' : 'příjmení',
    'last2' : 'příjmení2',
    'last3' : 'příjmení3',
    'last4' : 'příjmení4',
    'location' : 'místo',
    'magazine' : 'periodikum',
    'month' : 'měsíc',
    'newspaper' : 'periodikum',
    'others' : 'spoluautoři',
    'page' : 'strany',
    'pages' : 'strany',
    'place' : 'místo',
    'pmid' : 'pmid',
    'publisher' : 'vydavatel',
    'publication-place' : 'místo',
    'ref' : 'ref',
    'title' : 'titul',
    'url' : 'url',
    'volume' : 'ročník',
    // 'website' : 'periodikum', // ???
    'work' : 'periodikum',
    'year' : 'rok',
    '~' : '~'
  },

  /* TODO
  http://cs.wikipedia.org/w/index.php?title=Stirling%C5%AFv_motor&oldid=9297329
  OK * zachovat formátování 1. řádku
  OK * neprevedene polozky opatrit poznamkou "WIRE:neprevedeno"
  * automaticke doplnovani nekterych poli, opatrit poznamno "WIRE:doplneno"
  */

  convertAllCitations: function(buf) {
    var re = /\{\{([Cc]ite |[Cc]itation)/g;
    var mo;
    var pos = 0;
    var output = '';
    var lastPos = 0;
    var i = 0;

    while ((mo = re.exec(buf))) {
      // if (++i > 20) { con('emergency break'); break; }
      range = wirespec.getBalancedExpression(buf, mo.index, '{', '}');
      output += ""
        + buf.substring(lastPos, range.startPos)
        + wirespec.convertCitation(buf.substring(range.startPos, range.endPos))
      ;
      re.lastIndex = range.endPos;
      lastPos = range.endPos;
    }

    output += buf.substr(lastPos);
    return output;
  },

  convertCitation: function(buf) {
    var p = buf.indexOf('|');
    var templateName = wirespec.trim(buf.substring(2, p)).toLowerCase();

    if (wirespec.citationMetaDict[templateName]) {
      buf = '{{'
        + wirespec.convertCitationHeader(buf.substring(2, p + 1), templateName)
        + wirespec.convertCitationInside(buf.substring(p + 1, buf.length - 2), templateName)
        + "}}"
      ;
    }

    return buf;
  },

  // zachovat puvodni formatovani
  convertCitationHeader: function(buf, templateName) {
    return buf.replace(
      new RegExp(templateName, "i"),
      wirespec.citationMetaDict[templateName]
    );
  },

  convertCitationInside: function(buf, templateName) {
    var a = buf.split('|');
    var b, i, key, key2;

    var supplyParams = {};
    if (wirespec.citationSupplyParams && wirespec.citationSupplyParams[wirespec.citationMetaDict[templateName]]) {
      var sp = wirespec.citationSupplyParams[wirespec.citationMetaDict[templateName]];
      for (i in sp) {
        supplyParams[i] = sp[i];
      }
    }

    for (i in a) {
      b = a[i].split('=');
      key = wirespec.trim(b[0]);
      if (wirespec.citationDict[key]) {
        key2 = wirespec.citationDict[key];
        b[0] = b[0].replace(key, key2);
        if (supplyParams[key2]) {
          delete(supplyParams[key2]);
        }
      }
      else {
        b[0] = "<!--WIRE:nepřevedeno:-->" + b[0];
      }
      a[i] = b.join('=');
    }

    for (i in supplyParams) {
      a.push("<!--WIRE:doplněno:-->" + i + '=' + supplyParams[i]);
    }

    return a.join('|');
  },

  breakCoam: function(buf) {
    var re = /\{\{Infobox české obce a města/;
    var mo = re.exec(buf);

    if (mo) {
      var pos = mo.index;

      ar = wirespec.getBalancedExpression(buf, pos, '{', '}');
      startPos = ar.startPos;
      endPos = ar.endPos;

      buf = ""
        + buf.substr(0, startPos)
        + wirespec.convertCoam(buf.substr(startPos, endPos - startPos))
        + buf.substr(endPos)
      ;
    }

    return buf;
  },

  convertCoam: function(box) {
    // analyza

    box = box.substr(0, box.length - 2);  // strip template end

    var a = box.split(/\s\||\|\s/);
    var b;
    a.shift();  // strip template start

    var coam = [];

    for (i in a) {
      b = a[i].split('=');
      b[0] = wirespec.trim(b[0]);
      coam[b[0]] = b[1];
    }

    // synteza

    var re_zem = / *(s. ?š.|v. ?d.) */;
    var sidlo;
    var ibom;

    if (coam['název'].indexOf('-') >= 0) sidlo = "";
    else sidlo = ""
      + "{{Infobox sídlo\n"
      + "| název =" + wirespec.q(coam['název']) + "\n"
      + "| foto =" + wirespec.q(coam['foto']) + "\n"
      + "| popisek.foto =" + wirespec.q(coam['popisek.foto']) + "\n"
      + "| charakter = \n"
      + "| obyvatelé = \n"
      + "| č.p. = \n"
      + "| PSČ =" + wirespec.q(coam['PSČ']) + "\n"
      + "| obec = [[" + wirespec.trim(wirespec.q(coam['název'])) + "]]\n"
      + "| okres =" + wirespec.q(coam['okres']) + "\n"
      + "| země =" + wirespec.q(coam['země']) + "\n"
      + "| k.ú. = \n"
      + "| výměra = \n"
      + "| zem.šíř =" + wirespec.q(coam['zem.šíř']).replace(re_zem, "") + "\n"
      + "| zem.dél =" + wirespec.q(coam['zem.dél']).replace(re_zem, "") + "\n"
      + "| nad.výš = " + wirespec.trim(wirespec.q(coam['nad.výš'])) + "\n"
      + "| mapa =" + wirespec.trim(wirespec.q(coam['mapa'])) + "\n"
      + "}}"
    ;

    ibom = ""
      + "{{IBOM\n"
      + "| název =" + wirespec.q(coam['název']) + "\n"
      + "| status =" + wirespec.q(coam['status']) + "\n"
      + "| kraj =" + wirespec.q(coam['kraj']) + "\n"
      + "| okres =" + wirespec.q(coam['okres']) + "\n"
      + "| ob.roz.půs =" + wirespec.q(coam['ob.roz.půs']) + "\n"
      + "| pov.ob =" + wirespec.q(coam['pov.ob']) + "\n"
      + "| země =" + wirespec.q(coam['země']) + "\n"
      + "| výměra =" + wirespec.q(coam['výměra']) + "\n"
      + "| obyvatelé =" + wirespec.q(coam['obyvatelé']) + "\n"
      + "| početzsj =" + wirespec.q(coam['početzsj']) + "\n"
      + "| početč =" + wirespec.q(coam['početč']) + "\n"
      + "| početk =" + wirespec.q(coam['početk']) + "\n"
      + "| adresa =" + wirespec.q(coam['adresa']) + "\n"
      + "| web =" + wirespec.q(coam['web']) + "\n"
      + "| úř.web =" + wirespec.q(coam['úř.web']) + "\n"
      + "| email =" + wirespec.q(coam['email']) + "\n"
      + "| starosta =" + wirespec.q(coam['starosta']) + "\n"
      + "| foto = \n"
      + "| popisek.foto = \n"
      + "| znak =" + wirespec.q(coam['znak']) + "\n"
      + "| vlajka =" + wirespec.q(coam['vlajka']) + "\n"
      + "| mapa = \n"
      + "| NUTS5 = " + wirespec.trim(wirespec.q(coam['NUTS5'])).substr(7) + "\n"
      + "| NUTS4 =" + wirespec.q(coam['NUTS4']) + "\n"
      + "| NUTS3 =" + wirespec.q(coam['NUTS3']) + "\n"
      + "| části = \n"
      + "}}"
    ;

    return sidlo + ibom;
  },

  q: function(elem) {
    if (elem) {return elem;} else {return "";}
  },

  trim: function(str) {
    var i, startPos, endPos;

    startPos = 0;
    for (i = 0; i < str.length; ++i) {
      if (str.charCodeAt(i) <= 32)
        startPos = i + 1;
      else
        break;
    }

    endPos = str.length;
    for (i = str.length - 1; i >= 0; --i) {
      if (str.charCodeAt(i) <= 32)
        endPos = i;
      else
        break;
    }

    // alert ("" + startPos + "/" + endPos)
    return str.substr(startPos, endPos - startPos);
  },

  getBalancedExpression: function(buf, pos, openChar, closeChar) {
    var startPos, endPos;

    pos = buf.indexOf(openChar, pos);
    startPos = pos;
    ++pos;
    depth = 1;
    while (depth != 0 && pos < buf.length) {
      switch (buf.charAt(pos)) {
        case openChar: ++depth; break;
        case closeChar: --depth; break;
        // case '\\': ++pos; break;
      }
      ++pos;
    }
    endPos = pos;

    // return [startPos, endPos];
    return {startPos:startPos, endPos:endPos};
  }
};  // end of wirespec object

if (window.addOnloadHook) {
  addOnloadHook(function() { wire.init(); });  // when called from monobook.js on wiki
}
else {
  wire.init('/u');  // when called either as user script or from the bottom of html page
}
//</nowiki>