diff --git a/dist/scripts/docker-registry-ui-static.js b/dist/scripts/docker-registry-ui-static.js
index ef231ac..e3adbd5 100644
--- a/dist/scripts/docker-registry-ui-static.js
+++ b/dist/scripts/docker-registry-ui-static.js
@@ -15,4 +15,4 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,this._events={},this._headers={}}Http.prototype.addEventListener=function(t,e){this._events[t]=e;const r=this;switch(t){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){const t=new XMLHttpRequest;for(key in t.open(r._method,r._url),r._events)t.addEventListener(key,r._events[key]);for(key in r._headers)t.setRequestHeader(key,r._headers[key]);t.withCredentials=!0,t.hasHeader=Http.hasHeader,t.getErrorMessage=Http.getErrorMessage,t.send()}else e.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&e.bind(this)()});break;default:r.oReq.addEventListener(t,function(){e.bind(this)()})}},Http.prototype.setRequestHeader=function(t,e){this.oReq.setRequestHeader(t,e),this._headers[t]=e},Http.prototype.open=function(t,e){this._method=t,this._url=e,this.oReq.open(t,e)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(t){return this.getAllResponseHeaders().split("\n").some(function(e){return new RegExp("^"+t+":","i").test(e)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?this.withCredentials&&!this.hasHeader("Access-Control-Allow-Credentials")?"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `"+registryUI.url()+"` is therefore not allowed access.":"An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `"+window.location.origin+"`":"Incorrect server endpoint."};var registryUI={url:function(){var t="${URL}";return t||((t=window.location.origin+window.location.pathname).endsWith("/")?t.substr(0,t.length-1):t)},name:function(){const t="${REGISTRY_TITLE}";return t},pullUrl:"${PULL_URL}",isImageRemoveActivated:!0,catalog:{},taglist:{},taghistory:{}};window.addEventListener("DOMContentLoaded",function(){riot.mount("*")}),registryUI.bytesToSize=function(t){if(null==t||isNaN(t))return"?";if(0==t)return"0 Byte";const e=parseInt(Math.floor(Math.log(t)/Math.log(1024)));return Math.ceil(t/Math.pow(1024,e))+" "+["Bytes","KB","MB","GB","TB"][e]},registryUI.dateFormat=function(t){if(void 0===t)return"";const e=["a second","seconds","a minute","minutes","an hour","hours","a day","days","a month","months","a year","years"],r=[1,60,3600,86400,2592e3,31104e3,1/0],i=(new Date-t)/1e3;for(var a=0;a=i)return e[2*a];if(r[a+1]>i)return Math.floor(i/r[a])+" "+e[2*a+1]}},registryUI.getHistoryIcon=function(t){switch(t){case"architecture":return"memory";case"created":return"event";case"docker_version":return"";case"os":return"developer_board";case"Cmd":return"launch";case"Entrypoint":return"input";case"Env":return"notes";case"Labels":return"label";case"User":return"face";case"Volumes":return"storage";case"WorkingDir":return"home";case"author":return"account_circle";case"id":case"digest":return"settings_ethernet";case"created_by":return"build";case"size":return"get_app";case"ExposedPorts":return"router"}},registryUI.getPage=function(t,e,r){return r||(r=100),t?t.slice((e-1)*r,r*e):[]},registryUI.getNumPages=function(t,e){return e||(e=100),t?Math.trunc(t.length/e)+1:0},registryUI.getPageLabels=function(t,e){var r=[];if(1===e)return r;1!==t&&e>=10&&(r.push({icon:"first_page",page:1}),r.push({icon:"chevron_left",page:t-1}));for(var i=Math.round(Math.max(1,Math.min(t-5,e-10+1))),a=i;a10,"space-right":t===e&&e>10});return t!==e&&e>=10&&(r.push({icon:"chevron_right",page:t+1}),r.push({icon:"last_page",page:e})),r},registryUI.updateQueryString=function(t){var e="";for(var r in t)void 0!==t[r]&&(e+=(e.length>0?"&":"?")+r+"="+t[r]);history.pushState(null,"",e+window.location.hash)},registryUI.stripHttps=function(t){return t?t.replace(/^https?:\/\//,""):""},riot.tag2("app",'
',"","",function(t){registryUI.catalog.instance=this,registryUI.catalog.display=function(){registryUI.catalog.repositories=[];const t=new Http;t.addEventListener("load",function(){registryUI.catalog.repositories=[],200==this.status?(registryUI.url()||(registryUI._url=window.location.origin+window.location.pathname.replace(/\/+$/,"")),registryUI.catalog.repositories=JSON.parse(this.responseText).repositories||[],registryUI.catalog.repositories.sort(),registryUI.catalog.length=registryUI.catalog.repositories.length,registryUI.catalog.repositories=registryUI.catalog.repositories.reduce(function(t,e){const r=e.indexOf("/");if(r>0){const i=e.substring(0,r)+"/";return 0!=t.length&&t[t.length-1].repo==i||t.push({repo:i,images:[]}),t[t.length-1].images.push(e),t}return t.push(e),t},[])):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText)}),t.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.catalog.repositories=[]}),t.addEventListener("loadend",function(){registryUI.catalog.loadend=!0,registryUI.catalog.instance.update()}),t.open("GET",registryUI.url()+"/v2/_catalog?n=100000"),t.send()},registryUI.catalog.display()}),riot.tag2("copy-to-clipboard",' content_copy',"","",function(t){this.dockerCmd="docker pull "+registryUI.cleanName()+"/"+t.image.name+":"+t.image.tag,this.copy=function(){const t=this.refs.input;t.style.display="block",t.select(),document.execCommand("copy"),t.style.display="none",registryUI.snackbar("`"+this.dockerCmd+"` has been copied to clipboard.")}}),riot.tag2("image-date",'
',"","",function(t){this.on("updated",function(){this.tags["material-button"]&&(Array.isArray(this.tags["material-button"])?this.tags["material-button"]:[this.tags["material-button"]]).forEach(function(t){t.root.onclick=function(){registryUI.taglist.instance.trigger("page-update",t.p.page)}})})}),riot.tag2("remove-image",'delete',"","",function(t){const e=this;this.on("updated",function(){}),this.on("updated",function(){e.multiDelete!=e.opts.multiDelete&&(this.tags["material-button"]&&(this.delete=this.tags["material-button"].root.onclick=function(t){const r=e.opts.image.name,i=e.opts.image.tag,a=new Http;a.addEventListener("loadend",function(){if(registryUI.taglist.go(r),200==this.status){if(!this.hasHeader("Docker-Content-Digest"))return void registryUI.errorSnackbar("You need to add Access-Control-Expose-Headers: ['Docker-Content-Digest'] in your server configuration.");const e=this.getResponseHeader("Docker-Content-Digest"),a=new Http;a.addEventListener("loadend",function(){200==this.status||202==this.status?(registryUI.taglist.display(),registryUI.snackbar("Deleting "+r+":"+i+" image. Run `registry garbage-collect config.yml` on your registry")):404==this.status?t||registryUI.errorSnackbar("Digest not found"):registryUI.snackbar(this.responseText)}),a.open("DELETE",registryUI.url()+"/v2/"+r+"/manifests/"+e),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.addEventListener("error",function(){registryUI.errorSnackbar("An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE'].")}),a.send()}else 404==this.status?registryUI.errorSnackbar("Manifest for "+r+":"+i+" not found"):registryUI.snackbar(this.responseText)}),a.open("HEAD",registryUI.url()+"/v2/"+r+"/manifests/"+i),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.send()}),this.tags["material-checkbox"]&&(!this.opts.multiDelete&&this.tags["material-checkbox"].checked&&this.tags["material-checkbox"].toggle(),this.tags["material-checkbox"].on("toggle",function(){registryUI.taglist.instance.trigger("toggle-remove-image",this.checked)})),e.multiDelete=e.opts.multiDelete)})}),riot.tag2("tag-history-button",'history',"","",function(t){this.on("mount",function(){const t=this;this.refs.button.root.onclick=function(){registryUI.taghistory._image=t.opts.image,registryUI.taghistory.go(t.opts.image.name,t.opts.image.tag)}}),this.update()}),riot.tag2("tag-history-element",'
Tags of {registryUI.name() + \'/\' + registryUI.taglist.name}
{registryUI.taglist.tags.length} tags
Repository
Creation date
Size
Tag
History
delete
{image.name}
',"","",function(t){var e=registryUI.taglist.instance=this;e.page=registryUI.getPageQueryParam(),this.multiDelete=!1,this.toDelete=0,this.on("delete",function(){registryUI.isImageRemoveActivated&&this.multiDelete}),this.on("multi-delete",function(){registryUI.isImageRemoveActivated&&(this.multiDelete=!this.multiDelete)}),this.on("toggle-remove-image",function(t){t?this.toDelete++:this.toDelete--,this.toDelete<=1&&this.update()}),this.on("page-update",function(t){e.page=t<1?1:t,registryUI.updateQueryString(registryUI.getQueryParams({page:e.page})),this.toDelete=0,this.update()}),this._getRemoveImageTags=function(){var t=e.refs["taglist-tag"].tags["remove-image"];return t instanceof Array||(t=[t]),t},registryUI.taglist.bulkDelete=function(){e.multiDelete&&e.toDelete>0&&e._getRemoveImageTags().filter(function(t){return t.tags["material-checkbox"].checked}).forEach(function(t){t.delete(!0)})},this.on("update",function(){var t=this.refs["taglist-tag"].refs["remove-tag-checkbox"];t&&!t._toggle&&(t._toggle=t.toggle,t.toggle=function(t){t.altKey?e._getRemoveImageTags().filter(function(t){return!t.tags["material-checkbox"].checked}).forEach(function(t){t.tags["material-checkbox"].toggle()}):this._toggle()},t.on("toggle",function(){registryUI.taglist.instance.multiDelete=this.checked,registryUI.taglist.instance.update()}))}),registryUI.taglist.display=function(){if(registryUI.taglist.tags=[],"taglist"==route.routeName){const t=new Http;registryUI.taglist.instance.update(),t.addEventListener("load",function(){registryUI.taglist.tags=[],200==this.status?(registryUI.taglist.tags=JSON.parse(this.responseText).tags||[],registryUI.taglist.tags=registryUI.taglist.tags.map(function(t){return new registryUI.DockerImage(registryUI.taglist.name,t)}).sort(registryUI.DockerImage.compare),e.trigger("page-update",Math.min(e.page,registryUI.getNumPages(registryUI.taglist.tags)))):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText,!0)}),t.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.taglist.tags=[]}),t.addEventListener("loadend",function(){registryUI.taglist.loadend=!0,registryUI.taglist.instance.update()}),t.open("GET",registryUI.url()+"/v2/"+registryUI.taglist.name+"/tags/list"),t.send(),registryUI.taglist.asc=!0}},registryUI.taglist.display(),registryUI.taglist.instance.update(),registryUI.taglist.reverse=function(){registryUI.taglist.asc?(registryUI.taglist.tags.reverse(),registryUI.taglist.asc=!1):(registryUI.taglist.tags.sort(registryUI.DockerImage.compare),registryUI.taglist.asc=!0),registryUI.taglist.instance.update()}});
\ No newline at end of file
+function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,this._events={},this._headers={}}Http.prototype.getContentDigest=function(t){this.oReq.hasHeader("Docker-Content-Digest")?t(this.oReq.getResponseHeader("Docker-Content-Digest")):window.crypto&&window.TextEncoder&&crypto.subtle.digest("SHA-256",(new TextEncoder).encode(this.oReq.responseText)).then(function(e){t("sha256:"+Array.from(new Uint8Array(e)).map(function(t){return t.toString(16).padStart(2,"0")}).join(""))})},Http.prototype.addEventListener=function(t,e){this._events[t]=e;const i=this;switch(t){case"loadend":i.oReq.addEventListener("loadend",function(){if(401==this.status){const t=new XMLHttpRequest;for(key in t.open(i._method,i._url),i._events)t.addEventListener(key,i._events[key]);for(key in i._headers)t.setRequestHeader(key,i._headers[key]);t.withCredentials=!0,t.hasHeader=Http.hasHeader,t.getErrorMessage=Http.getErrorMessage,t.send()}else e.bind(this)()});break;case"load":i.oReq.addEventListener("load",function(){401!==this.status&&e.bind(this)()});break;default:i.oReq.addEventListener(t,function(){e.bind(this)()})}},Http.prototype.setRequestHeader=function(t,e){this.oReq.setRequestHeader(t,e),this._headers[t]=e},Http.prototype.open=function(t,e){this._method=t,this._url=e,this.oReq.open(t,e)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(t){return this.getAllResponseHeaders().split("\n").some(function(e){return new RegExp("^"+t+":","i").test(e)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?this.withCredentials&&!this.hasHeader("Access-Control-Allow-Credentials")?"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `"+registryUI.url()+"` is therefore not allowed access.":"An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `"+window.location.origin+"`":"Incorrect server endpoint."};var registryUI={url:function(){var t="${URL}";return t||((t=window.location.origin+window.location.pathname).endsWith("/")?t.substr(0,t.length-1):t)},name:function(){const t="${REGISTRY_TITLE}";return t},pullUrl:"${PULL_URL}",isImageRemoveActivated:!0,catalog:{},taglist:{},taghistory:{}};window.addEventListener("DOMContentLoaded",function(){riot.mount("*")}),registryUI.bytesToSize=function(t){if(null==t||isNaN(t))return"?";if(0==t)return"0 Byte";const e=parseInt(Math.floor(Math.log(t)/Math.log(1024)));return Math.ceil(t/Math.pow(1024,e))+" "+["Bytes","KB","MB","GB","TB"][e]},registryUI.dateFormat=function(t){if(void 0===t)return"";const e=["a second","seconds","a minute","minutes","an hour","hours","a day","days","a month","months","a year","years"],i=[1,60,3600,86400,2592e3,31104e3,1/0],r=(new Date-t)/1e3;for(var s=0;s=r)return e[2*s];if(i[s+1]>r)return Math.floor(r/i[s])+" "+e[2*s+1]}},registryUI.getHistoryIcon=function(t){switch(t){case"architecture":return"memory";case"created":return"event";case"docker_version":return"";case"os":return"developer_board";case"Cmd":return"launch";case"Entrypoint":return"input";case"Env":return"notes";case"Labels":return"label";case"User":return"face";case"Volumes":return"storage";case"WorkingDir":return"home";case"author":return"account_circle";case"id":case"digest":return"settings_ethernet";case"created_by":return"build";case"size":return"get_app";case"ExposedPorts":return"router"}},registryUI.getPage=function(t,e,i){return i||(i=100),t?t.slice((e-1)*i,i*e):[]},registryUI.getNumPages=function(t,e){return e||(e=100),t?Math.trunc(t.length/e)+1:0},registryUI.getPageLabels=function(t,e){var i=[];if(1===e)return i;1!==t&&e>=10&&(i.push({icon:"first_page",page:1}),i.push({icon:"chevron_left",page:t-1}));for(var r=Math.round(Math.max(1,Math.min(t-5,e-10+1))),s=r;s10,"space-right":t===e&&e>10});return t!==e&&e>=10&&(i.push({icon:"chevron_right",page:t+1}),i.push({icon:"last_page",page:e})),i},registryUI.updateQueryString=function(t){var e="";for(var i in t)void 0!==t[i]&&(e+=(e.length>0?"&":"?")+i+"="+t[i]);history.pushState(null,"",e+window.location.hash)},registryUI.stripHttps=function(t){return t?t.replace(/^https?:\/\//,""):""},riot.tag2("app",'
Docker Registry UI
',"","",function(t){registryUI.appTag=this,route.base("#!"),route("",function(){route.routeName="home",registryUI.catalog.display&&(registryUI.catalog.loadend=!1),registryUI.appTag.update()}),route("/taglist/*",function(t){route.routeName="taglist",registryUI.taglist.name=t,registryUI.taglist.display&&(registryUI.taglist.loadend=!1),registryUI.appTag.update()}),route("/taghistory/image/*/tag/*",function(t,e){route.routeName="taghistory",registryUI.taghistory.image=t,registryUI.taghistory.tag=e,registryUI.taghistory.display&&(registryUI.taghistory.loadend=!1),registryUI.appTag.update()}),registryUI.home=function(){"home"==route.routeName?registryUI.catalog.display:route("")},registryUI.taghistory.go=function(t,e){route("/taghistory/image/"+t+"/tag/"+e)},registryUI.snackbar=function(t,e){registryUI.appTag.tags["material-snackbar"].addToast({message:t,isError:e},15e3)},registryUI.errorSnackbar=function(t){return registryUI.snackbar(t,!0)},registryUI.showErrorCanNotReadContentDigest=function(){registryUI.errorSnackbar("Access on registry response was blocked. Try adding the header `Access-Control-Expose-Headers: Docker-Content-Digest` to your proxy or registry: https://docs.docker.com/registry/configuration/#http")},registryUI.cleanName=function(){const t=registryUI.pullUrl||registryUI.url()&®istryUI.url().length>0&®istryUI.url()||window.location.host;return registryUI.stripHttps(t)},route.parser(null,function(t,e){const i=e.replace(/\?/g,"\\?").replace(/\*/g,"([^?#]+?)").replace(/\.\./,".*"),r=new RegExp("^"+i+"$"),s=t.match(r);if(s)return s.slice(1)}),registryUI.isDigit=function(t){return t>="0"&&t<="9"},registryUI.DockerImage=function(t,e){this.name=t,this.tag=e,this.chars=0,riot.observable(this),this.on("get-size",function(){return void 0!==this.size?this.trigger("size",this.size):this.fillInfo()}),this.on("get-sha256",function(){return void 0!==this.size?this.trigger("sha256",this.sha256):this.fillInfo()}),this.on("get-date",function(){return void 0!==this.creationDate?this.trigger("creation-date",this.creationDate):this.fillInfo()}),this.on("content-digest-chars",function(t){this.chars=t}),this.on("get-content-digest-chars",function(){return this.trigger("content-digest-chars",this.chars)}),this.on("get-content-digest",function(){return void 0!==this.digest?this.trigger("content-digest",this.digest):this.fillInfo()})},registryUI.DockerImage._tagReduce=function(t,e){return t.length>0&®istryUI.isDigit(t[t.length-1].charAt(0))==registryUI.isDigit(e)?t[t.length-1]+=e:t.push(e),t},registryUI.DockerImage.compare=function(t,e){const i=t.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce,[]),r=e.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce,[]);for(var s=0;ssend {typeof opts.item === ⁗string⁗ ? opts.item : opts.item.repo}
',"","",function(t){registryUI.catalog.instance=this,registryUI.catalog.display=function(){registryUI.catalog.repositories=[];const t=new Http;t.addEventListener("load",function(){registryUI.catalog.repositories=[],200==this.status?(registryUI.url()||(registryUI._url=window.location.origin+window.location.pathname.replace(/\/+$/,"")),registryUI.catalog.repositories=JSON.parse(this.responseText).repositories||[],registryUI.catalog.repositories.sort(),registryUI.catalog.length=registryUI.catalog.repositories.length,registryUI.catalog.repositories=registryUI.catalog.repositories.reduce(function(t,e){const i=e.indexOf("/");if(i>0){const r=e.substring(0,i)+"/";return 0!=t.length&&t[t.length-1].repo==r||t.push({repo:r,images:[]}),t[t.length-1].images.push(e),t}return t.push(e),t},[])):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText)}),t.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.catalog.repositories=[]}),t.addEventListener("loadend",function(){registryUI.catalog.loadend=!0,registryUI.catalog.instance.update()}),t.open("GET",registryUI.url()+"/v2/_catalog?n=100000"),t.send()},registryUI.catalog.display()}),riot.tag2("copy-to-clipboard",'
content_copy
',"","",function(t){this.prefix="docker pull "+registryUI.cleanName()+"/"+t.image.name;const e=this;"tag"===t.target?e.dockerCmd=e.prefix+":"+t.image.tag:(t.image.one("content-digest",function(t){e.dockerCmd=e.prefix+"@"+t}),t.image.trigger("get-content-digest")),this.copy=function(){if(!e.dockerCmd)return void registryUI.showErrorCanNotReadContentDigest();const t=this.refs.input;t.style.display="block",t.select(),document.execCommand("copy"),t.style.display="none",registryUI.snackbar("`"+this.dockerCmd+"` has been copied to clipboard.")}}),riot.tag2("image-content-digest",'
',"","",function(t){this.on("updated",function(){this.tags["material-button"]&&(Array.isArray(this.tags["material-button"])?this.tags["material-button"]:[this.tags["material-button"]]).forEach(function(t){t.root.onclick=function(){registryUI.taglist.instance.trigger("page-update",t.p.page)}})})}),riot.tag2("remove-image",'delete',"","",function(t){const e=this;this.on("updated",function(){}),this.on("updated",function(){e.multiDelete!=e.opts.multiDelete&&(this.tags["material-button"]&&(this.delete=this.tags["material-button"].root.onclick=function(t){const i=e.opts.image.name,r=e.opts.image.tag,s=new Http;s.addEventListener("loadend",function(){if(registryUI.taglist.go(i),200==this.status){if(!this.hasHeader("Docker-Content-Digest"))return void registryUI.showErrorCanNotReadContentDigest();const e=this.getResponseHeader("Docker-Content-Digest"),s=new Http;s.addEventListener("loadend",function(){200==this.status||202==this.status?(registryUI.taglist.display(),registryUI.snackbar("Deleting "+i+":"+r+" image. Run `registry garbage-collect config.yml` on your registry")):404==this.status?t||registryUI.errorSnackbar("Digest not found"):registryUI.snackbar(this.responseText)}),s.open("DELETE",registryUI.url()+"/v2/"+i+"/manifests/"+e),s.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),s.addEventListener("error",function(){registryUI.errorSnackbar("An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE'].")}),s.send()}else 404==this.status?registryUI.errorSnackbar("Manifest for "+i+":"+r+" not found"):registryUI.snackbar(this.responseText)}),s.open("HEAD",registryUI.url()+"/v2/"+i+"/manifests/"+r),s.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),s.send()}),this.tags["material-checkbox"]&&(!this.opts.multiDelete&&this.tags["material-checkbox"].checked&&this.tags["material-checkbox"].toggle(),this.tags["material-checkbox"].on("toggle",function(){registryUI.taglist.instance.trigger("toggle-remove-image",this.checked)})),e.multiDelete=e.opts.multiDelete)})}),riot.tag2("tag-history-button",'history',"","",function(t){this.on("mount",function(){const t=this;this.refs.button.root.onclick=function(){registryUI.taghistory._image=t.opts.image,registryUI.taghistory.go(t.opts.image.name,t.opts.image.tag)}}),this.update()}),riot.tag2("tag-history-element",'
Sourced from {registryUI.name() + \'/\' + registryUI.taglist.name}
{registryUI.taglist.tags.length} tags
Creation date
Size
Content Digest
Tag
History
delete
',"","",function(t){var e=registryUI.taglist.instance=this;e.page=registryUI.getPageQueryParam(),registryUI.taglist.tags=[];const i=function(){const t=window.innerWidth;var e=0;e=t>=1440?71:t<1024?0:15+(t-1024)/416*56,registryUI.taglist.tags.map(function(t){t.trigger("content-digest-chars",e)})};window.addEventListener("resize",i),window.requestAnimationFrame(i),this.multiDelete=!1,this.toDelete=0,this.on("delete",function(){registryUI.isImageRemoveActivated&&this.multiDelete}),this.on("multi-delete",function(){registryUI.isImageRemoveActivated&&(this.multiDelete=!this.multiDelete)}),this.on("toggle-remove-image",function(t){t?this.toDelete++:this.toDelete--,this.toDelete<=1&&this.update()}),this.on("page-update",function(t){e.page=t<1?1:t,registryUI.updateQueryString(registryUI.getQueryParams({page:e.page})),this.toDelete=0,this.update()}),this._getRemoveImageTags=function(){var t=e.refs["taglist-tag"].tags["remove-image"];return t instanceof Array||(t=[t]),t},registryUI.taglist.bulkDelete=function(){e.multiDelete&&e.toDelete>0&&e._getRemoveImageTags().filter(function(t){return t.tags["material-checkbox"].checked}).forEach(function(t){t.delete(!0)})},this.on("update",function(){var t=this.refs["taglist-tag"].refs["remove-tag-checkbox"];t&&!t._toggle&&(t._toggle=t.toggle,t.toggle=function(t){t.altKey?e._getRemoveImageTags().filter(function(t){return!t.tags["material-checkbox"].checked}).forEach(function(t){t.tags["material-checkbox"].toggle()}):this._toggle()},t.on("toggle",function(){registryUI.taglist.instance.multiDelete=this.checked,registryUI.taglist.instance.update()}))}),registryUI.taglist.display=function(){if(registryUI.taglist.tags=[],"taglist"==route.routeName){const t=new Http;registryUI.taglist.instance.update(),t.addEventListener("load",function(){if(registryUI.taglist.tags=[],200==this.status){const t=JSON.parse(this.responseText).tags||[];registryUI.taglist.tags=t.map(function(t){return new registryUI.DockerImage(registryUI.taglist.name,t)}).sort(registryUI.DockerImage.compare),window.requestAnimationFrame(i),e.trigger("page-update",Math.min(e.page,registryUI.getNumPages(registryUI.taglist.tags)))}else 404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText,!0)}),t.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.taglist.tags=[]}),t.addEventListener("loadend",function(){registryUI.taglist.loadend=!0,registryUI.taglist.instance.update()}),t.open("GET",registryUI.url()+"/v2/"+registryUI.taglist.name+"/tags/list"),t.send(),registryUI.taglist.asc=!0}},registryUI.taglist.display(),registryUI.taglist.instance.update(),registryUI.taglist.reverse=function(){registryUI.taglist.asc?(registryUI.taglist.tags.reverse(),registryUI.taglist.asc=!1):(registryUI.taglist.tags.sort(registryUI.DockerImage.compare),registryUI.taglist.asc=!0),registryUI.taglist.instance.update()}});
\ No newline at end of file
diff --git a/dist/scripts/docker-registry-ui.js b/dist/scripts/docker-registry-ui.js
index 37b41b1..780cab7 100644
--- a/dist/scripts/docker-registry-ui.js
+++ b/dist/scripts/docker-registry-ui.js
@@ -15,4 +15,4 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;const r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){const e=new XMLHttpRequest;for(key in e.open(r._method,r._url),r._events)e.addEventListener(key,r._events[key]);for(key in r._headers)e.setRequestHeader(key,r._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.getErrorMessage=Http.getErrorMessage,e.send()}else t.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:r.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(t){return new RegExp("^"+e+":","i").test(t)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?this.withCredentials&&!this.hasHeader("Access-Control-Allow-Credentials")?"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `"+registryUI.url()+"` is therefore not allowed access.":"An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `"+window.location.origin+"`":"Incorrect server endpoint."};var registryUI={URL_QUERY_PARAM_REGEX:/[&?]url=/,URL_PARAM_REGEX:/^url=/,url:function(e){if(!registryUI._url){const e=registryUI.getUrlQueryParam();if(e)try{return registryUI._url=registryUI.decodeURI(e),registryUI._url}catch(e){console.log(e)}registryUI._url=registryUI.getRegistryServer(0)}return registryUI._url},name:function(){return registryUI.stripHttps(registryUI.url())},getRegistryServer:function(e){try{const t=JSON.parse(localStorage.getItem("registryServer"));if(t instanceof Array)return isNaN(e)?t.map(function(e){return e.trim().replace(/\/*$/,"")}):t[e]}catch(e){}return isNaN(e)?[]:""},addServer:function(e){const t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,""),-1==t.indexOf(e)&&(t.push(e),registryUI._url||registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(t)))},changeServer:function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");const r=t.indexOf(e);-1!=r&&(t.splice(r,1),t=[e].concat(t),registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(t)))},removeServer:function(e){const t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");const r=t.indexOf(e);-1!=r&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)),e==registryUI.url()&&(registryUI.updateHistory(registryUI.getRegistryServer(0)),route("")))},updateHistory:function(e){registryUI.updateQueryString({url:registryUI.encodeURI(e)}),registryUI._url=e},getUrlQueryParam:function(){const e=window.location.search;if(registryUI.URL_QUERY_PARAM_REGEX.test(e)){const t=e.split(/^\?|&/).find(function(e){return e&®istryUI.URL_PARAM_REGEX.test(e)});return t?t.replace(registryUI.URL_PARAM_REGEX,""):t}},encodeURI:function(e){if(e)return e.indexOf("&")<0?window.encodeURIComponent(e):btoa(e)},decodeURI:function(e){if(e)return e.startsWith("http")?window.decodeURIComponent(e):atob(e)},isImageRemoveActivated:!0,catalog:{},taglist:{},taghistory:{}};window.addEventListener("DOMContentLoaded",function(){riot.mount("*")}),registryUI.bytesToSize=function(e){if(null==e||isNaN(e))return"?";if(0==e)return"0 Byte";const t=parseInt(Math.floor(Math.log(e)/Math.log(1024)));return Math.ceil(e/Math.pow(1024,t))+" "+["Bytes","KB","MB","GB","TB"][t]},registryUI.dateFormat=function(e){if(void 0===e)return"";const t=["a second","seconds","a minute","minutes","an hour","hours","a day","days","a month","months","a year","years"],r=[1,60,3600,86400,2592e3,31104e3,1/0],i=(new Date-e)/1e3;for(var a=0;a=i)return t[2*a];if(r[a+1]>i)return Math.floor(i/r[a])+" "+t[2*a+1]}},registryUI.getHistoryIcon=function(e){switch(e){case"architecture":return"memory";case"created":return"event";case"docker_version":return"";case"os":return"developer_board";case"Cmd":return"launch";case"Entrypoint":return"input";case"Env":return"notes";case"Labels":return"label";case"User":return"face";case"Volumes":return"storage";case"WorkingDir":return"home";case"author":return"account_circle";case"id":case"digest":return"settings_ethernet";case"created_by":return"build";case"size":return"get_app";case"ExposedPorts":return"router"}},registryUI.getPage=function(e,t,r){return r||(r=100),e?e.slice((t-1)*r,r*t):[]},registryUI.getNumPages=function(e,t){return t||(t=100),e?Math.trunc(e.length/t)+1:0},registryUI.getPageLabels=function(e,t){var r=[];if(1===t)return r;1!==e&&t>=10&&(r.push({icon:"first_page",page:1}),r.push({icon:"chevron_left",page:e-1}));for(var i=Math.round(Math.max(1,Math.min(e-5,t-10+1))),a=i;a10,"space-right":e===t&&t>10});return e!==t&&t>=10&&(r.push({icon:"chevron_right",page:e+1}),r.push({icon:"last_page",page:t})),r},registryUI.updateQueryString=function(e){var t="";for(var r in e)void 0!==e[r]&&(t+=(t.length>0?"&":"?")+r+"="+e[r]);history.pushState(null,"",t+window.location.hash)},registryUI.stripHttps=function(e){return e?e.replace(/^https?:\/\//,""):""},riot.tag2("app",'
',"","",function(e){registryUI.catalog.instance=this,registryUI.catalog.display=function(){registryUI.catalog.repositories=[];const e=new Http;e.addEventListener("load",function(){registryUI.catalog.repositories=[],200==this.status?(registryUI.url()||(registryUI._url=window.location.origin+window.location.pathname.replace(/\/+$/,"")),registryUI.catalog.repositories=JSON.parse(this.responseText).repositories||[],registryUI.catalog.repositories.sort(),registryUI.catalog.length=registryUI.catalog.repositories.length,registryUI.catalog.repositories=registryUI.catalog.repositories.reduce(function(e,t){const r=t.indexOf("/");if(r>0){const i=t.substring(0,r)+"/";return 0!=e.length&&e[e.length-1].repo==i||e.push({repo:i,images:[]}),e[e.length-1].images.push(t),e}return e.push(t),e},[])):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText)}),e.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.catalog.repositories=[]}),e.addEventListener("loadend",function(){registryUI.catalog.loadend=!0,registryUI.catalog.instance.update()}),e.open("GET",registryUI.url()+"/v2/_catalog?n=100000"),e.send()},registryUI.catalog.display()}),riot.tag2("copy-to-clipboard",' content_copy',"","",function(e){this.dockerCmd="docker pull "+registryUI.cleanName()+"/"+e.image.name+":"+e.image.tag,this.copy=function(){const e=this.refs.input;e.style.display="block",e.select(),document.execCommand("copy"),e.style.display="none",registryUI.snackbar("`"+this.dockerCmd+"` has been copied to clipboard.")}}),riot.tag2("image-date",'
',"","",function(e){this.on("updated",function(){this.tags["material-button"]&&(Array.isArray(this.tags["material-button"])?this.tags["material-button"]:[this.tags["material-button"]]).forEach(function(e){e.root.onclick=function(){registryUI.taglist.instance.trigger("page-update",e.p.page)}})})}),riot.tag2("remove-image",'delete',"","",function(e){const t=this;this.on("updated",function(){}),this.on("updated",function(){t.multiDelete!=t.opts.multiDelete&&(this.tags["material-button"]&&(this.delete=this.tags["material-button"].root.onclick=function(e){const r=t.opts.image.name,i=t.opts.image.tag,a=new Http;a.addEventListener("loadend",function(){if(registryUI.taglist.go(r),200==this.status){if(!this.hasHeader("Docker-Content-Digest"))return void registryUI.errorSnackbar("You need to add Access-Control-Expose-Headers: ['Docker-Content-Digest'] in your server configuration.");const t=this.getResponseHeader("Docker-Content-Digest"),a=new Http;a.addEventListener("loadend",function(){200==this.status||202==this.status?(registryUI.taglist.display(),registryUI.snackbar("Deleting "+r+":"+i+" image. Run `registry garbage-collect config.yml` on your registry")):404==this.status?e||registryUI.errorSnackbar("Digest not found"):registryUI.snackbar(this.responseText)}),a.open("DELETE",registryUI.url()+"/v2/"+r+"/manifests/"+t),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.addEventListener("error",function(){registryUI.errorSnackbar("An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE'].")}),a.send()}else 404==this.status?registryUI.errorSnackbar("Manifest for "+r+":"+i+" not found"):registryUI.snackbar(this.responseText)}),a.open("HEAD",registryUI.url()+"/v2/"+r+"/manifests/"+i),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.send()}),this.tags["material-checkbox"]&&(!this.opts.multiDelete&&this.tags["material-checkbox"].checked&&this.tags["material-checkbox"].toggle(),this.tags["material-checkbox"].on("toggle",function(){registryUI.taglist.instance.trigger("toggle-remove-image",this.checked)})),t.multiDelete=t.opts.multiDelete)})}),riot.tag2("tag-history-button",'history',"","",function(e){this.on("mount",function(){const e=this;this.refs.button.root.onclick=function(){registryUI.taghistory._image=e.opts.image,registryUI.taghistory.go(e.opts.image.name,e.opts.image.tag)}}),this.update()}),riot.tag2("tag-history-element",'
Tags of {registryUI.name() + \'/\' + registryUI.taglist.name}
{registryUI.taglist.tags.length} tags
Repository
Creation date
Size
Tag
History
delete
{image.name}
',"","",function(e){var t=registryUI.taglist.instance=this;t.page=registryUI.getPageQueryParam(),this.multiDelete=!1,this.toDelete=0,this.on("delete",function(){registryUI.isImageRemoveActivated&&this.multiDelete}),this.on("multi-delete",function(){registryUI.isImageRemoveActivated&&(this.multiDelete=!this.multiDelete)}),this.on("toggle-remove-image",function(e){e?this.toDelete++:this.toDelete--,this.toDelete<=1&&this.update()}),this.on("page-update",function(e){t.page=e<1?1:e,registryUI.updateQueryString(registryUI.getQueryParams({page:t.page})),this.toDelete=0,this.update()}),this._getRemoveImageTags=function(){var e=t.refs["taglist-tag"].tags["remove-image"];return e instanceof Array||(e=[e]),e},registryUI.taglist.bulkDelete=function(){t.multiDelete&&t.toDelete>0&&t._getRemoveImageTags().filter(function(e){return e.tags["material-checkbox"].checked}).forEach(function(e){e.delete(!0)})},this.on("update",function(){var e=this.refs["taglist-tag"].refs["remove-tag-checkbox"];e&&!e._toggle&&(e._toggle=e.toggle,e.toggle=function(e){e.altKey?t._getRemoveImageTags().filter(function(e){return!e.tags["material-checkbox"].checked}).forEach(function(e){e.tags["material-checkbox"].toggle()}):this._toggle()},e.on("toggle",function(){registryUI.taglist.instance.multiDelete=this.checked,registryUI.taglist.instance.update()}))}),registryUI.taglist.display=function(){if(registryUI.taglist.tags=[],"taglist"==route.routeName){const e=new Http;registryUI.taglist.instance.update(),e.addEventListener("load",function(){registryUI.taglist.tags=[],200==this.status?(registryUI.taglist.tags=JSON.parse(this.responseText).tags||[],registryUI.taglist.tags=registryUI.taglist.tags.map(function(e){return new registryUI.DockerImage(registryUI.taglist.name,e)}).sort(registryUI.DockerImage.compare),t.trigger("page-update",Math.min(t.page,registryUI.getNumPages(registryUI.taglist.tags)))):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText,!0)}),e.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.taglist.tags=[]}),e.addEventListener("loadend",function(){registryUI.taglist.loadend=!0,registryUI.taglist.instance.update()}),e.open("GET",registryUI.url()+"/v2/"+registryUI.taglist.name+"/tags/list"),e.send(),registryUI.taglist.asc=!0}},registryUI.taglist.display(),registryUI.taglist.instance.update(),registryUI.taglist.reverse=function(){registryUI.taglist.asc?(registryUI.taglist.tags.reverse(),registryUI.taglist.asc=!1):(registryUI.taglist.tags.sort(registryUI.DockerImage.compare),registryUI.taglist.asc=!0),registryUI.taglist.instance.update()}}),riot.tag2("add",'
',"","",function(e){registryUI.removeTag=registryUI.removeTag||{},registryUI.removeTag.update=this.update,registryUI.removeTag.removeUrl=function(e){registryUI.removeServer(e),registryUI.removeTag.close()},registryUI.removeTag.close=function(){registryUI.removeTag.dialog.close(),registryUI.removeTag.update()},registryUI.removeTag.show=function(){registryUI.removeTag.dialog.open()},this.one("mount",function(){registryUI.removeTag.dialog=this.tags["material-popup"]})});
\ No newline at end of file
+function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,this._events={},this._headers={}}Http.prototype.getContentDigest=function(t){this.oReq.hasHeader("Docker-Content-Digest")?t(this.oReq.getResponseHeader("Docker-Content-Digest")):window.crypto&&window.TextEncoder&&crypto.subtle.digest("SHA-256",(new TextEncoder).encode(this.oReq.responseText)).then(function(e){t("sha256:"+Array.from(new Uint8Array(e)).map(function(t){return t.toString(16).padStart(2,"0")}).join(""))})},Http.prototype.addEventListener=function(t,e){this._events[t]=e;const r=this;switch(t){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){const t=new XMLHttpRequest;for(key in t.open(r._method,r._url),r._events)t.addEventListener(key,r._events[key]);for(key in r._headers)t.setRequestHeader(key,r._headers[key]);t.withCredentials=!0,t.hasHeader=Http.hasHeader,t.getErrorMessage=Http.getErrorMessage,t.send()}else e.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&e.bind(this)()});break;default:r.oReq.addEventListener(t,function(){e.bind(this)()})}},Http.prototype.setRequestHeader=function(t,e){this.oReq.setRequestHeader(t,e),this._headers[t]=e},Http.prototype.open=function(t,e){this._method=t,this._url=e,this.oReq.open(t,e)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(t){return this.getAllResponseHeaders().split("\n").some(function(e){return new RegExp("^"+t+":","i").test(e)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?this.withCredentials&&!this.hasHeader("Access-Control-Allow-Credentials")?"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `"+registryUI.url()+"` is therefore not allowed access.":"An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `"+window.location.origin+"`":"Incorrect server endpoint."};var registryUI={URL_QUERY_PARAM_REGEX:/[&?]url=/,URL_PARAM_REGEX:/^url=/,url:function(t){if(!registryUI._url){const t=registryUI.getUrlQueryParam();if(t)try{return registryUI._url=registryUI.decodeURI(t),registryUI._url}catch(t){console.log(t)}registryUI._url=registryUI.getRegistryServer(0)}return registryUI._url},name:function(){return registryUI.stripHttps(registryUI.url())},getRegistryServer:function(t){try{const e=JSON.parse(localStorage.getItem("registryServer"));if(e instanceof Array)return isNaN(t)?e.map(function(t){return t.trim().replace(/\/*$/,"")}):e[t]}catch(t){}return isNaN(t)?[]:""},addServer:function(t){const e=registryUI.getRegistryServer();t=t.trim().replace(/\/*$/,""),-1==e.indexOf(t)&&(e.push(t),registryUI._url||registryUI.updateHistory(t),localStorage.setItem("registryServer",JSON.stringify(e)))},changeServer:function(t){var e=registryUI.getRegistryServer();t=t.trim().replace(/\/*$/,"");const r=e.indexOf(t);-1!=r&&(e.splice(r,1),e=[t].concat(e),registryUI.updateHistory(t),localStorage.setItem("registryServer",JSON.stringify(e)))},removeServer:function(t){const e=registryUI.getRegistryServer();t=t.trim().replace(/\/*$/,"");const r=e.indexOf(t);-1!=r&&(e.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(e)),t==registryUI.url()&&(registryUI.updateHistory(registryUI.getRegistryServer(0)),route("")))},updateHistory:function(t){registryUI.updateQueryString({url:registryUI.encodeURI(t)}),registryUI._url=t},getUrlQueryParam:function(){const t=window.location.search;if(registryUI.URL_QUERY_PARAM_REGEX.test(t)){const e=t.split(/^\?|&/).find(function(t){return t&®istryUI.URL_PARAM_REGEX.test(t)});return e?e.replace(registryUI.URL_PARAM_REGEX,""):e}},encodeURI:function(t){if(t)return t.indexOf("&")<0?window.encodeURIComponent(t):btoa(t)},decodeURI:function(t){if(t)return t.startsWith("http")?window.decodeURIComponent(t):atob(t)},isImageRemoveActivated:!0,catalog:{},taglist:{},taghistory:{}};window.addEventListener("DOMContentLoaded",function(){riot.mount("*")}),registryUI.bytesToSize=function(t){if(null==t||isNaN(t))return"?";if(0==t)return"0 Byte";const e=parseInt(Math.floor(Math.log(t)/Math.log(1024)));return Math.ceil(t/Math.pow(1024,e))+" "+["Bytes","KB","MB","GB","TB"][e]},registryUI.dateFormat=function(t){if(void 0===t)return"";const e=["a second","seconds","a minute","minutes","an hour","hours","a day","days","a month","months","a year","years"],r=[1,60,3600,86400,2592e3,31104e3,1/0],i=(new Date-t)/1e3;for(var a=0;a=i)return e[2*a];if(r[a+1]>i)return Math.floor(i/r[a])+" "+e[2*a+1]}},registryUI.getHistoryIcon=function(t){switch(t){case"architecture":return"memory";case"created":return"event";case"docker_version":return"";case"os":return"developer_board";case"Cmd":return"launch";case"Entrypoint":return"input";case"Env":return"notes";case"Labels":return"label";case"User":return"face";case"Volumes":return"storage";case"WorkingDir":return"home";case"author":return"account_circle";case"id":case"digest":return"settings_ethernet";case"created_by":return"build";case"size":return"get_app";case"ExposedPorts":return"router"}},registryUI.getPage=function(t,e,r){return r||(r=100),t?t.slice((e-1)*r,r*e):[]},registryUI.getNumPages=function(t,e){return e||(e=100),t?Math.trunc(t.length/e)+1:0},registryUI.getPageLabels=function(t,e){var r=[];if(1===e)return r;1!==t&&e>=10&&(r.push({icon:"first_page",page:1}),r.push({icon:"chevron_left",page:t-1}));for(var i=Math.round(Math.max(1,Math.min(t-5,e-10+1))),a=i;a10,"space-right":t===e&&e>10});return t!==e&&e>=10&&(r.push({icon:"chevron_right",page:t+1}),r.push({icon:"last_page",page:e})),r},registryUI.updateQueryString=function(t){var e="";for(var r in t)void 0!==t[r]&&(e+=(e.length>0?"&":"?")+r+"="+t[r]);history.pushState(null,"",e+window.location.hash)},registryUI.stripHttps=function(t){return t?t.replace(/^https?:\/\//,""):""},riot.tag2("app",'
Docker Registry UI
',"","",function(t){registryUI.appTag=this,route.base("#!"),route("",function(){route.routeName="home",registryUI.catalog.display&&(registryUI.catalog.loadend=!1),registryUI.appTag.update()}),route("/taglist/*",function(t){route.routeName="taglist",registryUI.taglist.name=t,registryUI.taglist.display&&(registryUI.taglist.loadend=!1),registryUI.appTag.update()}),route("/taghistory/image/*/tag/*",function(t,e){route.routeName="taghistory",registryUI.taghistory.image=t,registryUI.taghistory.tag=e,registryUI.taghistory.display&&(registryUI.taghistory.loadend=!1),registryUI.appTag.update()}),registryUI.home=function(){"home"==route.routeName?registryUI.catalog.display:route("")},registryUI.taghistory.go=function(t,e){route("/taghistory/image/"+t+"/tag/"+e)},registryUI.snackbar=function(t,e){registryUI.appTag.tags["material-snackbar"].addToast({message:t,isError:e},15e3)},registryUI.errorSnackbar=function(t){return registryUI.snackbar(t,!0)},registryUI.showErrorCanNotReadContentDigest=function(){registryUI.errorSnackbar("Access on registry response was blocked. Try adding the header `Access-Control-Expose-Headers: Docker-Content-Digest` to your proxy or registry: https://docs.docker.com/registry/configuration/#http")},registryUI.cleanName=function(){const t=registryUI.pullUrl||registryUI.url()&®istryUI.url().length>0&®istryUI.url()||window.location.host;return registryUI.stripHttps(t)},route.parser(null,function(t,e){const r=e.replace(/\?/g,"\\?").replace(/\*/g,"([^?#]+?)").replace(/\.\./,".*"),i=new RegExp("^"+r+"$"),a=t.match(i);if(a)return a.slice(1)}),registryUI.isDigit=function(t){return t>="0"&&t<="9"},registryUI.DockerImage=function(t,e){this.name=t,this.tag=e,this.chars=0,riot.observable(this),this.on("get-size",function(){return void 0!==this.size?this.trigger("size",this.size):this.fillInfo()}),this.on("get-sha256",function(){return void 0!==this.size?this.trigger("sha256",this.sha256):this.fillInfo()}),this.on("get-date",function(){return void 0!==this.creationDate?this.trigger("creation-date",this.creationDate):this.fillInfo()}),this.on("content-digest-chars",function(t){this.chars=t}),this.on("get-content-digest-chars",function(){return this.trigger("content-digest-chars",this.chars)}),this.on("get-content-digest",function(){return void 0!==this.digest?this.trigger("content-digest",this.digest):this.fillInfo()})},registryUI.DockerImage._tagReduce=function(t,e){return t.length>0&®istryUI.isDigit(t[t.length-1].charAt(0))==registryUI.isDigit(e)?t[t.length-1]+=e:t.push(e),t},registryUI.DockerImage.compare=function(t,e){const r=t.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce,[]),i=e.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce,[]);for(var a=0;asend {typeof opts.item === ⁗string⁗ ? opts.item : opts.item.repo}
',"","",function(t){registryUI.catalog.instance=this,registryUI.catalog.display=function(){registryUI.catalog.repositories=[];const t=new Http;t.addEventListener("load",function(){registryUI.catalog.repositories=[],200==this.status?(registryUI.url()||(registryUI._url=window.location.origin+window.location.pathname.replace(/\/+$/,"")),registryUI.catalog.repositories=JSON.parse(this.responseText).repositories||[],registryUI.catalog.repositories.sort(),registryUI.catalog.length=registryUI.catalog.repositories.length,registryUI.catalog.repositories=registryUI.catalog.repositories.reduce(function(t,e){const r=e.indexOf("/");if(r>0){const i=e.substring(0,r)+"/";return 0!=t.length&&t[t.length-1].repo==i||t.push({repo:i,images:[]}),t[t.length-1].images.push(e),t}return t.push(e),t},[])):404==this.status?registryUI.snackbar("Server not found",!0):registryUI.snackbar(this.responseText)}),t.addEventListener("error",function(){registryUI.snackbar(this.getErrorMessage(),!0),registryUI.catalog.repositories=[]}),t.addEventListener("loadend",function(){registryUI.catalog.loadend=!0,registryUI.catalog.instance.update()}),t.open("GET",registryUI.url()+"/v2/_catalog?n=100000"),t.send()},registryUI.catalog.display()}),riot.tag2("copy-to-clipboard",'
content_copy
',"","",function(t){this.prefix="docker pull "+registryUI.cleanName()+"/"+t.image.name;const e=this;"tag"===t.target?e.dockerCmd=e.prefix+":"+t.image.tag:(t.image.one("content-digest",function(t){e.dockerCmd=e.prefix+"@"+t}),t.image.trigger("get-content-digest")),this.copy=function(){if(!e.dockerCmd)return void registryUI.showErrorCanNotReadContentDigest();const t=this.refs.input;t.style.display="block",t.select(),document.execCommand("copy"),t.style.display="none",registryUI.snackbar("`"+this.dockerCmd+"` has been copied to clipboard.")}}),riot.tag2("image-content-digest",'
',"","",function(t){this.on("updated",function(){this.tags["material-button"]&&(Array.isArray(this.tags["material-button"])?this.tags["material-button"]:[this.tags["material-button"]]).forEach(function(t){t.root.onclick=function(){registryUI.taglist.instance.trigger("page-update",t.p.page)}})})}),riot.tag2("remove-image",'delete',"","",function(t){const e=this;this.on("updated",function(){}),this.on("updated",function(){e.multiDelete!=e.opts.multiDelete&&(this.tags["material-button"]&&(this.delete=this.tags["material-button"].root.onclick=function(t){const r=e.opts.image.name,i=e.opts.image.tag,a=new Http;a.addEventListener("loadend",function(){if(registryUI.taglist.go(r),200==this.status){if(!this.hasHeader("Docker-Content-Digest"))return void registryUI.showErrorCanNotReadContentDigest();const e=this.getResponseHeader("Docker-Content-Digest"),a=new Http;a.addEventListener("loadend",function(){200==this.status||202==this.status?(registryUI.taglist.display(),registryUI.snackbar("Deleting "+r+":"+i+" image. Run `registry garbage-collect config.yml` on your registry")):404==this.status?t||registryUI.errorSnackbar("Digest not found"):registryUI.snackbar(this.responseText)}),a.open("DELETE",registryUI.url()+"/v2/"+r+"/manifests/"+e),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.addEventListener("error",function(){registryUI.errorSnackbar("An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE'].")}),a.send()}else 404==this.status?registryUI.errorSnackbar("Manifest for "+r+":"+i+" not found"):registryUI.snackbar(this.responseText)}),a.open("HEAD",registryUI.url()+"/v2/"+r+"/manifests/"+i),a.setRequestHeader("Accept","application/vnd.docker.distribution.manifest.v2+json"),a.send()}),this.tags["material-checkbox"]&&(!this.opts.multiDelete&&this.tags["material-checkbox"].checked&&this.tags["material-checkbox"].toggle(),this.tags["material-checkbox"].on("toggle",function(){registryUI.taglist.instance.trigger("toggle-remove-image",this.checked)})),e.multiDelete=e.opts.multiDelete)})}),riot.tag2("tag-history-button",'history',"","",function(t){this.on("mount",function(){const t=this;this.refs.button.root.onclick=function(){registryUI.taghistory._image=t.opts.image,registryUI.taghistory.go(t.opts.image.name,t.opts.image.tag)}}),this.update()}),riot.tag2("tag-history-element",'
+
\ No newline at end of file
diff --git a/src/tags/remove-image.tag b/src/tags/remove-image.tag
index 5e0212a..a8a38ae 100644
--- a/src/tags/remove-image.tag
+++ b/src/tags/remove-image.tag
@@ -38,7 +38,7 @@ along with this program. If not, see .
registryUI.taglist.go(name);
if (this.status == 200) {
if (!this.hasHeader('Docker-Content-Digest')) {
- registryUI.errorSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
+ registryUI.showErrorCanNotReadContentDigest();
return;
}
const digest = this.getResponseHeader('Docker-Content-Digest');
diff --git a/src/tags/taglist.tag b/src/tags/taglist.tag
index d12db84..564a538 100644
--- a/src/tags/taglist.tag
+++ b/src/tags/taglist.tag
@@ -22,7 +22,10 @@ along with this program. If not, see .
arrow_back
- Tags of { registryUI.name() + '/' + registryUI.taglist.name }
+ Tags of { registryUI.taglist.name }
+