Skillnad mellan versioner av "MediaWiki:Monobook.js"

Från LuggudeWiki
Hoppa till:navigering, sök
(Skapade sidan med 'JavaScript här kommer att laddas för dem som använder skalet Monobook: // Wiki user script to help maintain {{familytree}} or {{chart}} // boxes-and-lines diagrams, by al...')
 
 
(4 mellanliggande versioner av samma användare visas inte)
Rad 1: Rad 1:
/* JavaScript här kommer att laddas för dem som använder skalet Monobook */
+
importScript("User:Martinbrandt/familytree.js");
// 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 monobook.js file.  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
 

Nuvarande version från 28 november 2010 kl. 13.09

importScript("User:Martinbrandt/familytree.js");