diff --git a/public/css/base.css b/public/css/base.css index d4f7be51..63f1be7c 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -609,7 +609,7 @@ a:hover, a:active { z-index: 6; } -.big-check { +.big-check, .mal-check { display: none; } diff --git a/public/js/scripts-authed.min.js b/public/js/scripts-authed.min.js index 89b306ff..bddca87d 100644 --- a/public/js/scripts-authed.min.js +++ b/public/js/scripts-authed.min.js @@ -1,23 +1,612 @@ -var e=e||{};e.scope={};e.ASSUME_ES5=!1;e.ASSUME_NO_NATIVE_MAP=!1;e.ASSUME_NO_NATIVE_SET=!1;e.defineProperty=e.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(c,f,k){c!=Array.prototype&&c!=Object.prototype&&(c[f]=k.value)};e.getGlobal=function(c){return"undefined"!=typeof window&&window===c?c:"undefined"!=typeof global&&null!=global?global:c};e.global=e.getGlobal(this);e.SYMBOL_PREFIX="jscomp_symbol_"; -e.initSymbol=function(){e.initSymbol=function(){};e.global.Symbol||(e.global.Symbol=e.Symbol)};e.Symbol=function(){var c=0;return function(f){return e.SYMBOL_PREFIX+(f||"")+c++}}();e.initSymbolIterator=function(){e.initSymbol();var c=e.global.Symbol.iterator;c||(c=e.global.Symbol.iterator=e.global.Symbol("iterator"));"function"!=typeof Array.prototype[c]&&e.defineProperty(Array.prototype,c,{configurable:!0,writable:!0,value:function(){return e.arrayIterator(this)}});e.initSymbolIterator=function(){}}; -e.arrayIterator=function(c){var f=0;return e.iteratorPrototype(function(){return f { + let matches = (elm.document || elm.ownerDocument).querySelectorAll(selector), + i = matches.length; + while (--i >= 0 && matches.item(i) !== elm) {} return i > -1; + }; + + const AnimeClient = { + /** + * Placeholder function + */ + noop: () => {}, + /** + * DOM selector + * + * @param {string} selector - The dom selector string + * @param {object} [context] + * @return {[HTMLElement]} - array of dom elements + */ + $(selector, context = null) { + if (typeof selector !== 'string') { + return selector; + } + + context = (context !== null && context.nodeType === 1) + ? context + : document; + + let elements = []; + if (selector.match(/^#([\w]+$)/)) { + elements.push(document.getElementById(selector.split('#')[1])); + } else { + elements = [].slice.apply(context.querySelectorAll(selector)); + } + + return elements; + }, + /** + * Does the selector exist on the current page? + * + * @param {string} selector + * @returns {boolean} + */ + hasElement (selector) { + return AnimeClient.$(selector).length > 0; + }, + /** + * Scroll to the top of the Page + * + * @return {void} + */ + scrollToTop () { + window.scroll(0,0); + }, + /** + * Hide the selected element + * + * @param {string|Element} sel - the selector of the element to hide + * @return {void} + */ + hide (sel) { + sel.setAttribute('hidden', 'hidden'); + }, + /** + * UnHide the selected element + * + * @param {string|Element} sel - the selector of the element to hide + * @return {void} + */ + show (sel) { + sel.removeAttribute('hidden'); + }, + /** + * Display a message box + * + * @param {string} type - message type: info, error, success + * @param {string} message - the message itself + * @return {void} + */ + showMessage (type, message) { + let template = + `
+ + ${message} + +
`; + + let sel = AnimeClient.$('.message'); + if (sel[0] !== undefined) { + sel[0].remove(); + } + + AnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template); + }, + /** + * Finds the closest parent element matching the passed selector + * + * @param {HTMLElement} current - the current HTMLElement + * @param {string} parentSelector - selector for the parent element + * @return {HTMLElement|null} - the parent element + */ + closestParent (current, parentSelector) { + if (Element.prototype.closest !== undefined) { + return current.closest(parentSelector); + } + + while (current !== document.documentElement) { + if (matches(current, parentSelector)) { + return current; + } + + current = current.parentElement; + } + + return null; + }, + /** + * Generate a full url from a relative path + * + * @param {string} path - url path + * @return {string} - full url + */ + url (path) { + let uri = `//${document.location.host}`; + uri += (path.charAt(0) === '/') ? path : `/${path}`; + + return uri; + }, + /** + * Throttle execution of a function + * + * @see https://remysharp.com/2010/07/21/throttling-function-calls + * @see https://jsfiddle.net/jonathansampson/m7G64/ + * @param {Number} interval - the minimum throttle time in ms + * @param {Function} fn - the function to throttle + * @param {Object} [scope] - the 'this' object for the function + * @return {Function} + */ + throttle (interval, fn, scope) { + let wait = false; + return function (...args) { + const context = scope || this; + + if ( ! wait) { + fn.apply(context, args); + wait = true; + setTimeout(function() { + wait = false; + }, interval); + } + }; + }, + }; + + // ------------------------------------------------------------------------- + // ! Events + // ------------------------------------------------------------------------- + + function addEvent(sel, event, listener) { + // Recurse! + if (! event.match(/^([\w\-]+)$/)) { + event.split(' ').forEach((evt) => { + addEvent(sel, evt, listener); + }); + } + + sel.addEventListener(event, listener, false); + } + + function delegateEvent(sel, target, event, listener) { + // Attach the listener to the parent + addEvent(sel, event, (e) => { + // Get live version of the target selector + AnimeClient.$(target, sel).forEach((element) => { + if(e.target == element) { + listener.call(element, e); + e.stopPropagation(); + } + }); + }); + } + + /** + * Add an event listener + * + * @param {string|HTMLElement} sel - the parent selector to bind to + * @param {string} event - event name(s) to bind + * @param {string|HTMLElement|function} target - the element to directly bind the event to + * @param {function} [listener] - event listener callback + * @return {void} + */ + AnimeClient.on = (sel, event, target, listener) => { + if (listener === undefined) { + listener = target; + AnimeClient.$(sel).forEach((el) => { + addEvent(el, event, listener); + }); + } else { + AnimeClient.$(sel).forEach((el) => { + delegateEvent(el, target, event, listener); + }); + } + }; + + // ------------------------------------------------------------------------- + // ! Ajax + // ------------------------------------------------------------------------- + + /** + * Url encoding for non-get requests + * + * @param data + * @returns {string} + * @private + */ + function ajaxSerialize(data) { + let pairs = []; + + Object.keys(data).forEach((name) => { + let value = data[name].toString(); + + name = encodeURIComponent(name); + value = encodeURIComponent(value); + + pairs.push(`${name}=${value}`); + }); + + return pairs.join('&'); + } + + /** + * Make an ajax request + * + * Config:{ + * data: // data to send with the request + * type: // http verb of the request, defaults to GET + * success: // success callback + * error: // error callback + * } + * + * @param {string} url - the url to request + * @param {Object} config - the configuration object + * @return {void} + */ + AnimeClient.ajax = (url, config) => { + // Set some sane defaults + const defaultConfig = { + data: {}, + type: 'GET', + dataType: '', + success: AnimeClient.noop, + mimeType: 'application/x-www-form-urlencoded', + error: AnimeClient.noop + }; + + config = { + ...defaultConfig, + ...config, + }; + + let request = new XMLHttpRequest(); + let method = String(config.type).toUpperCase(); + + if (method === 'GET') { + url += (url.match(/\?/)) + ? ajaxSerialize(config.data) + : `?${ajaxSerialize(config.data)}`; + } + + request.open(method, url); + + request.onreadystatechange = () => { + if (request.readyState === 4) { + let responseText = ''; + + if (request.responseType === 'json') { + responseText = JSON.parse(request.responseText); + } else { + responseText = request.responseText; + } + + if (request.status > 299) { + config.error.call(null, request.status, responseText, request.response); + } else { + config.success.call(null, responseText, request.status); + } + } + }; + + if (config.dataType === 'json') { + config.data = JSON.stringify(config.data); + config.mimeType = 'application/json'; + } else { + config.data = ajaxSerialize(config.data); + } + + request.setRequestHeader('Content-Type', config.mimeType); + + switch (method) { + case 'GET': + request.send(null); + break; + + default: + request.send(config.data); + break; + } + }; + + /** + * Do a get request + * + * @param {string} url + * @param {object|function} data + * @param {function} [callback] + */ + AnimeClient.get = (url, data, callback = null) => { + if (callback === null) { + callback = data; + data = {}; + } + + return AnimeClient.ajax(url, { + data, + success: callback + }); + }; + + /** + * Event handlers + */ + // Close event for messages + AnimeClient.on('header', 'click', '.message', (e) => { + AnimeClient.hide(e.target); + }); + + // Confirm deleting of list or library items + AnimeClient.on('form.js-delete', 'submit', (event) => { + const proceed = confirm('Are you ABSOLUTELY SURE you want to delete this item?'); + + if (proceed === false) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + // Clear the api cache + AnimeClient.on('.js-clear-cache', 'click', () => { + AnimeClient.get('/cache_purge', () => { + AnimeClient.showMessage('success', 'Successfully purged api cache'); + }); + }); + + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js').then(reg => { + console.log('Service worker registered', reg.scope); + }).catch(error => { + console.error('Failed to register service worker', error); + }); + } + + function renderAnimeSearchResults (data) { + const results = []; + + data.forEach(x => { + const item = x.attributes; + const titles = item.titles.reduce((prev, current) => { + return prev + `${current}
`; + }, []); + + results.push(` + + `); + }); + + return results.join(''); + } + + function renderMangaSearchResults (data) { + const results = []; + + data.forEach(x => { + const item = x.attributes; + const titles = item.titles.reduce((prev, current) => { + return prev + `${current}
`; + }, []); + + results.push(` + + `); + }); + + return results.join(''); + } + + const search = (query) => { + // Show the loader + AnimeClient.$('.cssload-loader')[ 0 ].removeAttribute('hidden'); + + // Do the api search + AnimeClient.get(AnimeClient.url('/anime-collection/search'), { query }, (searchResults, status) => { + searchResults = JSON.parse(searchResults); + + // Hide the loader + AnimeClient.$('.cssload-loader')[ 0 ].setAttribute('hidden', 'hidden'); + + // Show the results + AnimeClient.$('#series_list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults.data); + }); + }; + + if (AnimeClient.hasElement('.anime #search')) { + AnimeClient.on('#search', 'keyup', AnimeClient.throttle(250, (e) => { + const query = encodeURIComponent(e.target.value); + if (query === '') { + return; + } + + search(query); + })); + } + + // Action to increment episode count + AnimeClient.on('body.anime.list', 'click', '.plus_one', (e) => { + let parentSel = AnimeClient.closestParent(e.target, 'article'); + let watchedCount = parseInt(AnimeClient.$('.completed_number', parentSel)[ 0 ].textContent, 10) || 0; + let totalCount = parseInt(AnimeClient.$('.total_number', parentSel)[ 0 ].textContent, 10); + let title = AnimeClient.$('.name a', parentSel)[ 0 ].textContent; + + // Setup the update data + let data = { + id: parentSel.dataset.kitsuId, + mal_id: parentSel.dataset.malId, + data: { + progress: watchedCount + 1 + } + }; + + // If the episode count is 0, and incremented, + // change status to currently watching + if (isNaN(watchedCount) || watchedCount === 0) { + data.data.status = 'current'; + } + + // If you increment at the last episode, mark as completed + if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) { + data.data.status = 'completed'; + } + + AnimeClient.show(AnimeClient.$('#loading-shadow')[ 0 ]); + + // okay, lets actually make some changes! + AnimeClient.ajax(AnimeClient.url('/anime/increment'), { + data, + dataType: 'json', + type: 'POST', + success: (res) => { + const resData = JSON.parse(res); + + if (resData.errors) { + AnimeClient.hide(AnimeClient.$('#loading-shadow')[ 0 ]); + AnimeClient.showMessage('error', `Failed to update ${title}. `); + AnimeClient.scrollToTop(); + return; + } + + if (resData.data.attributes.status === 'completed') { + AnimeClient.hide(parentSel); + } + + AnimeClient.hide(AnimeClient.$('#loading-shadow')[ 0 ]); + + AnimeClient.showMessage('success', `Successfully updated ${title}`); + AnimeClient.$('.completed_number', parentSel)[ 0 ].textContent = ++watchedCount; + AnimeClient.scrollToTop(); + }, + error: () => { + AnimeClient.hide(AnimeClient.$('#loading-shadow')[ 0 ]); + AnimeClient.showMessage('error', `Failed to update ${title}. `); + AnimeClient.scrollToTop(); + } + }); + }); + + const search$1 = (query) => { + AnimeClient.$('.cssload-loader')[ 0 ].removeAttribute('hidden'); + AnimeClient.get(AnimeClient.url('/manga/search'), { query }, (searchResults, status) => { + searchResults = JSON.parse(searchResults); + AnimeClient.$('.cssload-loader')[ 0 ].setAttribute('hidden', 'hidden'); + AnimeClient.$('#series_list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults.data); + }); + }; + + if (AnimeClient.hasElement('.manga #search')) { + AnimeClient.on('#search', 'keyup', AnimeClient.throttle(250, (e) => { + let query = encodeURIComponent(e.target.value); + if (query === '') { + return; + } + + search$1(query); + })); + } + + /** + * Javascript for editing manga, if logged in + */ + AnimeClient.on('.manga.list', 'click', '.edit_buttons button', (e) => { + let thisSel = e.target; + let parentSel = AnimeClient.closestParent(e.target, 'article'); + let type = thisSel.classList.contains('plus_one_chapter') ? 'chapter' : 'volume'; + let completed = parseInt(AnimeClient.$(`.${type}s_read`, parentSel)[ 0 ].textContent, 10) || 0; + let total = parseInt(AnimeClient.$(`.${type}_count`, parentSel)[ 0 ].textContent, 10); + let mangaName = AnimeClient.$('.name', parentSel)[ 0 ].textContent; + + if (isNaN(completed)) { + completed = 0; + } + + // Setup the update data + let data = { + id: parentSel.dataset.kitsuId, + mal_id: parentSel.dataset.malId, + data: { + progress: completed + } + }; + + // If the episode count is 0, and incremented, + // change status to currently reading + if (isNaN(completed) || completed === 0) { + data.data.status = 'current'; + } + + // If you increment at the last chapter, mark as completed + if ((!isNaN(completed)) && (completed + 1) === total) { + data.data.status = 'completed'; + } + + // Update the total count + data.data.progress = ++completed; + + AnimeClient.show(AnimeClient.$('#loading-shadow')[ 0 ]); + + AnimeClient.ajax(AnimeClient.url('/manga/update'), { + data, + dataType: 'json', + type: 'POST', + mimeType: 'application/json', + success: () => { + if (data.data.status === 'completed') { + AnimeClient.hide(parentSel); + } + + AnimeClient.hide(AnimeClient.$('#loading-shadow')[ 0 ]); + + AnimeClient.$(`.${type}s_read`, parentSel)[ 0 ].textContent = completed; + AnimeClient.showMessage('success', `Successfully updated ${mangaName}`); + AnimeClient.scrollToTop(); + }, + error: () => { + AnimeClient.hide(AnimeClient.$('#loading-shadow')[ 0 ]); + AnimeClient.showMessage('error', `Failed to update ${mangaName}`); + AnimeClient.scrollToTop(); + } + }); + }); + +}()); //# sourceMappingURL=scripts-authed.min.js.map diff --git a/public/js/scripts-authed.min.js.map b/public/js/scripts-authed.min.js.map index 96eccc20..ae57ba0b 100644 --- a/public/js/scripts-authed.min.js.map +++ b/public/js/scripts-authed.min.js.map @@ -1 +1 @@ -{"version":3,"file":"scripts-authed.min.js.map","sources":["src/base/AnimeClient.js"],"sourcesContent":["// -------------------------------------------------------------------------\n// ! Base\n// -------------------------------------------------------------------------\n\nconst matches = (elm, selector) => {\n\tlet matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),\n\t\ti = matches.length;\n\twhile (--i >= 0 && matches.item(i) !== elm) {};\n\treturn i > -1;\n}\n\nexport const AnimeClient = {\n\t/**\n\t * Placeholder function\n\t */\n\tnoop: () => {},\n\t/**\n\t * DOM selector\n\t *\n\t * @param {string} selector - The dom selector string\n\t * @param {object} [context]\n\t * @return {[HTMLElement]} - array of dom elements\n\t */\n\t$(selector, context = null) {\n\t\tif (typeof selector !== 'string') {\n\t\t\treturn selector;\n\t\t}\n\n\t\tcontext = (context !== null && context.nodeType === 1)\n\t\t\t? context\n\t\t\t: document;\n\n\t\tlet elements = [];\n\t\tif (selector.match(/^#([\\w]+$)/)) {\n\t\t\telements.push(document.getElementById(selector.split('#')[1]));\n\t\t} else {\n\t\t\telements = [].slice.apply(context.querySelectorAll(selector));\n\t\t}\n\n\t\treturn elements;\n\t},\n\t/**\n\t * Does the selector exist on the current page?\n\t *\n\t * @param {string} selector\n\t * @returns {boolean}\n\t */\n\thasElement (selector) {\n\t\treturn AnimeClient.$(selector).length > 0;\n\t},\n\t/**\n\t * Scroll to the top of the Page\n\t *\n\t * @return {void}\n\t */\n\tscrollToTop () {\n\t\twindow.scroll(0,0);\n\t},\n\t/**\n\t * Hide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\thide (sel) {\n\t\tsel.setAttribute('hidden', 'hidden');\n\t},\n\t/**\n\t * UnHide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\tshow (sel) {\n\t\tsel.removeAttribute('hidden');\n\t},\n\t/**\n\t * Display a message box\n\t *\n\t * @param {string} type - message type: info, error, success\n\t * @param {string} message - the message itself\n\t * @return {void}\n\t */\n\tshowMessage (type, message) {\n\t\tlet template =\n\t\t\t`
\n\t\t\t\t\n\t\t\t\t${message}\n\t\t\t\t\n\t\t\t
`;\n\n\t\tlet sel = AnimeClient.$('.message');\n\t\tif (sel[0] !== undefined) {\n\t\t\tsel[0].remove();\n\t\t}\n\n\t\tAnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);\n\t},\n\t/**\n\t * Finds the closest parent element matching the passed selector\n\t *\n\t * @param {HTMLElement} current - the current HTMLElement\n\t * @param {string} parentSelector - selector for the parent element\n\t * @return {HTMLElement|null} - the parent element\n\t */\n\tclosestParent (current, parentSelector) {\n\t\tif (Element.prototype.closest !== undefined) {\n\t\t\treturn current.closest(parentSelector);\n\t\t}\n\n\t\twhile (current !== document.documentElement) {\n\t\t\tif (matches(current, parentSelector)) {\n\t\t\t\treturn current;\n\t\t\t}\n\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\n\t\treturn null;\n\t},\n\t/**\n\t * Generate a full url from a relative path\n\t *\n\t * @param {string} path - url path\n\t * @return {string} - full url\n\t */\n\turl (path) {\n\t\tlet uri = `//${document.location.host}`;\n\t\turi += (path.charAt(0) === '/') ? path : `/${path}`;\n\n\t\treturn uri;\n\t},\n\t/**\n\t * Throttle execution of a function\n\t *\n\t * @see https://remysharp.com/2010/07/21/throttling-function-calls\n\t * @see https://jsfiddle.net/jonathansampson/m7G64/\n\t * @param {Number} interval - the minimum throttle time in ms\n\t * @param {Function} fn - the function to throttle\n\t * @param {Object} [scope] - the 'this' object for the function\n\t * @return {Function}\n\t */\n\tthrottle (interval, fn, scope) {\n\t\tlet wait = false;\n\t\treturn function (...args) {\n\t\t\tconst context = scope || this;\n\n\t\t\tif ( ! wait) {\n\t\t\t\tfn.apply(context, args);\n\t\t\t\twait = true;\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\twait = false;\n\t\t\t\t}, interval);\n\t\t\t}\n\t\t};\n\t},\n};\n\n// -------------------------------------------------------------------------\n// ! Events\n// -------------------------------------------------------------------------\n\nfunction addEvent(sel, event, listener) {\n\t// Recurse!\n\tif (! event.match(/^([\\w\\-]+)$/)) {\n\t\tevent.split(' ').forEach((evt) => {\n\t\t\taddEvent(sel, evt, listener);\n\t\t});\n\t}\n\n\tsel.addEventListener(event, listener, false);\n}\n\nfunction delegateEvent(sel, target, event, listener) {\n\t// Attach the listener to the parent\n\taddEvent(sel, event, (e) => {\n\t\t// Get live version of the target selector\n\t\tAnimeClient.$(target, sel).forEach((element) => {\n\t\t\tif(e.target == element) {\n\t\t\t\tlistener.call(element, e);\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Add an event listener\n *\n * @param {string|HTMLElement} sel - the parent selector to bind to\n * @param {string} event - event name(s) to bind\n * @param {string|HTMLElement|function} target - the element to directly bind the event to\n * @param {function} [listener] - event listener callback\n * @return {void}\n */\nAnimeClient.on = (sel, event, target, listener) => {\n\tif (listener === undefined) {\n\t\tlistener = target;\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\taddEvent(el, event, listener);\n\t\t});\n\t} else {\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\tdelegateEvent(el, target, event, listener);\n\t\t});\n\t}\n};\n\n// -------------------------------------------------------------------------\n// ! Ajax\n// -------------------------------------------------------------------------\n\n/**\n * Url encoding for non-get requests\n *\n * @param data\n * @returns {string}\n * @private\n */\nfunction ajaxSerialize(data) {\n\tlet pairs = [];\n\n\tObject.keys(data).forEach((name) => {\n\t\tlet value = data[name].toString();\n\n\t\tname = encodeURIComponent(name);\n\t\tvalue = encodeURIComponent(value);\n\n\t\tpairs.push(`${name}=${value}`);\n\t});\n\n\treturn pairs.join('&');\n}\n\n/**\n * Make an ajax request\n *\n * Config:{\n * \tdata: // data to send with the request\n * \ttype: // http verb of the request, defaults to GET\n * \tsuccess: // success callback\n * \terror: // error callback\n * }\n *\n * @param {string} url - the url to request\n * @param {Object} config - the configuration object\n * @return {void}\n */\nAnimeClient.ajax = (url, config) => {\n\t// Set some sane defaults\n\tconst defaultConfig = {\n\t\tdata: {},\n\t\ttype: 'GET',\n\t\tdataType: '',\n\t\tsuccess: AnimeClient.noop,\n\t\tmimeType: 'application/x-www-form-urlencoded',\n\t\terror: AnimeClient.noop\n\t}\n\n\tconfig = {\n\t\t...defaultConfig,\n\t\t...config,\n\t}\n\n\tlet request = new XMLHttpRequest();\n\tlet method = String(config.type).toUpperCase();\n\n\tif (method === 'GET') {\n\t\turl += (url.match(/\\?/))\n\t\t\t? ajaxSerialize(config.data)\n\t\t\t: `?${ajaxSerialize(config.data)}`;\n\t}\n\n\trequest.open(method, url);\n\n\trequest.onreadystatechange = () => {\n\t\tif (request.readyState === 4) {\n\t\t\tlet responseText = '';\n\n\t\t\tif (request.responseType === 'json') {\n\t\t\t\tresponseText = JSON.parse(request.responseText);\n\t\t\t} else {\n\t\t\t\tresponseText = request.responseText;\n\t\t\t}\n\n\t\t\tif (request.status > 299) {\n\t\t\t\tconfig.error.call(null, request.status, responseText, request.response);\n\t\t\t} else {\n\t\t\t\tconfig.success.call(null, responseText, request.status);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (config.dataType === 'json') {\n\t\tconfig.data = JSON.stringify(config.data);\n\t\tconfig.mimeType = 'application/json';\n\t} else {\n\t\tconfig.data = ajaxSerialize(config.data);\n\t}\n\n\trequest.setRequestHeader('Content-Type', config.mimeType);\n\n\tswitch (method) {\n\t\tcase 'GET':\n\t\t\trequest.send(null);\n\t\tbreak;\n\n\t\tdefault:\n\t\t\trequest.send(config.data);\n\t\tbreak;\n\t}\n};\n\n/**\n * Do a get request\n *\n * @param {string} url\n * @param {object|function} data\n * @param {function} [callback]\n */\nAnimeClient.get = (url, data, callback = null) => {\n\tif (callback === null) {\n\t\tcallback = data;\n\t\tdata = {};\n\t}\n\n\treturn AnimeClient.ajax(url, {\n\t\tdata,\n\t\tsuccess: callback\n\t});\n};\n\n// -------------------------------------------------------------------------\n// Export\n// -------------------------------------------------------------------------\n\nexport default AnimeClient;"],"names":[],"mappings":"IAuBG,EAAU,CAAV;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"scripts-authed.min.js.map","sources":["src/base/AnimeClient.js","src/base/events.js","src/index.js","src/template-helpers.js","src/anime.js","src/manga.js"],"sourcesContent":["// -------------------------------------------------------------------------\n// ! Base\n// -------------------------------------------------------------------------\n\nconst matches = (elm, selector) => {\n\tlet matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),\n\t\ti = matches.length;\n\twhile (--i >= 0 && matches.item(i) !== elm) {};\n\treturn i > -1;\n}\n\nexport const AnimeClient = {\n\t/**\n\t * Placeholder function\n\t */\n\tnoop: () => {},\n\t/**\n\t * DOM selector\n\t *\n\t * @param {string} selector - The dom selector string\n\t * @param {object} [context]\n\t * @return {[HTMLElement]} - array of dom elements\n\t */\n\t$(selector, context = null) {\n\t\tif (typeof selector !== 'string') {\n\t\t\treturn selector;\n\t\t}\n\n\t\tcontext = (context !== null && context.nodeType === 1)\n\t\t\t? context\n\t\t\t: document;\n\n\t\tlet elements = [];\n\t\tif (selector.match(/^#([\\w]+$)/)) {\n\t\t\telements.push(document.getElementById(selector.split('#')[1]));\n\t\t} else {\n\t\t\telements = [].slice.apply(context.querySelectorAll(selector));\n\t\t}\n\n\t\treturn elements;\n\t},\n\t/**\n\t * Does the selector exist on the current page?\n\t *\n\t * @param {string} selector\n\t * @returns {boolean}\n\t */\n\thasElement (selector) {\n\t\treturn AnimeClient.$(selector).length > 0;\n\t},\n\t/**\n\t * Scroll to the top of the Page\n\t *\n\t * @return {void}\n\t */\n\tscrollToTop () {\n\t\twindow.scroll(0,0);\n\t},\n\t/**\n\t * Hide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\thide (sel) {\n\t\tsel.setAttribute('hidden', 'hidden');\n\t},\n\t/**\n\t * UnHide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\tshow (sel) {\n\t\tsel.removeAttribute('hidden');\n\t},\n\t/**\n\t * Display a message box\n\t *\n\t * @param {string} type - message type: info, error, success\n\t * @param {string} message - the message itself\n\t * @return {void}\n\t */\n\tshowMessage (type, message) {\n\t\tlet template =\n\t\t\t`
\n\t\t\t\t\n\t\t\t\t${message}\n\t\t\t\t\n\t\t\t
`;\n\n\t\tlet sel = AnimeClient.$('.message');\n\t\tif (sel[0] !== undefined) {\n\t\t\tsel[0].remove();\n\t\t}\n\n\t\tAnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);\n\t},\n\t/**\n\t * Finds the closest parent element matching the passed selector\n\t *\n\t * @param {HTMLElement} current - the current HTMLElement\n\t * @param {string} parentSelector - selector for the parent element\n\t * @return {HTMLElement|null} - the parent element\n\t */\n\tclosestParent (current, parentSelector) {\n\t\tif (Element.prototype.closest !== undefined) {\n\t\t\treturn current.closest(parentSelector);\n\t\t}\n\n\t\twhile (current !== document.documentElement) {\n\t\t\tif (matches(current, parentSelector)) {\n\t\t\t\treturn current;\n\t\t\t}\n\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\n\t\treturn null;\n\t},\n\t/**\n\t * Generate a full url from a relative path\n\t *\n\t * @param {string} path - url path\n\t * @return {string} - full url\n\t */\n\turl (path) {\n\t\tlet uri = `//${document.location.host}`;\n\t\turi += (path.charAt(0) === '/') ? path : `/${path}`;\n\n\t\treturn uri;\n\t},\n\t/**\n\t * Throttle execution of a function\n\t *\n\t * @see https://remysharp.com/2010/07/21/throttling-function-calls\n\t * @see https://jsfiddle.net/jonathansampson/m7G64/\n\t * @param {Number} interval - the minimum throttle time in ms\n\t * @param {Function} fn - the function to throttle\n\t * @param {Object} [scope] - the 'this' object for the function\n\t * @return {Function}\n\t */\n\tthrottle (interval, fn, scope) {\n\t\tlet wait = false;\n\t\treturn function (...args) {\n\t\t\tconst context = scope || this;\n\n\t\t\tif ( ! wait) {\n\t\t\t\tfn.apply(context, args);\n\t\t\t\twait = true;\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\twait = false;\n\t\t\t\t}, interval);\n\t\t\t}\n\t\t};\n\t},\n};\n\n// -------------------------------------------------------------------------\n// ! Events\n// -------------------------------------------------------------------------\n\nfunction addEvent(sel, event, listener) {\n\t// Recurse!\n\tif (! event.match(/^([\\w\\-]+)$/)) {\n\t\tevent.split(' ').forEach((evt) => {\n\t\t\taddEvent(sel, evt, listener);\n\t\t});\n\t}\n\n\tsel.addEventListener(event, listener, false);\n}\n\nfunction delegateEvent(sel, target, event, listener) {\n\t// Attach the listener to the parent\n\taddEvent(sel, event, (e) => {\n\t\t// Get live version of the target selector\n\t\tAnimeClient.$(target, sel).forEach((element) => {\n\t\t\tif(e.target == element) {\n\t\t\t\tlistener.call(element, e);\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Add an event listener\n *\n * @param {string|HTMLElement} sel - the parent selector to bind to\n * @param {string} event - event name(s) to bind\n * @param {string|HTMLElement|function} target - the element to directly bind the event to\n * @param {function} [listener] - event listener callback\n * @return {void}\n */\nAnimeClient.on = (sel, event, target, listener) => {\n\tif (listener === undefined) {\n\t\tlistener = target;\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\taddEvent(el, event, listener);\n\t\t});\n\t} else {\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\tdelegateEvent(el, target, event, listener);\n\t\t});\n\t}\n};\n\n// -------------------------------------------------------------------------\n// ! Ajax\n// -------------------------------------------------------------------------\n\n/**\n * Url encoding for non-get requests\n *\n * @param data\n * @returns {string}\n * @private\n */\nfunction ajaxSerialize(data) {\n\tlet pairs = [];\n\n\tObject.keys(data).forEach((name) => {\n\t\tlet value = data[name].toString();\n\n\t\tname = encodeURIComponent(name);\n\t\tvalue = encodeURIComponent(value);\n\n\t\tpairs.push(`${name}=${value}`);\n\t});\n\n\treturn pairs.join('&');\n}\n\n/**\n * Make an ajax request\n *\n * Config:{\n * \tdata: // data to send with the request\n * \ttype: // http verb of the request, defaults to GET\n * \tsuccess: // success callback\n * \terror: // error callback\n * }\n *\n * @param {string} url - the url to request\n * @param {Object} config - the configuration object\n * @return {void}\n */\nAnimeClient.ajax = (url, config) => {\n\t// Set some sane defaults\n\tconst defaultConfig = {\n\t\tdata: {},\n\t\ttype: 'GET',\n\t\tdataType: '',\n\t\tsuccess: AnimeClient.noop,\n\t\tmimeType: 'application/x-www-form-urlencoded',\n\t\terror: AnimeClient.noop\n\t}\n\n\tconfig = {\n\t\t...defaultConfig,\n\t\t...config,\n\t}\n\n\tlet request = new XMLHttpRequest();\n\tlet method = String(config.type).toUpperCase();\n\n\tif (method === 'GET') {\n\t\turl += (url.match(/\\?/))\n\t\t\t? ajaxSerialize(config.data)\n\t\t\t: `?${ajaxSerialize(config.data)}`;\n\t}\n\n\trequest.open(method, url);\n\n\trequest.onreadystatechange = () => {\n\t\tif (request.readyState === 4) {\n\t\t\tlet responseText = '';\n\n\t\t\tif (request.responseType === 'json') {\n\t\t\t\tresponseText = JSON.parse(request.responseText);\n\t\t\t} else {\n\t\t\t\tresponseText = request.responseText;\n\t\t\t}\n\n\t\t\tif (request.status > 299) {\n\t\t\t\tconfig.error.call(null, request.status, responseText, request.response);\n\t\t\t} else {\n\t\t\t\tconfig.success.call(null, responseText, request.status);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (config.dataType === 'json') {\n\t\tconfig.data = JSON.stringify(config.data);\n\t\tconfig.mimeType = 'application/json';\n\t} else {\n\t\tconfig.data = ajaxSerialize(config.data);\n\t}\n\n\trequest.setRequestHeader('Content-Type', config.mimeType);\n\n\tswitch (method) {\n\t\tcase 'GET':\n\t\t\trequest.send(null);\n\t\tbreak;\n\n\t\tdefault:\n\t\t\trequest.send(config.data);\n\t\tbreak;\n\t}\n};\n\n/**\n * Do a get request\n *\n * @param {string} url\n * @param {object|function} data\n * @param {function} [callback]\n */\nAnimeClient.get = (url, data, callback = null) => {\n\tif (callback === null) {\n\t\tcallback = data;\n\t\tdata = {};\n\t}\n\n\treturn AnimeClient.ajax(url, {\n\t\tdata,\n\t\tsuccess: callback\n\t});\n};\n\n// -------------------------------------------------------------------------\n// Export\n// -------------------------------------------------------------------------\n\nexport default AnimeClient;","import _ from './AnimeClient.js';\n/**\n * Event handlers\n */\n// Close event for messages\n_.on('header', 'click', '.message', (e) => {\n\t_.hide(e.target);\n});\n\n// Confirm deleting of list or library items\n_.on('form.js-delete', 'submit', (event) => {\n\tconst proceed = confirm('Are you ABSOLUTELY SURE you want to delete this item?');\n\n\tif (proceed === false) {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t}\n});\n\n// Clear the api cache\n_.on('.js-clear-cache', 'click', () => {\n\t_.get('/cache_purge', () => {\n\t\t_.showMessage('success', 'Successfully purged api cache');\n\t});\n});\n","import './base/events.js';\n\nif ('serviceWorker' in navigator) {\n\tnavigator.serviceWorker.register('/sw.js').then(reg => {\n\t\tconsole.log('Service worker registered', reg.scope);\n\t}).catch(error => {\n\t\tconsole.error('Failed to register service worker', error);\n\t});\n}\n\n","export function renderAnimeSearchResults (data) {\n\tconst results = [];\n\n\tdata.forEach(x => {\n\t\tconst item = x.attributes;\n\t\tconst titles = item.titles.reduce((prev, current) => {\n\t\t\treturn prev + `${current}
`;\n\t\t}, []);\n\n\t\tresults.push(`\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tInfo Page\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t`);\n\t});\n\n\treturn results.join('');\n}\n\nexport function renderMangaSearchResults (data) {\n\tconst results = [];\n\n\tdata.forEach(x => {\n\t\tconst item = x.attributes;\n\t\tconst titles = item.titles.reduce((prev, current) => {\n\t\t\treturn prev + `${current}
`;\n\t\t}, []);\n\n\t\tresults.push(`\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tInfo Page\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t`);\n\t});\n\n\treturn results.join('');\n}","import _ from './base/AnimeClient.js'\nimport { renderAnimeSearchResults } from './template-helpers.js'\n\nconst search = (query) => {\n\t// Show the loader\n\t_.$('.cssload-loader')[ 0 ].removeAttribute('hidden');\n\n\t// Do the api search\n\t_.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {\n\t\tsearchResults = JSON.parse(searchResults);\n\n\t\t// Hide the loader\n\t\t_.$('.cssload-loader')[ 0 ].setAttribute('hidden', 'hidden');\n\n\t\t// Show the results\n\t\t_.$('#series_list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults.data);\n\t});\n};\n\nif (_.hasElement('.anime #search')) {\n\t_.on('#search', 'keyup', _.throttle(250, (e) => {\n\t\tconst query = encodeURIComponent(e.target.value);\n\t\tif (query === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tsearch(query);\n\t}));\n}\n\n// Action to increment episode count\n_.on('body.anime.list', 'click', '.plus_one', (e) => {\n\tlet parentSel = _.closestParent(e.target, 'article');\n\tlet watchedCount = parseInt(_.$('.completed_number', parentSel)[ 0 ].textContent, 10) || 0;\n\tlet totalCount = parseInt(_.$('.total_number', parentSel)[ 0 ].textContent, 10);\n\tlet title = _.$('.name a', parentSel)[ 0 ].textContent;\n\n\t// Setup the update data\n\tlet data = {\n\t\tid: parentSel.dataset.kitsuId,\n\t\tmal_id: parentSel.dataset.malId,\n\t\tdata: {\n\t\t\tprogress: watchedCount + 1\n\t\t}\n\t};\n\n\t// If the episode count is 0, and incremented,\n\t// change status to currently watching\n\tif (isNaN(watchedCount) || watchedCount === 0) {\n\t\tdata.data.status = 'current';\n\t}\n\n\t// If you increment at the last episode, mark as completed\n\tif ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {\n\t\tdata.data.status = 'completed';\n\t}\n\n\t_.show(_.$('#loading-shadow')[ 0 ]);\n\n\t// okay, lets actually make some changes!\n\t_.ajax(_.url('/anime/increment'), {\n\t\tdata,\n\t\tdataType: 'json',\n\t\ttype: 'POST',\n\t\tsuccess: (res) => {\n\t\t\tconst resData = JSON.parse(res);\n\n\t\t\tif (resData.errors) {\n\t\t\t\t_.hide(_.$('#loading-shadow')[ 0 ]);\n\t\t\t\t_.showMessage('error', `Failed to update ${title}. `);\n\t\t\t\t_.scrollToTop();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (resData.data.attributes.status === 'completed') {\n\t\t\t\t_.hide(parentSel);\n\t\t\t}\n\n\t\t\t_.hide(_.$('#loading-shadow')[ 0 ]);\n\n\t\t\t_.showMessage('success', `Successfully updated ${title}`);\n\t\t\t_.$('.completed_number', parentSel)[ 0 ].textContent = ++watchedCount;\n\t\t\t_.scrollToTop();\n\t\t},\n\t\terror: () => {\n\t\t\t_.hide(_.$('#loading-shadow')[ 0 ]);\n\t\t\t_.showMessage('error', `Failed to update ${title}. `);\n\t\t\t_.scrollToTop();\n\t\t}\n\t});\n});","import _ from './base/AnimeClient.js'\nimport { renderMangaSearchResults } from './template-helpers.js'\n\nconst search = (query) => {\n\t_.$('.cssload-loader')[ 0 ].removeAttribute('hidden');\n\t_.get(_.url('/manga/search'), { query }, (searchResults, status) => {\n\t\tsearchResults = JSON.parse(searchResults);\n\t\t_.$('.cssload-loader')[ 0 ].setAttribute('hidden', 'hidden');\n\t\t_.$('#series_list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults.data);\n\t});\n};\n\nif (_.hasElement('.manga #search')) {\n\t_.on('#search', 'keyup', _.throttle(250, (e) => {\n\t\tlet query = encodeURIComponent(e.target.value);\n\t\tif (query === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tsearch(query);\n\t}));\n}\n\n/**\n * Javascript for editing manga, if logged in\n */\n_.on('.manga.list', 'click', '.edit_buttons button', (e) => {\n\tlet thisSel = e.target;\n\tlet parentSel = _.closestParent(e.target, 'article');\n\tlet type = thisSel.classList.contains('plus_one_chapter') ? 'chapter' : 'volume';\n\tlet completed = parseInt(_.$(`.${type}s_read`, parentSel)[ 0 ].textContent, 10) || 0;\n\tlet total = parseInt(_.$(`.${type}_count`, parentSel)[ 0 ].textContent, 10);\n\tlet mangaName = _.$('.name', parentSel)[ 0 ].textContent;\n\n\tif (isNaN(completed)) {\n\t\tcompleted = 0;\n\t}\n\n\t// Setup the update data\n\tlet data = {\n\t\tid: parentSel.dataset.kitsuId,\n\t\tmal_id: parentSel.dataset.malId,\n\t\tdata: {\n\t\t\tprogress: completed\n\t\t}\n\t};\n\n\t// If the episode count is 0, and incremented,\n\t// change status to currently reading\n\tif (isNaN(completed) || completed === 0) {\n\t\tdata.data.status = 'current';\n\t}\n\n\t// If you increment at the last chapter, mark as completed\n\tif ((!isNaN(completed)) && (completed + 1) === total) {\n\t\tdata.data.status = 'completed';\n\t}\n\n\t// Update the total count\n\tdata.data.progress = ++completed;\n\n\t_.show(_.$('#loading-shadow')[ 0 ]);\n\n\t_.ajax(_.url('/manga/update'), {\n\t\tdata,\n\t\tdataType: 'json',\n\t\ttype: 'POST',\n\t\tmimeType: 'application/json',\n\t\tsuccess: () => {\n\t\t\tif (data.data.status === 'completed') {\n\t\t\t\t_.hide(parentSel);\n\t\t\t}\n\n\t\t\t_.hide(_.$('#loading-shadow')[ 0 ]);\n\n\t\t\t_.$(`.${type}s_read`, parentSel)[ 0 ].textContent = completed;\n\t\t\t_.showMessage('success', `Successfully updated ${mangaName}`);\n\t\t\t_.scrollToTop();\n\t\t},\n\t\terror: () => {\n\t\t\t_.hide(_.$('#loading-shadow')[ 0 ]);\n\t\t\t_.showMessage('error', `Failed to update ${mangaName}`);\n\t\t\t_.scrollToTop();\n\t\t}\n\t});\n});"],"names":["_","search"],"mappings":"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"} \ No newline at end of file diff --git a/public/js/scripts.min.js b/public/js/scripts.min.js index 52bba806..2203e0cf 100644 --- a/public/js/scripts.min.js +++ b/public/js/scripts.min.js @@ -1,14 +1,369 @@ -var d=d||{};d.scope={};d.ASSUME_ES5=!1;d.ASSUME_NO_NATIVE_MAP=!1;d.ASSUME_NO_NATIVE_SET=!1;d.defineProperty=d.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,e,g){a!=Array.prototype&&a!=Object.prototype&&(a[e]=g.value)};d.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};d.global=d.getGlobal(this);d.SYMBOL_PREFIX="jscomp_symbol_"; -d.initSymbol=function(){d.initSymbol=function(){};d.global.Symbol||(d.global.Symbol=d.Symbol)};d.Symbol=function(){var a=0;return function(e){return d.SYMBOL_PREFIX+(e||"")+a++}}();d.initSymbolIterator=function(){d.initSymbol();var a=d.global.Symbol.iterator;a||(a=d.global.Symbol.iterator=d.global.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&d.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return d.arrayIterator(this)}});d.initSymbolIterator=function(){}}; -d.arrayIterator=function(a){var e=0;return d.iteratorPrototype(function(){return e { + let matches = (elm.document || elm.ownerDocument).querySelectorAll(selector), + i = matches.length; + while (--i >= 0 && matches.item(i) !== elm) {} return i > -1; + }; + + const AnimeClient = { + /** + * Placeholder function + */ + noop: () => {}, + /** + * DOM selector + * + * @param {string} selector - The dom selector string + * @param {object} [context] + * @return {[HTMLElement]} - array of dom elements + */ + $(selector, context = null) { + if (typeof selector !== 'string') { + return selector; + } + + context = (context !== null && context.nodeType === 1) + ? context + : document; + + let elements = []; + if (selector.match(/^#([\w]+$)/)) { + elements.push(document.getElementById(selector.split('#')[1])); + } else { + elements = [].slice.apply(context.querySelectorAll(selector)); + } + + return elements; + }, + /** + * Does the selector exist on the current page? + * + * @param {string} selector + * @returns {boolean} + */ + hasElement (selector) { + return AnimeClient.$(selector).length > 0; + }, + /** + * Scroll to the top of the Page + * + * @return {void} + */ + scrollToTop () { + window.scroll(0,0); + }, + /** + * Hide the selected element + * + * @param {string|Element} sel - the selector of the element to hide + * @return {void} + */ + hide (sel) { + sel.setAttribute('hidden', 'hidden'); + }, + /** + * UnHide the selected element + * + * @param {string|Element} sel - the selector of the element to hide + * @return {void} + */ + show (sel) { + sel.removeAttribute('hidden'); + }, + /** + * Display a message box + * + * @param {string} type - message type: info, error, success + * @param {string} message - the message itself + * @return {void} + */ + showMessage (type, message) { + let template = + `
+ + ${message} + +
`; + + let sel = AnimeClient.$('.message'); + if (sel[0] !== undefined) { + sel[0].remove(); + } + + AnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template); + }, + /** + * Finds the closest parent element matching the passed selector + * + * @param {HTMLElement} current - the current HTMLElement + * @param {string} parentSelector - selector for the parent element + * @return {HTMLElement|null} - the parent element + */ + closestParent (current, parentSelector) { + if (Element.prototype.closest !== undefined) { + return current.closest(parentSelector); + } + + while (current !== document.documentElement) { + if (matches(current, parentSelector)) { + return current; + } + + current = current.parentElement; + } + + return null; + }, + /** + * Generate a full url from a relative path + * + * @param {string} path - url path + * @return {string} - full url + */ + url (path) { + let uri = `//${document.location.host}`; + uri += (path.charAt(0) === '/') ? path : `/${path}`; + + return uri; + }, + /** + * Throttle execution of a function + * + * @see https://remysharp.com/2010/07/21/throttling-function-calls + * @see https://jsfiddle.net/jonathansampson/m7G64/ + * @param {Number} interval - the minimum throttle time in ms + * @param {Function} fn - the function to throttle + * @param {Object} [scope] - the 'this' object for the function + * @return {Function} + */ + throttle (interval, fn, scope) { + let wait = false; + return function (...args) { + const context = scope || this; + + if ( ! wait) { + fn.apply(context, args); + wait = true; + setTimeout(function() { + wait = false; + }, interval); + } + }; + }, + }; + + // ------------------------------------------------------------------------- + // ! Events + // ------------------------------------------------------------------------- + + function addEvent(sel, event, listener) { + // Recurse! + if (! event.match(/^([\w\-]+)$/)) { + event.split(' ').forEach((evt) => { + addEvent(sel, evt, listener); + }); + } + + sel.addEventListener(event, listener, false); + } + + function delegateEvent(sel, target, event, listener) { + // Attach the listener to the parent + addEvent(sel, event, (e) => { + // Get live version of the target selector + AnimeClient.$(target, sel).forEach((element) => { + if(e.target == element) { + listener.call(element, e); + e.stopPropagation(); + } + }); + }); + } + + /** + * Add an event listener + * + * @param {string|HTMLElement} sel - the parent selector to bind to + * @param {string} event - event name(s) to bind + * @param {string|HTMLElement|function} target - the element to directly bind the event to + * @param {function} [listener] - event listener callback + * @return {void} + */ + AnimeClient.on = (sel, event, target, listener) => { + if (listener === undefined) { + listener = target; + AnimeClient.$(sel).forEach((el) => { + addEvent(el, event, listener); + }); + } else { + AnimeClient.$(sel).forEach((el) => { + delegateEvent(el, target, event, listener); + }); + } + }; + + // ------------------------------------------------------------------------- + // ! Ajax + // ------------------------------------------------------------------------- + + /** + * Url encoding for non-get requests + * + * @param data + * @returns {string} + * @private + */ + function ajaxSerialize(data) { + let pairs = []; + + Object.keys(data).forEach((name) => { + let value = data[name].toString(); + + name = encodeURIComponent(name); + value = encodeURIComponent(value); + + pairs.push(`${name}=${value}`); + }); + + return pairs.join('&'); + } + + /** + * Make an ajax request + * + * Config:{ + * data: // data to send with the request + * type: // http verb of the request, defaults to GET + * success: // success callback + * error: // error callback + * } + * + * @param {string} url - the url to request + * @param {Object} config - the configuration object + * @return {void} + */ + AnimeClient.ajax = (url, config) => { + // Set some sane defaults + const defaultConfig = { + data: {}, + type: 'GET', + dataType: '', + success: AnimeClient.noop, + mimeType: 'application/x-www-form-urlencoded', + error: AnimeClient.noop + }; + + config = { + ...defaultConfig, + ...config, + }; + + let request = new XMLHttpRequest(); + let method = String(config.type).toUpperCase(); + + if (method === 'GET') { + url += (url.match(/\?/)) + ? ajaxSerialize(config.data) + : `?${ajaxSerialize(config.data)}`; + } + + request.open(method, url); + + request.onreadystatechange = () => { + if (request.readyState === 4) { + let responseText = ''; + + if (request.responseType === 'json') { + responseText = JSON.parse(request.responseText); + } else { + responseText = request.responseText; + } + + if (request.status > 299) { + config.error.call(null, request.status, responseText, request.response); + } else { + config.success.call(null, responseText, request.status); + } + } + }; + + if (config.dataType === 'json') { + config.data = JSON.stringify(config.data); + config.mimeType = 'application/json'; + } else { + config.data = ajaxSerialize(config.data); + } + + request.setRequestHeader('Content-Type', config.mimeType); + + switch (method) { + case 'GET': + request.send(null); + break; + + default: + request.send(config.data); + break; + } + }; + + /** + * Do a get request + * + * @param {string} url + * @param {object|function} data + * @param {function} [callback] + */ + AnimeClient.get = (url, data, callback = null) => { + if (callback === null) { + callback = data; + data = {}; + } + + return AnimeClient.ajax(url, { + data, + success: callback + }); + }; + + /** + * Event handlers + */ + // Close event for messages + AnimeClient.on('header', 'click', '.message', (e) => { + AnimeClient.hide(e.target); + }); + + // Confirm deleting of list or library items + AnimeClient.on('form.js-delete', 'submit', (event) => { + const proceed = confirm('Are you ABSOLUTELY SURE you want to delete this item?'); + + if (proceed === false) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + // Clear the api cache + AnimeClient.on('.js-clear-cache', 'click', () => { + AnimeClient.get('/cache_purge', () => { + AnimeClient.showMessage('success', 'Successfully purged api cache'); + }); + }); + + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js').then(reg => { + console.log('Service worker registered', reg.scope); + }).catch(error => { + console.error('Failed to register service worker', error); + }); + } + +}()); //# sourceMappingURL=scripts.min.js.map diff --git a/public/js/scripts.min.js.map b/public/js/scripts.min.js.map index 77968041..5a96391e 100644 --- a/public/js/scripts.min.js.map +++ b/public/js/scripts.min.js.map @@ -1 +1 @@ -{"version":3,"file":"scripts.min.js.map","sources":["src/base/AnimeClient.js"],"sourcesContent":["// -------------------------------------------------------------------------\n// ! Base\n// -------------------------------------------------------------------------\n\nconst matches = (elm, selector) => {\n\tlet matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),\n\t\ti = matches.length;\n\twhile (--i >= 0 && matches.item(i) !== elm) {};\n\treturn i > -1;\n}\n\nexport const AnimeClient = {\n\t/**\n\t * Placeholder function\n\t */\n\tnoop: () => {},\n\t/**\n\t * DOM selector\n\t *\n\t * @param {string} selector - The dom selector string\n\t * @param {object} [context]\n\t * @return {[HTMLElement]} - array of dom elements\n\t */\n\t$(selector, context = null) {\n\t\tif (typeof selector !== 'string') {\n\t\t\treturn selector;\n\t\t}\n\n\t\tcontext = (context !== null && context.nodeType === 1)\n\t\t\t? context\n\t\t\t: document;\n\n\t\tlet elements = [];\n\t\tif (selector.match(/^#([\\w]+$)/)) {\n\t\t\telements.push(document.getElementById(selector.split('#')[1]));\n\t\t} else {\n\t\t\telements = [].slice.apply(context.querySelectorAll(selector));\n\t\t}\n\n\t\treturn elements;\n\t},\n\t/**\n\t * Does the selector exist on the current page?\n\t *\n\t * @param {string} selector\n\t * @returns {boolean}\n\t */\n\thasElement (selector) {\n\t\treturn AnimeClient.$(selector).length > 0;\n\t},\n\t/**\n\t * Scroll to the top of the Page\n\t *\n\t * @return {void}\n\t */\n\tscrollToTop () {\n\t\twindow.scroll(0,0);\n\t},\n\t/**\n\t * Hide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\thide (sel) {\n\t\tsel.setAttribute('hidden', 'hidden');\n\t},\n\t/**\n\t * UnHide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\tshow (sel) {\n\t\tsel.removeAttribute('hidden');\n\t},\n\t/**\n\t * Display a message box\n\t *\n\t * @param {string} type - message type: info, error, success\n\t * @param {string} message - the message itself\n\t * @return {void}\n\t */\n\tshowMessage (type, message) {\n\t\tlet template =\n\t\t\t`
\n\t\t\t\t\n\t\t\t\t${message}\n\t\t\t\t\n\t\t\t
`;\n\n\t\tlet sel = AnimeClient.$('.message');\n\t\tif (sel[0] !== undefined) {\n\t\t\tsel[0].remove();\n\t\t}\n\n\t\tAnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);\n\t},\n\t/**\n\t * Finds the closest parent element matching the passed selector\n\t *\n\t * @param {HTMLElement} current - the current HTMLElement\n\t * @param {string} parentSelector - selector for the parent element\n\t * @return {HTMLElement|null} - the parent element\n\t */\n\tclosestParent (current, parentSelector) {\n\t\tif (Element.prototype.closest !== undefined) {\n\t\t\treturn current.closest(parentSelector);\n\t\t}\n\n\t\twhile (current !== document.documentElement) {\n\t\t\tif (matches(current, parentSelector)) {\n\t\t\t\treturn current;\n\t\t\t}\n\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\n\t\treturn null;\n\t},\n\t/**\n\t * Generate a full url from a relative path\n\t *\n\t * @param {string} path - url path\n\t * @return {string} - full url\n\t */\n\turl (path) {\n\t\tlet uri = `//${document.location.host}`;\n\t\turi += (path.charAt(0) === '/') ? path : `/${path}`;\n\n\t\treturn uri;\n\t},\n\t/**\n\t * Throttle execution of a function\n\t *\n\t * @see https://remysharp.com/2010/07/21/throttling-function-calls\n\t * @see https://jsfiddle.net/jonathansampson/m7G64/\n\t * @param {Number} interval - the minimum throttle time in ms\n\t * @param {Function} fn - the function to throttle\n\t * @param {Object} [scope] - the 'this' object for the function\n\t * @return {Function}\n\t */\n\tthrottle (interval, fn, scope) {\n\t\tlet wait = false;\n\t\treturn function (...args) {\n\t\t\tconst context = scope || this;\n\n\t\t\tif ( ! wait) {\n\t\t\t\tfn.apply(context, args);\n\t\t\t\twait = true;\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\twait = false;\n\t\t\t\t}, interval);\n\t\t\t}\n\t\t};\n\t},\n};\n\n// -------------------------------------------------------------------------\n// ! Events\n// -------------------------------------------------------------------------\n\nfunction addEvent(sel, event, listener) {\n\t// Recurse!\n\tif (! event.match(/^([\\w\\-]+)$/)) {\n\t\tevent.split(' ').forEach((evt) => {\n\t\t\taddEvent(sel, evt, listener);\n\t\t});\n\t}\n\n\tsel.addEventListener(event, listener, false);\n}\n\nfunction delegateEvent(sel, target, event, listener) {\n\t// Attach the listener to the parent\n\taddEvent(sel, event, (e) => {\n\t\t// Get live version of the target selector\n\t\tAnimeClient.$(target, sel).forEach((element) => {\n\t\t\tif(e.target == element) {\n\t\t\t\tlistener.call(element, e);\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Add an event listener\n *\n * @param {string|HTMLElement} sel - the parent selector to bind to\n * @param {string} event - event name(s) to bind\n * @param {string|HTMLElement|function} target - the element to directly bind the event to\n * @param {function} [listener] - event listener callback\n * @return {void}\n */\nAnimeClient.on = (sel, event, target, listener) => {\n\tif (listener === undefined) {\n\t\tlistener = target;\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\taddEvent(el, event, listener);\n\t\t});\n\t} else {\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\tdelegateEvent(el, target, event, listener);\n\t\t});\n\t}\n};\n\n// -------------------------------------------------------------------------\n// ! Ajax\n// -------------------------------------------------------------------------\n\n/**\n * Url encoding for non-get requests\n *\n * @param data\n * @returns {string}\n * @private\n */\nfunction ajaxSerialize(data) {\n\tlet pairs = [];\n\n\tObject.keys(data).forEach((name) => {\n\t\tlet value = data[name].toString();\n\n\t\tname = encodeURIComponent(name);\n\t\tvalue = encodeURIComponent(value);\n\n\t\tpairs.push(`${name}=${value}`);\n\t});\n\n\treturn pairs.join('&');\n}\n\n/**\n * Make an ajax request\n *\n * Config:{\n * \tdata: // data to send with the request\n * \ttype: // http verb of the request, defaults to GET\n * \tsuccess: // success callback\n * \terror: // error callback\n * }\n *\n * @param {string} url - the url to request\n * @param {Object} config - the configuration object\n * @return {void}\n */\nAnimeClient.ajax = (url, config) => {\n\t// Set some sane defaults\n\tconst defaultConfig = {\n\t\tdata: {},\n\t\ttype: 'GET',\n\t\tdataType: '',\n\t\tsuccess: AnimeClient.noop,\n\t\tmimeType: 'application/x-www-form-urlencoded',\n\t\terror: AnimeClient.noop\n\t}\n\n\tconfig = {\n\t\t...defaultConfig,\n\t\t...config,\n\t}\n\n\tlet request = new XMLHttpRequest();\n\tlet method = String(config.type).toUpperCase();\n\n\tif (method === 'GET') {\n\t\turl += (url.match(/\\?/))\n\t\t\t? ajaxSerialize(config.data)\n\t\t\t: `?${ajaxSerialize(config.data)}`;\n\t}\n\n\trequest.open(method, url);\n\n\trequest.onreadystatechange = () => {\n\t\tif (request.readyState === 4) {\n\t\t\tlet responseText = '';\n\n\t\t\tif (request.responseType === 'json') {\n\t\t\t\tresponseText = JSON.parse(request.responseText);\n\t\t\t} else {\n\t\t\t\tresponseText = request.responseText;\n\t\t\t}\n\n\t\t\tif (request.status > 299) {\n\t\t\t\tconfig.error.call(null, request.status, responseText, request.response);\n\t\t\t} else {\n\t\t\t\tconfig.success.call(null, responseText, request.status);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (config.dataType === 'json') {\n\t\tconfig.data = JSON.stringify(config.data);\n\t\tconfig.mimeType = 'application/json';\n\t} else {\n\t\tconfig.data = ajaxSerialize(config.data);\n\t}\n\n\trequest.setRequestHeader('Content-Type', config.mimeType);\n\n\tswitch (method) {\n\t\tcase 'GET':\n\t\t\trequest.send(null);\n\t\tbreak;\n\n\t\tdefault:\n\t\t\trequest.send(config.data);\n\t\tbreak;\n\t}\n};\n\n/**\n * Do a get request\n *\n * @param {string} url\n * @param {object|function} data\n * @param {function} [callback]\n */\nAnimeClient.get = (url, data, callback = null) => {\n\tif (callback === null) {\n\t\tcallback = data;\n\t\tdata = {};\n\t}\n\n\treturn AnimeClient.ajax(url, {\n\t\tdata,\n\t\tsuccess: callback\n\t});\n};\n\n// -------------------------------------------------------------------------\n// Export\n// -------------------------------------------------------------------------\n\nexport default AnimeClient;"],"names":[],"mappings":"IAuBG,EAAU,CAAV;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"scripts.min.js.map","sources":["src/base/AnimeClient.js","src/base/events.js","src/index.js"],"sourcesContent":["// -------------------------------------------------------------------------\n// ! Base\n// -------------------------------------------------------------------------\n\nconst matches = (elm, selector) => {\n\tlet matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),\n\t\ti = matches.length;\n\twhile (--i >= 0 && matches.item(i) !== elm) {};\n\treturn i > -1;\n}\n\nexport const AnimeClient = {\n\t/**\n\t * Placeholder function\n\t */\n\tnoop: () => {},\n\t/**\n\t * DOM selector\n\t *\n\t * @param {string} selector - The dom selector string\n\t * @param {object} [context]\n\t * @return {[HTMLElement]} - array of dom elements\n\t */\n\t$(selector, context = null) {\n\t\tif (typeof selector !== 'string') {\n\t\t\treturn selector;\n\t\t}\n\n\t\tcontext = (context !== null && context.nodeType === 1)\n\t\t\t? context\n\t\t\t: document;\n\n\t\tlet elements = [];\n\t\tif (selector.match(/^#([\\w]+$)/)) {\n\t\t\telements.push(document.getElementById(selector.split('#')[1]));\n\t\t} else {\n\t\t\telements = [].slice.apply(context.querySelectorAll(selector));\n\t\t}\n\n\t\treturn elements;\n\t},\n\t/**\n\t * Does the selector exist on the current page?\n\t *\n\t * @param {string} selector\n\t * @returns {boolean}\n\t */\n\thasElement (selector) {\n\t\treturn AnimeClient.$(selector).length > 0;\n\t},\n\t/**\n\t * Scroll to the top of the Page\n\t *\n\t * @return {void}\n\t */\n\tscrollToTop () {\n\t\twindow.scroll(0,0);\n\t},\n\t/**\n\t * Hide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\thide (sel) {\n\t\tsel.setAttribute('hidden', 'hidden');\n\t},\n\t/**\n\t * UnHide the selected element\n\t *\n\t * @param {string|Element} sel - the selector of the element to hide\n\t * @return {void}\n\t */\n\tshow (sel) {\n\t\tsel.removeAttribute('hidden');\n\t},\n\t/**\n\t * Display a message box\n\t *\n\t * @param {string} type - message type: info, error, success\n\t * @param {string} message - the message itself\n\t * @return {void}\n\t */\n\tshowMessage (type, message) {\n\t\tlet template =\n\t\t\t`
\n\t\t\t\t\n\t\t\t\t${message}\n\t\t\t\t\n\t\t\t
`;\n\n\t\tlet sel = AnimeClient.$('.message');\n\t\tif (sel[0] !== undefined) {\n\t\t\tsel[0].remove();\n\t\t}\n\n\t\tAnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);\n\t},\n\t/**\n\t * Finds the closest parent element matching the passed selector\n\t *\n\t * @param {HTMLElement} current - the current HTMLElement\n\t * @param {string} parentSelector - selector for the parent element\n\t * @return {HTMLElement|null} - the parent element\n\t */\n\tclosestParent (current, parentSelector) {\n\t\tif (Element.prototype.closest !== undefined) {\n\t\t\treturn current.closest(parentSelector);\n\t\t}\n\n\t\twhile (current !== document.documentElement) {\n\t\t\tif (matches(current, parentSelector)) {\n\t\t\t\treturn current;\n\t\t\t}\n\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\n\t\treturn null;\n\t},\n\t/**\n\t * Generate a full url from a relative path\n\t *\n\t * @param {string} path - url path\n\t * @return {string} - full url\n\t */\n\turl (path) {\n\t\tlet uri = `//${document.location.host}`;\n\t\turi += (path.charAt(0) === '/') ? path : `/${path}`;\n\n\t\treturn uri;\n\t},\n\t/**\n\t * Throttle execution of a function\n\t *\n\t * @see https://remysharp.com/2010/07/21/throttling-function-calls\n\t * @see https://jsfiddle.net/jonathansampson/m7G64/\n\t * @param {Number} interval - the minimum throttle time in ms\n\t * @param {Function} fn - the function to throttle\n\t * @param {Object} [scope] - the 'this' object for the function\n\t * @return {Function}\n\t */\n\tthrottle (interval, fn, scope) {\n\t\tlet wait = false;\n\t\treturn function (...args) {\n\t\t\tconst context = scope || this;\n\n\t\t\tif ( ! wait) {\n\t\t\t\tfn.apply(context, args);\n\t\t\t\twait = true;\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\twait = false;\n\t\t\t\t}, interval);\n\t\t\t}\n\t\t};\n\t},\n};\n\n// -------------------------------------------------------------------------\n// ! Events\n// -------------------------------------------------------------------------\n\nfunction addEvent(sel, event, listener) {\n\t// Recurse!\n\tif (! event.match(/^([\\w\\-]+)$/)) {\n\t\tevent.split(' ').forEach((evt) => {\n\t\t\taddEvent(sel, evt, listener);\n\t\t});\n\t}\n\n\tsel.addEventListener(event, listener, false);\n}\n\nfunction delegateEvent(sel, target, event, listener) {\n\t// Attach the listener to the parent\n\taddEvent(sel, event, (e) => {\n\t\t// Get live version of the target selector\n\t\tAnimeClient.$(target, sel).forEach((element) => {\n\t\t\tif(e.target == element) {\n\t\t\t\tlistener.call(element, e);\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Add an event listener\n *\n * @param {string|HTMLElement} sel - the parent selector to bind to\n * @param {string} event - event name(s) to bind\n * @param {string|HTMLElement|function} target - the element to directly bind the event to\n * @param {function} [listener] - event listener callback\n * @return {void}\n */\nAnimeClient.on = (sel, event, target, listener) => {\n\tif (listener === undefined) {\n\t\tlistener = target;\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\taddEvent(el, event, listener);\n\t\t});\n\t} else {\n\t\tAnimeClient.$(sel).forEach((el) => {\n\t\t\tdelegateEvent(el, target, event, listener);\n\t\t});\n\t}\n};\n\n// -------------------------------------------------------------------------\n// ! Ajax\n// -------------------------------------------------------------------------\n\n/**\n * Url encoding for non-get requests\n *\n * @param data\n * @returns {string}\n * @private\n */\nfunction ajaxSerialize(data) {\n\tlet pairs = [];\n\n\tObject.keys(data).forEach((name) => {\n\t\tlet value = data[name].toString();\n\n\t\tname = encodeURIComponent(name);\n\t\tvalue = encodeURIComponent(value);\n\n\t\tpairs.push(`${name}=${value}`);\n\t});\n\n\treturn pairs.join('&');\n}\n\n/**\n * Make an ajax request\n *\n * Config:{\n * \tdata: // data to send with the request\n * \ttype: // http verb of the request, defaults to GET\n * \tsuccess: // success callback\n * \terror: // error callback\n * }\n *\n * @param {string} url - the url to request\n * @param {Object} config - the configuration object\n * @return {void}\n */\nAnimeClient.ajax = (url, config) => {\n\t// Set some sane defaults\n\tconst defaultConfig = {\n\t\tdata: {},\n\t\ttype: 'GET',\n\t\tdataType: '',\n\t\tsuccess: AnimeClient.noop,\n\t\tmimeType: 'application/x-www-form-urlencoded',\n\t\terror: AnimeClient.noop\n\t}\n\n\tconfig = {\n\t\t...defaultConfig,\n\t\t...config,\n\t}\n\n\tlet request = new XMLHttpRequest();\n\tlet method = String(config.type).toUpperCase();\n\n\tif (method === 'GET') {\n\t\turl += (url.match(/\\?/))\n\t\t\t? ajaxSerialize(config.data)\n\t\t\t: `?${ajaxSerialize(config.data)}`;\n\t}\n\n\trequest.open(method, url);\n\n\trequest.onreadystatechange = () => {\n\t\tif (request.readyState === 4) {\n\t\t\tlet responseText = '';\n\n\t\t\tif (request.responseType === 'json') {\n\t\t\t\tresponseText = JSON.parse(request.responseText);\n\t\t\t} else {\n\t\t\t\tresponseText = request.responseText;\n\t\t\t}\n\n\t\t\tif (request.status > 299) {\n\t\t\t\tconfig.error.call(null, request.status, responseText, request.response);\n\t\t\t} else {\n\t\t\t\tconfig.success.call(null, responseText, request.status);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (config.dataType === 'json') {\n\t\tconfig.data = JSON.stringify(config.data);\n\t\tconfig.mimeType = 'application/json';\n\t} else {\n\t\tconfig.data = ajaxSerialize(config.data);\n\t}\n\n\trequest.setRequestHeader('Content-Type', config.mimeType);\n\n\tswitch (method) {\n\t\tcase 'GET':\n\t\t\trequest.send(null);\n\t\tbreak;\n\n\t\tdefault:\n\t\t\trequest.send(config.data);\n\t\tbreak;\n\t}\n};\n\n/**\n * Do a get request\n *\n * @param {string} url\n * @param {object|function} data\n * @param {function} [callback]\n */\nAnimeClient.get = (url, data, callback = null) => {\n\tif (callback === null) {\n\t\tcallback = data;\n\t\tdata = {};\n\t}\n\n\treturn AnimeClient.ajax(url, {\n\t\tdata,\n\t\tsuccess: callback\n\t});\n};\n\n// -------------------------------------------------------------------------\n// Export\n// -------------------------------------------------------------------------\n\nexport default AnimeClient;","import _ from './AnimeClient.js';\n/**\n * Event handlers\n */\n// Close event for messages\n_.on('header', 'click', '.message', (e) => {\n\t_.hide(e.target);\n});\n\n// Confirm deleting of list or library items\n_.on('form.js-delete', 'submit', (event) => {\n\tconst proceed = confirm('Are you ABSOLUTELY SURE you want to delete this item?');\n\n\tif (proceed === false) {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t}\n});\n\n// Clear the api cache\n_.on('.js-clear-cache', 'click', () => {\n\t_.get('/cache_purge', () => {\n\t\t_.showMessage('success', 'Successfully purged api cache');\n\t});\n});\n","import './base/events.js';\n\nif ('serviceWorker' in navigator) {\n\tnavigator.serviceWorker.register('/sw.js').then(reg => {\n\t\tconsole.log('Service worker registered', reg.scope);\n\t}).catch(error => {\n\t\tconsole.error('Failed to register service worker', error);\n\t});\n}\n\n"],"names":["_"],"mappings":"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"} \ No newline at end of file diff --git a/public/js/src/template-helpers.js b/public/js/src/template-helpers.js index 5f0cabb7..2a207eee 100644 --- a/public/js/src/template-helpers.js +++ b/public/js/src/template-helpers.js @@ -1,3 +1,14 @@ +import _ from './base/AnimeClient.js'; + +// Wire up mal checkbox +_.on('main', 'change', '.big-check', (e) => { + const id = e.target.id; + _.$('.mal-check').forEach(el => { + el.checked = false; + }) + document.getElementById(`mal_${id}`).checked = true; +}); + export function renderAnimeSearchResults (data) { const results = []; @@ -10,6 +21,7 @@ export function renderAnimeSearchResults (data) { results.push(`