Nota: Después de guardar, debes recargar la caché de tu navegador para ver los cambios:

  • Mozilla: Pulsa Recargar (o Ctrl-R)
  • Internet Explorer / Opera: Ctrl-F5
  • Safari: Cmd-R
  • Konqueror Ctrl-R.
/* Este script proporciona funciones para recibir los parámetros {{tl|Diálogo/button}}, y para registrar las dependencias de la página.
Cualquier página JavaScript que importe esta página también debe contener un enlace comentado, [[Especial:LoQueEnlazaAquí/{{FULLPAGENAME}}]].

Para instrucciones, ver la [[{{TALKPAGENAME}}|página de discusión]]. [[Categoría:Wikinoticias:Páginas de diálogo JS|receive]] */

if (! window.wikidialog) window.wikidialog = {};
if (! window.wikidialog.receiveAnonymous) (function () {
    //
    // version
    //
    function receiveVersion() { return '0.4 (en español) (2017-02-05)'; }
    //
    window.wikidialog.safeHtml = function (s) {
        return s.replace(/\</g,'&lt;').replace(/\>/g,'&gt;');
    };
    function getWikidialogID() {
        var s = location.search.match(/[\?&]wikidialogid=(\d+)/);
        if (s && s.length && (s.length > 1)) {
            var thisID = Number(s[1])
            if (sessionStorage) {
                var nextID = 0;
                if (sessionStorage.wikidialogNextID) { nextID = Number(sessionStorage.wikidialogNextID); }
                if (nextID <= thisID) { sessionStorage.wikidialogNextID = (thisID + 1); }
            }
            return thisID;
        }
        if (sessionStorage) {
            if (! sessionStorage.wikidialogNextID) sessionStorage.wikidialogNextID = '1';
            return (Number(sessionStorage.wikidialogNextID) - 1);
        }
        return 0;
    }
    function getData() {
        var id = getWikidialogID();
        var data = {};
        var params = sessionStorage['wikidialog:' + id + ':params'];
        if (params) {
            params = params.split(',');
            for (var k = 0; k < params.length; k++) if (params[k]) data[params[k]] = sessionStorage['wikidialog:' + id + ':in:' + params[k]];
        }
        return data;
    }
    function saveGeometries(id, tag, carry) {
        var areas = $('input.wikidialog-text, textarea.wikidialog-textarea').map(function () {
            var t = $(this);
            if (t.css('display') == 'none') return;
            return t;
        });
        if ((typeof carry) == "object") {
            areas = areas.map(function () {
                var t = $(this);
                var tid = t.attr('id');
                for (var k = 0; k < carry.length; k++)
                    if (tid == ('wikidialog-' + carry[k]))
                        return t;
                return;
            });
        }
        areas.each(function () {
            var t = $(this);
            var name = t.attr('id').substring(11);
            sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':scroll-top' ] = t.scrollTop();
            sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':scroll-left'] = t.scrollLeft();
            sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':height'     ] = t.height();
            sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':width'      ] = t.width();
        });
    }
    function collectGeometry(id, tag, name) {
        sessionStorage.removeItem('wikidialog:' + id + ':' + tag + ':' + name + ':scroll-top');
        sessionStorage.removeItem('wikidialog:' + id + ':' + tag + ':' + name + ':scroll-left');
        sessionStorage.removeItem('wikidialog:' + id + ':' + tag + ':' + name + ':height');
        sessionStorage.removeItem('wikidialog:' + id + ':' + tag + ':' + name + ':width');
    }
    function restoreGeometries(id, tag) {
        $('input.wikidialog-text, textarea.wikidialog-textarea').each(function () {
            var t = $(this);
            if (t.css('display') == 'none') return;
            var name = t.attr('id').substring(11);
            var st = sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':scroll-top' ];
            var sl = sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':scroll-left'];
            var ht = sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':height'     ];
            var wd = sessionStorage['wikidialog:' + id + ':' + tag + ':' + name + ':width'      ];
            if (st) t.scrollTop( st);
            if (sl) t.scrollLeft(sl);
            if (ht) t.height(    ht);
            if (wd) t.width(     wd);
            collectGeometry(id, tag, name);
        });
    }
    function addNote(s) {
        var id = getWikidialogID();
        var note = sessionStorage['wikidialog:' + id + ':notes'];
        s = 'Nota: ' + window.wikidialog.safeHtml(s) + '<br/>';
        sessionStorage['wikidialog:' + id + ':notes'] = (note ? (note + s) : s);
    }
    function clearNotes() {
        var id = getWikidialogID();
        sessionStorage.removeItem('wikidialog:' + id + ':notes');
        sessionStorage.removeItem('wikidialog:' + id + ':keep-notes');
    }
    function getNotes() {
        return sessionStorage['wikidialog:' + getWikidialogID() + ':notes'];
    }
    function releaseSavedFieldValues(sfx, id) {
        if (! sfx) sfx = ''; // precaution
        if (! id) id = getWikidialogID(); // default
        var ls = sessionStorage['wikidialog:' + id + ':saves' + sfx];
        if (! ls) ls = ''; // precaution
        ls = ls.split(',');
        var k;
        for (k = ls.length - 1; k>=0; k--) {
            sessionStorage.removeItem('wikidialog:' + id + ':sv' + sfx + ':' + ls[k]);
            collectGeometry(id, ('sv' + sfx), ls[k]);
        }
        sessionStorage.removeItem('wikidialog:' + id + ':saves' + sfx);
    }
    function activateSavedFieldValues(sfx, id) {
        if (! sfx) return;
        if (! id) id = getWikidialogID(); // default
        releaseSavedFieldValues('', id);
        var ls = sessionStorage['wikidialog:' + id + ':saves' + sfx];
        if (! ls) ls = ''; // precaution
        ls = ls.split(',');
        for (var k = ls.length - 1; k>=0; k--) {
            sessionStorage[           'wikidialog:' + id + ':sv:'            + ls[k]] =
                sessionStorage[       'wikidialog:' + id + ':sv' + sfx + ':' + ls[k]];
            sessionStorage[           'wikidialog:' + id + ':sv:'            + ls[k].substring(11) + ':scroll-top'] =
                sessionStorage[       'wikidialog:' + id + ':sv' + sfx + ':' + ls[k].substring(11) + ':scroll-top'];
            sessionStorage[           'wikidialog:' + id + ':sv:'            + ls[k].substring(11) + ':scroll-left'] =
                sessionStorage[       'wikidialog:' + id + ':sv' + sfx + ':' + ls[k].substring(11) + ':scroll-left'];
            sessionStorage[           'wikidialog:' + id + ':sv:'            + ls[k].substring(11) + ':height'] =
                sessionStorage[       'wikidialog:' + id + ':sv' + sfx + ':' + ls[k].substring(11) + ':height'];
            sessionStorage[           'wikidialog:' + id + ':sv:'            + ls[k].substring(11) + ':width'] =
                sessionStorage[       'wikidialog:' + id + ':sv' + sfx + ':' + ls[k].substring(11) + ':width'];
            // don't remove yet; hopefully no field is listed more than once,
            // but if it is we don't want that to cause misbehavior here
        }
        for (var k = ls.length - 1; k>=0; k--) {
            sessionStorage.removeItem('wikidialog:' + id + ':sv' + sfx + ':' + ls[k]);
            collectGeometry(id, ('sv' + sfx), ls[k]);
        }
        sessionStorage['wikidialog:' + id + ':saves'] = sessionStorage['wikidialog:' + id + ':saves' + sfx];
        sessionStorage.removeItem('wikidialog:' + id + ':saves' + sfx);
    }
    function saveFieldValues(id, sfx) {
        if (! sfx) sfx = ''; // precaution
        var s = '';
        var n = 64; // SINGLE POINT TO ADJUST max number of fields to save
        $('input.wikidialog-text, textarea.wikidialog-textarea, select.wikidialog-select').each(function () {
            var t = $(this);
            if (t.css('display') == 'none') return;
            n--;
            if (n <= 0) return;
            var tname = t.attr('id');
            var tvalue = t.val();
            sessionStorage['wikidialog:' + id + ':sv' + sfx + ':' + tname] = tvalue;
            if (s) s += ',';
            s += tname;
        });
        saveGeometries(id, ('sv' + sfx));
        $('input.wikidialog-checkbox').each(function () {
            n--;
            if (n <= 0) return;
            var t = $(this);
            var tname = t.attr('id');
            var tchecked = (t[0].checked ? 'yes' : '');
            sessionStorage['wikidialog:' + id + ':sv' + sfx + ':' + tname] = tchecked;
            if (s) s += ',';
            s += tname;
        });
        sessionStorage['wikidialog:' + id + ':saves' + sfx] = s;
    }
    function restoreSavedFieldValues() {
        var id = getWikidialogID();
        var ls = sessionStorage['wikidialog:' + id + ':saves'];
        if (! ls) ls = ''; // precaution;
        ls = ls.split(',');
        $('input.wikidialog-text, textarea.wikidialog-textarea').each(function () {
            var n = $(this);
            var ni = n.attr('id');
            var k;
            for (k = ls.length - 1; k>=0; k--) if (ls[k] == ni) {
                var sv = sessionStorage['wikidialog:' + id + ':sv:' + ni];
                if (! sv) sv = ''; // precaution
                n.val(sv);
                break;
            }
        });
        restoreGeometries(id, 'sv');
        $('input.wikidialog-checkbox').each(function () {
            var n = $(this);
            var ni = n.attr('id');
            var k;
            for (k = ls.length - 1; k>=0; k--) if (ls[k] == ni) {
                var sv = sessionStorage['wikidialog:' + id + ':sv:' + ni];
                if (sv) n.attr('Seleccionado','Sí');
                else n.removeAttr('Seleccionado');
                break;
            }
        });
        $('select.wikidialog-select').each(function () {
            var n = $(this);
            var ni = n.attr('id');
            for (k = ls.length - 1; k>=0; k--) if (ls[k] == ni) {
                var sv = sessionStorage['wikidialog:' + id + ':sv:' + ni];
                var k;
                for (var j=0; j<this.length; j++)
                    if (this[j].value == sv) {
                        this.selectedIndex = j;
                        break;
                    }
                break;
            }
        });
    }
    function clearRollbackDelegating() {
        var id = getWikidialogID();
        if (sessionStorage['wikidialog:' + id + ':rb-available']) {
            sessionStorage.removeItem('wikidialog:' + id + ':rb-available');
            //
            sessionStorage.removeItem('wikidialog:' + id + ':rb-incoming');
            sessionStorage.removeItem('wikidialog:' + id + ':rb-unauth');
            sessionStorage.removeItem('wikidialog:' + id + ':rb-origin');
            sessionStorage.removeItem('wikidialog:' + id + ':rb-proxy');
            //
            sessionStorage.removeItem('wikidialog:' + id + ':rb-added');
            //
            ls = sessionStorage['wikidialog:' + id + ':rb-changed'];
            if (! ls) ls = ''; // precaution;
            ls = ls.split(',');
            for (k = ls.length - 1; k>=0; k--) sessionStorage.removeItem('wikidialog:' + id + ':rb-in:' + ls[k]);
            sessionStorage.removeItem('wikidialog:' + id + ':rb-changed');
            //
            releaseSavedFieldValues('-rb', id);
        }
    }
    function clearRollbackSimple() {
        var id = getWikidialogID();
        if (sessionStorage['wikidialog:' + id + ':prevurl']) {
            sessionStorage.removeItem('wikidialog:' + id + ':prevurl');
            clearRollbackDelegating();
        }
    }
    function getStableUrl(unforced) {
        var currentUrl = (location.pathname + location.search);
        if (unforced) {
            return currentUrl;
        } else {
            var newUrl = mw.util.getUrl(mw.config.get('wgPageName')) + '?wikidialogid=' + getWikidialogID();
            if (newUrl == currentUrl) {
                newUrl += '&x=x';
            }
            return newUrl;
        }
    }
    function stashUnstableUrl() {
        //
        // if we're here, and there's no id in the url, by definition it's unstable
        //
        var s = location.search.match(/[\?&]wikidialogid=(\d+)/);
        if (s && s.length && (s.length > 1)) {
            sessionStorage.removeItem('wikidialogUnstableUrl');
        } else {
            sessionStorage.wikidialogUnstableUrl = location.href;
        }
    }
    function delegateVerified(data, metaproc, shortcut, shortcutParam) {
        var olddata = getData();
        var id = getWikidialogID();
        if (data) for (var p in data) {
            sessionStorage['wikidialog:' + id + ':in:' + p] = data[p];
            var params = sessionStorage['wikidialog:' + id + ':params'];
            if (! params) params = ''; // robust against undefined, which may arise during query-url conversion
            if (! (p in olddata)) sessionStorage['wikidialog:' + id + ':params'] = params + ',' + p + ',';
        }
        var carry = '';
        if (metaproc) {
            var metadata = metaproc({
                    unauth: sessionStorage['wikidialog:' + id + ':unauth'],
                    origin: sessionStorage['wikidialog:' + id + ':origin'],
                    proxy:  sessionStorage['wikidialog:' + id + ':proxy']
                });
            sessionStorage['wikidialog:' + id + ':unauth'] = ((('unauth' in metadata) && metadata.unauth) ? metadata.unauth : '');
            sessionStorage['wikidialog:' + id + ':origin'] = ((('origin' in metadata) && metadata.origin) ? metadata.origin : '');
            sessionStorage['wikidialog:' + id + ':proxy' ] = ((('proxy'  in metadata) && metadata.proxy ) ? metadata.proxy  : '');
            carry                                          = ((('carry'  in metadata) && metadata.carry ) ? metadata.carry  : '');
        }
        sessionStorage['wikidialog:' + id + ':incoming'] = getStableUrl(shortcut);
        if (sessionStorage['wikidialog:' + id + ':keep-notes']) {
            sessionStorage.removeItem('wikidialog:' + id + ':keep-notes');
        } else {
            clearNotes();
        }
        saveGeometries(id, 'in', carry);
        if (shortcut) shortcut(shortcutParam);
        else location = getStableUrl(shortcut);
    }
    function authenticIncoming() {
        var id = getWikidialogID();
        var origin = sessionStorage['wikidialog:' + id + ':origin'];
        if (! origin) origin = ''; // precaution
        else origin = mw.util.getUrl(origin);
        var proxy = sessionStorage['wikidialog:' + id + ':proxy'];
        if (! proxy) proxy = ''; // precaution
        else proxy = mw.util.getUrl(proxy);
        var someFound = false;
        var noneFailed = true;
        $('span.wikidialog-origin').each(function () { // [[Plantilla:Diálogo/require origin]]
            someFound = true;
            var s = $(this).text();
            s = s.split('&');
            if (s.length == 1) return; // [[Plantilla:Diálogo/null requirement]]
            if (origin) {
                if (s[1]) s[1] = mw.util.getUrl(s[1]);
                for (var k = 2; (k < s.length); k++) s[k] = mw.util.getUrl(s[k]);
                if (s[1] == proxy) for (var k = 2; (k < s.length); k++) if (s[k] == origin) return;
            }
            noneFailed = false;
            return false;
        });
        if (! someFound) {
            addNote('No hay autenticación saliente porque no se encontró una cláusula de requisito.');
        } else if (! noneFailed) {
            addNote('No hay autenticación saliente porque falló una cláusula de requisito.');
        }
        return (someFound && noneFailed);
    }
    function registerAuthentication(origin, callback) {
        var proxy = '';
        if (! origin) origin = mw.config.get('wgPageName');
        else if ((typeof origin) == 'function') {
            callback = origin;
            origin = mw.config.get('wgPageName');
        } else proxy = mw.config.get('wgPageName');
        $('span.wikidialog-button, input.wikidialog-button, button.wikidialog-button').each(function () {
            var button = $(this);
            var wikidialogButtonData = button.data();
            if ('wikidialogButtonData' in wikidialogButtonData)
                wikidialogButtonData = wikidialogButtonData.wikidialogButtonData;
            else wikidialogButtonData = {};
            wikidialogButtonData.origin = origin;
            if (proxy) wikidialogButtonData.proxy = proxy;
            button.data('wikidialogButtonData',wikidialogButtonData);
        });
        if (callback) callback();
    }
    function registerUnauthenticated(requesting) {
        $('span.wikidialog-button, input.wikidialog-button, button.wikidialog-button').each(function () {
            var button = $(this);
            var wikidialogButtonData = button.data();
            if ('wikidialogButtonData' in wikidialogButtonData)
                wikidialogButtonData = wikidialogButtonData.wikidialogButtonData;
            else wikidialogButtonData = {};
            wikidialogButtonData.unauth = requesting;
            button.data('wikidialogButtonData',wikidialogButtonData);
        });
    }
    function maybeCollect(activeList, stack) {
        function isActive(id) {
            for (var k = activeList.length - 1; k>=0; k--   ) if (activeList[k] == id) return true;
            for (var k = stack.length - 2;      k>=0; k -= 2) if (stack[k]      == id) return true;
            return false;
        }
        for (var id = Number(sessionStorage.wikidialogNextID) - 1; id>=0; id--) if (! isActive(id)) {
            sessionStorage.removeItem('wikidialog:' + id + ':incoming');
            sessionStorage.removeItem('wikidialog:' + id + ':unauth');
            sessionStorage.removeItem('wikidialog:' + id + ':origin');
            sessionStorage.removeItem('wikidialog:' + id + ':proxy');
            sessionStorage.removeItem('wikidialog:' + id + ':newparams');
            var ls = sessionStorage['wikidialog:' + id + ':params'];
            if (! ls) ls = ''; // precaution;
            ls = ls.split(',');
            for (var k = ls.length - 1; k>=0; k--) {
                sessionStorage.removeItem('wikidialog:' + id + ':in:' + ls[k]);
                collectGeometry(id,'in',ls[k]);
            }
            sessionStorage.removeItem('wikidialog:' + id + ':params');
        }
    }
    var maxIDsKept = 4; // SINGLE POINT TO ADJUST number of active IDs kept
    function updateActiveList() {
        var id = getWikidialogID();
        var a = sessionStorage.wikidialogActiveList;
        if (! a) {
            // rather than use specialized code here, drop into the general case
            a = ('' + id);
        }
        a = a.split(',');
        var s = sessionStorage.wikidialogStack;
        if (! s) s = ''; // precaution
        s = s.split('&');
        var k;
        for (k=0; k<a.length; k++)
            if (a[k] == id) {
                for (; k>0; k--) a[k] = a[k - 1];
                a[0] = id;
                sessionStorage.wikidialogActiveList = a.join(',');
                maybeCollect(a, s);
                return;
            }
        if (a.length < maxIDsKept) {
            sessionStorage.wikidialogActiveList = id + ',' + a.join(',');
            a[a.length] = id;
            maybeCollect(a, s);
            return;
        }
        for (k = a.length - 1; k>0; k--) a[k] = a[k-1];
        a[0] = id;
        sessionStorage.wikidialogActiveList = a.join(',');
        maybeCollect(a, s);
    }
    function canRollbackSimple() {
        var prevurl = sessionStorage['wikidialog:' + getWikidialogID() + ':prevurl'];
        if (! prevurl) return false;
        clearRollbackDelegating();
        var s = prevurl.match(/[\?&]wikidialogid=(\d+)/);
        if (s && s.length && (s.length > 0)) {
            // safe because contains an id
            return true;
        }
        s = prevurl.match(/[\?&]wikidialogrolledback=/);
        if (s && s.length && (s.length > 0)) {
            // hopefully safe because active predecessor, if any, is warned 
            return true;
        }
        // no grounds to claim safety (this shouldn't happen)
        return false;
    }
    function canRollbackDelegating() {
        var id = getWikidialogID();
        if (sessionStorage['wikidialog:' + id + ':prevurl']) {
            clearRollbackDelegating();
            return false;
        } else if (sessionStorage['wikidialog:' + id + ':rb-available']) {
            return true;
        } else {
            return false;
        }
    }
    window.wikidialog.addNote = addNote;
    window.wikidialog.clearNotes = clearNotes;
    window.wikidialog.getNotes = getNotes;
    window.wikidialog.keepNotes = function () {
        // impending non-manual delegation
        sessionStorage['wikidialog:' + getWikidialogID() + ':keep-notes'] = 'x';
    }
    window.wikidialog.canRollback = function () {
        return (canRollbackSimple() || canRollbackDelegating());
    }
    window.wikidialog.rollback = function (shortcut, shortcutParam) {
        window.wikidialog.noSequence(); // if we get here at all, don't sequence
        var id = getWikidialogID();
        var prevurl = sessionStorage['wikidialog:' + id + ':prevurl'];
        if (prevurl) {
            clearRollbackDelegating();
            clearRollbackSimple();
            delete window.wikidialog; // precautionary rescript
            location = prevurl;
            return true;
        }
        if (! sessionStorage['wikidialog:' + id + ':rb-available']) {
            return false; // not delegating-rollback either
        }
        //
        // incoming; last chance to bail
        //
        var d = sessionStorage['wikidialog:' + id + ':rb-incoming'];
        if (! d) {
            // bailing; something's very wrong
            clearRollbackDelegating();
            return false;
        }
        sessionStorage['wikidialog:' + id + ':incoming'] = d;
        //
        // unauth
        //
        d = sessionStorage['wikidialog:' + id + ':rb-unauth'];
        if (! d) { sessionStorage.removeItem('wikidialog:' + id + ':unauth'); }
        else {     sessionStorage[           'wikidialog:' + id + ':unauth'] = d; }
        //
        // origin
        //
        d = sessionStorage['wikidialog:' + id + ':rb-origin'];
        if (! d) { sessionStorage.removeItem('wikidialog:' + id + ':origin'); }
        else {     sessionStorage[           'wikidialog:' + id + ':origin'] = d; }
        //
        // proxy
        //
        d = sessionStorage['wikidialog:' + id + ':rb-proxy'];
        if (! d) { sessionStorage.removeItem('wikidialog:' + id + ':proxy'); }
        else {     sessionStorage[           'wikidialog:' + id + ':proxy'] = d; }
        //
        // added
        //
        var ls = sessionStorage['wikidialog:' + id + ':rb-added'];
        if (! ls) ls = ''; // precaution
        ls = ls.split(',');
        d = getData();
        var k;
        for (k = ls.length - 1; k>=0; k--) {
            sessionStorage.removeItem('wikidialog:' + id + ':in:' + ls[k]);
            delete d[ls[k]];
        }
        var s = '';
        for (var p in d) {
            if (s) { s += ','; }
            s += p;
        }
        sessionStorage['wikidialog:' + id + ':params'] = s;
        //
        // changed
        //
        var ls = sessionStorage['wikidialog:' + id + ':rb-changed'];
        if (! ls) ls = ''; // precaution
        ls = ls.split(',');
        var k;
        for (k = ls.length - 1; k>=0; k--)
            sessionStorage['wikidialog:' + id + ':in:' + ls[k]] = sessionStorage['wikidialog:' + id + ':rb-in:' + ls[k]];
        //
        // saved
        //
        activateSavedFieldValues('-rb', id);
        //
        // one use only
        //
        clearRollbackDelegating();
        //
        // nothing is new anymore
        //
        sessionStorage['wikidialog:' + id + ':newparams'] = '&';
        //
        // let it fly
        //
        if (shortcut) { // check not removed, though shortcut now considered required
            shortcut(shortcutParam);
        } else {
            $(window).unbind('unload.wikidialog'); // would erase activated
            sessionStorage['wikidialog:' + id + ':incoming'] = getStableUrl();
            delete window.wikidialog; // precautionary rescript
            location = getStableUrl();
        }
        return true;
    }
    window.wikidialog.pushPrevious = function () {
        //
        // is there a prevurl?
        //
        var id = getWikidialogID();
        var prevurl = sessionStorage['wikidialog:' + id + ':prevurl'];
        if (! prevurl) {
            return 'no previous url to push';
        }
        clearRollbackDelegating();
        //
        // is prevurl missing an id?
        //
        var previd = prevurl.match(/[\?&]wikidialogid=(\d+)/);
        if (previd && previd.length && (previd.length > 1)) {
            previd = previd[1];
        } else {
            return 'no id in previous url';
        }
        //
        // is the stack already full?
        //
        var stack = sessionStorage.wikidialogStack;
        stack = (stack ? stack.split('&') : []);
        if (stack.length >= maxIDsKept) {
            return 'stack already full';
        }
        //
        // add to the stack
        //
        stack[stack.length] = previd;
        stack[stack.length] = prevurl;
        stack = stack.join('&');
        sessionStorage.wikidialogStack = stack;
    }
    window.wikidialog.popUrl = function () {
        var stack = sessionStorage.wikidialogStack;
        stack = (stack ? stack.split('&') : []);
        if (stack.length <= 0) return false;
        window.wikidialog.noSequence(); // wary diving into unknown situation
        var prevurl = stack.pop();
        stack.pop();
        if (stack.length == 0) {
            sessionStorage.removeItem('wikidialogStack');
        } else {
            sessionStorage.wikidialogStack = stack.join('&');
        }
        clearRollbackDelegating();
        clearRollbackSimple();
        location = prevurl;
        return true;
    }
    window.wikidialog.validIncoming = function() {
        if (! sessionStorage) return false;
        var incoming = sessionStorage['wikidialog:' + getWikidialogID() + ':incoming'];
        if (! incoming) return false;
        if (incoming == (location.pathname + location.search)) return true;
        sessionStorage.wikidialogNextID++;
        return false;
    }
    window.wikidialog.receiveAnonymous = function (callback) {
        if (! window.wikidialog.validIncoming()) return;
        var id = getWikidialogID();
        var args = [{origin: sessionStorage['wikidialog:' + id + ':origin'], proxy: sessionStorage['wikidialog:' + id + ':proxy']}];
        for (var k = 1; k < arguments.length; k++) args.push(arguments[k]);
        callback.apply(getData(), args);
    }
    window.wikidialog.pageQuery = function (pageName, fields, callback) {
        //
        // fetch subject page info
        //   at need:     error
        //   on request:  exists, name, content, timestamp,
        //                protected, flagged, categories
        //
        var data = {
            format: 'json',
            action: 'query',
            prop:   'revisions',
            rvprop: 'timestamp',
            rvlimit: 1,
            titles:  pageName
        };
        if ('flagged'    in fields) data.prop   += '|flagged';
        if ('categories' in fields) data.prop   += '|categories';
        if ('content'    in fields) data.rvprop += '|content';
        if ('protected'  in fields) {
            data.prop += '|info';
            data.inprop = 'protection';
        }
        $.getJSON(
            mw.util.wikiScript('api'),
            data
        ).done(function(data) {
            if (! (data.query && data.query.pages))
                fields.error = 'Falló la consulta de la página';
            else for (var p in data.query.pages) {
                if ('invalid' in data.query.pages[p]){
                    fields.error = 'Nombre de página no valido';
                } else if ('missing' in data.query.pages[p]) {
                    if ('exists' in fields) fields.exists = false;
                    else fields.error = 'Página no encontrada';
                } else {
                    if ('exists' in fields) fields.exists = true;
                    if ('name' in fields) {
                        if (data.query.pages[p].title)
                            fields.name = data.query.pages[p].title; // normalize
                        else fields.error = 'Falló la consulta de la página';
                    }
                    if ('content' in fields) {
                        if (data.query.pages[p].revisions &&
                            data.query.pages[p].revisions[0] &&
                            data.query.pages[p].revisions[0]['*']
                           ) fields.content = data.query.pages[p].revisions[0]['*'];
                        else fields.error = 'Falló la consulta de la página';
                    }
                    if ('timestamp' in fields) {
                        if (data.query.pages[p].revisions &&
                            data.query.pages[p].revisions[0] &&
                            data.query.pages[p].revisions[0].timestamp
                           ) fields.timestamp = data.query.pages[p].revisions[0].timestamp;
                        else fields.error = 'Falló la consulta de la página';
                    }
                    if ('protected' in fields) {
                        if ('protection' in data.query.pages[p]) {
                            if (data.query.pages[p].protection[0] && data.query.pages[p].protection[1]) {
                                if (('level' in data.query.pages[p].protection[0]) &&
                                    ('level' in data.query.pages[p].protection[1])
                                   ) fields.protected = ((data.query.pages[p].protection[0].level == 'sysop') &&
                                                         (data.query.pages[p].protection[1].level == 'sysop'));
                                else fields.error = 'Falló la consulta de la página';
                            } else fields.protected = false;
                        } else fields.error = 'Falló la consulta de la página';
                    }
                    if ('flagged' in fields) {
                        if ('flagged' in data.query.pages[p]) {
                            if ('pending_since' in data.query.pages[p].flagged)
                                fields.flagged = 'Pendiente';
                            else fields.flagged = 'Actual';
                        } else fields.flagged = 'Nunca';
                    }
                    if ('categories' in fields) {
                        fields.categories = '';
                        if (data.query.pages[p].categories)
                            for (var j = (data.query.pages[p].categories.length - 1); j >= 0; j--)
                                if (data.query.pages[p].categories[j].title)
                                    fields.categories += '"' + data.query.pages[p].categories[j].title + '" ';
                                else fields.error = 'Falló la consulta de la página';
                    }
                }
            }
            callback(fields);
        }).fail(function () {
            fields.error = 'Falló la consulta de la página';
            callback(fields);
        });
    }
    window.wikidialog.checkProtected = function (remotePageName, callbackDone, callbackFail) {
        window.wikidialog.pageQuery(remotePageName, { protected: '' }, function (data) {
            if ('error' in data)     callbackFail(data.error + ': ' + window.wikidialog.safeHtml(remotePageName));
            else if (data.protected) callbackDone();
            else                     callbackFail('ERROR: Página no protegida a nivel de administrador: ' + window.wikidialog.safeHtml(remotePageName));
        });
    }
    window.wikidialog.pageHistory = function (pageName, fields, callback) {
        //
        // fetch subject page info
        //   revid, timestamp, user, minor, size, comment
        //
        var data = {
            format: 'json',
            action: 'query',
            prop:   'revisions',
            rvprop: 'ids|timestamp|user|flags|size|comment',
            rvlimit: 50,
            titles:  pageName
        };
        if (('continue' in fields) && fields['continue']) {
            data.rvcontinue = fields.continue;
            delete fields.continue;
        }
        if ('direction' in fields) {
            data.rvdir = fields.direction;
            delete fields.direction;
        }
        $.getJSON(
            mw.util.wikiScript('api'),
            data
        ).done(function(data) {
            if (! (data.query && data.query.pages))
                fields.error = 'Falló la consulta de la página';
            else for (var p in data.query.pages) {
                if ('invalid' in data.query.pages[p]){
                    fields.error = 'Nombre de página no valido';
                } else if ('missing' in data.query.pages[p]) {
                    fields.error = 'Página no encontrada';
                } else if (data.query.pages[p].revisions &&
                           data.query.pages[p].revisions[0]) {
                    if (('continue' in data) && ('rvcontinue' in data.continue)) {
                        fields.continue = data.continue.rvcontinue;
                    }
                    fields['revid'] = '';
                    fields['timestamp'] = '';
                    fields['user'] = '';
                    fields['minor'] = '';
                    fields['size'] = '';
                    fields['comment'] = '';
                    for (var k=(data.query.pages[p].revisions.length - 1); k >= 0; k--) {
                        if (! data.query.pages[p].revisions[k]) continue;
                        if (fields['revid']) {
                            fields['revid']     = '&' + fields['revid'];
                            fields['timestamp'] = '&' + fields['timestamp'];
                            fields['user']      = '&' + fields['user'];
                            fields['minor']     = '&' + fields['minor'];
                            fields['size']      = '&' + fields['size'];
                            fields['comment']   = '&' + fields['comment'];
                        } else {
                            fields['revid']     = '';
                            fields['timestamp'] = '';
                            fields['user']      = '';
                            fields['minor']     = '';
                            fields['size']      = '';
                            fields['comment']   = '';
                        }
                        if (       'revid'     in data.query.pages[p].revisions[k])
                          { fields['revid']     = data.query.pages[p].revisions[k].revid     + fields['revid']; }
                        if (       'timestamp' in data.query.pages[p].revisions[k])
                          { fields['timestamp'] = data.query.pages[p].revisions[k].timestamp + fields['timestamp']; }
                        if (       'user'      in data.query.pages[p].revisions[k])
                          { fields['user']      = data.query.pages[p].revisions[k].user      + fields['user']; }
                        if (       'minor'     in data.query.pages[p].revisions[k])
                          { fields['minor']     = 'm'                                        + fields['minor']; }
                        if (       'size'      in data.query.pages[p].revisions[k])
                          { fields['size']      = data.query.pages[p].revisions[k].size      + fields['size']; }
                        if (       'comment'   in data.query.pages[p].revisions[k])
                          { fields['comment']   = data.query.pages[p].revisions[k].comment   + fields['comment']; }
                    }
                } else {
                    fields.error = 'Falló la consulta de la página';
                }
            }
            callback(fields);
        }).fail(function () {
            fields.error = 'Falló la consulta de la página';
            callback(fields);
        });
    }
    window.wikidialog.categoryMembers = function (pageName, fields, callback) {
        //
        // fetch category members info
        //   title, type, timestamp
        //
        var data = {
            format:  'json',
            action:  'query',
            list:    'categorymembers',
            cmtitle: pageName,
            cmprop:  'title|type|timestamp',
            cmlimit: 50,
        };
        if (('continue' in fields) && fields['continue']) {
            data.cmcontinue = fields.continue;
            delete fields.continue;
        }
        if ('direction' in fields) {
            data.cmdir = fields.direction;
            delete fields.direction;
        }
        if ('type' in fields) {
            data.cmtype = fields.type;
            delete fields.type;
        }
        if ('sort' in fields) {
            data.cmsort = fields.sort;
            delete fields.sort;
        }
        $.getJSON(
            mw.util.wikiScript('api'),
            data
        ).done(function(data) {
            if (data.error && data.error.code) {
                if (data.error.code == 'cminvalidcategory') fields.error = 'invalid category title';
                else if (data.error.code == 'cmbadcontinue') fields.error = 'invalid category-members continue value';
                else fields.error = 'category members query misfired';
                addNote('Category members query result error code: ' + data.error.code);
            } else if (! (data.query && data.query.categorymembers)) {
                fields.error = 'category members query misfired';
                addNote('Category members query result has no categorymembers element.');
            } else {
                if (('continue' in data) && ('cmcontinue' in data.continue)) {
                    fields.continue = data.continue.cmcontinue;
                }
                fields['title'] = '';
                fields['type'] = '';
                fields['timestamp'] = '';
                for (var k=(data.query.categorymembers.length - 1); k >= 0; k--) {
                    if (! data.query.categorymembers[k]) continue;
                    if (fields['title']) {
                        fields['title']     = '&' + fields['title'];
                        fields['type']      = '&' + fields['type'];
                        fields['timestamp'] = '&' + fields['timestamp'];
                    } else {
                        fields['title']     = '';
                        fields['type']      = '';
                        fields['timestamp'] = '';
                    }
                    if (       'title'     in data.query.categorymembers[k])
                      { fields['title']     = data.query.categorymembers[k].title     + fields['title']; }
                    if (       'type'      in data.query.categorymembers[k])
                      { fields['type']      = data.query.categorymembers[k].type      + fields['type']; }
                    if (       'timestamp' in data.query.categorymembers[k])
                      { fields['timestamp'] = data.query.categorymembers[k].timestamp + fields['timestamp']; }
                }
            }
            callback(fields);
        }).fail(function () {
            fields.error = 'category members query misfired';
            addNote('Category members query API call failed.');
            callback(fields);
        });
    }
    window.wikidialog.commit = function () {
        clearRollbackSimple();
        clearRollbackDelegating();
    }
    window.wikidialog.proxy = function (remotePageName, remoteProtected, callbackDone, callbackFail) {
        stashUnstableUrl();
        if ((typeof remoteProtected) != "boolean") {
            callbackFail = callbackDone;
            callbackDone = remoteProtected;
            remoteProtected = false;
        }
        if (! authenticIncoming()) {
            registerUnauthenticated(remotePageName);
            if (callbackFail) callbackFail('Solicitud de acción entrante no autenticada');
            return false;
        }
        if (remoteProtected) {
            registerAuthentication(remotePageName, callbackDone);
        } else {
            registerUnauthenticated(remotePageName);
            addNote('No hay autenticación saliente porque la página remota no está protegida a nivel de administrador.');
            if (callbackFail)
                callbackFail('Página remota no autenticada\n  ' +
                             window.wikidialog.safeHtml(remotePageName));
        }
    }
    window.wikidialog.purelySelfContained = function (callbackDone, callbackFail) {
        stashUnstableUrl();
        if (! authenticIncoming()) {
            if (callbackFail) callbackFail('Solicitud de acción entrante no autenticada');
            return false;
        }
        registerAuthentication(callbackDone);
    }
    window.wikidialog.recover = function () {
        //
        // reinstate carried-over field geometries
        //
        restoreGeometries(getWikidialogID(), 'in');
        //
        // reinstate saved field values
        //
        restoreSavedFieldValues();
        releaseSavedFieldValues();
        //
        // cache expiry
        //
        updateActiveList();
        //
        // register event handler for unload
        //
        var id = getWikidialogID();
        function handleUnload() {
            //
            // expect to leave this id and maybe come back later
            // (not suitable for use on forced-reload delegating-rollback)
            //
            saveFieldValues(id);
            if (sessionStorage.wikidialogNextID) {
                var s = location.search.match(/[\?&]wikidialogid=(\d+)/);
                if (s && s.length && (s.length > 1)) {} else
                    sessionStorage.wikidialogNextID++; // avoid accidental revisit
            } else {
                sessionStorage.wikidialogNextID = (getWikidialogID() + 2);
            }
        }
        $(window).unbind('unload.wikidialog');
        $(window).one('unload.wikidialog', handleUnload);
    }
    function delegateToInternal(withRollback, params, metaproc, shortcut, shortcutParam) {
        $('input.wikidialog-button, button.wikidialog-button').attr('disabled',true);
        if (withRollback) {
            window.wikidialog.commit(); // disable any pre-existing rollback
            // set up rollback-delegating data
            var id = getWikidialogID();
            sessionStorage['wikidialog:' + id + ':rb-available'] = 'x';
            sessionStorage['wikidialog:' + id + ':rb-incoming'] = sessionStorage['wikidialog:' + id + ':incoming'];
            if (sessionStorage['wikidialog:' + id + ':unauth']) {
                sessionStorage['wikidialog:' + id + ':rb-unauth'] = sessionStorage['wikidialog:' + id + ':unauth'];
            }
            if (sessionStorage['wikidialog:' + id + ':origin']) {
                sessionStorage['wikidialog:' + id + ':rb-origin'] = sessionStorage['wikidialog:' + id + ':origin'];
            }
            if (sessionStorage['wikidialog:' + id + ':proxy']) {
                sessionStorage['wikidialog:' + id + ':rb-proxy'] = sessionStorage['wikidialog:' + id + ':proxy'];
            }
            saveFieldValues(id, '-rb');
            var storedParams = sessionStorage['wikidialog:' + id + ':params'];
            storedParams = (storedParams ? storedParams.split(',') : []);
            var added = '';   // parameters that didn't exist previously
            var changed = ''; // parameters that did exist previously
            var k;
            for (var p in params) {
                for (k=0; true; k++) {
                    if (k >= storedParams.length) {
                        added += (added ? (',' + p) : p);
                        break;
                    } else if (p == storedParams[k]) {
                        changed += (changed ? (',' + p) : p);
                        sessionStorage[    'wikidialog:' + id + ':rb-in:' + p] =
                            sessionStorage['wikidialog:' + id + ':in:'    + p];
                        break;
                    }
                }
            }
            sessionStorage['wikidialog:' + id + ':rb-added'  ] = added;
            sessionStorage['wikidialog:' + id + ':rb-changed'] = changed;
        } else if (canRollbackSimple()) {
            clearRollbackSimple(); // delegating rollback already cleared
        } else if (canRollbackDelegating()) {
            // augment rollback data
            var id = getWikidialogID();
            var storedParams = sessionStorage['wikidialog:' + id + ':params'];
            storedParams = (storedParams ? storedParams.split(',') : []);
            var added = sessionStorage['wikidialog:' + id + ':rb-added'];
            var changed = sessionStorage['wikidialog:' + id + ':rb-changed'];
            changed = changed.split(',');
            var k, j;
            for (var p in params) {
                for (k=0; true; k++) {
                    if (k >= storedParams.length) {
                        added += (added ? (',' + p) : p);
                        break;
                    } else if (p == storedParams[k]) {
                        for (j=0; true; j++) {
                            if (j >= changed.length) {
                                changed[changed.length] = p;
                                sessionStorage[    'wikidialog:' + id + ':rb-in:' + p] =
                                    sessionStorage['wikidialog:' + id + ':in:'    + p];
                                break;
                            } else if (p == changed[j]) {
                                // value for rollback already stored
                                break;
                            }
                        }
                        break;
                    }
                }
            }
            sessionStorage['wikidialog:' + id + ':rb-added'] = added;
            sessionStorage['wikidialog:' + id + ':rb-changed'] = changed.join(',');
        }
        delegateVerified(params, metaproc, shortcut, shortcutParam);
    }
    window.wikidialog.delegateTo = function (shortcut, params, metaproc, shortcutParam) {
        // clear newparams
        sessionStorage['wikidialog:' + getWikidialogID() + ':newparams'] = '&';
        delegateToInternal(false, params, metaproc, shortcut, shortcutParam);
    }
    window.wikidialog.delegateToPreservingNewparams = function (shortcut, params, metaproc, shortcutParam) {
        // leave newparams alone
        delegateToInternal(false, params, metaproc, shortcut, shortcutParam);
    }
    window.wikidialog.delegateToSettingNewparams = function (shortcut, params, metaproc, shortcutParam) {
        var id = getWikidialogID();
        var x = '&';
        for (var p in params) { x += p + '&'; }
        sessionStorage['wikidialog:' + id + ':newparams'] = x;
        delegateToInternal(false, params, metaproc, shortcut, shortcutParam);
    }
    window.wikidialog.delegateToWithRollback = function (shortcut, params, metaproc, shortcutParam) {
        var id = getWikidialogID();
        var x = '&';
        for (var p in params) { x += p + '&'; }
        sessionStorage['wikidialog:' + id + ':newparams'] = x;
        delegateToInternal(true, params, metaproc, shortcut, shortcutParam);
    }
    window.wikidialog.startSequence = function () {
        sessionStorage.wikidialogSequenceBound = 10; // POINT TO ADJUST max action-sequence length
                                                     //   also occurs in gadget
        sessionStorage.removeItem('wikidialogSequenceOngoing'); // precaution, should be unnecessary
    }
    window.wikidialog.stepSequence = function () {
        if (sessionStorage.wikidialogSequenceBound) {
            var n = Number(sessionStorage.wikidialogSequenceBound);
            if (n > 0) {
                sessionStorage.wikidialogSequenceBound = (n - 1);
            }
            if (n > 1) {
                sessionStorage.wikidialogSequenceOngoing = 'x';
            }
            else {
                sessionStorage.removeItem('wikidialogSequenceOngoing');
            }
        }
    }
    window.wikidialog.noSequence = function (o) {
        sessionStorage.removeItem('wikidialogSequenceBound');
        sessionStorage.removeItem('wikidialogSequenceOngoing');
        if (o && (typeof(o) == 'object')) {
            delete o['ACTION-SEQUENCE-BOUND'];
        }
    }
    window.wikidialog.ongoingSequence = function () {
        return (sessionStorage.wikidialogSequenceOngoing ? true : false);
    }
    window.wikidialog.transclude = function (s) {
        function disassemble(s) {
            // break string into array of substrings
            //   even elements are text other than inclusion directives
            //   odd elements are inclusion directive labels, sans angle brackets
            // split keeps the parenthesized part of the delimiter; (?: ... ) is merely grouping
            return s.split(/<(noinclude|includeonly|onlyinclude|\/noinclude|\/includeonly|\/onlyinclude)(?:\s+[^<>]*)?>/);
        }
        function includeonly(s) {
            for (var k=1; k<s.length; k+=2)
                if ((s[k] == 'includeonly') || (s[k] == '/includeonly'))
                    s[k] = '';
            return s;
        }
        function hasOnlyinclude(s) {
            for (var k=1; k<s.length; k+=2) if (s[k] == 'onlyinclude') return true;
            return false;
        }
        function onlyinclude(s) {
            var suppressFlag = true;
            s[0] = '';
            for (var k=1; k<s.length; k+=2) {
                if (suppressFlag) {
                    if (s[k] == 'onlyinclude') suppressFlag = false;
                    else                       s[k+1] = '';
                    s[k] = '';
                } else
                    if (s[k] == '/onlyinclude') {
                        suppressFlag = true;
                        s[k] = '';
                        s[k+1] = '';
                    }
            }
            return s;
        }
        function noinclude(s) {
            var suppressFlag = false;
            for (var k=1; k<s.length; k+=2) {
                if (suppressFlag) {
                    if (s[k] == '/noinclude') suppressFlag = false;
                    else                      s[k+1] = '';
                    s[k] = '';
                } else
                    if (s[k] == 'noinclude') {
                        suppressFlag = true;
                        s[k] = '';
                        s[k+1] = '';
                    }
            }
            return s;
        }
        function reassemble(s) {
            for (var k=2; k<s.length; k+=2) {
                if (s[k-1] != '') s[0] += '<' + s[k-1] + '>';
                s[0] += s[k];
            }
            return s[0];
        }
        //
        s = includeonly(disassemble(s));
        if (hasOnlyinclude(s)) s = onlyinclude(s);
        s = reassemble(noinclude(s));
        return s;
    }
    function makeTparamRgx(name) {
        // the placement of parentheses here is dictated by substForTparams
        return new RegExp('(\\{\\{\\{)' + name + '(\\|[^\\|\\{\\}]*)?(\\}\\}\\})');
    }
    window.wikidialog.substituteTemplateParameters = function (data, s) {
        //
        // substitute data for template parameters in raw wiki markup string s
        // safe to substitute arbitrarily because it's raw, provided we encode =[]<>{|}
        //
        function encodeMarkup(raw) {
            return raw.replace(/\||\=+|<+|>+|\[+|\]+|\{+|\}+/g,
                function (s) {
                    function rpt(n, t) {
                        var s = '';
                        while (n > 16) {
                            s += '{{' + t + '|16}}';
                            n -= 16;
                        }
                        s += '{{' + t;
                        if (n > 1) s += ('|' + n);
                        s += '}}';
                        return s;
                    }
                    switch (s[0]) {
                      case '=': return rpt(s.length, '==');  // [[Template:==]]
                      case '[': return rpt(s.length, '(-');  // [[Template:(-]]
                      case ']': return rpt(s.length, '-)');  // [[Template:-)]]
                      case '<': return rpt(s.length, '(\\'); // [[Template:(\]]
                      case '>': return rpt(s.length, '\\)'); // [[Template:\)]]
                      case '{': return rpt(s.length, '(*');  // [[Template:(*]]
                      case '|': return '{{!}}';                  // [[Template:!]]
                      case '}': return rpt(s.length, '*)');  // [[Template:*)]]
                    }
                });
        }
        var d = data;
        data = {};
        for (var q in d)
            if ((typeof d[q]) != "string")
                data[q] = (d[q] ? d[q].toString() : '');
            else
                data[q] = d[q];
        if (! s) return s;
        s = s.split(makeTparamRgx("([^\\{\\|\\}]*)"));
        for (var k = 0; (k < s.length); k++)
            if ((s[k] == '{{{') & (s[k+1] != '{{{')) {
                if (s[k+1] in data) {
                    s[k] = '';
                    s[k+1] = data[s[k+1]];
                    s[k+1] = encodeMarkup(s[k+1]);
                    s[k+2] = '';
                    s[k+3] = '';
                }
                k += 3;
            }
        return s.join('');
    }
    window.wikidialog.substituteParameters = function (pageName, actionParams, requests, content, callback) {
        // requests is optional
        if ((typeof requests) == "string") {
            callback = content;
            content = requests;
            requests = {};
        }
        function makeInitRgx(name) {
            var maxBracesDepth = 32; // SINGLE POINT TO ADJUST max braces depth in Diálogo/init calls
            var rgx = "\\{[^\\{\\}]*\\}";
            for (var j = 1; j <= maxBracesDepth; j++)
                rgx = "\\{[^\\{\\}]*(?:" + rgx + "[^\\{\\}]*)*\\}";
            rgx = "\\{\\{\\s*[Dd]ialog/init\\s*\\|\\s*" + name + "\\s*\\|([^\\{\\|\\}]*(?:" +
                rgx + "[^\\{\\|\\}]*)*)((?:\\|[^\\{\\|\\}\\=]*)?)\\}\\}";
            return new RegExp(rgx);
        }
        function reservedParam(s) {
            return ((s.search(/[A-Z]/) == 0) && (s.search(/[a-z]/) < 0));
        }
        function localParam (s) {
            return (s.search(/local/) == 0);
        }
        function activeParams () {
            //
            // result is ampersand separated and delimited,
            // so each name on it is ampersand delimited
            //
            var id = getWikidialogID();
            var result = sessionStorage['wikidialog:' + id + ':newparams'];
            if (! result) {
                result = sessionStorage['wikidialog:' + id + ':params'];
                if (! result) {
                    result = '&';
                } else {
                    result = (',' + result + ',').replace(/,+/g, '&');
                }
            }
            return result;
        }
        function paramIsActive(p) {
            return (activeParams().search('&' + p + '&') >= 0);
        }
        function paramIsTranscluded(p) {
            var rgx = makeTparamRgx(p);
            if (typeof content == "string") return (content.search(rgx) >= 0);
            for (var k = 0; (k < content.length); k++)
                if (content[k].search(rgx) >= 0) return true;
            return false;
        }
        function substForTparams(data) {
            if (typeof content == "string")
                content = window.wikidialog.substituteTemplateParameters(data, content);
            else
                for (var k = 0; (k < content.length); k++)
                    content[k] = window.wikidialog.substituteTemplateParameters(data, content[k]);
        }
        function processInitCalls(pred, callback) {
            //
            // processes [[Template:Diálogo/init|Diálogo/init]] calls
            // pred is optional; if pred omitted, doesn't reassemble
            //
            function f(n) { // process first n calls, from nth back to 1st
                if (n < 1) {
                    callback();
                    return;
                }
                //
                // process nth remaining Diálogo/init call, then recurse
                //
                if (! content[4*n - 3]) { // already processed
                    f(n - 1);
                    return;
                }
                if (! pred(content[4*n - 2])) { // defer processing
                    f(n - 1);
                    return;
                }
                if (reservedParam(content[4*n - 3])) { // reserved
                    content[4*n - 3] = '';
                    if (! content[4*n - 1]) content[4*n - 2] = ''; // leave body for later expansion
                    content[4*n - 1] = '';
                    f(n - 1);
                    return;
                }
                if (content[4*n - 2].search(/\{\{/) < 0) { // nothing to expand
                    actionParams[content[4*n - 3]] = content[4*n - 2];
                    content[4*n - 3] = '';
                    if (! content[4*n - 1]) content[4*n - 2] = '';
                    content[4*n - 1] = '';
                    f(n - 1);
                    return;
                }
                $.ajax({
                    type: 'POST',
                    url:  mw.util.wikiScript('api'),
                    data: { format:  'json',
                            action:  'expandtemplates',
                            text:    content[4*n - 2],
                            title:   pageName,
                            prop:    'wikitext'
                          },
                    datatype: 'json'
                }).done(function(data) {
                    if ((! data.expandtemplates) || (! data.expandtemplates.wikitext))
                        actionParams[content[4*n - 3]] = '';
                    else
                        actionParams[content[4*n - 3]] = data.expandtemplates.wikitext;
                    content[4*n - 3] = '';
                    if (! content[4*n - 1]) content[4*n - 2] = ''; // leave body for later expansion
                    content[4*n - 1] = '';
                    f(n - 1);
                }).fail(function () {
                    show('Se ha producido un error al procesar la llamada a Diálogo/init: Fallo la solicitud de expansión de plantillas.');
                });
            }
            function makeReassembler(callback) {
                return (function() {
                    content = content.join('');
                    callback();
                });
            }
            //
            // find the calls
            //
            if (typeof content == "string") content = content.split(makeInitRgx("([\\w-]+)"));
            var n = (content.length - 1) / 4;
            while (n > 8) { // SINGLE POINT TO ADJUST max number of Diálogo/init calls processed
                content[4*n - 3] = '';
                if (! content[4*n - 1]) content[4*n - 2] = '';
                content[4*n - 1] = '';
                n--;
            }
            if (! callback) {
                callback = makeReassembler(pred);
                pred = (function (s) { return true; });
            }
            f(n);
        }
        function stepNine() {
            //
            // process remaining Diálogo/init calls, reassemble, and done
            //
            processInitCalls(function () {
                callback(content);
            });
        }
        function stepEight() {
            //
            // fetch fileinfo if requested
            //
            if (! (   actionParams['file']
                   && paramIsTranscluded('FILE-INFO-[^\\{\\|\\}]*')
                   && actionParams['local-file-info'])) {
                stepNine();
                return;
            }
            $.ajax({
                type: 'POST',
                url:  mw.util.wikiScript('api'),
                data: { format:  'json',
                        action:  'query',
                        titles:  actionParams['file'],
                        prop:    'imageinfo',
                        iiprop:  (  paramIsTranscluded('FILE-INFO-META-[^\\{\\|\\}]*')
                                  ? 'size|extmetadata'
                                  : 'size')
                      },
                datatype: 'json'
            }).done(function(data) {
                if (data.query && data.query.pages) {
                    var fields = {};
                    for (var p in data.query.pages) {
                        var ii = data.query.pages[p].imageinfo;
                        if (ii && ii[0]) {
                            if (ii[0]['size'  ]) fields['FILE-INFO-SIZE'  ] = ii[0]['size'  ];
                            if (ii[0]['width' ]) fields['FILE-INFO-WIDTH' ] = ii[0]['width' ];
                            if (ii[0]['height']) fields['FILE-INFO-HEIGHT'] = ii[0]['height'];
                            var emd = ii[0]['extmetadata'];
                            if (emd) for (var q in emd) {
                                if (emd[q]['value'])
                                    fields['FILE-INFO-META-' + q.toUpperCase()] = emd[q]['value'];
                            }
                        }
                    }
                    substForTparams(fields);
                }
                stepNine();
            }).fail(function () {
                show('Error al procesar la solicitud de diálogo: Error de la solicitud de información de la imagen.');
            });
        }
        function stepSeven() {
            //
            // fetch expanded text if requested
            //
            if (! (   paramIsTranscluded('EXPANDED-TEXT')
                   && actionParams['local-text-to-expand'])) {
                stepEight();
                return;
            }
            $.ajax({
                type: 'POST',
                url:  mw.util.wikiScript('api'),
                data: { format:  'json',
                        action:  'expandtemplates',
                        text:    actionParams['local-text-to-expand'],
                        title:   pageName,
                        prop:    'wikitext'
                      },
                datatype: 'json'
            }).done(function(data) {
                if ((data.expandtemplates) && (data.expandtemplates.wikitext))
                    substForTparams({ 'EXPANDED-TEXT': data.expandtemplates.wikitext });
                stepEight();
            }).fail(function () {
                show('Se ha producido un error al procesar la solicitud de diálogo: La solicitud de expansión de plantillas ha fallado.');
            });
        }
        function stepSix() {
            //
            // fetch category members if requested
            //
            if (! (   actionParams['category']
                   && paramIsTranscluded('CATEGORY-MEMBERS-[^\\{\\|\\}]*')
                   && actionParams['local-category-members'])) {
                stepSeven();
                return;
            }
            var fields = {};
            if (actionParams['local-category-members-continue' ]) fields.continue  = actionParams['local-category-members-continue'];
            if (actionParams['local-category-members-direction']) fields.direction = actionParams['local-category-members-direction'];
            if (actionParams['local-category-members-type'     ]) fields.type      = actionParams['local-category-members-type'];
            if (actionParams['local-category-members-sort'     ]) fields.sort      = actionParams['local-category-members-sort'];
            window.wikidialog.categoryMembers(actionParams['category'], fields,
                function (result) {
                    var data = {};
                    for (var p in result) {
                        data        ['CATEGORY-MEMBERS-' + p.toUpperCase()] = result[p];
                        actionParams['CATEGORY-MEMBERS-' + p.toUpperCase()] = result[p];
                    }
                    substForTparams(data);
                    stepSeven();
                });
        }
        function stepFive() {
            //
            // fetch page history if requested
            //
            if (! (   actionParams['subject']
                   && paramIsTranscluded('SUBJECT-HISTORY-[^\\{\\|\\}]*')
                   && actionParams['local-subject-history'])) {
                stepSix();
                return;
            }
            var fields = {};
            if (actionParams['local-subject-history-continue' ]) fields.continue  = actionParams['local-subject-history-continue'];
            if (actionParams['local-subject-history-direction']) fields.direction = actionParams['local-subject-history-direction'];
            window.wikidialog.pageHistory(actionParams['subject'], fields,
                function (result) {
                    var data = {};
                    for (var p in result) {
                        data        ['SUBJECT-HISTORY-' + p.toUpperCase()] = result[p];
                        actionParams['SUBJECT-HISTORY-' + p.toUpperCase()] = result[p];
                    }
                    substForTparams(data);
                    stepSix();
                });
        }
        function stepFour() {
            //
            // process first round of Diálogo/init calls
            //
            processInitCalls((function (s) {
                    return (   (s.search(makeTparamRgx('SUBJECT-HISTORY-[^\\{\\|\\}]*')) < 0)
                            && (s.search(makeTparamRgx('CATEGORY-MEMBERS-[^\\{\\|\\}]*')) < 0)
                            && (s.search(makeTparamRgx('EXPANDED-TEXT')) < 0));
                }), stepFive);
        }
        function stepThree() {
            //
            // substitute for most template parameters, excluding complicated
            // query results controled by local parameter via Diálogo/init
            //
            substForTparams(actionParams)
            stepFour();
        }
        function stepTwo() {
            //
            // fetch preload page if named and called for
            //
            if (! ('PRELOAD-PAGENAME' in actionParams)) {
                stepThree();
                return;
            }
            if (! (paramIsTranscluded("PRELOAD-CONTENT"))) {
                stepThree();
                return;
            }
            window.wikidialog.pageQuery(
                actionParams['PRELOAD-PAGENAME'],
                { content: '' },
                function (fields) {
                    if ('error' in fields) {
                        stepThree();
                        return;
                    }
                    actionParams['PRELOAD-CONTENT'] = window.wikidialog.transclude(fields.content);
                    stepThree();
                });
        }
        function stepOne() {
            //
            // remove reserved and local parameters
            //
            var subtractParams = {};
            for (var p in actionParams) if (reservedParam(p) || localParam(p)) {
                subtractParams[p] = actionParams[p];
            }
            for (var p in subtractParams) {
                delete actionParams[p];
            }
            //
            // prep request/authentication parameters
            //
            var id = getWikidialogID();
            var origin = sessionStorage['wikidialog:' + id + ':origin'];
            if (origin) {
                actionParams['INCOMING-AUTHENTICATED'] = origin;
                actionParams['REQUESTING-PAGE'] = origin;
            }
            else {
                var unauth = sessionStorage['wikidialog:' + id + ':unauth'];
                if (unauth) actionParams['REQUESTING-PAGE'] = unauth;
            }
            actionParams['ACTIVE-PARAMETERS'] = activeParams();
            //
            // prep user name/groups parameters
            //
            var un = mw.config.get('wgUserName');
            actionParams['USERNAME'] = (!un ? "" : un);
            var ug = mw.config.get('wgUserGroups');
            actionParams['USER-GROUPS'] = (!ug ? "" : ug.join(" "));
            //
            // prep version parameters
            //
            if (window.wikidialog.gadgetVersion) {
                actionParams['DIALOG-GADGET-VERSION'] = window.wikidialog.gadgetVersion();
            }
            actionParams['DIALOG-RECEIVE-VERSION'] = receiveVersion();
            //
            // prep stack-depth parameter
            //
            var stack = sessionStorage.wikidialogStack;
            if (stack) {
                actionParams['STACK-DEPTH'] = '' + (stack.split('&').length / 2);
            }
            //
            // prep action-dependent parameters
            //
            for (var p in requests)
                if (reservedParam(p)) {
                    actionParams[p] = requests[p];
                }
            //
            // prep sequence parameter
            //
            if (sessionStorage.wikidialogSequenceBound) {
                actionParams['ACTION-SEQUENCE-BOUND'] = sessionStorage.wikidialogSequenceBound;
            }
            //
            // fetch subject page info unless there's no subject
            //
            if (! ('subject' in actionParams)) {
                stepTwo();
                return;
            }
            var fields = { name: '', exists: '' };
            for (var p in requests) fields[p] = '';
            if (paramIsTranscluded("SUBJECT-CONTENT"))    fields.content = '';
            if (paramIsTranscluded("SUBJECT-TIMESTAMP"))  fields.timestamp = '';
            if (paramIsTranscluded("SUBJECT-CATEGORIES")) fields.categories = '';
            if (paramIsTranscluded("SUBJECT-FLAGGED"))    fields.flagged = '';
            window.wikidialog.pageQuery(actionParams.subject, fields, function (fields) {
                if ('error' in fields) {
                    stepTwo();
                    return;
                }
                if (('exists' in fields) && (! fields.exists)) {
                    actionParams['SUBJECT-EXISTS'] = 'false';
                    stepTwo();
                    return;
                }
                actionParams['SUBJECT-EXISTS'] = 'true';
                actionParams.subject = fields.name; // normalize
                if ('content' in fields)    actionParams['SUBJECT-CONTENT']    = fields.content;
                if ('timestamp' in fields)  actionParams['SUBJECT-TIMESTAMP']  = fields.timestamp;
                if ('categories' in fields) actionParams['SUBJECT-CATEGORIES'] = fields.categories;
                if ('flagged' in fields)    actionParams['SUBJECT-FLAGGED']    = fields.flagged;
                stepTwo();
            });
        }
        stepOne();
    }
})();