Refactor & modulize JavaScript files

This commit is contained in:
printempw 2017-07-13 17:03:14 +08:00
parent fe04c49f7e
commit 104f5a32a7
30 changed files with 2596 additions and 2675 deletions

View File

@ -1,824 +0,0 @@
'use strict';
let pluginsTable;
$(document).ready(function() {
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue'
});
swal.setDefaults({
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
});
$.extend(true, $.fn.dataTable.defaults, {
language: trans('common.datatables'),
scrollX: true,
pageLength: 25,
autoWidth: false,
processing: true,
serverSide: true
});
if (window.location.href.indexOf(url('admin/users')) >= 0) {
initUsersTable();
} else if (window.location.href.indexOf(url('admin/players')) >= 0) {
initPlayersTable();
} else if (window.location.href.indexOf(url('admin/plugins/manage')) >= 0) {
pluginsTable = initPluginsTable();
}
});
$('#layout-skins-list [data-skin]').click(function(e) {
e.preventDefault();
var skin_name = $(this).data('skin');
$('body').removeClass(current_skin).addClass(skin_name);
current_skin = skin_name;
});
$('#color-submit').click(function() {
$.ajax({
type: "POST",
url: "./customize?action=color",
dataType: "json",
data: { "color_scheme": current_skin },
success: function(json) {
if (json.errno == 0)
toastr.success(json.msg);
else
toastr.warning(json.msg);
},
error: showAjaxError
});
});
function changeUserEmail(uid) {
let dom = $(`tr#user-${uid} > td:nth-child(2)`);
swal({
text: trans('admin.newUserEmail'),
showCancelButton: true,
input: 'text',
inputValue: dom.text()
}).then(email => {
$.ajax({
type: "POST",
url: "./users?action=email",
dataType: "json",
data: { 'uid': uid, 'email': email },
success: json => {
if (json.errno == 0) {
dom.text(email);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
function changeUserNickName(uid) {
let dom = $(`tr#user-${uid} > td:nth-child(3)`);
swal({
text: trans('admin.newUserNickname'),
showCancelButton: true,
input: 'text',
inputValue: dom.text()
}).then(nickname => {
$.ajax({
type: "POST",
url: "./users?action=nickname",
dataType: "json",
data: { 'uid': uid, 'nickname': nickname },
success: json => {
if (json.errno == 0) {
dom.text(nickname);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
function changeUserPwd(uid) {
swal({
text: trans('admin.newUserPassword'),
showCancelButton: true,
input: 'password',
}).then(password => {
return Promise.resolve($.ajax({
type: "POST",
url: "./users?action=password",
dataType: "json",
data: { 'uid': uid, 'password': password }
}));
}).then(json => {
if (json.errno == 0)
toastr.success(json.msg);
else
toastr.warning(json.msg);
}).catch(error => showAjaxError);
}
function changeUserScore(uid, score) {
$.ajax({
type: "POST",
url: "./users?action=score",
dataType: "json",
// handle id formatted as '#user-1234'
data: { 'uid': uid.slice(5), 'score': score },
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function changeBanStatus(uid) {
$.ajax({
type: "POST",
url: "./users?action=ban",
dataType: "json",
data: { 'uid': uid },
success: function(json) {
if (json.errno == 0) {
let dom = $(`#ban-${uid}`);
if (dom.attr('data') == 'banned') {
dom.text(trans('admin.ban')).attr('data', 'normal');
} else {
dom.text(trans('admin.unban')).attr('data', 'banned');
}
$(`#user-${uid} > td.status`).text(
json.permission == -1 ? trans('admin.banned') : trans('admin.normal')
);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function changeAdminStatus(uid) {
$.ajax({
type: "POST",
url: "./users?action=admin",
dataType: "json",
data: { 'uid': uid },
success: function(json) {
if (json.errno == 0) {
let dom = $(`#admin-${uid}`);
if (dom.attr('data') == 'admin') {
dom.text(trans('admin.setAdmin')).attr('data', 'normal');
} else {
dom.text(trans('admin.unsetAdmin')).attr('data', 'admin');
}
$(`#user-${uid} > td.status`).text(
json.permission == 1 ? trans('admin.admin') : trans('admin.normal')
);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function deleteUserAccount(uid) {
swal({
text: trans('admin.deleteUserNotice'),
type: 'warning',
showCancelButton: true
}).then(() => {
return Promise.resolve($.ajax({
type: "POST",
url: "./users?action=delete",
dataType: "json",
data: { 'uid': uid }
}))
}).then(json => {
if (json.errno == 0) {
$('tr#user-' + uid).remove();
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
}).catch(error => showAjaxError);
}
$('body').on('keypress', '.score', function(event){
if (event.which == 13) {
changeUserScore($(this).parent().parent().attr('id'), $(this).val());
$(this).blur();
}
});
function changePreference() {
$.ajax({
type: "POST",
url: "./players?action=preference",
dataType: "json",
data: { 'pid': $(this).parent().parent().attr('id'), 'preference': $(this).val() },
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function changeTexture(pid, playerName) {
let dom = `
<div class="form-group">
<label for="model">${trans('admin.textureType')}</label>
<select class="form-control" id="model">
<option value="steve">${trans('admin.skin', {'model': 'Steve'})}</option>
<option value="alex">${trans('admin.skin', {'model': 'Alex'})}</option>
<option value="cape">${trans('admin.cape')}</option>
</select>
</div>
<div class="form-group">
<label for="tid">${trans('admin.pid')}</label>
<input id="tid" class="form-control" type="text" placeholder="${trans('admin.pidNotice')}">
</div>`;
showModal(dom, trans('admin.changePlayerTexture', {'player': playerName}), 'default', {
callback: `ajaxChangeTexture(${pid})`
});
return;
}
function ajaxChangeTexture(pid) {
// remove interference of modal which is hide
$('.modal').each(function() {
if ($(this).css('display') == "none")
$(this).remove();
});
var model = $('#model').val();
var tid = $('#tid').val();
$.ajax({
type: "POST",
url: "./players?action=texture",
dataType: "json",
data: { 'pid': pid, 'model': model, 'tid': tid },
success: function(json) {
if (json.errno == 0) {
$('#'+pid+'-'+model).attr('src', '../preview/64/'+tid+'.png');
$('.modal').modal('hide');
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function changePlayerName(pid, oldName) {
let dom = $(`tr#${pid} > td:nth-child(3)`);
swal({
text: trans('admin.changePlayerNameNotice'),
input: 'text',
inputValue: oldName,
inputValidator: name => {
return new Promise((resolve, reject) => {
if (name) {
resolve();
} else {
reject(trans('admin.emptyPlayerName'));
}
})
}
}).then(name => {
return Promise.resolve($.ajax({
type: 'POST',
url: './players?action=name',
dataType: 'json',
data: { pid: pid, name: name }
}));
}).then(json => {
if (json.errno == 0) {
dom.text(json.name);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
}).catch(error => showAjaxError);
}
function changeOwner(pid) {
let dom = $(`#${pid} > td:nth-child(2)`);
swal({
html: `${trans('admin.changePlayerOwner')}<br><small>&nbsp;</small>`,
input: 'number',
inputValue: dom.text(),
showCancelButton: true
}).then(uid => {
$.ajax({
type: "POST",
url: "./players?action=owner",
dataType: "json",
data: { 'pid': pid, 'uid': uid },
success: function (json) {
if (json.errno == 0) {
dom.text(uid);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
$('.swal2-input').on('input', debounce(() => {
const uid = $('.swal2-input').val();
if (uid > 0) {
Promise.resolve($.ajax({
type: 'GET',
url: `./user/${uid}`,
dataType: 'json'
})).then(result => {
$('.swal2-content').html(
trans('admin.changePlayerOwner') +
'<small style="display: block; margin-top: .5em;">' +
trans('admin.targetUser', { nickname: result.user.nickname }) +
'</small>'
);
}).catch(() => {
$('.swal2-content').html(`${trans('admin.changePlayerOwner')}<br><small>${trans('admin.noSuchUser')}</small>`);
});
}
}, 350));
}
function deletePlayer(pid) {
swal({
text: trans('admin.deletePlayerNotice'),
type: 'warning',
showCancelButton: true
}).then(() => {
return Promise.resolve($.ajax({
type: "POST",
url: "./players?action=delete",
dataType: "json",
data: { 'pid': pid },
success: function (json) {
},
error: showAjaxError
}))
}).then(json => {
if (json.errno == 0) {
$('tr#' + pid).remove();
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
}).catch(error => showAjaxError);
}
function enablePlugin(name) {
$.ajax({
type: "POST",
url: "?action=enable&name=" + name,
dataType: "json",
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function disablePlugin(name) {
$.ajax({
type: "POST",
url: "?action=disable&name=" + name,
dataType: "json",
success: function(json) {
if (json.errno == 0) {
toastr.warning(json.msg);
pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function deletePlugin(name) {
swal({
text: trans('admin.confirmDeletion'),
type: 'warning',
showCancelButton: true
}).then(function() {
$.ajax({
type: "POST",
url: "?action=delete&name=" + name,
dataType: "json",
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
function downloadUpdates() {
var file_size = 0;
var progress = 0;
console.log("Prepared to download");
$.ajax({
url: './update/download?action=prepare-download',
type: 'GET',
dataType: 'json',
beforeSend: function() {
$('#update-button').html('<i class="fa fa-spinner fa-spin"></i> '+trans('admin.preparing')).prop('disabled', 'disabled');
},
})
.done(function(json) {
console.log(json);
file_size = json.file_size;
$('#file-size').html(file_size);
$('#modal-start-download').modal({
'backdrop': 'static',
'keyboard': false
});
console.log("started downloading");
$.ajax({
url: './update/download?action=start-download',
type: 'POST',
dataType: 'json'
})
.done(function(json) {
// set progress to 100 when got the response
progress = 100;
console.log("Downloading finished");
console.log(json);
})
.fail(showAjaxError);
var interval_id = window.setInterval(function() {
$('#imported-progress').html(progress);
$('.progress-bar').css('width', progress+'%').attr('aria-valuenow', progress);
if (progress == 100) {
clearInterval(interval_id);
$('.modal-title').html('<i class="fa fa-spinner fa-spin"></i> ' + trans('admin.extracting'));
$('.modal-body').append('<p>'+trans('admin.downloadCompleted')+'</p>')
console.log("Start extracting");
$.ajax({
url: './update/download?action=extract',
type: 'POST',
dataType: 'json'
})
.done(function(json) {
console.log("Package extracted and files are covered");
$('#modal-start-download').modal('toggle');
swal({
type: 'success',
html: json.msg
}).then(function() {
window.location = "../";
}, function(dismiss) {
window.location = "../";
});
})
.fail(showAjaxError);
} else {
$.ajax({
url: './update/download?action=get-file-size',
type: 'GET'
})
.done(function(json) {
progress = (json.size / file_size * 100).toFixed(2);
console.log("Progress: "+progress);
})
.fail(showAjaxError);
}
}, 300);
})
.fail(showAjaxError);
}
function initUsersTable() {
let dataUrl = url('admin/user-data');
if (getQueryString('uid')) {
dataUrl += '?uid=' + getQueryString('uid');
}
$('#user-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
rowCallback: (row, data) => {
$(row).attr('id', `user-${data.uid}`);
},
columnDefs: [
{
targets: 0,
data: 'uid',
width: '1%'
},
{
targets: 1,
data: 'email'
},
{
targets: 2,
data: 'nickname'
},
{
targets: 3,
data: 'score',
render: data => {
return `<input type="number" class="form-control score" value="${data}" title="${trans('admin.scoreTip')}" data-toggle="tooltip" data-placement="right">`;
}
},
{
targets: 4,
data: 'players_count',
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeePlayers')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/players?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 5,
data: 'permission',
className: 'status',
render: data => {
switch (data) {
case -1:
return trans('admin.banned');
case 0:
return trans('admin.normal');
case 1:
return trans('admin.admin');
case 2:
return trans('admin.superAdmin');
}
}
},
{
targets: 6,
data: 'register_at'
},
{
targets: 7,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let operationsHtml, adminOption = '', bannedOption = '', deleteUserButton;
if (row.permission !== 2) {
if (data === 2) {
if (row.permission === 1) {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="admin" href="javascript:changeAdminStatus(${row.uid});">${trans('admin.unsetAdmin')}</a></li>`;
} else {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="normal" href="javascript:changeAdminStatus(${row.uid});">${trans('admin.setAdmin')}</a></li>`;
}
}
if (row.permission === -1) {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="banned" href="javascript:changeBanStatus(${row.uid});">${trans('admin.unban')}</a></li>`;
} else {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="normal" href="javascript:changeBanStatus(${row.uid});">${trans('admin.ban')}</a></li>`;
}
}
if (data === 2) {
if (row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteSuperAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" href="javascript:deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
} else {
if (row.permission === 1 || row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" href="javascript:deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
}
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="javascript:changeUserEmail(${row.uid});">${trans('admin.changeEmail')}</a></li>
<li><a href="javascript:changeUserNickName(${row.uid});">${trans('admin.changeNickName')}</a></li>
<li><a href="javascript:changeUserPwd(${row.uid});">${trans('admin.changePassword')}</a></li>
${adminOption}${bannedOption}
</ul>
</div>
${deleteUserButton}`;
}
}
]
});
}
function initPlayersTable() {
let dataUrl = url('admin/player-data');
if (getQueryString('uid')) {
dataUrl += '?uid=' + getQueryString('uid');
}
$('#player-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
columnDefs: [
{
targets: 0,
data: 'pid',
width: '1%'
},
{
targets: 1,
data: 'uid',
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeeUser')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/users?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 2,
data: 'player_name'
},
{
targets: 3,
data: 'preference',
render: data => {
return `
<select class="form-control" onchange="changePreference.call(this)">
<option ${(data == "default") ? 'selected=selected' : ''} value="default">Default</option>
<option ${(data == "slim") ? 'selected=selected' : ''} value="slim">Slim</option>
</select>`;
}
},
{
targets: 4,
searchable: false,
orderable: false,
render: (data, type, row) => {
let html = { steve: '', alex: '', cape: '' };
['steve', 'alex', 'cape'].forEach(textureType => {
if (row['tid_' + textureType] === 0) {
html[textureType] = `<img id="${row.pid}-${row['tid_' + textureType]}" width="64" />`;
} else {
html[textureType] = `
<a href="${url('/')}skinlib/show/${row['tid_' + textureType]}">
<img id="${row.pid}-${row['tid_' + textureType]}" width="64" src="${url('/')}preview/64/${row['tid_' + textureType]}.png" />
</a>`;
}
});
return html.steve + html.alex + html.cape;
}
},
{
targets: 5,
data: 'last_modified'
},
{
targets: 6,
searchable: false,
orderable: false,
render: (data, type, row) => {
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="javascript:changeTexture(${row.pid}, '${row.player_name}');">${trans('admin.changeTexture')}</a></li>
<li><a href="javascript:changePlayerName(${row.pid}, '${row.player_name}');">${trans('admin.changePlayerName')}</a></li>
<li><a href="javascript:changeOwner(${row.pid});">${trans('admin.changeOwner')}</a></li>
</ul>
</div>
<a class="btn btn-danger btn-sm" href="javascript:deletePlayer(${row.pid});">${trans('admin.deletePlayer')}</a>`;
}
}
]
});
}
function initPluginsTable() {
return $('#plugin-table').DataTable({
ajax: url('admin/plugins/data'),
columnDefs: [
{
targets: 0,
data: 'title'
},
{
targets: 1,
data: 'description',
width: '35%'
},
{
targets: 2,
data: 'author',
render: data => {
if (data.url === '' || data.url === null) {
return data.author;
} else {
return `<a href="${data.url}" target="_blank">${data.author}</a>`;
}
}
},
{
targets: 3,
data: 'version'
},
{
targets: 4,
data: 'status'
},
{
targets: 5,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let switchEnableButton, configViewButton, deletePluginButton;
if (data.enabled) {
switchEnableButton = `
<a class="btn btn-warning btn-sm" href="javascript:disablePlugin('${row.name}');">${trans('admin.disablePlugin')}</a>`;
} else {
switchEnableButton = `
<a class="btn btn-primary btn-sm" href="javascript:enablePlugin('${row.name}');">${trans('admin.enablePlugin')}</a>`;
}
if (data.enabled && data.hasConfigView) {
configViewButton = `
<a class="btn btn-default btn-sm" href="${url('/')}admin/plugins/config/${row.name}">${trans('admin.configurePlugin')}</a>`;
} else {
configViewButton = `
<a class="btn btn-default btn-sm" disabled="disabled" title="${trans('admin.noPluginConfigNotice')}" data-toggle="tooltip" data-placement="top">${trans('admin.configurePlugin')}</a>`;
}
deletePluginButton = `
<a class="btn btn-danger btn-sm" href="javascript:deletePlugin('${row.name}');">${trans('admin.deletePlugin')}</a>`;
return switchEnableButton + configViewButton + deletePluginButton;
}
}
]
});
}

View File

@ -0,0 +1,21 @@
/* global current_skin:true */
'use strict';
$('#layout-skins-list [data-skin]').click(function (e) {
e.preventDefault();
let skin_name = $(this).data('skin');
$('body').removeClass(current_skin).addClass(skin_name);
current_skin = skin_name;
});
$('#color-submit').click(() => {
fetch({
type: 'POST',
url: url('admin/customize?action=color'),
dataType: 'json',
data: { color_scheme: current_skin }
}).then(({ errno, msg }) => {
(errno == 0) ? toastr.success(msg) : toastr.warning(msg);
}).catch(err => showAjaxError(err));
});

View File

@ -0,0 +1,24 @@
/* global initUsersTable, initPlayersTable, initPluginsTable */
'use strict';
$.pluginsTable = null;
$(document).ready(() => {
$.extend(true, $.fn.dataTable.defaults, {
language: trans('common.datatables'),
scrollX: true,
pageLength: 25,
autoWidth: false,
processing: true,
serverSide: true
});
if (window.location.pathname.includes('admin/users')) {
initUsersTable();
} else if (window.location.pathname.includes('admin/players')) {
initPlayersTable();
} else if (window.location.pathname.includes('admin/plugins/manage')) {
$.pluginsTable = initPluginsTable();
}
});

View File

@ -0,0 +1,164 @@
/* exported changePreference, changeTexture, ajaxChangeTexture, changePlayerName, changeOwner, deletePlayer */
'use strict';
function changePreference() {
fetch({
type: 'POST',
url: url('admin/players?action=preference'),
dataType: 'json',
data: {
pid: $(this).parent().parent().attr('id'),
preference: $(this).val()
}
}).then(({ errno, msg }) => {
(errno == 0) ? toastr.success(msg) : toastr.warning(msg);
}).catch(err => showAjaxError(err));
}
function changeTexture(pid, playerName) {
let dom = `
<div class="form-group">
<label for="model">${trans('admin.textureType')}</label>
<select class="form-control" id="model">
<option value="steve">${trans('admin.skin', { 'model': 'Steve' })}</option>
<option value="alex">${trans('admin.skin', { 'model': 'Alex' })}</option>
<option value="cape">${trans('admin.cape')}</option>
</select>
</div>
<div class="form-group">
<label for="tid">${trans('admin.pid')}</label>
<input id="tid" class="form-control" type="text" placeholder="${trans('admin.pidNotice')}">
</div>`;
showModal(dom, trans('admin.changePlayerTexture', { 'player': playerName }), 'default', {
callback: `ajaxChangeTexture(${pid})`
});
return;
}
function ajaxChangeTexture(pid) {
// Remove interference of modal which is hide
$('.modal').each(function () {
if ($(this).css('display') == 'none') $(this).remove();
});
var model = $('#model').val();
var tid = $('#tid').val();
fetch({
type: 'POST',
url: url('admin/players?action=texture'),
dataType: 'json',
data: { pid: pid, model: model, tid: tid }
}).then(({ errno, msg }) => {
if (errno == 0) {
$(`#${pid}-${model}`).attr('src', url(`preview/64/${tid}.png`));
$('.modal').modal('hide');
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changePlayerName(pid, oldName) {
let dom = $(`tr#${pid} > td:nth-child(3)`);
let newPlayerName = '';
swal({
text: trans('admin.changePlayerNameNotice'),
input: 'text',
inputValue: oldName,
inputValidator: name => (new Promise((resolve, reject) => {
(newPlayerName = name) ? resolve() : reject(trans('admin.emptyPlayerName'));
}))
}).then(name => fetch({
type: 'POST',
url: url('admin/players?action=name'),
dataType: 'json',
data: { pid: pid, name: name }
})).then(({ errno, msg }) => {
if (errno == 0) {
dom.text(newPlayerName);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changeOwner(pid) {
let dom = $(`#${pid} > td:nth-child(2)`);
let owner = 0;
swal({
html: `${trans('admin.changePlayerOwner')}<br><small>&nbsp;</small>`,
input: 'number',
inputValue: dom.text(),
showCancelButton: true
}).then(uid => {
owner = uid;
return fetch({
type: 'POST',
url: url('admin/players?action=owner'),
dataType: 'json',
data: { pid: pid, uid: uid }
});
}).then(({ errno, msg }) => {
if (errno == 0) {
dom.text(owner);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
$('.swal2-input').on('input', debounce(() => {
let uid = $('.swal2-input').val();
if (isNaN(uid) || uid <= 0)
return;
fetch({
type: 'GET',
url: url(`admin/user/${uid}`),
dataType: 'json'
}).then(result => {
$('.swal2-content').html(
trans('admin.changePlayerOwner') +
'<small style="display: block; margin-top: .5em;">' +
trans('admin.targetUser', { nickname: result.user.nickname }) +
'</small>'
);
}).catch(() => {
$('.swal2-content').html(`
${trans('admin.changePlayerOwner')}<br>
<small>${trans('admin.noSuchUser')}</small>
`);
});
}, 350));
}
function deletePlayer(pid) {
swal({
text: trans('admin.deletePlayerNotice'),
type: 'warning',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('admin/players?action=delete'),
dataType: 'json',
data: { pid: pid }
})).then(({ errno, msg }) => {
if (errno == 0) {
$(`tr#${pid}`).remove();
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,55 @@
/* exported enablePlugin, disablePlugin, deletePlugin */
'use strict';
function enablePlugin(name) {
fetch({
type: 'POST',
url: url(`admin/plugins?action=enable&name=${name}`),
dataType: 'json'
}).then(({ errno, msg }) => {
if (errno == 0) {
toastr.success(msg);
$.pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function disablePlugin(name) {
fetch({
type: 'POST',
url: url(`admin/plugins?action=disable&name=${name}`),
dataType: 'json'
}).then(({ errno, msg }) => {
if (errno == 0) {
toastr.warning(msg);
$.pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function deletePlugin(name) {
swal({
text: trans('admin.confirmDeletion'),
type: 'warning',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url(`admin/plugins?action=delete&name=${name}`),
dataType: 'json'
})).then(({ errno, msg }) => {
if (errno == 0) {
toastr.success(msg);
$.pluginsTable.ajax.reload(null, false);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,272 @@
/* exported initUsersTable, initPlayersTable, initPluginsTable */
'use strict';
function initUsersTable() {
let uid = getQueryString('uid');
let dataUrl = url('admin/user-data') + (uid ? `?uid=${uid}` : '');
$('#user-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
rowCallback: (row, data) => {
$(row).attr('id', `user-${data.uid}`);
},
columnDefs: [
{
targets: 0,
data: 'uid',
width: '1%'
},
{
targets: 1,
data: 'email'
},
{
targets: 2,
data: 'nickname'
},
{
targets: 3,
data: 'score',
render: data => {
return `<input type="number" class="form-control score" value="${data}" title="${trans('admin.scoreTip')}" data-toggle="tooltip" data-placement="right">`;
}
},
{
targets: 4,
data: 'players_count',
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeePlayers')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/players?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 5,
data: 'permission',
className: 'status',
render: data => {
switch (data) {
case -1:
return trans('admin.banned');
case 0:
return trans('admin.normal');
case 1:
return trans('admin.admin');
case 2:
return trans('admin.superAdmin');
}
}
},
{
targets: 6,
data: 'register_at'
},
{
targets: 7,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let adminOption = '', bannedOption = '', deleteUserButton;
if (row.permission !== 2) {
if (data === 2) {
if (row.permission === 1) {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="admin" href="javascript:changeAdminStatus(${row.uid});">${trans('admin.unsetAdmin')}</a></li>`;
} else {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="normal" href="javascript:changeAdminStatus(${row.uid});">${trans('admin.setAdmin')}</a></li>`;
}
}
if (row.permission === -1) {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="banned" href="javascript:changeBanStatus(${row.uid});">${trans('admin.unban')}</a></li>`;
} else {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="normal" href="javascript:changeBanStatus(${row.uid});">${trans('admin.ban')}</a></li>`;
}
}
if (data === 2) {
if (row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteSuperAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" href="javascript:deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
} else {
if (row.permission === 1 || row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" href="javascript:deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
}
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="javascript:changeUserEmail(${row.uid});">${trans('admin.changeEmail')}</a></li>
<li><a href="javascript:changeUserNickName(${row.uid});">${trans('admin.changeNickName')}</a></li>
<li><a href="javascript:changeUserPwd(${row.uid});">${trans('admin.changePassword')}</a></li>
${adminOption}${bannedOption}
</ul>
</div>
${deleteUserButton}`;
}
}
]
});
}
function initPlayersTable() {
let uid = getQueryString('uid');
let dataUrl = url('admin/player-data') + (uid ? `?uid=${uid}` : '');
$('#player-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
columnDefs: [
{
targets: 0,
data: 'pid',
width: '1%'
},
{
targets: 1,
data: 'uid',
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeeUser')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/users?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 2,
data: 'player_name'
},
{
targets: 3,
data: 'preference',
render: data => {
return `
<select class="form-control" onchange="changePreference.call(this)">
<option ${(data == 'default') ? 'selected=selected' : ''} value="default">Default</option>
<option ${(data == 'slim') ? 'selected=selected' : ''} value="slim">Slim</option>
</select>`;
}
},
{
targets: 4,
searchable: false,
orderable: false,
render: (data, type, row) => {
let html = { steve: '', alex: '', cape: '' };
['steve', 'alex', 'cape'].forEach(textureType => {
if (row['tid_' + textureType] === 0) {
html[textureType] = `<img id="${row.pid}-${row['tid_' + textureType]}" width="64" />`;
} else {
html[textureType] = `
<a href="${url('/')}skinlib/show/${row['tid_' + textureType]}">
<img id="${row.pid}-${row['tid_' + textureType]}" width="64" src="${url('/')}preview/64/${row['tid_' + textureType]}.png" />
</a>`;
}
});
return html.steve + html.alex + html.cape;
}
},
{
targets: 5,
data: 'last_modified'
},
{
targets: 6,
searchable: false,
orderable: false,
render: (data, type, row) => {
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="javascript:changeTexture(${row.pid}, '${row.player_name}');">${trans('admin.changeTexture')}</a></li>
<li><a href="javascript:changePlayerName(${row.pid}, '${row.player_name}');">${trans('admin.changePlayerName')}</a></li>
<li><a href="javascript:changeOwner(${row.pid});">${trans('admin.changeOwner')}</a></li>
</ul>
</div>
<a class="btn btn-danger btn-sm" href="javascript:deletePlayer(${row.pid});">${trans('admin.deletePlayer')}</a>`;
}
}
]
});
}
function initPluginsTable() {
return $('#plugin-table').DataTable({
ajax: url('admin/plugins/data'),
columnDefs: [
{
targets: 0,
data: 'title'
},
{
targets: 1,
data: 'description',
width: '35%'
},
{
targets: 2,
data: 'author',
render: data => {
if (data.url === '' || data.url === null) {
return data.author;
} else {
return `<a href="${data.url}" target="_blank">${data.author}</a>`;
}
}
},
{
targets: 3,
data: 'version'
},
{
targets: 4,
data: 'status'
},
{
targets: 5,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let switchEnableButton, configViewButton, deletePluginButton;
if (data.enabled) {
switchEnableButton = `
<a class="btn btn-warning btn-sm" href="javascript:disablePlugin('${row.name}');">${trans('admin.disablePlugin')}</a>`;
} else {
switchEnableButton = `
<a class="btn btn-primary btn-sm" href="javascript:enablePlugin('${row.name}');">${trans('admin.enablePlugin')}</a>`;
}
if (data.enabled && data.hasConfigView) {
configViewButton = `
<a class="btn btn-default btn-sm" href="${url('/')}admin/plugins/config/${row.name}">${trans('admin.configurePlugin')}</a>`;
} else {
configViewButton = `
<a class="btn btn-default btn-sm" disabled="disabled" title="${trans('admin.noPluginConfigNotice')}" data-toggle="tooltip" data-placement="top">${trans('admin.configurePlugin')}</a>`;
}
deletePluginButton = `
<a class="btn btn-danger btn-sm" href="javascript:deletePlugin('${row.name}');">${trans('admin.deletePlugin')}</a>`;
return switchEnableButton + configViewButton + deletePluginButton;
}
}
]
});
}

View File

@ -0,0 +1,91 @@
/* exported downloadUpdates */
'use strict';
function downloadUpdates() {
var fileSize = 0;
var progress = 0;
console.log('Prepare to download');
fetch({
url: url('admin/update/download?action=prepare-download'),
type: 'GET',
dataType: 'json',
beforeSend: function() {
$('#update-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('admin.preparing')
).prop('disabled', 'disabled');
}
}).then(json => {
console.log(json);
fileSize = json.file_size;
$('#file-size').html(fileSize);
$('#modal-start-download').modal({
'backdrop': 'static',
'keyboard': false
});
console.log('Start downloading');
fetch({
url: url('admin/update/download?action=start-download'),
type: 'POST',
dataType: 'json'
}).then(json => {
// Set progress to 100 when got the response
progress = 100;
console.log('Downloading finished');
console.log(json);
}).catch(err => showAjaxError(err));
// Downloading progress polling
let interval_id = window.setInterval(() => {
$('#imported-progress').html(progress);
$('.progress-bar').css('width', progress+'%').attr('aria-valuenow', progress);
if (progress == 100) {
clearInterval(interval_id);
$('.modal-title').html('<i class="fa fa-spinner fa-spin"></i> ' + trans('admin.extracting'));
$('.modal-body').append(`<p>${ trans('admin.downloadCompleted') }</p>`);
console.log('Start extracting');
fetch({
url: url('admin/update/download?action=extract'),
type: 'POST',
dataType: 'json'
}).then(json => {
console.log('Package extracted and files are covered');
$('#modal-start-download').modal('toggle');
swal({
type: 'success',
html: json.msg
}).then(function() {
window.location = url('/');
}, function() {
window.location = url('/');
});
}).catch(err => showAjaxError(err));
} else {
fetch({
url: url('admin/update/download?action=get-file-size'),
type: 'GET'
}).then(json => {
progress = (json.size / fileSize * 100).toFixed(2);
console.log('Progress: ' + progress);
}).catch(err => showAjaxError(err));
}
}, 300);
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,169 @@
/* exported changeUserEmail, changeUserNickName, changeUserPwd,
changeBanStatus, changeAdminStatus, deleteUserAccount */
'use strict';
function changeUserEmail(uid) {
let dom = $(`tr#user-${uid} > td:nth-child(2)`),
newUserEmail = '';
swal({
text: trans('admin.newUserEmail'),
showCancelButton: true,
input: 'text',
inputValue: dom.text(),
inputValidator: value => (new Promise((resolve, reject) => {
(newUserEmail = value) ? resolve() : reject(trans('auth.emptyEmail'));
}))
}).then(email => fetch({
type: 'POST',
url: url('admin/users?action=email'),
dataType: 'json',
data: { uid: uid, email: email }
})).then(({ errno, msg }) => {
if (errno == 0) {
dom.text(newUserEmail);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changeUserNickName(uid) {
let dom = $(`tr#user-${uid} > td:nth-child(3)`),
newNickName = '';
swal({
text: trans('admin.newUserNickname'),
showCancelButton: true,
input: 'text',
inputValue: dom.text(),
inputValidator: value => (new Promise((resolve, reject) => {
(newNickName = value) ? resolve() : reject(trans('auth.emptyNickname'));
}))
}).then(nickname => fetch({
type: 'POST',
url: url('admin/users?action=nickname'),
dataType: 'json',
data: { uid: uid, nickname: nickname }
})).then(({ errno, msg }) => {
if (errno == 0) {
dom.text(newNickName);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changeUserPwd(uid) {
swal({
text: trans('admin.newUserPassword'),
showCancelButton: true,
input: 'password',
}).then(password => fetch({
type: 'POST',
url: url('admin/users?action=password'),
dataType: 'json',
data: { uid: uid, password: password }
})).then(({ errno, msg }) => {
(errno == 0) ? toastr.success(msg) : toastr.warning(msg);
}).catch(err => showAjaxError(err));
}
function changeUserScore(uid, score) {
fetch({
type: 'POST',
url: url('admin/users?action=score'),
dataType: 'json',
// Handle id formatted as '#user-1234'
data: { uid: uid.slice(5), score: score }
}).then(({ errno, msg }) => {
(errno == 0) ? toastr.success(msg) : toastr.warning(msg);
}).catch(err => showAjaxError(err));
}
function changeBanStatus(uid) {
fetch({
type: 'POST',
url: url('admin/users?action=ban'),
dataType: 'json',
data: { uid: uid }
}).then(({ errno, msg, permission }) => {
if (errno == 0) {
let dom = $(`#ban-${uid}`);
if (dom.attr('data') == 'banned') {
dom.text(trans('admin.ban')).attr('data', 'normal');
} else {
dom.text(trans('admin.unban')).attr('data', 'banned');
}
$(`#user-${uid} > td.status`).text(
permission == -1 ? trans('admin.banned') : trans('admin.normal')
);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changeAdminStatus(uid) {
fetch({
type: 'POST',
url: url('admin/users?action=admin'),
dataType: 'json',
data: { uid: uid }
}).then(({ errno, msg, permission }) => {
if (errno == 0) {
let dom = $(`#admin-${uid}`);
if (dom.attr('data') == 'admin') {
dom.text(trans('admin.setAdmin')).attr('data', 'normal');
} else {
dom.text(trans('admin.unsetAdmin')).attr('data', 'admin');
}
$(`#user-${uid} > td.status`).text(
(permission == 1) ? trans('admin.admin') : trans('admin.normal')
);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function deleteUserAccount(uid) {
swal({
text: trans('admin.deleteUserNotice'),
type: 'warning',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('admin/users?action=delete'),
dataType: 'json',
data: { uid: uid }
})).then(({ errno, msg }) => {
if (errno == 0) {
$('tr#user-' + uid).remove();
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
$('body').on('keypress', '.score', function(event){
// Change score when Enter key is pressed
if (event.which == 13) {
$(this).blur();
changeUserScore($(this).parent().parent().attr('id'), $(this).val());
}
});

View File

@ -1,245 +0,0 @@
'use strict';
$(document).ready(() => $('input').iCheck({
checkboxClass: 'icheckbox_square-blue'
}));
function freshCaptcha() {
$('.captcha').attr('src', './captcha?' + new Date().getTime());
$('#captcha').val('');
}
var login_fails = 0;
$('#login-button').click(function () {
var data = new Object();
data.identification = $('#identification').val();
data.password = $('#password').val();
data.keep = $('#keep').prop('checked') ? true : false;
if (data.identification == "") {
showMsg(trans('auth.emptyIdentification'));
$('#identification').focus();
} else if (data.password == "") {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else {
// if captcha form is shown
if ($('#captcha-form').css('display') == "block") {
data.captcha = $("#captcha").val();
if (data.captcha == "") {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
return false;
}
}
$.ajax({
type: "POST",
url: "./login",
dataType: "json",
data: data,
beforeSend: () => {
$('#login-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.loggingIn')
).prop('disabled', 'disabled');
},
success: (json) => {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
// redirect to last requested path
let redirect_to = url(blessing.redirect_to || "user");
window.setTimeout(() => (window.location = redirect_to), 1000);
} else {
if (json.login_fails > 3) {
if ($('#captcha-form').css('display') == "none") {
swal({
type: 'error',
html: trans('auth.tooManyFails')
});
}
$('#captcha-form').show();
}
freshCaptcha();
showMsg(json.msg, 'warning');
$('#login-button').html(trans('auth.login')).prop('disabled', '');
}
},
error: (json) => {
showAjaxError(json);
$('#login-button').html(trans('auth.login')).prop('disabled', '');
}
});
}
return false;
});
$('.captcha').click(freshCaptcha);
$('#register-button').click(function () {
var email = $('#email').val();
var password = $('#password').val();
var nickname = $('#nickname').val();
var captcha = $('#captcha').val();
// check valid email address
if (email == "") {
showMsg(trans('auth.emptyEmail'));
$('#email').focus();
} else if (!/\S+@\S+\.\S+/.test(email)) {
showMsg(trans('auth.invalidEmail'), 'warning');
} else if (password == "") {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else if (password.length < 8 || password.length > 16) {
showMsg(trans('auth.invalidPassword'), 'warning');
$('#password').focus();
} else if ($('#confirm-pwd').val() == "") {
showMsg(trans('auth.emptyConfirmPwd'));
$('#confirm-pwd').focus();
} else if (password != $('#confirm-pwd').val()) {
showMsg(trans('auth.invalidConfirmPwd'), 'warning');
$('#confirm-pwd').focus();
} else if (nickname == "") {
showMsg(trans('auth.emptyNickname'));
$('#nickname').focus();
} else if (captcha == "") {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
} else {
$.ajax({
type: "POST",
url: "./register",
dataType: "json",
data: { 'email': email, 'password': password, 'nickname': nickname, 'captcha': captcha },
beforeSend: function () {
$('#register-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.registering')
).prop('disabled', 'disabled');
},
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
window.setTimeout('window.location = "../user"', 1000);
} else {
showMsg(json.msg, 'warning');
freshCaptcha();
$('#register-button').html(trans('auth.register')).prop('disabled', '');
}
},
error: (json) => {
showAjaxError(json);
$('#register-button').html(trans('auth.register')).prop('disabled', '');
}
});
}
return false;
});
$('#forgot-button').click(function () {
var email = $('#email').val();
var captcha = $('#captcha').val();
// check valid email address
if (email == "") {
showMsg(trans('auth.emptyEmail'));
$('#email').focus();
} else if (!/\S+@\S+\.\S+/.test(email)) {
showMsg(trans('auth.invalidEmail'), 'warning');
} else if (captcha == "") {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
} else {
$.ajax({
type: "POST",
url: "./forgot",
dataType: "json",
data: { 'email': email, 'captcha': captcha },
beforeSend: () => {
$('#forgot-button').html('<i class="fa fa-spinner fa-spin"></i> '+trans('auth.sending')).prop('disabled', 'disabled');
},
success: (json) => {
if (json.errno == 0) {
showMsg(json.msg, 'success');
$('#forgot-button').html(trans('auth.send')).prop('disabled', 'disabled');
} else {
showMsg(json.msg, 'warning');
freshCaptcha();
$('#forgot-button').html(trans('auth.send')).prop('disabled', '');
}
},
error: (json) => {
showAjaxError(json);
$('#forgot-button').html(trans('auth.send')).prop('disabled', '');
}
});
}
return false;
});
$('#reset-button').click(function () {
var uid = $('#uid').val();
var password = $('#password').val();
if (password == "") {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else if (password.length < 8 || password.length > 16) {
showMsg(trans('auth.invalidPassword'), 'warning');
$('#password').focus();
} else if ($('#confirm-pwd').val() == "") {
showMsg(trans('auth.emptyConfirmPwd'));
$('#confirm-pwd').focus();
} else if (password != $('#confirm-pwd').val()) {
showMsg(trans('auth.invalidConfirmPwd'), 'warning');
$('#confirm-pwd').focus();
} else {
$.ajax({
type: "POST",
url: "./reset",
dataType: "json",
data: { 'uid': uid, 'password': password },
beforeSend: () => {
$('#reset-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.resetting')
).prop('disabled', 'disabled');
},
success: (json) => {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
}).then(() => (window.location = "./login"));
} else {
showMsg(json.msg, 'warning');
$('#reset-button').html(trans('auth.reset')).prop('disabled', '');
}
},
error: (json) => {
showAjaxError(json);
$('#reset-button').html(trans('auth.reset')).prop('disabled', '');
}
});
}
return false;
});

View File

@ -0,0 +1,11 @@
'use strict';
function refreshCaptcha() {
let timestamp = new Date().getTime();
// Refresh Captcha Image
$('.captcha').attr('src', url(`auth/captcha?${timestamp}`));
// Clear input
$('#captcha').val('');
}
$('.captcha').click(refreshCaptcha);

View File

@ -0,0 +1,66 @@
/* global refreshCaptcha */
'use strict';
$('#login-button').click(() => {
let data = {
identification: $('#identification').val(),
password: $('#password').val(),
keep: $('#keep').prop('checked') ? true : false
};
if (data.identification == '') {
showMsg(trans('auth.emptyIdentification'));
$('#identification').focus();
} else if (data.password == '') {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else {
// Verify it when captcha form is shown
if ($('#captcha-form').css('display') == 'block') {
data.captcha = $('#captcha').val();
if (data.captcha == '') {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
return false;
}
}
fetch({
type: 'POST',
url: url('auth/login'),
dataType: 'json',
data: data,
beforeSend: () => {
$('#login-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.loggingIn')
).prop('disabled', 'disabled');
}
}).then(({ errno, msg, login_fails }) => {
if (errno == 0) {
swal({ type: 'success', html: msg });
window.setTimeout(() => {
window.location = url(blessing.redirect_to || 'user');
}, 1000);
} else {
if (login_fails > 3) {
if ($('#captcha-form').css('display') == 'none') {
swal({ type: 'error', html: trans('auth.tooManyFails') });
$('#captcha-form').show();
}
}
refreshCaptcha();
showMsg(msg, 'warning');
$('#login-button').html(trans('auth.login')).prop('disabled', '');
}
}).catch(err => {
showAjaxError(err);
$('#login-button').html(trans('auth.login')).prop('disabled', '');
});
}
});

View File

@ -0,0 +1,71 @@
/* global refreshCaptcha */
'use strict';
$('#register-button').click(() => {
let data = {
email: $('#email').val(),
password: $('#password').val(),
nickname: $('#nickname').val(),
captcha: $('#captcha').val()
};
(function validate({ email, password, nickname, captcha }, callback) {
// Massive form validation
if (email == '') {
showMsg(trans('auth.emptyEmail'));
$('#email').focus();
} else if (!/\S+@\S+\.\S+/.test(email)) {
showMsg(trans('auth.invalidEmail'), 'warning');
} else if (password == '') {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else if (password.length < 8 || password.length > 16) {
showMsg(trans('auth.invalidPassword'), 'warning');
$('#password').focus();
} else if ($('#confirm-pwd').val() == '') {
showMsg(trans('auth.emptyConfirmPwd'));
$('#confirm-pwd').focus();
} else if (password != $('#confirm-pwd').val()) {
showMsg(trans('auth.invalidConfirmPwd'), 'warning');
$('#confirm-pwd').focus();
} else if (nickname == '') {
showMsg(trans('auth.emptyNickname'));
$('#nickname').focus();
} else if (captcha == '') {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
} else {
callback();
}
return;
})(data, () => {
fetch({
type: 'POST',
url: url('auth/register'),
dataType: 'json',
data: data,
beforeSend: function () {
$('#register-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.registering')
).prop('disabled', 'disabled');
}
}).then(({ errno, msg }) => {
if (errno == 0) {
swal({ type: 'success', html: msg });
window.setTimeout(() => {
window.location = url('user');
}, 1000);
} else {
showMsg(msg, 'warning');
refreshCaptcha();
$('#register-button').html(trans('auth.register')).prop('disabled', '');
}
}).catch(err => {
showAjaxError(err);
$('#register-button').html(trans('auth.register')).prop('disabled', '');
});
});
});

View File

@ -0,0 +1,98 @@
/* global refreshCaptcha */
'use strict';
$('#forgot-button').click(() => {
let data = {
email: $('#email').val(),
captcha: $('#captcha').val()
};
(function validate({ email, captcha }, callback) {
if (email == '') {
showMsg(trans('auth.emptyEmail'));
$('#email').focus();
} else if (!/\S+@\S+\.\S+/.test(email)) {
showMsg(trans('auth.invalidEmail'), 'warning');
} else if (captcha == '') {
showMsg(trans('auth.emptyCaptcha'));
$('#captcha').focus();
} else {
callback();
}
})(data, () => {
fetch({
type: 'POST',
url: url('auth/forgot'),
dataType: 'json',
data: data,
beforeSend: () => {
$('#forgot-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.sending')
).prop('disabled', 'disabled');
}
}).then(({ errno, msg }) => {
if (errno == 0) {
showMsg(msg, 'success');
$('#forgot-button').html(trans('auth.send')).prop('disabled', 'disabled');
} else {
showMsg(msg, 'warning');
refreshCaptcha();
$('#forgot-button').html(trans('auth.send')).prop('disabled', '');
}
}).catch(err => {
showAjaxError(err);
$('#forgot-button').html(trans('auth.send')).prop('disabled', '');
});
});
});
$('#reset-button').click(() => {
let data = {
uid: $('#uid').val(),
password: $('#password').val()
};
(function validate({ password }, callback) {
if (password == '') {
showMsg(trans('auth.emptyPassword'));
$('#password').focus();
} else if (password.length < 8 || password.length > 16) {
showMsg(trans('auth.invalidPassword'), 'warning');
$('#password').focus();
} else if ($('#confirm-pwd').val() == '') {
showMsg(trans('auth.emptyConfirmPwd'));
$('#confirm-pwd').focus();
} else if (password != $('#confirm-pwd').val()) {
showMsg(trans('auth.invalidConfirmPwd'), 'warning');
$('#confirm-pwd').focus();
} else {
callback();
}
})(data, () => {
fetch({
type: 'POST',
url: url('auth/reset'),
dataType: 'json',
data: data,
beforeSend: () => {
$('#reset-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('auth.resetting')
).prop('disabled', 'disabled');
}
}).then(({ errno, msg }) => {
if (errno == 0) {
swal({
type: 'success',
html: msg
}).then(() => (window.location = url('auth/login')));
} else {
showMsg(msg, 'warning');
$('#reset-button').html(trans('auth.reset')).prop('disabled', '');
}
}).catch(err => {
showAjaxError(err);
$('#reset-button').html(trans('auth.reset')).prop('disabled', '');
});
});
});

View File

@ -0,0 +1,51 @@
/* exported trans */
'use strict';
$.locales = {};
$.currentLocale = {};
/**
* Load current selected language.
*
* @return void
*/
function loadLocales() {
for (let lang in $.locales) {
if (!isEmpty($.locales[lang])) {
$.currentLocale = $.locales[lang] || {};
}
}
}
/**
* Translate according to given key.
*
* @param {string} key
* @param {dict} parameters
* @return {string}
*/
function trans(key, parameters = {}) {
if (isEmpty($.currentLocale)) {
loadLocales();
}
let segments = key.split('.');
let temp = $.currentLocale || {};
for (let i in segments) {
if (isEmpty(temp[segments[i]])) {
return key;
} else {
temp = temp[segments[i]];
}
}
for (let i in parameters) {
if (!isEmpty(parameters[i])) {
temp = temp.replace(':'+i, parameters[i]);
}
}
return temp;
}

View File

@ -0,0 +1,36 @@
'use strict';
$.defaultPaginatorConfig = {
visiblePages: 5,
currentPage: 1,
first: '<li><a style="cursor: pointer;">«</a></li>',
prev: '<li><a style="cursor: pointer;"></a></li>',
next: '<li><a style="cursor: pointer;"></a></li>',
last: '<li><a style="cursor: pointer;">»</a></li>',
page: '<li><a style="cursor: pointer;">{{page}}</a></li>',
wrapper: '<ul class="pagination pagination-sm no-margin"></ul>'
};
$(window).resize(activateLayout);
$(document).ready(() => {
activateLayout();
$('li.active > ul').show();
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue'
});
swal.setDefaults({
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
});
});
function activateLayout() {
if (location.pathname == '/' || location.pathname.includes('auth'))
return;
$.AdminLTE.layout.activate();
}

View File

@ -0,0 +1,29 @@
'use strict';
function confirmLogout() {
swal({
text: trans('general.confirmLogout'),
type: 'warning',
showCancelButton: true,
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
}).then(() => {
logout().then(json => {
swal({
type: 'success',
html: json.msg
});
window.setTimeout(() => window.location = url(), 1000);
});
});
}
function logout() {
return fetch({
type: 'POST',
url: url('auth/logout'),
dataType: 'json'
});
}
$('#logout-button').click(() => confirmLogout());

View File

@ -0,0 +1,59 @@
/* exported showMsg, showAjaxError, showModal */
'use strict';
/**
* Show message to div#msg with level
*
* @param {string} msg
* @param {string} type
* @return {void}
*/
function showMsg(msg, type = 'info') {
$('[id=msg]').removeClass().addClass('callout').addClass(`callout-${type}`).html(msg);
}
/**
* Show modal if error occured when sending an ajax request.
*
* @param {object} json
* @return {void}
*/
function showAjaxError(json) {
if (typeof json == 'string') {
return console.warn(json);
}
if (! json.responseText) {
return console.warn('Empty Ajax response body.');
}
showModal(json.responseText.replace(/\n/g, '<br />'), trans('general.fatalError'), 'danger');
}
function showModal(msg, title = 'Message', type = 'default', options = {}) {
let btnType = (type != 'default') ? 'btn-outline' : 'btn-primary';
let onClick = (options.callback === undefined) ? 'data-dismiss="modal"' : `onclick="${options.callback}"`;
let dom = `
<div class="modal modal-${type} fade in">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">${title}</h4>
</div>
<div class="modal-body">
<p>${msg}</p>
</div>
<div class="modal-footer">
<button type="button" ${onClick} class="btn ${btnType}">OK</button>
</div>
</div>
</div>
</div>`;
$(dom).modal(options);
}

View File

@ -0,0 +1,30 @@
'use strict';
// polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
// polyfill of String.prototype.endsWith
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}

View File

@ -0,0 +1,100 @@
/* global MSP */
/* exported TexturePreview */
class TexturePreview {
constructor(type, tid, preference) {
this.tid = tid;
this.type = type;
this.selector = $(`#${type}`);
this.preference = (type == 'steve') ? 'default' : 'slim';
this.playerPreference = preference;
}
change2dPreview() {
this.selector
.attr('src', url(`preview/200/${this.tid}.png`))
.show()
.parent().attr('href', url(`skinlib/show/${this.tid}`))
.next().hide();
return this;
}
change3dPreview() {
if (this.playerPreference == this.preference || this.type == 'cape') {
fetch({
type: 'GET',
url: url(`skinlib/info/${this.tid}`),
dataType: 'json'
}).then(({ hash }) => {
let textureUrl = url(`textures/${hash}`);
if (this.type == 'cape') {
MSP.changeCape(textureUrl);
} else {
MSP.changeSkin(textureUrl);
}
}).catch(err => showAjaxError(err));
}
return this;
}
showNotUploaded() {
this.selector.hide().parent().next().show();
// clear 3D preview of cape
if (this.type == 'cape') {
MSP.changeCape('');
}
return this;
}
}
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview = () => {
if (TexturePreview.previewType == '2D') return;
$('#preview-2d').hide();
let canvas = null;
if ($(window).width() < 800) {
canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
} else {
canvas = MSP.get3dSkinCanvas(350, 350);
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
}
};
TexturePreview.show3dPreview = () => {
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview();
$('#preview-2d').hide();
$('.operations').show();
$('#preview-switch').html(trans('user.switch2dPreview'));
};
TexturePreview.show2dPreview = () => {
TexturePreview.previewType = '2D';
$('#canvas3d').remove();
$('.operations').hide();
$('#preview-2d').show();
$('#preview-switch').html(trans('user.switch3dPreview')).attr('onclick', 'show3dPreview();');
};
// change 3D preview status
$('.fa-pause').click(function () {
MSP.setStatus('rotation', ! MSP.getStatus('rotation'));
MSP.setStatus('movements', ! MSP.getStatus('movements'));
$(this).toggleClass('fa-pause').toggleClass('fa-play');
});
$('.fa-forward').click(() => MSP.setStatus('running', ! MSP.getStatus('running')));
$('.fa-repeat' ).click(() => MSP.setStatus('rotation', ! MSP.getStatus('rotation')));

View File

@ -0,0 +1,98 @@
/* exported isEmpty, fetch, getQueryString, debounce, url */
'use strict';
console.log(`\n %c Blessing Skin v${blessing.version} %c https://blessing.studio \n\n`, 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
/**
* Check if given value is empty.
*
* @param {any} obj
* @return {Boolean}
*/
function isEmpty(obj) {
// null and undefined are "empty"
if (obj == null) return true;
if (typeof (obj) == 'number' || typeof (obj) == 'boolean') return false;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// If it isn't an object at this point
// it is empty, but it can't be anything *but* empty
// Is it empty? Depends on your application.
if (typeof obj !== 'object') return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
/**
* A fake fetch API. Returns Promises.
*
* @param {array} option Same as options of jQuery.ajax()
* @return {Promise}
*/
function fetch(option) {
return Promise.resolve($.ajax(option));
}
/**
* Get parameters in query string with key.
*
* @param {string} key
* @param {string} defaultValue
* @return {string}
*/
function getQueryString(key, defaultValue) {
let result = location.search.match(new RegExp('[?&]'+key+'=([^&]+)','i'));
if (result == null || result.length < 1){
return defaultValue;
} else {
return result[1];
}
}
/**
* Return a debounced function
*
* @param {Function} func
* @param {number} delay
* @param {Array} args
* @param {Object} context
*/
function debounce(func, delay, args = [], context = undefined) {
if (isNaN(delay) || typeof func !== 'function') {
throw new Error('Arguments type of function "debounce" is incorrent!');
}
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
function url(relativeUri) {
relativeUri = relativeUri || '';
blessing.base_url = blessing.base_url || '';
if (relativeUri[0] != '/') {
relativeUri = '/' + relativeUri;
}
return blessing.base_url + relativeUri;
}

View File

@ -1,384 +0,0 @@
'use strict';
console.log(`\n %c Blessing Skin v${blessing.version} %c https://blessing.studio \n\n`,"color: #fadfa3; background: #030307; padding:5px 0;","background: #fadfa3; padding:5px 0;");
$.locales = {};
$.currentLocale = {};
$.defaultPaginatorConfig = {
visiblePages: 5,
currentPage: 1,
first: '<li><a style="cursor: pointer;">«</a></li>',
prev: '<li><a style="cursor: pointer;"></a></li>',
next: '<li><a style="cursor: pointer;"></a></li>',
last: '<li><a style="cursor: pointer;">»</a></li>',
page: '<li><a style="cursor: pointer;">{{page}}</a></li>',
wrapper: '<ul class="pagination pagination-sm no-margin"></ul>'
};
// polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
// polyfill of String.prototype.endsWith
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
$(window).ready(activateLayout).resize(activateLayout);
function activateLayout() {
if (location.pathname == "/" || location.pathname.includes('auth'))
return;
$.AdminLTE.layout.activate();
}
/**
* Check if given value is empty.
*
* @param {any} obj
* @return {Boolean}
*/
function isEmpty(obj) {
// null and undefined are "empty"
if (obj == null) return true;
if (typeof (obj) == 'number' || typeof (obj) == 'boolean') return false;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// If it isn't an object at this point
// it is empty, but it can't be anything *but* empty
// Is it empty? Depends on your application.
if (typeof obj !== "object") return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
/**
* Load current selected language.
*
* @return void
*/
function loadLocales() {
for (lang in $.locales) {
if (!isEmpty($.locales[lang])) {
$.currentLocale = $.locales[lang] || {};
}
}
}
/**
* Translate according to given key.
*
* @param {string} key
* @param {dict} parameters
* @return {string}
*/
function trans(key, parameters = {}) {
if (isEmpty($.currentLocale)) {
loadLocales();
}
let segments = key.split('.');
let temp = $.currentLocale || {};
for (i in segments) {
if (isEmpty(temp[segments[i]])) {
return key;
} else {
temp = temp[segments[i]];
}
}
for (i in parameters) {
if (!isEmpty(parameters[i])) {
temp = temp.replace(':'+i, parameters[i]);
}
}
return temp;
}
function showModal(msg, title = 'Message', type = 'default', options = {}) {
let btnType = (type != "default") ? "btn-outline" : "btn-primary";
let onClick = (options.callback === undefined) ? 'data-dismiss="modal"' : `onclick="${options.callback}"`;
let dom = `
<div class="modal modal-${type} fade in">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">${title}</h4>
</div>
<div class="modal-body">
<p>${msg}</p>
</div>
<div class="modal-footer">
<button type="button" ${onClick} class="btn ${btnType}">OK</button>
</div>
</div>
</div>
</div>`;
$(dom).modal(options);
}
/**
* Show message to div#msg with level
*
* @param {string} msg
* @param {string} type
* @return {void}
*/
function showMsg(msg, type = 'info') {
$("[id=msg]").removeClass().addClass("callout").addClass('callout-'+type).html(msg);
}
/**
* Show modal if error occured when sending an ajax request.
*
* @param {object} json
* @return {void}
*/
function showAjaxError(json) {
if (!json.responseText) {
console.warn('Empty Ajax response body.');
return;
}
showModal(json.responseText.replace(/\n/g, '<br />'), trans('general.fatalError'), 'danger');
}
/**
* Get parameters in query string with key.
*
* @param {string} key
* @param {string} defaultValue
* @return {string}
*/
function getQueryString(key, defaultValue) {
result = location.search.match(new RegExp('[\?\&]'+key+'=([^\&]+)','i'));
if (result == null || result.length < 1){
return defaultValue;
} else {
return result[1];
}
}
/**
* Return a debounced function
*
* @param {Function} func
* @param {number} delay
* @param {Array} args
* @param {Object} context
*/
function debounce(func, delay, args = [], context = undefined) {
if (isNaN(delay) || typeof func !== 'function') {
throw new Error('Arguments type of function "debounce" is incorrent!');
}
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
}
}
function url(relativeUri) {
relativeUri = relativeUri || "";
blessing.base_url = blessing.base_url || "";
if (relativeUri[0] != "/") {
relativeUri = "/" + relativeUri;
}
return blessing.base_url + relativeUri;
}
function confirmLogout() {
swal({
text: trans('general.confirmLogout'),
type: 'warning',
showCancelButton: true,
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
}).then(() => {
logout().then((json) => {
swal({
type: 'success',
html: json.msg
});
window.setTimeout(() => window.location = url(), 1000);
});
});
}
function logout() {
return Promise.resolve($.ajax({
type: "POST",
url: url('auth/logout'),
dataType: "json"
}));
}
$('#logout-button').click(() => confirmLogout());
$(document).ready(() => $('li.active > ul').show());
var TexturePreview = function (type, tid, preference) {
this.tid = tid;
this.type = type;
this.selector = $('#' + type);
this.preference = type == 'steve' ? 'default' : 'slim';
this.playerPreference = preference;
this.change2dPreview = function () {
this.selector
.attr('src', url(`preview/200/${this.tid}.png`))
.show()
.parent().attr('href', url('skinlib/show/' + this.tid))
.next().hide();
return this;
}
this.change3dPreview = function () {
if (this.playerPreference == this.preference || this.type == 'cape') {
$.ajax({
type: "GET",
url: url(`skinlib/info/${this.tid}`),
dataType: "json",
success: (json) => {
let textureUrl = url('textures/' + json.hash);
if (this.type == 'cape') {
MSP.changeCape(textureUrl);
} else {
MSP.changeSkin(textureUrl);
}
},
error: (json) => showAjaxError(json)
});
}
return this;
}
this.showNotUploaded = function () {
this.selector.hide().parent().next().show();
// clear 3D preview of cape
if (this.type == 'cape') {
MSP.changeCape('');
}
return this;
}
}
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview = () => {
if (TexturePreview.previewType == '2D') return;
$('#preview-2d').hide();
if ($(window).width() < 800) {
var canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
} else {
var canvas = MSP.get3dSkinCanvas(350, 350);
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
}
}
TexturePreview.show3dPreview = () => {
TexturePreview.previewType = "3D";
TexturePreview.init3dPreview();
$('#preview-2d').hide();
$('.operations').show();
$('#preview-switch').html(trans('user.switch2dPreview'));
}
TexturePreview.show2dPreview = () => {
TexturePreview.previewType = '2D';
$('#canvas3d').remove();
$('.operations').hide();
$('#preview-2d').show();
$('#preview-switch').html(trans('user.switch3dPreview')).attr('onclick', 'show3dPreview();');
}
// change 3D preview status
$('.fa-pause').click(function () {
MSP.setStatus('rotation', ! MSP.getStatus('rotation'));
MSP.setStatus('movements', ! MSP.getStatus('movements'));
$(this).toggleClass('fa-pause').toggleClass('fa-play');
});
$('.fa-forward').click(() => MSP.setStatus('running', !MSP.getStatus('running')) );
$('.fa-repeat' ).click(() => MSP.setStatus('rotation', !MSP.getStatus('rotation')) );
(function ($) {
if ($('#copyright-text').length != 0 && $('#copyright-text').text().indexOf('Blessing') >= 0) {
return;
}
$.ajax({
type: 'POST',
url: 'https://work.prinzeugen.net/statistics/whitelist',
dataType: 'json',
data: { site_name: blessing.site_name, site_url: blessing.base_url }
}).done((json) => {
if (!json.inWhiteList) {
// :(
showModal("It looks like that you have removed the program's copyright. It's better for you to restore it ASAP, otherwise something terrible will be applied to your site :)<br><br>If there is a false alarm, please send a mail to h@prinzeugen.net to correct it.", 'CMN BAD ASS', 'danger', {
'backdrop': 'static',
'keyboard': false
});
}
});
})(jQuery);

View File

@ -1,537 +0,0 @@
'use strict';
$(document).ready(() => {
swal.setDefaults({
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
});
if ($('#skinlib-container').length != 0) {
// Initially render skinlib
requestSkinlibData().then(result => {
renderSkinlib(result.items);
updatePaginator(
$.skinlib.page,
result.total_pages || 1
);
});
}
});
$('#private').on('ifToggled', function () {
$(this).prop('checked') ? $('#msg').show() : $('#msg').hide();
});
$('#type-skin').on('ifToggled', function () {
$(this).prop('checked') ? $('#skin-type').show() : $('#skin-type').hide();
});
$.skinlib = {
page: getQueryString('page', 1),
filter: getQueryString('filter', 'skin'),
sort: getQueryString('sort', 'time'),
uploader: getQueryString('uploader', 0),
keyword: decodeURI(getQueryString('keyword', ''))
};
$(document).ready(() => {
});
function renderSkinlib(items) {
let container = $('#skinlib-container').html('');
if (items.length === 0) {
$('#skinlib-paginator').hide();
container.html(`<p style="text-align: center; margin: 30px 0;">
${ trans('general.noResult') }
</p>`);
} else {
$('#skinlib-paginator').show();
for (const item of items) {
container.append(renderSkinlibItemComponent(item));
}
}
$('.overlay').hide();
}
function reloadSkinlib() {
requestSkinlibData().then(result => {
$('.overlay').show();
renderSkinlib(result.items);
updatePaginator($.skinlib.page, result.total_pages || 1);
}).then(() => {
updateUrlQueryString();
updateBreadCrumb();
});
}
function requestSkinlibData() {
return Promise.resolve($.ajax({
type: 'GET',
url: url('skinlib/data'),
dataType: 'json',
data: $.skinlib,
error: showAjaxError
}));
}
function renderSkinlibItemComponent(item) {
let title = "";
let anonymous = "";
let liked = item.liked ? 'liked' : '';
if (item.liked === undefined) {
// If user haven't logged in
title = trans('skinlib.anonymous');
anonymous = 'anonymous';
} else {
title = item.liked ? trans('skinlib.removeFromCloset') : trans('skinlib.addToCloset');
}
return `<a href="${ url('skinlib/show/' + item.tid) }">
<div class="item" tid="${ item.tid }">
<div class="item-body">
<img src="${ url('preview/' + item.tid + '.png') }">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${ item.name }">${ item.name }
<small>${ trans("skinlib.filter." + item.type) }</small>
</span>
</p>
<a title="${title}" class="more like ${liked} ${anonymous}" tid="${ item.tid }" href="javascript:;" data-placement="top" data-toggle="tooltip"><i class="fa fa-heart"></i></a>
<small class="more private-label ${(item.public == 0) ? '' : 'hide'}" tid="${ item.tid }">
${ trans('skinlib.private') }
</small>
</div>
</div>
</a>`;
}
function updatePaginator(currentPage, totalPages) {
$.skinlib.page = currentPage;
$('p.pagination').text(trans('general.pagination', {
page: currentPage,
total: totalPages
}));
let paginator = $('#skinlib-paginator');
if (paginator.html().length == 0) {
// init paginator
$('#skinlib-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
currentPage: parseInt(currentPage),
totalPages: parseInt(totalPages),
onPageChange: onPageChange
}));
} else {
$('#skinlib-paginator').jqPaginator('option', {
currentPage: parseInt(currentPage),
totalPages: parseInt(totalPages)
});
}
let pageSelectElement = $('select.pagination').html('');
for (let i = 1; i <= totalPages; i++) {
pageSelectElement.append(`
<option value="${i}" ${ (i == currentPage) ? 'selected' : '' }>${i}</option>
`);
}
}
function onPageChange(page, type) {
$.skinlib.page = page;
updateBreadCrumb();
if (type == "init") {
console.log('Init paginator', page);
} else {
$('.overlay').show();
reloadSkinlib();
console.log('Rendering page', page);
}
}
$('select.pagination').on('change', function () {
onPageChange(parseInt($(this).val()));
});
$(document).on('click', '.more.like', function () {
let tid = $(this).attr('tid');
if ($(this).hasClass('anonymous'))
return;
if ($(this).hasClass('liked')) {
removeFromCloset(tid);
} else {
addToCloset(tid);
}
});
$('.filter').click(function (e) {
e.preventDefault();
let selectedFilter = $(this).data('filter');
if (selectedFilter == "uploader") {
$.skinlib.uploader = $(this).data('uid');
console.log('Show items uploaded by uid ' + $.skinlib.uploader);
} else {
$.skinlib.filter = selectedFilter;
console.log('Filter by ' + $.skinlib.filter);
}
reloadSkinlib();
});
$('.sort').click(function (e) {
e.preventDefault();
$.skinlib.sort = $(this).data('sort');
console.log('Sort by ' + $.skinlib.sort);
reloadSkinlib();
});
$('#search-form').submit(function (e) {
e.preventDefault();
$.skinlib.keyword = $('#navbar-search-input').val();
console.log('Search keyword: ' + $.skinlib.keyword);
reloadSkinlib();
});
function updateUrlQueryString() {
let query = $.param($.skinlib);
window.history.pushState(null, null, url(`skinlib?${query}`));
}
function updateBreadCrumb() {
if ($.skinlib.filter == "cape") {
$('#filter-indicator').html(trans('general.cape'));
} else {
$('#filter-indicator').html(trans('general.skin') + `<small>
${ trans('skinlib.filter.' + $.skinlib.filter) }
</small>`);
}
if ($.skinlib.uploader != 0) {
$('#uploader-indicator').html(trans('skinlib.filter.uploader', {uid: $.skinlib.uploader}));
} else {
$('#uploader-indicator').html(trans('skinlib.filter.allUsers'));
}
$('#sort-indicator').html(trans('skinlib.sort.' + $.skinlib.sort));
if ($.skinlib.keyword != "") {
$('#search-indicator').html(trans('general.searchResult', {
keyword: decodeURI($.skinlib.keyword)
}));
$('#navbar-search-input').val(decodeURI($.skinlib.keyword));
}
}
function addToCloset(tid) {
$.getJSON(url(`skinlib/info/${tid}`), (json) => {
swal({
title: trans('skinlib.setItemName'),
inputValue: json.name,
input: 'text',
showCancelButton: true,
inputValidator: (value) => {
return new Promise((resolve, reject) => {
value ? resolve() : reject(trans('skinlib.emptyItemName'));
});
}
}).then((result) => ajaxAddToCloset(tid, result));
});
}
/**
* Update button action & likes of texture.
*
* @param {int} tid
* @param {string} action add|remove
* @return {null}
*/
function updateTextureStatus(tid, action) {
let likes = parseInt($('#likes').html()) + (action == "add" ? 1 : -1);
action = (action == "add") ? 'removeFromCloset' : 'addToCloset';
$(`a[tid=${tid}]`).attr('href', `javascript:${action}(${tid});`).attr('title', trans('skinlib.' + action)).toggleClass('liked');
$('#'+tid).attr('href', `javascript:${action}(${tid});`).html(trans('skinlib.' + action));
$('#likes').html(likes);
}
function ajaxAddToCloset(tid, name) {
// remove interference of modal which is hide
$('.modal').each(function () {
return ($(this).css('display') == "none") ? $(this).remove() : null;
});
$.ajax({
type: "POST",
url: url("user/closet/add"),
dataType: "json",
data: { 'tid': tid, 'name': name },
success: (json) => {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
$('.modal').modal('hide');
updateTextureStatus(tid, 'add');
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function removeFromCloset(tid) {
swal({
text: trans('user.removeFromClosetNotice'),
type: 'warning',
showCancelButton: true,
cancelButtonColor: '#3085d6',
confirmButtonColor: '#d33'
}).then(() => {
$.ajax({
type: "POST",
url: url("/user/closet/remove"),
dataType: "json",
data: { 'tid' : tid },
success: (json) => {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
updateTextureStatus(tid, 'remove');
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
$('body').on('change', '#file', () => handleFiles()).on('ifToggled', '#type-cape', () => {
MSP.clear();
handleFiles();
});
// Real-time preview
function handleFiles(files, type) {
files = files || $('#file').prop('files');
type = type || $('#type-cape').prop('checked') ? "cape" : "skin";
if (files.length > 0) {
let file = files[0];
if (file.type === "image/png" || file.type === "image/x-png") {
let reader = new FileReader();
reader.onload = function (e) {
let img = new Image();
img.onload = () => {
(type == "skin") ? MSP.changeSkin(img.src) : MSP.changeCape(img.src);
let domTextureName = $('#name');
if (domTextureName.val() === '' || domTextureName.val() === domTextureName.attr('data-last-file-name')) {
const fileName = file.name.replace(/\.[Pp][Nn][Gg]$/, '');
domTextureName.attr('data-last-file-name', fileName);
domTextureName.val(fileName);
}
};
img.onerror = () => toastr.warning(trans('skinlib.fileExtError'));
img.src = this.result;
};
reader.readAsDataURL(file);
} else {
toastr.warning(trans('skinlib.encodingError'));
}
}
};
function upload() {
let form = new FormData();
let file = $('#file').prop('files')[0];
form.append('name', $('#name').val());
form.append('file', file);
form.append('public', ! $('#private').prop('checked'));
if ($('#type-skin').prop('checked')) {
form.append('type', $('#skin-type').val());
} else if ($('#type-cape').prop('checked')) {
form.append('type', 'cape');
} else {
return toastr.info(trans('skinlib.emptyTextureType'));
}
if (file === undefined) {
toastr.info(trans('skinlib.emptyUploadFile'));
$('#file').focus();
} else if ($('#name').val() == "") {
toastr.info(trans('skinlib.emptyTextureName'));
$('#name').focus();
} else if (file.type !== "image/png") {
toastr.warning(trans('skinlib.fileExtError'));
$('#file').focus();
} else {
$.ajax({
type: "POST",
url: url("skinlib/upload"),
contentType: false,
dataType: "json",
data: form,
processData: false,
beforeSend: () => {
$('#upload-button').html('<i class="fa fa-spinner fa-spin"></i> ' + trans('skinlib.uploading')).prop('disabled', 'disabled');
},
success: (json) => {
if (json.errno == 0) {
let redirect = function () {
toastr.info(trans('skinlib.redirecting'));
window.setTimeout(() => {
window.location = url(`skinlib/show/${json.tid}`);
}, 1000);
};
// always redirect
swal({
type: 'success',
html: json.msg
}).then(redirect, redirect);
} else {
swal({
type: 'warning',
html: json.msg
}).then(() => {
$('#upload-button').html(trans('skinlib.upload')).prop('disabled', '');
});
}
},
error: (json) => {
$('#upload-button').html(trans('skinlib.upload')).prop('disabled', '');
showAjaxError(json);
}
});
}
return false;
}
function changeTextureName(tid, oldName) {
swal({
text: trans('skinlib.setNewTextureName'),
input: 'text',
inputValue: oldName,
showCancelButton: true,
inputValidator: (value) => {
return new Promise((resolve, reject) => {
(value) ? resolve() : reject(trans('skinlib.emptyNewTextureName'));
});
}
}).then((new_name) => {
$.ajax({
type: "POST",
url: url("skinlib/rename"),
dataType: "json",
data: { 'tid': tid, 'new_name': new_name },
success: (json) => {
if (json.errno == 0) {
$('#name').text(new_name);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
$(document).on('click', '.private-label', function () {
swal({
text: trans('skinlib.setPublicNotice'),
type: 'warning',
showCancelButton: true
}).then(() => {
changePrivacy($(this).attr('tid'));
$(this).remove();
});
});
function changePrivacy(tid) {
$.ajax({
type: "POST",
url: url(`skinlib/privacy`),
dataType: "json",
data: { 'tid': tid },
success: (json) => {
if (json.errno == 0) {
toastr.success(json.msg);
if (json.public == "0")
$('a:contains("' + trans('skinlib.setAsPrivate') + '")').html(trans('skinlib.setAsPublic'));
else
$('a:contains("' + trans('skinlib.setAsPublic') + '")').html(trans('skinlib.setAsPrivate'));
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function deleteTexture(tid) {
swal({
text: trans('skinlib.deleteNotice'),
type: 'warning',
showCancelButton: true
}).then(function() {
$.ajax({
type: "POST",
url: url("skinlib/delete"),
dataType: "json",
data: { 'tid': tid },
success: (json) => {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
}).then(() => window.location = url('skinlib') );
} else {
swal({
type: 'warning',
html: json.msg
});
}
},
error: showAjaxError
});
});
}

View File

@ -0,0 +1,215 @@
'use strict';
$.skinlib = {
page: getQueryString('page', 1),
filter: getQueryString('filter', 'skin'),
sort: getQueryString('sort', 'time'),
uploader: getQueryString('uploader', 0),
keyword: decodeURI(getQueryString('keyword', ''))
};
$(document).ready(() => {
if ($('#skinlib-container').length != 0) {
// Initially render skinlib
requestSkinlibData().then(result => {
renderSkinlib(result.items);
updatePaginator(
$.skinlib.page,
result.total_pages || 1
);
});
}
});
$('select.pagination').on('change', function () {
onPageChange(parseInt($(this).val()));
});
$('.filter').click(function (e) {
e.preventDefault();
let selectedFilter = $(this).data('filter');
if (selectedFilter == 'uploader') {
$.skinlib.uploader = $(this).data('uid');
console.log('Show items uploaded by uid ' + $.skinlib.uploader);
} else {
$.skinlib.filter = selectedFilter;
console.log('Filter by ' + $.skinlib.filter);
}
reloadSkinlib();
});
$('.sort').click(function (e) {
e.preventDefault();
$.skinlib.sort = $(this).data('sort');
console.log('Sort by ' + $.skinlib.sort);
reloadSkinlib();
});
$('#search-form').submit(function (e) {
e.preventDefault();
$.skinlib.keyword = $('#navbar-search-input').val();
console.log('Search keyword: ' + $.skinlib.keyword);
reloadSkinlib();
});
function renderSkinlib(items) {
let container = $('#skinlib-container').html('');
if (items.length === 0) {
$('#skinlib-paginator').hide();
container.html(`<p style="text-align: center; margin: 30px 0;">
${ trans('general.noResult') }
</p>`);
} else {
$('#skinlib-paginator').show();
for (const item of items) {
container.append(renderSkinlibItemComponent(item));
}
}
$('.overlay').hide();
}
function reloadSkinlib() {
requestSkinlibData().then(result => {
$('.overlay').show();
renderSkinlib(result.items);
updatePaginator($.skinlib.page, result.total_pages || 1);
}).then(() => {
updateUrlQueryString();
updateBreadCrumb();
});
}
function requestSkinlibData() {
return fetch({
type: 'GET',
url: url('skinlib/data'),
dataType: 'json',
data: $.skinlib,
error: showAjaxError
});
}
function renderSkinlibItemComponent(item) {
let title = '';
let anonymous = '';
let liked = item.liked ? 'liked' : '';
if (item.liked === undefined) {
// If user haven't logged in
title = trans('skinlib.anonymous');
anonymous = 'anonymous';
} else {
title = item.liked ? trans('skinlib.removeFromCloset') : trans('skinlib.addToCloset');
}
return `<a href="${ url('skinlib/show/' + item.tid) }">
<div class="item" tid="${ item.tid }">
<div class="item-body">
<img src="${ url('preview/' + item.tid + '.png') }">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${ item.name }">${ item.name }
<small>${ trans('skinlib.filter.' + item.type) }</small>
</span>
</p>
<a title="${title}" class="more like ${liked} ${anonymous}" tid="${ item.tid }" href="javascript:;" data-placement="top" data-toggle="tooltip"><i class="fa fa-heart"></i></a>
<small class="more private-label ${(item.public == 0) ? '' : 'hide'}" tid="${ item.tid }">
${ trans('skinlib.private') }
</small>
</div>
</div>
</a>`;
}
function updatePaginator(currentPage, totalPages) {
$.skinlib.page = currentPage;
$('p.pagination').text(trans('general.pagination', {
page: currentPage,
total: totalPages
}));
let paginator = $('#skinlib-paginator');
if (paginator.html().length == 0) {
// init paginator
$('#skinlib-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
currentPage: parseInt(currentPage),
totalPages: parseInt(totalPages),
onPageChange: onPageChange
}));
} else {
$('#skinlib-paginator').jqPaginator('option', {
currentPage: parseInt(currentPage),
totalPages: parseInt(totalPages)
});
}
let pageSelectElement = $('select.pagination').html('');
for (let i = 1; i <= totalPages; i++) {
pageSelectElement.append(`
<option value="${i}" ${ (i == currentPage) ? 'selected' : '' }>${i}</option>
`);
}
}
function onPageChange(page, type) {
$.skinlib.page = page;
updateBreadCrumb();
if (type == 'init') {
console.log('Init paginator', page);
} else {
$('.overlay').show();
reloadSkinlib();
console.log('Rendering page', page);
}
}
function updateUrlQueryString() {
let query = $.param($.skinlib);
window.history.pushState(null, null, url(`skinlib?${query}`));
}
function updateBreadCrumb() {
if ($.skinlib.filter == 'cape') {
$('#filter-indicator').html(trans('general.cape'));
} else {
$('#filter-indicator').html(trans('general.skin') + `<small>
${ trans('skinlib.filter.' + $.skinlib.filter) }
</small>`);
}
if ($.skinlib.uploader != 0) {
$('#uploader-indicator').html(trans('skinlib.filter.uploader', { uid: $.skinlib.uploader }));
} else {
$('#uploader-indicator').html(trans('skinlib.filter.allUsers'));
}
$('#sort-indicator').html(trans('skinlib.sort.' + $.skinlib.sort));
if ($.skinlib.keyword != '') {
$('#search-indicator').html(trans('general.searchResult', {
keyword: decodeURI($.skinlib.keyword)
}));
$('#navbar-search-input').val(decodeURI($.skinlib.keyword));
}
}

View File

@ -0,0 +1,173 @@
/* exported changeTextureName, deleteTexture */
'use strict';
$(document).on('click', '.more.like', function () {
let tid = $(this).attr('tid');
if ($(this).hasClass('anonymous'))
return;
if ($(this).hasClass('liked')) {
removeFromCloset(tid);
} else {
addToCloset(tid);
}
});
function addToCloset(tid) {
$.getJSON(url(`skinlib/info/${tid}`), ({ name }) => {
swal({
title: trans('skinlib.setItemName'),
inputValue: name,
input: 'text',
showCancelButton: true,
inputValidator: value => (new Promise((resolve, reject) => {
value ? resolve() : reject(trans('skinlib.emptyItemName'));
}))
}).then((result) => ajaxAddToCloset(tid, result));
});
}
function ajaxAddToCloset(tid, name) {
// Remove interference of modal which is hide
$('.modal').each(function () {
return ($(this).css('display') == 'none') ? $(this).remove() : null;
});
fetch({
type: 'POST',
url: url('user/closet/add'),
dataType: 'json',
data: { tid: tid, name: name }
}).then(({ errno, msg }) => {
if (errno == 0) {
swal({ type: 'success', html: msg });
$('.modal').modal('hide');
updateTextureStatus(tid, 'add');
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function removeFromCloset(tid) {
swal({
text: trans('user.removeFromClosetNotice'),
type: 'warning',
showCancelButton: true,
cancelButtonColor: '#3085d6',
confirmButtonColor: '#d33'
}).then(() => fetch({
type: 'POST',
url: url('/user/closet/remove'),
dataType: 'json',
data: { tid: tid }
})).then(({ errno, msg }) => {
if (errno == 0) {
swal({ type: 'success', html: msg });
updateTextureStatus(tid, 'remove');
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function changeTextureName(tid, oldName) {
let newTextureName = '';
swal({
text: trans('skinlib.setNewTextureName'),
input: 'text',
inputValue: oldName,
showCancelButton: true,
inputValidator: value => (new Promise((resolve, reject) => {
(newTextureName = value) ? resolve() : reject(trans('skinlib.emptyNewTextureName'));
}))
}).then(name => fetch({
type: 'POST',
url: url('skinlib/rename'),
dataType: 'json',
data: { tid: tid, new_name: name }
})).then(({ errno, msg }) => {
if (errno == 0) {
$('#name').text(newTextureName);
toastr.success(msg);
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
/**
* Update button action & likes of texture.
*
* @param {int} tid
* @param {string} action add|remove
* @return {null}
*/
function updateTextureStatus(tid, action) {
let likes = parseInt($('#likes').html()) + (action == 'add' ? 1 : -1);
action = (action == 'add') ? 'removeFromCloset' : 'addToCloset';
$(`a[tid=${tid}]`).attr('href', `javascript:${action}(${tid});`).attr('title', trans(`skinlib.${action}`)).toggleClass('liked');
$(`#${tid}`).attr('href', `javascript:${action}(${tid});`).html(trans(`skinlib.${action}`));
$('#likes').html(likes);
}
$(document).on('click', '.private-label', function () {
swal({
text: trans('skinlib.setPublicNotice'),
type: 'warning',
showCancelButton: true
}).then(() => {
changePrivacy($(this).attr('tid'));
$(this).remove();
});
});
function changePrivacy(tid) {
fetch({
type: 'POST',
url: url('skinlib/privacy'),
dataType: 'json',
data: { tid: tid }
}).then(result => {
let { errno, msg } = result;
if (errno == 0) {
toastr.success(msg);
if (result.public == '0') {
$(`a:contains("${trans('skinlib.setAsPrivate')}")`).html(trans('skinlib.setAsPublic'));
} else {
$(`a:contains("${trans('skinlib.setAsPublic')}")`).html(trans('skinlib.setAsPrivate'));
}
} else {
toastr.warning(msg);
}
}).catch(err => showAjaxError(err));
}
function deleteTexture(tid) {
swal({
text: trans('skinlib.deleteNotice'),
type: 'warning',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('skinlib/delete'),
dataType: 'json',
data: { tid: tid }
})).then(({ errno, msg }) => {
if (errno == 0) {
swal({ type: 'success', html: msg }).then(() => {
window.location = url('skinlib');
});
} else {
swal({ type: 'warning', html: msg });
}
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,130 @@
/* global MSP */
/* exported upload */
'use strict';
$('#private').on('ifToggled', function () {
$(this).prop('checked') ? $('#msg').show() : $('#msg').hide();
});
$('#type-skin').on('ifToggled', function () {
$(this).prop('checked') ? $('#skin-type').show() : $('#skin-type').hide();
});
$('body').on('change', '#file', () => handleFiles()).on('ifToggled', '#type-cape', () => {
MSP.clear();
handleFiles();
});
// Real-time preview
function handleFiles(files, type) {
files = files || $('#file').prop('files');
type = type || $('#type-cape').prop('checked') ? 'cape' : 'skin';
if (files.length > 0) {
let file = files[0];
if (file.type === 'image/png' || file.type === 'image/x-png') {
let reader = new FileReader();
reader.onload = function () {
let img = new Image();
img.onload = () => {
let $name = $('#name');
(type == 'skin') ? MSP.changeSkin(img.src) : MSP.changeCape(img.src);
if ($name.val() === '' || $name.val() === $name.attr('data-last-file-name')) {
// Remove png extension in filename
const fileName = file.name.replace(/\.[Pp][Nn][Gg]$/, '');
$name.attr('data-last-file-name', fileName);
$name.val(fileName);
}
};
img.onerror = () => toastr.warning(trans('skinlib.fileExtError'));
img.src = this.result;
};
reader.readAsDataURL(file);
} else {
toastr.warning(trans('skinlib.encodingError'));
}
}
}
function upload() {
let form = new FormData();
let file = $('#file').prop('files')[0];
form.append('name', $('#name').val());
form.append('file', file);
form.append('public', ! $('#private').prop('checked'));
if ($('#type-skin').prop('checked')) {
form.append('type', $('#skin-type').val());
} else if ($('#type-cape').prop('checked')) {
form.append('type', 'cape');
} else {
return toastr.info(trans('skinlib.emptyTextureType'));
}
(function validate(form, file, callback) {
if (file === undefined) {
toastr.info(trans('skinlib.emptyUploadFile'));
$('#file').focus();
} else if ($('#name').val() == '') {
toastr.info(trans('skinlib.emptyTextureName'));
$('#name').focus();
} else if (file.type !== 'image/png') {
toastr.warning(trans('skinlib.fileExtError'));
$('#file').focus();
} else {
callback();
}
})(form, file, () => {
fetch({
type: 'POST',
url: url('skinlib/upload'),
contentType: false,
dataType: 'json',
data: form,
processData: false,
beforeSend: () => {
$('#upload-button').html(
'<i class="fa fa-spinner fa-spin"></i> ' + trans('skinlib.uploading')
).prop('disabled', 'disabled');
}
}).then(({ errno, msg, tid }) => {
if (errno == 0) {
let redirect = function () {
toastr.info(trans('skinlib.redirecting'));
window.setTimeout(() => {
window.location = url(`skinlib/show/${tid}`);
}, 1000);
};
// Always redirect
swal({
type: 'success',
html: msg
}).then(redirect, redirect);
} else {
swal({
type: 'warning',
html: msg
}).then(() => {
$('#upload-button').html(trans('skinlib.upload')).prop('disabled', '');
});
}
}).catch(err => {
showAjaxError(err);
$('#upload-button').html(trans('skinlib.upload')).prop('disabled', '');
});
});
}

View File

@ -1,685 +0,0 @@
'use strict';
$('body').on('click', '.player', function () {
$('.player-selected').removeClass('player-selected');
$(this).addClass('player-selected');
showPlayerTexturePreview(this.id);
});
$('body').on('click', '#preview-switch', () => {
TexturePreview.previewType == '3D' ? TexturePreview.show2dPreview() : TexturePreview.show3dPreview();
});
let selectedTextures = [];
$('body').on('click', '.item-body', function () {
$('.item-selected').parent().removeClass('item-selected');
$(this).parent().addClass('item-selected');
const tid = parseInt($(this).parent().attr('tid'));
$.ajax({
type: "POST",
url: "../skinlib/info/" + tid,
dataType: "json",
success: (json) => {
if (json.type == "cape") {
MSP.changeCape('../textures/' + json.hash);
selectedTextures['cape'] = tid;
} else {
MSP.changeSkin('../textures/' + json.hash);
selectedTextures['skin'] = tid;
}
if (selectedTextures['skin'] !== undefined && selectedTextures['cape'] !== undefined)
$('#textures-indicator').text(`${trans('general.skin')} & ${trans('general.cape')}`);
else if (selectedTextures['skin'] != undefined)
$('#textures-indicator').text(trans('general.skin'));
else if (selectedTextures['cape'] != undefined)
$('#textures-indicator').text(trans('general.cape'));
},
error: showAjaxError
});
});
$('body').on('click', '.category-switch', () => {
const category = $('a[href="#skin-category"]').parent().hasClass('active') ? 'cape' : 'skin';
const page = parseInt($('#closet-paginator').attr(`last-${category}-page`));
const search = $('input[name=q]').val();
reloadCloset(category, page, search);
});
function renderClosetItemComponent(item) {
return `
<div class="item" tid="${item.tid}">
<div class="item-body">
<img src="${url('/')}preview/${item.tid}.png">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${item.name}">${item.name} <small>(${item.type})</small></span>
</p>
<a href="${url('/')}skinlib/show/${item.tid}" title="${trans('user.viewInSkinlib')}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
<span title="${trans('general.more')}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
<li><a href="javascript:renameClosetItem(${item.tid}, '${item.name}');">${trans('user.renameItem')}</a></li>
<li><a href="javascript:removeFromCloset(${item.tid});">${trans('user.removeItem')}</a></li>
<li><a href="javascript:setAsAvatar(${item.tid});">${trans('user.setAsAvatar')}</a></li>
</ul>
</div>
</div>`;
}
function renderCloset(items, category) {
const search = $('input[name=q]').val();
let container = $(`#${category}-category`);
container.html('');
if (items.length === 0) {
$('#closet-paginator').hide();
if (search === '') {
container.html(`<div class="empty-msg">
${trans('user.emptyClosetMsg', { url: url('skinlib?filter=' + category) })}</div>`);
} else {
container.html(`<div class="empty-msg">${trans('general.noResult')}</div>`);
}
} else {
$('#closet-paginator').show();
for (const item of items) {
container.append(renderClosetItemComponent(item));
}
}
}
function reloadCloset(category, page, search) {
Promise.resolve($.ajax({
type: 'GET',
url: url('user/closet-data'),
dataType: 'json',
data: {
category: category,
page: page,
q: search
}
})).then(result => {
renderCloset(result.items, result.category);
let paginator = $('#closet-paginator');
paginator.attr(`last-${result.category}-page`, page);
paginator.jqPaginator('option', {
currentPage: page,
totalPages: result.total_pages
});
}).catch(error => showAjaxError);
}
function showPlayerTexturePreview(pid) {
$.ajax({
type: "POST",
url: url('user/player/show'),
dataType: "json",
data: { "pid": pid },
success: (json) => {
['steve', 'alex', 'cape'].forEach((type) => {
let tid = json[`tid_${type}`];
let preview = new TexturePreview(type, tid, json.preference);
if (tid) {
preview.change2dPreview().change3dPreview();
} else {
preview.showNotUploaded();
}
});
if ((json.preference == "default" && !json.tid_steve) || (json.preference == "slim" && !json.tid_alex)) {
// show default skin
MSP.changeSkin(defaultSkin);
}
console.log(`Texture previews of player ${json.player_name} rendered.`);
},
error: showAjaxError
});
}
function renameClosetItem(tid, oldName) {
swal({
title: trans('user.renameClosetItem'),
input: 'text',
inputValue: oldName,
showCancelButton: true,
inputValidator: function(value) {
return new Promise(function(resolve, reject) {
if (value) {
resolve();
} else {
reject(trans('skinlib.emptyNewTextureName'));
}
});
}
}).then(function(new_name) {
$.ajax({
type: "POST",
url: "./closet/rename",
dataType: "json",
data: { 'tid': tid, 'new_name': new_name },
success: function(json) {
if (json.errno == 0) {
$("[tid="+tid+"]>.item-footer>.texture-name>span").html(new_name);
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
function removeFromCloset(tid) {
swal({
text: trans('user.removeFromClosetNotice'),
type: 'warning',
showCancelButton: true
}).then(function() {
$.ajax({
type: "POST",
url: "./closet/remove",
dataType: "json",
data: { 'tid' : tid },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
$('div[tid='+tid+']').remove();
['skin', 'cape'].forEach(type => {
let container = $(`#${type}-category`);
if ($.trim(container.html()) == '') {
container.html(`<div class="empty-msg">
${trans('user.emptyClosetMsg', { url: url('skinlib?filter=' + type) })}</div>`);
}
})
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
function setAsAvatar(tid) {
swal({
title: trans('user.setAvatar'),
text: trans('user.setAvatarNotice'),
type: 'question',
showCancelButton: true
}).then(function() {
$.ajax({
type: "POST",
url: "./profile/avatar",
dataType: "json",
data: { "tid": tid },
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
// refersh avatars
$('[alt="User Image"]').each(function() {
$(this).prop('src', $(this).attr('src') + '?' + new Date().getTime());
})
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
}
$(document).ready(function() {
$('input[type=radio]').iCheck({
radioClass: 'iradio_square-blue'
});
swal.setDefaults({
confirmButtonText: trans('general.confirm'),
cancelButtonText: trans('general.cancel')
});
if (window.location.pathname.includes('/user/closet')) {
Promise.resolve($.ajax({
type: 'GET',
url: url('/user/closet-data'),
dataType: 'json'
})).then(result => {
renderCloset(result.items, result.category);
$('#closet-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
totalPages: result.total_pages,
onPageChange: page => {
reloadCloset(
$('#skin-category').hasClass('active') ? 'skin' : 'cape',
page,
$('input[name=q]').val()
);
}
}));
}).catch(error => showAjaxError);
$('input[name=q]').on('input', debounce(() => {
const category = $('#skin-category').hasClass('active') ? 'skin' : 'cape';
reloadCloset(
category,
1,
$('input[name=q]').val()
);
}, 350));
}
});
function setTexture() {
var pid = 0;
$('input[name="player"]').each(function(){
if (this.checked) pid = this.id;
});
if (!pid) {
toastr.info(trans('user.emptySelectedPlayer'));
} else if (selectedTextures['skin'] == undefined && selectedTextures['cape'] == undefined) {
toastr.info(trans('user.emptySelectedTexture'));
} else {
$.ajax({
type: "POST",
url: url('user/player/set'),
dataType: "json",
data: {
'pid': pid,
'tid[skin]': selectedTextures['skin'],
'tid[cape]': selectedTextures['cape']
},
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
$('#modal-use-as').modal('hide');
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
}
$('body').on('change', '#preference', function() {
$.ajax({
type: "POST",
url: url('user/player/preference'),
dataType: "json",
data: { 'pid' : $(this).attr('pid'), 'preference' : $(this).val() },
success: function(json) {
if (json.errno == 0) {
toastr.success(json.msg);
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
});
function changePlayerName(pid, current_player_name) {
swal({
title: trans('user.changePlayerName'),
text: $('#player_name').attr('placeholder'),
inputValue: current_player_name,
input: 'text',
showCancelButton: true,
inputValidator: function(value) {
return new Promise(function(resolve, reject) {
if (value) {
resolve();
} else {
reject(trans('user.emptyPlayerName'));
}
});
}
}).then(function(new_player_name) {
$.ajax({
type: "POST",
url: url('user/player/rename'),
dataType: "json",
data: { 'pid' : pid, 'new_player_name' : new_player_name },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
$('td:contains("'+pid+'")').next().html(new_player_name);
} else {
swal({
type: 'error',
html: json.msg
});
}
},
error: showAjaxError
});
});
}
function clearTexture(pid) {
let dom = `
<div class="form-group">
<input type="checkbox" id="clear-steve"> Default (Steve)
</div>
<div class="form-group">
<input type="checkbox" id="clear-alex"> Slim (Alex)
</div>
<div class="form-group">
<input type="checkbox" id="clear-cape"> ${trans('general.cape')}
</div>
<script>
$('input[type=checkbox]').iCheck({ checkboxClass: 'icheckbox_square-blue' });
</script>
`;
showModal(dom, trans('user.chooseClearTexture'), 'default', { callback: `ajaxClearTexture(${pid})` });
return;
}
function ajaxClearTexture(pid) {
$('.modal').each(function () {
if ($(this).css('display') == "none")
$(this).remove();
});
let data = { pid: pid };
['steve', 'alex', 'cape'].forEach(type => {
data[type] = $(`#clear-${type}`).prop('checked') ? 1 : 0;
});
if (data['steve'] == 0 && data['alex'] == 0 && data['cape'] == 0) {
toastr.warning(trans('user.noClearChoice'));
return;
}
Promise.resolve($.ajax({
type: 'POST',
url: url('user/player/texture/clear'),
dataType: 'json',
data: data
})).then(json => {
swal({ type: json.errno == 0 ? 'success' : 'error', html: json.msg });
$('.modal').modal('hide');
}).catch(error => showAjaxError);
}
function deletePlayer(pid) {
swal({
title: trans('user.deletePlayer'),
text: trans('user.deletePlayerNotice'),
type: 'warning',
showCancelButton: true,
cancelButtonColor: '#3085d6',
confirmButtonColor: '#d33'
}).then(function() {
$.ajax({
type: "POST",
url: url('user/player/delete'),
dataType: "json",
data: { 'pid' : pid },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
}).then(function() {
$('tr#'+pid).remove();
});
} else {
swal({
type: 'error',
html: json.msg
});
}
},
error: showAjaxError
});
});
}
function addNewPlayer() {
$.ajax({
type: "POST",
url: url('user/player/add'),
dataType: "json",
data: { 'player_name' : $('#player_name').val() },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
}).then(function() {
location.reload();
});
$('#modal-add-player').modal('hide');
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}
function changeNickName() {
var new_nickname = $('#new-nickname').val();
if (!new_nickname) {
swal({
type: 'error',
html: trans('user.emptyNewNickName')
});
return;
}
swal({
text: trans('user.changeNickName', { new_nickname: new_nickname }),
type: 'question',
showCancelButton: true
}).then(function() {
$.ajax({
type: "POST",
url: "./profile?action=nickname",
dataType: "json",
data: { 'new_nickname' : new_nickname },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
});
$('.nickname').each(function() {
$(this).html(new_nickname);
});
} else {
swal({
type: 'warning',
html: json.msg
});
}
},
error: showAjaxError
});
});
}
function changePassword() {
let domOldPwd = $('#password');
let domNewPwd = $('#new-passwd');
let domConfirmPwd = $('#confirm-pwd');
let password = domOldPwd.val();
let new_passwd = domNewPwd.val();
if (password == "") {
toastr.info(trans('user.emptyPassword'));
domOldPwd.focus();
} else if (new_passwd == "") {
toastr.info(trans('user.emptyNewPassword'));
domNewPwd.focus();
} else if (domConfirmPwd.val() == "") {
toastr.info(trans('auth.emptyConfirmPwd'));
domConfirmPwd.focus();
} else if (new_passwd != domConfirmPwd.val()) {
toastr.warning(trans('auth.invalidConfirmPwd'));
domConfirmPwd.focus();
} else {
Promise.resolve($.ajax({
type: "POST",
url: "./profile?action=password",
dataType: "json",
data: { 'current_password': password, 'new_password': new_passwd }
})).then(result => {
if (result.errno == 0) {
return swal({ type: 'success', text: result.msg })
.then(() => {
return logout();
}).then(result => {
if (result.errno == 0) {
window.location = url('auth/login');
}
}).catch(error => showAjaxError);
} else {
return swal({ type: 'warning', text: result.msg });
}
}).catch(error => showAjaxError);
}
}
$('#new-email').focusin(function() {
$('#current-password').parent().show();
}).focusout(function() {
window.setTimeout(function() {
if (!$('#current-password').is(':focus'))
$('#current-password').parent().hide();
}, 10);
})
function changeEmail() {
var new_email = $('#new-email').val();
if (!new_email) {
swal({
type: 'error',
html: trans('user.emptyNewEmail')
});
return;
}
// check valid email address
if (!/\S+@\S+\.\S+/.test(new_email)) {
swal({
type: 'warning',
html: trans('auth.invalidEmail')
}); return;
}
swal({
text: trans('user.changeEmail', { new_email: new_email }),
type: 'question',
showCancelButton: true
}).then(() => {
return Promise.resolve($.ajax({
type: 'POST',
url: './profile?action=email',
dataType: 'json',
data: { 'new_email': new_email, 'password': $('#current-password').val() }
}));
}).then(result => {
if (result.errno == 0) {
return swal({ type: 'success', text: result.msg })
.then(() => {
return logout();
}).then(result => {
if (result.errno == 0) {
window.location = url('auth/login');
}
}).catch(error => showAjaxError);
} else {
return swal({ type: 'warning', text: result.msg });
}
}).catch(error => showAjaxError);
}
function deleteAccount() {
var password = $('.modal-body>#password').val();
if (!password) {
swal({
type: 'warning',
html: trans('user.emptyDeletePassword')
}); return;
}
$.ajax({
type: "POST",
url: "./profile?action=delete",
dataType: "json",
data: { 'password' : password },
success: function(json) {
if (json.errno == 0) {
swal({
type: 'success',
html: json.msg
}).then(function() {
window.location = "../auth/login";
});
} else {
swal({
type: 'warning',
html: json.msg
});
}
},
error: showAjaxError
});
}
function signIn() {
$.ajax({
type: "POST",
url: "./user/sign-in",
dataType: "json",
success: function(json) {
if (json.errno == 0) {
$('#score').html(json.score);
var dom = '<i class="fa fa-calendar-check-o"></i> &nbsp;' + trans('user.signInRemainingTime', { time: String(json.remaining_time) });
$('#sign-in-button').attr('disabled', 'disabled').html(dom);
if (json.storage.used > 1024) {
$('#user-storage').html(`<b>${Math.round(json.storage.used)}</b>/ ${Math.round(json.storage.total)} MB`);
} else {
$('#user-storage').html(`<b>${Math.round(json.storage.used)}</b>/ ${Math.round(json.storage.total)} KB`);
}
$('#user-storage-bar').css('width', `${json.storage.percentage}%`)
swal({
type: 'success',
html: json.msg
});
} else {
toastr.warning(json.msg);
}
},
error: showAjaxError
});
}

View File

@ -0,0 +1,240 @@
/* global MSP */
/* exported renameClosetItem, removeFromCloset, setAsAvatar */
'use strict';
var selectedTextures = [];
$(document).ready(function () {
if (! window.location.pathname.includes('/user/closet'))
return;
fetch({
type: 'GET',
url: url('/user/closet-data'),
dataType: 'json'
}).then(result => {
renderCloset(result.items, result.category);
$('#closet-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
totalPages: result.total_pages,
onPageChange: page => reloadCloset(
$('#skin-category').hasClass('active') ? 'skin' : 'cape',
page, $('input[name=q]').val()
)
}));
}).catch(err => showAjaxError(err));
$('input[name=q]').on('input', debounce(() => {
let category = $('#skin-category').hasClass('active') ? 'skin' : 'cape';
reloadCloset(category, 1, $('input[name=q]').val());
}, 350));
});
$('body').on('click', '.item-body', function () {
$('.item-selected').parent().removeClass('item-selected');
let $item = $(this).parent();
$item.addClass('item-selected');
let tid = parseInt($item.attr('tid'));
fetch({
type: 'POST',
url: url(`skinlib/info/${tid}`),
dataType: 'json'
}).then(result => {
if (result.type == 'cape') {
MSP.changeCape(url(`textures/${result.hash}`));
selectedTextures['cape'] = tid;
} else {
MSP.changeSkin(url(`textures/${result.hash}`));
selectedTextures['skin'] = tid;
}
let skin = selectedTextures['skin'],
cape = selectedTextures['cape'];
let $indicator = $('#textures-indicator');
if (skin !== undefined && cape !== undefined) {
$indicator.text(`${trans('general.skin')} & ${trans('general.cape')}`);
} else if (skin != undefined) {
$indicator.text(trans('general.skin'));
} else if (cape != undefined) {
$indicator.text(trans('general.cape'));
}
}).catch(err => showAjaxError(err));
});
$('body').on('click', '.category-switch', () => {
let category = $('a[href="#skin-category"]').parent().hasClass('active') ? 'cape' : 'skin';
let search = $('input[name=q]').val();
let page = parseInt($('#closet-paginator').attr(`last-${category}-page`));
reloadCloset(category, page, search);
});
function renderClosetItemComponent(item) {
return `
<div class="item" tid="${item.tid}">
<div class="item-body">
<img src="${url('/')}preview/${item.tid}.png">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${item.name}">${item.name} <small>(${item.type})</small></span>
</p>
<a href="${url('/')}skinlib/show/${item.tid}" title="${trans('user.viewInSkinlib')}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
<span title="${trans('general.more')}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
<li><a href="javascript:renameClosetItem(${item.tid}, '${item.name}');">${trans('user.renameItem')}</a></li>
<li><a href="javascript:removeFromCloset(${item.tid});">${trans('user.removeItem')}</a></li>
<li><a href="javascript:setAsAvatar(${item.tid});">${trans('user.setAsAvatar')}</a></li>
</ul>
</div>
</div>`;
}
/**
* Render closet with giving items & category.
*
* @param {array} items
* @param {string} category
*/
function renderCloset(items, category) {
let search = $('input[name=q]').val();
let container = $(`#${category}-category`).html('');
if (items.length === 0) {
$('#closet-paginator').hide();
if (search === '') {
container.html('<div class="empty-msg">' +
trans('user.emptyClosetMsg', { url: url(`skinlib?filter=${category}`) }) +
'</div>');
} else {
container.html(`<div class="empty-msg">${trans('general.noResult')}</div>`);
}
} else {
$('#closet-paginator').show();
for (let item of items) {
container.append(renderClosetItemComponent(item));
}
}
}
/**
* Reload and render closet.
*
* @param {string} category
* @param {integer} page
* @param {string} search
*/
function reloadCloset(category, page, search) {
fetch({
type: 'GET',
url: url('user/closet-data'),
dataType: 'json',
data: {
category: category,
page: page,
q: search
}
}).then(result => {
renderCloset(result.items, result.category);
let paginator = $('#closet-paginator');
paginator.attr(`last-${result.category}-page`, page);
paginator.jqPaginator('option', {
currentPage: page,
totalPages: result.total_pages
});
}).catch(err => showAjaxError(err));
}
function renameClosetItem(tid, oldName) {
let newTextureName = '';
swal({
title: trans('user.renameClosetItem'),
input: 'text',
inputValue: oldName,
showCancelButton: true,
inputValidator: value => (new Promise((resolve, reject) => {
(newTextureName = value) ? resolve() : reject(trans('skinlib.emptyNewTextureName'));
}))
}).then(name => fetch({
type: 'POST',
url: url('closet/rename'),
dataType: 'json',
data: { tid: tid, new_name: name }
})).then(result => {
if (result.errno == 0) {
$(`[tid=${tid}]>.item-footer>.texture-name>span`).html(newTextureName);
toastr.success(result.msg);
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}
function removeFromCloset(tid) {
swal({
text: trans('user.removeFromClosetNotice'),
type: 'warning',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('closet/remove'),
dataType: 'json',
data: { tid: tid }
})).then(result => {
if (result.errno == 0) {
swal({ type: 'success', html: result.msg });
$(`div[tid=${tid}]`).remove();
['skin', 'cape'].forEach(type => {
let container = $(`#${type}-category`);
if ($.trim(container.html()) == '') {
let msg = trans('user.emptyClosetMsg', { url: url(`skinlib?filter=${type}`) });
container.html(`<div class="empty-msg">${msg}</div>`);
}
});
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}
function setAsAvatar(tid) {
swal({
title: trans('user.setAvatar'),
text: trans('user.setAvatarNotice'),
type: 'question',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('profile/avatar'),
dataType: 'json',
data: { tid: tid }
})).then(result => {
if (result.errno == 0) {
toastr.success(result.msg);
// Refersh avatars
$('[alt="User Image"]').each(function () {
$(this).prop('src', $(this).attr('src') + '?' + new Date().getTime());
});
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,213 @@
/* global MSP, defaultSkin, selectedTextures */
/* exported changePlayerName, clearTexture, ajaxClearTexture, addNewPlayer, deletePlayer, setTexture */
'use strict';
$('body').on('click', '.player', function () {
$('.player-selected').removeClass('player-selected');
$(this).addClass('player-selected');
showPlayerTexturePreview(this.id);
});
$('body').on('click', '#preview-switch', () => {
TexturePreview.previewType == '3D' ? TexturePreview.show2dPreview() : TexturePreview.show3dPreview();
});
function showPlayerTexturePreview(pid) {
fetch({
type: 'POST',
url: url('user/player/show'),
dataType: 'json',
data: { pid: pid }
}).then(result => {
// Render skin preview of selected player
['steve', 'alex', 'cape'].forEach((type) => {
let tid = result[`tid_${type}`];
let preview = new TexturePreview(type, tid, result.preference);
if (tid) {
preview.change2dPreview().change3dPreview();
} else {
preview.showNotUploaded();
}
});
if ((result.preference == 'default' && !result.tid_steve) ||
(result.preference == 'slim' && !result.tid_alex))
{
// show default skin
MSP.changeSkin(defaultSkin);
}
console.log(`Texture previews of player ${result.player_name} rendered.`);
}).catch(err => showAjaxError(err));
}
$('body').on('change', '#preference', function () {
fetch({
type: 'POST',
url: url('user/player/preference'),
dataType: 'json',
data: { pid: $(this).attr('pid'), preference: $(this).val() }
}).then(result => {
if (result.errno == 0) {
toastr.success(result.msg);
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
});
function changePlayerName(pid, currentPlayerName) {
let newPlayerName = '';
swal({
title: trans('user.changePlayerName'),
text: $('#player_name').attr('placeholder'),
inputValue: currentPlayerName,
input: 'text',
showCancelButton: true,
inputValidator: value => (new Promise((resolve, reject) => {
(newPlayerName = value) ? resolve() : reject(trans('skinlib.emptyPlayerName'));
}))
}).then(name => fetch({
type: 'POST',
url: url('user/player/rename'),
dataType: 'json',
data: { pid: pid, new_player_name: name }
})).then(result => {
if (result.errno == 0) {
swal({ type: 'success', html: result.msg });
$(`td:contains("${pid}")`).next().html(newPlayerName);
} else {
swal({ type: 'error', html: result.msg });
}
}).catch(err => showAjaxError(err));
}
function clearTexture(pid) {
let dom = `<div class="form-group">
<input type="checkbox" id="clear-steve"> Default (Steve)
</div>
<div class="form-group">
<input type="checkbox" id="clear-alex"> Slim (Alex)
</div>
<div class="form-group">
<input type="checkbox" id="clear-cape"> ${trans('general.cape')}
</div>
<script>
$('input[type=checkbox]').iCheck({ checkboxClass: 'icheckbox_square-blue' });
</script>`;
return showModal(dom, trans('user.chooseClearTexture'), 'default', {
callback: `ajaxClearTexture(${pid})`
});
}
function ajaxClearTexture(pid) {
$('.modal').each(function () {
if ($(this).css('display') == 'none') $(this).remove();
});
let data = { pid: pid };
['steve', 'alex', 'cape'].forEach(type => {
data[type] = $(`#clear-${type}`).prop('checked') ? 1 : 0;
});
if (data['steve'] == 0 && data['alex'] == 0 && data['cape'] == 0) {
return toastr.warning(trans('user.noClearChoice'));
}
fetch({
type: 'POST',
url: url('user/player/texture/clear'),
dataType: 'json',
data: data
}).then(result => {
swal({ type: result.errno == 0 ? 'success' : 'error', html: result.msg });
$('.modal').modal('hide');
}).catch(err => showAjaxError(err));
}
function deletePlayer(pid) {
swal({
title: trans('user.deletePlayer'),
text: trans('user.deletePlayerNotice'),
type: 'warning',
showCancelButton: true,
cancelButtonColor: '#3085d6',
confirmButtonColor: '#d33'
}).then(() => fetch({
type: 'POST',
url: url('user/player/delete'),
dataType: 'json',
data: { pid: pid }
})).then(result => {
if (result.errno == 0) {
swal({
type: 'success',
html: result.msg
}).then(() => $(`tr#${pid}`).remove());
} else {
swal({ type: 'error', html: result.msg });
}
}).catch(err => showAjaxError(err));
}
function addNewPlayer() {
fetch({
type: 'POST',
url: url('user/player/add'),
dataType: 'json',
data: { player_name: $('#player_name').val() }
}).then(result => {
if (result.errno == 0) {
swal({
type: 'success',
html: result.msg
}).then(() => location.reload());
$('#modal-add-player').modal('hide');
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}
function setTexture() {
let pid = 0,
skin = selectedTextures['skin'],
cape = selectedTextures['cape'];
$('input[name="player"]').each(function(){
if (this.checked) pid = this.id;
});
if (! pid) {
toastr.info(trans('user.emptySelectedPlayer'));
} else if (skin == undefined && cape == undefined) {
toastr.info(trans('user.emptySelectedTexture'));
} else {
fetch({
type: 'POST',
url: url('user/player/set'),
dataType: 'json',
data: {
'pid': pid,
'tid[skin]': skin,
'tid[cape]': cape
}
}).then(result => {
if (result.errno == 0) {
swal({ type: 'success', html: result.msg });
$('#modal-use-as').modal('hide');
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}
}

View File

@ -0,0 +1,153 @@
/* exported changeNickName, changePassword, changeEmail, deleteAccount, deleteAccount */
'use strict';
function changeNickName() {
let name = $('#new-nickname').val();
if (! name) {
return swal({ type: 'error', html: trans('user.emptyNewNickName') });
}
swal({
text: trans('user.changeNickName', { new_nickname: name }),
type: 'question',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('user/profile?action=nickname'),
dataType: 'json',
data: { new_nickname: name }
})).then(result => {
if (result.errno == 0) {
$('.nickname').each(function () {
$(this).html(name);
});
return swal({ type: 'success', html: result.msg });
} else {
return swal({ type: 'warning', html: result.msg });
}
}).catch(err => showAjaxError(err));
}
function changePassword() {
let $oldPasswd = $('#password'),
$newPasswd = $('#new-passwd'),
$confirmPwd = $('#confirm-pwd');
let password = $oldPasswd.val(),
newPasswd = $newPasswd.val();
if (password == '') {
toastr.info(trans('user.emptyPassword'));
$oldPasswd.focus();
} else if (newPasswd == '') {
toastr.info(trans('user.emptyNewPassword'));
$newPasswd.focus();
} else if ($confirmPwd.val() == '') {
toastr.info(trans('auth.emptyConfirmPwd'));
$confirmPwd.focus();
} else if (newPasswd != $confirmPwd.val()) {
toastr.warning(trans('auth.invalidConfirmPwd'));
$confirmPwd.focus();
} else {
fetch({
type: 'POST',
url: url('user/profile?action=password'),
dataType: 'json',
data: { 'current_password': password, 'new_password': newPasswd }
}).then(result => {
if (result.errno == 0) {
return swal({
type: 'success',
text: result.msg }
).then(() => {
return logout();
}).then(result => {
if (result.errno == 0) {
window.location = url('auth/login');
}
}).catch(err => showAjaxError(err));
} else {
return swal({ type: 'warning', text: result.msg });
}
}).catch(err => showAjaxError(err));
}
}
$('#new-email').focusin(() => {
$('#current-password').parent().show();
}).focusout(debounce(() => {
let dom = $('#current-password');
if (! dom.is(':focus')) {
dom.parent().hide();
}
}, 10));
function changeEmail() {
var newEmail = $('#new-email').val();
if (! newEmail) {
return swal({ type: 'error', html: trans('user.emptyNewEmail') });
}
// check valid email address
if (!/\S+@\S+\.\S+/.test(newEmail)) {
return swal({ type: 'warning', html: trans('auth.invalidEmail') });
}
swal({
text: trans('user.changeEmail', { new_email: newEmail }),
type: 'question',
showCancelButton: true
}).then(() => fetch({
type: 'POST',
url: url('user/profile?action=email'),
dataType: 'json',
data: { new_email: newEmail, password: $('#current-password').val() }
})).then(result => {
if (result.errno == 0) {
return swal({
type: 'success',
text: result.msg
}).then(() => {
return logout();
}).then(result => {
if (result.errno == 0) {
window.location = url('auth/login');
}
}).catch(err => showAjaxError(err));
} else {
return swal({ type: 'warning', text: result.msg });
}
}).catch(err => showAjaxError(err));
}
function deleteAccount() {
let password = $('.modal-body>#password').val();
if (! password) {
return swal({ type: 'warning', html: trans('user.emptyDeletePassword') });
}
fetch({
type: 'POST',
url: url('user/profile?action=delete'),
dataType: 'json',
data: { password: password }
}).then(result => {
if (result.errno == 0) {
return swal({
type: 'success',
html: result.msg
}).then(() => {
window.location = url('auth/login');
});
} else {
return swal({ type: 'warning', html: result.msg });
}
}).catch(err => showAjaxError(err));
}

View File

@ -0,0 +1,27 @@
/* exported signIn */
function signIn() {
fetch({
type: 'POST',
url: url('user/sign-in'),
dataType: 'json'
}).then(result => {
if (result.errno == 0) {
$('#score').html(result.score);
var dom = '<i class="fa fa-calendar-check-o"></i> &nbsp;' + trans('user.signInRemainingTime', { time: String(result.remaining_time) });
$('#sign-in-button').attr('disabled', 'disabled').html(dom);
if (result.storage.used > 1024) {
$('#user-storage').html(`<b>${Math.round(result.storage.used)}</b>/ ${Math.round(result.storage.total)} MB`);
} else {
$('#user-storage').html(`<b>${Math.round(result.storage.used)}</b>/ ${Math.round(result.storage.total)} KB`);
}
$('#user-storage-bar').css('width', `${result.storage.percentage}%`);
return swal({ type: 'success', html: result.msg });
} else {
toastr.warning(result.msg);
}
}).catch(err => showAjaxError(err));
}