mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-29 08:29:54 +03:00
Update 0.2.2: Fix issue #19
This commit is contained in:
parent
36cbda1263
commit
d34d793b73
9 changed files with 135 additions and 46 deletions
2
dist/scripts/script-static.js
vendored
2
dist/scripts/script-static.js
vendored
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
function Http(){this.oReq=new XMLHttpRequest,this._events={},this._headers={}}Http.prototype.addEventListener=function(t,e){this._events[t]=e;var s=this;switch(t){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var t=new XMLHttpRequest;for(key in s._events)t.addEventListener(key,s._events[key]);for(key in s._headers)t.setRequestHeader(key,s._headers[key]);t.withCredentials=!0,t.open(s._method,s._url),t.send()}else e.bind(this)()});break;case"load":s.oReq.addEventListener("load",function(){401!==this.status&&e.bind(this)()});break;default:s.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()};var registryUI={};registryUI.url=function(){return"${URL}"},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("catalog"),riot.mount("taglist"),riot.mount("app");
|
||||
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var s=this;switch(e){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(s._method,s._url);for(key in s._events)e.addEventListener(key,s._events[key]);for(key in s._headers)e.setRequestHeader(key,s._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else t.bind(this)()});break;case"load":s.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:s.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 t.match(new RegExp("^"+e+":"),"i")})};var registryUI={};registryUI.url=function(){return"${URL}"},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("catalog"),riot.mount("taglist"),riot.mount("app");
|
2
dist/scripts/script.js
vendored
2
dist/scripts/script.js
vendored
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
function Http(){this.oReq=new XMLHttpRequest,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;for(key in r._events)e.addEventListener(key,r._events[key]);for(key in r._headers)e.setRequestHeader(key,r._headers[key]);e.withCredentials=!0,e.open(r._method,r._url),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()};var registryUI={};registryUI.url=function(){return registryUI.getRegistryServer(0)},registryUI.getRegistryServer=function(e){try{var 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)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r==-1&&(t.push(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),t=[e].concat(t),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("add"),riot.mount("change"),riot.mount("remove"),riot.mount("menu"),riot.mount("app");
|
||||
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(r._method,r._url);for(key in 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.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 t.match(new RegExp("^"+e+":"),"i")})};var registryUI={};registryUI.url=function(){return registryUI.getRegistryServer(0)},registryUI.getRegistryServer=function(e){try{var 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)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r==-1&&(t.push(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),t=[e].concat(t),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("add"),riot.mount("change"),riot.mount("remove"),riot.mount("menu"),riot.mount("app");
|
2
dist/scripts/tags-static.js
vendored
2
dist/scripts/tags-static.js
vendored
File diff suppressed because one or more lines are too long
2
dist/scripts/tags.js
vendored
2
dist/scripts/tags.js
vendored
File diff suppressed because one or more lines are too long
135
dist/scripts/vendor.js
vendored
135
dist/scripts/vendor.js
vendored
|
@ -6,7 +6,7 @@
|
|||
|
||||
// nb. This is for IE10 and lower _only_.
|
||||
var supportCustomEvent = window.CustomEvent;
|
||||
if (!supportCustomEvent || typeof supportCustomEvent == 'object') {
|
||||
if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
|
||||
supportCustomEvent = function CustomEvent(event, x) {
|
||||
x = x || {};
|
||||
var ev = document.createEvent('CustomEvent');
|
||||
|
@ -66,7 +66,7 @@
|
|||
* @param {Element} el to blur
|
||||
*/
|
||||
function safeBlur(el) {
|
||||
if (el && el.blur && el != document.body) {
|
||||
if (el && el.blur && el !== document.body) {
|
||||
el.blur();
|
||||
}
|
||||
}
|
||||
|
@ -78,13 +78,24 @@
|
|||
*/
|
||||
function inNodeList(nodeList, node) {
|
||||
for (var i = 0; i < nodeList.length; ++i) {
|
||||
if (nodeList[i] == node) {
|
||||
if (nodeList[i] === node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLFormElement} el to check
|
||||
* @return {boolean} whether this form has method="dialog"
|
||||
*/
|
||||
function isFormMethodDialog(el) {
|
||||
if (!el || !el.hasAttribute('method')) {
|
||||
return false;
|
||||
}
|
||||
return el.getAttribute('method').toLowerCase() === 'dialog';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!HTMLDialogElement} dialog to upgrade
|
||||
* @constructor
|
||||
|
@ -121,6 +132,7 @@
|
|||
}.bind(this);
|
||||
var timeout;
|
||||
var delayModel = function(ev) {
|
||||
if (ev.target !== dialog) { return; } // not for a child element
|
||||
var cand = 'DOMNodeRemoved';
|
||||
removed |= (ev.type.substr(0, cand.length) === cand);
|
||||
window.clearTimeout(timeout);
|
||||
|
@ -363,7 +375,7 @@
|
|||
}
|
||||
var cssTop = rule.style.getPropertyValue('top');
|
||||
var cssBottom = rule.style.getPropertyValue('bottom');
|
||||
if ((cssTop && cssTop != 'auto') || (cssBottom && cssBottom != 'auto')) {
|
||||
if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -373,7 +385,7 @@
|
|||
|
||||
dialogPolyfill.needsCentering = function(dialog) {
|
||||
var computedStyle = window.getComputedStyle(dialog);
|
||||
if (computedStyle.position != 'absolute') {
|
||||
if (computedStyle.position !== 'absolute') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -381,9 +393,10 @@
|
|||
// WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
|
||||
// Firefox returns the used value. So we do this crazy thing instead: check
|
||||
// the inline style and then go through CSS rules.
|
||||
if ((dialog.style.top != 'auto' && dialog.style.top != '') ||
|
||||
(dialog.style.bottom != 'auto' && dialog.style.bottom != ''))
|
||||
if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||
|
||||
(dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {
|
||||
return false;
|
||||
}
|
||||
return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
|
||||
};
|
||||
|
||||
|
@ -391,7 +404,7 @@
|
|||
* @param {!Element} element to force upgrade
|
||||
*/
|
||||
dialogPolyfill.forceRegisterDialog = function(element) {
|
||||
if (element.showModal) {
|
||||
if (window.HTMLDialogElement || element.showModal) {
|
||||
console.warn('This browser already supports <dialog>, the polyfill ' +
|
||||
'may not work correctly', element);
|
||||
}
|
||||
|
@ -449,10 +462,8 @@
|
|||
continue;
|
||||
} else if (c.localName === 'dialog') {
|
||||
removed.push(c);
|
||||
} else {
|
||||
var q = c.querySelector('dialog');
|
||||
q && removed.push(q);
|
||||
}
|
||||
removed = removed.concat(c.querySelectorAll('dialog'));
|
||||
}
|
||||
});
|
||||
removed.length && checkDOM(removed);
|
||||
|
@ -603,7 +614,7 @@
|
|||
*/
|
||||
dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {
|
||||
var index = this.pendingDialogStack.indexOf(dpi);
|
||||
if (index == -1) { return; }
|
||||
if (index === -1) { return; }
|
||||
|
||||
this.pendingDialogStack.splice(index, 1);
|
||||
if (this.pendingDialogStack.length === 0) {
|
||||
|
@ -613,33 +624,103 @@
|
|||
};
|
||||
|
||||
dialogPolyfill.dm = new dialogPolyfill.DialogManager();
|
||||
dialogPolyfill.formSubmitter = null;
|
||||
dialogPolyfill.useValue = null;
|
||||
|
||||
/**
|
||||
* Installs global handlers, such as click listers and native method overrides. These are needed
|
||||
* even if a no dialog is registered, as they deal with <form method="dialog">.
|
||||
*/
|
||||
if (window.HTMLDialogElement === undefined) {
|
||||
|
||||
/**
|
||||
* If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with
|
||||
* one that returns the correct value.
|
||||
*/
|
||||
var testForm = document.createElement('form');
|
||||
testForm.setAttribute('method', 'dialog');
|
||||
if (testForm.method !== 'dialog') {
|
||||
var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');
|
||||
var realGet = methodDescriptor.get;
|
||||
methodDescriptor.get = function() {
|
||||
if (isFormMethodDialog(this)) {
|
||||
return 'dialog';
|
||||
}
|
||||
return realGet.call(this);
|
||||
};
|
||||
var realSet = methodDescriptor.set;
|
||||
methodDescriptor.set = function(v) {
|
||||
if (typeof v === 'string' && v.toLowerCase() === 'dialog') {
|
||||
return this.setAttribute('method', v);
|
||||
}
|
||||
return realSet.call(this, v);
|
||||
};
|
||||
Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Global 'click' handler, to capture the <input type="submit"> or <button> element which has
|
||||
* submitted a <form method="dialog">. Needed as Safari and others don't report this inside
|
||||
* document.activeElement.
|
||||
*/
|
||||
document.addEventListener('click', function(ev) {
|
||||
dialogPolyfill.formSubmitter = null;
|
||||
dialogPolyfill.useValue = null;
|
||||
if (ev.defaultPrevented) { return; } // e.g. a submit which prevents default submission
|
||||
|
||||
var target = /** @type {Element} */ (ev.target);
|
||||
if (!target || !isFormMethodDialog(target.form)) { return; }
|
||||
|
||||
var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);
|
||||
if (!valid) {
|
||||
if (!(target.localName === 'input' && target.type === 'image')) { return; }
|
||||
// this is a <input type="image">, which can submit forms
|
||||
dialogPolyfill.useValue = ev.offsetX + ',' + ev.offsetY;
|
||||
}
|
||||
|
||||
var dialog = findNearestDialog(target);
|
||||
if (!dialog) { return; }
|
||||
|
||||
dialogPolyfill.formSubmitter = target;
|
||||
}, false);
|
||||
|
||||
/**
|
||||
* Replace the native HTMLFormElement.submit() method, as it won't fire the
|
||||
* submit event and give us a chance to respond.
|
||||
*/
|
||||
var nativeFormSubmit = HTMLFormElement.prototype.submit;
|
||||
function replacementFormSubmit() {
|
||||
if (!isFormMethodDialog(this)) {
|
||||
return nativeFormSubmit.call(this);
|
||||
}
|
||||
var dialog = findNearestDialog(this);
|
||||
dialog && dialog.close();
|
||||
}
|
||||
HTMLFormElement.prototype.submit = replacementFormSubmit;
|
||||
|
||||
/**
|
||||
* Global form 'dialog' method handler. Closes a dialog correctly on submit
|
||||
* and possibly sets its return value.
|
||||
*/
|
||||
document.addEventListener('submit', function(ev) {
|
||||
var target = ev.target;
|
||||
if (!target || !target.hasAttribute('method')) { return; }
|
||||
if (target.getAttribute('method').toLowerCase() !== 'dialog') { return; }
|
||||
var form = /** @type {HTMLFormElement} */ (ev.target);
|
||||
if (!isFormMethodDialog(form)) { return; }
|
||||
ev.preventDefault();
|
||||
|
||||
var dialog = findNearestDialog(/** @type {Element} */ (ev.target));
|
||||
var dialog = findNearestDialog(form);
|
||||
if (!dialog) { return; }
|
||||
|
||||
// FIXME: The original event doesn't contain the element used to submit the
|
||||
// form (if any). Look in some possible places.
|
||||
var returnValue;
|
||||
var cands = [document.activeElement, ev.explicitOriginalTarget];
|
||||
var els = ['BUTTON', 'INPUT'];
|
||||
cands.some(function(cand) {
|
||||
if (cand && cand.form == ev.target && els.indexOf(cand.nodeName.toUpperCase()) != -1) {
|
||||
returnValue = cand.value;
|
||||
return true;
|
||||
// Forms can only be submitted via .submit() or a click (?), but anyway: sanity-check that
|
||||
// the submitter is correct before using its value as .returnValue.
|
||||
var s = dialogPolyfill.formSubmitter;
|
||||
if (s && s.form === form) {
|
||||
dialog.close(dialogPolyfill.useValue || s.value);
|
||||
} else {
|
||||
dialog.close();
|
||||
}
|
||||
});
|
||||
dialog.close(returnValue);
|
||||
dialogPolyfill.formSubmitter = null;
|
||||
}, true);
|
||||
}
|
||||
|
||||
dialogPolyfill['forceRegisterDialog'] = dialogPolyfill.forceRegisterDialog;
|
||||
dialogPolyfill['registerDialog'] = dialogPolyfill.registerDialog;
|
||||
|
|
6
dist/vendor.css
vendored
6
dist/vendor.css
vendored
|
@ -12,11 +12,11 @@ dialog {
|
|||
padding: 1em;
|
||||
background: white;
|
||||
color: black;
|
||||
display: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
dialog[open] {
|
||||
display: block;
|
||||
dialog:not([open]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
dialog + .backdrop {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
function Http() {
|
||||
this.oReq = new XMLHttpRequest();
|
||||
this.oReq.hasHeader = Http.hasHeader;
|
||||
this._events = {};
|
||||
this._headers = {};
|
||||
}
|
||||
|
@ -29,6 +30,7 @@ Http.prototype.addEventListener = function(e, f) {
|
|||
self.oReq.addEventListener('loadend', function() {
|
||||
if (this.status == 401) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open(self._method, self._url);
|
||||
for (key in self._events) {
|
||||
req.addEventListener(key, self._events[key]);
|
||||
}
|
||||
|
@ -36,7 +38,7 @@ Http.prototype.addEventListener = function(e, f) {
|
|||
req.setRequestHeader(key, self._headers[key]);
|
||||
}
|
||||
req.withCredentials = true;
|
||||
req.open(self._method, self._url);
|
||||
req.hasHeader = Http.hasHeader;
|
||||
req.send();
|
||||
} else {
|
||||
f.bind(this)();
|
||||
|
@ -77,3 +79,9 @@ Http.prototype.open = function(m, u) {
|
|||
Http.prototype.send = function() {
|
||||
this.oReq.send();
|
||||
};
|
||||
|
||||
Http.hasHeader = function(header) {
|
||||
return this.getAllResponseHeaders().split('\n').some(function(h) {
|
||||
return h.match(new RegExp('^' + header + ':'), 'i');
|
||||
});
|
||||
};
|
|
@ -24,16 +24,16 @@
|
|||
|
||||
registryUI.removeImage.remove = function (name, tag) {
|
||||
var oReq = new Http();
|
||||
oReq.addEventListener('load', function () {
|
||||
oReq.addEventListener('loadend', function () {
|
||||
registryUI.taglist.refresh();
|
||||
if (this.status == 200) {
|
||||
if (!this.getAllResponseHeaders().includes('Docker-Content-Digest')) {
|
||||
registryUI.taglist.createSnackbar('You need tu add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
|
||||
if (!this.hasHeader('Docker-Content-Digest')) {
|
||||
registryUI.taglist.createSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
|
||||
return;
|
||||
}
|
||||
var digest = this.getResponseHeader('Docker-Content-Digest');
|
||||
var oReq = new Http();
|
||||
oReq.addEventListener('load', function () {
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
registryUI.taglist.refresh();
|
||||
registryUI.taglist.createSnackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue