Version 5.1 - All the GraphQL #32
@ -181,6 +181,7 @@ $routes = [
|
|||||||
'user_info' => [
|
'user_info' => [
|
||||||
'path' => '/user/{username}',
|
'path' => '/user/{username}',
|
||||||
'controller' => 'user',
|
'controller' => 'user',
|
||||||
|
'action' => 'about',
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'username' => '.*?'
|
'username' => '.*?'
|
||||||
]
|
]
|
||||||
|
@ -37,28 +37,30 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"consolidation/robo": "~1.0",
|
"consolidation/robo": "~1.0",
|
||||||
|
"filp/whoops": "^2.1",
|
||||||
"henrikbjorn/lurker": "^1.1.0",
|
"henrikbjorn/lurker": "^1.1.0",
|
||||||
"pdepend/pdepend": "^2.2",
|
"pdepend/pdepend": "^2.2",
|
||||||
"phploc/phploc": "^4.0",
|
"phploc/phploc": "^4.0",
|
||||||
"phpmd/phpmd": "^2.4",
|
"phpmd/phpmd": "^2.4",
|
||||||
"phpstan/phpstan": "^0.9.1",
|
"phpstan/phpstan": "^0.10.5",
|
||||||
"phpunit/phpunit": "^6.0",
|
"phpunit/phpunit": "^7.4.3",
|
||||||
"roave/security-advisories": "dev-master",
|
"roave/security-advisories": "dev-master",
|
||||||
"robmorgan/phinx": "^0.10.6",
|
"robmorgan/phinx": "^0.10.6",
|
||||||
"sebastian/phpcpd": "^3.0",
|
"sebastian/phpcpd": "^4.1.0",
|
||||||
"spatie/phpunit-snapshot-assertions": "^1.2.0",
|
"spatie/phpunit-snapshot-assertions": "^1.2.0",
|
||||||
"squizlabs/php_codesniffer": "^3.2.2",
|
"squizlabs/php_codesniffer": "^3.2.2",
|
||||||
"symfony/var-dumper": "^4.0.1",
|
"symfony/var-dumper": "^4.0.1",
|
||||||
"theseer/phpdox": "^0.11.0",
|
"theseer/phpdox": "*"
|
||||||
"filp/whoops": "^2.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vendor/bin/robo build",
|
"build": "vendor/bin/robo build",
|
||||||
"build:css": "cd public && npm run build && cd ..",
|
"build:css": "cd public && npm run build:css && cd ..",
|
||||||
|
"build:js": "cd public && npm run build:js && cd ..",
|
||||||
"clean": "vendor/bin/robo clean",
|
"clean": "vendor/bin/robo clean",
|
||||||
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
||||||
"phpstan": "phpstan analyse -l 4 -c phpstan.neon src tests ./console index.php",
|
"phpstan": "phpstan analyse -l 4 -c phpstan.neon src tests ./console index.php",
|
||||||
"watch:css": "cd public && npm run watch",
|
"watch:css": "cd public && npm run watch:css",
|
||||||
|
"watch:js": "cd public && npm run watch:js",
|
||||||
"test": "vendor/bin/phpunit"
|
"test": "vendor/bin/phpunit"
|
||||||
},
|
},
|
||||||
"scripts-descriptions": {
|
"scripts-descriptions": {
|
||||||
|
27
public/js/scripts-authed.min.js
vendored
27
public/js/scripts-authed.min.js
vendored
@ -7,17 +7,18 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
|
|||||||
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var 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=
|
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var 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}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
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}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
||||||
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
||||||
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+
|
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=
|
||||||
item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+
|
x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+
|
||||||
item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;
|
x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+
|
||||||
var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+
|
item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+
|
||||||
x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});
|
item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+
|
||||||
return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,
|
'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},
|
||||||
function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,
|
function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=
|
||||||
data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status="completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+
|
AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status=
|
||||||
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:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});
|
"completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var 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]);
|
||||||
var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(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,function(e){var query=
|
AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,
|
||||||
encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",
|
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,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=
|
||||||
parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status===
|
AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||
|
||||||
"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:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
|
completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){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:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
|
||||||
//# sourceMappingURL=scripts-authed.min.js.map
|
//# sourceMappingURL=scripts-authed.min.js.map
|
||||||
|
File diff suppressed because one or more lines are too long
2
public/js/scripts.min.js
vendored
2
public/js/scripts.min.js
vendored
@ -7,5 +7,5 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
|
|||||||
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var 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=
|
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var 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}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
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}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
||||||
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
||||||
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})})})();
|
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)})})();
|
||||||
//# sourceMappingURL=scripts.min.js.map
|
//# sourceMappingURL=scripts.min.js.map
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,10 +1,10 @@
|
|||||||
import './base/events.js';
|
import './base/events.js';
|
||||||
|
|
||||||
/* if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('/sw.js').then(reg => {
|
navigator.serviceWorker.register('/sw.js').then(reg => {
|
||||||
console.log('Service worker registered', reg.scope);
|
console.log('Service worker registered', reg.scope);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('Failed to register service worker', error);
|
console.error('Failed to register service worker', error);
|
||||||
});
|
});
|
||||||
} */
|
}
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
|
|||||||
// Background is the first color by default
|
// Background is the first color by default
|
||||||
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
||||||
imagefill($img, 0, 0, $fillColor);
|
imagefill($img, 0, 0, $fillColor);
|
||||||
|
|
||||||
$textColor = imagecolorallocate($img, 64, 64, 64);
|
$textColor = imagecolorallocate($img, 64, 64, 64);
|
||||||
|
|
||||||
imagealphablending($img, TRUE);
|
imagealphablending($img, TRUE);
|
||||||
@ -280,11 +280,11 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
|
|||||||
imagesavealpha($img, TRUE);
|
imagesavealpha($img, TRUE);
|
||||||
imagepng($img, $path . '/placeholder.png', 9);
|
imagepng($img, $path . '/placeholder.png', 9);
|
||||||
imagedestroy($img);
|
imagedestroy($img);
|
||||||
|
|
||||||
$pngImage = imagecreatefrompng($path . '/placeholder.png');
|
$pngImage = imagecreatefrompng($path . '/placeholder.png');
|
||||||
imagealphablending($pngImage, TRUE);
|
imagealphablending($pngImage, TRUE);
|
||||||
imagesavealpha($pngImage, TRUE);
|
imagesavealpha($pngImage, TRUE);
|
||||||
|
|
||||||
imagewebp($pngImage, $path . '/placeholder.webp');
|
imagewebp($pngImage, $path . '/placeholder.webp');
|
||||||
|
|
||||||
imagedestroy($pngImage);
|
imagedestroy($pngImage);
|
||||||
|
@ -1,198 +1,198 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* Hummingbird Anime List Client
|
* Hummingbird Anime List Client
|
||||||
*
|
*
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
*
|
*
|
||||||
* PHP version 7.1
|
* PHP version 7.1
|
||||||
*
|
*
|
||||||
* @package HummingbirdAnimeClient
|
* @package HummingbirdAnimeClient
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2018 Timothy J. Warren
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
* @version 4.1
|
* @version 4.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
use function Aviat\AnimeClient\createPlaceholderImage;
|
use function Aviat\AnimeClient\createPlaceholderImage;
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
|
use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\View\HtmlView;
|
use Aviat\Ion\View\HtmlView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for handling routes that don't fit elsewhere
|
* Controller for handling routes that don't fit elsewhere
|
||||||
*/
|
*/
|
||||||
final class Images extends BaseController {
|
final class Images extends BaseController {
|
||||||
/**
|
/**
|
||||||
* Get image covers from kitsu
|
* Get image covers from kitsu
|
||||||
*
|
*
|
||||||
* @param string $type The category of image
|
* @param string $type The category of image
|
||||||
* @param string $file The filename to look for
|
* @param string $file The filename to look for
|
||||||
* @param bool $display Whether to output the image to the server
|
* @param bool $display Whether to output the image to the server
|
||||||
* @throws \Aviat\Ion\Di\ContainerException
|
* @throws \Aviat\Ion\Di\ContainerException
|
||||||
* @throws \Aviat\Ion\Di\NotFoundException
|
* @throws \Aviat\Ion\Di\NotFoundException
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @throws \TypeError
|
* @throws \TypeError
|
||||||
* @throws \Error
|
* @throws \Error
|
||||||
* @throws \Throwable
|
* @throws \Throwable
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function cache(string $type, string $file, $display = TRUE): void
|
public function cache(string $type, string $file, $display = TRUE): void
|
||||||
{
|
{
|
||||||
$currentUrl = $this->request->getUri()->__toString();
|
$currentUrl = $this->request->getUri()->__toString();
|
||||||
|
|
||||||
$kitsuUrl = 'https://media.kitsu.io/';
|
$kitsuUrl = 'https://media.kitsu.io/';
|
||||||
$fileName = str_replace('-original', '', $file);
|
$fileName = str_replace('-original', '', $file);
|
||||||
[$id, $ext] = explode('.', basename($fileName));
|
[$id, $ext] = explode('.', basename($fileName));
|
||||||
|
|
||||||
$baseSavePath = $this->config->get('img_cache_path');
|
$baseSavePath = $this->config->get('img_cache_path');
|
||||||
|
|
||||||
// Kitsu doesn't serve webp, but for most use cases,
|
// Kitsu doesn't serve webp, but for most use cases,
|
||||||
// jpg is a safe assumption
|
// jpg is a safe assumption
|
||||||
$tryJpg = ['anime','characters','manga','people'];
|
$tryJpg = ['anime','characters','manga','people'];
|
||||||
if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
|
if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
|
||||||
{
|
{
|
||||||
$ext = 'jpg';
|
$ext = 'jpg';
|
||||||
$currentUrl = str_replace('webp', 'jpg', $currentUrl);
|
$currentUrl = str_replace('webp', 'jpg', $currentUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
$typeMap = [
|
$typeMap = [
|
||||||
'anime' => [
|
'anime' => [
|
||||||
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
||||||
'width' => 220,
|
'width' => 220,
|
||||||
'height' => 312,
|
'height' => 312,
|
||||||
],
|
],
|
||||||
'avatars' => [
|
'avatars' => [
|
||||||
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
||||||
'width' => null,
|
'width' => null,
|
||||||
'height' => null,
|
'height' => null,
|
||||||
],
|
],
|
||||||
'characters' => [
|
'characters' => [
|
||||||
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
||||||
'width' => 225,
|
'width' => 225,
|
||||||
'height' => 350,
|
'height' => 350,
|
||||||
],
|
],
|
||||||
'manga' => [
|
'manga' => [
|
||||||
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
||||||
'width' => 220,
|
'width' => 220,
|
||||||
'height' => 312,
|
'height' => 312,
|
||||||
],
|
],
|
||||||
'people' => [
|
'people' => [
|
||||||
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
||||||
'width' => null,
|
'width' => null,
|
||||||
'height' => null,
|
'height' => null,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$imageType = $typeMap[$type] ?? NULL;
|
$imageType = $typeMap[$type] ?? NULL;
|
||||||
|
|
||||||
if (NULL === $imageType)
|
if (NULL === $imageType)
|
||||||
{
|
{
|
||||||
$this->getPlaceholder($baseSavePath, 200, 200);
|
$this->getPlaceholder($baseSavePath, 200, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$kitsuUrl .= $imageType['kitsuUrl'];
|
$kitsuUrl .= $imageType['kitsuUrl'];
|
||||||
$width = $imageType['width'];
|
$width = $imageType['width'];
|
||||||
$height = $imageType['height'];
|
$height = $imageType['height'];
|
||||||
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
||||||
|
|
||||||
$promise = (new HummingbirdClient)->request($kitsuUrl);
|
$promise = (new HummingbirdClient)->request($kitsuUrl);
|
||||||
$response = wait($promise);
|
$response = wait($promise);
|
||||||
|
|
||||||
if ($response->getStatus() !== 200)
|
if ($response->getStatus() !== 200)
|
||||||
{
|
{
|
||||||
// Try a few different file types before giving up
|
// Try a few different file types before giving up
|
||||||
// webm => jpg => png => gif
|
// webm => jpg => png => gif
|
||||||
$nextType = [
|
$nextType = [
|
||||||
'jpg' => 'png',
|
'jpg' => 'png',
|
||||||
'png' => 'gif',
|
'png' => 'gif',
|
||||||
];
|
];
|
||||||
|
|
||||||
if (array_key_exists($ext, $nextType))
|
if (array_key_exists($ext, $nextType))
|
||||||
{
|
{
|
||||||
$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
|
$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
|
||||||
$this->redirect($newUrl, 303);
|
$this->redirect($newUrl, 303);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($display)
|
if ($display)
|
||||||
{
|
{
|
||||||
$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
|
$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
|
createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = wait($response->getBody());
|
$data = wait($response->getBody());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[$origWidth] = getimagesizefromstring($data);
|
[$origWidth] = getimagesizefromstring($data);
|
||||||
$gdImg = imagecreatefromstring($data);
|
$gdImg = imagecreatefromstring($data);
|
||||||
$resizedImg = imagescale($gdImg, $width ?? $origWidth);
|
$resizedImg = imagescale($gdImg, $width ?? $origWidth);
|
||||||
|
|
||||||
if ($ext === 'gif')
|
if ($ext === 'gif')
|
||||||
{
|
{
|
||||||
file_put_contents("{$filePrefix}.gif", $data);
|
file_put_contents("{$filePrefix}.gif", $data);
|
||||||
imagepalletetotruecolor($gdImg);
|
\imagepalletetotruecolor($gdImg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the webp versions
|
// save the webp versions
|
||||||
imagewebp($gdImg, "{$filePrefix}-original.webp");
|
imagewebp($gdImg, "{$filePrefix}-original.webp");
|
||||||
imagewebp($resizedImg, "{$filePrefix}.webp");
|
imagewebp($resizedImg, "{$filePrefix}.webp");
|
||||||
|
|
||||||
// save the scaled jpeg file
|
// save the scaled jpeg file
|
||||||
imagejpeg($resizedImg, "{$filePrefix}.jpg");
|
imagejpeg($resizedImg, "{$filePrefix}.jpg");
|
||||||
|
|
||||||
// And the original
|
// And the original
|
||||||
file_put_contents("{$filePrefix}-original.jpg", $data);
|
file_put_contents("{$filePrefix}-original.jpg", $data);
|
||||||
|
|
||||||
imagedestroy($gdImg);
|
imagedestroy($gdImg);
|
||||||
imagedestroy($resizedImg);
|
imagedestroy($resizedImg);
|
||||||
|
|
||||||
if ($display)
|
if ($display)
|
||||||
{
|
{
|
||||||
$contentType = ($ext === 'webp')
|
$contentType = ($ext === 'webp')
|
||||||
? "image/webp"
|
? "image/webp"
|
||||||
: $response->getHeader('content-type')[0];
|
: $response->getHeader('content-type')[0];
|
||||||
|
|
||||||
$outputFile = (strpos($file, '-original') !== FALSE)
|
$outputFile = (strpos($file, '-original') !== FALSE)
|
||||||
? "{$filePrefix}-original.{$ext}"
|
? "{$filePrefix}-original.{$ext}"
|
||||||
: "{$filePrefix}.{$ext}";
|
: "{$filePrefix}.{$ext}";
|
||||||
|
|
||||||
header("Content-Type: {$contentType}");
|
header("Content-Type: {$contentType}");
|
||||||
echo file_get_contents($outputFile);
|
echo file_get_contents($outputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a placeholder for a missing image
|
* Get a placeholder for a missing image
|
||||||
*
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @param int|null $width
|
* @param int|null $width
|
||||||
* @param int|null $height
|
* @param int|null $height
|
||||||
*/
|
*/
|
||||||
private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
|
private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
|
||||||
{
|
{
|
||||||
$height = $height ?? $width;
|
$height = $height ?? $width;
|
||||||
|
|
||||||
$filename = $path . '/placeholder.png';
|
$filename = $path . '/placeholder.png';
|
||||||
|
|
||||||
if ( ! file_exists($path . '/placeholder.png'))
|
if ( ! file_exists($path . '/placeholder.png'))
|
||||||
{
|
{
|
||||||
createPlaceholderImage($path, $width, $height);
|
createPlaceholderImage($path, $width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
header('Content-Type: image/png');
|
header('Content-Type: image/png');
|
||||||
echo file_get_contents($filename);
|
echo file_get_contents($filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aviat\Ion\ConfigInterface;
|
|
||||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,12 +41,6 @@ class Util {
|
|||||||
'me'
|
'me'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* The config manager
|
|
||||||
* @var ConfigInterface
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the Util class
|
* Set up the Util class
|
||||||
*
|
*
|
||||||
@ -58,7 +51,6 @@ class Util {
|
|||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$this->setContainer($container);
|
$this->setContainer($container);
|
||||||
$this->config = $container->get('config');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
31
sw.js
31
sw.js
@ -5,36 +5,17 @@ async function fromCache (request) {
|
|||||||
return await cache.match(request);
|
return await cache.match(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fromNetwork (request) {
|
async function updateCache (request) {
|
||||||
return await fetch(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update (request) {
|
|
||||||
const cache = await caches.open(CACHE_NAME);
|
const cache = await caches.open(CACHE_NAME);
|
||||||
const response = await fetch(request);
|
const response = await fetch(request);
|
||||||
|
|
||||||
if (request.url.includes('/public/images/')) {
|
if (request.url.includes('/public/images/')) {
|
||||||
console.log('Saving to cache: ', request.url);
|
|
||||||
await cache.put(request, response.clone());
|
await cache.put(request, response.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* function refresh (response) {
|
|
||||||
return self.clients.matchAll().then(clients => {
|
|
||||||
clients.forEach(client => {
|
|
||||||
const message = {
|
|
||||||
type: 'refresh',
|
|
||||||
url: response.url,
|
|
||||||
eTag: response.headers.get('ETag')
|
|
||||||
};
|
|
||||||
|
|
||||||
client.postMessage(JSON.stringify(message));
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} */
|
|
||||||
|
|
||||||
self.addEventListener('install', event => {
|
self.addEventListener('install', event => {
|
||||||
console.log('Public Folder Worker installed');
|
console.log('Public Folder Worker installed');
|
||||||
|
|
||||||
@ -55,8 +36,8 @@ self.addEventListener('install', event => {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('activate', event => {
|
self.addEventListener('activate', () => {
|
||||||
console.log('Public Folder Worker activated');
|
console.info('Public Folder Worker activated');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull css, images, and javascript from cache
|
// Pull css, images, and javascript from cache
|
||||||
@ -71,11 +52,7 @@ self.addEventListener('fetch', event => {
|
|||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
event.respondWith(cached);
|
event.respondWith(cached);
|
||||||
} else {
|
} else {
|
||||||
event.respondWith(fromNetwork(event.request));
|
event.respondWith(updateCache(event.request));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
event.waitUntil(
|
|
||||||
update(event.request)
|
|
||||||
);
|
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user