/
fleisar
/
kdk
Обзор
Документация
Войти
/
fleisar
/
kdk
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
app/main.js
929 строк
46 KB
fleisar
fix(title): change json passing method and anime info fetch
27 мар 2025, 19:02
27 мар 2025, 19:02
4f0b06b
Код
Авторство
О чём код?
/* global $, URI, Bind, LazyLoad, configCollector, Anilibria, kodik, kodikBackend, hotkeys shikimori, Template, ContainerManager, App, ListStorage, ContainerManager, HoverMenu */ const shikimoriIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 104.5 104.5" preserveAspectRatio="xMidYMid meet"> <g transform="matrix(0.012775 0 0 0.012775 -15.304452 -9.893285)"> <path d="M1725 8911c-6-5-39-19-73-32-35-12-66-28-69-36-3-8 25-41 68-80 127-114 229-225 284-308 29-44 81-111 115-150 191-213 444-597 655-994 136-256 145-283 157-476 15-246 4-566-31-900-17-159-31-294-31-298 0-13-28-2-249 96-142 63-216 102-242 126-21 20-78 57-127 84-107 57-135 59-307 22-162-34-232-73-397-218-38-33-68-65-68-71 0-11-68-114-164-249l-48-68 17-64c14-52 17-91 13-198-4-88-2-141 6-153 6-10 9-43 7-71l-3-52 59-27c33-16 75-27 98-26 28 0 48-7 65-22 72-65 125-81 180-53 24 12 25 15 21 108-4 110-3 111 84 129 84 17 192 7 478-45 265-48 630-105 671-105 19 0 35-13 65-54 22-29 63-74 93-100l53-48 90 7c160 11 347 36 407 55 57 18 63 18 171 0 62-10 159-23 217-29 264-28 300-34 311-47 7-9 17-11 28-4 12 6 25 4 42-5 95-55 109-59 259-61 84-1 177-8 221-17 198-41 788-114 1066-133l102-7-14-28c-20-37-19-49 3-47 9 1 46 0 82 0l65-2 53-73c29-39 59-81 67-92 13-19 16-18 73 38 33 33 70 81 83 108l23 49 145-5c214-7 786-18 856-16 33 1 111 8 173 16 62 8 133 15 158 15 54 0 55 5-43-127-68-91-158-172-221-199-19-7-47-25-63-38-16-14-95-46-184-76-88-29-168-62-184-76-16-13-35-24-42-24-8 0-56-9-106-21-77-18-93-25-93-40 0-35 89-20 293 50 86 29 117 36 117 26 0-9-53-31-145-62-86-28-145-53-145-60 0-7 8-13 17-13 25 0 8-11-29-19-23-5-32-12-30-24 2-9-2-17-9-17-6 0-9-7-5-15 3-8 2-15-3-15-13 0-46 41-85 106-20 34-42 64-49 66-6 3-43 1-82-3-38-4-80-8-92-8-16-1-23-7-23-19 0-17-15-20-131-31-95-9-134-10-142-2-15 15-27 14-27-3 0-10-16-16-52-20-68-7-331-28-385-31-22-2-46-6-52-9-15-10-33 3-27 19 2 7-2 16-10 19-22 9-22 9-26-22-3-27-4-27-85-30-53-2-83 1-83 8 0 5 8 10 18 10 9 0 33 10 52 22 19 11 51 22 70 24 19 1 41 2 48 3 6 1 12 6 12 12 0 6-16 10-37 9-21-1-65-1-98 0l-60 2 40 10c23 6 49 22 63 38l22 29-57 11c-32 7-64 15-72 18-12 4-13-5-7-52l6-58-37 7c-20 4-47 15-60 25-19 14-38 17-95 15l-72-3-9-33c-13-51-21-53-51-14-15 19-34 35-42 35-11 0-13 6-9 20 4 13 2 20-7 20-7 1-165 3-351 5-186 2-362 9-390 15-29 5-65 12-82 14-16 1 0 4 37 5 36 0 73 4 82 7 44 17 1 24-148 24-166 0-198 7-161 34 15 11 9 14-45 19-35 4-84 7-109 7-38 1-43 2-27 12 11 6 41 8 72 5 54-6 70 4 37 22-10 5-67 11-128 14-109 6-223 31-247 55-6 6-37 12-68 12-30 0-71 5-90 10-19 6-89 15-155 21-79 6-134 16-160 29-37 18-60 19-335 14-286-4-401-12-485-33-22-6-83-20-135-31-151-34-422-182-531-290-103-101-116-134-165-415-23-135-25-168-21-330 5-206 18-266 91-421 151-318 375-464 610-396 103 30 215 52 260 52 72 0 216 22 226 36 21 24-22 32-205 36-201 6-248 14-351 64-75 36-117 82-150 161l-16 38 45 40c86 77 235 152 397 198 47 14 186 39 310 56 124 17 234 36 244 42 11 6 31 13 45 15 14 2 70 9 124 16 100 12 140 35 45 25-43-4-49-3-30 5 16 8 42 8 91 1 70-11 121-7 121 8 0 5-7 9-17 9-9 0-14 2-11 5 3 4 48 10 99 16 260 27 885 89 898 89 9 0 23 5 31 10 13 9 13 12-3 24-15 12-12 14 35 24 44 8 54 7 64-6 9-13 37-16 157-16 141-1 146 0 156 21 9 20 6 22-40 32-28 6-58 12-67 12-58 5 266 38 376 39 46 0 143 9 215 21 112 18 172 21 392 20 143-1 319 3 390 10 90 8 147 8 185 1 33-6 57-7 61-1 3 5 57 18 120 30 63 11 141 29 174 39 66 21 267 55 416 69 52 6 141 23 199 39 58 17 139 37 180 45 130 27 366 117 385 146 3 6 38 27 78 48 185 97 296 289 326 563 6 58 6 89-1 98-14 16-15 16 65 35 80 19 161 63 153 83-3 8-8 20-11 27-9 20 58 91 101 106 22 8 54 24 71 35 29 18 33 26 33 65 0 35 9 58 40 106 22 33 55 76 75 95 39 37 44 68 21 116-8 16-27 94-41 174-30 164-36 175-127 221-57 29-63 35-118 119-46 69-38 96 41 151 53 37-7 30-94-11-45-21-79-15-136 24-40 28-58 32-153 41l-66 5-127-85c-74-50-137-85-152-85-13 0-86-34-161-77-75-42-153-84-172-94-30-15-545-159-569-159-5 0 16 62 46 138 88 223 144 424 173 627 40 283 104 646 121 699 53 156 143 300 399 631 241 313 528 701 550 745 34 66 100 320 100 383 0 12-13 24-39 34-41 16-51 37-51 107 0 45-44 108-137 198-69 67-85 77-128 84-77 13-215 10-265-5-108-32-114-36-133-76-11-22-36-60-56-85-20-25-57-74-83-110-63-89-127-146-206-182-66-30-173-99-283-184-84-64-149-141-199-234-25-46-69-118-98-159-72-104-107-180-139-297-25-92-27-115-29-324-2-124-7-450-12-725-11-526-12-541-66-750-26-102-180-603-188-610-2-3-61-10-130-16-108-10-143-9-245 6-86 13-145 15-220 10-82-5-103-4-103 7 0 46 37 206 76 329 47 148 203 889 244 1155 25 163 27 424 5 640-18 175-38 263-94 399-36 89-48 107-106 161-66 62-85 94-85 144 0 34-75 140-117 165-15 9-44 41-63 72-19 30-44 59-54 65-10 6-38 28-61 49-57 52-105 70-201 77-148 9-158 3-340-177-100-99-190-177-241-211-64-42-86-64-97-92-19-45-135-147-214-187-34-17-63-39-69-54-6-13-37-44-69-68-32-24-66-59-76-78-11-19-30-43-43-53-13-10-34-37-46-58-13-22-34-47-47-56-20-13-23-22-19-49 3-21 0-41-9-54-14-19-13-21 5-21 12 0 21-3 21-6 0-3-38-52-85-109-47-57-85-106-85-109 0-3 62 25 138 63 75 37 223 102 327 144 105 42 244 99 310 128 133 59 178 65 222 33 61-44 64-61 73-435 10-376 18-483 57-824 23-199 26-272 26-542 1-172-2-315-5-318-8-8-247 15-317 31-36 8-105 28-154 45-49 16-144 40-211 54-66 13-152 31-191 39-38 8-158 24-265 37l-194 22-41 38c-22 20-68 53-103 74-67 39-75 50-91 133-34 167-14 380 54 607 41 132 57 168 123 270 114 177 150 295 159 520 5 128 7 144 33 190 15 28 30 62 33 76 9 34-26 170-57 223-13 23-26 54-30 69-10 43-90 116-191 176-70 42-125 88-248 210-88 86-193 197-235 246-134 160-385 414-414 420-15 4-46 21-70 39-65 50-105 67-167 73-31 2-73 11-94 19-53 20-116 18-151-5l-29-20-72 40c-74 40-94 46-110 30zm6166-4640c-14-10-31-17-38-14-18 7 14 32 42 32 18 1 18-1-4-18zm-721-631c13-8 12-10-4-10-10 0-25-8-32-17-12-17-13-16-14 4 0 18 10 30 30 32 3 0 12-4 20-9zm-1670-50c8-5 12-12 8-15-10-10-49 5-43 16 8 11 16 11 35-1zm1253-61c49-10 56-15 59-38 3-25 6-26 58-26 30 1 84 4 120 8 63 6 51-2-22-18-16-3-28-10-28-16 0-7 22-8 66-3 50 5 65 4 60-5-4-7-16-9-26-6-10 3-43-1-73-10-53-15-56-15-97 6-35 19-52 21-115 16-47-3-77-1-84 6-8 8-19 5-41-11-26-19-41-22-127-21l-98 1 168 33c93 18 171 36 174 39 11 11-39 6-217-19-96-14-188-24-205-24-16 1 7 7 53 14 123 19 222 43 267 65 50 23 41 22 108 9z" fill="currentColor"/> <path d="M5800 3701c0-24 14-41 34-41 23 0 35 7 55 33 12 16 10 17-38 17-28 0-51-4-51-9z" fill="currentColor"/> <path d="M4885 3145c30-25 64-39 123-49 38-6 41 3 10 31-37 33-64 43-115 43l-48-1 30-24z" fill="currentColor"/> <path d="M7637 2964c-3-3-3-36 0-73 6-74-10-119-62-174-33-34-211-124-329-166-266-94-975-167-1948-201-602-21-871-36-971-55-38-7-85-11-102-8-18 3-35 0-39-6-4-7-34-8-86-4-57 4-80 3-80-6 0-6-16-11-39-11-45 0-63-16-31-28 15-5-22-15-122-30-168-26-158-26-158-7 0 8-4 15-10 15-5 0-10-5-10-11 0-8-5-8-15 1-18 15-45 6-45-15 0-19-25-29-115-44-38-7-80-14-92-17-13-2-23 0-23 6 0 5 12 10 28 10 43 0 72 11 72 27 0 12-10 14-52 8-94-13-143-29-146-47-2-12 3-18 15-18 15 0 16-2 3-10-8-5-24-10-34-10-11 0-27-6-35-13-9-8-106-42-216-76-519-162-713-263-903-466-62-66-142-173-142-189 0-16 31-3 41 17 14 27 73 83 129 122 23 16 54 44 67 62 14 18 30 33 35 33 5 0 36 21 69 46 65 50 205 127 221 122 6-2-8-17-31-32-45-32-112-111-220-260l-71-98 0 53c-1 48-2 52-14 36-8-10-29-61-47-113-24-69-44-108-76-145-49-58-67-88-59-97 10-9 77 60 95 98 9 20 31 54 49 75l32 40 0-33c0-43 12-41 118 12 161 81 275 118 382 123 58 3 140 16 210 33 150 38 312 66 480 83 136 14 278 16 297 4 5-3 75-2 154 3 269 17 363 21 358 16-5-5-532-46-591-46-24 0-28-3-19-13 9-11 55-13 234-9 147 3 235 1 258-6 34-11 188-25 459-42 99-6 163-15 202-29 50-17 169-41 353-71 114-19 143-28 184-58 35-26 54-32 95-32 28-1 71-5 96-10 29-6 151-2 350 9 175 11 335 15 375 11 112-11 471 6 638 30 118 17 176 31 257 63 58 22 134 50 170 62 149 51 258 123 378 249 102 108 122 151 207 431 65 214 67 224 75 370 12 217-4 270-112 365-27 23-48 50-48 61 0 55-64 112-93 83z" fill="currentColor"/> </g> </svg> `; $(() => { const { readyDOM } = window.utils; const defaultConfig = { showHistory: true, realtimeSearch: true, collectInformation: true, }; const url = new URI('#'); const binds = { aPage: new Bind('a[data-page]', 'click'), aWindow: new Bind('[data-window]', 'click'), hover: new Bind('header a', 'mousemove'), bodyScroll: new Bind('.page.general', 'scroll'), lazyLoad: new LazyLoad({}), titleHover: new HoverMenu('.ui-title', [ 'Открыть', '<span class="material-icons icon">info</span>Информация', '<span class="material-icons icon">playlist_add</span>Добавить в коллекцию', '<span class="material-icons icon">playlist_remove</span>Удалить из коллекции', '<span class="material-icons icon">content_copy</span>Скопировать название', { text: `<span class="icon">${shikimoriIcon}</span>Открыть в Shikimori`, classes: ['externalLink'], }, ], { classes: ['icon-list', 'ui-hovermenu', 'ui-material-list'] }), config: new configCollector({ config: defaultConfig }), anilibria: new Anilibria(), kodik: new kodik('.page.player .player-kodik'), kodikBackend: new kodikBackend('07e3119af111900bf95bd7c9554430a4'), sHistory: new ListStorage('sHistory'), sFavorite: new ListStorage('sFavorite'), sCollections: new ListStorage('sCollections'), titleHigh: new Bind('.ui-title', 'mousemove'), }; const toRGB = (hex) => { const hexCode = hex.split(''); const red = parseInt(hexCode[1] + hexCode[2], 16); const green = parseInt(hexCode[3] + hexCode[4], 16); const blue = parseInt(hexCode[5] + hexCode[6], 16); return [red, green, blue]; }; const collections = { title(data, inner = this.titleBase) { return `<a data-page="player" href="#player/shikimori/${data.id}" title="${(data.russian ? `${data.russian}\n` : '') + data.name}">${inner(data)}</a>`; }, titleBase(data, bottom, options = {}) { return ` <div class="ui-title${options.classes ? ` ${options.classes.join(' ')}` : ''}" data-add='${JSON.stringify(data)}' ${options.style ? `style="${Object.keys(options.style).map((k) => `${k}:${options.style[k]}`).join(';')}"` : ''}> <div class="preview"> <div class="lazy backdrop" data-bg="${shikimori.origin}${data.image.preview}"></div> <div class="lazy" data-bg="${shikimori.origin}${binds.config.config.highPreview ? data.image.original || data.image.preview : data.image.preview}"></div> </div> <div>${bottom || ''}</div> </div> `; }, titleExtended(data, bottom, options = {}) { return ` <div class="ui-title${options.classes ? ` ${options.classes.join(' ')}` : ''}" data-add='${JSON.stringify(data)}' ${options.style ? `style="${Object.keys(options.style).map((k) => `${k}:${options.style[k]}`).join(';')}"` : ''}> <div class="preview"> <div class="lazy backdrop" data-bg="${shikimori.origin}${data.image.preview}"></div> <div class="lazy" data-bg="${shikimori.origin}${binds.config.config.highPreview ? data.image.original || data.image.preview : data.image.preview}"></div> </div> <div>${bottom || ''}</div> </div> `; }, minTitle(name, time, from) { return `<a data-window="search" data-add='{"text":"${name}"}'><div class="ui-min-title" style="--from:'${from}'" title="Время: ${time}"><h4>${name}</h4></div></a>`; }, titleInfo(data) { const formatDate = (dateString) => ( new Date(dateString).toLocaleDateString( undefined, { day: 'numeric', month: 'long', year: 'numeric' }, ) ); const template = new Template('template-title-info'); const payload = { ...data, titleCard: this.titleBase(data, '', { style: { margin: 'auto' }, classes: ['inactive'] }), normalizedName: data.russian || data.name, status: data.status || 'Неизвестно', airedOn: data.aired_on != null ? formatDate(data.aired_on) : 'Неизвестно', releasedOn: data.released_on != null ? formatDate(data.released_on) : 'Неизвестно', normalizedEpisodes: `${data.episodes_aired}${data.episodes > 0 ? ` из ${data.episodes}` : ''}`, collectionAdd: { title: data }, collectionRemove: { remove: data.id }, openInShikimori: `${shikimori.origin}${data.url}`, }; return template.clone(payload); }, loadingResults() { return '<div class="ui-load"><span class="material-icons">hourglass_empty</span></div>'; }, noResults(text = 'Не удалось ничего найти') { return `<div class="ui-error"><div><span class="material-icons">announcement</span><h5>${text}</h5></div></div>`; }, progress(statistic, digits = 2) { const data = statistic[1]; const progress = []; const precent = 360 / Object.keys(data).filter((k) => (data[k].size !== '0B' ? k : null)).length; const description = []; Object.keys(data).filter((k) => (data[k].size !== '0B' ? k : null)).forEach((k, i) => { const precents = Math.ceil(data[k].part * 10 ** (digits + 2)) / 10 ** digits; progress.push(`<div title="${k} - ${precents}%" style="--color:${data[k].color || precent * i}deg;--part:${data[k].part}"></div>`); description.push(`<div style="--color:${data[k].color || precent * i}deg" title="${precents}%" class="ui-progress-part">${data[k].description || k} (${data[k].size})</div>`); }); return `<span>Всего: ${statistic[0]}</span><div class="ui-progress">${progress.join('')}</div>${description.join('')}`; }, playerBar(data) { const template = new Template('template-player-bar'); const payload = { data: JSON.stringify(data), name: data.russian || data.name, }; return template.clone(payload); }, materialItem(text, data) { return `${data.window || data.page || data.href ? `<a ${data.data || data.href ? ` href="${data.data || data.href}"` : ''} ${data.href ? 'target="_blank"' : ''} ${data.page ? `data-page='${data.page}'` : ''} ${data.window ? `data-window='${data.window}'${data.data ? ` data-add="${data.data}"` : ''}` : ''}>` : ''}<li ${data.active ? 'class="active"' : ''}>${text}</li>${data.window || data.page || data.href ? '</a>' : ''}`; }, consoleRow(text, color) { return `<pre style="color:${color};margin:0">${text}</pre>`; }, filterItem(name, title, options) { return `<div class="ui-select"><span>${title}</span><select name="${name}">${Object.values(options).map((i) => `<option value="${i.value}" ${i.selected ? 'selected' : ''}>${i.name}</option>`).join('')}</select><i class="material-icons">expand_more</i></div>`; }, collection(name, data) { const template = new Template('template-collection'); return template.clone({ data, name }); }, collectionBreadcrumb(name) { const template = new Template('template-collection-breadcrumb'); return template.clone({ name }); }, collectionAdd() { const template = new Template('template-collection-alert'); return template.clone({ text: 'Чтобы добавить тайтл в коллекцию, выберите её в списке ниже' }); }, collectionRemove() { const template = new Template('template-collection-alert'); return template.clone({ text: 'Чтобы удалить тайтл из коллекции, выберите её в списке ниже' }); }, }; const config = (k, v) => { const body = $('body'); const kodik = $('.page.player .player-kodik.player'); switch (k) { case 'disableAnimations': { body.attr('disableAnimations', v.toString()); break; } case 'showTime': { $('.ui-clock') .attr('display', v.toString()); break; } case 'showReleases': { $('header [data-window=releases]') .attr('display', v.toString()); break; } case 'showCollection': { $('header [data-window=collections]') .attr('display', v.toString()); break; } case 'showHistory': { $('header [data-window=history]') .attr('display', v.toString()); break; } case 'showFavorite': { $('header [data-window=favorite]') .attr('display', v.toString()); break; } case 'colorBG': { body.css('--colorBackground', toRGB(v) .join(', ')); break; } case 'colorText': { body.css('--colorText', toRGB(v) .join(', ')); break; } case 'colorFill': { body.css('--colorFill', toRGB(v) .join(', ')); break; } case 'colorHref': { body.css('--colorHref', toRGB(v) .join(', ')); break; } case 'colorAttention': { body.css('--colorAttention', toRGB(v) .join(', ')); break; } case 'highEffects': { body.attr('highEffects', v.toString()); break; } case 'playerMode': { $('div.page.player, div.window.config div.player-preview') .attr('playerMode', v); break; } case 'showTracking': { $('header [data-window=tracking]') .attr('display', v.toString()); break; } case 'disableAds': { if (v) { kodik.attr('sandbox', 'allow-same-origin allow-scripts'); } else { kodik.removeAttr('sandbox'); } break; } default: break; } }; const windowManager = new ContainerManager('.windows'); const windows = { bind() { binds.aWindow.bind((e) => { const name = e.delegateTarget.dataset.window; const data = JSON.parse(e.delegateTarget.dataset.add || 'null'); if (windowManager.currentContainer === name && data == null) { return windowManager.close(); } return windowManager.open(name, data); }, 'windows.bind'); $('.windows').on('click', (e) => { if (e.target.classList.contains('windows')) { windowManager.close(); } }); }, array: { config: () => { $('.window.config .version').text(`[${App.info.short_name}]${App.info.update.version}(rel:${App.info.update.release})`); $('.window.config .storage').html(collections.progress(workers.storage._data())); }, 'title-info': (title) => { $('.windows .window.title-info .ui-grid').html(collections.titleInfo(title)); const timeline = $('.window.title-info .ui-material-list.timeline'); const links = $('.window.title-info .ui-material-list.links'); const anime = shikimori.anime(title.id); anime.franchise(); anime.then((r) => { timeline.html(''); r.nodes.forEach((n) => { timeline.append(collections.materialItem(n.name, { page: 'player', data: `#player/shikimori/${n.id}`, active: n.id === title.id, })); }); binds.aPage.update(); }); anime.external_links(); anime.then((r) => { links.html(''); r.forEach((l) => { const kind = workers.shikimori.linkTitle(l.kind); links.append(collections.materialItem(`<span class="icon"><img src="${shikimori.origin}/assets/blocks/b-external_links/${kind ? l.kind : 'official_site'}.png" alt="${kind || 'official_site'}" height="100%"></span>${kind || l.kind}`, { href: l.url })); }); }); binds.lazyLoad.update(); binds.titleHigh.update(); binds.aWindow.update(); }, search: (data) => { const searchDebounceTime = 1e3 / 4; let searchInputDebounce = null; if (data != null && data.text) { $('.window.search input[type=text]').val(data.text); workers.search.search(data.text); } $('.window.search .search-input>input[type=text]').focus().unbind('input').on('input', (e) => { const search = $('.window.search input[type=text]').val(); if (e.key === 'Enter') { workers.search.search(search); clearTimeout(searchInputDebounce); } if ( search.length >= 2 && binds.config.config.realtimeSearch ) { clearTimeout(searchInputDebounce); searchInputDebounce = setTimeout(() => { workers.search.search(search); }, searchDebounceTime); } }); $('.window.search .search-input>button').unbind('click').click(() => { workers.search.search($('.window.search input[type=text]').val()); }); }, history() { const results = $('.window.history .results'); const hs = workers.metric.history(); results.html(''); if (hs === false) return results.html(collections.noResults('Сбор данных отключён')); Object.values(hs.get()).reverse().forEach((d) => results.append(collections.title(d))); binds.titleHigh.update(); binds.lazyLoad.update(); }, collections(data) { const results = $('.window.collections .result').html(''); const titleList = $('.window.collections .titles').html(''); const cll = workers.metric.collections(); if (cll === false) { results.html(collections.noResults('Сбор данных отключён')); return; } const renderList = () => { const list = cll.get().reverse(); results.html(list.length > 0 ? '' : collections.noResults('Пока тут пусто')); list.forEach((d) => results.append( collections.collection(d.name, { id: d.id, name: d.name, new: data != null ? data.title : undefined, delete: data != null ? data.remove : undefined, }), )); binds.aWindow.update(); }; renderList(); if (data == null) { return; } if (data.id === null) { const newCollectionName = prompt('Название новой коллекции'); if (newCollectionName == null) { return; } cll.push({ id: Date.now(), name: newCollectionName }); renderList(); } if (data.id != null) { const listStorage = new ListStorage(`collection-${data.id}`); results.html(''); if (data.new != null && !listStorage.has(data.new.id)) { listStorage.push(data.new); } if (data.delete != null) { listStorage.delete(data.delete); } const items = listStorage.get().reverse(); results.append(collections.collectionBreadcrumb(data.name)); items.forEach((d) => { titleList.append(collections.title(d)); }); binds.titleHigh.update(); binds.titleHover.updateSelector(); binds.lazyLoad.update(); binds.aWindow.update(); } if (data.title != null) { results.prepend(collections.collectionAdd()); } if (data.remove != null) { results.prepend(collections.collectionRemove()); } }, }, }; Object.entries(windows.array).forEach(([windowName]) => { windowManager.register(windowName, `.window.${windowName}`); windowManager.addEventListener(windowName, 'open', windows.array[windowName]); }); ['info-usage', 'info-processing', 'about', 'tutorial'].forEach((windowName) => { windowManager.register(windowName, `.window.${windowName}`); }); $('header nav > a[data-window]').each((_, element) => { const name = $(element).data('window'); windowManager.addEventListener(name, 'open', () => { $(element).addClass('active'); }); windowManager.addEventListener(name, 'close', () => { $(element).removeClass('active'); }); }); const pageManager = new ContainerManager('main', true); const pages = { _current: 'general', levels: [], init() { return this.processor(); }, bind() { binds.aPage.bind((e) => { const data = JSON.parse(e.delegateTarget.dataset.add || 'null'); const name = e.delegateTarget.dataset.page; if (pageManager.currentContainer === name && data == null) { return pageManager.close(); } return pageManager.open(name, data); }, 'pages.bind'); }, processor() { this.levels = url.address.split('/'); switch (this.levels[0]) { case 'player': return pageManager.open('player'); case 'general': default: return pageManager.open('general'); } }, array: { general: { state: null, shikimori: shikimori.animes(), main() { $('main>.vcontainer').html(''); this.scrollUpdate(); }, load(page = 1) { this.state = 'loading'; this.shikimori.page(page).then((r) => { const results = $('.page.general div.all'); if (r.length === 0) { results.append(collections.noResults('Не удалось загрузить больше.')); return; } r.forEach((t) => { results.append(collections.title(t, collections.titleExtended)); }); App.loaded(); this.state = null; binds.lazyLoad.update(); binds.titleHigh.update(); binds.aWindow.update(); binds.titleHover.updateSelector(); }, () => { $('.page.general>.vcontainer').html(collections.noResults()); }); }, scrollUpdate() { let pageOffset = 1; this.load(pageOffset); workers.console.log('Load titles in general page...'); binds.bodyScroll.bind(({ target }) => { pageOffset += 1; const toBottom = target.children[0].clientHeight - target.scrollTop - target.clientHeight; if (toBottom < 500 && this.state !== 'loading') this.load(pageOffset); }, 'pages.general._scrollUpdate'); }, }, player: { _current: 'kodik', _title: undefined, _data: undefined, _state: undefined, main(data) { App.loaded(); this.update(data); }, load() { workers.kodik(); }, update(data) { if (this._title !== pages.levels[1].toString() + pages.levels[2].toString()) { this.setPlayer(pages.levels[1], pages.levels[2]); } if (data != null && data.action === 'reload') { this.players.kodik.reload(); } }, setPlayer(name, id) { workers.console.log(`Running player - ${name}:${id}`); this._title = name.toString() + id.toString(); shikimori.animes().reset().ids([Number(id)]).then((r) => { this._data = r[0]; App.title(`KDK - ${this._data.russian || this._data.name}`); $('.page.player .ui-bar').html(collections.playerBar(this._data)); const history = workers.metric.history(); if (history != null) { history.push({ id: this._data.id, name: this._data.name, russian: this._data.russian, image: { preview: this._data.image.preview, original: this._data.image.original }, }); } $('header>nav>a.player-show').show() .attr('title', this._data.russian || this._data.name || 'Плеер') .attr('data-add', JSON.stringify(this._data || '{}')); binds.aWindow.update(); binds.aPage.update(); }); $(`.page.player .player-${this._current}`).hide(); const players = { kodik: $('.page.player .player-kodik'), }; switch (name) { case 'animetop': break; case 'shikimori': players.kodik.show(); this.players.kodik.setShikimori(id); break; case 'kodik': default: players.kodik.show(); this.players.kodik.setVideo(id); break; } }, players: { kodik: { setShikimori(id) { binds.kodikBackend.search({ shikimori_id: Number(id) }).then((d) => { if (d.results.length === 0) { alert('anime not found'); pageManager.open('general'); return; } $('.page.player .player-kodik').attr('src', d.results[0].link); }); }, setVideo(id) { }, reload() { const iframe = $('.page.player .player-kodik').get(0); iframe.src = iframe.src; }, }, }, }, }, }; Object.entries(pages.array).forEach(([pageName, actions]) => { let isWorking = false; pageManager.register(pageName, `.page.${pageName}`); pageManager.addEventListener(pageName, 'open', (data) => { if (isWorking) { if (pages.array[pageName].update != null) { pages.array[pageName].update(data); } } else { if (pages.array[pageName].main != null) { pages.array[pageName].main(data); } isWorking = true; } windowManager.close(); }); pageManager.addEventListener(pageName, 'close', () => { if (pages.array[pageName].main == null) { return; } if (pages.array[pageName].unload != null) { pages.array[pageName].unload(); } }); }); $('header nav > a[data-page]').each((_, element) => { const name = $(element).data('page'); pageManager.addEventListener(name, 'open', () => { $(element).addClass('active'); }); pageManager.addEventListener(name, 'close', () => { $(element).removeClass('active'); }); }); const workers = { ui() { binds.hover.bind(function (e) { $(this).css({ '--x': `${e.offsetX}px`, '--y': `${e.offsetY}px`, }); }, 'jq.custom'); pages.init(); pages.bind(); binds.titleHigh.bind(function (e) { $(this).css({ '--x': e.offsetX, '--y': e.offsetY, }); }, 'worker.ui.title'); }, search: { dom: { results: $('.window.search .results'), }, search(text) { this.dom.results.html(collections.loadingResults()); shikimori.animes().status('').order('').search(text) .then((r) => { this.dom.results.html(''); if (r.length === 0) $('.window.search .results').html(collections.noResults()); r.forEach((t) => { this.dom.results.append(collections.title(t)); }); binds.titleHover.updateSelector(); binds.titleHigh.update(); binds.lazyLoad.update(); binds.aWindow.update(); }); }, }, clock: { set() { const date = new Date(); $('.ui-clock').text(`${date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`}:${date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`}`); window.clock = setInterval(() => { const date = new Date(); const elem = $('.ui-clock'); const format = `${date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`}:${date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`}`; if (elem.text() !== format) elem.text(format); }, 1e3); }, }, hotkeys() { hotkeys('esc,alt+o,alt+s,alt+c', (e, h) => { switch (h.key) { case 'esc': if (windows.current !== null) windowManager.close(); else pageManager.open('general'); break; case 'alt+o': windowManager.open('config'); break; case 'alt+s': windowManager.open('search'); break; case 'alt+c': windowManager.open('console'); break; default: break; } }); }, metric: { state: () => binds.config.config.collectInformation, init() { }, history() { return workers.metric.state() ? binds.sHistory : false; }, favorite() { return workers.metric.state() ? binds.sFavorite : false; }, collections() { return workers.metric.state() ? binds.sCollections : false; }, }, hoverMenu() { let tile; binds.titleHover.addEventListener('click', (id) => { switch (Number(id)) { case 0: { url.set(`#player/shikimori/${tile.id}`, {}); break; } case 1: { windowManager.open('title-info', tile); break; } case 2: { windowManager.open('collections', { title: tile }); break; } case 3: { windowManager.open('collections', { remove: tile.id }); break; } case 4: { navigator.clipboard.writeText(tile.name); break; } case 5: { window.open(shikimori.origin + tile.url); break; } default: break; } }); binds.titleHover.addEventListener('open', (e) => { const { add = '{}' } = e.currentTarget.dataset; tile = JSON.parse(add); }); }, styles() { const f = () => { const hd = document.querySelector('header'); const cs = document.querySelector('#--headerCeilSize').offsetHeight; document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`); hd.children[0].style.height = ((document.querySelectorAll('header>nav>a[display=true]').length + 4) * cs >= hd.offsetHeight) ? 'fit-content' : '100%'; }; window.addEventListener('resize', f); readyDOM(f); }, storage: { init() { this.size._build(); }, size: { _build() { Object.keys(this._old).forEach((k) => this[`old${k}`] = () => this._Processor(this._old[k])); Object.keys(this._list).forEach((k) => this[k] = () => this._Processor(this._list[k])); }, _Processor(name) { const list = localStorage.getItem(name); return list === null ? 0 : new Blob([list]).size; }, _old: { Favorite: 'lsfavorite', Cache: 'cache', History: 'lshistory', }, _list: { favorite: 'sFavorite', history: 'sHistory', }, all() { let size = this.self(); Object.values(this._old).forEach((v) => size += this._Processor(v)); Object.values(this.list).forEach((l) => size += this._Processor(l)); return size; }, self() { let file; let size = 0; App.info.sources.forEach((s) => { try { file = storage.get(s.name); } catch (e) { console.groupCollapsed('Unable to load file'); console.error(e); return console.groupEnd(); } size += new Blob([file]).size; }); return size; }, }, _data() { const data = { system: { size: this.size.self(), description: 'Файлы сайта' }, oldcache: { size: this.size.oldCache(), description: 'Старые данные кэша' }, oldhistory: { size: this.size.oldHistory(), description: 'Старая история просмотров' }, oldfavorite: { size: this.size.oldFavorite(), description: 'Старый список любимого' }, favorite: { size: this.size.favorite(), description: 'Список любимого' }, history: { size: this.size.history(), description: 'История просмотров' }, }; const all = Object.values(data).reduce((a, b) => a + b.size, 0); Object.values(data).forEach((value) => { value.part = value.size / all; value.size = this._convert(value.size); }); return [this._convert(all), data]; }, _convert(bytes, digits = 2) { const sizes = [ [2 ** 0, 'B'], [2 ** 10, 'KB'], [2 ** 20, 'MB'], [2 ** 30, 'GB'], ]; const [measureSize, measureUnit] = sizes.reduce((prev, curr) => ( (Math.abs(curr[0] - bytes) < Math.abs(prev[0] - bytes)) ? curr : prev)); const delimiter = 10 ** digits; return `${Math.ceil((bytes / measureSize) * delimiter) / delimiter}${measureUnit}`; }, }, shikimori: { linkTitle(kind) { switch (kind) { case 'wikipedia': return 'Wikipedia'; case 'anime_news_network': return 'Anime News Network'; case 'myanimelist': return 'MyAnimeList'; case 'anime_db': return 'AniDB'; case 'world_art': return 'World Art'; case 'twitter': return 'Twitter'; case 'official_site': return 'Официальный сайт'; case 'kage_project': return 'Kage Project'; case 'kinopoisk': return 'Кинопоиск'; case 'ruranobe': return 'РуРанобэ'; case 'readmanga': return 'ReadManga'; case 'novelupdates': return 'NovelUpdates'; case 'mangaupdates': return 'MangaUpdates'; case 'mangafox': return 'MangaFox'; case 'mangachan': return 'Манга-тян'; case 'mangahub': return 'Mangahub'; case 'smotret_anime': return 'Смотреть аниме'; case 'youtube_channel': return 'YouTube'; case 'novel_tl': return 'Novel.tl'; case 'mangalib': return 'MangaLib'; case 'ranobelib': return 'RanobeLib'; case 'remanga': return 'ReManga'; case 'mangadex': return 'MangaDex'; case 'more_tv': return 'more.tv'; case 'baike_baidu_wiki': return 'Baike Baidu Wiki'; case 'namu_wiki': return 'Namu Wiki'; } }, }, console: { log(text, col = '#fff') { const log = $('.window.console .result'); log.append(collections.consoleRow(text, col)); $('.window.console').scrollTop(log.height()); }, error(text) { return this.log(text, '#f77'); }, warn(text) { return this.log(text, '#ff7'); }, }, kodik() { binds.kodik.on('current_episode', (d) => { pages.array.player._state = d; }).on('time_update', (data) => { const history = workers.metric.history(); const { id } = pages.array.player._data; if (history == null || id == null) { return false; } const item = history.get(id); Object.assign(item, { episodes: { ...item.episodes || {}, [pages.array.player._state.episode]: data, }, }); history.set(id, item); }); }, redirect() { url.onRedirect((st) => { console.groupCollapsed(`Redirected to #${url.address}`); console.log(st); console.groupEnd(); workers.console.log(`Page changed to ${url.address}`); pages.processor(); }); }, }; // config load binds.config.change(config); // ui preload workers.ui(); workers.styles(); workers.hoverMenu(); workers.clock.set(); // internal init workers.storage.init(); workers.hotkeys(); workers.redirect(); // app link windows.bind(); App.chrome.sw(); // window.KDK = { url, }; // debug info workers.console.log('Start loading...'); workers.console.log(`Current version: ${App.info.update.version}(${App.info.update.release})`); console.log(`KDK Anime ${App.info.update.version}(${App.info.update.release})`); });