Last active
August 29, 2015 14:03
-
-
Save fbender/398ec9064a3d430942c4 to your computer and use it in GitHub Desktop.
SPAN toggler with JSON import / export
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// License: MIT | |
// Wrote this to help me with merging two spreadsheets using a WinMerge diff report. This is in no way optimized. | |
// The report HTML only requires adding the JS and desired CSS for the toggled state, e.g.: | |
// <style>span.toggled{background-color:lightcyan !important;}</style> | |
// Gecko-based browsers with context menu support have "Import" and "Export" items added to their context menu. | |
"use strict"; | |
function getNodes(selector) { | |
var nodelist = document.querySelectorAll(selector); | |
var arr = []; | |
for(var i = nodelist.length; i--; arr.unshift(nodelist[i])); | |
return arr; | |
} | |
function setId(node) { | |
var newId = ''; | |
if (typeof window.setAutoIdIndex != "number") { | |
window.setAutoIdIndex = -1; | |
} | |
if (node.id.length < 1) { | |
do { | |
newId = 'auto' + (window.setAutoIdIndex++); | |
} | |
while (document.getElementById(newId) != null); | |
return (node.id = newId); | |
} | |
else | |
console.log('Found node ' + node.id); | |
} | |
function toggleClass(event) { | |
if (event.target.nodeName == 'SPAN') | |
window.toggledIds[event.target.id] = event.target.classList.toggle('toggled'); | |
else | |
console.log('Not a span: ' + event.target.nodeName); | |
} | |
function handleImport(event) { | |
var file = event.target.files[0]; | |
//alert('File: ' + file.name); | |
var reader = new FileReader(); | |
reader.addEventListener('load', function(event) { | |
try { | |
JSON.parse(reader.result); | |
localStorage.setItem('toggledIds', reader.result); | |
resetToggleState(); | |
loadToggledIds(); | |
alert('Import succeded.'); | |
} | |
catch (e) { | |
alert('Reading failed. Invalid file?'); | |
} | |
}); | |
reader.readAsText(file); | |
} | |
function resetToggleState() { | |
getNodes('span.toggled').forEach(function(node){ | |
node.classList.remove('toggled'); | |
}); | |
} | |
function loadToggledIds() { | |
// get toggled Ids from localStorage | |
var toggledIds = localStorage.getItem('toggledIds'); | |
try { | |
window.toggledIds = JSON.parse(toggledIds); | |
} | |
catch (e) { | |
window.toggledIds = Object.create(null); | |
} | |
window.lastSavedTime = toggledIds['lastSave']; | |
window.__toggledIds_ = toggledIds['__toggledIds_']; | |
delete window.toggledIds.lastSave; | |
delete window.toggledIds.__toggledIds_; | |
// get hard-toggled nodes | |
getNodes('span.toggled').forEach(function(node){ | |
window.toggledIds[node.id] = true; | |
}); | |
//resetToggleState(); | |
Object.getOwnPropertyNames(window.toggledIds).forEach(function(id){ | |
var node = document.getElementById(id); | |
if (node && window.toggledIds[id]) { | |
node.classList.add('toggled'); | |
} | |
else | |
console.log('Node '+id+' not toggled'); | |
}); | |
} | |
function saveToggledIds(toggledObject) { | |
var lastSavedTime = Date().toString(); | |
console.log('Saving toggled Ids ...'); | |
var toggledIds = Object.getOwnPropertyNames(toggledObject).filter(function(id){ | |
return toggledObject[id]; | |
}); | |
Object.getOwnPropertyNames(toggledObject).forEach(function(id){ | |
if (!toggledObject[id]) { // delete un-toggled entries | |
delete toggledObject[id]; | |
} | |
}); | |
toggledObject.lastSave = lastSavedTime; | |
toggledObject.__toggledIds_ = toggledIds; | |
localStorage.setItem('toggledIds', JSON.stringify(toggledObject)); | |
delete toggledObject.lastSave; | |
delete toggledObject.__toggledIds_; | |
var info = document.getElementById('lastSaved'); | |
if (!info) { | |
info = document.createElement('div'); | |
info.id = 'lastSaved'; | |
info.style.position = 'fixed'; | |
info.style.bottom = 0; | |
info.style.right = 0; | |
info.style.padding = '0.2em 0.4em'; | |
info.style.transition = 'background-color 0.3s ease-out'; | |
document.body.appendChild(info); | |
} | |
info.style.backgroundColor = 'white'; | |
info.textContent = 'Last saved: ' + lastSavedTime; | |
setTimeout(function() { | |
info.style.backgroundColor = 'lightpink'; | |
}, 200); | |
window.lastSavedTime = lastSavedTime; | |
} | |
function createContextMenu() { | |
var menu = document.createElement('menu'); | |
menu.id = 'importexport'; | |
menu.type = 'context'; | |
var items = [ | |
{ | |
label: 'Export', | |
funct: function(){ | |
saveToggledIds(window.toggledIds); | |
var data = [localStorage.getItem('toggledIds')]; | |
//var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; | |
var blob = new Blob(data, {type : 'application/json'}); // the blob | |
var down = document.getElementById('fileexport'); | |
down.href = window.URL.createObjectURL(blob); | |
down.click(); | |
} | |
}, | |
{ | |
label: 'Import', | |
funct: function(){ | |
var up = document.getElementById('fileimport'); | |
up.click(); | |
} | |
} | |
]; | |
items.forEach(function(item) { | |
var menuitem = document.createElement('menuitem'); | |
menuitem.setAttribute('label', item.label); | |
menuitem.addEventListener('click', item.funct); | |
menu.appendChild(menuitem); | |
}); | |
var oldMenu = document.getElementById(menu.id); | |
if (oldMenu) { | |
oldMenu.parentElement.removeChild(oldMenu); | |
} | |
oldMenu = null; // destroy old menu for good | |
document.body.appendChild(menu); | |
document.body.setAttribute('contextmenu', menu.id); | |
var upload = document.createElement('input'); | |
upload.id = 'fileimport'; | |
upload.type = 'file'; | |
upload.style.opacity = 0; | |
upload.style.position = 'fixed'; | |
upload.style.top = 0; | |
upload.style.left = 0; | |
upload.style.zIndex = -1; | |
upload.addEventListener('change', handleImport); | |
document.body.appendChild(upload); | |
var download = document.createElement('a'); | |
download.id = 'fileexport'; | |
download.setAttribute('download', 'state.json'); | |
download.style.opacity = 0; | |
download.style.position = 'fixed'; | |
download.style.top = 0; | |
download.style.left = 0; | |
download.style.zIndex = -1; | |
document.body.appendChild(download); | |
} | |
//mod = document.querySelectorAll('.sf17b16'); | |
// add Id to each span | |
var spans = getNodes('span'); | |
spans.forEach(function(node){setId(node);}); | |
loadToggledIds(); | |
createContextMenu(); | |
// add events | |
document.body.addEventListener('click', toggleClass); | |
window.addEventListener('beforeunload', function(){saveToggledIds(window.toggledIds);}); | |
setInterval(function(){saveToggledIds(window.toggledIds);}, 15000); // save every 10s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment