Справица:Solutions.js — разлика између измена
Пређи на навигацију
Пређи на претрагу
м (Minimalna kompatibilna verzija) |
м (18px -> 20px) |
||
| (8 међуизмена истог корисника није приказано) | |||
| Ред 1: | Ред 1: | ||
/* eslint-disable max-statements */ | |||
(function() { | (function() { | ||
'use strict'; | 'use strict'; | ||
var $body = $('body'); | var $body = $('body'); | ||
var questions = []; | |||
var allSolutionsShown = false; | |||
var CORRECT_SRC = 'https://upload.wikimedia.org/wikipedia/en/thumb/f/fb/Yes_check.svg/20px-Yes_check.svg.png'; | |||
var INCORRECT_SRC = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/X_mark.svg/20px-X_mark.svg.png'; | |||
var staticId = 0; | |||
var questionIdMap = {}; | |||
var interactiveMode = null; | |||
function createButton(msg) { | function createButton(msg) { | ||
return $('<span>', { | return $('<span>', { | ||
| Ред 25: | Ред 34: | ||
} | } | ||
function click() { | 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'); | var $label = $(this).find('.label'); | ||
if ( | allSolutionsShown = !allSolutionsShown; | ||
$ | if (allSolutionsShown) { | ||
$label.text(mw.message('gadget-solutions-hide-all').plain()); | |||
} else { | |||
$label.text(mw.message('gadget-solutions-show-all').plain()); | $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 { | } 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) { | 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 | $content | ||
// TODO: unhide -> show | |||
.find('.unhide-solutions:not(.loaded)') | .find('.unhide-solutions:not(.loaded)') | ||
.addClass('loaded') | .addClass('loaded') | ||
.append(createButton( | .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); | mw.hook('wikipage.content').add(hook); | ||
})(); | })(); | ||
Тренутна верзија на датум 18. март 2026. у 22:40
/* 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/20px-Yes_check.svg.png';
var INCORRECT_SRC = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/X_mark.svg/20px-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);
})();