// ==UserScript==
// @name Klatu's Elements script
// @namespace Klatu
// @version 18
// @description Deck manager
// @author Klatu
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js
// @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.6.0/clipboard.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/notify/0.4.2/notify.min.js
// @match http://www.kongregate.com/games/zanzarino/elements*
// @grant none
// @noframes
// ==/UserScript==
jQuery.noConflict();
//main function
window.addEventListener('load', () => {
var copyDeckCodeClipboard,
createDeckPopup,
createDeckButton,
createDeckCodeInput,
createDeckNameInput,
deckBeingDeleted,
deckBeingModified,
decks,
decksDiv,
deleteConfirmButton,
deleteConfirmPopup,
deleteConfirmSpan,
div,
exportClipboard,
exportImportContainer,
exportButton,
importButton,
importPopup,
importPopupButton,
importPopupInput,
modifyDeckButton,
modifyDeckPopup,
modifyDeckCodeInput,
modifyDeckNameInput,
popupContainer,
style,
styleElement,
tab,
tabLink;
function showPopup(popup) {
popupContainer.style.display = 'flex';
for (var child of popupContainer.children) child.style.display = 'none';
popup.style.display = 'block';
}
function hidePopups() {
popupContainer.style.display = 'none';
}
function createCloseCross() {
var cross = document.createElement('span');
cross.className = 'klatu-close-cross';
cross.addEventListener('click', hidePopups);
return cross;
}
function saveDecks() {
localStorage.setItem('elementsDecks', JSON.stringify(decks));
}
function createDeck() {
var deck = {
name: createDeckNameInput.value,
code: createDeckCodeInput.value
};
decks.push(deck);
saveDecks();
hidePopups();
updateDecksDiv();
}
function focusWhenUserPressesTab(e) {
if (e.which === 9 || e.keyCode === 9) {
e.preventDefault();
this.focus();
}
}
function createForm(submitFunction) {
function submit(e) {
if (e.which === 13 || e.keyCode === 13) {
e.preventDefault();
submitFunction();
}
}
//link the last element with the first one and add the submit listener to it
arguments[arguments.length - 1].addEventListener('keydown', focusWhenUserPressesTab.bind(arguments[1]));
arguments[arguments.length - 1].addEventListener('keydown', submit);
//link the each of the other elements with the next ones
for (var i = 1; arguments[i + 1]; i++) {
arguments[i].addEventListener('keydown', focusWhenUserPressesTab.bind(arguments[i + 1]));
arguments[i].addEventListener('keydown', submit);
}
}
function modifyDeck() {
deckBeingModified.name = modifyDeckNameInput.value;
deckBeingModified.code = modifyDeckCodeInput.value;
saveDecks();
hidePopups();
updateDecksDiv();
}
function deleteDeck() {
decks.splice(decks.indexOf(deckBeingDeleted), 1);
saveDecks();
updateDecksDiv();
hidePopups();
}
function showCreateDeckPopup() {
createDeckNameInput.value = '';
createDeckCodeInput.value = '';
showPopup(createDeckPopup);
createDeckNameInput.focus();
}
function copyDeckCode() {
if (!Clipboard.isSupported()) {
alert('Wait for the script\'s next version please :(');
}
}
function showModifyDeckPopup() {
var index = this.parentNode.parentNode.getAttribute('data-klatu-index');
deckBeingModified = decks[index];
modifyDeckNameInput.value = deckBeingModified.name;
modifyDeckCodeInput.value = deckBeingModified.code;
showPopup(modifyDeckPopup);
}
function showDeleteConfirmPopup() {
var index = this.parentNode.parentNode.getAttribute('data-klatu-index');
deckBeingDeleted = decks[index];
deleteConfirmSpan.innerText = 'Do you want to delete the deck "'+deckBeingDeleted.name+'"?';
showPopup(deleteConfirmPopup);
}
function returnElementWhenItExists(id) {
return new Promise((resolve, reject) => {
var element = $(id),
interval;
if (element) resolve(element);
interval = setInterval(() => {
var element = $(id);
if (element) {
clearInterval(interval);
resolve(element);
}
}, 1000);
});
}
function updateDecksDiv() {
for (var i = decksDiv.children.length - 1; 0 <= i; i--) decksDiv.children[i].remove();
for (i = 0; i < decks.length; i++) {
var deck = decks[i];
var deckDiv = document.createElement('div');
var deckNameDiv = document.createElement('div');
var deckActionsDiv=document.createElement('div');
var deckCopySpan = document.createElement('span');
var deckModifySpan = document.createElement('span');
var deckDeleteSpan = document.createElement('span');
deckDiv.setAttribute('data-klatu-index', i);
deckDiv.className = 'klatu-deck-div klatu-flex klatu-spaced-flex';
deckNameDiv.innerText = deck.name;
deckNameDiv.className = 'klatu-deck-name-div';
deckDiv.appendChild(deckNameDiv);
deckCopySpan.setAttribute('data-clipboard-text', deck.code);
deckCopySpan.addEventListener('click', copyDeckCode);
deckActionsDiv.appendChild(deckCopySpan);
deckActionsDiv.className='klatu-deck-actions-div';
deckModifySpan.className = 'klatu-deck-modify-span';
deckModifySpan.addEventListener('click', showModifyDeckPopup);
deckActionsDiv.appendChild(deckModifySpan);
deckCopySpan.className = 'klatu-deck-copy-span';
deckDeleteSpan.className = 'klatu-deck-delete-span';
deckDeleteSpan.addEventListener('click', showDeleteConfirmPopup);
deckActionsDiv.appendChild(deckDeleteSpan);
deckDiv.appendChild(deckActionsDiv);
if (i % 2) deckDiv.className += ' even';
decksDiv.appendChild(deckDiv);
}
}
function importDecks(event){
try{
var decksBeingImported=JSON.parse(importPopupInput.value);
if(decksBeingImported instanceof Array){
for(var deckBeingImported of decksBeingImported){
isInDecks=false;
for(var i=0; i<decks.length&&!isInDecks; i++){
var deck=decks[i];
if(deck.name===deckBeingImported.name&&deck.code===deckBeingImported.code) isInDecks=true;
}
if(!isInDecks) decks.push(deckBeingImported);
}
saveDecks();
updateDecksDiv();
hidePopups();
}
else jQuery.notify("Invalid code.", "error");
}
catch(error){
jQuery.notify("Invalid code.", "error");
}
}
//add own style
style =
'.klatu-close-cross {' +
' position: absolute;' +
' top: 4px;' +
' right: 4px;' +
' background-color: #E95420;' +
' border-radius: 50%;' +
' width: 18px;' +
' height: 18px;' +
' text-align: center;' +
' line-height: 18px;' +
' cursor: pointer;' +
'}' +
'.klatu-close-cross:before {' +
' content: "×";' +
' font-size: 15px;' +
'}' +
'.klatu-popup {' +
' padding: 26px 26px 8px 26px;' +
' position: relative;' +
' background-color: #333;' +
' text-align: center;' +
' font-size: 22px;' +
' color: white;' +
'}' +
'.klatu-popup>input {' +
' margin: 0 auto;' +
' display: block;' +
' width: 750px;' +
' font-family: monospace;' +
'}' +
'.klatu-flex {' +
' display: flex;' +
' align-items: center;' +
'}' +
'.klatu-centered-flex {'+
' justify-content: center;' +
'}'+
'.klatu-spaced-flex {'+
' justify-content: space-between;' +
'}'+
'.klatus-elements-script-button {' +
' display: block;' +
' padding: 6px 12px;' +
' margin: 8px auto 0 auto;' +
' font-size: 14px;' +
' font-weight: 400;' +
' line-height: 1.42857143;' +
' text-align: center;' +
' white-space: nowrap;' +
' vertical-align: middle;' +
' -ms-touch-action: manipulation;' +
' touch-action: manipulation;' +
' cursor: pointer;' +
' -webkit-user-select: none;' +
' -moz-user-select: none;' +
' -ms-user-select: none;' +
' user-select: none;' +
' width: 120px;' +
' background-image: none;' +
' border: 1px solid transparent;' +
' border-radius: 4px;' +
' color: #fff;' +
' background-color: #337ab7;' +
' border-color: #2e6da4;' +
'}' +
'.klatus-elements-script-button.export, .klatus-elements-script-button.import {' +
' background-color: #666;' +
' border-color: #af9052;' +
'}' +
'.klatu-deck-actions-div>span {' +
' font-size: 20px;' +
' font-weight: bold;' +
' padding: 1px 3px 1px 3px;' +
' cursor: pointer;' +
'}' +
'.klatu-deck-div.even {' +
' background-color: #333;' +
'}' +
'.klatu-deck-delete-span:before {' +
' content: "×";' +
' color: red;' +
'}' +
'.klatu-deck-copy-span:before {' +
' content: "?";' +
' color: #0275d8' +
'}' +
'.klatu-deck-modify-span:before {' +
' content: "✍";' +
' color: #f0ad4e' +
'}' +
'.tabpane {' +
' height: 489px' +
'}' +
'#klatu-popup-container {' +
' justify-content: center;' +
' align-items: center;' +
' position: fixed;' +
' z-index: 9999;' +
' top: 0;' +
' left: 0;' +
' width: 100vw;' +
' height: 100vh;' +
' background-color: rgba(0, 0, 0, 0.75);' +
'}' +
'.notiyjs-corner {' +
' z-index: 99999;' +
'}'+
'.klatu-deck-div {' +
' font-size: 15px;' +
' font-family: sans-serif;' +
' padding: 6px;' +
' background-color: #111;' +
' color: #eee;' +
'}';
styleElement = document.createElement('style');
styleElement.innerHTML = style;
document.head.appendChild(styleElement);
//create the popup container
popupContainer = document.createElement('div');
popupContainer.id = 'klatu-popup-container';
popupContainer.style.display = 'none';
popupContainer.addEventListener('click', function(e) {
if (e.target === this) hidePopups();
});
document.body.appendChild(popupContainer);
//hide visible popups when the user presses escape
window.addEventListener('keyup', (e) => {
if (e.which === 27 || e.keyCode === 27) hidePopups();
});
//create the delete confirm popup
deleteConfirmPopup = document.createElement('div');
deleteConfirmPopup.className = 'klatu-popup';
deleteConfirmSpan = document.createElement('span');
deleteConfirmButton = document.createElement('button');
deleteConfirmButton.className = 'klatus-elements-script-button';
deleteConfirmButton.innerText = 'Yes';
deleteConfirmButton.addEventListener('click', deleteDeck);
deleteConfirmPopup.appendChild(deleteConfirmSpan);
deleteConfirmPopup.appendChild(deleteConfirmButton);
deleteConfirmPopup.appendChild(createCloseCross());
popupContainer.appendChild(deleteConfirmPopup);
//create the create deck popup
createDeckPopup = document.createElement('div');
createDeckPopup.className = 'klatu-popup';
createDeckNameInput = document.createElement('input');
createDeckNameInput.placeholder = 'Enter your deck\'s name';
createDeckPopup.appendChild(createDeckNameInput);
createDeckCodeInput = document.createElement('input');
createDeckCodeInput.placeholder = 'Enter your deck\'s code';
createDeckPopup.appendChild(createDeckCodeInput);
createDeckButton = document.createElement('button');
createDeckButton.className = 'klatus-elements-script-button';
createDeckButton.innerText = 'Add deck';
createDeckButton.addEventListener('click', createDeck);
createDeckPopup.appendChild(createDeckButton);
createDeckPopup.appendChild(createCloseCross());
popupContainer.appendChild(createDeckPopup);
createForm(createDeck, createDeckNameInput, createDeckCodeInput);
//create the modify deck popup
modifyDeckPopup = document.createElement('div');
modifyDeckPopup.className = 'klatu-popup';
modifyDeckNameInput = document.createElement('input');
modifyDeckNameInput.placeholder = 'Enter your deck\'s new name';
modifyDeckPopup.appendChild(modifyDeckNameInput);
modifyDeckCodeInput = document.createElement('input');
modifyDeckCodeInput.placeholder = 'Enter your deck\'s new code';
modifyDeckPopup.appendChild(modifyDeckCodeInput);
modifyDeckButton = document.createElement('button');
modifyDeckButton.className = 'klatus-elements-script-button';
modifyDeckButton.innerText = 'Modify deck';
modifyDeckButton.addEventListener('click', modifyDeck);
modifyDeckPopup.appendChild(modifyDeckButton);
modifyDeckPopup.appendChild(createCloseCross());
popupContainer.appendChild(modifyDeckPopup);
createForm(modifyDeck, modifyDeckNameInput, modifyDeckCodeInput);
//create the deck manager div
div = document.createElement('div');
div.style.display='none';
div.id = 'klatus-elements-script-tab-pane';
div.className = 'tabpane';
//create the DECKS tab
tab = document.createElement('li');
tab.id = 'klatus-elements-script-tab';
tab.className = 'tab';
tabLink = document.createElement('a');
tabLink.innerText = 'Decks';
tabLink.key = div.id;
tabLink.href = '#' + div.id;
tabLink.addEventListener('click', function(e) {
holodeck._tabs.setActiveTab(this);
e.preventDefault();
});
tab.appendChild(tabLink);
//load saved decks
decks = localStorage.getItem('elementsDecks') ? JSON.parse(localStorage.getItem('elementsDecks')) : [];
//create the decks div
decksDiv = document.createElement('div');
div.appendChild(decksDiv);
jQuery(decksDiv).sortable({
update: (event, deckDivObject) => {
var item = deckDivObject.item;
var deck = decks.splice(item[0].getAttribute('data-klatu-index'), 1)[0];
decks.splice(item.index(), 0, deck);
saveDecks();
updateDecksDiv();
},
axis: 'y'
});
updateDecksDiv();
//create the add deck button
var button = document.createElement('button');
button.innerText = 'Add deck';
button.className = 'klatus-elements-script-button';
button.addEventListener('click', showCreateDeckPopup);
div.appendChild(button);
//create the export/import container
exportImportContainer = document.createElement('div');
exportImportContainer.className = 'klatu-flex klatu-centered-flex';
div.appendChild(exportImportContainer);
//create the export button
exportButton = document.createElement('button');
exportButton.className = 'export klatus-elements-script-button';
exportButton.innerText = 'Export decks';
exportImportContainer.appendChild(exportButton);
//create the import button
importButton = document.createElement('button');
importButton.className = 'import klatus-elements-script-button';
importButton.innerText = 'Import decks';
importButton.addEventListener('click', ()=>{
showPopup(importPopup);
importPopupInput.value = '';
importPopupInput.focus();
});
exportImportContainer.appendChild(importButton);
//create the import popup
importPopup = document.createElement('div');
importPopup.className = 'klatu-popup';
importPopupInput = document.createElement('input');
importPopupInput.placeholder = 'Enter your decks\' import code';
importPopupInput.addEventListener('keydown', e=>{
if(e.which===13||e.keyCode===13){ //if the user pressed enter...
importDecks();
}
});
importPopup.appendChild(importPopupInput);
importPopupButton = document.createElement('button');
importPopupButton.className = 'klatus-elements-script-button';
importPopupButton.innerText = 'Import';
importPopupButton.addEventListener('click', importDecks);
importPopup.appendChild(importPopupButton);
importPopup.appendChild(createCloseCross());
popupContainer.appendChild(importPopup);
//append my elements to the body when the appropriate elements are ready
returnElementWhenItExists('main_tab_set').then((element) => element.appendChild(tab));
returnElementWhenItExists('kong_game_ui').then((element) => element.appendChild(div));
//add the tab and the div to the holodeck
new Promise((resolve, reject) => {
var interval;
if (window.holodeck && window.holodeck._tabs) resolve(window.holodeck._tabs);
interval = setInterval(() => {
if (window.holodeck && window.holodeck._tabs) {
clearInterval(interval);
resolve(window.holodeck._tabs);
}
}, 1000);
}).then(tabs => {
tabs.links.push(tabLink);
tabs.containers._object[div.id] = div;
});
//instantiate Clipboard to allow copying deck codes by clicking the appropriate icons
copyDeckCodeClipboard = new Clipboard('.klatu-deck-copy-span');
copyDeckCodeClipboard.on('success', () => jQuery.notify("Your deck code has been copied to your clipboard.", "success"));
copyDeckCodeClipboard.on('error', () => jQuery.notify("There was an error while copying your deck code to your clipboard.", "error"));
//instantiate Clipboard to allow exporting all the user's decks
exportClipboard = new Clipboard(exportButton, {
text: ()=>JSON.stringify(decks)
});
exportClipboard.on('success', () => jQuery.notify("Your export code has been copied to your clipboard.", "success"));
exportClipboard.on('error', () => jQuery.notify("There was an error while copying your export code to your clipboard.", "error"));
});