diff --git a/public/css/mango.css b/public/css/mango.css index 1eb7922..91eaca6 100644 --- a/public/css/mango.css +++ b/public/css/mango.css @@ -26,3 +26,9 @@ .uk-search { width: 100%; } +#selectable .ui-selecting { + background: #EEE6B9; +} +#selectable .ui-selected { + background: #F4E487; +} diff --git a/public/js/download.js b/public/js/download.js new file mode 100644 index 0000000..60b5a96 --- /dev/null +++ b/public/js/download.js @@ -0,0 +1,257 @@ +$(() => { + $('#search-input').keypress(event => { + if (event.which === 13) { + search(); + } + }); + $('.filter-field').each((i, ele) => { + $(ele).change(() => { + buildTable(); + }); + }); +}); +const toggleSpinner = () => { + var attr = $('#spinner').attr('hidden'); + if (attr) { + $('#spinner').removeAttr('hidden'); + $('#search-btn').attr('hidden', ''); + } + else { + $('#search-btn').removeAttr('hidden'); + $('#spinner').attr('hidden', ''); + } + searching = !searching; +}; +var searching = false; +var globalChapters; +const search = () => { + if (searching) { + return; + } + $('#manga-details').attr('hidden', ''); + $('#filter-form').attr('hidden', ''); + $('table').attr('hidden', ''); + $('#selection-controls').attr('hidden', ''); + $('#filter-notification').attr('hidden', ''); + toggleSpinner(); + const input = $('input').val(); + + if (input === "") { + toggleSpinner(); + return; + } + + var int_id = -1; + + try { + const path = new URL(input).pathname; + const match = /\/title\/([0-9]+)/.exec(path); + int_id = parseInt(match[1]); + } + catch(e) {} + + int_id = parseInt(input); + + if (int_id <= 0 || isNaN(int_id)) { + alert('danger', 'Please make sure you are using a valid manga ID or manga URL from Mangadex.'); + toggleSpinner(); + return; + } + + $.getJSON("/api/admin/mangadex/manga/" + int_id) + .done((data) => { + if (data.error) { + alert('danger', 'Failed to get manga info. Error: ' + data.error); + return; + } + + const cover = baseURL + data.cover_url; + $('#cover').attr("src", cover); + $('#title').text("Title: " + data.title); + $('#artist').text("Artist: " + data.artist); + $('#author').text("Author: " + data.author); + + $('#manga-details').removeAttr('hidden'); + + console.log(data.chapters); + globalChapters = data.chapters; + + let langs = new Set(); + let group_names = new Set(); + data.chapters.forEach(chp => { + Object.entries(chp.groups).forEach(([k, v]) => { + group_names.add(k); + }); + langs.add(chp.language); + }); + + const comp = (a, b) => { + var ai; + var bi; + try {ai = parseFloat(a);} catch(e) {} + try {bi = parseFloat(b);} catch(e) {} + if (typeof ai === 'undefined') return -1; + if (typeof bi === 'undefined') return 1; + if (ai < bi) return 1; + if (ai > bi) return -1; + return 0; + }; + + langs = [...langs].sort(); + group_names = [...group_names].sort(); + + langs.unshift('All'); + group_names.unshift('All'); + + $('select#lang-select').append(langs.map(e => ``).join('')); + $('select#group-select').append(group_names.map(e => ``).join('')); + + $('#filter-form').removeAttr('hidden'); + + buildTable(); + }) + .fail((jqXHR, status) => { + alert('danger', 'Failed to get manga info. Error: ' + status); + }) + .always(() => { + toggleSpinner(); + }); +}; +const parseRange = str => { + const regex = /^[\t ]*(?:(?:(<|<=|>|>=)[\t ]*([0-9]+))|(?:([0-9]+))|(?:([0-9]+)[\t ]*-[\t ]*([0-9]+))|(?:[\t ]*))[\t ]*$/m; + const matches = str.match(regex); + var num; + + if (!matches) { + alert('danger', `Failed to parse filter input ${str}`); + return [null, null]; + } + else if (typeof matches[1] !== 'undefined' && typeof matches[2] !== 'undefined') { + // e.g., <= 30 + num = parseInt(matches[2]); + if (isNaN(num)) { + alert('danger', `Failed to parse filter input ${str}`); + return [null, null]; + } + switch (matches[1]) { + case '<': + return [null, num - 1]; + case '<=': + return [null, num]; + case '>': + return [num + 1, null]; + case '>=': + return [num, null]; + } + } + else if (typeof matches[3] !== 'undefined') { + // a single number + num = parseInt(matches[3]); + if (isNaN(num)) { + alert('danger', `Failed to parse filter input ${str}`); + return [null, null]; + } + return [num, num]; + } + else if (typeof matches[4] !== 'undefined' && typeof matches[5] !== 'undefined') { + // e.g., 10 - 23 + num = parseInt(matches[4]); + const n2 = parseInt(matches[5]); + if (isNaN(num) || isNaN(n2) || num > n2) { + alert('danger', `Failed to parse filter input ${str}`); + return [null, null]; + } + return [num, n2]; + } + else { + // empty or space only + return [null, null]; + } +}; +const getFilters = () => { + const filters = {}; + $('.uk-select').each((i, ele) => { + const id = $(ele).attr('id'); + const by = id.split('-')[0]; + const choice = $(ele).val(); + filters[by] = choice; + }); + filters.volume = parseRange($('#volume-range').val()); + filters.chapter = parseRange($('#chapter-range').val()); + return filters; +}; +const buildTable = () => { + $('table').attr('hidden', ''); + $('#selection-controls').attr('hidden', ''); + $('#filter-notification').attr('hidden', ''); + console.log('rebuilding table'); + const filters = getFilters(); + console.log('filters:', filters); + var chapters = globalChapters.slice(); + Object.entries(filters).forEach(([k, v]) => { + if (v === 'All') return; + if (k === 'group') { + chapters = chapters.filter(c => v in c.groups); + return; + } + if (k === 'lang') { + chapters = chapters.filter(c => c.language === v); + return; + } + const lb = parseFloat(v[0]); + const ub = parseFloat(v[1]); + if (isNaN(lb) && isNaN(ub)) return; + chapters = chapters.filter(c => { + const val = parseFloat(c[k]); + if (isNaN(val)) return false; + if (isNaN(lb)) + return val <= ub; + else if (isNaN(ub)) + return val >= lb; + else + return val >= lb && val <= ub; + }); + }); + console.log('filtered chapters:', chapters); + $('#count-text').text(`${chapters.length} chapters found`); + + const chaptersLimit = 1000; + if (chapters.length > chaptersLimit) { + $('#filter-notification').text(`Mango can only list ${chaptersLimit} chapters, but we found ${chapters.length} chapters in this manga. Please use the filter options above to narrow down your search.`); + $('#filter-notification').removeAttr('hidden'); + return; + } + + const inner = chapters.map(chp => { + const group_str = Object.entries(chp.groups).map(([k, v]) => { + return `${k}`; + }).join(' | '); + return `
ID | @@ -70,188 +77,6 @@ var baseURL = "<%= base_url %>".replace(/\/$/, ""); - + + <% end %>
---|