From d5518a164e045890d832d90d3965d2f0569b19ce Mon Sep 17 00:00:00 2001 From: nazrin Date: Fri, 27 Dec 2024 04:15:08 +0000 Subject: [PATCH] Added WebDAV syncing --- .gitignore | 3 ++ css/index.css | 7 ++++ css/settings.css | 19 ++++++++++ editor.html | 2 ++ index.html | 31 ++++++++++------- js/editor.js | 10 +++--- js/file.js | 11 ++++-- js/index.js | 2 +- js/settings.js | 36 +++++++++++++++++++ js/storage.js | 29 +++++++-------- js/webdav.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 72 ++++++++++++++++++++------------------ settings.html | 41 ++++++++++++++++++++++ 13 files changed, 284 insertions(+), 70 deletions(-) create mode 100644 .gitignore create mode 100644 css/settings.css create mode 100644 js/settings.js create mode 100644 js/webdav.js create mode 100644 settings.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..41bb909 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ + +.msc + diff --git a/css/index.css b/css/index.css index 1df0154..89fbc83 100644 --- a/css/index.css +++ b/css/index.css @@ -143,3 +143,10 @@ header #new-file .btn-svg svg { margin-left:auto; margin-right:5px; } +.file-type[data-type="JS"]{ + color: #ffde24 +} +.file-type[data-type="CSS"]{ + color: #15a0dc +} + diff --git a/css/settings.css b/css/settings.css new file mode 100644 index 0000000..2016088 --- /dev/null +++ b/css/settings.css @@ -0,0 +1,19 @@ + +main{ + color: white; + margin: 10px; +} + +input[type="text"], input[type="password"]{ + display: block; + width: 100%; + background: #222; + color: white; + outline: none; + border: none; + font-size: 1.1em; + padding: 2px; + margin-bottom: 5px; +} + + diff --git a/editor.html b/editor.html index cd4a494..ea84545 100644 --- a/editor.html +++ b/editor.html @@ -50,5 +50,7 @@ + + diff --git a/index.html b/index.html index d02fae4..e16392e 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,23 @@ - + - Custom Web + Custom Web - - - - -
+ + + + +
- Custom Web + + Custom Web + + + + + + @@ -27,8 +34,8 @@
- - - - + + + + diff --git a/js/editor.js b/js/editor.js index 46402f4..7705fea 100644 --- a/js/editor.js +++ b/js/editor.js @@ -138,8 +138,8 @@ function createLinkEntry(link = "") { enableInput.addEventListener("change", save); function save() { - let info = file.info; - info.enabled = enableInput.checked; + let info = file.info; + info.enabled = enableInput.checked; info.name = nameInput.value; info.content = editor.getValue(); @@ -154,8 +154,10 @@ function save() { } info.links = links; - file.save(); - saveBtn.classList.remove("edited"); + file.touch(); + file.save(); + WebDAV.putFile(file.info); + saveBtn.classList.remove("edited"); } saveBtn.addEventListener("click", save); window.addEventListener("keydown", (e) => { diff --git a/js/file.js b/js/file.js index 67f2fd3..52316eb 100644 --- a/js/file.js +++ b/js/file.js @@ -2,13 +2,16 @@ function File(id){ let _this = this; if(!id) id = File.uuid(); let info = this.info = {id, enabled:true}; - + this.changeInfo = function(i){ for(let _i in i){ info[_i] = i[_i]; } }; - + this.touch = function(){ + info.lastModified = ((new Date()).getTime() / 1000)|0; + } + this.save = function(){ return new Promise(function(succ, err){ Storage.save({[File.PREFIX+info.id]:info}).then(_ => { @@ -16,12 +19,14 @@ function File(id){ }); }); }; - + this.onChange = function(info){}; //Placeholder function Storage.onChange(File.PREFIX+id, newInfo => { _this.changeInfo(newInfo); _this.onChange(newInfo); }); + + return this; } File.PREFIX = "FILE-"; File.saveIndex = function(id, enabled){ diff --git a/js/index.js b/js/index.js index f14d073..51fabe6 100644 --- a/js/index.js +++ b/js/index.js @@ -5,7 +5,7 @@ function createFileEntry(fileObject, element){ file.innerHTML = ` ${info.name} - ${info.type} + ${info.type}
diff --git a/js/settings.js b/js/settings.js new file mode 100644 index 0000000..d869b14 --- /dev/null +++ b/js/settings.js @@ -0,0 +1,36 @@ + +let settings = {}; + +document.querySelector("input#webdav-push-all").addEventListener("click", WebDAV.putAllFiles); +document.querySelector("input#webdav-pull-all").addEventListener("click", WebDAV.loadAllFiles); + +async function main(){ + const webdavAddressInput = document.querySelector("input#webdav-address") + const webdavUsernameInput = document.querySelector("input#webdav-username") + const webdavPasswordInput = document.querySelector("input#webdav-password") + + document.getElementById("optionsForm").addEventListener("change", ev => { + console.log(ev) + settings.webdavAddress = webdavAddressInput.value; + settings.webdavUsername = webdavUsernameInput.value; + settings.webdavPassword = webdavPasswordInput.value; + Storage.saveSync({ settings }); + WebDAV.init(); + }); + + const data = await Storage.loadSync(["settings"]); + Object.assign(settings, data.settings); + + console.log(settings) + + if(settings.webdavAddress !== undefined) + webdavAddressInput.value = settings.webdavAddress; + if(settings.webdavUsername !== undefined) + webdavUsernameInput.value = settings.webdavUsername; + if(settings.webdavPassword !== undefined) + webdavPasswordInput.value = settings.webdavPassword; + + WebDAV.init(); +} +main(); + diff --git a/js/storage.js b/js/storage.js index 0abd85b..dd1dba5 100644 --- a/js/storage.js +++ b/js/storage.js @@ -1,19 +1,13 @@ let Storage = {}; -Storage.save = function(object){ - return new Promise(function(succ, err){ - chrome.storage.sync.set(object, succ); - }); -} -Storage.load = function(objects){ - return new Promise(function(succ, err){ - chrome.storage.sync.get(objects, succ); - }); -} -Storage.remove = function(objects){ - return new Promise(function(succ, err){ - chrome.storage.sync.remove(objects, succ); - }); -} + +Storage.save = browser.storage.local.set; +Storage.load = browser.storage.local.get; +Storage.remove = browser.storage.local.remove; + +Storage.saveSync = browser.storage.sync.set; +Storage.loadSync = browser.storage.sync.get; +Storage.removeSync = browser.storage.sync.remove; + Storage.callbacks = {}; Storage.onChange = function(key, fn){ if(!Storage.callbacks[key]) Storage.callbacks[key] = []; @@ -26,9 +20,10 @@ Storage.emit = function(key, newValue){ }); } chrome.storage.onChanged.addListener(function(objects, area){ - if(area!="sync") return; + if(area!="local") return; for(let id in objects){ let newValue = objects[id].newValue; if(newValue) { Storage.emit(id, newValue); } } -}); \ No newline at end of file +}); + diff --git a/js/webdav.js b/js/webdav.js new file mode 100644 index 0000000..b1f357c --- /dev/null +++ b/js/webdav.js @@ -0,0 +1,91 @@ +let WebDAV = {}; + +WebDAV.normAddress = function(path){ + return settings.webdavAddress.replace(/\/$/, ""); +} +WebDAV.makePath = function(path){ + if(typeof(path) !== "string") + path = path.join("/"); + return [ WebDAV.normAddress(), path ].join("/"); +} +WebDAV.req = function(path, type, opts){ + path = WebDAV.makePath(path); + opts = opts ?? {}; + opts.method = type; + opts.headers = { + Authorization: "Basic " + btoa(`${settings.webdavUsername}:${settings.webdavPassword}`), + }; + return fetch(path, opts); +} +WebDAV.mkcol = async (path, opts) => WebDAV.req(path, "MKCOL", opts); +WebDAV.get = async (path, opts) => WebDAV.req(path, "GET", opts); +WebDAV.put = async (path, body, opts) => { + opts = opts ?? {}; + opts.body = body; + return WebDAV.req(path, "PUT", opts); +} +WebDAV.propfind = async (path, opts) => { + let res = await WebDAV.req(path, "PROPFIND", opts); + let xmlText = await res.text(); + let parser = new DOMParser(); + let xmlDoc = parser.parseFromString(xmlText, "text/xml"); + + let responses = xmlDoc.getElementsByTagName("D:response"); + + let items = Array.from(responses).map(response => { + let href = response.getElementsByTagName("D:href")[0].textContent; + let propstat = response.getElementsByTagName("D:propstat")[0]; + let lastModified = propstat.getElementsByTagName("D:lastmodified")[0].textContent; + return { href, lastModified }; + }); + + return items; +} + +WebDAV.listAllFiles = async function(){ + let files = []; + for(file of await WebDAV.propfind([])){ + if(!file.href.endsWith(".json")) + continue; + let id = file.href.split("/").pop().replace(/\.json$/, "") + let lastModified = parseInt(file.lastModified); + files.push({ id, lastModified }); + } + return files; +} +WebDAV.loadFile = async function(id){ + let info = await (await WebDAV.get(`${id}.json`)).json(); + let file = File(info.id); + file.changeInfo(info); + file.save(); +} +WebDAV.loadAllFiles = async function(){ + for({ id, lastModified } of await WebDAV.listAllFiles()){ + try { + let file = await File.load(id); + console.log(file.info, lastModified) + if(file.info.lastModified && file.info.lastModified >= lastModified){ + console.log("Skipping newer file", id) + continue; + } + console.log("Pulling outdated file", id) + } catch(exc){ + console.log("Pulling unknown file", id) + } + await WebDAV.loadFile(id); + } +} +WebDAV.putFile = async function(info){ + WebDAV.put(`${info.id}.json`, JSON.stringify(info)); +} +WebDAV.putAllFiles = async function(){ + for(file of await File.loadAll()) + await WebDAV.putFile(file.info); +} + +WebDAV.init = async function(){ + if(!settings.webdavAddress) + return; + await WebDAV.mkcol([]); +} + diff --git a/manifest.json b/manifest.json index 10d9c73..73b86c5 100644 --- a/manifest.json +++ b/manifest.json @@ -1,33 +1,39 @@ -{ - "name": "Custom Web", - "description": "Customize web pages with scripts and styles", - "icons": { - "16": "img/icon16.png", - "48": "img/icon48.png", - "128": "img/icon128.png" - }, - "version": "1.0.0.2", - "manifest_version": 2, - "permissions" : ["storage"], - "background": { - "scripts": ["js/storage.js", "js/file.js", "background.js"], - "persistent": false - }, - "content_scripts": [ - { - "matches": [""], - "all_frames": true, - "run_at": "document_start", - "js": [ - "js/storage.js", - "js/file.js", - "content.js" - ] - } - ], - "browser_action": { - "default_title": "Custom Web", - "default_icon": "img/icon128.png", - "default_popup": "popup.html" - } -} +{ + "name": "Custom Web", + "description": "Customize web pages with scripts and styles", + "icons": { + "16": "img/icon16.png", + "48": "img/icon48.png", + "128": "img/icon128.png" + }, + "version": "1.0.0.2", + "manifest_version": 2, + "permissions" : ["storage"], + "background": { + "scripts": ["js/storage.js", "js/file.js", "background.js"], + "persistent": false + }, + "content_scripts": [ + { + "matches": [""], + "all_frames": true, + "run_at": "document_start", + "js": [ + "js/storage.js", + "js/file.js", + "content.js" + ] + } + ], + "browser_action": { + "default_title": "Custom Web", + "default_icon": "img/icon128.png", + "default_popup": "popup.html" + }, + "browser_specific_settings": { + "gecko": { + "id": "tanyaCustomWeb@git.sylvie.moe", + "strict_min_version": "58.0" + } + } +} diff --git a/settings.html b/settings.html new file mode 100644 index 0000000..5c75f1c --- /dev/null +++ b/settings.html @@ -0,0 +1,41 @@ + + + + + Custom Web - Settings + + + + + + +
+ + Custom Web +
+
+
+
+

Storage

+ + + + + + + + + + + + +
+
+
+ + + + + + +