Clean up
This commit is contained in:
parent
ce11b56444
commit
7103d52a6e
|
|
@ -1,4 +0,0 @@
|
|||
# http://editorconfig.org
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,244 +0,0 @@
|
|||
/* eslint no-unused-vars: "off" */
|
||||
|
||||
const $ = require('jquery');
|
||||
window.jQuery = window.$ = $;
|
||||
|
||||
describe('tests for "i18n" module', () => {
|
||||
const modulePath = '../common/i18n';
|
||||
|
||||
it('load locales', () => {
|
||||
window.isEmpty = obj => !obj; // Just for test
|
||||
const loadLocales = require(modulePath).loadLocales;
|
||||
|
||||
$.locales = {
|
||||
en: { text: 'text', nested: { sth: ':sth here!' } }
|
||||
};
|
||||
|
||||
loadLocales();
|
||||
expect($.currentLocale).toEqual({ text: 'text', nested: { sth: ':sth here!' } });
|
||||
});
|
||||
|
||||
it('get translated text', () => {
|
||||
const trans = require(modulePath).trans;
|
||||
|
||||
$.currentLocale = undefined; // Should load locale automatically
|
||||
$.locales = {
|
||||
en: { text: 'text', nested: { sth: ':sth here!' } }
|
||||
};
|
||||
|
||||
expect(trans('text')).toBe('text');
|
||||
expect(trans('text.nothing')).toBe('text.nothing');
|
||||
expect(trans('nested.sth')).toBe(':sth here!');
|
||||
expect(trans('nested.sth', { sth: 'abc' })).toBe('abc here!');
|
||||
});
|
||||
});
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('tests for "logout" module', () => {
|
||||
const modulePath = '../common/logout';
|
||||
|
||||
it('logout', async () => {
|
||||
const swal = jest.fn()
|
||||
.mockReturnValueOnce(Promise.reject())
|
||||
.mockReturnValueOnce(Promise.resolve());
|
||||
const trans = jest.fn(key => key);
|
||||
const fetch = jest.fn()
|
||||
.mockReturnValueOnce(Promise.resolve({ msg: 'success' }))
|
||||
.mockReturnValueOnce(Promise.reject());
|
||||
const showAjaxError = jest.fn();
|
||||
window.swal = swal;
|
||||
window.trans = trans;
|
||||
window.fetch = fetch;
|
||||
window.url = jest.fn(path => path);
|
||||
window.showAjaxError = showAjaxError;
|
||||
|
||||
document.body.innerHTML = '<button id="logout-button"></button>';
|
||||
require(modulePath);
|
||||
|
||||
await $('button').click();
|
||||
expect(fetch).not.toBeCalled();
|
||||
|
||||
await $('button').click();
|
||||
expect(swal).toBeCalledWith({
|
||||
text: 'general.confirmLogout',
|
||||
type: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'general.confirm',
|
||||
cancelButtonText: 'general.cancel'
|
||||
});
|
||||
expect(fetch).toBeCalledWith({
|
||||
type: 'POST',
|
||||
url: 'auth/logout',
|
||||
dataType: 'json'
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(url).toBeCalled();
|
||||
|
||||
await $('button').click();
|
||||
expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
|
||||
|
||||
await $('button').click();
|
||||
expect(showAjaxError).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
describe('tests for "notify" module', () => {
|
||||
const modulePath = '../common/notify';
|
||||
|
||||
it('show message', () => {
|
||||
document.body.innerHTML = '<div id="msg" class="a-class"></div>';
|
||||
|
||||
const showMsg = require(modulePath).showMsg;
|
||||
|
||||
showMsg('msg1');
|
||||
expect($('div').hasClass('a-class')).toBe(false);
|
||||
expect($('div').hasClass('callout')).toBe(true);
|
||||
expect($('div').hasClass('callout-info')).toBe(true);
|
||||
expect($('div').html()).toBe('msg1');
|
||||
|
||||
showMsg('msg2', 'warning');
|
||||
expect($('div').hasClass('callout-info')).toBe(false);
|
||||
expect($('div').hasClass('callout')).toBe(true);
|
||||
expect($('div').hasClass('callout-warning')).toBe(true);
|
||||
expect($('div').html()).toBe('msg2');
|
||||
});
|
||||
|
||||
it('show ajax error', () => {
|
||||
window.trans = jest.fn(key => key);
|
||||
$.fn.modal = jest.fn();
|
||||
|
||||
const showAjaxError = require(modulePath).showAjaxError;
|
||||
|
||||
showAjaxError({ responseText: 'error' });
|
||||
expect(window.trans).toBeCalledWith('general.fatalError');
|
||||
});
|
||||
|
||||
it('show modal dialog', () => {
|
||||
const modal = jest.fn();
|
||||
$.fn.modal = modal;
|
||||
|
||||
const showModal = require(modulePath).showModal;
|
||||
showModal('');
|
||||
expect(modal).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tests for "polyfill" module', () => {
|
||||
const modulePath = '../common/polyfill';
|
||||
|
||||
String.prototype.includes = undefined;
|
||||
String.prototype.endsWith = undefined;
|
||||
require(modulePath);
|
||||
|
||||
it('String#includes', () => {
|
||||
expect('blessing-skin'.includes('skin')).toBe(true);
|
||||
expect('blessing-skin'.includes('server')).toBe(false);
|
||||
expect('blessing-skin'.includes('skin', 9)).toBe(true);
|
||||
expect('blessing-skin'.includes('blessing', 9)).toBe(false);
|
||||
});
|
||||
|
||||
it('String#endsWith', () => {
|
||||
expect('blessing-skin'.endsWith('skin')).toBe(true);
|
||||
expect('blessing-skin'.endsWith('server')).toBe(false);
|
||||
expect('blessing-skin'.endsWith('blessing', 8)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tests for "utils" module', () => {
|
||||
const modulePath = '../common/utils';
|
||||
|
||||
window.blessing = {
|
||||
version: ''
|
||||
};
|
||||
|
||||
it('check a variable if it is empty', () => {
|
||||
const isEmpty = require(modulePath).isEmpty;
|
||||
|
||||
expect(isEmpty()).toBe(true);
|
||||
expect(isEmpty(null)).toBe(true);
|
||||
expect(isEmpty(undefined)).toBe(true);
|
||||
expect(isEmpty(0)).toBe(false);
|
||||
expect(isEmpty(false)).toBe(false);
|
||||
expect(isEmpty('')).toBe(true);
|
||||
expect(isEmpty({})).toBe(true);
|
||||
expect(isEmpty({ sth: '' })).toBe(false);
|
||||
expect(isEmpty([])).toBe(true);
|
||||
expect(isEmpty([1])).toBe(false);
|
||||
expect(isEmpty('something')).toBe(false);
|
||||
expect(isEmpty(Symbol())).toBe(true);
|
||||
});
|
||||
|
||||
it('fake fetch', () => {
|
||||
$.ajax = jest.fn();
|
||||
const fetch = require(modulePath).fetch;
|
||||
const xhr = fetch({ type: 'GET' });
|
||||
|
||||
expect($.ajax).toBeCalledWith({ type: 'GET' });
|
||||
expect(Object.getPrototypeOf(xhr)).toBe(Promise.prototype);
|
||||
});
|
||||
|
||||
it('make a debounced function', done => {
|
||||
const func = jest.fn();
|
||||
const debounce = require(modulePath).debounce;
|
||||
const debounced = debounce(func, 100);
|
||||
|
||||
debounced();
|
||||
debounced();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(func.mock.calls.length).toBe(1);
|
||||
done();
|
||||
}, 100);
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(() => debounce(func, 'string')).toThrow();
|
||||
expect(() => debounce('not a function', 100)).toThrow();
|
||||
});
|
||||
|
||||
it('get a absolute url', () => {
|
||||
window.blessing = { base_url: 'http://localhost' };
|
||||
const url = require(modulePath).url;
|
||||
|
||||
expect(url()).toBe('http://localhost/');
|
||||
expect(url('test')).toBe('http://localhost/test');
|
||||
expect(url('/test')).toBe('http://localhost/test');
|
||||
});
|
||||
|
||||
it('get argument from query string', () => {
|
||||
const { getQueryString } = require(modulePath);
|
||||
|
||||
Object.defineProperty(window.location, 'search', {
|
||||
value: '?key1=val1',
|
||||
writable: true
|
||||
});
|
||||
expect(getQueryString('key1')).toBe('val1');
|
||||
expect(getQueryString('key2')).toBeUndefined();
|
||||
expect(getQueryString('key2', 'val2')).toBe('val2');
|
||||
window.location.search = '?key1=val1&key2=val2';
|
||||
expect(getQueryString('key2', 'val2')).toBe('val2');
|
||||
});
|
||||
|
||||
it('test `isMobileBrowserScrolling` function', () => {
|
||||
const { isMobileBrowserScrolling } = require(modulePath);
|
||||
expect(isMobileBrowserScrolling()).toBe(false);
|
||||
|
||||
$.cachedWindowWidth = 50;
|
||||
$.cachedWindowHeight = 50;
|
||||
expect(isMobileBrowserScrolling()).toBe(false);
|
||||
|
||||
$.cachedWindowWidth = 60;
|
||||
$.cachedWindowHeight = 0;
|
||||
expect(isMobileBrowserScrolling()).toBe(false);
|
||||
|
||||
$.cachedWindowWidth = 0;
|
||||
$.cachedWindowHeight = 50;
|
||||
expect(isMobileBrowserScrolling()).toBe(true);
|
||||
expect(isMobileBrowserScrolling()).toBe(true); // For the second condition
|
||||
|
||||
$.lastWindowHeight = 60;
|
||||
expect(isMobileBrowserScrolling()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* global current_skin:true */
|
||||
|
||||
'use strict';
|
||||
|
||||
$('#layout-skins-list [data-skin]').click(function (e) {
|
||||
e.preventDefault();
|
||||
const skin_name = $(this).data('skin');
|
||||
$('body').removeClass(current_skin).addClass(skin_name);
|
||||
current_skin = skin_name;
|
||||
});
|
||||
|
||||
async function submitColor() {
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/customize?action=color'),
|
||||
dataType: 'json',
|
||||
data: { color_scheme: current_skin }
|
||||
});
|
||||
errno === 0 ? toastr.success(msg) : toastr.warning(msg);
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
$('#color-submit').click(submitColor);
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = submitColor;
|
||||
}
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if ($('#player-table').length === 1) {
|
||||
$(document).ready(initPlayersTable);
|
||||
}
|
||||
|
||||
function initPlayersTable() {
|
||||
const specificUid = getQueryString('uid');
|
||||
const query = specificUid ? `?uid=${specificUid}` : '';
|
||||
|
||||
$('#player-table').DataTable({
|
||||
ajax: url(`admin/player-data${query}`),
|
||||
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
|
||||
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
|
||||
columnDefs: playersTableColumnDefs
|
||||
}).on('xhr.dt', handleDataTablesAjaxError);
|
||||
}
|
||||
|
||||
const playersTableColumnDefs = [
|
||||
{
|
||||
targets: 0,
|
||||
data: 'pid',
|
||||
width: '1%'
|
||||
},
|
||||
{
|
||||
targets: 1,
|
||||
data: 'uid',
|
||||
render: (data, type, row) => `<a href="${url('admin/users?uid=' + row.uid)}" title="${trans('admin.inspectHisOwner')}" data-toggle="tooltip" data-placement="right">${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 (Steve)</option>
|
||||
<option ${(data === 'slim') ? 'selected=selected' : ''} value="slim">Slim (Alex)</option>
|
||||
</select>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 4,
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: (data, type, row) => ['steve', 'alex', 'cape'].reduce((html, type) => {
|
||||
const currentTypeTid = row[`tid_${type}`];
|
||||
const imageId = `${row.pid}-${currentTypeTid}`;
|
||||
|
||||
if (currentTypeTid === 0) {
|
||||
return html + `<img id="${imageId}" width="64" />`;
|
||||
} else {
|
||||
return html + `
|
||||
<a href="${ url('skinlib/show/' + currentTypeTid) }">
|
||||
<img id="${imageId}" width="64" src="${url('/preview/64/' + currentTypeTid)}.png" />
|
||||
</a>`;
|
||||
}
|
||||
}, '')
|
||||
},
|
||||
{
|
||||
targets: 5,
|
||||
data: 'last_modified'
|
||||
},
|
||||
{
|
||||
targets: 6,
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: (data, type, row) => `
|
||||
<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 style="cursor: pointer" onclick="changeTexture(${row.pid}, '${row.player_name}');">${trans('admin.changeTexture')}</a></li>
|
||||
<li><a style="cursor: pointer" onclick="changePlayerName(${row.pid}, '${row.player_name}');">${trans('admin.changePlayerName')}</a></li>
|
||||
<li><a style="cursor: pointer" onclick="changeOwner(${row.pid});">${trans('admin.changeOwner')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deletePlayer(${row.pid});">${trans('admin.deletePlayer')}</a>`
|
||||
}
|
||||
];
|
||||
|
||||
async function changePreference() {
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/players?action=preference'),
|
||||
dataType: 'json',
|
||||
data: {
|
||||
pid: $(this).parent().parent().attr('id'),
|
||||
preference: $(this).val()
|
||||
}
|
||||
});
|
||||
errno === 0 ? toastr.success(msg) : toastr.warning(msg);
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function changeTexture(pid, playerName) {
|
||||
const 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;
|
||||
}
|
||||
|
||||
async function ajaxChangeTexture(pid) {
|
||||
// Remove interference of modal which is hide
|
||||
$('.modal').each(function () {
|
||||
if ($(this).css('display') === 'none') $(this).remove();
|
||||
});
|
||||
|
||||
const model = $('#model').val();
|
||||
const tid = $('#tid').val();
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/players?action=texture'),
|
||||
dataType: 'json',
|
||||
data: { pid: pid, model: model, tid: tid }
|
||||
});
|
||||
if (errno === 0) {
|
||||
$(`#${pid}-${model}`).attr('src', url(`preview/64/${tid}.png`));
|
||||
$('.modal').modal('hide');
|
||||
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changePlayerName(pid, oldName) {
|
||||
const dom = $(`tr#${pid} > td:nth-child(3)`);
|
||||
let newPlayerName;
|
||||
|
||||
try {
|
||||
newPlayerName = await swal({
|
||||
text: trans('admin.changePlayerNameNotice'),
|
||||
input: 'text',
|
||||
inputValue: oldName,
|
||||
inputValidator: name => (new Promise((resolve, reject) => {
|
||||
name ? resolve() : reject(trans('admin.emptyPlayerName'));
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/players?action=name'),
|
||||
dataType: 'json',
|
||||
data: { pid: pid, name: newPlayerName }
|
||||
});
|
||||
if (errno === 0) {
|
||||
dom.text(newPlayerName);
|
||||
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function changeOwner(pid) {
|
||||
const dom = $(`#${pid} > td:nth-child(2)`);
|
||||
let owner = 0;
|
||||
|
||||
swal({
|
||||
html: `${trans('admin.changePlayerOwner')}<br><small> </small>`,
|
||||
input: 'number',
|
||||
inputValue: dom.text(),
|
||||
showCancelButton: true
|
||||
}).then(async uid => {
|
||||
owner = uid;
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/players?action=owner'),
|
||||
dataType: 'json',
|
||||
data: { pid, uid }
|
||||
});
|
||||
if (errno === 0) {
|
||||
dom.text(owner);
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
});
|
||||
|
||||
$('.swal2-input').on('input', debounce(showNicknameInSwal, 350));
|
||||
}
|
||||
|
||||
async function showNicknameInSwal() {
|
||||
const uid = $('.swal2-input').val();
|
||||
|
||||
if (isNaN(uid) || uid <= 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
const { user } = await fetch({
|
||||
type: 'GET',
|
||||
url: url(`admin/user/${uid}`),
|
||||
dataType: 'json'
|
||||
});
|
||||
$('.swal2-content').html(
|
||||
trans('admin.changePlayerOwner') +
|
||||
'<small style="display: block; margin-top: .5em;">' +
|
||||
trans('admin.targetUser', { nickname: user.nickname }) +
|
||||
'</small>'
|
||||
);
|
||||
} catch (error) {
|
||||
$('.swal2-content').html(`
|
||||
${trans('admin.changePlayerOwner')}<br>
|
||||
<small>${trans('admin.noSuchUser')}</small>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePlayer(pid) {
|
||||
try {
|
||||
await swal({
|
||||
text: trans('admin.deletePlayerNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/players?action=delete'),
|
||||
dataType: 'json',
|
||||
data: { pid: pid }
|
||||
});
|
||||
if (errno === 0) {
|
||||
$(`tr#${pid}`).remove();
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
initPlayersTable,
|
||||
changeOwner,
|
||||
showNicknameInSwal,
|
||||
deletePlayer,
|
||||
changeTexture,
|
||||
changePlayerName,
|
||||
changePreference,
|
||||
ajaxChangeTexture,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if ($('#plugin-table').length === 1) {
|
||||
$(document).ready(initPluginsTable);
|
||||
}
|
||||
|
||||
function initPluginsTable() {
|
||||
$.pluginsTable = $('#plugin-table').DataTable({
|
||||
ajax: url('admin/plugins/data'),
|
||||
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
|
||||
columnDefs: pluginsTableColumnDefs
|
||||
}).on('xhr.dt', handleDataTablesAjaxError);
|
||||
}
|
||||
|
||||
const pluginsTableColumnDefs = [
|
||||
{
|
||||
targets: 0,
|
||||
data: 'title'
|
||||
},
|
||||
{
|
||||
targets: 1,
|
||||
data: 'description',
|
||||
orderable: false,
|
||||
width: '35%'
|
||||
},
|
||||
{
|
||||
targets: 2,
|
||||
data: 'author',
|
||||
render: data => isEmpty(data.url) ? data.author : `<a href="${data.url}" target="_blank">${data.author}</a>`
|
||||
},
|
||||
{
|
||||
targets: 3,
|
||||
data: 'version',
|
||||
orderable: false
|
||||
},
|
||||
{
|
||||
targets: 4,
|
||||
data: 'dependencies',
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: data => {
|
||||
if (data.requirements.length === 0) {
|
||||
return `<i>${trans('admin.noDependencies')}</i>`;
|
||||
}
|
||||
|
||||
let result = data.isRequirementsSatisfied ? '' : `<a href="http://t.cn/RrT7SqC" target="_blank" class="label label-primary">${trans('admin.whyDependencies')}</a><br>`;
|
||||
|
||||
for (const name in data.requirements) {
|
||||
const constraint = data.requirements[name];
|
||||
const color = (name in data.unsatisfiedRequirements) ? 'red' : 'green';
|
||||
|
||||
result += `<span class="label bg-${color}">${name}: ${constraint}</span><br>`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 5,
|
||||
data: 'status'
|
||||
},
|
||||
{
|
||||
targets: 6,
|
||||
data: 'operations',
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: (data, type, row) => {
|
||||
let toggleButton, configViewButton;
|
||||
|
||||
if (data.enabled) {
|
||||
toggleButton = `<a class="btn btn-warning btn-sm" onclick="disablePlugin('${row.name}');">${trans('admin.disablePlugin')}</a>`;
|
||||
} else {
|
||||
toggleButton = `<a class="btn btn-primary btn-sm" onclick="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>`;
|
||||
}
|
||||
|
||||
const deletePluginButton = `<a class="btn btn-danger btn-sm" onclick="deletePlugin('${row.name}');">${trans('admin.deletePlugin')}</a>`;
|
||||
|
||||
return toggleButton + configViewButton + deletePluginButton;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
async function enablePlugin(name) {
|
||||
try {
|
||||
const { requirements } = await fetch({
|
||||
type: 'POST',
|
||||
url: url(`admin/plugins/manage?action=requirements&name=${name}`),
|
||||
dataType: 'json'
|
||||
});
|
||||
|
||||
if (requirements.length === 0) {
|
||||
await swal({
|
||||
text: trans('admin.noDependenciesNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
}
|
||||
|
||||
const { errno, msg, reason } = await fetch({
|
||||
type: 'POST',
|
||||
url: url(`admin/plugins/manage?action=enable&name=${name}`),
|
||||
dataType: 'json'
|
||||
});
|
||||
|
||||
if (errno === 0) {
|
||||
toastr.success(msg);
|
||||
|
||||
$.pluginsTable.ajax.reload(null, false);
|
||||
} else {
|
||||
swal({ type: 'warning', html: `<p>${msg}</p><ul><li>${reason.join('</li><li>')}</li></ul>` });
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function disablePlugin(name) {
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url(`admin/plugins/manage?action=disable&name=${name}`),
|
||||
dataType: 'json'
|
||||
});
|
||||
if (errno === 0) {
|
||||
toastr.success(msg);
|
||||
|
||||
$.pluginsTable.ajax.reload(null, false);
|
||||
} else {
|
||||
swal({ type: 'warning', html: msg });
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePlugin(name) {
|
||||
try {
|
||||
await swal({
|
||||
text: trans('admin.confirmDeletion'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url(`admin/plugins/manage?action=delete&name=${name}`),
|
||||
dataType: 'json'
|
||||
});
|
||||
if (errno === 0) {
|
||||
toastr.success(msg);
|
||||
|
||||
$.pluginsTable.ajax.reload(null, false);
|
||||
} else {
|
||||
swal({ type: 'warning', html: msg });
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
initPluginsTable,
|
||||
deletePlugin,
|
||||
enablePlugin,
|
||||
disablePlugin,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,346 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if ($('#user-table').length === 1) {
|
||||
$(document).ready(initUsersTable);
|
||||
}
|
||||
|
||||
function initUsersTable() {
|
||||
const specificUid = getQueryString('uid');
|
||||
const query = specificUid ? `?uid=${specificUid}` : '';
|
||||
|
||||
$('#user-table').DataTable({
|
||||
ajax: url(`admin/user-data${query}`),
|
||||
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
|
||||
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
|
||||
rowCallback: (row, data) => $(row).attr('id', `user-${data.uid}`),
|
||||
columnDefs: usersTableColumnDefs
|
||||
}).on('xhr.dt', handleDataTablesAjaxError);
|
||||
}
|
||||
|
||||
const userPermissions = {
|
||||
'-1': 'banned',
|
||||
'0': 'normal',
|
||||
'1': 'admin',
|
||||
'2': 'superAdmin'
|
||||
};
|
||||
|
||||
const usersTableColumnDefs = [
|
||||
{
|
||||
targets: 0,
|
||||
data: 'uid',
|
||||
width: '1%'
|
||||
},
|
||||
{
|
||||
targets: 1,
|
||||
data: 'email'
|
||||
},
|
||||
{
|
||||
targets: 2,
|
||||
data: 'nickname'
|
||||
},
|
||||
{
|
||||
targets: 3,
|
||||
data: 'score',
|
||||
render: data => `<input type="number" class="form-control score" value="${data}" title="${trans('admin.scoreTip')}" data-toggle="tooltip" data-placement="right">`
|
||||
},
|
||||
{
|
||||
targets: 4,
|
||||
data: 'players_count',
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: (data, type, row) => `<a href="${url('admin/players?uid='+row.uid)}" title="${trans('admin.inspectHisPlayers')}" data-toggle="tooltip" data-placement="right">${data}</span>`
|
||||
},
|
||||
{
|
||||
targets: 5,
|
||||
data: 'permission',
|
||||
className: 'status',
|
||||
render: data => trans('admin.' + userPermissions[data])
|
||||
},
|
||||
{
|
||||
targets: 6,
|
||||
data: 'register_at'
|
||||
},
|
||||
{
|
||||
targets: 7,
|
||||
data: 'operations',
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: renderUsersTableOperations
|
||||
}
|
||||
];
|
||||
|
||||
function renderUsersTableOperations(currentUserPermission, type, row) {
|
||||
let adminOption = '', bannedOption = '', deleteUserButton;
|
||||
|
||||
if (row.permission !== 2) {
|
||||
// Only SUPER admins are allowed to set/unset admins
|
||||
if (currentUserPermission === 2) {
|
||||
const adminStatus = row.permission === 1 ? 'admin' : 'normal';
|
||||
adminOption = `<li class="divider"></li> <li><a id="admin-${row.uid}" data="${adminStatus}" onclick="changeAdminStatus(${row.uid});">
|
||||
${ adminStatus === 'admin' ? trans('admin.unsetAdmin') : trans('admin.setAdmin') }
|
||||
</a></li>`;
|
||||
}
|
||||
|
||||
const banStatus = row.permission === -1 ? 'banned' : 'normal';
|
||||
bannedOption = `<li class="divider"></li> <li><a id="ban-${row.uid}" data="${banStatus}" onclick="changeBanStatus(${row.uid});">
|
||||
${ banStatus === 'banned' ? trans('admin.unban') : trans('admin.ban') }
|
||||
</a></li>`;
|
||||
}
|
||||
|
||||
if (currentUserPermission === 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" onclick="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" onclick="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 onclick="changeUserEmail(${row.uid});">${trans('admin.changeEmail')}</a></li>
|
||||
<li><a onclick="changeUserNickName(${row.uid});">${trans('admin.changeNickName')}</a></li>
|
||||
<li><a onclick="changeUserPwd(${row.uid});">${trans('admin.changePassword')}</a></li>
|
||||
${adminOption}
|
||||
${bannedOption}
|
||||
</ul>
|
||||
</div>
|
||||
${deleteUserButton}`;
|
||||
}
|
||||
|
||||
async function changeUserEmail(uid) {
|
||||
const dom = $(`tr#user-${uid} > td:nth-child(2)`);
|
||||
let newUserEmail = '';
|
||||
|
||||
try {
|
||||
newUserEmail = await swal({
|
||||
text: trans('admin.newUserEmail'),
|
||||
showCancelButton: true,
|
||||
input: 'text',
|
||||
inputValue: dom.text(),
|
||||
inputValidator: value => (new Promise((resolve, reject) => {
|
||||
value ? resolve() : reject(trans('auth.emptyEmail'));
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=email'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid, email: newUserEmail }
|
||||
});
|
||||
|
||||
if (errno === 0) {
|
||||
dom.text(newUserEmail);
|
||||
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeUserNickName(uid) {
|
||||
const dom = $(`tr#user-${uid} > td:nth-child(3)`);
|
||||
let newNickName = '';
|
||||
|
||||
try {
|
||||
newNickName = await swal({
|
||||
text: trans('admin.newUserNickname'),
|
||||
showCancelButton: true,
|
||||
input: 'text',
|
||||
inputValue: dom.text(),
|
||||
inputValidator: value => (new Promise((resolve, reject) => {
|
||||
value ? resolve() : reject(trans('auth.emptyNickname'));
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=nickname'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid, nickname: newNickName }
|
||||
});
|
||||
if (errno === 0) {
|
||||
dom.text(newNickName);
|
||||
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeUserPwd(uid) {
|
||||
let password;
|
||||
try {
|
||||
password = await swal({
|
||||
text: trans('admin.newUserPassword'),
|
||||
showCancelButton: true,
|
||||
input: 'password',
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=password'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid, password: password }
|
||||
});
|
||||
errno === 0 ? toastr.success(msg) : toastr.warning(msg);
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeUserScore(uid, score) {
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=score'),
|
||||
dataType: 'json',
|
||||
// Handle id formatted as '#user-1234'
|
||||
data: { uid: uid.slice(5), score: score }
|
||||
});
|
||||
errno === 0 ? toastr.success(msg) : toastr.warning(msg);
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeBanStatus(uid) {
|
||||
try {
|
||||
const { errno, msg, permission } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=ban'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid }
|
||||
});
|
||||
|
||||
if (errno === 0) {
|
||||
const 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 (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeAdminStatus(uid) {
|
||||
try {
|
||||
const { errno, msg, permission } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=admin'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid }
|
||||
});
|
||||
|
||||
if (errno === 0) {
|
||||
const 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 (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUserAccount(uid) {
|
||||
try {
|
||||
await swal({
|
||||
text: trans('admin.deleteUserNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { errno, msg } = await fetch({
|
||||
type: 'POST',
|
||||
url: url('admin/users?action=delete'),
|
||||
dataType: 'json',
|
||||
data: { uid: uid }
|
||||
});
|
||||
|
||||
if (errno === 0) {
|
||||
$('tr#user-' + uid).remove();
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
$('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());
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
initUsersTable,
|
||||
changeUserPwd,
|
||||
changeBanStatus,
|
||||
changeUserEmail,
|
||||
changeUserScore,
|
||||
changeAdminStatus,
|
||||
deleteUserAccount,
|
||||
changeUserNickName,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
$.locales = Object.create(null);
|
||||
$.currentLocale = Object.create(null);
|
||||
|
||||
/**
|
||||
* Load current selected language.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function loadLocales() {
|
||||
for (const lang in $.locales) {
|
||||
if (!isEmpty($.locales[lang])) {
|
||||
$.currentLocale = $.locales[lang] || Object.create(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate according to given key.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {dict} parameters
|
||||
* @return {string}
|
||||
*/
|
||||
function trans(key, parameters = {}) {
|
||||
if (isEmpty($.currentLocale)) {
|
||||
loadLocales();
|
||||
}
|
||||
|
||||
const segments = key.split('.');
|
||||
let temp = $.currentLocale || {};
|
||||
|
||||
for (const i in segments) {
|
||||
if (isEmpty(temp[segments[i]])) {
|
||||
return key;
|
||||
} else {
|
||||
temp = temp[segments[i]];
|
||||
}
|
||||
}
|
||||
|
||||
for (const i in parameters) {
|
||||
if (!isEmpty(parameters[i])) {
|
||||
temp = temp.replace(':'+i, parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
trans,
|
||||
loadLocales,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
'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>'
|
||||
};
|
||||
|
||||
$(document).ready(() => {
|
||||
$('input').iCheck({
|
||||
radioClass: 'iradio_square-blue',
|
||||
checkboxClass: 'icheckbox_square-blue'
|
||||
});
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
swal.setDefaults({
|
||||
confirmButtonText: trans('general.confirm'),
|
||||
cancelButtonText: trans('general.cancel')
|
||||
});
|
||||
});
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
async function confirmLogout() {
|
||||
try {
|
||||
await swal({
|
||||
text: trans('general.confirmLogout'),
|
||||
type: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: trans('general.confirm'),
|
||||
cancelButtonText: trans('general.cancel')
|
||||
});
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { msg } = await logout();
|
||||
setTimeout(() => window.location = url(), 1000);
|
||||
swal({
|
||||
type: 'success',
|
||||
html: msg
|
||||
});
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
return fetch({
|
||||
type: 'POST',
|
||||
url: url('auth/logout'),
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
|
||||
$('#logout-button').click(confirmLogout);
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
'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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a bootstrap modal.
|
||||
*
|
||||
* @param {string} msg Modal content
|
||||
* @param {string} title Modal title
|
||||
* @param {string} type Modal type, default|info|success|warning|error
|
||||
* @param {object} options All $.fn.modal options, plus { btnText, callback, destroyOnClose }
|
||||
* @return {void}
|
||||
*/
|
||||
function showModal(msg, title = 'Message', type = 'default', options = {}) {
|
||||
const btnType = (type !== 'default') ? 'btn-outline' : 'btn-primary';
|
||||
const btnText = options.btnText || 'OK';
|
||||
const onClick = (options.callback === undefined) ? 'data-dismiss="modal"' : `onclick="${options.callback}"`;
|
||||
const destroyOnClose = (options.destroyOnClose === false) ? false : true;
|
||||
|
||||
const 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}">${btnText}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
$(dom).on('hidden.bs.modal', function () {
|
||||
destroyOnClose && $(this).remove();
|
||||
}).modal(options);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
showMsg,
|
||||
showModal,
|
||||
showAjaxError,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// polyfill of String.prototype.includes
|
||||
if (!String.prototype.includes) {
|
||||
String.prototype.includes = function(search) {
|
||||
// Copied from core-js
|
||||
return !!~this.indexOf(search, arguments.length > 1 ? arguments[1] : undefined);
|
||||
};
|
||||
}
|
||||
|
||||
// polyfill of String.prototype.endsWith
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
const subjectString = this.toString();
|
||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
|
||||
position = subjectString.length;
|
||||
}
|
||||
position -= searchString.length;
|
||||
const lastIndex = subjectString.lastIndexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/* global skinview3d */
|
||||
|
||||
// TODO: Help wanted. This file needs to be tested.
|
||||
|
||||
$.msp = {};
|
||||
$.msp.handles = {};
|
||||
$.msp.control = null;
|
||||
$.msp.config = {
|
||||
domElement: document.getElementById('preview-3d-container'),
|
||||
slim: false,
|
||||
width: $('#preview-3d-container').width(),
|
||||
height: $('#preview-3d-container').height(),
|
||||
skinUrl: '',
|
||||
capeUrl: ''
|
||||
};
|
||||
|
||||
function initSkinViewer(cameraPositionZ = 70) {
|
||||
disposeSkinViewer();
|
||||
|
||||
$.msp.viewer = new skinview3d.SkinViewer($.msp.config);
|
||||
$.msp.viewer.camera.position.z = cameraPositionZ;
|
||||
|
||||
$.msp.viewer.animation = new skinview3d.CompositeAnimation();
|
||||
|
||||
// Init all available animations and pause them
|
||||
$.msp.handles.walk = $.msp.viewer.animation.add(skinview3d.WalkingAnimation);
|
||||
$.msp.handles.run = $.msp.viewer.animation.add(skinview3d.RunningAnimation);
|
||||
$.msp.handles.rotate = $.msp.viewer.animation.add(skinview3d.RotatingAnimation);
|
||||
$.msp.handles.run.paused = true;
|
||||
|
||||
$.msp.control = skinview3d.createOrbitControls($.msp.viewer);
|
||||
}
|
||||
|
||||
function applySkinViewerConfig(config) {
|
||||
config = config || $.msp.config;
|
||||
|
||||
for (const key in config) {
|
||||
$.msp.viewer[key] = config[key];
|
||||
}
|
||||
}
|
||||
|
||||
function disposeSkinViewer() {
|
||||
if ($.msp.viewer instanceof skinview3d.SkinViewer) {
|
||||
$.msp.viewer.dispose();
|
||||
$.msp.handles = {};
|
||||
$.msp.control = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function registerAnimationController() {
|
||||
$('.fa-pause').click(function () {
|
||||
$.msp.viewer.animationPaused = !$.msp.viewer.animationPaused;
|
||||
$(this).toggleClass('fa-pause').toggleClass('fa-play');
|
||||
});
|
||||
|
||||
$('.fa-forward').click(function () {
|
||||
$.msp.handles.run.paused = !$.msp.handles.run.paused;
|
||||
$.msp.handles.walk.paused = !$.msp.handles.run.paused;
|
||||
});
|
||||
|
||||
$('.fa-repeat').click(() => ($.msp.handles.rotate.paused = !$.msp.handles.rotate.paused));
|
||||
$('.fa-stop').click(() => {
|
||||
initSkinViewer();
|
||||
|
||||
// Pause all animations respectively
|
||||
for (const key in $.msp.handles) {
|
||||
$.msp.handles[key].paused = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function registerWindowResizeHandler() {
|
||||
$(window).resize(function () {
|
||||
$.msp.viewer.width = $('#preview-3d-container').width();
|
||||
$.msp.viewer.height = $('#preview-3d-container').height();
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = { initSkinViewer, applySkinViewerConfig, registerAnimationController, registerWindowResizeHandler };
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
console.log(
|
||||
`%c Blessing Skin %c v${blessing.version} %c Made with %c<3%c by printempw.%c https://blessing.studio`,
|
||||
'color:#fadfa3;background:#030307;padding:5px 0;margin:10px 0;',
|
||||
'background:#fadfa3;padding:5px 0;',
|
||||
'font-style:italic;',
|
||||
'color:red;',
|
||||
'font-style:italic;', ''
|
||||
);
|
||||
|
||||
/**
|
||||
* Check if given value is empty.
|
||||
*
|
||||
* @param {any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isEmpty(obj) {
|
||||
|
||||
// null and undefined are "empty"
|
||||
if (obj == null) return true; // eslint-disable-line eqeqeq
|
||||
|
||||
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 (const 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) {
|
||||
const result = location.search.match(new RegExp('[?&]'+key+'=([^&]+)','i'));
|
||||
|
||||
if (result === null || result.length < 1){
|
||||
return defaultValue;
|
||||
} else {
|
||||
return result[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the `resize` event is fired by scrolling on a mobile browser
|
||||
* whose address bar (e.g. Chrome) will hide automatically when scrolling.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isMobileBrowserScrolling() {
|
||||
const currentWindowWidth = $(window).width();
|
||||
const currentWindowHeight = $(window).height();
|
||||
|
||||
if ($.cachedWindowWidth === undefined) {
|
||||
$.cachedWindowWidth = currentWindowWidth;
|
||||
}
|
||||
|
||||
if ($.cachedWindowHeight === undefined) {
|
||||
$.cachedWindowHeight = currentWindowHeight;
|
||||
}
|
||||
|
||||
const isWidthChanged = (currentWindowWidth !== $.cachedWindowWidth);
|
||||
const isHeightChanged = (currentWindowHeight !== $.cachedWindowHeight);
|
||||
|
||||
// If the window width & height changes simultaneously, the resize can't be fired by scrolling.
|
||||
if (isWidthChanged && isHeightChanged) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If only width was changed, it also can't be.
|
||||
if (isWidthChanged) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If width didn't change but height changed ?
|
||||
if (isHeightChanged) {
|
||||
const last = $.lastWindowHeight;
|
||||
$.lastWindowHeight = currentWindowHeight;
|
||||
|
||||
if (last === undefined || currentWindowHeight === last) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If both width & height did not change
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 incorrect!');
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
return function () {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func.apply(context, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
function url(relativeUri = '') {
|
||||
blessing.base_url = blessing.base_url || '';
|
||||
|
||||
if (relativeUri[0] !== '/') {
|
||||
relativeUri = '/' + relativeUri;
|
||||
}
|
||||
|
||||
return blessing.base_url + relativeUri;
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
url,
|
||||
fetch,
|
||||
isEmpty,
|
||||
debounce,
|
||||
getQueryString,
|
||||
isMobileBrowserScrolling,
|
||||
};
|
||||
}
|
||||
|
|
@ -3,3 +3,12 @@ import './i18n';
|
|||
import './net';
|
||||
import './layout';
|
||||
import './logout';
|
||||
|
||||
console.log(
|
||||
`%c Blessing Skin %c v${blessing.version} %c Made with %c<3%c by printempw.%c https://blessing.studio`,
|
||||
'color:#fadfa3;background:#030307;padding:5px 0;margin:10px 0;',
|
||||
'background:#fadfa3;padding:5px 0;',
|
||||
'font-style:italic;',
|
||||
'color:red;',
|
||||
'font-style:italic;', ''
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user