diff --git a/web/index.html b/web/index.html index cecb2eb..0b583f7 100644 --- a/web/index.html +++ b/web/index.html @@ -112,9 +112,20 @@
  • /
  • -
  • - {{project.name}} -
  • + {{#if !version && !currentStage}} +
  • + {{project.name}} + +
  • + {{else}} +
  • + {{project.name}} +
  • + {{/if}} {{/if}} {{#if project && version}}
  • diff --git a/web/js/index.js b/web/js/index.js index d0f7f8d..57c82c5 100644 --- a/web/js/index.js +++ b/web/js/index.js @@ -24,11 +24,43 @@ Ractive.DEBUG = false; releases: {}, }; }, + decorators: { + menu: function(node) { + new PureDropdown(node); + return { + teardown: function() { + return; + }, + }; + }, + }, }); setPaths(); + r.on({ + "triggerBuild": function(event) { + event.original.preventDefault(); + var secret = window.prompt("Please enter the trigger secret for this project:"); + triggerBuild(r.get("project.id"), secret); + }, + }); + + + function triggerBuild(projectID, secret) { + ajax("POST", "/trigger/" + projectID, { + secret: secret + }, + function(result) { + window.location = "/"; + }, + function(result) { + r.set("error", err(result).message); + }); + } + + function setPaths() { var paths = window.location.pathname.split("/"); @@ -66,7 +98,19 @@ Ractive.DEBUG = false; setStatus(result.data[i]); hasRelease(result.data[i].id, ""); } + + result.data.sort(function(a, b) { + if (a.name > b.name) { + return 1; + } + if (a.name < b.name) { + return -1; + } + return 0; + }); r.set("projects", result.data); + + window.setTimeout(getProjects, 10000); }, function(result) { r.set("error", err(result).message); @@ -157,7 +201,13 @@ function ajax(type, url, data, success, error) { req.onload = function() { if (req.status >= 200 && req.status < 400) { if (success && typeof success === 'function') { - success(JSON.parse(req.responseText)); + var result; + try { + result = JSON.parse(req.responseText); + } catch (e) { + result = ""; + } + success(result); } return; } @@ -174,11 +224,13 @@ function ajax(type, url, data, success, error) { }; } + var sendData; if (type != "get") { req.setRequestHeader("Content-Type", "application/json"); + sendData = JSON.stringify(data); } - req.send(data); + req.send(sendData); } function get(url, success, error) { @@ -209,3 +261,159 @@ function formatDate(strDate) { } return date.toLocaleDateString() + " at " + date.toLocaleTimeString(); } + + +function PureDropdown(dropdownParent) { + "use strict"; + + var PREFIX = 'pure-', + ACTIVE_CLASS_NAME = PREFIX + 'menu-active', + ARIA_ROLE = 'role', + ARIA_HIDDEN = 'aria-hidden', + MENU_OPEN = 0, + MENU_CLOSED = 1, + MENU_PARENT_CLASS_NAME = 'pure-menu-has-children', + MENU_ACTIVE_SELECTOR = '.pure-menu-active', + MENU_LINK_SELECTOR = '.pure-menu-link', + MENU_SELECTOR = '.pure-menu-children', + DISMISS_EVENT = (window.hasOwnProperty && + window.hasOwnProperty('ontouchstart')) ? + 'touchstart' : 'mousedown', + + ARROW_KEYS_ENABLED = true, + + ddm = this; // drop down menu + + this._state = MENU_CLOSED; + + this.show = function() { + if (this._state !== MENU_OPEN) { + this._dropdownParent.classList.add(ACTIVE_CLASS_NAME); + this._menu.setAttribute(ARIA_HIDDEN, false); + this._state = MENU_OPEN; + } + }; + + this.hide = function() { + if (this._state !== MENU_CLOSED) { + this._dropdownParent.classList.remove(ACTIVE_CLASS_NAME); + this._menu.setAttribute(ARIA_HIDDEN, true); + this._link.focus(); + this._state = MENU_CLOSED; + } + }; + + this.toggle = function() { + this[this._state === MENU_CLOSED ? 'show' : 'hide'](); + }; + + this.halt = function(e) { + e.stopPropagation(); + e.preventDefault(); + }; + + this._dropdownParent = dropdownParent; + this._link = this._dropdownParent.querySelector(MENU_LINK_SELECTOR); + this._menu = this._dropdownParent.querySelector(MENU_SELECTOR); + this._firstMenuLink = this._menu.querySelector(MENU_LINK_SELECTOR); + + // Set ARIA attributes + this._link.setAttribute('aria-haspopup', 'true'); + this._menu.setAttribute(ARIA_ROLE, 'menu'); + this._menu.setAttribute('aria-labelledby', this._link.getAttribute('id')); + this._menu.setAttribute('aria-hidden', 'true'); + [].forEach.call( + this._menu.querySelectorAll('li'), + function(el) { + el.setAttribute(ARIA_ROLE, 'presentation'); + } + ); + [].forEach.call( + this._menu.querySelectorAll('a'), + function(el) { + el.setAttribute(ARIA_ROLE, 'menuitem'); + } + ); + + // Toggle on click + this._link.addEventListener('click', function(e) { + e.stopPropagation(); + e.preventDefault(); + ddm.toggle(); + }); + + // Keyboard navigation + document.addEventListener('keydown', function(e) { + var currentLink, + previousSibling, + nextSibling, + previousLink, + nextLink; + + // if the menu isn't active, ignore + if (ddm._state !== MENU_OPEN) { + return; + } + + // if the menu is the parent of an open, active submenu, ignore + if (ddm._menu.querySelector(MENU_ACTIVE_SELECTOR)) { + return; + } + + currentLink = ddm._menu.querySelector(':focus'); + + // Dismiss an open menu on ESC + if (e.keyCode === 27) { + /* Esc */ + ddm.halt(e); + ddm.hide(); + } + // Go to the next link on down arrow + else if (ARROW_KEYS_ENABLED && e.keyCode === 40) { + /* Down arrow */ + ddm.halt(e); + // get the nextSibling (an LI) of the current link's LI + nextSibling = (currentLink) ? currentLink.parentNode.nextSibling : null; + // if the nextSibling is a text node (not an element), go to the next one + while (nextSibling && nextSibling.nodeType !== 1) { + nextSibling = nextSibling.nextSibling; + } + nextLink = (nextSibling) ? nextSibling.querySelector('.pure-menu-link') : null; + // if there is no currently focused link, focus the first one + if (!currentLink) { + ddm._menu.querySelector('.pure-menu-link').focus(); + } else if (nextLink) { + nextLink.focus(); + } + } + // Go to the previous link on up arrow + else if (ARROW_KEYS_ENABLED && e.keyCode === 38) { + /* Up arrow */ + ddm.halt(e); + // get the currently focused link + previousSibling = (currentLink) ? currentLink.parentNode.previousSibling : null; + while (previousSibling && previousSibling.nodeType !== 1) { + previousSibling = previousSibling.previousSibling; + } + previousLink = (previousSibling) ? previousSibling.querySelector('.pure-menu-link') : null; + // if there is no currently focused link, focus the last link + if (!currentLink) { + ddm._menu.querySelector('.pure-menu-item:last-child .pure-menu-link').focus(); + } + // else if there is a previous item, go to the previous item + else if (previousLink) { + previousLink.focus(); + } + } + }); + + // Dismiss an open menu on outside event + document.addEventListener(DISMISS_EVENT, function(e) { + var target = e.target; + if (target !== ddm._link && !ddm._menu.contains(target)) { + ddm.hide(); + ddm._link.blur(); + } + }); + +} diff --git a/webProject.go b/webProject.go index 17b6418..736eba5 100644 --- a/webProject.go +++ b/webProject.go @@ -240,6 +240,11 @@ func triggerPost(w http.ResponseWriter, r *http.Request) { return } + if strings.TrimSpace(project.TriggerSecret) == "" { + four04(w, r) + return + } + input := &triggerInput{} if errHandled(parseInput(r, input), w, r) { return