Справица:Solutions.js

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу
/* eslint-disable max-statements */
(function() {
    'use strict';
    var $body = $('body');
    var questions = [];
    var allSolutionsShown = false;
    var CORRECT_SRC = 'https://upload.wikimedia.org/wikipedia/en/thumb/f/fb/Yes_check.svg/18px-Yes_check.svg.png';
    var INCORRECT_SRC = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/X_mark.svg/18px-X_mark.svg.png';
    var staticId = 0;
    var questionIdMap = {};
    var interactiveMode = null;

    function createButton(msg) {
        return $('<span>', {
            'class': [
                'oo-ui-buttonElement',
                'oo-ui-buttonElement-framed',
                'oo-ui-flaggedElement-primary',
                'oo-ui-flaggedElement-progressive',
                'oo-ui-labelElement',
                'oo-ui-widget-enabled'
            ].join(' ')
        }).append(
            $('<button>', {
                'class': 'oo-ui-buttonElement-button',
                'tabindex': '0'
            }).append(
                $('<span>', {
                    'class': 'oo-ui-labelElement-label label',
                    'text': mw.message('gadget-solutions-' + msg).plain()
                })
            )
        );
    }

    function createCorrectImage(correct) {
        // TODO: alt
        return $('<img>', {
            'class': 'correct',
            'src': correct ?
                CORRECT_SRC :
                INCORRECT_SRC
        });
    }

    function Question($elem) {
        this.id = staticId++;
        questionIdMap[this.id] = this;
        this.$elem = $elem.addClass('loaded');
        this.state = allSolutionsShown;
        if (allSolutionsShown) {
            this.$butt = createButton('hide');
            this.show();
        } else {
            this.$butt = createButton('show');
            this.hide();
        }
        this.appendButton();
    }

    Question.prototype.isAttached = function() {
        return $.contains(document, this.$elem[0]);
    };

    Question.prototype.show = function() {
        this.$butt
            .off()
            .click(this.hide.bind(this))
            .find('.label')
            .text(mw.message('gadget-solutions-hide').plain());
        if (this.explanation) {
            this.explanation.show();
        }
    };

    Question.prototype.hide = function() {
        this.$butt
            .off()
            .click(this.show.bind(this))
            .find('.label')
            .text(mw.message('gadget-solutions-show').plain());
        if (this.explanation) {
            this.explanation.hide();
        }
    };

    Question.prototype.appendButton = function() {
        this.$elem.after(this.$butt);
    };

    function Text($elem) {
        this.solution = $elem.text().trim();
        this.$input = $('<input>', {
            type: 'text'
        });
        Question.call(this, $elem);
    }
    OO.inheritClass(Text, Question);

    Text.prototype.show = function() {
        Question.prototype.show.call(this);
        this.$elem.addClass('spoiler show');
        var answer = this.$input.val().trim();
        this.$input.val('');
        this.$elem.text(this.solution);
        if (answer) {
            this.$elem.prepend(createCorrectImage(this.solution === answer));
        }
    };

    Text.prototype.hide = function() {
        Question.prototype.hide.call(this);
        this.$elem
            .removeClass('spoiler')
            .empty()
            .append(this.$input);
    };

    function Choice($elem, inputType) {
        this.inputType = inputType;
        Question.call(this, $elem);
    }
    OO.inheritClass(Choice, Question);

    Choice.prototype.show = function() {
        Question.prototype.show.call(this);
        this.$elem.addClass('show');
        this.$elem.find('li').each(function(_, el) {
            var $li = $(el);
            var isCorrect = $li.find('.solution').length === 1;
            var $input = $li.find('input');
            var isChecked = $input.prop('checked');
            $input.remove();
            $li.html($li.find('label').html());
            if (isChecked) {
                $li.prepend(createCorrectImage(isCorrect === isChecked));
            }
        });
    };

    Choice.prototype.hide = function() {
        Question.prototype.hide.call(this);
        this.$elem.removeClass('show');
        this.$elem.find('li').each(function(index, el) {
            var $li = $(el);
            var id = 'solution-' + this.id + '-' + index;
            $li.find('img.correct').remove();
            $li
                .html($('<label>', {
                    'for': id,
                    'html': $li.html()
                }))
                .prepend($('<input>', {
                    id: id,
                    name: 'solution-' + this.id,
                    type: this.inputType
                }));
        }.bind(this));
    };

    function Select($elem) {
        var options = $elem.attr('data-options');
        if (options) {
            this.options = options
                .split(',')
                .map(function(opt) {
                    return opt.trim();
                });
            this.$select = $('<select>')
                .append(this.options.map(function(opt) {
                    return $('<option>', {
                        text: opt
                    });
                }));
        }
        Question.call(this, $elem);
    }
    OO.inheritClass(Select, Question);

    Select.prototype.show = function() {
        Question.prototype.show.call(this);
        this.$elem.find('> ul > li, > ol > li, td').each(function(_, el) {
            var $li = $(el);
            var images = [];
            $li.find('select').each(function(__, el2) {
                var $select = $(el2);
                var selected = $select.prop('selectedIndex');
                var answer = $select.attr('data-answer');
                var options = $select
                    .find('option')
                    .map(function(___, opt) {
                        return $(opt).text();
                    }).toArray();
                var correct = options.indexOf(answer);
                $select.replaceWith($('<span>', {
                    'data-options': options.join(','),
                    'class': 'spoiler show',
                    'text': answer
                }));
                images.push(createCorrectImage(selected === correct));
            });
            $li.prepend(images);
        });
    };

    Select.prototype.hide = function() {
        Question.prototype.hide.call(this);
        var $thisSelect = this.$select;
        this.$elem.find('> ul > li, > ol > li, td').each(function(_, el) {
            var $li = $(el);
            $li.find('img.correct').remove();
            $li.find('.spoiler').each(function(__, el2) {
                var $spoiler = $(el2);
                var $oldSelect = $thisSelect;
                if (!$oldSelect) {
                    // Each list item has different options.
                    $oldSelect = $('<select>').append(
                        $spoiler
                            .attr('data-options')
                            .split(',')
                            .map(function(opt) {
                                return $('<option>', {
                                    text: opt.trim()
                                });
                            })
                    );
                }
                $spoiler.replaceWith(
                    $oldSelect
                        .clone()
                        .attr('data-answer', $spoiler.text())
                );
            });
        });
    };

    function Explanation($elem) {
        Question.call(this, $elem);
        questionIdMap[this.id - 1].explanation = this;
    }
    OO.inheritClass(Explanation, Question);

    Explanation.prototype.appendButton = $.noop;

    Explanation.prototype.show = function() {
        Question.prototype.show.call(this);
        this.$elem.addClass('show');
    };

    Explanation.prototype.hide = function() {
        Question.prototype.hide.call(this);
        this.$elem.removeClass('show');
    };

    function Plain($elem) {
        Question.call(this, $elem);
    }
    OO.inheritClass(Plain, Question);

    Plain.prototype.show = function() {
        Question.prototype.show.call(this);
        this.$elem.addClass('show');
    };

    Plain.prototype.hide = function() {
        Question.prototype.hide.call(this);
        this.$elem.removeClass('show');
    };

    function createQuestion(_, elem) {
        var $elem = $(elem);
        var type = $elem.attr('data-solution');
        if (!interactiveMode) {
            if (type === 'explanation') {
                return new Explanation($elem);
            }
            return new Plain($elem);
        }
        switch (type) {
            case 'text': return new Text($elem);
            case 'single': return new Choice($elem, 'radio');
            case 'multiple': return new Choice($elem, 'checkbox');
            case 'select': return new Select($elem);
            case 'explanation': return new Explanation($elem);
            default: return new Plain($elem);
        }
    }

    function toggleAll() {
        var $label = $(this).find('.label');
        allSolutionsShown = !allSolutionsShown;
        if (allSolutionsShown) {
            $label.text(mw.message('gadget-solutions-hide-all').plain());
        } else {
            $label.text(mw.message('gadget-solutions-show-all').plain());
        }
        questions.forEach(function(q) {
            if (allSolutionsShown) {
                q.show();
            } else {
                q.hide();
            }
        });
    }

    function checkInteractiveMode() {
        if (interactiveMode !== null) {
            // We already checked for interactive mode.
            return;
        }
        var interactiveModeLS = mw.storage.get('solutions-interactive');
        if (interactiveModeLS) {
            // The interactive mode has been set.
            interactiveMode = interactiveModeLS === 'yes';
        } else {
            mw.storage.set('solutions-interactive', 'yes');
            interactiveMode = true;
            mw.notification.notify(mw.message('gadget-solutions-interactive-notification'), {
                type: 'info'
            });
        }
    }

    function toggleInteractive() {
        mw.storage.set('solutions-interactive', interactiveMode ? 'no' : 'yes');
        window.location.reload();
    }

    function hook($content) {
        if ($content.find('[data-solution]').length) {
            // Only check for interactive mode if we're on a page where it can be used.
            checkInteractiveMode();
        }
        $content
            // TODO: unhide -> show
            .find('.unhide-solutions:not(.loaded)')
            .addClass('loaded')
            .append([
                createButton(
                    allSolutionsShown ?
                        'hide-all' :
                        'show-all'
                ).click(toggleAll),
                createButton(
                    interactiveMode ?
                        'disable-interactive' :
                        'enable-interactive'
                ).click(toggleInteractive)
            ]);
        questions = questions.filter(function(q) {
            var ret = q.isAttached();
            if (!ret) {
                delete questionIdMap[q.id];
            }
            return q.isAttached();
        }).concat(
            $content.find('[data-solution]:not(.loaded)')
                .map(createQuestion)
                .toArray()
        );
    }
    mw.hook('wikipage.content').add(hook);
})();