Difference between revisions of "MediaWiki:Vector.js"
From Hillvanden.com
(Created page with "- →Any JavaScript here will be loaded for users using the Vector skin: // Wiki user script to help maintain {{familytree}} or {{chart}} // boxes-and-lines diagrams, by allo...") |
Latest revision as of 20:29, 7 August 2013
/* Any JavaScript here will be loaded for users using the Vector skin */ // Wiki user script to help maintain {{familytree}} or {{chart}} // boxes-and-lines diagrams, by allowing you to edit the diagram // in a simpler and more standard ASCII art format. // Greg Ubben, 1 Dec 2008 // // To install, add: importScript("User:GregU/familytree.js"); // to your vector.js (or monobook.js) page. This adds an option // to the toolbox menu when editing familytrees. // // IE may work better than Firefox since it supports typeover mode. // // TODO: // - Anything we can do to improve [[WP:ACCESSIBILITY]] // - Some smarts with border/boxstyle // // Advanced ideas: // - Draw line between start and end of selection // - Cut/copy/paste rectangular selections (no existing library??) // - include overwrite/typeover mode emulation for Firefox // - Java GUI version where you drag boxes and lines on a grid addOnloadHook (function() { // wraps entire script var Summary = "Edited {{%s}} using [[User:GregU/familytree.js|familytree.js]]"; var Special = [ "border", "boxstyle", "colspan", "rowspan" ]; var Template; // familytree or chart ? var Style = null; var Center = 40; // center small diagrams on this column var Maxwidth = 80; var Picky = 0; // complain instead of self-correct? var rows; var boxes; // Add/replace convert option at top of toolbox menu on sidebar. // function update_menu (item) { var node = document.getElementById("t-diagram"); if (node) node.parentNode.removeChild(node); node = document.getElementById("t-whatlinkshere"); if (item == "wiki2art") addPortletLink ("p-tb", "javascript:wiki2art()", "Templates → Art", "t-diagram", "Convert {{" + Template + "}}... to ASCII art", "", node); if (item == "art2wiki") addPortletLink ("p-tb", "javascript:art2wiki()", "Art → Templates", "t-diagram", "Convert ASCII art back to {{" + Template + "}}...", "", node); } function wiki2art() { try { Style = null; var textarea = document.editform.wpTextbox1; var scroll_pos = textarea.scrollTop; var pattern = /\{\{(familytree|chart)\/start[\S\s]*?\{\{\w+\/end}}/ig; textarea.value = textarea.value.replace(pattern, wiki2art_replace); textarea.setAttribute("wrap", "off"); // work around problem with Firefox ignoring wrap (bug 302710) textarea.style.display = "block"; textarea.scrollTop = scroll_pos; // Mozilla only? update_menu ("art2wiki"); document.editform.wpSave.disabled = true; } catch (e) { alert ("Could not convert to ASCII art because:\n\n" + e); } } function wiki2art_replace (text, tmpl) { var rows = []; var parts = {}; if (text.indexOf("\n") == -1) return text; // don't convert a 1-line legend // Sanity check, if non-empty but no lines begin with {{. // if (text.search(/\n\s*\{\{.*\n/) == -1 && text.search(/\n\s*[^\s<].*\n/) != -1) { toss ("Out of sync; looks like this already is art."); return text; } Template = tmpl.toLowerCase(); Maxwidth = (Template == "chart" ? 50 : 80); Style = Style || new MarkupStyle(text); parse_templates (text, rows); var start = "{{" + rows.shift().join("|") + "}}\n"; var end = "{{" + rows.pop().join("|") + "}}"; layout_tiles (rows, parts); var art = pad_text( touchup( parts.art )); var width = art.indexOf("\n") / 2; width = (width > 50 && Maxwidth > 50) ? Maxwidth : 50; var ruler = Array(11).join("0-1-2-3-4-5-6-7-8-9-") .slice(0, width*2-1); return start + "\n" + ruler + "\n" + art + "\n" + parts.list + "\n" + end; } // Remember markup spacing styles based on first occurrences. // So to change the markup style, just change the first one // then toggle twice to "refresh". // function MarkupStyle (text) { this.initial = ""; this.lead = " "; this.equal = "="; var res; text = text || ""; text = text.replace(/^.*\n/, ""); // strip {{familytree/start}} res = text.match(/\w( *)\|/); if (res) { this.initial = res[1]; // space after template name? } res = text.match(/\|(\s*)\w{2,5}(\s*=\s*)[^\s=|}]/); if (res) { this.lead = res[1]; // params indented on new lines? this.equal = res[2]; } this.trail = (/\n/.test(this.lead) ? " " : ""); this.trim = (text.search(/\| \| (\|?}}|\|\s*\w+\s*=)/) == -1); this.param = function(name,value) { return this.lead + name + this.equal + value + this.trail; } } // Parse textual series of {{familytree|...|...}} templates // into a list of parameter lists. The parameters can contain // arbitrarily complex nested wiki syntax like [[foo|bar]] and // {{foo|bar|{{{1|baz}}}}} but this simple strategy of just // counting double brackets and braces should be good enough. // function parse_templates (text, rows) { var pattern = /([[\]{}])\1|\||<!--[\S\s]*?-->|<nowiki>[\S\s]*?<\/nowiki>/ig; var level = 0; var row, start, res; while ((res = pattern.exec(text)) != null) { if (res[1]) { (res[1]=="[" || res[1]=="{") ? level++ : level--; } if (res[0] == "{{" && level == 1) { row = []; start = res.index + 2; } if (res[0] == "|" && level == 1) { row.push(text.slice(start, res.index)); start = res.index + 1; } if (res[0] == "}}" && level == 0) { row.push(text.slice(start, res.index)); rows.push(row); } } if (level != 0) throw "Mismatched {{...}} or [[...]]"; } function layout_tiles (rows, parts) { var art = ""; var params = {}; var order = []; var specpat = new RegExp("^((" + Special.join("|") + ")_)\\s*(\\S.*)" ); // Tweak name so it is valid (matches namepat from map_boxes() // and is 2 to 5 characters long) and so it is unique if the // same name is used on several templates with different values. // Then store it in params{} and order[]. // // Could remember mappings in another hash, and change // back to original name on output (if original name not // already used on line). Probably best not to though. // function goodname (name, value) { var res, prefix="", nn; if (res = name.match(specpat)) { prefix = res[1]; name = res[3]; } nn = alias[name]; if (!nn) { // first encounter on this template nn = name; if (nn.search(/\w.*\w/) == -1 && value.search(/\w.*\w/) > -1) nn = value.toUpperCase(); nn = nn.replace( /[^\w.\/&]/g, "_"); nn = nn.replace( /_*([\W_])[\W_]*/g, "$1"); nn = nn.replace( /^[\W_]*(.{0,4}[^\W_]).*/, "$1"); nn = nn.replace( /^.?$/, "A0001"); var base = nn; var num = 1; while (nn in params && (params[nn] != value || prefix)) { num++; nn = base.slice(0, 5 - String(num).length) + num; } alias[name] = nn; } nn = prefix + nn; if (! (nn in params)) { order.push(nn); params[nn] = value; } return nn; } // FRANKLIN = Benjamin Franklin FRANK // FRANKLIN = Frank N. Furter FRAN2 boxstyle_FRANKLIN = red // FRANKLIN = Franklin Richards FRAN3 // FRANKLIN = Frank N. Furter boxstyle_FRANKLIN = blue for (var r=0; r < rows.length; r++) { var row = rows[r]; var seen = {}; var alias = {}; // mapped to different name on this row? if (row[0].search(/^\s*(familytree|chart)\s*$/i) == -1) throw "Unrecognized template {{" + row[0] + "}}"; for (var i=0; i < Special.length; i++) alias[Special[i]] = Special[i]; // don't truncate boxstyle // Pass 1: Do only the assignments first, because if the // same parameter name is used on a previous row with a // different value, then we need to rename this parameter // and its boxes before they are output. // for (var c=1; c < row.length; c++) { var cell = row[c]; var i = cell.indexOf("="); if (i < 0 || cell == "=") continue; var name = trim(cell.slice(0,i)); var value = trim(cell.slice(i+1)); if (value.indexOf("\n") >= 0) toss ('Parameter "' + name + '" spans multiple lines.'); value = value.replace(/\n\s*/g, " "); if (seen[name] && value != seen[name]) throw 'Parameter "' + name + '" has multiple values on template ' + (r+1); seen[name] = value; goodname(name, value); } // Pass 2: Now layout the tiles and boxes. // for (var c=1; c < row.length; c++) { var cell = trim(row[c]); if (istile(cell) && ! (cell in seen)) { art += pad(cell, 2); } else if (cell.indexOf("=") == -1) // it's a BOX { cell = goodname(cell, cell.replace(/_/g, " ")).slice(0,5); // Don't adjoin a {{chart}} wide cell if can avoid if (cell.length == 4 && /\w$/.test(art)) cell = " " + cell; art += (" "+cell+" ").substr(cell.length/2, 6); } } art += "\n"; } // list the parameter values, one per line // TODO: Styles referenced via [1], [2], etc var param_width = 5; for (var name in params) if (name.length > 8) param_width = 14; // any boxstyle_FOO ? var param_list = ""; while (name = order.shift()) { param_list += pad(name, param_width) + " = " + (params[name] || "") + "\n"; } parts.art = art; parts.list = param_list; } // Make the art more readable by converting some symbols. // Mainly just fills in --- and ~~~ horizontal lines for now. // 1. Fill in a ~ tile followed by a ~ tile or a box // 2. Fill in a box followed by a ~ tile // TOM - v - SUE becomes TOM ---v--- SUE // function touchup (art) { art = art.replace( /!/g, "|"); art = art.replace( /([,`^)}*+-]|\b[Xadijqrv]) (?=[.'^({*+-]|[acijlqrv]| ?\w\w)/g, "$1-"); art = art.replace( /([~%#\]]|\b[ADFLVfhy]) (?=[~%#[]|[7ACJKVXehy]| ?\w\w)/g, "$1~"); art = art.replace( /(\w\w ? ?) (?=[.'^({*+-]|[acijlqrv]\b)/g, "$1-"); art = art.replace( /(\w\w ? ?) (?=[~%#[]|[7ACJKVXehy]\b)/g, "$1~"); art = art.replace( /(\w\w ) (-|~)/g, "$1$2$2"); return art; } // Trim and pad a multi-line diagram with spaces to its maximum // width, adding a margin on both sides and a 1-line padded // margin above and below. Also tweaks the alignment if most // of the alignment indicators are mis-aligned on odd. // If margin is not given (wiki2art), it depends on the width. // function pad_text (text, margin) { // trim trailing spaces and leading and trailing lines text = text.replace(/\t/g, " "); // just in case text = text.replace(/ *\r*$/mg, ""); text = text.replace(/^\n*/, "\n"); text = text.replace(/\n*$/, "\n"); // trim indentation if not empty while (text.search(/(^|\n).?\S|^\s*$/) == -1) { text = text.replace(/^ /mg, ""); } var rows = text.split("\n"); var width = 0; var align = 0; var alignpat = /[^\w\s=~&\/\[\].-]|[A-Z0-9]+([\/&._]?[A-Z0-9])+/ig; var res; for (var i=0; i < rows.length; i++) { width = Math.max(width, rows[i].length); // Are majority of alignment indicators on odd or even? // while ((res = alignpat.exec(rows[i])) != null) { var len = res[0].length; if (len % 2) // even boxes are ambiguous ((res.index + len/2) & 1) ? align-- : align++; } } // If formatting for display, center diagram on column 40, but // at least a 4-cell left margin unless close to max width. // The margin gives room to draw another box on the left, and // you can then toggle view twice to indent another 4 cells. // if (margin == null) { margin = Center - width / 2; margin = Math.max(margin & ~1, 8); if (width/2 + margin > Maxwidth) margin = 0; } else if (align < 0) margin++; margin = pad("", margin); text = ""; for (var i=0; i < rows.length; i++) { text += margin + pad(rows[i], width) + margin + "\n"; } return text; } // Pad str with spaces on right to width len, but don't truncate. // function pad (str, len) { if (str.length < len) str += Array(len - str.length + 1).join(" "); return str; } function trim (str) { return str.replace(/^\s+|\s+$/g, ""); } function art2wiki() { try { var textarea = document.editform.wpTextbox1; var scroll_pos = textarea.scrollTop; var pattern = /\{\{(familytree|chart)\/start[\S\s]*?\{\{\w+\/end}}/ig; textarea.value = textarea.value.replace(pattern, art2wiki_replace); textarea.removeAttribute("wrap"); textarea.style.display = "inline"; // Firefox work-around textarea.scrollTop = scroll_pos; // Firefox only? document.editform.wpSave.disabled = false; update_menu ("wiki2art"); if (document.editform.wpSummary.value.search(/^(\/\* .* \*\/)? *$/) == 0) document.editform.wpSummary.value += Summary.replace("%s", Template); } catch (e) { alert ("Could not convert ASCII art because:\n\n" + e); } } function art2wiki_replace (text, tmpl) { var label = {}; var param_rows = []; Template = tmpl.toLowerCase(); rows = []; boxes = []; if (text.indexOf("\n") == -1) return text; // don't convert a 1-line legend // Sanity check, if any lines begin with {{... // if (text.search(/\n\s*\{\{.*\n/) != -1) { toss ("Out of sync; looks like this already is wikitext."); return text; } var res = text.match(/^(.*}})([\S\s]*)\{\{/); if (res == null) throw "Didn't find end of /start tag on same line"; parse_art (res[2], label,rows); map_boxes (rows, boxes); map_tiles (boxes,rows, param_rows); crop_rows (param_rows); var temps = to_wikitext (label, param_rows); var start = summarize (res[1], boxes.count); return start + "\n" + temps + "{{" + tmpl + "/end}}"; } // Parse the simple ASCII art, storing the diagram in // rows[] and the labels in label{} // function parse_art (text, label,outrows) { // remove any rulers or comments (messages) text = text.replace(/^.*1-2-3-4-5-6-7-8-9.*\n/mg, ""); text = text.replace(/^ *\/\/.*/mg, ""); // Parse the name=value definitions into label{}. // We're as flexible as possible, allowing defs // with no RHS, defs in multiple columns, and // defs quickly jotted to the right of the art. // However, a value cannot span lines. And assume // foo===bar is part of the art, where === is ---. // AAA=Freddy overrides AAA=AAA overrides AAA= // text = text.replace(/([^\s=]+) *=(?!=) *(.*?)(\t| (?=.*\w.*=)| *$)/mg, function (str,name,value) { if (! /\w/.test(name)) // art return str; if (! label[name] || label[name] == name && value) label[name] = value; if (value != label[name] && value != name && value) throw 'Parameter "' + name + '" has multiple values.'; return ""; }); // Treat ..... same as ~~~~~ text = text.replace(/\.{3,}/g, function(s){ return s.replace(/./g, "~"); }); text = pad_text(text, 4); var a = text.slice(0,-1).split("\n"); while (a.length) outrows.push(a.shift()); // At this point, outrows[] should contain the diagram padded // to the maximum width with two extra blank cells on each // side (1 box overlap + 1 neighbor) and with the vertical // lines aligned on the even characters (assuming diagram is // consistent in this). } // Find which cells are occupied by boxes, even if the box // names are real short (must be at least 2 characters) or // real long. Doing this first makes processing the tiles // easier. Returns the 2D boxes array. // function map_boxes (rows, boxes) { var namepat = /[A-Z0-9]+([\/&._]?[A-Z0-9])+/ig; var row, map, res, name, pos; boxes.count = 0; for (var i=0; i < rows.length; i++) { row = rows[i]; map = new Array(row.length); while ((res = namepat.exec(row)) != null) { name = res[0]; // Handle cases where wide {{chart}} tiles look like boxes. // If it looks like they could be tiles, then they're tiles, // else they're boxes. We rely on user to not use ambiguous // box names like a2b2c (though names like a2 and a2b should // actually work as long as they remain aligned on odd). // if (Template == "chart" && res.index % 2 == 0) { while (name.search(/^[a-z]2[^\W_].../) == 0) { name = name.slice(2); res.index += 2; } // Tiles: m2 m2P m2n2 m2n2P Boxes: m2ab m2abc m2Pn2 if (name.search(/^([a-z]2)*.?$/) == 0) continue; // Also allow convenience shortcut of SPPPRPPPPPP // to be used as alternative to S P R P P P if (name.search(/^(?=.*PPP)([bmnoPSYWMHR]P){3,}.?$/) == 0) continue; } // Even allow on odd alignment if it's all PPPPPPPPPPPs if (Template == "chart" && name.search(/^P{6,}.$/) == 0) continue; if (name.length % 2 == 1 && res.index % 2 == 0) toss (name + " is aligned ambiguously"); pos = (res.index + name.length / 2) & ~1; if (map[pos-2]) throw "box [" + name + "] overlaps [" + map[pos-2] + "]"; map[pos-2] = name; map[pos] = name; map[pos+2] = name; // Blank out the name. If it's a long name (>5) and // a horizontal line joins it, extend the line into // the extra space from shortening the name. var before = row.slice(0, res.index); var blank = name.replace(/./g, " "); var after = row.slice(res.index + name.length); var half = name.length / 2; if (res = before.match(/(-|~) ?$/)) blank = Array((half+1)|0).join(res[1]) + blank.slice(half); if (res = after.match(/^ ?(-|~)/)) blank = blank.slice(0,half) + Array((half+1.6)|0).join(res[1]); row = before + blank + after; boxes.count++; if (row.slice(pos-2, pos+3).search(/[^\s[\]P~=_-]/) >= 0) toss ("A tile overlaps box [" + name + "]"); } boxes.push(map); rows[i] = row; } } function map_tiles (boxes,rows, param_rows) { Tile.invert_symbols(); for (var r=1; r < rows.length-1; r++) { var row = rows[r]; var params = []; var res = row.match(/^.(..)*?([^\s[\]P~=_-])/); if (res) toss (res[2] + " is mis-aligned on row " + r); for (var c=2; c < row.length-2; c += 2) { if (boxes[r][c]) { params.push( boxes[r][c] ); c += 4; } else { var t = new Tile(r,c); t.tweak(r-1, c, 0); t.tweak(r+1, c, 2); t.tweak(r, c-2, 3); t.tweak(r, c+2, 1); params.push( t.symbol() ); } } param_rows.push(params); } } // Crop unneeded spaces from beginnings and ends of parameter // lists if entire columns are unused. The rows are assumed // to be the same virtual width. If a margin is desired, use // {{familytree/start| style=margin:1em}}, not empty rows/columns. // // (In rare cases there could also be leading/trailing rows that // are empty, but don't crop them. Should only happen if these // lines were blank exept for character(s) in the odd cells. // Which shouldn't happen by accident.) // function crop_rows (rows) { var min = 9999; var max = 0; // Find first and last columns used // for (var r=0; r < rows.length; r++) { var params = rows[r]; var col = 0; // virtual column / width var first = 9999; // first used column var last = 0; // last used column for (var i=0; i < params.length; i++) { var param = params[i]; if (param != ' ' && first > col) first = col; if (! istile(param)) col += 2; // it's a 3-wide box if (param != ' ') last = col; col++; } min = Math.min(min, first); max = Math.max(max, last); } if (min > max) return; // all blank var extra = col - max - 1; // amount to trim on right // Now crop leading and trailing params in blank columns. // Though the param list lengths vary, their virtual widths // should all be the same, and will continue to be consistent // after shaving the same amount off of each end. // for (r=0; r < rows.length; r++) { rows[r].splice(0, min); rows[r].splice(rows[r].length - extra, extra); } } function to_wikitext (label, rows) { var style = Style || new MarkupStyle(); var result = ""; var first_part = "{{" + Template + style.initial; var label_used = {}; var i, attr; for (i=0; i < Special.length; i++) { attr = Special[i]; if (attr in label) { first_part += "|" + attr + "=" + label[attr]; label_used[attr] = 1; } } for (var r=0; r < rows.length; r++) { var params = rows[r]; var seen = {}; var last_part = ""; var param; result += first_part; while (param = params.shift()) { result += "|"; if (istile(param) && !(param in label)) { result += param; continue; } if (! (param in seen)) { seen[param] = 1; if (param in label) { last_part += "|" + style.param(param, label[param]); label_used[param] = 1; } for (i=0; i < Special.length; i++) { attr = Special[i] + "_" + param; if (attr in label) { last_part += "|" + style.param(attr, label[attr]); label_used[attr] = 1; seen[param] = 2; } } } // If param.length < 5, center it so it looks better. // Unless it's used in any per-box attributes like boxstyle_FOO, // in which case it must be flush left to work correctly. if (seen[param] == 2 || param.length > 5) result += pad(param, 5); else result += (" "+param+" ").substr(param.length/2, 5); } if (style.trim) result = result.replace(/(\| )+$/g, ""); // trim empty cells result += last_part + "}}\n"; } var unused = ""; for (i in label) { if (! (i in label_used) && label[i] && label[i] != i) unused += "|" + style.param(i, label[i]); } if (unused) result += "<!-- Unused parameters: -->\n" + "{{" + Template + style.initial + unused + "}}\n"; return result; } // Create a slightly more useful summary than the default. // The user is hoped to revise this to a more meaningful summary // than can be calculated automatically. For example: // // summary = Family tree diagram for Barack Obama, connecting // 29 individuals in 4 generations. Generations are // arranged in rows, with Barack appearing 3rd on the // 3rd such row. // function summarize (tag, count) { if (tag.search(/\|\s*summary\s*=/) == -1) tag = tag.replace(/}}$/, "| summary=Boxes and lines diagram with " + count + " boxes}}"); else tag = tag.replace(/\d+(?= (boxes|nodes|individuals))/, count); return tag; } function istile (sym) { return sym.length <= 1 || Template == "chart" && /^[a-z]2$/.test(sym); } function Tile(r,c) { var a = get_tile(r,c); this.orig_sym = a[0]; this.sides = a[1].slice(0,4); // copy vs ref this.weight = a[1][4]; // If edge is a line but next tile not same with > weight, change it // If edge is blank but next tile is line with >= weight, change it // this.tweak = function (r,c,dir) { var neighbor = get_tile(r,c); var specs = neighbor[1]; var ne_line = specs[dir ^ 2]; var us_line = this.sides[dir]; if (us_line > 0 && ne_line != us_line && specs[4] > this.weight || us_line == 0 && ne_line > 0 && specs[4] >= this.weight) this.sides[dir] = ne_line; } this.symbol = function() { var ch = new_symbol[this.sides]; if (ch == null || /[ :~!-]/.test(ch)) ch = this.orig_sym; return ch; } function get_tile(r,c) { if (boxes[r][c]) return ["BOX", [0, 0, 0, 0, 20]]; var ch = rows[r].charAt(c); var ch2 = rows[r].charAt(c+1); if (/[ P_=~-]/.test(ch) && /[^ [\]P_=~-]/.test(ch2)) // mis-aligned? ch = ch2; if (/\w/.test(ch) && ch2 == '2') // {{chart}} long symbol? ch += '2'; if (ch == '|' || ch == '1') ch = '!'; if (ch == '_' || ch == '=') ch = '-'; var specs = symbols[ch] || [0, 0, 0, 0, 20]; if (specs.length > 5 && Template == "chart") // t, T, k, G specs = specs.slice(5); return [ch, specs]; } } // Build reverse lookup table needed by Tile objects. // There is some conflict between the {{familytree}} and {{chart}} symbols. // A few recently-added symbols map to different specs, and some specs // map back to different symbols. Hence the extra logic here depending // on the current Template family. // Tile.invert_symbols = function() { new_symbol = {}; var start = (Template == "chart") ? -5 : 0; for (var sym in symbols) { var nesw = symbols[sym].slice(start,start+4).join(); if (! (nesw in new_symbol) || Template == "chart") new_symbol[nesw] = sym; } } function toss (msg) // Soft throw. { if (Picky) throw msg; } // I haven't tuned many of these weights yet. // Hopefully we won't need to go to per-edge weights. // // Doubt: // 0 space // 1 ^ v ( ) // 2 - ! ~ : // 3 + . , ' ` / \ BOX var new_symbol = {}; var symbols = { // N, E, S, W, Weight " " : [ 0, 0, 0, 0, 90 ], "-" : [ 0, 1, 0, 1, 50 ], "!" : [ 1, 0, 1, 0, 50 ], "+" : [ 1, 1, 1, 1, 20 ], "," : [ 0, 1, 1, 0, 20 ], "." : [ 0, 0, 1, 1, 20 ], "`" : [ 1, 1, 0, 0, 20 ], "'" : [ 1, 0, 0, 1, 20 ], "^" : [ 1, 1, 0, 1, 70 ], "v" : [ 0, 1, 1, 1, 70 ], "(" : [ 1, 0, 1, 1, 70 ], ")" : [ 1, 1, 1, 0, 70 ], "~" : [ 0, 2, 0, 2, 50 ], ":" : [ 2, 0, 2, 0, 50 ], "%" : [ 2, 2, 2, 2, 20 ], "F" : [ 0, 2, 2, 0, 20 ], "7" : [ 0, 0, 2, 2, 20 ], "L" : [ 2, 2, 0, 0, 20 ], "J" : [ 2, 0, 0, 2, 20 ], "A" : [ 2, 2, 0, 2, 70 ], "V" : [ 0, 2, 2, 2, 70 ], "C" : [ 2, 0, 2, 2, 70 ], "D" : [ 2, 2, 2, 0, 70 ], "*" : [ 2, 1, 2, 1, 51 ], "#" : [ 1, 2, 1, 2, 51 ], // don't tweak ---#--- "h" : [ 1, 2, 0, 2, 33 ], "y" : [ 0, 2, 1, 2, 33 ], "{" : [ 2, 0, 2, 1, 33 ], "}" : [ 2, 1, 2, 0, 33 ], "t" : [ 2, 1, 0, 1, 33, 1, 2, 1, 2, 51 ], "[" : [ 1, 0, 1, 2, 33 ], "]" : [ 1, 2, 1, 0, 33 ], "X" : [ 2, 1, 2, 2, 33 ], "T" : [ 0, 1, 2, 2, 33, 0, 0, 3, 3, 20 ], "K" : [ 2, 0, 1, 2, 33 ], "k" : [ 1, 0, 2, 2, 33, 3, 1, 3, 0, 33 ], "G" : [ 2, 2, 1, 0, 33, 3, 0, 3, 3, 70 ], // chart "P" : [ 0, 3, 0, 3, 50 ], "Q" : [ 3, 0, 3, 0, 50 ], "R" : [ 3, 3, 3, 3, 20 ], "S" : [ 0, 3, 3, 0, 20 ], "Y" : [ 3, 3, 0, 0, 20 ], "Z" : [ 3, 0, 0, 3, 20 ], "W" : [ 3, 3, 0, 3, 70 ], "M" : [ 0, 3, 3, 3, 70 ], "H" : [ 3, 3, 3, 0, 70 ], "c" : [ 2, 0, 2, 1, 33 ], "d" : [ 2, 1, 2, 0, 33 ], "i" : [ 2, 1, 0, 1, 33 ], "j" : [ 0, 1, 2, 1, 33 ], "e" : [ 1, 0, 1, 2, 33 ], "f" : [ 1, 2, 1, 0, 33 ], "a" : [ 3, 1, 3, 1, 51 ], "b" : [ 1, 3, 1, 3, 51 ], // don't tweak ---b--- "l" : [ 3, 0, 3, 1, 33 ], "m" : [ 0, 3, 1, 3, 33 ], "n" : [ 1, 3, 0, 3, 33 ], "o" : [ 1, 3, 1, 0, 33 ], "p" : [ 1, 0, 1, 3, 33 ], "q" : [ 3, 1, 0, 1, 33 ], "r" : [ 0, 1, 3, 1, 33 ], "a2" : [ 3, 2, 3, 2, 54 ], "b2" : [ 2, 3, 2, 3, 54 ], "k2" : [ 3, 2, 3, 0, 44 ], "l2" : [ 3, 0, 3, 2, 44 ], "m2" : [ 0, 3, 2, 3, 44 ], "n2" : [ 2, 3, 0, 3, 44 ], "o2" : [ 2, 3, 2, 0, 44 ], "p2" : [ 2, 0, 2, 3, 44 ], "q2" : [ 3, 2, 0, 2, 44 ], "r2" : [ 0, 2, 3, 2, 44 ] }; window.wiki2art = wiki2art; // expose to HTML link window.art2wiki = art2wiki; if (document.editform) { var textbox = document.editform.wpTextbox1; var res = textbox.value.match(/\{\{(familytree|chart)\/start[\S\s]*\{\{\w+\/end/i); if (res) { Template = res[1]; if (res[0].search(/^\s*\{\{(familytree|chart)\s*\|/mi) > 0) update_menu ("wiki2art"); else update_menu ("art2wiki"); } } } ); // end of script and addOnloadHook() wrapper