Started to refactor JS code, and use Shadow DOM.

Styling is a bit messed up now, and some interactivity probably doesn't
work.
This commit is contained in:
WrinklyNinja
2014-07-18 12:36:41 +01:00
parent 3d01034a8f
commit b26f75f872
6 changed files with 403 additions and 99 deletions
+80
View File
@@ -0,0 +1,80 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2013-2014 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
'use strict';
/* Create a <plugin-card> element type. */
var pluginCardProto = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var template = document.getElementById('pluginCard');
var clone = document.importNode(template.content, true);
this.createShadowRoot().appendChild(clone);
var h1 = document.createElement('h1');
this.appendChild(h1);
var crc = document.createElement('div');
crc.className = 'crc';
this.appendChild(crc);
var version = document.createElement('div');
version.className = 'version';
this.appendChild(version);
}
}
});
var PluginCard = document.registerElement('plugin-card', {prototype: pluginCardProto});
/* Create a <plugin-li> element type. */
var pluginLIProto = Object.create(HTMLLIElement.prototype, {
createdCallback: {
value: function() {
var template = document.getElementById('pluginLI');
var clone = document.importNode(template.content, true);
this.createShadowRoot().appendChild(clone);
var name = document.createElement('span');
name.className = 'name';
this.appendChild(name);
var priority = document.createElement('span');
priority.className = 'priority';
this.appendChild(priority);
}
}
});
var PluginListItem = document.registerElement('plugin-li', {
prototype: pluginLIProto,
extends: 'li'
});
+8
View File
@@ -0,0 +1,8 @@
/*
RequireJS order 1.0.5 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
(function(){function k(a){var b=a.currentTarget||a.srcElement,c;if(a.type==="load"||l.test(b.readyState)){a=b.getAttribute("data-requiremodule");j[a]=!0;for(a=0;c=g[a];a++)if(j[c.name])c.req([c.name],c.onLoad);else break;a>0&&g.splice(0,a);setTimeout(function(){b.parentNode.removeChild(b)},15)}}function m(a){var b,c;a.setAttribute("data-orderloaded","loaded");for(a=0;c=h[a];a++)if((b=i[c])&&b.getAttribute("data-orderloaded")==="loaded")delete i[c],require.addScriptToDom(b);else break;a>0&&h.splice(0,
a)}var f=typeof document!=="undefined"&&typeof window!=="undefined"&&document.createElement("script"),n=f&&(f.async||window.opera&&Object.prototype.toString.call(window.opera)==="[object Opera]"||"MozAppearance"in document.documentElement.style),o=f&&f.readyState==="uninitialized",l=/^(complete|loaded)$/,g=[],j={},i={},h=[],f=null;define({version:"1.0.5",load:function(a,b,c,e){var d;b.nameToUrl?(d=b.nameToUrl(a,null),require.s.skipAsync[d]=!0,n||e.isBuild?b([a],c):o?(e=require.s.contexts._,!e.urlFetched[d]&&
!e.loaded[a]&&(e.urlFetched[d]=!0,require.resourcesReady(!1),e.scriptCount+=1,d=require.attach(d,e,a,null,null,m),i[a]=d,h.push(a)),b([a],c)):b.specified(a)?b([a],c):(g.push({name:a,req:b,onLoad:c}),require.attach(d,null,a,k,"script/cache"))):b([a],c)}})})();
+154
View File
@@ -0,0 +1,154 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2013-2014 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
'use strict';
/* Plugin object for managing data and UI interaction. */
function Plugin(obj) {
this.name = obj.name;
this.crc = obj.crc;
this.version = obj.version;
this.isActive = obj.isActive;
this.isDummy = obj.isDummy;
this.loadsBSA = obj.loadsBSA;
this.masterlist = obj.masterlist;
this.userlist = obj.userlist;
this.id = this.name.replace(/\s+/g, '');
Plugin.prototype.onMenuItemClick = function(evt) {
}
Plugin.prototype.onMenuClick = function(evt) {
card.querySelector('.editMetadata').addEventListener('click', this.onMenuItemClick, false);
card.querySelector('.editMetadata').addEventListener('click', this.onMenuItemClick, false);
card.querySelector('.editMetadata').addEventListener('click', this.onMenuItemClick, false);
}
Plugin.prototype.createCard = function() {
var card = new PluginCard();
this.card = card;
card.id = this.id;
card.querySelector('h1').textContent = this.name;
card.querySelector('.crc').textContent = this.crc;
card.querySelector('.version').textContent = this.version;
card.setAttribute('data-active', this.isActive);
card.setAttribute('data-dummy', this.isDummy);
card.setAttribute('data-bsa', this.loadsBSA);
card.setAttribute('data-edits', this.userlist != undefined);
card.shadowRoot.querySelector('.pluginMenu').addEventListener('click', this.onMenuClick, false);
document.getElementById('main').appendChild(card);
}
Plugin.prototype.getPriorityString = function() {
if (this.userlist) {
var priorityText = 'Priority: ' + this.userlist.modPriority + ', Global: ';
if (this.userlist.isGlobalPriority) {
priorityText += '✓';
} else {
priorityText += '✗';
}
return priorityText;
} else if (this.masterlist) {
var priorityText = 'Priority: ' + this.masterlist.modPriority + ', Global: ';
if (this.masterlist.isGlobalPriority) {
priorityText += '✓';
} else {
priorityText += '✗';
}
return priorityText;
} else {
return 'Priority: 0, Global: ✗';
}
}
Plugin.prototype.createListItem = function() {
var li = new PluginListItem();
li.shadowRoot.querySelector('a').href = this.id;
li.querySelector('.name').textContent = this.name;
li.querySelector('.priority').textContent = this.getPriorityString();
li.setAttribute('data-dummy', this.isDummy);
li.setAttribute('data-bsa', this.loadsBSA);
li.setAttribute('data-edits', this.userlist != undefined);
document.getElementById('pluginsNav').appendChild(li);
}
Plugin.prototype.changeAction = function(change) {
console.log(change);
}
Plugin.prototype.observer = function(changes) {
//console.log(changes);
changes.forEach(function(change) {
//for (var i = 0; i < changes.length; ++i) {
if (change.name == 'name') {
change.object.card.querySelector('h1').textContent = change.object[change.name];
}
});
//changes.forEach(changeAction);
}
this.createCard();
this.createListItem();
Object.observe(this, this.observer);
}
function jsonToPlugin(key, value) {
if (value !== null && value.__type === 'Plugin') {
var p = new Plugin(value);
return p;
}
return value;
}
+22 -99
View File
@@ -408,6 +408,8 @@ function showEditor(evt) {
setupTable(tables[i].getElementsByTagName('tbody')[0]);
}
/* Fill in table data. */
/* Set up table tab event handlers. */
var elements = editor.getElementsByClassName('tableTabs')[0].children;
for (var i = 0; i < elements.length; ++i) {
@@ -616,13 +618,7 @@ function setupEventHandlers() {
document.getElementById('cancelSortButton').addEventListener('click', cancelSort, false);
document.getElementById('filtersToggle').addEventListener('click', toggleFiltersList, false);
/* Set up handlers for game menus. */
elements = document.getElementById('main').children;
for (var i = 2; i < elements.length; ++i) {
var pluginMenu = elements[i].getElementsByClassName('pluginMenu')[0];
pluginMenu.addEventListener('click', toggleMenu, false);
}
/* Set up event handler for hover text. */
document.body.addEventListener('mouseover', toggleHoverText, false);
}
function processCefError(errorCode, errorMessage) {
@@ -735,25 +731,7 @@ function initGlobalVars() {
});
}
function getPriorityString(plugin) {
if (plugin.userlist) {
var priorityText = 'Priority: ' + plugin.userlist.modPriority + ', Global: ';
if (plugin.userlist.isGlobalPriority) {
priorityText += '✓';
} else {
priorityText += '✗';
}
return priorityText;
} else if (plugin.masterlist) {
var priorityText = 'Priority: ' + plugin.masterlist.modPriority + ', Global: ';
if (plugin.masterlist.isGlobalPriority) {
priorityText += '✓';
} else {
priorityText += '✗';
}
return priorityText;
} else {
return 'Priority: 0, Global: ✗';
}
}
function getTagsStrings(plugin) {
var tagsAdded = [];
@@ -806,10 +784,11 @@ function getTagsStrings(plugin) {
tagsRemoved: tagsRemoved.join(', ')
};
}
function updateInterfaceWithGameInfo(response) {
try {
loot.game = JSON.parse(response);
loot.game = JSON.parse(response, jsonToPlugin);
} catch (e) {
console.log(e);
console.log('Response: ' + response);
@@ -842,76 +821,20 @@ function updateInterfaceWithGameInfo(response) {
totalMessageNo = loot.game.globalMessages.length;
var pluginsList = document.getElementById('main');
var pluginsNav = document.getElementById('pluginsNav');
for (var i = 0; i < loot.game.plugins.length; ++i) {
loot.game.plugins.forEach(function(plugin) {
var content, clone;
/* First add link to navbar. */
content = document.getElementById('pluginNav').content;
clone = document.importNode(content, true);
pluginsNav.appendChild(clone);
clone = pluginsNav.lastElementChild;
clone.getElementsByClassName('name')[0].textContent = loot.game.plugins[i].name;
clone.getElementsByTagName('a')[0].href = '#' + loot.game.plugins[i].name.replace(/\s+/g, '');
clone.getElementsByClassName('priority')[0].textContent = getPriorityString(loot.game.plugins[i]);
if (loot.game.plugins[i].isDummy) {
clone.getElementsByClassName('dummyPlugin')[0].className += ' fa fa-eye-slash';
}
if (loot.game.plugins[i].loadsBSA) {
clone.getElementsByClassName('loadsBSA')[0].className += ' fa fa-paperclip';
}
if (loot.game.plugins[i].userlist) {
clone.getElementsByClassName('hasUserEdits')[0].className += ' fa fa-user';
}
/* Now add plugin 'card'. */
content = document.getElementById('pluginSection').content;
clone = document.importNode(content, true);
pluginsList.appendChild(clone);
clone = pluginsList.lastElementChild;
clone.setAttribute('data-active', loot.game.plugins[i].isActive);
clone.id = loot.game.plugins[i].name.replace(/\s+/g, '');
if (loot.game.plugins[i].isActive) {
if (plugin.isActive) {
++activePluginNo;
}
/*if (loot.game.plugins[i].isDirty) {
/*if (plugin.isDirty) {
++dirtyPluginNo;
}*/
clone.getElementsByTagName('h1')[0].textContent = loot.game.plugins[i].name;
if (loot.game.plugins[i].crc != 0) {
clone.getElementsByClassName('crc')[0].textContent = 'CRC: ' + loot.game.plugins[i].crc;
} else {
clone.getElementsByClassName('crc')[0].textContent = '';
}
if (loot.game.plugins[i].isDummy) {
showElement(clone.getElementsByClassName('dummyPlugin')[0]);
}
if (loot.game.plugins[i].loadsBSA) {
showElement(clone.getElementsByClassName('loadsBSA')[0]);
}
if (loot.game.plugins[i].userlist) {
showElement(clone.getElementsByClassName('hasUserEdits')[0]);
}
if (loot.game.plugins[i].version) {
clone.getElementsByClassName('version')[0].textContent = 'Version: ' + loot.game.plugins[i].version;
} else {
hideElement(clone.getElementsByClassName('version')[0]);
}
var tags = getTagsStrings(loot.game.plugins[i]);
/*
var tags = getTagsStrings(plugin);
if (tags.tagsAdded) {
clone.getElementsByClassName('tag add')[0].textContent = tags.tagsAdded;
} else {
@@ -923,16 +846,12 @@ function updateInterfaceWithGameInfo(response) {
hideElement(clone.getElementsByClassName('tag remove')[0]);
}
clone.getElementsByClassName('editMetadata')[0].setAttribute('data-target', clone.id);
clone.getElementsByClassName('copyMetadata')[0].setAttribute('data-target', clone.id);
clone.getElementsByClassName('clearMetadata')[0].setAttribute('data-target', clone.id);
if (loot.game.plugins[i].masterlist && loot.game.plugins[i].masterlist.msg && loot.game.plugins[i].masterlist.msg.length != 0) {
for (var j = 0; j < loot.game.plugins[i].masterlist.msg.length; ++j) {
if (plugin.masterlist && plugin.masterlist.msg && plugin.masterlist.msg.length != 0) {
plugin.masterlist.msg.forEach(function(message) {
var messageLi = document.createElement('li');
messageLi.className = loot.game.plugins[i].masterlist.msg[j].type;
messageLi.className = message.type;
// Use the Marked library for Markdown formatting support.
messageLi.innerHTML = marked(loot.game.plugins[i].masterlist.msg[j].content[0].str);
messageLi.innerHTML = marked(message.content[0].str);
clone.getElementsByTagName('ul')[0].appendChild(messageLi);
if (messageLi.className == 'warn') {
@@ -941,11 +860,12 @@ function updateInterfaceWithGameInfo(response) {
errorMessageNo++;
}
totalMessageNo++;
}
});
} else {
clone.getElementsByTagName('ul')[0].className += ' hidden';
}
}
*/
});
document.getElementById('filterTotalMessageNo').textContent = totalMessageNo;
document.getElementById('totalMessageNo').textContent = totalMessageNo;
document.getElementById('totalWarningNo').textContent = warnMessageNo;
@@ -970,7 +890,10 @@ function getGameData() {
});
}
require(['js/marked'], function(response) {
require.config({
baseUrl: "js",
});
require(['marked', 'order!custom', 'order!plugin'], function(response) {
marked = response;
/* Make sure settings are what I want. */
marked.setOptions({
+138
View File
@@ -7,6 +7,144 @@
<script src="js/require.js"></script>
</head>
<body>
<template id="pluginCard">
<style>
@import 'css/font-awesome.min.css';
:host {
display: block;
background:white;
overflow:hidden;
margin:1em;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
padding:1em;
position: relative;
overflow:visible;
}
:host.flip {
transform: rotateY(180deg);
}
.icons {
background: transparent;
margin: 0;
padding: 0;
list-style-type: none;
position: absolute;
top: 1em;
right: 0.5em;
overflow:visible;
}
.icons .fa {
color: grey;
text-align: center;
display: inline-block;
margin: 0.2em;
cursor: pointer;
}
.icons > li:hover {
color: black;
}
.pluginMenu {
width: 1.5em;
text-align: center;
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;
}
.menu > li {
padding: 0.2em 0.5em;
cursor: pointer;
}
.menu > li:hover {
background:#e3e3e3;
}
::content h1 {
color: blue;
}
.activeTick {
color: green;
}
:host([data-active=false]) .activeTick {
display: none;
}
:host([data-bsa=false]) .loadsBSA {
display: none;
}
:host([data-dummy=false]) .dummyPlugin {
display: none;
}
:host([data-edits=false]) .hasUserEdits {
display: none;
}
</style>
<span class="activeTick fa fa-check" title="Active Plugin"></span>
<content select="h1"></content>
<content select=".version"></content>
<content select=".crc"></content>
<div class="tag add"></div>
<div class="tag remove"></div>
<ul>
<!-- Plugin message <li> elements go here. -->
</ul>
<ol class="icons">
<li class="dummyPlugin fa fa-eye-slash" title="Dummy Plugin"></li>
<li class="loadsBSA fa fa-paperclip" title="Loads BSA"></li>
<li class="hasUserEdits fa fa-user" title="Has User Metadata"></li>
<li class="fa fa-ellipsis-v fa-lg pluginMenu" data-action="toggle-menu">
<ol class="menu hidden">
<li class="editMetadata" data-action="show-editor" data-target=""><span class="fa fa-pencil fa-fw"></span> Edit Metadata</li>
<li class="copyMetadata" data-action="copy-metadata" data-target=""><span class="fa fa-copy fa-fw"></span> Copy Metadata As Text</li>
<li class="clearMetadata" data-action="clear-metadata" data-target=""><span class="fa fa-trash-o fa-fw"></span> Clear User Metadata</li>
</ol>
</li>
</ol>
</template>
<template id="pluginLI">
<style>
@import 'css/font-awesome.min.css';
:host > span {
display: inline-block;
width: 1.2em;
}
:host([data-bsa=false]) .loadsBSA {
visibility: hidden;
}
:host([data-dummy=false]) .dummyPlugin {
visibility: hidden;
}
:host([data-edits=false]) .hasUserEdits {
visibility: hidden;
}
</style>
<span class="dummyPlugin fa fa-eye-slash" title="Dummy Plugin"></span>
<span class="loadsBSA fa fa-paperclip" title="Loads BSA"></span>
<span class="hasUserEdits fa fa-user" title="Has User Metadata"></span>
<a href="">
<content select=".name"></content>
<content select=".priority"></content>
</a>
</template>
<template id="fileRow">
<tr>
<td><input class="file" type="text" readonly required>
+1
View File
@@ -225,6 +225,7 @@ namespace loot {
// the first will be handled by userlist lookups, and the other two are probably
// going to get moved around, haven't decided how best to handle the split between masterlist, userlist and plugin-sourced metadata.
YAML::Node pluginNode;
pluginNode["__type"] = "Plugin"; // For conversion back into a JS typed object.
pluginNode["name"] = plugin.Name();
pluginNode["isActive"] = g_app_state.CurrentGame().IsActive(plugin.Name());
pluginNode["isDummy"] = false; // Set to false for now because null is a bit iffy and we just don't know yet. Although, we could read the record count from the TES4 header... Usual check is (plugin.second.FormIDs().size() == 0);