new directory structure
authorAndrew Lorimer <andrew@lorimer.id.au>
Mon, 19 Aug 2019 09:01:20 +0000 (19:01 +1000)
committerAndrew Lorimer <andrew@lorimer.id.au>
Mon, 19 Aug 2019 09:01:20 +0000 (19:01 +1000)
14 files changed:
main.js [deleted file]
manifest.json [deleted file]
newtab.html [deleted file]
options.html [deleted file]
options.js [deleted file]
sortable.min.js [deleted file]
src/main.js [new file with mode: 0644]
src/manifest.json [new file with mode: 0644]
src/newtab.html [new file with mode: 0644]
src/options.html [new file with mode: 0644]
src/options.js [new file with mode: 0644]
src/sortable.min.js [new file with mode: 0644]
src/style.css [new file with mode: 0644]
style.css [deleted file]
diff --git a/main.js b/main.js
deleted file mode 100644 (file)
index 3af538f..0000000
--- a/main.js
+++ /dev/null
@@ -1,608 +0,0 @@
-var tick = "✔";
-var addText = "+";
-var rmText = "−";
-var removebg = "#bf616a";
-var hovergrn = "#a3be8c";
-var hoverbg = "#434c5e";
-var hoverbg2 = "#848ead";
-var editMode = false;
-var dragSrcEl = null;
-var thumbServer = "https://www.google.com/s2/favicons?domain=";
-var defaultColumns= [
-      ["General",
-        ["Github", "https://github.com"],
-        ["Wikipedia", "https://en.wikipedia.org"],
-        ["Gmail", "https://mail.google.com"]
-      ],
-      ["Productivity",
-        ["Desmos", "https://www.desmos.com/calculator"],
-        ["Wolfram", "https://wolframalpha.com"],
-        ["Hacker News", "https://news.ycombinator.com"]
-      ],
-      ["Social",
-        ["Reddit", "https://www.reddit.com"],
-        ["YouTube", "https://youtube.com"],
-        ["Instagram", "https://instagram.com"]
-      ]
-]
-
-
-// --------------------------------
-//
-// Initialisation
-//
-// --------------------------------
-
-
-document.addEventListener("DOMContentLoaded", loadLists);
-
-function loadLists() {
-
-  // Retrieve lists from storage and trigger callback to generate HTML
-  
-  console.log("Getting lists from storage");
-  chrome.storage.sync.get({"lists": defaultColumns}, parseColumns);
-
-  document.getElementById('edit').addEventListener('click', edit, false);
-  document.getElementById('addcol').addEventListener('click', addColumn, false);
-
-  // Set colours from preferences
-
-  var bgCallback = function(colourPref) { document.body.style.background = colourPref["bgvalue"]; };
-  var fgCallback = function(colourPref) { document.body.style.color = colourPref["fgvalue"]; };
-  var hrCallback = function(colourPref) {
-    document.documentElement.style.setProperty('--hover-bg', colourPref["hrvalue"]);
-    hoverbg = colourPref["hrvalue"];
-  };
-
-  chrome.storage.sync.get({"bgvalue": "#2e3440"}, bgCallback);
-  chrome.storage.sync.get({"fgvalue": "#d8dee9"}, fgCallback);
-  chrome.storage.sync.get({"hrvalue": "#434c5e"}, hrCallback);
-
-}
-
-
-function parseColumns(config) {
-
-  var columns = config["lists"]
-
-  // Generate elements for each column
-  for (let col of columns) {
-
-    var ul = genColumn(col[0]);
-    document.getElementById("links").appendChild(ul);
-
-    // Iterate through links
-    for(let item of col.slice(1)) {
-      li = genItem(ul, item[0], item[1]);
-      ul.appendChild(li);
-    }
-
-    var sortableProperties = { group: "usercolumns", animation: 150, onSort: function (evt) {saveConfig();} };
-    new Sortable(ul, sortableProperties);
-
-  }
-}
-
-
-function genColumn(title) {
-
-    // Generate HTML elements for a column (without items)
-
-    var ul = document.createElement("ul");
-    ul.setAttribute("id", title);
-    ul.setAttribute('draggable', 'false');
-
-    var grip = document.createElement("span");
-    grip.setAttribute("class", "grip");
-    grip.addEventListener('mousedown', enableDrag);
-    grip.addEventListener('mouseup', disableDrag);
-    ul.appendChild(grip)
-
-    var titleDiv = document.createElement("div");
-    titleDiv.setAttribute("class", "title");
-
-    var titleText = document.createElement("p");
-    titleText.innerText = title;
-    titleDiv.appendChild(titleText);
-
-    var addBtn = document.createElement("span");
-    addBtn.innerText = addText;
-    addBtn.setAttribute("class", "add");
-    addBtn.setAttribute("id", "add-" + title);
-    addBtn.addEventListener("click", addItem);
-    titleDiv.appendChild(addBtn);
-
-    ul.appendChild(titleDiv);
-
-    return ul;
-
-}
-
-
-function genItem(ul, nme, url) {
-
-  // Generate HTML elements for an item
-
-  var li = document.createElement("li");
-  li.setAttribute("class", ul.id + "-" + (ul.getElementsByTagName("li").length + 1).toString() );
-  li.addEventListener("mouseup", saveConfig);
-
-  var img = requestThumbnail(url);
-
-  var link = document.createElement("a");
-  link.className = "item";
-  link.href = url;
-
-  var rmBtn = document.createElement("span");
-  rmBtn.className = "remove";
-  rmBtn.id = "delete-" + li.id;
-  rmBtn.innerText = "-";
-  rmBtn.addEventListener("click", removeItem);
-
-  link.appendChild(img);
-  link.insertAdjacentHTML("beforeend", nme);
-  li.appendChild(link);
-  li.appendChild(rmBtn);
-  
-  return li;
-
-}
-
-
-function requestThumbnail(imageUrl) {
-  // Get thumbnail from Google's favicon server
-  var img = document.createElement("img");
-  var xhr = new XMLHttpRequest();
-  xhr.open('GET', thumbServer + imageUrl);
-  xhr.responseType = "blob";
-  xhr.onload = function() {
-    img.setAttribute("data-src", thumbServer + imageUrl);
-    img.className = "icon";
-    var objUrl = URL.createObjectURL(xhr.response);
-    img.setAttribute("src", objUrl);
-  }.bind(this);
-  xhr.send();
-  return img;
-}
-
-
-// --------------------------------
-//
-// Edit mode
-//
-// --------------------------------
-
-
-function edit(event) {
-  
-  // Enter/exit edit mode
-  
-  if (editMode == true) {
-    console.log("Exited edit mode");
-    closeEdit(this);
-    return false;
-  }
-
-  console.log("Entered edit mode");
-
-  this.style.background = hovergrn;
-  this.innerText = tick;
-  addBtn= document.getElementById("addcol");
-  addBtn.style.display = "flex";
-  var cols = document.getElementsByTagName("ul");
-
-  for (let col of cols) {
-    
-    var titleDiv = col.getElementsByClassName("title")[0];
-    titleDiv.getElementsByClassName("add")[0].style.display = "flex";
-
-    col.style.bottom = "26px";
-    col.getElementsByClassName("grip")[0].style.display = "inline-block";
-
-    var rmColBtn = document.createElement("span"); 
-    rmColBtn.className = "rmcol";
-    rmColBtn.id = "rmcol-" + col.id;
-    rmColBtn.innerText = rmText;
-    titleDiv.appendChild(rmColBtn);
-
-    var titleStatic = titleDiv.getElementsByTagName("p")[0];
-    var titleInput = document.createElement("input");
-    titleInput.type = "text";
-    titleInput.className = "colname";
-    titleInput.placeholder = titleStatic.innerText;
-    titleInput.value = titleStatic.innerText;
-    titleDiv.insertBefore(titleInput, titleStatic);
-    titleStatic.remove();
-
-  }
-
-  updateListeners();
-  editMode = true;
-}
-
-function closeEdit(editBtn) {
-
-  // Exit edit mode and clean up elements
-
-  editMode = false;
-  editBtn.style.background = "";
-  editBtn.innerText = "e";
-
-  var addBtn = document.getElementById("addcol");
-  addBtn.style.display = "none";
-
-  var cols = document.getElementsByTagName("ul");
-
-  for (let col of cols) {
-
-    col.style.bottom = "0";
-    col.getElementsByClassName("grip")[0].style.display = "none";
-
-    var rmColBtn = col.getElementsByClassName("title")[0].getElementsByClassName("rmcol")[0];
-    rmColBtn.remove();
-
-    var titleDiv = col.getElementsByClassName("title")[0];
-    titleDiv.getElementsByClassName("add")[0].style.display = "";
-
-    titleInput = titleDiv.getElementsByClassName("colname")[0];
-    titleStatic = document.createElement("p");
-    titleStatic.innerText = titleInput.value;
-    if (titleStatic.innerText == "") {
-      titleStatic.innerText = titleInput.placeholder;
-    }
-    titleInput.remove();
-    titleDiv.appendChild(titleStatic);
-
-  }
-
-  saveConfig();
-
-}
-
-function addColumn(event) {
-
-  // Create a new column from Edit mode
-
-  var ul = document.createElement("ul");
-
-  // Make sure columns do not share an id
-  var ex = document.querySelectorAll('[id^="new "]'); // existing "new" columns
-  if (ex.length > 0) {
-    indices = []
-    for (let i of ex) {
-     indices.push(Number(i.id.split(" ")[1])); 
-    }
-    ul.setAttribute("id", "new " + (Math.max.apply(Math, indices)+1).toString());
-  }
-  else {
-    ul.setAttribute("id", "new 1");
-  }
-  ul.setAttribute('draggable', 'false');
-
-  var grip = document.createElement("span");
-  grip.setAttribute("class", "grip");
-  grip.addEventListener("mousedown", enableDrag);
-  grip.addEventListener("mouseup", disableDrag);
-  grip.style.display = "inline-block";
-  ul.style.bottom = "26px";
-  ul.appendChild(grip)
-
-  var titleDiv = document.createElement("div");
-  titleDiv.setAttribute("class", "title");
-
-  var titleInput = document.createElement("input");
-  titleInput.type = "text";
-  titleInput.className = "colname";
-  titleInput.placeholder = ul.id;
-  titleDiv.appendChild(titleInput);
-
-  var addBtn = document.createElement("span");
-  addBtn.style.display = "flex";
-  addBtn.innerText = addText;
-  addBtn.setAttribute("class", "add");
-  addBtn.setAttribute("id", "add-" + ul.id);
-  addBtn.addEventListener("click", addItem);
-  titleDiv.appendChild(addBtn);
-
-  var rmColBtn = document.createElement("span"); 
-  rmColBtn.className = "rmcol";
-  rmColBtn.id = "rmcol-" + ul.id;
-  rmColBtn.innerText = rmText;
-  titleDiv.appendChild(rmColBtn);
-
-  ul.appendChild(titleDiv);
-
-  document.getElementById("links").appendChild(ul);
-  saveConfig();
-  updateListeners();
-  titleInput.focus();
-
-}
-
-
-function removeColumn(event) {
-
-  // Delete column in edit mode
-  
-  this.parentNode.parentNode.remove();
-  saveConfig();
-
-}
-
-
-function removeItem(event) {
-
-  // Remove link in edit or normal mode
-  
-  this.parentNode.outerHTML = "";
-  delete this.parentNode;
-  saveConfig();
-
-}
-
-
-function addItem(event) {
-
-  // Interface for adding a new list item to an existing category
-
-  var ul = this.parentNode.parentNode;
-  var id = ul.id;
-
-  // Check if a form has already been generated for this column
-  existing = ul.getElementsByClassName("new");
-  if (existing.length > 0) {
-    existing[0].getElementsByClassName("name")[0].focus();
-    return false;
-  }
-
-  var li = document.createElement("li");
-  li.setAttribute("class", "new");
-  li.addEventListener("keyup", formKeys); 
-
-  var saveBtn = document.createElement("span");
-  saveBtn.className = "save";
-  saveBtn.tabIndex = "3";
-  saveBtn.id = "save-" + ul.id;
-  saveBtn.innerText = tick;
-  saveBtn.addEventListener("click", saveItem);
-  saveBtn.addEventListener("mouseover", saveMouseOver);
-  saveBtn.addEventListener("mouseout", saveMouseOut);
-  li.appendChild(saveBtn);
-
-  var nameInput = document.createElement("input");
-  nameInput.type = "text";
-  nameInput.className = "name";
-  nameInput.placeholder = "name";
-  nameInput.tabIndex = "1";
-  li.appendChild(nameInput);
-
-  var urlInput = document.createElement("input");
-  urlInput.type = "url";
-  urlInput.className = "url";
-  urlInput.placeholder = "url";
-  urlInput.tabIndex = "2";
-  urlInput.spellcheck = "false";
-  li.appendChild(urlInput);
-
-  ul.appendChild(li);
-  updateListeners();
-  nameInput.focus();
-
-}
-
-
-function saveItem(event) {
-
-  // Add new item to a column after pressing the save button in form
-
-  var li = this.parentNode;
-  var ul = this.parentNode.parentNode;
-  var nameField = li.getElementsByClassName("name")[0];
-  var urlField = li.getElementsByClassName("url")[0];
-
-  if (nameField.value != "" && urlField.value != "" && urlField.validity.typeMismatch== false) {
-
-    var newli = genItem(ul, nameField.value, urlField.value);
-    li.remove();
-    delete li;
-
-    ul.appendChild(newli);
-    saveConfig();
-
-  }
-  else {
-
-    if (nameField.value == "" && urlField.value == "") {
-      console.log("No data supplied, deleting form");
-      li.remove();
-    }
-    else {
-      console.log("Missing data, press Esc to delete form");
-    }
-
-  }
-
-}
-
-
-// --------------------------------
-//
-// UI event listeners
-//
-// --------------------------------
-
-
-function updateListeners() {
-
-  // Update event listeners for interface elements, since listeners are tied DOM objects which are lost on dataTransfer operations
-
-  var addBtns = document.getElementsByClassName("add");
-  for (let addBtn of addBtns) {
-    addBtn.addEventListener("click", addItem);
-  }
-
-  var rmBtns = document.getElementsByClassName("rm");
-  for (let rmBtn of rmBtns) {
-    rmBtn.addEventListener("click", removeItem);
-  }
-
-  var rmColBtns = document.getElementsByClassName("rmcol");
-  for (let rmColBtn of rmColBtns) {
-    rmColBtn.addEventListener("click", removeColumn); 
-  }
-
-  var saveBtns = document.getElementsByClassName("save");
-  for (let saveBtn of saveBtns) {
-    saveBtn.addEventListener("click", saveItem);
-    saveBtn.addEventListener("mouseover", saveMouseOver);
-    saveBtn.addEventListener("mouseout", saveMouseOut);
-  }
-
-}
-
-
-function saveMouseOver(event) {
-  nameField = this.parentNode.getElementsByClassName("name")[0];
-  urlField = this.parentNode.getElementsByClassName("url")[0];
-  if (nameField.value === ''  || urlField.value === '' || urlField.validity.typeMismatch == true) {
-    this.style.background = removebg;
-  }
-  else {
-    this.style.background = hovergrn; 
-  }
-}
-
-
-function saveMouseOut(event) {
-  this.style.background = hoverbg2;
-}
-
-
-function enableDrag() {
-  // Enable drag & drop when grip is grabbed (otherwise any mouse click triggers dragStart)
-  console.log("Drag started");
-  uls = document.getElementsByTagName("ul");
-  for (let ul of uls) {
-    ul.setAttribute("draggable", "true");
-    ul.addEventListener('dragstart', dragStart, false);
-    ul.addEventListener('dragover', dragOver, false);
-    ul.addEventListener('drop', drop, false);
-  }
-}
-
-
-function disableDrag() {
-  // Disable drag after grip has been released
-  console.log("Drag ended");
-  uls = document.getElementsByTagName("ul");
-  for (let ul of uls) {
-    ul.setAttribute("draggable", "false");
-    ul.removeEventListener('dragstart');
-    ul.removeEventListener('dragover');
-    ul.removeEventListener('drop');
-  }
-}
-
-
-function dragStart(e) {
-  dragSrcEl = this;
-  e.dataTransfer.effectAllowed = 'move';
-  e.dataTransfer.setData('text/html', this.innerHTML);
-}
-
-
-function dragOver(e) {
-  if (e.preventDefault) {
-    e.preventDefault();
-  }
-  e.dataTransfer.dropEffect = 'move';
-  return false;
-}
-
-
-function drop(e) {
-  if (e.stopPropagation); {
-    e.stopPropagation();
-  }
-  if (dragSrcEl != this) {
-    var srcTitleInput = dragSrcEl.getElementsByClassName("colname")[0].value;
-    var destTitleInput = this.getElementsByClassName("colname")[0].value;
-    dragSrcEl.innerHTML = this.innerHTML;
-    dragSrcEl.getElementsByClassName("colname")[0].value = destTitleInput;
-    this.innerHTML = e.dataTransfer.getData('text/html');
-    this.getElementsByClassName("colname")[0].value = srcTitleInput;
-    saveConfig();
-    updateListeners();
-  }
-  return false;
-}
-
-
-function formKeys(e) {
-  var focus = document.activeElement;
-  if (focus.parentNode.className != "new") {
-    return false;
-  }
-  switch (e.which) {
-    case 27: // escape
-      focus.parentNode.remove();
-      break;
-    case 13: // enter
-      focus.parentNode.getElementsByClassName("save")[0].click();
-      break;
-  }
-}
-
-
-// --------------------------------
-//
-// Configuration management
-//
-// --------------------------------
-
-
-function saveConfig() {
-  
-  // Save current DOM structure as JSON data
-
-  console.log("Saving settings");
-  data = []
-  for (let ul of document.getElementsByTagName("ul")) {
-    data.push(columnToArray(ul, true));
-  }
-  chrome.storage.sync.set( {"lists": data} );
-
-}
-
-
-function columnToArray(ul, title = false) {
-
-  // Convert a column of data (ul) to a 2D array, optionally including the title
-
-  var data = [];
-  var items = ul.getElementsByClassName("item");
-
-  if (title == true && editMode == true) {
-    data[0] = ul.getElementsByClassName("title")[0].getElementsByTagName("input")[0].value;
-    if (data[0] == "" || data[0] == null) {
-      console.log("Using default category name since input was empty");
-      data[0] = ul.getElementsByClassName("title")[0].getElementsByTagName("input")[0].placeholder;
-    }
-  }
-  else if (title == true) {
-    data[0] = ul.getElementsByClassName('title')[0].getElementsByTagName("p")[0].textContent;
-  }
-
-  for (let li of items) {
-    if (li.class == "new" || li.class == "title") {
-      continue; // Ignore input forms and titles (already handled)
-    }
-    else {
-      data.push([li.innerText, li.getAttribute("href")]);
-    }
-  }
-
-  return data;
-
-}
diff --git a/manifest.json b/manifest.json
deleted file mode 100644 (file)
index 320055b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-   "chrome_url_overrides": {
-      "newtab": "newtab.html"
-   },
-   "content_security_policy": "script-src 'self'; object-src 'self'",
-   "manifest_version": 2,
-   "name": "newtab",
-   "description": "A simple new tab page with customisable links",
-   "options_ui": {
-      "chrome_style": true,
-      "page": "options.html"
-   },
-   "permissions": [ "storage" ],
-   "version": "2"
-}
diff --git a/newtab.html b/newtab.html
deleted file mode 100644 (file)
index b572bfe..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-  <head>
-
-    <title>New Tab</title>
-    <meta charset="utf-8">
-    <meta name=viewport content="width=device-width, initial-scale=1">
-
-    <script type="text/javascript" src="main.js" ></script>
-    <script type="text/javascript" src="sortable.min.js"></script>
-
-    <link rel="stylesheet" href="style.css">
-
-  </head>
-
-  <body>
-
-    <span id="edit">e</span>
-    <span id="addcol">+</span>
-
-    <div id="links"></div>
-    
-  </body>
-
-</html>
diff --git a/options.html b/options.html
deleted file mode 100644 (file)
index 1cbdbf3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-  <head>
-
-    <title>newtab</title>
-    <script src="options.js"></script>
-    <style>
-      body {
-        padding-left: 65px;
-        padding-right: 65px;
-      }
-
-      button {
-        float: right;
-        width: 65px;
-      }
-
-      table {
-        text-align: right;
-      }
-    </style>
-
-  </head>
-
-  <body>
-
-    <table>
-      <tr>
-        <td>Background</td><td><input type="text" id="bg"></td>
-      </tr>
-      <tr>
-        <td>Foreground</td><td><input type="text" id="fg"></td>
-      </tr>
-      <tr>
-        <td>Hover</td><td><input type="text" id="hr"></td>
-      </tr>
-    </table>
-
-    <button id="save">Save</button>
-
-    <p>Made by<br /><a href="https://git.lorimer.id.au/newtab.git">Andrew Lorimer</a></p>
-
-  </body>
-
-</html>
diff --git a/options.js b/options.js
deleted file mode 100644 (file)
index 31ff156..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-function save_options() {
-  var bgvalue = document.getElementById('bg').value;
-  var fgvalue = document.getElementById('fg').value;
-  var hrvalue = document.getElementById('hr').value;
-  console.log(bgvalue);
-
-  chrome.storage.sync.set({
-    "bgvalue": bgvalue,
-    "fgvalue": fgvalue,
-    "hrvalue": hrvalue,
-  }, function() {
-    window.close();
-
-  });
-}
-
-
-function restore_options() {
-  chrome.storage.sync.get({
-    "bgvalue": "#2e3440",
-    "fgvalue": "#d8dee9",
-    "hrvalue": "#434c5e"
-  },
-    function(items) {
-      document.getElementById('bg').value = items["bgvalue"];
-      document.getElementById('fg').value = items["fgvalue"];
-      document.getElementById('hr').value = items["hrvalue"];
-    });
-  document.getElementById('save').addEventListener('click', save_options);
-}
-
-document.addEventListener('DOMContentLoaded', restore_options);
diff --git a/sortable.min.js b/sortable.min.js
deleted file mode 100644 (file)
index e95d2a3..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! Sortable 1.4.2 - MIT | git://github.com/rubaxa/Sortable.git */
-!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){if(!a||!a.nodeType||1!==a.nodeType)throw"Sortable: `el` must be HTMLElement, and not "+{}.toString.call(a);this.el=a,this.options=b=r({},b),a[L]=this;var c={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1};for(var d in c)!(d in b)&&(b[d]=c[d]);V(b);for(var f in this)"_"===f.charAt(0)&&(this[f]=this[f].bind(this));this.nativeDraggable=b.forceFallback?!1:P,e(a,"mousedown",this._onTapStart),e(a,"touchstart",this._onTapStart),this.nativeDraggable&&(e(a,"dragover",this),e(a,"dragenter",this)),T.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){v&&v.state!==a&&(h(v,"display",a?"none":""),!a&&v.state&&w.insertBefore(v,s),v.state=a)}function c(a,b,c){if(a){c=c||N,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")(?=\\s)","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function d(a){a.dataTransfer&&(a.dataTransfer.dropEffect="move"),a.preventDefault()}function e(a,b,c){a.addEventListener(b,c,!1)}function f(a,b,c){a.removeEventListener(b,c,!1)}function g(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(K," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(K," ")}}function h(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return N.defaultView&&N.defaultView.getComputedStyle?c=N.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function i(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function j(a,b,c,d,e,f,g){var h=N.createEvent("Event"),i=(a||b[L]).options,j="on"+c.charAt(0).toUpperCase()+c.substr(1);h.initEvent(c,!0,!0),h.to=b,h.from=e||b,h.item=d||b,h.clone=v,h.oldIndex=f,h.newIndex=g,b.dispatchEvent(h),i[j]&&i[j].call(a,h)}function k(a,b,c,d,e,f){var g,h,i=a[L],j=i.options.onMove;return g=N.createEvent("Event"),g.initEvent("move",!0,!0),g.to=b,g.from=a,g.dragged=c,g.draggedRect=d,g.related=e||b,g.relatedRect=f||b.getBoundingClientRect(),a.dispatchEvent(g),j&&(h=j.call(i,g)),h}function l(a){a.draggable=!1}function m(){R=!1}function n(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return(b.clientY-(d.top+d.height)>5||b.clientX-(d.right+d.width)>5)&&c}function o(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function p(a){var b=0;if(!a||!a.parentNode)return-1;for(;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function q(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function r(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}var s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J={},K=/\s+/g,L="Sortable"+(new Date).getTime(),M=window,N=M.document,O=M.parseInt,P=!!("draggable"in N.createElement("div")),Q=function(a){return a=N.createElement("x"),a.style.cssText="pointer-events:auto","auto"===a.style.pointerEvents}(),R=!1,S=Math.abs,T=([].slice,[]),U=q(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(z!==c&&(y=b.scroll,z=c,y===!0)){y=c;do if(y.offsetWidth<y.scrollWidth||y.offsetHeight<y.scrollHeight)break;while(y=y.parentNode)}y&&(d=y,e=y.getBoundingClientRect(),f=(S(e.right-j)<=h)-(S(e.left-j)<=h),g=(S(e.bottom-k)<=h)-(S(e.top-k)<=h)),f||g||(f=(h>=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=M)),(J.vx!==f||J.vy!==g||J.el!==d)&&(J.el=d,J.vx=f,J.vy=g,clearInterval(J.pid),d&&(J.pid=setInterval(function(){d===M?M.scrollTo(M.pageXOffset+f*i,M.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30),V=function(a){var b=a.group;b&&"object"==typeof b||(b=a.group={name:b}),["pull","put"].forEach(function(a){a in b||(b[a]=!0)}),a.groups=" "+b.name+(b.put.join?" "+b.put.join(" "):"")+" "};return a.prototype={constructor:a,_onTapStart:function(a){var b=this,d=this.el,e=this.options,f=a.type,g=a.touches&&a.touches[0],h=(g||a).target,i=h,k=e.filter;if(!("mousedown"===f&&0!==a.button||e.disabled)&&(h=c(h,e.draggable,d))){if(D=p(h),"function"==typeof k){if(k.call(this,a,h,this))return j(b,i,"filter",h,d,D),void a.preventDefault()}else if(k&&(k=k.split(",").some(function(a){return a=c(i,a.trim(),d),a?(j(b,a,"filter",h,d,D),!0):void 0})))return void a.preventDefault();(!e.handle||c(i,e.handle,d))&&this._prepareDragStart(a,g,h)}},_prepareDragStart:function(a,b,c){var d,f=this,h=f.el,j=f.options,k=h.ownerDocument;c&&!s&&c.parentNode===h&&(G=a,w=h,s=c,t=s.parentNode,x=s.nextSibling,F=j.group,d=function(){f._disableDelayedDrag(),s.draggable=!0,g(s,f.options.chosenClass,!0),f._triggerDragStart(b)},j.ignore.split(",").forEach(function(a){i(s,a.trim(),l)}),e(k,"mouseup",f._onDrop),e(k,"touchend",f._onDrop),e(k,"touchcancel",f._onDrop),j.delay?(e(k,"mouseup",f._disableDelayedDrag),e(k,"touchend",f._disableDelayedDrag),e(k,"touchcancel",f._disableDelayedDrag),e(k,"mousemove",f._disableDelayedDrag),e(k,"touchmove",f._disableDelayedDrag),f._dragStartTimer=setTimeout(d,j.delay)):d())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),f(a,"mouseup",this._disableDelayedDrag),f(a,"touchend",this._disableDelayedDrag),f(a,"touchcancel",this._disableDelayedDrag),f(a,"mousemove",this._disableDelayedDrag),f(a,"touchmove",this._disableDelayedDrag)},_triggerDragStart:function(a){a?(G={target:s,clientX:a.clientX,clientY:a.clientY},this._onDragStart(G,"touch")):this.nativeDraggable?(e(s,"dragend",this),e(w,"dragstart",this._onDragStart)):this._onDragStart(G,!0);try{N.selection?N.selection.empty():window.getSelection().removeAllRanges()}catch(b){}},_dragStarted:function(){w&&s&&(g(s,this.options.ghostClass,!0),a.active=this,j(this,w,"start",s,w,D))},_emulateDragOver:function(){if(H){if(this._lastX===H.clientX&&this._lastY===H.clientY)return;this._lastX=H.clientX,this._lastY=H.clientY,Q||h(u,"display","none");var a=N.elementFromPoint(H.clientX,H.clientY),b=a,c=" "+this.options.group.name,d=T.length;if(b)do{if(b[L]&&b[L].options.groups.indexOf(c)>-1){for(;d--;)T[d]({clientX:H.clientX,clientY:H.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);Q||h(u,"display","")}},_onTouchMove:function(b){if(G){a.active||this._dragStarted(),this._appendGhost();var c=b.touches?b.touches[0]:b,d=c.clientX-G.clientX,e=c.clientY-G.clientY,f=b.touches?"translate3d("+d+"px,"+e+"px,0)":"translate("+d+"px,"+e+"px)";I=!0,H=c,h(u,"webkitTransform",f),h(u,"mozTransform",f),h(u,"msTransform",f),h(u,"transform",f),b.preventDefault()}},_appendGhost:function(){if(!u){var a,b=s.getBoundingClientRect(),c=h(s),d=this.options;u=s.cloneNode(!0),g(u,d.ghostClass,!1),g(u,d.fallbackClass,!0),h(u,"top",b.top-O(c.marginTop,10)),h(u,"left",b.left-O(c.marginLeft,10)),h(u,"width",b.width),h(u,"height",b.height),h(u,"opacity","0.8"),h(u,"position","fixed"),h(u,"zIndex","100000"),h(u,"pointerEvents","none"),d.fallbackOnBody&&N.body.appendChild(u)||w.appendChild(u),a=u.getBoundingClientRect(),h(u,"width",2*b.width-a.width),h(u,"height",2*b.height-a.height)}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;this._offUpEvents(),"clone"==F.pull&&(v=s.cloneNode(!0),h(v,"display","none"),w.insertBefore(v,s)),b?("touch"===b?(e(N,"touchmove",this._onTouchMove),e(N,"touchend",this._onDrop),e(N,"touchcancel",this._onDrop)):(e(N,"mousemove",this._onTouchMove),e(N,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,50)):(c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,s)),e(N,"drop",this),setTimeout(this._dragStarted,0))},_onDragOver:function(a){var d,e,f,g=this.el,i=this.options,j=i.group,l=j.put,o=F===j,p=i.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!i.dragoverBubble&&a.stopPropagation()),I=!0,F&&!i.disabled&&(o?p||(f=!w.contains(s)):F.pull&&l&&(F.name===j.name||l.indexOf&&~l.indexOf(F.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(U(a,i,this.el),R)return;if(d=c(a.target,i.draggable,g),e=s.getBoundingClientRect(),f)return b(!0),void(v||x?w.insertBefore(s,v||x):p||w.appendChild(s));if(0===g.children.length||g.children[0]===u||g===a.target&&(d=n(g,a))){if(d){if(d.animated)return;r=d.getBoundingClientRect()}b(o),k(w,g,s,e,d,r)!==!1&&(s.contains(g)||(g.appendChild(s),t=g),this._animate(e,s),d&&this._animate(r,d))}else if(d&&!d.animated&&d!==s&&void 0!==d.parentNode[L]){A!==d&&(A=d,B=h(d),C=h(d.parentNode));var q,r=d.getBoundingClientRect(),y=r.right-r.left,z=r.bottom-r.top,D=/left|right|inline/.test(B.cssFloat+B.display)||"flex"==C.display&&0===C["flex-direction"].indexOf("row"),E=d.offsetWidth>s.offsetWidth,G=d.offsetHeight>s.offsetHeight,H=(D?(a.clientX-r.left)/y:(a.clientY-r.top)/z)>.5,J=d.nextElementSibling,K=k(w,g,s,e,d,r);if(K!==!1){if(R=!0,setTimeout(m,30),b(o),1===K||-1===K)q=1===K;else if(D){var M=s.offsetTop,N=d.offsetTop;q=M===N?d.previousElementSibling===s&&!E||H&&E:N>M}else q=J!==s&&!G||H&&G;s.contains(g)||(q&&!J?g.appendChild(s):d.parentNode.insertBefore(s,q?J:d)),t=s.parentNode,this._animate(e,s),this._animate(r,d)}}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();h(b,"transition","none"),h(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,h(b,"transition","all "+c+"ms"),h(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){h(b,"transition",""),h(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;f(N,"touchmove",this._onTouchMove),f(a,"mouseup",this._onDrop),f(a,"touchend",this._onDrop),f(a,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(J.pid),clearTimeout(this._dragStartTimer),f(N,"mousemove",this._onTouchMove),this.nativeDraggable&&(f(N,"drop",this),f(c,"dragstart",this._onDragStart)),this._offUpEvents(),b&&(I&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation()),u&&u.parentNode.removeChild(u),s&&(this.nativeDraggable&&f(s,"dragend",this),l(s),g(s,this.options.ghostClass,!1),g(s,this.options.chosenClass,!1),w!==t?(E=p(s),E>=0&&(j(null,t,"sort",s,w,D,E),j(this,w,"sort",s,w,D,E),j(null,t,"add",s,w,D,E),j(this,w,"remove",s,w,D,E))):(v&&v.parentNode.removeChild(v),s.nextSibling!==x&&(E=p(s),E>=0&&(j(this,w,"update",s,w,D,E),j(this,w,"sort",s,w,D,E)))),a.active&&((null===E||-1===E)&&(E=D),j(this,w,"end",s,w,D,E),this.save())),w=s=t=u=x=v=y=z=G=H=I=E=A=B=F=a.active=null)},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?s&&(this._onDragOver(a),d(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],d=this.el.children,e=0,f=d.length,g=this.options;f>e;e++)a=d[e],c(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||o(a));return b},sort:function(a){var b={},d=this.el;this.toArray().forEach(function(a,e){var f=d.children[e];c(f,this.options.draggable,d)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(d.removeChild(b[a]),d.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return c(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:(c[a]=b,void("group"===a&&V(c)))},destroy:function(){var a=this.el;a[L]=null,f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),this.nativeDraggable&&(f(a,"dragover",this),f(a,"dragenter",this)),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),T.splice(T.indexOf(this._onDragOver),1),this._onDrop(),this.el=a=null}},a.utils={on:e,off:f,css:h,find:i,is:function(a,b){return!!c(a,b,a)},extend:r,throttle:q,closest:c,toggleClass:g,index:p},a.create=function(b,c){return new a(b,c)},a.version="1.4.2",a});
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
new file mode 100644 (file)
index 0000000..3af538f
--- /dev/null
@@ -0,0 +1,608 @@
+var tick = "✔";
+var addText = "+";
+var rmText = "−";
+var removebg = "#bf616a";
+var hovergrn = "#a3be8c";
+var hoverbg = "#434c5e";
+var hoverbg2 = "#848ead";
+var editMode = false;
+var dragSrcEl = null;
+var thumbServer = "https://www.google.com/s2/favicons?domain=";
+var defaultColumns= [
+      ["General",
+        ["Github", "https://github.com"],
+        ["Wikipedia", "https://en.wikipedia.org"],
+        ["Gmail", "https://mail.google.com"]
+      ],
+      ["Productivity",
+        ["Desmos", "https://www.desmos.com/calculator"],
+        ["Wolfram", "https://wolframalpha.com"],
+        ["Hacker News", "https://news.ycombinator.com"]
+      ],
+      ["Social",
+        ["Reddit", "https://www.reddit.com"],
+        ["YouTube", "https://youtube.com"],
+        ["Instagram", "https://instagram.com"]
+      ]
+]
+
+
+// --------------------------------
+//
+// Initialisation
+//
+// --------------------------------
+
+
+document.addEventListener("DOMContentLoaded", loadLists);
+
+function loadLists() {
+
+  // Retrieve lists from storage and trigger callback to generate HTML
+  
+  console.log("Getting lists from storage");
+  chrome.storage.sync.get({"lists": defaultColumns}, parseColumns);
+
+  document.getElementById('edit').addEventListener('click', edit, false);
+  document.getElementById('addcol').addEventListener('click', addColumn, false);
+
+  // Set colours from preferences
+
+  var bgCallback = function(colourPref) { document.body.style.background = colourPref["bgvalue"]; };
+  var fgCallback = function(colourPref) { document.body.style.color = colourPref["fgvalue"]; };
+  var hrCallback = function(colourPref) {
+    document.documentElement.style.setProperty('--hover-bg', colourPref["hrvalue"]);
+    hoverbg = colourPref["hrvalue"];
+  };
+
+  chrome.storage.sync.get({"bgvalue": "#2e3440"}, bgCallback);
+  chrome.storage.sync.get({"fgvalue": "#d8dee9"}, fgCallback);
+  chrome.storage.sync.get({"hrvalue": "#434c5e"}, hrCallback);
+
+}
+
+
+function parseColumns(config) {
+
+  var columns = config["lists"]
+
+  // Generate elements for each column
+  for (let col of columns) {
+
+    var ul = genColumn(col[0]);
+    document.getElementById("links").appendChild(ul);
+
+    // Iterate through links
+    for(let item of col.slice(1)) {
+      li = genItem(ul, item[0], item[1]);
+      ul.appendChild(li);
+    }
+
+    var sortableProperties = { group: "usercolumns", animation: 150, onSort: function (evt) {saveConfig();} };
+    new Sortable(ul, sortableProperties);
+
+  }
+}
+
+
+function genColumn(title) {
+
+    // Generate HTML elements for a column (without items)
+
+    var ul = document.createElement("ul");
+    ul.setAttribute("id", title);
+    ul.setAttribute('draggable', 'false');
+
+    var grip = document.createElement("span");
+    grip.setAttribute("class", "grip");
+    grip.addEventListener('mousedown', enableDrag);
+    grip.addEventListener('mouseup', disableDrag);
+    ul.appendChild(grip)
+
+    var titleDiv = document.createElement("div");
+    titleDiv.setAttribute("class", "title");
+
+    var titleText = document.createElement("p");
+    titleText.innerText = title;
+    titleDiv.appendChild(titleText);
+
+    var addBtn = document.createElement("span");
+    addBtn.innerText = addText;
+    addBtn.setAttribute("class", "add");
+    addBtn.setAttribute("id", "add-" + title);
+    addBtn.addEventListener("click", addItem);
+    titleDiv.appendChild(addBtn);
+
+    ul.appendChild(titleDiv);
+
+    return ul;
+
+}
+
+
+function genItem(ul, nme, url) {
+
+  // Generate HTML elements for an item
+
+  var li = document.createElement("li");
+  li.setAttribute("class", ul.id + "-" + (ul.getElementsByTagName("li").length + 1).toString() );
+  li.addEventListener("mouseup", saveConfig);
+
+  var img = requestThumbnail(url);
+
+  var link = document.createElement("a");
+  link.className = "item";
+  link.href = url;
+
+  var rmBtn = document.createElement("span");
+  rmBtn.className = "remove";
+  rmBtn.id = "delete-" + li.id;
+  rmBtn.innerText = "-";
+  rmBtn.addEventListener("click", removeItem);
+
+  link.appendChild(img);
+  link.insertAdjacentHTML("beforeend", nme);
+  li.appendChild(link);
+  li.appendChild(rmBtn);
+  
+  return li;
+
+}
+
+
+function requestThumbnail(imageUrl) {
+  // Get thumbnail from Google's favicon server
+  var img = document.createElement("img");
+  var xhr = new XMLHttpRequest();
+  xhr.open('GET', thumbServer + imageUrl);
+  xhr.responseType = "blob";
+  xhr.onload = function() {
+    img.setAttribute("data-src", thumbServer + imageUrl);
+    img.className = "icon";
+    var objUrl = URL.createObjectURL(xhr.response);
+    img.setAttribute("src", objUrl);
+  }.bind(this);
+  xhr.send();
+  return img;
+}
+
+
+// --------------------------------
+//
+// Edit mode
+//
+// --------------------------------
+
+
+function edit(event) {
+  
+  // Enter/exit edit mode
+  
+  if (editMode == true) {
+    console.log("Exited edit mode");
+    closeEdit(this);
+    return false;
+  }
+
+  console.log("Entered edit mode");
+
+  this.style.background = hovergrn;
+  this.innerText = tick;
+  addBtn= document.getElementById("addcol");
+  addBtn.style.display = "flex";
+  var cols = document.getElementsByTagName("ul");
+
+  for (let col of cols) {
+    
+    var titleDiv = col.getElementsByClassName("title")[0];
+    titleDiv.getElementsByClassName("add")[0].style.display = "flex";
+
+    col.style.bottom = "26px";
+    col.getElementsByClassName("grip")[0].style.display = "inline-block";
+
+    var rmColBtn = document.createElement("span"); 
+    rmColBtn.className = "rmcol";
+    rmColBtn.id = "rmcol-" + col.id;
+    rmColBtn.innerText = rmText;
+    titleDiv.appendChild(rmColBtn);
+
+    var titleStatic = titleDiv.getElementsByTagName("p")[0];
+    var titleInput = document.createElement("input");
+    titleInput.type = "text";
+    titleInput.className = "colname";
+    titleInput.placeholder = titleStatic.innerText;
+    titleInput.value = titleStatic.innerText;
+    titleDiv.insertBefore(titleInput, titleStatic);
+    titleStatic.remove();
+
+  }
+
+  updateListeners();
+  editMode = true;
+}
+
+function closeEdit(editBtn) {
+
+  // Exit edit mode and clean up elements
+
+  editMode = false;
+  editBtn.style.background = "";
+  editBtn.innerText = "e";
+
+  var addBtn = document.getElementById("addcol");
+  addBtn.style.display = "none";
+
+  var cols = document.getElementsByTagName("ul");
+
+  for (let col of cols) {
+
+    col.style.bottom = "0";
+    col.getElementsByClassName("grip")[0].style.display = "none";
+
+    var rmColBtn = col.getElementsByClassName("title")[0].getElementsByClassName("rmcol")[0];
+    rmColBtn.remove();
+
+    var titleDiv = col.getElementsByClassName("title")[0];
+    titleDiv.getElementsByClassName("add")[0].style.display = "";
+
+    titleInput = titleDiv.getElementsByClassName("colname")[0];
+    titleStatic = document.createElement("p");
+    titleStatic.innerText = titleInput.value;
+    if (titleStatic.innerText == "") {
+      titleStatic.innerText = titleInput.placeholder;
+    }
+    titleInput.remove();
+    titleDiv.appendChild(titleStatic);
+
+  }
+
+  saveConfig();
+
+}
+
+function addColumn(event) {
+
+  // Create a new column from Edit mode
+
+  var ul = document.createElement("ul");
+
+  // Make sure columns do not share an id
+  var ex = document.querySelectorAll('[id^="new "]'); // existing "new" columns
+  if (ex.length > 0) {
+    indices = []
+    for (let i of ex) {
+     indices.push(Number(i.id.split(" ")[1])); 
+    }
+    ul.setAttribute("id", "new " + (Math.max.apply(Math, indices)+1).toString());
+  }
+  else {
+    ul.setAttribute("id", "new 1");
+  }
+  ul.setAttribute('draggable', 'false');
+
+  var grip = document.createElement("span");
+  grip.setAttribute("class", "grip");
+  grip.addEventListener("mousedown", enableDrag);
+  grip.addEventListener("mouseup", disableDrag);
+  grip.style.display = "inline-block";
+  ul.style.bottom = "26px";
+  ul.appendChild(grip)
+
+  var titleDiv = document.createElement("div");
+  titleDiv.setAttribute("class", "title");
+
+  var titleInput = document.createElement("input");
+  titleInput.type = "text";
+  titleInput.className = "colname";
+  titleInput.placeholder = ul.id;
+  titleDiv.appendChild(titleInput);
+
+  var addBtn = document.createElement("span");
+  addBtn.style.display = "flex";
+  addBtn.innerText = addText;
+  addBtn.setAttribute("class", "add");
+  addBtn.setAttribute("id", "add-" + ul.id);
+  addBtn.addEventListener("click", addItem);
+  titleDiv.appendChild(addBtn);
+
+  var rmColBtn = document.createElement("span"); 
+  rmColBtn.className = "rmcol";
+  rmColBtn.id = "rmcol-" + ul.id;
+  rmColBtn.innerText = rmText;
+  titleDiv.appendChild(rmColBtn);
+
+  ul.appendChild(titleDiv);
+
+  document.getElementById("links").appendChild(ul);
+  saveConfig();
+  updateListeners();
+  titleInput.focus();
+
+}
+
+
+function removeColumn(event) {
+
+  // Delete column in edit mode
+  
+  this.parentNode.parentNode.remove();
+  saveConfig();
+
+}
+
+
+function removeItem(event) {
+
+  // Remove link in edit or normal mode
+  
+  this.parentNode.outerHTML = "";
+  delete this.parentNode;
+  saveConfig();
+
+}
+
+
+function addItem(event) {
+
+  // Interface for adding a new list item to an existing category
+
+  var ul = this.parentNode.parentNode;
+  var id = ul.id;
+
+  // Check if a form has already been generated for this column
+  existing = ul.getElementsByClassName("new");
+  if (existing.length > 0) {
+    existing[0].getElementsByClassName("name")[0].focus();
+    return false;
+  }
+
+  var li = document.createElement("li");
+  li.setAttribute("class", "new");
+  li.addEventListener("keyup", formKeys); 
+
+  var saveBtn = document.createElement("span");
+  saveBtn.className = "save";
+  saveBtn.tabIndex = "3";
+  saveBtn.id = "save-" + ul.id;
+  saveBtn.innerText = tick;
+  saveBtn.addEventListener("click", saveItem);
+  saveBtn.addEventListener("mouseover", saveMouseOver);
+  saveBtn.addEventListener("mouseout", saveMouseOut);
+  li.appendChild(saveBtn);
+
+  var nameInput = document.createElement("input");
+  nameInput.type = "text";
+  nameInput.className = "name";
+  nameInput.placeholder = "name";
+  nameInput.tabIndex = "1";
+  li.appendChild(nameInput);
+
+  var urlInput = document.createElement("input");
+  urlInput.type = "url";
+  urlInput.className = "url";
+  urlInput.placeholder = "url";
+  urlInput.tabIndex = "2";
+  urlInput.spellcheck = "false";
+  li.appendChild(urlInput);
+
+  ul.appendChild(li);
+  updateListeners();
+  nameInput.focus();
+
+}
+
+
+function saveItem(event) {
+
+  // Add new item to a column after pressing the save button in form
+
+  var li = this.parentNode;
+  var ul = this.parentNode.parentNode;
+  var nameField = li.getElementsByClassName("name")[0];
+  var urlField = li.getElementsByClassName("url")[0];
+
+  if (nameField.value != "" && urlField.value != "" && urlField.validity.typeMismatch== false) {
+
+    var newli = genItem(ul, nameField.value, urlField.value);
+    li.remove();
+    delete li;
+
+    ul.appendChild(newli);
+    saveConfig();
+
+  }
+  else {
+
+    if (nameField.value == "" && urlField.value == "") {
+      console.log("No data supplied, deleting form");
+      li.remove();
+    }
+    else {
+      console.log("Missing data, press Esc to delete form");
+    }
+
+  }
+
+}
+
+
+// --------------------------------
+//
+// UI event listeners
+//
+// --------------------------------
+
+
+function updateListeners() {
+
+  // Update event listeners for interface elements, since listeners are tied DOM objects which are lost on dataTransfer operations
+
+  var addBtns = document.getElementsByClassName("add");
+  for (let addBtn of addBtns) {
+    addBtn.addEventListener("click", addItem);
+  }
+
+  var rmBtns = document.getElementsByClassName("rm");
+  for (let rmBtn of rmBtns) {
+    rmBtn.addEventListener("click", removeItem);
+  }
+
+  var rmColBtns = document.getElementsByClassName("rmcol");
+  for (let rmColBtn of rmColBtns) {
+    rmColBtn.addEventListener("click", removeColumn); 
+  }
+
+  var saveBtns = document.getElementsByClassName("save");
+  for (let saveBtn of saveBtns) {
+    saveBtn.addEventListener("click", saveItem);
+    saveBtn.addEventListener("mouseover", saveMouseOver);
+    saveBtn.addEventListener("mouseout", saveMouseOut);
+  }
+
+}
+
+
+function saveMouseOver(event) {
+  nameField = this.parentNode.getElementsByClassName("name")[0];
+  urlField = this.parentNode.getElementsByClassName("url")[0];
+  if (nameField.value === ''  || urlField.value === '' || urlField.validity.typeMismatch == true) {
+    this.style.background = removebg;
+  }
+  else {
+    this.style.background = hovergrn; 
+  }
+}
+
+
+function saveMouseOut(event) {
+  this.style.background = hoverbg2;
+}
+
+
+function enableDrag() {
+  // Enable drag & drop when grip is grabbed (otherwise any mouse click triggers dragStart)
+  console.log("Drag started");
+  uls = document.getElementsByTagName("ul");
+  for (let ul of uls) {
+    ul.setAttribute("draggable", "true");
+    ul.addEventListener('dragstart', dragStart, false);
+    ul.addEventListener('dragover', dragOver, false);
+    ul.addEventListener('drop', drop, false);
+  }
+}
+
+
+function disableDrag() {
+  // Disable drag after grip has been released
+  console.log("Drag ended");
+  uls = document.getElementsByTagName("ul");
+  for (let ul of uls) {
+    ul.setAttribute("draggable", "false");
+    ul.removeEventListener('dragstart');
+    ul.removeEventListener('dragover');
+    ul.removeEventListener('drop');
+  }
+}
+
+
+function dragStart(e) {
+  dragSrcEl = this;
+  e.dataTransfer.effectAllowed = 'move';
+  e.dataTransfer.setData('text/html', this.innerHTML);
+}
+
+
+function dragOver(e) {
+  if (e.preventDefault) {
+    e.preventDefault();
+  }
+  e.dataTransfer.dropEffect = 'move';
+  return false;
+}
+
+
+function drop(e) {
+  if (e.stopPropagation); {
+    e.stopPropagation();
+  }
+  if (dragSrcEl != this) {
+    var srcTitleInput = dragSrcEl.getElementsByClassName("colname")[0].value;
+    var destTitleInput = this.getElementsByClassName("colname")[0].value;
+    dragSrcEl.innerHTML = this.innerHTML;
+    dragSrcEl.getElementsByClassName("colname")[0].value = destTitleInput;
+    this.innerHTML = e.dataTransfer.getData('text/html');
+    this.getElementsByClassName("colname")[0].value = srcTitleInput;
+    saveConfig();
+    updateListeners();
+  }
+  return false;
+}
+
+
+function formKeys(e) {
+  var focus = document.activeElement;
+  if (focus.parentNode.className != "new") {
+    return false;
+  }
+  switch (e.which) {
+    case 27: // escape
+      focus.parentNode.remove();
+      break;
+    case 13: // enter
+      focus.parentNode.getElementsByClassName("save")[0].click();
+      break;
+  }
+}
+
+
+// --------------------------------
+//
+// Configuration management
+//
+// --------------------------------
+
+
+function saveConfig() {
+  
+  // Save current DOM structure as JSON data
+
+  console.log("Saving settings");
+  data = []
+  for (let ul of document.getElementsByTagName("ul")) {
+    data.push(columnToArray(ul, true));
+  }
+  chrome.storage.sync.set( {"lists": data} );
+
+}
+
+
+function columnToArray(ul, title = false) {
+
+  // Convert a column of data (ul) to a 2D array, optionally including the title
+
+  var data = [];
+  var items = ul.getElementsByClassName("item");
+
+  if (title == true && editMode == true) {
+    data[0] = ul.getElementsByClassName("title")[0].getElementsByTagName("input")[0].value;
+    if (data[0] == "" || data[0] == null) {
+      console.log("Using default category name since input was empty");
+      data[0] = ul.getElementsByClassName("title")[0].getElementsByTagName("input")[0].placeholder;
+    }
+  }
+  else if (title == true) {
+    data[0] = ul.getElementsByClassName('title')[0].getElementsByTagName("p")[0].textContent;
+  }
+
+  for (let li of items) {
+    if (li.class == "new" || li.class == "title") {
+      continue; // Ignore input forms and titles (already handled)
+    }
+    else {
+      data.push([li.innerText, li.getAttribute("href")]);
+    }
+  }
+
+  return data;
+
+}
diff --git a/src/manifest.json b/src/manifest.json
new file mode 100644 (file)
index 0000000..320055b
--- /dev/null
@@ -0,0 +1,15 @@
+{
+   "chrome_url_overrides": {
+      "newtab": "newtab.html"
+   },
+   "content_security_policy": "script-src 'self'; object-src 'self'",
+   "manifest_version": 2,
+   "name": "newtab",
+   "description": "A simple new tab page with customisable links",
+   "options_ui": {
+      "chrome_style": true,
+      "page": "options.html"
+   },
+   "permissions": [ "storage" ],
+   "version": "2"
+}
diff --git a/src/newtab.html b/src/newtab.html
new file mode 100644 (file)
index 0000000..b572bfe
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+
+    <title>New Tab</title>
+    <meta charset="utf-8">
+    <meta name=viewport content="width=device-width, initial-scale=1">
+
+    <script type="text/javascript" src="main.js" ></script>
+    <script type="text/javascript" src="sortable.min.js"></script>
+
+    <link rel="stylesheet" href="style.css">
+
+  </head>
+
+  <body>
+
+    <span id="edit">e</span>
+    <span id="addcol">+</span>
+
+    <div id="links"></div>
+    
+  </body>
+
+</html>
diff --git a/src/options.html b/src/options.html
new file mode 100644 (file)
index 0000000..1cbdbf3
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+
+    <title>newtab</title>
+    <script src="options.js"></script>
+    <style>
+      body {
+        padding-left: 65px;
+        padding-right: 65px;
+      }
+
+      button {
+        float: right;
+        width: 65px;
+      }
+
+      table {
+        text-align: right;
+      }
+    </style>
+
+  </head>
+
+  <body>
+
+    <table>
+      <tr>
+        <td>Background</td><td><input type="text" id="bg"></td>
+      </tr>
+      <tr>
+        <td>Foreground</td><td><input type="text" id="fg"></td>
+      </tr>
+      <tr>
+        <td>Hover</td><td><input type="text" id="hr"></td>
+      </tr>
+    </table>
+
+    <button id="save">Save</button>
+
+    <p>Made by<br /><a href="https://git.lorimer.id.au/newtab.git">Andrew Lorimer</a></p>
+
+  </body>
+
+</html>
diff --git a/src/options.js b/src/options.js
new file mode 100644 (file)
index 0000000..31ff156
--- /dev/null
@@ -0,0 +1,32 @@
+function save_options() {
+  var bgvalue = document.getElementById('bg').value;
+  var fgvalue = document.getElementById('fg').value;
+  var hrvalue = document.getElementById('hr').value;
+  console.log(bgvalue);
+
+  chrome.storage.sync.set({
+    "bgvalue": bgvalue,
+    "fgvalue": fgvalue,
+    "hrvalue": hrvalue,
+  }, function() {
+    window.close();
+
+  });
+}
+
+
+function restore_options() {
+  chrome.storage.sync.get({
+    "bgvalue": "#2e3440",
+    "fgvalue": "#d8dee9",
+    "hrvalue": "#434c5e"
+  },
+    function(items) {
+      document.getElementById('bg').value = items["bgvalue"];
+      document.getElementById('fg').value = items["fgvalue"];
+      document.getElementById('hr').value = items["hrvalue"];
+    });
+  document.getElementById('save').addEventListener('click', save_options);
+}
+
+document.addEventListener('DOMContentLoaded', restore_options);
diff --git a/src/sortable.min.js b/src/sortable.min.js
new file mode 100644 (file)
index 0000000..e95d2a3
--- /dev/null
@@ -0,0 +1,2 @@
+/*! Sortable 1.4.2 - MIT | git://github.com/rubaxa/Sortable.git */
+!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){if(!a||!a.nodeType||1!==a.nodeType)throw"Sortable: `el` must be HTMLElement, and not "+{}.toString.call(a);this.el=a,this.options=b=r({},b),a[L]=this;var c={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1};for(var d in c)!(d in b)&&(b[d]=c[d]);V(b);for(var f in this)"_"===f.charAt(0)&&(this[f]=this[f].bind(this));this.nativeDraggable=b.forceFallback?!1:P,e(a,"mousedown",this._onTapStart),e(a,"touchstart",this._onTapStart),this.nativeDraggable&&(e(a,"dragover",this),e(a,"dragenter",this)),T.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){v&&v.state!==a&&(h(v,"display",a?"none":""),!a&&v.state&&w.insertBefore(v,s),v.state=a)}function c(a,b,c){if(a){c=c||N,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")(?=\\s)","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function d(a){a.dataTransfer&&(a.dataTransfer.dropEffect="move"),a.preventDefault()}function e(a,b,c){a.addEventListener(b,c,!1)}function f(a,b,c){a.removeEventListener(b,c,!1)}function g(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(K," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(K," ")}}function h(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return N.defaultView&&N.defaultView.getComputedStyle?c=N.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function i(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function j(a,b,c,d,e,f,g){var h=N.createEvent("Event"),i=(a||b[L]).options,j="on"+c.charAt(0).toUpperCase()+c.substr(1);h.initEvent(c,!0,!0),h.to=b,h.from=e||b,h.item=d||b,h.clone=v,h.oldIndex=f,h.newIndex=g,b.dispatchEvent(h),i[j]&&i[j].call(a,h)}function k(a,b,c,d,e,f){var g,h,i=a[L],j=i.options.onMove;return g=N.createEvent("Event"),g.initEvent("move",!0,!0),g.to=b,g.from=a,g.dragged=c,g.draggedRect=d,g.related=e||b,g.relatedRect=f||b.getBoundingClientRect(),a.dispatchEvent(g),j&&(h=j.call(i,g)),h}function l(a){a.draggable=!1}function m(){R=!1}function n(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return(b.clientY-(d.top+d.height)>5||b.clientX-(d.right+d.width)>5)&&c}function o(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function p(a){var b=0;if(!a||!a.parentNode)return-1;for(;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function q(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function r(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}var s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J={},K=/\s+/g,L="Sortable"+(new Date).getTime(),M=window,N=M.document,O=M.parseInt,P=!!("draggable"in N.createElement("div")),Q=function(a){return a=N.createElement("x"),a.style.cssText="pointer-events:auto","auto"===a.style.pointerEvents}(),R=!1,S=Math.abs,T=([].slice,[]),U=q(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(z!==c&&(y=b.scroll,z=c,y===!0)){y=c;do if(y.offsetWidth<y.scrollWidth||y.offsetHeight<y.scrollHeight)break;while(y=y.parentNode)}y&&(d=y,e=y.getBoundingClientRect(),f=(S(e.right-j)<=h)-(S(e.left-j)<=h),g=(S(e.bottom-k)<=h)-(S(e.top-k)<=h)),f||g||(f=(h>=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=M)),(J.vx!==f||J.vy!==g||J.el!==d)&&(J.el=d,J.vx=f,J.vy=g,clearInterval(J.pid),d&&(J.pid=setInterval(function(){d===M?M.scrollTo(M.pageXOffset+f*i,M.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30),V=function(a){var b=a.group;b&&"object"==typeof b||(b=a.group={name:b}),["pull","put"].forEach(function(a){a in b||(b[a]=!0)}),a.groups=" "+b.name+(b.put.join?" "+b.put.join(" "):"")+" "};return a.prototype={constructor:a,_onTapStart:function(a){var b=this,d=this.el,e=this.options,f=a.type,g=a.touches&&a.touches[0],h=(g||a).target,i=h,k=e.filter;if(!("mousedown"===f&&0!==a.button||e.disabled)&&(h=c(h,e.draggable,d))){if(D=p(h),"function"==typeof k){if(k.call(this,a,h,this))return j(b,i,"filter",h,d,D),void a.preventDefault()}else if(k&&(k=k.split(",").some(function(a){return a=c(i,a.trim(),d),a?(j(b,a,"filter",h,d,D),!0):void 0})))return void a.preventDefault();(!e.handle||c(i,e.handle,d))&&this._prepareDragStart(a,g,h)}},_prepareDragStart:function(a,b,c){var d,f=this,h=f.el,j=f.options,k=h.ownerDocument;c&&!s&&c.parentNode===h&&(G=a,w=h,s=c,t=s.parentNode,x=s.nextSibling,F=j.group,d=function(){f._disableDelayedDrag(),s.draggable=!0,g(s,f.options.chosenClass,!0),f._triggerDragStart(b)},j.ignore.split(",").forEach(function(a){i(s,a.trim(),l)}),e(k,"mouseup",f._onDrop),e(k,"touchend",f._onDrop),e(k,"touchcancel",f._onDrop),j.delay?(e(k,"mouseup",f._disableDelayedDrag),e(k,"touchend",f._disableDelayedDrag),e(k,"touchcancel",f._disableDelayedDrag),e(k,"mousemove",f._disableDelayedDrag),e(k,"touchmove",f._disableDelayedDrag),f._dragStartTimer=setTimeout(d,j.delay)):d())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),f(a,"mouseup",this._disableDelayedDrag),f(a,"touchend",this._disableDelayedDrag),f(a,"touchcancel",this._disableDelayedDrag),f(a,"mousemove",this._disableDelayedDrag),f(a,"touchmove",this._disableDelayedDrag)},_triggerDragStart:function(a){a?(G={target:s,clientX:a.clientX,clientY:a.clientY},this._onDragStart(G,"touch")):this.nativeDraggable?(e(s,"dragend",this),e(w,"dragstart",this._onDragStart)):this._onDragStart(G,!0);try{N.selection?N.selection.empty():window.getSelection().removeAllRanges()}catch(b){}},_dragStarted:function(){w&&s&&(g(s,this.options.ghostClass,!0),a.active=this,j(this,w,"start",s,w,D))},_emulateDragOver:function(){if(H){if(this._lastX===H.clientX&&this._lastY===H.clientY)return;this._lastX=H.clientX,this._lastY=H.clientY,Q||h(u,"display","none");var a=N.elementFromPoint(H.clientX,H.clientY),b=a,c=" "+this.options.group.name,d=T.length;if(b)do{if(b[L]&&b[L].options.groups.indexOf(c)>-1){for(;d--;)T[d]({clientX:H.clientX,clientY:H.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);Q||h(u,"display","")}},_onTouchMove:function(b){if(G){a.active||this._dragStarted(),this._appendGhost();var c=b.touches?b.touches[0]:b,d=c.clientX-G.clientX,e=c.clientY-G.clientY,f=b.touches?"translate3d("+d+"px,"+e+"px,0)":"translate("+d+"px,"+e+"px)";I=!0,H=c,h(u,"webkitTransform",f),h(u,"mozTransform",f),h(u,"msTransform",f),h(u,"transform",f),b.preventDefault()}},_appendGhost:function(){if(!u){var a,b=s.getBoundingClientRect(),c=h(s),d=this.options;u=s.cloneNode(!0),g(u,d.ghostClass,!1),g(u,d.fallbackClass,!0),h(u,"top",b.top-O(c.marginTop,10)),h(u,"left",b.left-O(c.marginLeft,10)),h(u,"width",b.width),h(u,"height",b.height),h(u,"opacity","0.8"),h(u,"position","fixed"),h(u,"zIndex","100000"),h(u,"pointerEvents","none"),d.fallbackOnBody&&N.body.appendChild(u)||w.appendChild(u),a=u.getBoundingClientRect(),h(u,"width",2*b.width-a.width),h(u,"height",2*b.height-a.height)}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;this._offUpEvents(),"clone"==F.pull&&(v=s.cloneNode(!0),h(v,"display","none"),w.insertBefore(v,s)),b?("touch"===b?(e(N,"touchmove",this._onTouchMove),e(N,"touchend",this._onDrop),e(N,"touchcancel",this._onDrop)):(e(N,"mousemove",this._onTouchMove),e(N,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,50)):(c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,s)),e(N,"drop",this),setTimeout(this._dragStarted,0))},_onDragOver:function(a){var d,e,f,g=this.el,i=this.options,j=i.group,l=j.put,o=F===j,p=i.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!i.dragoverBubble&&a.stopPropagation()),I=!0,F&&!i.disabled&&(o?p||(f=!w.contains(s)):F.pull&&l&&(F.name===j.name||l.indexOf&&~l.indexOf(F.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(U(a,i,this.el),R)return;if(d=c(a.target,i.draggable,g),e=s.getBoundingClientRect(),f)return b(!0),void(v||x?w.insertBefore(s,v||x):p||w.appendChild(s));if(0===g.children.length||g.children[0]===u||g===a.target&&(d=n(g,a))){if(d){if(d.animated)return;r=d.getBoundingClientRect()}b(o),k(w,g,s,e,d,r)!==!1&&(s.contains(g)||(g.appendChild(s),t=g),this._animate(e,s),d&&this._animate(r,d))}else if(d&&!d.animated&&d!==s&&void 0!==d.parentNode[L]){A!==d&&(A=d,B=h(d),C=h(d.parentNode));var q,r=d.getBoundingClientRect(),y=r.right-r.left,z=r.bottom-r.top,D=/left|right|inline/.test(B.cssFloat+B.display)||"flex"==C.display&&0===C["flex-direction"].indexOf("row"),E=d.offsetWidth>s.offsetWidth,G=d.offsetHeight>s.offsetHeight,H=(D?(a.clientX-r.left)/y:(a.clientY-r.top)/z)>.5,J=d.nextElementSibling,K=k(w,g,s,e,d,r);if(K!==!1){if(R=!0,setTimeout(m,30),b(o),1===K||-1===K)q=1===K;else if(D){var M=s.offsetTop,N=d.offsetTop;q=M===N?d.previousElementSibling===s&&!E||H&&E:N>M}else q=J!==s&&!G||H&&G;s.contains(g)||(q&&!J?g.appendChild(s):d.parentNode.insertBefore(s,q?J:d)),t=s.parentNode,this._animate(e,s),this._animate(r,d)}}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();h(b,"transition","none"),h(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,h(b,"transition","all "+c+"ms"),h(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){h(b,"transition",""),h(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;f(N,"touchmove",this._onTouchMove),f(a,"mouseup",this._onDrop),f(a,"touchend",this._onDrop),f(a,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(J.pid),clearTimeout(this._dragStartTimer),f(N,"mousemove",this._onTouchMove),this.nativeDraggable&&(f(N,"drop",this),f(c,"dragstart",this._onDragStart)),this._offUpEvents(),b&&(I&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation()),u&&u.parentNode.removeChild(u),s&&(this.nativeDraggable&&f(s,"dragend",this),l(s),g(s,this.options.ghostClass,!1),g(s,this.options.chosenClass,!1),w!==t?(E=p(s),E>=0&&(j(null,t,"sort",s,w,D,E),j(this,w,"sort",s,w,D,E),j(null,t,"add",s,w,D,E),j(this,w,"remove",s,w,D,E))):(v&&v.parentNode.removeChild(v),s.nextSibling!==x&&(E=p(s),E>=0&&(j(this,w,"update",s,w,D,E),j(this,w,"sort",s,w,D,E)))),a.active&&((null===E||-1===E)&&(E=D),j(this,w,"end",s,w,D,E),this.save())),w=s=t=u=x=v=y=z=G=H=I=E=A=B=F=a.active=null)},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?s&&(this._onDragOver(a),d(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],d=this.el.children,e=0,f=d.length,g=this.options;f>e;e++)a=d[e],c(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||o(a));return b},sort:function(a){var b={},d=this.el;this.toArray().forEach(function(a,e){var f=d.children[e];c(f,this.options.draggable,d)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(d.removeChild(b[a]),d.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return c(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:(c[a]=b,void("group"===a&&V(c)))},destroy:function(){var a=this.el;a[L]=null,f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),this.nativeDraggable&&(f(a,"dragover",this),f(a,"dragenter",this)),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),T.splice(T.indexOf(this._onDragOver),1),this._onDrop(),this.el=a=null}},a.utils={on:e,off:f,css:h,find:i,is:function(a,b){return!!c(a,b,a)},extend:r,throttle:q,closest:c,toggleClass:g,index:p},a.create=function(b,c){return new a(b,c)},a.version="1.4.2",a});
\ No newline at end of file
diff --git a/src/style.css b/src/style.css
new file mode 100644 (file)
index 0000000..c29e55d
--- /dev/null
@@ -0,0 +1,248 @@
+:root {
+  --hover-bg: #434c5e;
+  --hover-grn: #a3be8c;
+  --hover-bg-2: #848ead;
+  --remove-bg: #bf616a;
+  --default-fg: #d8dee9;
+  --default-bg: #2e3440;
+}
+  
+html, body {
+  font-size: 16px !important;
+  width: 100%;
+  height: 100%;
+}
+
+body {
+  margin: 0;
+  color: var(--default-fg);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+#links {
+  display: flex;
+  flex-direction: row;
+  display: -webkit-flex;
+  -webkit-flex-direction: row;
+  min-height: 283px;
+}
+
+ul {
+  position: relative;
+  list-style: none;
+  padding: 0;
+  width: 10rem;
+  margin: 0.5rem 2em 2em 2em;
+  float: left;
+}
+
+img {
+  float: left;
+  margin-top: 1px;
+  padding-right: 3px;
+}
+
+input {
+  font-size: 16px !important;
+  padding: 0;
+  height: 18.5px;
+  width: 112px;
+  margin: 5px;
+}
+
+input.name {
+  margin-bottom: 2.5px
+}
+
+input.url {
+  margin-top: 2.5px
+}
+
+input.colname {
+  width: calc(100% - 68px);
+  height: 19px;
+  margin: 14px 0 6px 0;
+  border: 2px var(--hover-bg) dashed;
+  background-color: var(inherit);
+  color: inherit;
+  font-family: inherit;
+}
+
+.remove {
+  width: 29px;
+  height: 35px;
+  padding: 0;
+  background-color: var(--hover-bg);
+  float: right;
+  text-align: center;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+  font-family: monospace;
+  display: flex;
+}
+
+.save {
+  padding-left: 8px;
+  float: right;
+  height: 60px;
+  width: 21px;
+  display: flex;
+  text-align: center;
+  align-items: center;
+  cursor: pointer;
+  background-color: var(--hover-bg-2);
+}
+
+.save:hover, .save:focus, .save:focus-within {
+  background-color: var(--hover-grn);
+}
+
+.remove:hover {
+  background-color: var(--remove-bg);
+}
+
+#edit, #addcol {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  background-color: var(--hover-bg);
+  width: 29px;
+  height: 29px;
+  padding: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+  font-weight: bold;
+}
+
+#edit:hover {
+  background-color: var(--hover-bg-2);
+}
+
+#addcol {
+  display: none;
+  right: 29px;
+}
+
+#addcol:hover {
+  background-color: var(--hover-grn);
+}
+
+.add, .rmcol {
+  background-color: var(--hover-bg);
+  margin-top: 14px;
+  width: 29px;
+  height: 29px;
+  padding: 0;
+  justify-content: center;
+  align-items: center;
+  float: right;
+  display: none;
+  cursor: pointer;
+}
+
+.add:hover {
+  background-color: var(--hover-grn) !important;
+}
+
+.rmcol {
+  display: flex;
+}
+
+.rmcol:hover {
+  background-color: var(--remove-bg) !important;
+}
+
+.new {
+  background-color: var(--hover-bg);
+}
+
+.title:hover > .add {
+  display: flex;
+}
+
+.title:hover .add:not(:hover),
+li:hover > .remove:not(:hover) {
+  display: flex;
+  background-color: var(--hover-bg);
+}
+
+li:hover > a, li > a:focus, li > a:focus-within {
+  display: inline-block;
+  background-color: var(--hover-bg);
+}
+
+li:hover {
+  background-color: var(--hover-bg);
+}
+
+a {
+  color: inherit;
+  outline: none;
+  display: block;
+  text-decoration: none;
+  padding: 0.5rem;
+  height: 19px;
+}
+
+.remove:not(:hover) {
+  display: none;
+}
+
+a, ul > .title > p, .colname {
+  width: calc(100% - 45px);
+  display: inline-block;
+}
+
+li.sortable-chosen.sortable-ghost {
+  background-color: var(--hover-bg-2);
+}
+
+.over {
+ /* border: 2px dashed var(--default-fg);*/
+}
+
+span.grip {
+  content: '....';
+  position: relative;
+  right: 20px;
+  top: 36px;
+  width: 10px;
+  height: 20px;
+  display: none;
+  overflow: hidden;
+  line-height: 5px;
+  padding: 3px 4px;
+  cursor: move;
+  vertical-align: middle;
+  font-size: 12px;
+  font-family: sans-serif;
+  font-weight: bold;
+  letter-spacing: 2px;
+}
+
+span.grip::after {
+  content: '.. .. .. ..';
+}
+
+.glyphicon-move {
+  cursor: move;
+  cursor: -webkit-grabbing;
+}
+
+.title {
+  margin-bottom: 0.5rem;
+  font-weight: bold;
+  text-align: left;
+  border-bottom: 1px solid var(--hover-bg);
+}
+
+.title p {
+  width: 160px;
+  margin: 16px 0 8px 0;
+}
diff --git a/style.css b/style.css
deleted file mode 100644 (file)
index c29e55d..0000000
--- a/style.css
+++ /dev/null
@@ -1,248 +0,0 @@
-:root {
-  --hover-bg: #434c5e;
-  --hover-grn: #a3be8c;
-  --hover-bg-2: #848ead;
-  --remove-bg: #bf616a;
-  --default-fg: #d8dee9;
-  --default-bg: #2e3440;
-}
-  
-html, body {
-  font-size: 16px !important;
-  width: 100%;
-  height: 100%;
-}
-
-body {
-  margin: 0;
-  color: var(--default-fg);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-}
-
-#links {
-  display: flex;
-  flex-direction: row;
-  display: -webkit-flex;
-  -webkit-flex-direction: row;
-  min-height: 283px;
-}
-
-ul {
-  position: relative;
-  list-style: none;
-  padding: 0;
-  width: 10rem;
-  margin: 0.5rem 2em 2em 2em;
-  float: left;
-}
-
-img {
-  float: left;
-  margin-top: 1px;
-  padding-right: 3px;
-}
-
-input {
-  font-size: 16px !important;
-  padding: 0;
-  height: 18.5px;
-  width: 112px;
-  margin: 5px;
-}
-
-input.name {
-  margin-bottom: 2.5px
-}
-
-input.url {
-  margin-top: 2.5px
-}
-
-input.colname {
-  width: calc(100% - 68px);
-  height: 19px;
-  margin: 14px 0 6px 0;
-  border: 2px var(--hover-bg) dashed;
-  background-color: var(inherit);
-  color: inherit;
-  font-family: inherit;
-}
-
-.remove {
-  width: 29px;
-  height: 35px;
-  padding: 0;
-  background-color: var(--hover-bg);
-  float: right;
-  text-align: center;
-  justify-content: center;
-  align-items: center;
-  cursor: pointer;
-  font-family: monospace;
-  display: flex;
-}
-
-.save {
-  padding-left: 8px;
-  float: right;
-  height: 60px;
-  width: 21px;
-  display: flex;
-  text-align: center;
-  align-items: center;
-  cursor: pointer;
-  background-color: var(--hover-bg-2);
-}
-
-.save:hover, .save:focus, .save:focus-within {
-  background-color: var(--hover-grn);
-}
-
-.remove:hover {
-  background-color: var(--remove-bg);
-}
-
-#edit, #addcol {
-  position: absolute;
-  right: 0;
-  bottom: 0;
-  background-color: var(--hover-bg);
-  width: 29px;
-  height: 29px;
-  padding: 0;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  cursor: pointer;
-  font-weight: bold;
-}
-
-#edit:hover {
-  background-color: var(--hover-bg-2);
-}
-
-#addcol {
-  display: none;
-  right: 29px;
-}
-
-#addcol:hover {
-  background-color: var(--hover-grn);
-}
-
-.add, .rmcol {
-  background-color: var(--hover-bg);
-  margin-top: 14px;
-  width: 29px;
-  height: 29px;
-  padding: 0;
-  justify-content: center;
-  align-items: center;
-  float: right;
-  display: none;
-  cursor: pointer;
-}
-
-.add:hover {
-  background-color: var(--hover-grn) !important;
-}
-
-.rmcol {
-  display: flex;
-}
-
-.rmcol:hover {
-  background-color: var(--remove-bg) !important;
-}
-
-.new {
-  background-color: var(--hover-bg);
-}
-
-.title:hover > .add {
-  display: flex;
-}
-
-.title:hover .add:not(:hover),
-li:hover > .remove:not(:hover) {
-  display: flex;
-  background-color: var(--hover-bg);
-}
-
-li:hover > a, li > a:focus, li > a:focus-within {
-  display: inline-block;
-  background-color: var(--hover-bg);
-}
-
-li:hover {
-  background-color: var(--hover-bg);
-}
-
-a {
-  color: inherit;
-  outline: none;
-  display: block;
-  text-decoration: none;
-  padding: 0.5rem;
-  height: 19px;
-}
-
-.remove:not(:hover) {
-  display: none;
-}
-
-a, ul > .title > p, .colname {
-  width: calc(100% - 45px);
-  display: inline-block;
-}
-
-li.sortable-chosen.sortable-ghost {
-  background-color: var(--hover-bg-2);
-}
-
-.over {
- /* border: 2px dashed var(--default-fg);*/
-}
-
-span.grip {
-  content: '....';
-  position: relative;
-  right: 20px;
-  top: 36px;
-  width: 10px;
-  height: 20px;
-  display: none;
-  overflow: hidden;
-  line-height: 5px;
-  padding: 3px 4px;
-  cursor: move;
-  vertical-align: middle;
-  font-size: 12px;
-  font-family: sans-serif;
-  font-weight: bold;
-  letter-spacing: 2px;
-}
-
-span.grip::after {
-  content: '.. .. .. ..';
-}
-
-.glyphicon-move {
-  cursor: move;
-  cursor: -webkit-grabbing;
-}
-
-.title {
-  margin-bottom: 0.5rem;
-  font-weight: bold;
-  text-align: left;
-  border-bottom: 1px solid var(--hover-bg);
-}
-
-.title p {
-  width: 160px;
-  margin: 16px 0 8px 0;
-}