diff --git a/resources/assets/src/js/admin.js b/resources/assets/src/js/admin.js
deleted file mode 100644
index 529605d7..00000000
--- a/resources/assets/src/js/admin.js
+++ /dev/null
@@ -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 = `
-
-
-
-
-
-
-
-
`;
-
- 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')}
`,
- 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') +
- '' +
- trans('admin.targetUser', { nickname: result.user.nickname }) +
- ''
- );
- }).catch(() => {
- $('.swal2-content').html(`${trans('admin.changePlayerOwner')}
${trans('admin.noSuchUser')}`);
- });
- }
- }, 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(' '+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(' ' + trans('admin.extracting'));
- $('.modal-body').append(''+trans('admin.downloadCompleted')+'
')
-
- 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 ``;
- }
- },
- {
- targets: 4,
- data: 'players_count',
- render: (data, type, row) => {
- return `${data}`;
- }
- },
- {
- 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 = `
- ${trans('admin.unsetAdmin')}`;
- } else {
- adminOption = `
- ${trans('admin.setAdmin')}`;
- }
- }
- if (row.permission === -1) {
- bannedOption = `
- ${trans('admin.unban')}`;
- } else {
- bannedOption = `
- ${trans('admin.ban')}`;
- }
- }
-
- if (data === 2) {
- if (row.permission === 2) {
- deleteUserButton = `
- ${trans('admin.deleteUser')}`;
- } else {
- deleteUserButton = `
- ${trans('admin.deleteUser')}`;
- }
- } else {
- if (row.permission === 1 || row.permission === 2) {
- deleteUserButton = `
- ${trans('admin.deleteUser')}`;
- } else {
- deleteUserButton = `
- ${trans('admin.deleteUser')}`;
- }
- }
-
- return `
-
-
-
-
- ${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 `${data}`;
- }
- },
- {
- targets: 2,
- data: 'player_name'
- },
- {
- targets: 3,
- data: 'preference',
- render: data => {
- return `
- `;
- }
- },
- {
- 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] = `
`;
- } else {
- html[textureType] = `
-
-
- `;
- }
- });
- return html.steve + html.alex + html.cape;
- }
- },
- {
- targets: 5,
- data: 'last_modified'
- },
- {
- targets: 6,
- searchable: false,
- orderable: false,
- render: (data, type, row) => {
- return `
-
-
-
-
- ${trans('admin.deletePlayer')}`;
- }
- }
- ]
- });
-}
-
-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 `${data.author}`;
- }
- }
- },
- {
- 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 = `
- ${trans('admin.disablePlugin')}`;
- } else {
- switchEnableButton = `
- ${trans('admin.enablePlugin')}`;
- }
- if (data.enabled && data.hasConfigView) {
- configViewButton = `
- ${trans('admin.configurePlugin')}`;
- } else {
- configViewButton = `
- ${trans('admin.configurePlugin')}`;
- }
- deletePluginButton = `
- ${trans('admin.deletePlugin')}`;
- return switchEnableButton + configViewButton + deletePluginButton;
- }
- }
- ]
- });
-}
diff --git a/resources/assets/src/js/admin/customize.js b/resources/assets/src/js/admin/customize.js
new file mode 100644
index 00000000..2da4a1ea
--- /dev/null
+++ b/resources/assets/src/js/admin/customize.js
@@ -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));
+});
diff --git a/resources/assets/src/js/admin/index.js b/resources/assets/src/js/admin/index.js
new file mode 100644
index 00000000..73aba2b2
--- /dev/null
+++ b/resources/assets/src/js/admin/index.js
@@ -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();
+ }
+});
diff --git a/resources/assets/src/js/admin/players.js b/resources/assets/src/js/admin/players.js
new file mode 100644
index 00000000..5500da06
--- /dev/null
+++ b/resources/assets/src/js/admin/players.js
@@ -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 = `
+
+
+
+
+
+
+
+
`;
+
+ 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')}
`,
+ 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') +
+ '' +
+ trans('admin.targetUser', { nickname: result.user.nickname }) +
+ ''
+ );
+ }).catch(() => {
+ $('.swal2-content').html(`
+ ${trans('admin.changePlayerOwner')}
+ ${trans('admin.noSuchUser')}
+ `);
+ });
+ }, 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));
+}
diff --git a/resources/assets/src/js/admin/plugins.js b/resources/assets/src/js/admin/plugins.js
new file mode 100644
index 00000000..656e4093
--- /dev/null
+++ b/resources/assets/src/js/admin/plugins.js
@@ -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));
+}
diff --git a/resources/assets/src/js/admin/tables.js b/resources/assets/src/js/admin/tables.js
new file mode 100644
index 00000000..48c065cf
--- /dev/null
+++ b/resources/assets/src/js/admin/tables.js
@@ -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 ``;
+ }
+ },
+ {
+ targets: 4,
+ data: 'players_count',
+ render: (data, type, row) => {
+ return `${data}`;
+ }
+ },
+ {
+ 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 = `
+ ${trans('admin.unsetAdmin')}`;
+ } else {
+ adminOption = `
+ ${trans('admin.setAdmin')}`;
+ }
+ }
+ if (row.permission === -1) {
+ bannedOption = `
+ ${trans('admin.unban')}`;
+ } else {
+ bannedOption = `
+ ${trans('admin.ban')}`;
+ }
+ }
+
+ if (data === 2) {
+ if (row.permission === 2) {
+ deleteUserButton = `
+ ${trans('admin.deleteUser')}`;
+ } else {
+ deleteUserButton = `
+ ${trans('admin.deleteUser')}`;
+ }
+ } else {
+ if (row.permission === 1 || row.permission === 2) {
+ deleteUserButton = `
+ ${trans('admin.deleteUser')}`;
+ } else {
+ deleteUserButton = `
+ ${trans('admin.deleteUser')}`;
+ }
+ }
+
+ return `
+
+
+
+
+ ${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 `${data}`;
+ }
+ },
+ {
+ targets: 2,
+ data: 'player_name'
+ },
+ {
+ targets: 3,
+ data: 'preference',
+ render: data => {
+ return `
+ `;
+ }
+ },
+ {
+ 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] = `
`;
+ } else {
+ html[textureType] = `
+
+
+ `;
+ }
+ });
+ return html.steve + html.alex + html.cape;
+ }
+ },
+ {
+ targets: 5,
+ data: 'last_modified'
+ },
+ {
+ targets: 6,
+ searchable: false,
+ orderable: false,
+ render: (data, type, row) => {
+ return `
+
+
+
+
+ ${trans('admin.deletePlayer')}`;
+ }
+ }
+ ]
+ });
+}
+
+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 `${data.author}`;
+ }
+ }
+ },
+ {
+ 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 = `
+ ${trans('admin.disablePlugin')}`;
+ } else {
+ switchEnableButton = `
+ ${trans('admin.enablePlugin')}`;
+ }
+ if (data.enabled && data.hasConfigView) {
+ configViewButton = `
+ ${trans('admin.configurePlugin')}`;
+ } else {
+ configViewButton = `
+ ${trans('admin.configurePlugin')}`;
+ }
+ deletePluginButton = `
+ ${trans('admin.deletePlugin')}`;
+ return switchEnableButton + configViewButton + deletePluginButton;
+ }
+ }
+ ]
+ });
+}
diff --git a/resources/assets/src/js/admin/update.js b/resources/assets/src/js/admin/update.js
new file mode 100644
index 00000000..117b61f8
--- /dev/null
+++ b/resources/assets/src/js/admin/update.js
@@ -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(
+ ' ' + 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(' ' + trans('admin.extracting'));
+ $('.modal-body').append(`${ trans('admin.downloadCompleted') }
`);
+
+ 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));
+
+}
diff --git a/resources/assets/src/js/admin/users.js b/resources/assets/src/js/admin/users.js
new file mode 100644
index 00000000..f65f9277
--- /dev/null
+++ b/resources/assets/src/js/admin/users.js
@@ -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());
+ }
+});
diff --git a/resources/assets/src/js/auth.js b/resources/assets/src/js/auth.js
deleted file mode 100644
index f0ce79de..00000000
--- a/resources/assets/src/js/auth.js
+++ /dev/null
@@ -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(
- ' ' + 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(
- ' ' + 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(' '+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(
- ' ' + 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;
-});
diff --git a/resources/assets/src/js/auth/captcha.js b/resources/assets/src/js/auth/captcha.js
new file mode 100644
index 00000000..1bd12edc
--- /dev/null
+++ b/resources/assets/src/js/auth/captcha.js
@@ -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);
diff --git a/resources/assets/src/js/auth/login.js b/resources/assets/src/js/auth/login.js
new file mode 100644
index 00000000..12c6e591
--- /dev/null
+++ b/resources/assets/src/js/auth/login.js
@@ -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(
+ ' ' + 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', '');
+ });
+ }
+});
diff --git a/resources/assets/src/js/auth/register.js b/resources/assets/src/js/auth/register.js
new file mode 100644
index 00000000..4d4e79e5
--- /dev/null
+++ b/resources/assets/src/js/auth/register.js
@@ -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(
+ ' ' + 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', '');
+ });
+ });
+});
diff --git a/resources/assets/src/js/auth/reset.js b/resources/assets/src/js/auth/reset.js
new file mode 100644
index 00000000..4ee88d35
--- /dev/null
+++ b/resources/assets/src/js/auth/reset.js
@@ -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(
+ ' ' + 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(
+ ' ' + 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', '');
+ });
+ });
+});
diff --git a/resources/assets/src/js/common/i18n.js b/resources/assets/src/js/common/i18n.js
new file mode 100644
index 00000000..ab0e6771
--- /dev/null
+++ b/resources/assets/src/js/common/i18n.js
@@ -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;
+}
diff --git a/resources/assets/src/js/common/layout.js b/resources/assets/src/js/common/layout.js
new file mode 100644
index 00000000..2891e920
--- /dev/null
+++ b/resources/assets/src/js/common/layout.js
@@ -0,0 +1,36 @@
+'use strict';
+
+$.defaultPaginatorConfig = {
+ visiblePages: 5,
+ currentPage: 1,
+ first: '«',
+ prev: '‹',
+ next: '›',
+ last: '»',
+ page: '{{page}}',
+ wrapper: ''
+};
+
+$(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();
+}
diff --git a/resources/assets/src/js/common/logout.js b/resources/assets/src/js/common/logout.js
new file mode 100644
index 00000000..244adf44
--- /dev/null
+++ b/resources/assets/src/js/common/logout.js
@@ -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());
diff --git a/resources/assets/src/js/common/notify.js b/resources/assets/src/js/common/notify.js
new file mode 100644
index 00000000..cdc5700e
--- /dev/null
+++ b/resources/assets/src/js/common/notify.js
@@ -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, '
'), 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 = `
+ `;
+
+ $(dom).modal(options);
+}
diff --git a/resources/assets/src/js/common/polyfill.js b/resources/assets/src/js/common/polyfill.js
new file mode 100644
index 00000000..95695b03
--- /dev/null
+++ b/resources/assets/src/js/common/polyfill.js
@@ -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;
+ };
+}
diff --git a/resources/assets/src/js/common/texture-preview.js b/resources/assets/src/js/common/texture-preview.js
new file mode 100644
index 00000000..0ad98f1d
--- /dev/null
+++ b/resources/assets/src/js/common/texture-preview.js
@@ -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')));
diff --git a/resources/assets/src/js/common/utils.js b/resources/assets/src/js/common/utils.js
new file mode 100644
index 00000000..3ba9a17d
--- /dev/null
+++ b/resources/assets/src/js/common/utils.js
@@ -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;
+}
diff --git a/resources/assets/src/js/general.js b/resources/assets/src/js/general.js
deleted file mode 100644
index 9781977e..00000000
--- a/resources/assets/src/js/general.js
+++ /dev/null
@@ -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: '«',
- prev: '‹',
- next: '›',
- last: '»',
- page: '{{page}}',
- wrapper: ''
-};
-
-// 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 = `
- `;
-
- $(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, '
'), 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 :)
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);
diff --git a/resources/assets/src/js/skinlib.js b/resources/assets/src/js/skinlib.js
deleted file mode 100644
index 95eedec5..00000000
--- a/resources/assets/src/js/skinlib.js
+++ /dev/null
@@ -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(`
- ${ trans('general.noResult') }
-
`);
- } 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 `
-
-
-
 })
-
-
-
-
- `;
-}
-
-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(`
-
- `);
- }
-}
-
-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') + `
- ${ trans('skinlib.filter.' + $.skinlib.filter) }
- `);
- }
-
- 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(' ' + 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
- });
- });
-}
diff --git a/resources/assets/src/js/skinlib/index.js b/resources/assets/src/js/skinlib/index.js
new file mode 100644
index 00000000..4e9aeff0
--- /dev/null
+++ b/resources/assets/src/js/skinlib/index.js
@@ -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(`
+ ${ trans('general.noResult') }
+
`);
+ } 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 `
+
+
+
 })
+
+
+
+
+ `;
+}
+
+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(`
+
+ `);
+ }
+}
+
+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') + `
+ ${ trans('skinlib.filter.' + $.skinlib.filter) }
+ `);
+ }
+
+ 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));
+ }
+}
diff --git a/resources/assets/src/js/skinlib/operations.js b/resources/assets/src/js/skinlib/operations.js
new file mode 100644
index 00000000..9569d79d
--- /dev/null
+++ b/resources/assets/src/js/skinlib/operations.js
@@ -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));
+}
diff --git a/resources/assets/src/js/skinlib/upload.js b/resources/assets/src/js/skinlib/upload.js
new file mode 100644
index 00000000..5848b8f6
--- /dev/null
+++ b/resources/assets/src/js/skinlib/upload.js
@@ -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(
+ ' ' + 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', '');
+ });
+ });
+}
diff --git a/resources/assets/src/js/user.js b/resources/assets/src/js/user.js
deleted file mode 100644
index 999bc5ea..00000000
--- a/resources/assets/src/js/user.js
+++ /dev/null
@@ -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 `
-
-
-
}preview/${item.tid}.png)
-
-
-
`;
-}
-
-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(`
- ${trans('user.emptyClosetMsg', { url: url('skinlib?filter=' + category) })}
`);
- } else {
- container.html(`${trans('general.noResult')}
`);
- }
- } 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(`
- ${trans('user.emptyClosetMsg', { url: url('skinlib?filter=' + type) })}
`);
- }
- })
- } 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 = `
-
- Default (Steve)
-
-
- Slim (Alex)
-
-
- ${trans('general.cape')}
-
-
- `;
- 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 = ' ' + trans('user.signInRemainingTime', { time: String(json.remaining_time) });
- $('#sign-in-button').attr('disabled', 'disabled').html(dom);
-
- if (json.storage.used > 1024) {
- $('#user-storage').html(`${Math.round(json.storage.used)}/ ${Math.round(json.storage.total)} MB`);
- } else {
- $('#user-storage').html(`${Math.round(json.storage.used)}/ ${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
- });
-}
diff --git a/resources/assets/src/js/user/closet.js b/resources/assets/src/js/user/closet.js
new file mode 100644
index 00000000..6fb3c467
--- /dev/null
+++ b/resources/assets/src/js/user/closet.js
@@ -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 `
+
+
+
}preview/${item.tid}.png)
+
+
+
`;
+}
+
+/**
+ * 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('' +
+ trans('user.emptyClosetMsg', { url: url(`skinlib?filter=${category}`) }) +
+ '
');
+ } else {
+ container.html(`${trans('general.noResult')}
`);
+ }
+
+ } 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(`${msg}
`);
+ }
+ });
+ } 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));
+}
diff --git a/resources/assets/src/js/user/player.js b/resources/assets/src/js/user/player.js
new file mode 100644
index 00000000..db0891cb
--- /dev/null
+++ b/resources/assets/src/js/user/player.js
@@ -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 = `
+ Default (Steve)
+
+
+ Slim (Alex)
+
+
+ ${trans('general.cape')}
+
+ `;
+
+ 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));
+ }
+}
diff --git a/resources/assets/src/js/user/profile.js b/resources/assets/src/js/user/profile.js
new file mode 100644
index 00000000..4413ae16
--- /dev/null
+++ b/resources/assets/src/js/user/profile.js
@@ -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));
+}
diff --git a/resources/assets/src/js/user/sign.js b/resources/assets/src/js/user/sign.js
new file mode 100644
index 00000000..449325ab
--- /dev/null
+++ b/resources/assets/src/js/user/sign.js
@@ -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 = ' ' + trans('user.signInRemainingTime', { time: String(result.remaining_time) });
+ $('#sign-in-button').attr('disabled', 'disabled').html(dom);
+
+ if (result.storage.used > 1024) {
+ $('#user-storage').html(`${Math.round(result.storage.used)}/ ${Math.round(result.storage.total)} MB`);
+ } else {
+ $('#user-storage').html(`${Math.round(result.storage.used)}/ ${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));
+}