MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
Remove WikiEditor insert-file button |
WikiEditor insert dialogs |
||
| Line 579: | Line 579: | ||
return; | return; | ||
} | } | ||
var AB_INSERT_LEVEL_FIELDS = [ | |||
{ data: '', label: 'Infobox' }, | |||
{ data: 'plays', label: 'Plays' }, | |||
{ data: 'creator', label: 'Creator' }, | |||
{ data: 'title', label: 'Title' }, | |||
{ data: 'description', label: 'Description' }, | |||
{ data: 'clears', label: 'Clears' }, | |||
{ data: 'favorites', label: 'Favorites' }, | |||
{ data: 'difficulty', label: 'Difficulty' } | |||
]; | |||
var AB_INSERT_PLAYER_FIELDS = [ | |||
{ data: '', label: 'Infobox' }, | |||
{ data: 'name', label: 'Name' }, | |||
{ data: 'role', label: 'Role' }, | |||
{ data: 'levels', label: 'Levels' }, | |||
{ data: 'clears', label: 'Clears' }, | |||
{ data: 'followers', label: 'Followers' } | |||
]; | |||
var AB_INSERT_ALIGN = [ | |||
{ data: '', label: 'None' }, | |||
{ data: 'left', label: 'Left' }, | |||
{ data: 'right', label: 'Right' }, | |||
{ data: 'center', label: 'Center' }, | |||
{ data: 'inline', label: 'Inline' } | |||
]; | |||
function abInsertSanitize(value) { | |||
return String(value || '') | |||
.replace(/\|/g, '') | |||
.replace(/\{\{/g, '') | |||
.replace(/\}\}/g, '') | |||
.trim(); | |||
} | |||
function abInsertDigits(value) { | |||
return String(value || '') | |||
.replace(/^rbxassetid:\/\//i, '') | |||
.replace(/[^0-9]/g, ''); | |||
} | |||
function abInsertIsUrl(value) { | |||
return /^https?:\/\//i.test(String(value || '').trim()); | |||
} | |||
function abInsertTemplate(name, id, field) { | |||
var clean = abInsertDigits(id); | |||
if (!clean) { | |||
return ''; | |||
} | |||
if (field) { | |||
return '{{' + name + '|' + clean + '|' + field + '}}'; | |||
} | |||
return '{{' + name + '|' + clean + '}}'; | |||
} | |||
function abInsertImageWikitext(values) { | |||
var source = abInsertSanitize(values.source); | |||
if (!source) { | |||
return ''; | |||
} | |||
if (!abInsertIsUrl(source)) { | |||
source = abInsertDigits(source); | |||
if (!source) { | |||
return ''; | |||
} | |||
} | |||
var bits = ['{{image', source]; | |||
var width = abInsertDigits(values.width); | |||
var height = abInsertDigits(values.height); | |||
if (width && height) { | |||
bits.push(width + 'x' + height + 'px'); | |||
} else if (width) { | |||
bits.push(width + 'px'); | |||
} else if (height) { | |||
bits.push('x' + height + 'px'); | |||
} | |||
if (values.align) { | |||
bits.push(values.align); | |||
} | |||
if (values.caption) { | |||
bits.push(abInsertSanitize(values.caption)); | |||
} | |||
if (values.alt) { | |||
bits.push('alt=' + abInsertSanitize(values.alt)); | |||
} | |||
if (values.link) { | |||
bits.push('link=' + abInsertSanitize(values.link)); | |||
} | |||
return bits[0] + '|' + bits.slice(1).join('|') + '}}'; | |||
} | |||
var abInsertManager = null; | |||
var AbWikiInsertDialog = null; | |||
function abEnsureInsertDialog() { | |||
if (AbWikiInsertDialog) { | |||
return; | |||
} | |||
AbWikiInsertDialog = function (config) { | |||
AbWikiInsertDialog.super.call(this, config); | |||
this.abKind = config.abKind; | |||
this.abTextarea = config.abTextarea; | |||
}; | |||
OO.inheritClass(AbWikiInsertDialog, OO.ui.ProcessDialog); | |||
AbWikiInsertDialog.static.name = 'abWikiInsert'; | |||
AbWikiInsertDialog.static.title = 'Insert'; | |||
AbWikiInsertDialog.static.actions = [ | |||
{ action: 'insert', label: 'Insert', flags: ['primary', 'progressive'] }, | |||
{ action: 'cancel', label: 'Cancel', flags: 'safe' } | |||
]; | |||
AbWikiInsertDialog.prototype.getSetupProcess = function (data) { | |||
data = data || {}; | |||
this.abKind = data.abKind || this.abKind; | |||
this.abTextarea = data.abTextarea || this.abTextarea; | |||
var dialog = this; | |||
return AbWikiInsertDialog.super.prototype.getSetupProcess.call(this, data).next(function () { | |||
var titles = { | |||
level: 'Insert level', | |||
player: 'Insert player', | |||
image: 'Insert image' | |||
}; | |||
if (dialog.title && dialog.title.setLabel) { | |||
dialog.title.setLabel(titles[dialog.abKind] || 'Insert'); | |||
} | |||
dialog.setSize(dialog.abKind === 'image' ? 'large' : 'medium'); | |||
dialog.abResetFromSelection(); | |||
dialog.abUpdatePreview(); | |||
dialog.updateSize(); | |||
}); | |||
}; | |||
AbWikiInsertDialog.prototype.initialize = function () { | |||
AbWikiInsertDialog.super.prototype.initialize.call(this); | |||
this.abSource = new OO.ui.TextInputWidget({ | |||
placeholder: '123', | |||
autocomplete: false | |||
}); | |||
this.abField = new OO.ui.DropdownInputWidget({ | |||
options: AB_INSERT_LEVEL_FIELDS | |||
}); | |||
this.abWidth = new OO.ui.TextInputWidget({ | |||
placeholder: '300' | |||
}); | |||
this.abHeight = new OO.ui.TextInputWidget({ | |||
placeholder: 'optional' | |||
}); | |||
this.abAlign = new OO.ui.DropdownInputWidget({ | |||
options: AB_INSERT_ALIGN | |||
}); | |||
this.abCaption = new OO.ui.TextInputWidget(); | |||
this.abAlt = new OO.ui.TextInputWidget(); | |||
this.abLink = new OO.ui.TextInputWidget(); | |||
this.abPreview = new OO.ui.TextInputWidget({ | |||
readOnly: true | |||
}); | |||
this.abSourceLayout = new OO.ui.FieldLayout(this.abSource, { | |||
label: 'ID', | |||
align: 'top' | |||
}); | |||
this.abFieldLayout = new OO.ui.FieldLayout(this.abField, { | |||
label: 'Insert as', | |||
align: 'top' | |||
}); | |||
this.abSizeLayout = new OO.ui.HorizontalLayout({ | |||
items: [ | |||
new OO.ui.FieldLayout(this.abWidth, { label: 'Width (px)', align: 'top' }), | |||
new OO.ui.FieldLayout(this.abHeight, { label: 'Height (px)', align: 'top' }) | |||
] | |||
}); | |||
this.abAlignLayout = new OO.ui.FieldLayout(this.abAlign, { | |||
label: 'Align', | |||
align: 'top' | |||
}); | |||
this.abCaptionLayout = new OO.ui.FieldLayout(this.abCaption, { | |||
label: 'Caption', | |||
align: 'top' | |||
}); | |||
this.abAltLayout = new OO.ui.FieldLayout(this.abAlt, { | |||
label: 'Alt text', | |||
align: 'top' | |||
}); | |||
this.abLinkLayout = new OO.ui.FieldLayout(this.abLink, { | |||
label: 'Link', | |||
align: 'top' | |||
}); | |||
this.abPreviewLayout = new OO.ui.FieldLayout(this.abPreview, { | |||
label: 'Wikitext', | |||
align: 'top' | |||
}); | |||
this.abForm = new OO.ui.PanelLayout({ | |||
padded: true, | |||
expanded: false | |||
}); | |||
this.abForm.$element.append( | |||
this.abSourceLayout.$element, | |||
this.abFieldLayout.$element, | |||
this.abSizeLayout.$element, | |||
this.abAlignLayout.$element, | |||
this.abCaptionLayout.$element, | |||
this.abAltLayout.$element, | |||
this.abLinkLayout.$element, | |||
this.abPreviewLayout.$element | |||
); | |||
this.$body.append(this.abForm.$element); | |||
this.$element.addClass('ab-wiki-insert-dialog'); | |||
var dialog = this; | |||
function onChange() { | |||
dialog.abUpdatePreview(); | |||
} | |||
this.abSource.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
this.abField.connect(this, { change: onChange }); | |||
this.abWidth.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
this.abHeight.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
this.abAlign.connect(this, { change: onChange }); | |||
this.abCaption.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
this.abAlt.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
this.abLink.connect(this, { change: onChange, enter: 'abTryInsert' }); | |||
}; | |||
AbWikiInsertDialog.prototype.abTryInsert = function () { | |||
if (this.abCurrentWikitext()) { | |||
this.executeAction('insert'); | |||
} | |||
}; | |||
AbWikiInsertDialog.prototype.getReadyProcess = function (data) { | |||
var dialog = this; | |||
return AbWikiInsertDialog.super.prototype.getReadyProcess.call(this, data).next(function () { | |||
dialog.abSource.focus(); | |||
}); | |||
}; | |||
AbWikiInsertDialog.prototype.abResetFromSelection = function () { | |||
var kind = this.abKind; | |||
var selected = ''; | |||
if (this.abTextarea && this.abTextarea.textSelection) { | |||
selected = String(this.abTextarea.textSelection('getSelection') || '').trim(); | |||
} | |||
this.abSource.setValue(''); | |||
this.abField.setValue(''); | |||
this.abWidth.setValue(''); | |||
this.abHeight.setValue(''); | |||
this.abAlign.setValue(''); | |||
this.abCaption.setValue(''); | |||
this.abAlt.setValue(''); | |||
this.abLink.setValue(''); | |||
if (kind === 'image') { | |||
this.abSource.setPlaceholder('123 or https://'); | |||
this.abSourceLayout.setLabel('Asset ID or URL'); | |||
this.abField.setOptions(AB_INSERT_LEVEL_FIELDS); | |||
this.abFieldLayout.toggle(false); | |||
this.abSizeLayout.toggle(true); | |||
this.abAlignLayout.toggle(true); | |||
this.abCaptionLayout.toggle(true); | |||
this.abAltLayout.toggle(true); | |||
this.abLinkLayout.toggle(true); | |||
if (selected) { | |||
this.abSource.setValue(selected); | |||
} | |||
} else { | |||
this.abSource.setPlaceholder('123'); | |||
this.abSourceLayout.setLabel(kind === 'player' ? 'Player ID' : 'Level ID'); | |||
this.abField.setOptions( | |||
kind === 'player' ? AB_INSERT_PLAYER_FIELDS : AB_INSERT_LEVEL_FIELDS | |||
); | |||
this.abFieldLayout.toggle(true); | |||
this.abSizeLayout.toggle(false); | |||
this.abAlignLayout.toggle(false); | |||
this.abCaptionLayout.toggle(false); | |||
this.abAltLayout.toggle(false); | |||
this.abLinkLayout.toggle(false); | |||
if (abInsertDigits(selected)) { | |||
this.abSource.setValue(abInsertDigits(selected)); | |||
} | |||
} | |||
}; | |||
AbWikiInsertDialog.prototype.abCurrentWikitext = function () { | |||
if (this.abKind === 'image') { | |||
return abInsertImageWikitext({ | |||
source: this.abSource.getValue(), | |||
width: this.abWidth.getValue(), | |||
height: this.abHeight.getValue(), | |||
align: this.abAlign.getValue(), | |||
caption: this.abCaption.getValue(), | |||
alt: this.abAlt.getValue(), | |||
link: this.abLink.getValue() | |||
}); | |||
} | |||
return abInsertTemplate( | |||
this.abKind === 'player' ? 'player' : 'level', | |||
this.abSource.getValue(), | |||
this.abField.getValue() | |||
); | |||
}; | |||
AbWikiInsertDialog.prototype.abUpdatePreview = function () { | |||
var wikitext = this.abCurrentWikitext(); | |||
this.abPreview.setValue(wikitext); | |||
this.actions.setAbilities({ insert: !!wikitext }); | |||
}; | |||
AbWikiInsertDialog.prototype.getBodyHeight = function () { | |||
return Math.max(this.$body[0].scrollHeight, this.abKind === 'image' ? 380 : 200); | |||
}; | |||
AbWikiInsertDialog.prototype.getActionProcess = function (action) { | |||
var dialog = this; | |||
if (action === 'insert') { | |||
return new OO.ui.Process(function () { | |||
var wikitext = dialog.abCurrentWikitext(); | |||
if (wikitext && dialog.abTextarea) { | |||
dialog.abTextarea.textSelection('encapsulateSelection', { | |||
pre: '', | |||
peri: wikitext, | |||
post: '', | |||
replace: true, | |||
selectPeri: false | |||
}); | |||
} | |||
dialog.close({ action: action }); | |||
}); | |||
} | |||
if (action === 'cancel') { | |||
return new OO.ui.Process(function () { | |||
dialog.close({ action: action }); | |||
}); | |||
} | |||
return AbWikiInsertDialog.super.prototype.getActionProcess.call(this, action); | |||
}; | |||
} | |||
function abOpenInsertDialog($textarea, kind) { | |||
mw.loader.using(['oojs-ui-windows', 'oojs-ui-widgets']).then(function () { | |||
abEnsureInsertDialog(); | |||
if (!abInsertManager) { | |||
abInsertManager = new OO.ui.WindowManager(); | |||
$(document.body).append(abInsertManager.$element); | |||
abInsertManager.addWindows([new AbWikiInsertDialog({ size: 'medium' })]); | |||
} | |||
abInsertManager.openWindow('abWikiInsert', { | |||
abKind: kind, | |||
abTextarea: $textarea | |||
}); | |||
}); | |||
} | |||
mw.hook('wikiEditor.toolbarReady').add(function ($textarea) { | mw.hook('wikiEditor.toolbarReady').add(function ($textarea) { | ||
if (!$textarea || !$textarea.wikiEditor) { | if (!$textarea || !$textarea.wikiEditor) { | ||
| Line 598: | Line 953: | ||
oouiIcon: 'play', | oouiIcon: 'play', | ||
action: { | action: { | ||
type: ' | type: 'callback', | ||
execute: function () { | |||
abOpenInsertDialog($textarea, 'level'); | |||
} | } | ||
} | } | ||
| Line 611: | Line 964: | ||
oouiIcon: 'userAvatar', | oouiIcon: 'userAvatar', | ||
action: { | action: { | ||
type: ' | type: 'callback', | ||
execute: function () { | |||
abOpenInsertDialog($textarea, 'player'); | |||
} | } | ||
} | } | ||
| Line 624: | Line 975: | ||
oouiIcon: 'image', | oouiIcon: 'image', | ||
action: { | action: { | ||
type: ' | type: 'callback', | ||
execute: function () { | |||
abOpenInsertDialog($textarea, 'image'); | |||
} | } | ||
} | } | ||
Revision as of 19:21, 29 August 2026
$(function () {
if (!mw.config.get('wgIsMainPage')) {
return;
}
function hideMainPageComments() {
$('#ext-comments-container, .ext-comments-comments-list, .comment-list-toolbar').hide();
}
hideMainPageComments();
$('.home-card-action a').attr({
target: '_blank',
rel: 'noopener noreferrer'
});
if (mw.hook) {
mw.hook('wikipage.content').add(hideMainPageComments);
}
setTimeout(hideMainPageComments, 400);
});
var AB_DIFFICULTY = { 1: 'Easy', 2: 'Normal', 3: 'Hard', 4: 'INSANE' };
function abSetDifficulty($els, difficulty) {
if (!$els || !$els.length) {
return;
}
$els.removeClass('ab-difficulty-1 ab-difficulty-2 ab-difficulty-3 ab-difficulty-4');
var label = AB_DIFFICULTY[difficulty];
if (!label) {
$els.text('-').attr('data-difficulty', '0');
return;
}
$els
.addClass('ab-difficulty ab-difficulty-' + difficulty)
.attr('data-difficulty', String(difficulty))
.text(label);
}
function abCompactCount(n) {
var num = Number(n) || 0;
if (num >= 1e9) {
return (num / 1e9).toFixed(1).replace(/\.0$/, '') + 'B';
}
if (num >= 1e6) {
return (num / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
}
if (num >= 1e3) {
return (num / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
}
return String(Math.floor(num));
}
var AB_ROLE_NAMES = {
owner: 'Owner',
moderator: 'Moderator',
admin: 'Admin',
scout: 'Scout'
};
function abFormatRole(role) {
if (role == null) {
return '';
}
role = String(role).trim();
if (!role) {
return '';
}
var lower = role.toLowerCase();
if (lower === 'user') {
return '';
}
if (AB_ROLE_NAMES[lower]) {
return AB_ROLE_NAMES[lower];
}
return lower.replace(/[_-]+/g, ' ').replace(/\b\w/g, function (ch) {
return ch.toUpperCase();
});
}
function abSetRole($els, role) {
if (!$els || !$els.length) {
return;
}
$els.removeClass('ab-role-owner ab-role-moderator ab-role-scout');
var slug = String(role || '')
.trim()
.toLowerCase();
var label = abFormatRole(role);
if (!label) {
$els.text('').removeAttr('data-role').removeClass('ab-role');
return;
}
$els.addClass('ab-role');
if (slug === 'owner' || slug === 'moderator' || slug === 'scout') {
$els.addClass('ab-role-' + slug);
}
$els.attr('data-role', slug).text(label);
}
$(function () {
var levelId = mw.config.get('abLevelId');
var $box = $('.ab-level-infobox');
if (!levelId || !$box.length) {
return;
}
var defaultThumbs = ['82374078680299', '105249369413891', '104825146904235'];
function thumbAssetId(level) {
if (!level || level.locked) {
return defaultThumbs[0];
}
var raw = String(level.thumbnail || '').replace(/^rbxassetid:\/\//i, '').replace(/[^0-9]/g, '');
if (raw && raw !== '0') {
return raw;
}
return defaultThumbs[Math.abs(Number(level.id) || Number(levelId) || 0) % defaultThumbs.length];
}
function setThumb(assetId) {
var $thumb = $box.find('.ab-level-thumb');
if (!$thumb.length || !assetId) {
return;
}
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=thumbnails&assetIds=' +
encodeURIComponent(assetId) +
'&size=' +
encodeURIComponent('420x420')
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var item = data && data.data && data.data[0];
if (!item || item.state !== 'Completed' || !item.imageUrl) {
return;
}
$thumb.attr('src', item.imageUrl).removeClass('ab-level-thumb--placeholder');
})
.catch(function () {});
}
function atUsername(name) {
name = String(name || '')
.replace(/^@+/, '')
.trim();
return name ? '@' + name : '';
}
function setCreator(level) {
var $el = $box.find('.ab-level-creator');
if (!$el.length || !level) {
return;
}
var creatorId = String(level.creator || '').replace(/[^0-9]/g, '');
function apply(name) {
var label = atUsername(name);
if (!label) {
return;
}
var $link = $el.find('a');
if ($link.length) {
$link.text(label);
return;
}
if (!creatorId) {
$el.text(label);
return;
}
$('<a>')
.attr('href', 'https://adventure-builder.net/players/' + encodeURIComponent(creatorId))
.attr('target', '_blank')
.attr('rel', 'noopener noreferrer')
.text(label)
.appendTo($el.empty());
}
var seeded = level.creator_name || level.creatorName;
if (seeded) {
apply(seeded);
return;
}
if (!creatorId) {
return;
}
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=username&userId=' +
encodeURIComponent(creatorId)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
apply(data && (data.name || data.displayName));
})
.catch(function () {});
}
fetch('https://adventure-builder.net/api/v1/levels/' + encodeURIComponent(levelId))
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var level = data && (data.level || data);
if (!level || level.locked || !level.id) {
setThumb(defaultThumbs[0]);
return;
}
if (level.title) {
$box.find('.pi-title').text(level.title);
}
if (level.id) {
$box.find('.ab-level-stat[data-stat="id"]').text(String(level.id));
}
setCreator(level);
[
['plays', level.plays],
['clears', level.clears],
['favorites', level.favorites]
].forEach(function (pair) {
$box.find('.ab-level-stat[data-stat="' + pair[0] + '"]').text(
abCompactCount(pair[1])
);
});
$box.find('.ab-level-stat[data-stat="featured"]').text(
level.featured ? 'Yes' : 'No'
);
abSetDifficulty($box.find('.ab-level-stat[data-stat="difficulty"]'), level.difficulty);
setThumb(thumbAssetId(level));
})
.catch(function () {
if ($box.find('.ab-level-thumb--placeholder').length) {
setThumb(defaultThumbs[Math.abs(Number(levelId) || 0) % defaultThumbs.length]);
}
});
});
$(function () {
var $bits = $('.ab-inline-level');
if (!$bits.length) {
return;
}
function atUsername(name) {
name = String(name || '')
.replace(/^@+/, '')
.trim();
return name ? '@' + name : '';
}
function applyLevel(id, level) {
var $els = $('.ab-inline-level[data-level-id="' + id + '"]');
if (!level || level.locked || !level.id) {
return;
}
$els.filter('[data-stat="plays"]').text(abCompactCount(level.plays));
$els.filter('[data-stat="clears"]').text(abCompactCount(level.clears));
$els.filter('[data-stat="favorites"]').text(abCompactCount(level.favorites));
if (level.title) {
$els.filter('[data-stat="title"]').text(level.title);
}
if (Object.prototype.hasOwnProperty.call(level, 'description')) {
var desc = String(level.description || '')
.replace(/\s+/g, ' ')
.trim();
$els.filter('[data-stat="description"]').text(desc || '-');
}
$els.filter('[data-stat="featured"]').text(level.featured ? 'Yes' : 'No');
abSetDifficulty($els.filter('[data-stat="difficulty"]'), level.difficulty);
var $creator = $els.filter('[data-stat="creator"]');
if ($creator.length) {
var creatorId = String(level.creator || $creator.attr('data-creator-id') || '').replace(
/[^0-9]/g,
''
);
function setCreatorName(name) {
var label = atUsername(name);
if (!label) {
return;
}
$creator.text(label);
if (creatorId) {
$creator.attr(
'href',
'https://adventure-builder.net/players/' + encodeURIComponent(creatorId)
);
}
}
if (level.creator_name || level.creatorName) {
setCreatorName(level.creator_name || level.creatorName);
return;
}
if (!creatorId) {
return;
}
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=username&userId=' +
encodeURIComponent(creatorId)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
setCreatorName(data && (data.name || data.displayName));
})
.catch(function () {});
}
}
var ids = [];
$bits.each(function () {
var id = String($(this).attr('data-level-id') || '').replace(/[^0-9]/g, '');
if (id && ids.indexOf(id) === -1) {
ids.push(id);
}
});
ids.forEach(function (id) {
fetch('https://adventure-builder.net/api/v1/levels/' + encodeURIComponent(id))
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
applyLevel(id, data && (data.level || data));
})
.catch(function () {});
});
});
$(function () {
var $bits = $('.ab-inline-player');
if (!$bits.length) {
return;
}
function atUsername(name) {
name = String(name || '')
.replace(/^@+/, '')
.trim();
return name ? '@' + name : '';
}
function applyPlayer(id, profile, name) {
var $els = $('.ab-inline-player[data-player-id="' + id + '"]');
if (name) {
$els.filter('[data-stat="name"]').text(atUsername(name));
}
if (!profile) {
return;
}
$els.filter('[data-stat="levels"]').text(abCompactCount(profile.levels_count));
$els.filter('[data-stat="clears"]').text(abCompactCount(profile.clears));
$els.filter('[data-stat="followers"]').text(abCompactCount(profile.followers));
abSetRole($els.filter('[data-stat="role"]'), profile.role);
}
var ids = [];
$bits.each(function () {
var id = String($(this).attr('data-player-id') || '').replace(/[^0-9]/g, '');
if (id && ids.indexOf(id) === -1) {
ids.push(id);
}
});
ids.forEach(function (id) {
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=username&userId=' +
encodeURIComponent(id)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var name = data && (data.name || data.displayName);
if (name) {
applyPlayer(id, null, name);
}
})
.catch(function () {});
fetch('https://adventure-builder.net/api/v1/profiles/' + encodeURIComponent(id))
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
applyPlayer(id, data && (data.profile || data), null);
})
.catch(function () {});
});
});
$(function () {
var userId = mw.config.get('abRobloxUserId');
if (!userId) {
return;
}
var $img = $('.profile-avatar-image');
if ($img.length) {
var fallback = $img.attr('src');
$img.on('error', function () {
$img.attr('src', fallback);
});
$img.attr(
'src',
'https://adventure-builder.net/api/roblox-proxy?service=profile-picture&userId=' +
encodeURIComponent(userId)
);
}
var $stats = $('.profile-header-statistics');
if (!$stats.length) {
return;
}
fetch(
'https://adventure-builder.net/api/v1/profiles/' + encodeURIComponent(userId)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (profile) {
if (!profile) {
return;
}
[
[profile.levels_count, 'Levels'],
[profile.clears, 'Clears'],
[profile.followers, 'Followers']
].forEach(function (pair) {
$('<li>')
.addClass('ab-central-stat')
.text(abCompactCount(pair[0]) + ' ' + pair[1])
.appendTo($stats);
});
})
.catch(function () {});
});
$(function () {
var playerId = mw.config.get('abPlayerId');
var $box = $('.ab-player-infobox');
if (!playerId || !$box.length) {
return;
}
function atUsername(name) {
name = String(name || '')
.replace(/^@+/, '')
.trim();
return name ? '@' + name : '';
}
function setPfp() {
var $thumb = $box.find('.ab-player-thumb');
if (!$thumb.length) {
return;
}
$thumb
.on('error', function () {
$thumb.css('visibility', 'hidden');
})
.attr(
'src',
'https://adventure-builder.net/api/roblox-proxy?service=profile-picture&userId=' +
encodeURIComponent(playerId)
)
.removeClass('ab-player-thumb--placeholder');
}
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=username&userId=' +
encodeURIComponent(playerId)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var label = atUsername(data && (data.name || data.displayName));
if (label) {
$box.find('.ab-player-title').text(label);
$box.find('.ab-player-thumb').attr('alt', label);
}
})
.catch(function () {});
fetch('https://adventure-builder.net/api/v1/profiles/' + encodeURIComponent(playerId))
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var profile = data && (data.profile || data);
if (!profile) {
return;
}
[
['levels', profile.levels_count],
['clears', profile.clears],
['followers', profile.followers]
].forEach(function (pair) {
$box.find('.ab-level-stat[data-stat="' + pair[0] + '"]').text(
abCompactCount(pair[1])
);
});
setRole(profile.role);
})
.catch(function () {});
function setRole(role) {
var label = abFormatRole(role);
var $stat = $box.find('.ab-level-stat[data-stat="role"]');
var $row = $stat.closest('.pi-data');
if (!label) {
$row.remove();
return;
}
if (!$row.length) {
$row = $(
'<div class="pi-item pi-data pi-item-spacing pi-border-color">' +
'<div class="pi-data-label">Role</div>' +
'<div class="pi-data-value ab-level-stat" data-stat="role"></div>' +
'</div>'
);
$stat = $row.find('[data-stat="role"]');
}
abSetRole($stat, role);
var $levels = $box.find('.ab-level-stat[data-stat="levels"]').closest('.pi-data');
if ($levels.length) {
$levels.before($row);
} else {
var $nav = $box.find('.pi-navigation');
if ($nav.length) {
$nav.before($row);
} else {
$box.append($row);
}
}
}
setPfp();
});
$(function () {
var $imgs = $('.roblox-asset img[data-asset-id].roblox-asset-img--pending');
if (!$imgs.length) {
return;
}
$imgs.each(function () {
var $img = $(this);
var assetId = String($img.attr('data-asset-id') || '').replace(/[^0-9]/g, '');
var size = String($img.attr('data-thumb-size') || '420x420');
if (!assetId) {
return;
}
fetch(
'https://adventure-builder.net/api/roblox-proxy?service=thumbnails&assetIds=' +
encodeURIComponent(assetId) +
'&size=' +
encodeURIComponent(size)
)
.then(function (res) {
return res.ok ? res.json() : null;
})
.then(function (data) {
var item = data && data.data && data.data[0];
if (!item || item.state !== 'Completed' || !item.imageUrl) {
return;
}
$img.attr('src', item.imageUrl).removeClass('roblox-asset-img--pending');
})
.catch(function () {});
});
});
$(function () {
var action = mw.config.get('wgAction');
if (action !== 'edit' && action !== 'submit') {
return;
}
if (!mw.hook) {
return;
}
var AB_INSERT_LEVEL_FIELDS = [
{ data: '', label: 'Infobox' },
{ data: 'plays', label: 'Plays' },
{ data: 'creator', label: 'Creator' },
{ data: 'title', label: 'Title' },
{ data: 'description', label: 'Description' },
{ data: 'clears', label: 'Clears' },
{ data: 'favorites', label: 'Favorites' },
{ data: 'difficulty', label: 'Difficulty' }
];
var AB_INSERT_PLAYER_FIELDS = [
{ data: '', label: 'Infobox' },
{ data: 'name', label: 'Name' },
{ data: 'role', label: 'Role' },
{ data: 'levels', label: 'Levels' },
{ data: 'clears', label: 'Clears' },
{ data: 'followers', label: 'Followers' }
];
var AB_INSERT_ALIGN = [
{ data: '', label: 'None' },
{ data: 'left', label: 'Left' },
{ data: 'right', label: 'Right' },
{ data: 'center', label: 'Center' },
{ data: 'inline', label: 'Inline' }
];
function abInsertSanitize(value) {
return String(value || '')
.replace(/\|/g, '')
.replace(/\{\{/g, '')
.replace(/\}\}/g, '')
.trim();
}
function abInsertDigits(value) {
return String(value || '')
.replace(/^rbxassetid:\/\//i, '')
.replace(/[^0-9]/g, '');
}
function abInsertIsUrl(value) {
return /^https?:\/\//i.test(String(value || '').trim());
}
function abInsertTemplate(name, id, field) {
var clean = abInsertDigits(id);
if (!clean) {
return '';
}
if (field) {
return '{{' + name + '|' + clean + '|' + field + '}}';
}
return '{{' + name + '|' + clean + '}}';
}
function abInsertImageWikitext(values) {
var source = abInsertSanitize(values.source);
if (!source) {
return '';
}
if (!abInsertIsUrl(source)) {
source = abInsertDigits(source);
if (!source) {
return '';
}
}
var bits = ['{{image', source];
var width = abInsertDigits(values.width);
var height = abInsertDigits(values.height);
if (width && height) {
bits.push(width + 'x' + height + 'px');
} else if (width) {
bits.push(width + 'px');
} else if (height) {
bits.push('x' + height + 'px');
}
if (values.align) {
bits.push(values.align);
}
if (values.caption) {
bits.push(abInsertSanitize(values.caption));
}
if (values.alt) {
bits.push('alt=' + abInsertSanitize(values.alt));
}
if (values.link) {
bits.push('link=' + abInsertSanitize(values.link));
}
return bits[0] + '|' + bits.slice(1).join('|') + '}}';
}
var abInsertManager = null;
var AbWikiInsertDialog = null;
function abEnsureInsertDialog() {
if (AbWikiInsertDialog) {
return;
}
AbWikiInsertDialog = function (config) {
AbWikiInsertDialog.super.call(this, config);
this.abKind = config.abKind;
this.abTextarea = config.abTextarea;
};
OO.inheritClass(AbWikiInsertDialog, OO.ui.ProcessDialog);
AbWikiInsertDialog.static.name = 'abWikiInsert';
AbWikiInsertDialog.static.title = 'Insert';
AbWikiInsertDialog.static.actions = [
{ action: 'insert', label: 'Insert', flags: ['primary', 'progressive'] },
{ action: 'cancel', label: 'Cancel', flags: 'safe' }
];
AbWikiInsertDialog.prototype.getSetupProcess = function (data) {
data = data || {};
this.abKind = data.abKind || this.abKind;
this.abTextarea = data.abTextarea || this.abTextarea;
var dialog = this;
return AbWikiInsertDialog.super.prototype.getSetupProcess.call(this, data).next(function () {
var titles = {
level: 'Insert level',
player: 'Insert player',
image: 'Insert image'
};
if (dialog.title && dialog.title.setLabel) {
dialog.title.setLabel(titles[dialog.abKind] || 'Insert');
}
dialog.setSize(dialog.abKind === 'image' ? 'large' : 'medium');
dialog.abResetFromSelection();
dialog.abUpdatePreview();
dialog.updateSize();
});
};
AbWikiInsertDialog.prototype.initialize = function () {
AbWikiInsertDialog.super.prototype.initialize.call(this);
this.abSource = new OO.ui.TextInputWidget({
placeholder: '123',
autocomplete: false
});
this.abField = new OO.ui.DropdownInputWidget({
options: AB_INSERT_LEVEL_FIELDS
});
this.abWidth = new OO.ui.TextInputWidget({
placeholder: '300'
});
this.abHeight = new OO.ui.TextInputWidget({
placeholder: 'optional'
});
this.abAlign = new OO.ui.DropdownInputWidget({
options: AB_INSERT_ALIGN
});
this.abCaption = new OO.ui.TextInputWidget();
this.abAlt = new OO.ui.TextInputWidget();
this.abLink = new OO.ui.TextInputWidget();
this.abPreview = new OO.ui.TextInputWidget({
readOnly: true
});
this.abSourceLayout = new OO.ui.FieldLayout(this.abSource, {
label: 'ID',
align: 'top'
});
this.abFieldLayout = new OO.ui.FieldLayout(this.abField, {
label: 'Insert as',
align: 'top'
});
this.abSizeLayout = new OO.ui.HorizontalLayout({
items: [
new OO.ui.FieldLayout(this.abWidth, { label: 'Width (px)', align: 'top' }),
new OO.ui.FieldLayout(this.abHeight, { label: 'Height (px)', align: 'top' })
]
});
this.abAlignLayout = new OO.ui.FieldLayout(this.abAlign, {
label: 'Align',
align: 'top'
});
this.abCaptionLayout = new OO.ui.FieldLayout(this.abCaption, {
label: 'Caption',
align: 'top'
});
this.abAltLayout = new OO.ui.FieldLayout(this.abAlt, {
label: 'Alt text',
align: 'top'
});
this.abLinkLayout = new OO.ui.FieldLayout(this.abLink, {
label: 'Link',
align: 'top'
});
this.abPreviewLayout = new OO.ui.FieldLayout(this.abPreview, {
label: 'Wikitext',
align: 'top'
});
this.abForm = new OO.ui.PanelLayout({
padded: true,
expanded: false
});
this.abForm.$element.append(
this.abSourceLayout.$element,
this.abFieldLayout.$element,
this.abSizeLayout.$element,
this.abAlignLayout.$element,
this.abCaptionLayout.$element,
this.abAltLayout.$element,
this.abLinkLayout.$element,
this.abPreviewLayout.$element
);
this.$body.append(this.abForm.$element);
this.$element.addClass('ab-wiki-insert-dialog');
var dialog = this;
function onChange() {
dialog.abUpdatePreview();
}
this.abSource.connect(this, { change: onChange, enter: 'abTryInsert' });
this.abField.connect(this, { change: onChange });
this.abWidth.connect(this, { change: onChange, enter: 'abTryInsert' });
this.abHeight.connect(this, { change: onChange, enter: 'abTryInsert' });
this.abAlign.connect(this, { change: onChange });
this.abCaption.connect(this, { change: onChange, enter: 'abTryInsert' });
this.abAlt.connect(this, { change: onChange, enter: 'abTryInsert' });
this.abLink.connect(this, { change: onChange, enter: 'abTryInsert' });
};
AbWikiInsertDialog.prototype.abTryInsert = function () {
if (this.abCurrentWikitext()) {
this.executeAction('insert');
}
};
AbWikiInsertDialog.prototype.getReadyProcess = function (data) {
var dialog = this;
return AbWikiInsertDialog.super.prototype.getReadyProcess.call(this, data).next(function () {
dialog.abSource.focus();
});
};
AbWikiInsertDialog.prototype.abResetFromSelection = function () {
var kind = this.abKind;
var selected = '';
if (this.abTextarea && this.abTextarea.textSelection) {
selected = String(this.abTextarea.textSelection('getSelection') || '').trim();
}
this.abSource.setValue('');
this.abField.setValue('');
this.abWidth.setValue('');
this.abHeight.setValue('');
this.abAlign.setValue('');
this.abCaption.setValue('');
this.abAlt.setValue('');
this.abLink.setValue('');
if (kind === 'image') {
this.abSource.setPlaceholder('123 or https://');
this.abSourceLayout.setLabel('Asset ID or URL');
this.abField.setOptions(AB_INSERT_LEVEL_FIELDS);
this.abFieldLayout.toggle(false);
this.abSizeLayout.toggle(true);
this.abAlignLayout.toggle(true);
this.abCaptionLayout.toggle(true);
this.abAltLayout.toggle(true);
this.abLinkLayout.toggle(true);
if (selected) {
this.abSource.setValue(selected);
}
} else {
this.abSource.setPlaceholder('123');
this.abSourceLayout.setLabel(kind === 'player' ? 'Player ID' : 'Level ID');
this.abField.setOptions(
kind === 'player' ? AB_INSERT_PLAYER_FIELDS : AB_INSERT_LEVEL_FIELDS
);
this.abFieldLayout.toggle(true);
this.abSizeLayout.toggle(false);
this.abAlignLayout.toggle(false);
this.abCaptionLayout.toggle(false);
this.abAltLayout.toggle(false);
this.abLinkLayout.toggle(false);
if (abInsertDigits(selected)) {
this.abSource.setValue(abInsertDigits(selected));
}
}
};
AbWikiInsertDialog.prototype.abCurrentWikitext = function () {
if (this.abKind === 'image') {
return abInsertImageWikitext({
source: this.abSource.getValue(),
width: this.abWidth.getValue(),
height: this.abHeight.getValue(),
align: this.abAlign.getValue(),
caption: this.abCaption.getValue(),
alt: this.abAlt.getValue(),
link: this.abLink.getValue()
});
}
return abInsertTemplate(
this.abKind === 'player' ? 'player' : 'level',
this.abSource.getValue(),
this.abField.getValue()
);
};
AbWikiInsertDialog.prototype.abUpdatePreview = function () {
var wikitext = this.abCurrentWikitext();
this.abPreview.setValue(wikitext);
this.actions.setAbilities({ insert: !!wikitext });
};
AbWikiInsertDialog.prototype.getBodyHeight = function () {
return Math.max(this.$body[0].scrollHeight, this.abKind === 'image' ? 380 : 200);
};
AbWikiInsertDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === 'insert') {
return new OO.ui.Process(function () {
var wikitext = dialog.abCurrentWikitext();
if (wikitext && dialog.abTextarea) {
dialog.abTextarea.textSelection('encapsulateSelection', {
pre: '',
peri: wikitext,
post: '',
replace: true,
selectPeri: false
});
}
dialog.close({ action: action });
});
}
if (action === 'cancel') {
return new OO.ui.Process(function () {
dialog.close({ action: action });
});
}
return AbWikiInsertDialog.super.prototype.getActionProcess.call(this, action);
};
}
function abOpenInsertDialog($textarea, kind) {
mw.loader.using(['oojs-ui-windows', 'oojs-ui-widgets']).then(function () {
abEnsureInsertDialog();
if (!abInsertManager) {
abInsertManager = new OO.ui.WindowManager();
$(document.body).append(abInsertManager.$element);
abInsertManager.addWindows([new AbWikiInsertDialog({ size: 'medium' })]);
}
abInsertManager.openWindow('abWikiInsert', {
abKind: kind,
abTextarea: $textarea
});
});
}
mw.hook('wikiEditor.toolbarReady').add(function ($textarea) {
if (!$textarea || !$textarea.wikiEditor) {
return;
}
$textarea.wikiEditor('removeFromToolbar', {
section: 'main',
group: 'insert',
tool: 'file'
});
mw.loader.using('oojs-ui.styles.icons-user').then(function () {
$textarea.wikiEditor('addToToolbar', {
section: 'main',
group: 'insert',
tools: {
ablevel: {
label: 'Level',
type: 'button',
oouiIcon: 'play',
action: {
type: 'callback',
execute: function () {
abOpenInsertDialog($textarea, 'level');
}
}
},
abplayer: {
label: 'Player',
type: 'button',
oouiIcon: 'userAvatar',
action: {
type: 'callback',
execute: function () {
abOpenInsertDialog($textarea, 'player');
}
}
},
abimage: {
label: 'Image',
type: 'button',
oouiIcon: 'image',
action: {
type: 'callback',
execute: function () {
abOpenInsertDialog($textarea, 'image');
}
}
}
}
});
});
});
});