Ծանուցում. Հիշելուց հետո կատարված փոփոխությունները տեսնելու համար մաքրեք ձեր զննարկիչի հիշապահեստը. Mozilla / Firefox / Safari՝ Ctrl+Shift+R (Cmd+Shift+R Mac OS X-ում) Konqueror՝ F5 Opera՝ Tools→Preferences ընտրացանկից։ Internet Explorer՝ Ctrl+F5

$(function () {
    "use strict";
    var proofreadNamespaces = {
        index: 106,
        page: 104
    };
    var fetchedPages = {};
    var foundSections = {};

    var UI = {
        $body: $('#mw-content-text'),
        $input: $('<input type="text" style="width:80%">'),
        $button: $('<button disabled="disabled">'),
        $status: $('<div style="background:#EEE;padding:5px 10px;text-align: center">'),
        $warnings: $('<div style="background:#EED;padding:5px 10px; display:none"><h2>Warnings</h2></div>'),
        $sections: $('<div style="background:#DED;padding:5px 10px; display:none"><h2>Sections</h2><table><thead><tr><td>Section name</td><td>Pages</td></tr></thead></table></div>'),
        $sectionsTBody: $('<tbody></tbody>'),
        init: function () {
            // TODO: i18n
            $('#firstHeading span').text('SectionHarvester');
            UI.$body.text('');

            UI.$input.appendTo(UI.$body);
            UI.$input.attr('placeholder', 'URL of the index page');
            UI.$input.on('change', UI.validateInput);
            UI.$input.on('keyup', UI.validateInput);

            UI.$button.appendTo(UI.$body);
            UI.$button.text('Harvest!');

            UI.$button.click(harvestSections);

            UI.$status.appendTo(UI.$body);
            UI.$warnings.appendTo(UI.$body);
            UI.$sections.appendTo(UI.$body);
            UI.$sectionsTBody = UI.$sections.find('table');
        },
        validateInput: function () {
            var url = UI.$input.val();
            // TODO: some error message?
            if (validateURL(url)) {
                UI.$button.prop('disabled', false);
            } else {
                UI.$button.prop('disabled', true);
            }
        },
        getInputValue: function () {
            return UI.$input.val();
        },
        setStatus: function (message) {
            UI.$status.text(message);
        },
        addPageWarning: function (pageId, message) {
            UI.$warnings.show();
            // TODO: i18n and links!
            UI.$warnings.append('<div>Page ' + getPageTitleById(pageId) + ' - ' + message + '</div>');
        },
        showSections: function () {
            var sortedKeys = Object.keys(foundSections).sort(function (a, b) {
                // TODO: sort according to the real pages!
                a = a.toLowerCase();
                b = b.toLowerCase();
                if (a < b) return -1;
                if (a > b) return 1;
                return 0;
            });
            sortedKeys.map(function (sectionName) {
                var pageNumbers = foundSections[sectionName].map(getPageTitleById).join(', ');
                UI.$sectionsTBody.append('<tr><td>' + sectionName + '</td><td>' + pageNumbers + '</td></tr>');
            });
            UI.$sections.show();
        }
    };
    var getPageTitleById = function (pageId) {
        var titleArray = fetchedPages[pageId].title.split('/');
        return titleArray.length > 1 ? titleArray[1] : titleArray[0];
    };
    var validateURL = function (url) {
        // TODO: validate! - only current wikisource!
        return url.match(/^https?:\/\/[a-z-]+.wikisource.org\/wiki\/.+/) !== null;
    };
    var PageValidator = {
        beforeFirstSection: function (pageId, content) {
            // TODO: more validation
            if (content.trim() === '') {
                return;
            }
            UI.addPageWarning(pageId, 'Text before first section: <textarea style="display:block" rows="3">' + content + '</textarea>');
        },
        afterEndSection: function (pageId, content) {
            // TODO: more validation
            if (content.trim() === '') {
                return;
            }
            UI.addPageWarning(pageId, 'Text after last section: <textarea style="display:block" rows="3">' + content + '</textarea>');
        },
        noSections: function (pageId, content) {
            // TODO: validate?!
            UI.addPageWarning(pageId, 'No sections found!');
        },
        newSectionName: function (pageId, sectionName) {
            // TODO: validate name?
            if (!foundSections[sectionName]) {
                foundSections[sectionName] = [];
            }
            // TODO: hold section contents!!!
            foundSections[sectionName].push(pageId);
        },
        betweenSections: function (pageId, content) {
            // TODO: more validation
            if (content.trim() === '') {
                return;
            }
            UI.addPageWarning(pageId, 'Text between sections: <textarea style="display:block" rows="3">' + content + '</textarea>');
        },
        sectionNoEnd: function (pageId, sectionName) {
            UI.addPageWarning(pageId, 'Section ' + sectionName + ' didn\'t end!');
        },
        sectionNameConflict: function (pageId, sectionName, sectionNameEnd) {
            if (sectionName !== sectionNameEnd) {
                UI.addPageWarning(pageId, 'Section ' + sectionName + ' ended with name ' + sectionNameEnd);
            }
        }
    };
    var updateProofreadNamespaces = function () {
        // https://www.mediawiki.org/wiki/Extension_talk:Proofread_Page#API_Documentation_.26_Improvement
        var deferred = $.Deferred();
        $.get('/w/api.php', {
            "action": "query",
            "meta": "proofreadinfo",
            "format": "json",
            "piprop": "namespaces"
        }).done(function (d) {
            proofreadNamespaces = d.query.proofreadnamespaces;
            deferred.resolve();
        });
        return deferred;
    };
    var harvestSections = function () {
        var urlArray = UI.getInputValue().substr(6).split(':'); // find namespace colon
        var url = decodeURIComponent(urlArray[1]);
        UI.setStatus('Fetching list of pages...'); //TODO: i18n
        getPageContentsRecursive(url).then(function (pages) {
            fetchedPages = pages;
            Object.keys(fetchedPages).map(scanPage);
            return true;
        }).then(function () {
            // TODO: section validation
            UI.showSections();
            UI.setStatus('Ready! Harvested sections are below.');
        });
    };
    var scanPage = function (id) {
        // TODO: process only pages with numeric titles: .djvu/\d+
        // otherwise show an error.
        UI.setStatus('Scanning page:' + fetchedPages[id].title);
        var content = fetchedPages[id].revisions[0]['*'];
        var offset = 0;
        fetchedPages[id].foundSections = -1;
        do {
            fetchedPages[id].foundSections++;
            offset = findNextSection(id, content, offset);
        } while (offset !== false);
    };
    var findNextSection = function (pageId, content, offset) {
        // TODO: invalid XML markup validation
        var sectionBeginString = '<section begin="'; // TODO: i18n ? https://www.mediawiki.org/wiki/Extension:Labeled_Section_Transclusion#Localisation
        var sectionEndString = '<section end="';

        var beginTagStart = content.indexOf(sectionBeginString, offset);
        if (beginTagStart === -1) {
            if (offset === 0) {
                // no sections in the page
                PageValidator.noSections(pageId);
            } else {
                PageValidator.afterEndSection(pageId, content.substr(offset));
            }
            return false;
        }
        if (offset === 0) {
            PageValidator.beforeFirstSection(pageId, content.substring(0, beginTagStart));
        } else {
            PageValidator.betweenSections(pageId, content.substring(offset, beginTagStart));
        }
        var beginNameStart = beginTagStart + sectionBeginString.length;
        var beginNameEnd = content.indexOf('"', beginNameStart); // TODO: quotes in the title?
        var beginTagEnd = content.indexOf('>', beginNameEnd) + 1;
        var sectionName = content.substring(beginNameStart, beginNameEnd);

        PageValidator.newSectionName(pageId, sectionName);

        var endTagStart = content.indexOf(sectionEndString, beginTagEnd);
        if (endTagStart === -1) {
            PageValidator.sectionNoEnd(pageId, sectionName);
            return beginTagEnd;
        }
        var endNameStart = endTagStart + sectionEndString.length;
        var endNameEnd = content.indexOf('"', endNameStart); // TODO: quotes in the title?
        var endTagEnd = content.indexOf('>', endNameEnd) + 1;
        var sectionNameEnd = content.substring(endNameStart, endNameEnd);
        PageValidator.sectionNameConflict(pageId, sectionName, sectionNameEnd);

        return endTagEnd;
    };
    var getPageContentsRecursive = function (url, queryContinue) {
        var deferred = $.Deferred();
        var getObject = {
            "action": "query",
            "generator": "allpages",
            "gaplimit": "50",
            "gapprefix": url,
            "gapnamespace": proofreadNamespaces.page.id,
            "prop": "revisions",
            "rvprop": "content",
            "format": "json"
        };
        getObject = $.extend(getObject, queryContinue || {});
        $.get('/w/api.php', getObject).done(function (d) {
            if (d["query-continue"]) {
                // not all results have been received
                // add the values of
                // .query-continue.allpages and .query-continue.revisions
                // to the query next time [we're using generators!]
                getPageContentsRecursive(url, $.extend(d["query-continue"].allpages, d["query-continue"].revisions)).then(function (pages) {
                    deferred.resolve($.extend(pages, d.query.pages));
                });
            } else {
                deferred.resolve(d.query.pages);
            }
        });
        return deferred;

    };

    // get information about namespaces that Proofread extension uses
    updateProofreadNamespaces().then(UI.init).fail(function () {
        // TODO:
        alert('Cannot use Wikisource API');
    });
});