Bug fixes.

* Fixed card flip transition causing editor controls to be inaccessible.
* Moved plugin menu out of the plugin card, to solve issues with z-index
overlaps with other cards.
* Custom elements now have all their event handlers removed as
appropriate.
This commit is contained in:
WrinklyNinja
2014-07-19 19:28:44 +01:00
parent 09be913477
commit 2cc9502db2
2 changed files with 216 additions and 72 deletions
+177 -28
View File
@@ -22,6 +22,77 @@
<http://www.gnu.org/licenses/>.
*/
'use strict';
/* Create a <plugin-menu> element type. */
var pluginMenuProto = Object.create(HTMLElement.prototype, {
onMenuItemClick: {
value: function(evt) {
var pluginID = evt.target.parentNode.host.getAttribute('data-for');
var pluginCard = document.getElementById(pluginID);
if (evt.target.id == 'editMetadata') {
/* Show editing controls. */
pluginCard.showEditor();
} else if (evt.target.id == 'copyMetadata') {
} else if (evt.target.id == 'clearMetadata') {
showMessageDialog('Clear Plugin Metadata', 'Are you sure you want to clear all existing user-added metadata from "' + pluginCard.querySelector('h1').textContent + '"?');
}
}
},
onClick: {
value: function(evt) {
var menus = document.getElementsByTagName('plugin-menu');
for (var i = 0; i < menus.length; ++i) {
if (!evt.newMenu) {
menus[i].parentElement.removeChild(menus[i]);
}
}
}
},
createdCallback: {
value: function() {
var template = document.getElementById('pluginMenu');
var clone = document.importNode(template.content, true);
this.createShadowRoot().appendChild(clone);
this.id = 'activePluginMenu';
/* Add an event listener so that the menu gets closed. */
main.addEventListener('click', this.onClick, false);
/* Add event listeners for the menu items. */
this.shadowRoot.querySelector('#editMetadata').addEventListener('click', this.onMenuItemClick, false);
this.shadowRoot.querySelector('#copyMetadata').addEventListener('click', this.onMenuItemClick, false);
this.shadowRoot.querySelector('#clearMetadata').addEventListener('click', this.onMenuItemClick, false);
}
},
detachedCallback: {
value: function() {
/* Remove menu close listener. */
document.getElementById('main').removeEventListener('click', this.onClick, false);
/* Remove event listeners for the menu items. */
this.shadowRoot.querySelector('#editMetadata').removeEventListener('click', this.onMenuItemClick, false);
this.shadowRoot.querySelector('#copyMetadata').removeEventListener('click', this.onMenuItemClick, false);
this.shadowRoot.querySelector('#clearMetadata').removeEventListener('click', this.onMenuItemClick, false);
}
}
});
var PluginMenu = document.registerElement('plugin-menu', {prototype: pluginMenuProto});
/* Create a <plugin-card> element type. */
var pluginCardProto = Object.create(HTMLElement.prototype, {
@@ -63,12 +134,34 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
}
}
if (isValid) {
var card = evt.target.parentElement.parentElement.parentNode.host;
/* Set up table tab event handlers. */
var elements = card.shadowRoot.querySelector('#tableTabs').children;
for (var i = 0; i < elements.length; ++i) {
if (elements[i].hasAttribute('data-for')) {
elements[i].removeEventListener('click', card.showEditorTable, false);
}
}
/* Set up button event handlers. */
card.shadowRoot.querySelector('#accept').removeEventListener('click', card.hideEditor, false);
card.shadowRoot.querySelector('#cancel').removeEventListener('click', card.hideEditor, false);
/* Remove drag 'n' drop event handlers. */
var elements = document.getElementById('pluginsNav').children;
for (var i = 0; i < elements.length; ++i) {
elements[i].removeAttribute('draggable', true);
elements[i].removeEventListener('dragstart', handlePluginDragStart, false);
}
elements = card.shadowRoot.querySelector('table');
for (var i = 0; i < elements.length; ++i) {
if (elements[i].className.indexOf('loadAfter') != -1 || elements[i].className.indexOf('req') != -1 || elements[i].className.indexOf('inc') != -1) {
elements[i].removeEventListener('drop', handlePluginDrop, false);
elements[i].removeEventListener('dragover', handlePluginDragOver, false);
}
}
/* Disable priority hover in plugins list. */
document.getElementById('pluginsNav').classList.toggle('editMode', false);
@@ -77,7 +170,7 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
document.getElementsByTagName('header')[0].classList.toggle('editMode', false);
/* Hide editor. */
evt.target.parentElement.parentElement.parentNode.host.classList.toggle('flip');
card.classList.toggle('flip');
}
}
},
@@ -121,29 +214,43 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
}
},
onMenuItemClick: {
onMenuButtonClick: {
value: function(evt) {
if (evt.target.id == 'editMetadata') {
/* Show editing controls. */
evt.target.parentElement.parentElement.parentNode.host.showEditor();
} else if (evt.target.id == 'copyMetadata') {
/* Open a new plugin menu. */
var menu = new PluginMenu();
var card = evt.currentTarget.parentElement.parentElement.parentNode.host;
} else if (evt.target.id == 'clearMetadata') {
showMessageDialog('Clear Plugin Metadata', 'Are you sure you want to clear all existing user-added metadata from "' + evt.target.parentElement.parentElement.querySelector('h1').textContent + '"?');
menu.setAttribute('data-for', card.id);
/* To prevent the click event closing this menu just after it was
opened, attach an ID to it, then stop the current event and send
off a new one. */
menu.id = Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
var main = document.getElementById('main');
main.appendChild(menu);
/* Set page position of menu. */
function getOffset( el, stopEl ) {
var _x = 0;
var _y = 0;
while( el && el != stopEl ) {
_x += el.offsetLeft;
_y += el.offsetTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
}
},
var offset = getOffset(evt.target, main);
onMenuClick: {
value: function(evt) {
var section = evt.currentTarget.parentElement.parentElement;
section.querySelector('#editMetadata').addEventListener('click', section.parentNode.host.onMenuItemClick, false);
section.querySelector('#copyMetadata').addEventListener('click', section.parentNode.host.onMenuItemClick, false);
section.querySelector('#clearMetadata').addEventListener('click', section.parentNode.host.onMenuItemClick, false);
menu.style.top = (offset.top + evt.target.offsetHeight + 10) + 'px';
menu.style.right = (main.offsetWidth - offset.left - evt.target.offsetWidth - 10) + 'px';
section.querySelector('#menu').classList.toggle('hidden');
evt.stopPropagation();
menu.dispatchEvent(new CustomEvent('click', { newMenu: true }));
}
},
@@ -175,10 +282,16 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
var messages = document.createElement('ul');
this.appendChild(messages);
this.shadowRoot.querySelector('#menuButton').addEventListener('click', this.onMenuClick, false);
this.shadowRoot.querySelector('#menuButton').addEventListener('click', this.onMenuButtonClick, false);
}
},
detachedCallback: {
value: function() {
this.shadowRoot.querySelector('#menuButton').removeEventListener('click', this.onMenuButtonClick, false);
}
}
});
@@ -218,7 +331,12 @@ var messageDialogProto = Object.create(HTMLDialogElement.prototype, {
onButtonClick: {
value: function(evt) {
evt.currentTarget.parentElement.parentElement.close( evt.target.className == 'accept' );
var dialog = evt.currentTarget.parentElement.parentElement;
dialog.querySelector('.accept').removeEventListener('click', dialog.onButtonClick, false);
dialog.querySelector('.cancel').removeEventListener('click', dialog.onButtonClick, false);
dialog.close( evt.target.className == 'accept' );
}
},
@@ -270,13 +388,6 @@ var messageDialogProto = Object.create(HTMLDialogElement.prototype, {
buttons.appendChild(cancel);
}
},
detachedCallback: {
value: function() {
this.removeEventListener('click', this.onButtonClick, false);
}
}
});
@@ -291,7 +402,21 @@ var EditableTableProto = Object.create(HTMLTableElement.prototype, {
removeRow: {
value: function(evt) {
evt.target.parentElement.parentElement.removeChild(evt.target.parentElement);
var tr = evt.target.parentElement;
var tbody = tr.parentElement
var table = tbody.parentElement;
/* Remove row edit listeners. */
var inputs = row.getElementsByTagName('input');
for (var i = 0; i < inputs.length; ++i) {
inputs[i].removeEventListener('dblclick', toggleInputRO, false);
}
/* Remove deletion listener. */
evt.target.removeEventListener('click', table.removeRow, false);
/* Now remove row. */
tbody.removeChild(tr);
}
},
@@ -349,6 +474,30 @@ var EditableTableProto = Object.create(HTMLTableElement.prototype, {
},
detachedCallback: {
value: function() {
/* Remove event listeners. */
var tbody = this.getElementsByTagName('tbody')[0];
/* Remove row edit listeners. */
var inputs = tbody.getElementsByTagName('input');
for (var i = 0; i < inputs.length; ++i) {
inputs[i].removeEventListener('dblclick', toggleInputRO, false);
}
/* Remove deletion listener. */
var icons = tbody.getElementsByClassName('fa-trash-o');
for (var i = 0; i < icons.length; ++i) {
icons[i].removeEventListener('click', this.removeRow, false);
}
/* Remove new row listener. */
this.querySelector('tbody tr:last-child').removeEventListener('dblclick', this.addEmptyRow, false);
}
},
});
var EditableTable = document.registerElement('editable-table', {
prototype: EditableTableProto,
+39 -44
View File
@@ -22,7 +22,7 @@
position: relative;
overflow:visible;
transition: 0.6s;
transform-style: flat;
transform-style: preserve-3d;
}
:host([data-active=false]) #activeTick {
visibility: hidden;
@@ -37,28 +37,27 @@
display: none;
}
:host(.flip) {
transform: rotateY(180deg);
}
#front, #editor {
backface-visibility: hidden;
transition: 0.6s;
display: block;
}
#editor {
transform: rotateY(180deg);
transform: rotateY(180deg) translateZ(1px);
display:none;
}
:host(.flip) {
transform: rotateY(180deg);
}
:host(.flip) #front {
display:none;
display: none;
}
:host(.flip) #editor {
backface-visibility: visible;
display:block;
display: block;
}
#icons {
background: transparent;
margin: 0;
@@ -74,7 +73,6 @@
text-align: center;
display: inline-block;
margin: 0.2em;
cursor: pointer;
}
#icons > li:hover {
color: black;
@@ -85,32 +83,6 @@
margin: 0.2em;
cursor: pointer;
}
#menu {
background: white;
box-shadow:0 0 3px 0px rgba(0,0,0,0.5);
font:10pt/1.5 Helvetica,sans-serif;
list-style-type: none;
position: absolute;
right: 0.5em;
width: 14em;
z-index: 2;
text-align: left;
opacity: 1;
padding:0;
color: black;
top: 2em;
}
#menu > li {
padding: 0.2em 0.5em;
cursor: pointer;
display: block;
}
#menu > li:hover {
background:#e3e3e3;
}
#menu span {
padding-right: 0.4em;
}
::content h1, h1 {
margin: 0;
font-weight: normal;
@@ -176,11 +148,6 @@
<li id="menuButton" class="fa fa-ellipsis-v fa-lg">
</li>
</ol>
<ol id="menu" class="hidden">
<li id="editMetadata"><span class="fa fa-pencil fa-fw"></span>Edit Metadata</li>
<li id="copyMetadata"><span class="fa fa-copy fa-fw"></span>Copy Metadata As Text</li>
<li id="clearMetadata"><span class="fa fa-trash-o fa-fw"></span>Clear User Metadata</li>
</ol>
</section>
<section id="editor">
<h1></h1>
@@ -293,6 +260,34 @@
<content select=".priority"></content>
</a>
</template>
<template id="pluginMenu">
<style>
@import 'css/font-awesome.min.css';
:host {
background: white;
box-shadow:0 0 3px 0px rgba(0,0,0,0.5);
font:10pt/1.5 Helvetica,sans-serif;
position: absolute;
z-index: 5;
width: 14em;
text-align: left;
padding:0;
}
div {
padding: 0.2em 0.5em;
cursor: pointer;
}
div:hover {
background: #e3e3e3;
}
span {
padding-right: 0.4em;
}
</style>
<div id="editMetadata"><span class="fa fa-pencil fa-fw"></span>Edit Metadata</div>
<div id="copyMetadata"><span class="fa fa-copy fa-fw"></span>Copy Metadata As Text</div>
<div id="clearMetadata"><span class="fa fa-trash-o fa-fw"></span>Clear User Metadata</div>
</template>
<template id="fileRow">
<tr>
@@ -349,7 +344,7 @@
<td><input class="registry" type="text" readonly>
<td class="fa fa-trash-o fa-fw">
</template>
<noscript>The LOOT Report requires Javascript to be enabled in order to function.</noscript>
<header>
<div id="headerOverlay"></div>