From 104f5a32a7cc2dcabf9fae0d40b12aedc0671281 Mon Sep 17 00:00:00 2001 From: printempw Date: Thu, 13 Jul 2017 17:03:14 +0800 Subject: [PATCH] Refactor & modulize JavaScript files --- resources/assets/src/js/admin.js | 824 ------------------ resources/assets/src/js/admin/customize.js | 21 + resources/assets/src/js/admin/index.js | 24 + resources/assets/src/js/admin/players.js | 164 ++++ resources/assets/src/js/admin/plugins.js | 55 ++ resources/assets/src/js/admin/tables.js | 272 ++++++ resources/assets/src/js/admin/update.js | 91 ++ resources/assets/src/js/admin/users.js | 169 ++++ resources/assets/src/js/auth.js | 245 ------ resources/assets/src/js/auth/captcha.js | 11 + resources/assets/src/js/auth/login.js | 66 ++ resources/assets/src/js/auth/register.js | 71 ++ resources/assets/src/js/auth/reset.js | 98 +++ resources/assets/src/js/common/i18n.js | 51 ++ resources/assets/src/js/common/layout.js | 36 + resources/assets/src/js/common/logout.js | 29 + resources/assets/src/js/common/notify.js | 59 ++ resources/assets/src/js/common/polyfill.js | 30 + .../assets/src/js/common/texture-preview.js | 100 +++ resources/assets/src/js/common/utils.js | 98 +++ resources/assets/src/js/general.js | 384 -------- resources/assets/src/js/skinlib.js | 537 ------------ resources/assets/src/js/skinlib/index.js | 215 +++++ resources/assets/src/js/skinlib/operations.js | 173 ++++ resources/assets/src/js/skinlib/upload.js | 130 +++ resources/assets/src/js/user.js | 685 --------------- resources/assets/src/js/user/closet.js | 240 +++++ resources/assets/src/js/user/player.js | 213 +++++ resources/assets/src/js/user/profile.js | 153 ++++ resources/assets/src/js/user/sign.js | 27 + 30 files changed, 2596 insertions(+), 2675 deletions(-) delete mode 100644 resources/assets/src/js/admin.js create mode 100644 resources/assets/src/js/admin/customize.js create mode 100644 resources/assets/src/js/admin/index.js create mode 100644 resources/assets/src/js/admin/players.js create mode 100644 resources/assets/src/js/admin/plugins.js create mode 100644 resources/assets/src/js/admin/tables.js create mode 100644 resources/assets/src/js/admin/update.js create mode 100644 resources/assets/src/js/admin/users.js delete mode 100644 resources/assets/src/js/auth.js create mode 100644 resources/assets/src/js/auth/captcha.js create mode 100644 resources/assets/src/js/auth/login.js create mode 100644 resources/assets/src/js/auth/register.js create mode 100644 resources/assets/src/js/auth/reset.js create mode 100644 resources/assets/src/js/common/i18n.js create mode 100644 resources/assets/src/js/common/layout.js create mode 100644 resources/assets/src/js/common/logout.js create mode 100644 resources/assets/src/js/common/notify.js create mode 100644 resources/assets/src/js/common/polyfill.js create mode 100644 resources/assets/src/js/common/texture-preview.js create mode 100644 resources/assets/src/js/common/utils.js delete mode 100644 resources/assets/src/js/general.js delete mode 100644 resources/assets/src/js/skinlib.js create mode 100644 resources/assets/src/js/skinlib/index.js create mode 100644 resources/assets/src/js/skinlib/operations.js create mode 100644 resources/assets/src/js/skinlib/upload.js delete mode 100644 resources/assets/src/js/user.js create mode 100644 resources/assets/src/js/user/closet.js create mode 100644 resources/assets/src/js/user/player.js create mode 100644 resources/assets/src/js/user/profile.js create mode 100644 resources/assets/src/js/user/sign.js 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 ` -
    -
    - -
    - -
    `; -} - -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 ` +
    +
    + +
    + +
    `; +} + +/** + * 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)); +}