Added WebDAV syncing
This commit is contained in:
parent
b9e8278689
commit
d5518a164e
13 changed files with 284 additions and 70 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
.msc
|
||||||
|
|
||||||
|
|
@ -143,3 +143,10 @@ header #new-file .btn-svg svg {
|
||||||
margin-left:auto;
|
margin-left:auto;
|
||||||
margin-right:5px;
|
margin-right:5px;
|
||||||
}
|
}
|
||||||
|
.file-type[data-type="JS"]{
|
||||||
|
color: #ffde24
|
||||||
|
}
|
||||||
|
.file-type[data-type="CSS"]{
|
||||||
|
color: #15a0dc
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
19
css/settings.css
Normal file
19
css/settings.css
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,5 +50,7 @@
|
||||||
<script src="js/ace/ace.js"></script>
|
<script src="js/ace/ace.js"></script>
|
||||||
<script src="js/storage.js"></script>
|
<script src="js/storage.js"></script>
|
||||||
<script src="js/file.js"></script>
|
<script src="js/file.js"></script>
|
||||||
|
<script src="js/webdav.js"></script>
|
||||||
|
<script src="js/settings.js"></script>
|
||||||
<script src="js/editor.js"></script>
|
<script src="js/editor.js"></script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,14 @@
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<img src="img/icon48.png"/>
|
<img src="img/icon48.png"/>
|
||||||
|
<span>
|
||||||
<span>Custom Web</span>
|
<span>Custom Web</span>
|
||||||
|
<span class="btn-svg" id="settings-page">
|
||||||
|
<a href="settings.html">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
<span id="new-file">
|
<span id="new-file">
|
||||||
<span class="btn btn-svg" id="new-up" title="Upload file">
|
<span class="btn btn-svg" id="new-up" title="Upload file">
|
||||||
<svg viewBox="0 0 24 24">
|
<svg viewBox="0 0 24 24">
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,9 @@ function save() {
|
||||||
}
|
}
|
||||||
info.links = links;
|
info.links = links;
|
||||||
|
|
||||||
|
file.touch();
|
||||||
file.save();
|
file.save();
|
||||||
|
WebDAV.putFile(file.info);
|
||||||
saveBtn.classList.remove("edited");
|
saveBtn.classList.remove("edited");
|
||||||
}
|
}
|
||||||
saveBtn.addEventListener("click", save);
|
saveBtn.addEventListener("click", save);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ function File(id){
|
||||||
info[_i] = i[_i];
|
info[_i] = i[_i];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.touch = function(){
|
||||||
|
info.lastModified = ((new Date()).getTime() / 1000)|0;
|
||||||
|
}
|
||||||
|
|
||||||
this.save = function(){
|
this.save = function(){
|
||||||
return new Promise(function(succ, err){
|
return new Promise(function(succ, err){
|
||||||
|
|
@ -22,6 +25,8 @@ function File(id){
|
||||||
_this.changeInfo(newInfo);
|
_this.changeInfo(newInfo);
|
||||||
_this.onChange(newInfo);
|
_this.onChange(newInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
File.PREFIX = "FILE-";
|
File.PREFIX = "FILE-";
|
||||||
File.saveIndex = function(id, enabled){
|
File.saveIndex = function(id, enabled){
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ function createFileEntry(fileObject, element){
|
||||||
file.innerHTML = `
|
file.innerHTML = `
|
||||||
<label class="check" title="Enable or disable file"><input type="checkbox" ${info.enabled?"checked='true'":""}><span></span></label>
|
<label class="check" title="Enable or disable file"><input type="checkbox" ${info.enabled?"checked='true'":""}><span></span></label>
|
||||||
<span class="file-name">${info.name}</span>
|
<span class="file-name">${info.name}</span>
|
||||||
<span class="file-type">${info.type}</span>
|
<span class="file-type" data-type="${info.type}">${info.type}</span>
|
||||||
<div class="file-icons">
|
<div class="file-icons">
|
||||||
<span class="file-icon file-edit" title="Edit file">
|
<span class="file-icon file-edit" title="Edit file">
|
||||||
<svg viewBox="0 0 48 48">
|
<svg viewBox="0 0 48 48">
|
||||||
|
|
|
||||||
36
js/settings.js
Normal file
36
js/settings.js
Normal file
|
|
@ -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();
|
||||||
|
|
||||||
|
|
@ -1,19 +1,13 @@
|
||||||
let Storage = {};
|
let Storage = {};
|
||||||
Storage.save = function(object){
|
|
||||||
return new Promise(function(succ, err){
|
Storage.save = browser.storage.local.set;
|
||||||
chrome.storage.sync.set(object, succ);
|
Storage.load = browser.storage.local.get;
|
||||||
});
|
Storage.remove = browser.storage.local.remove;
|
||||||
}
|
|
||||||
Storage.load = function(objects){
|
Storage.saveSync = browser.storage.sync.set;
|
||||||
return new Promise(function(succ, err){
|
Storage.loadSync = browser.storage.sync.get;
|
||||||
chrome.storage.sync.get(objects, succ);
|
Storage.removeSync = browser.storage.sync.remove;
|
||||||
});
|
|
||||||
}
|
|
||||||
Storage.remove = function(objects){
|
|
||||||
return new Promise(function(succ, err){
|
|
||||||
chrome.storage.sync.remove(objects, succ);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Storage.callbacks = {};
|
Storage.callbacks = {};
|
||||||
Storage.onChange = function(key, fn){
|
Storage.onChange = function(key, fn){
|
||||||
if(!Storage.callbacks[key]) Storage.callbacks[key] = [];
|
if(!Storage.callbacks[key]) Storage.callbacks[key] = [];
|
||||||
|
|
@ -26,9 +20,10 @@ Storage.emit = function(key, newValue){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
chrome.storage.onChanged.addListener(function(objects, area){
|
chrome.storage.onChanged.addListener(function(objects, area){
|
||||||
if(area!="sync") return;
|
if(area!="local") return;
|
||||||
for(let id in objects){
|
for(let id in objects){
|
||||||
let newValue = objects[id].newValue;
|
let newValue = objects[id].newValue;
|
||||||
if(newValue) { Storage.emit(id, newValue); }
|
if(newValue) { Storage.emit(id, newValue); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
91
js/webdav.js
Normal file
91
js/webdav.js
Normal file
|
|
@ -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([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -29,5 +29,11 @@
|
||||||
"default_title": "Custom Web",
|
"default_title": "Custom Web",
|
||||||
"default_icon": "img/icon128.png",
|
"default_icon": "img/icon128.png",
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
|
},
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "tanyaCustomWeb@git.sylvie.moe",
|
||||||
|
"strict_min_version": "58.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
settings.html
Normal file
41
settings.html
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>Custom Web - Settings</title>
|
||||||
|
<link rel="shortcut icon" type="image/png" href="img/icon48.png">
|
||||||
|
<link rel="stylesheet" href="css/common.css"/>
|
||||||
|
<link rel="stylesheet" href="css/index.css"/>
|
||||||
|
<link rel="stylesheet" href="css/settings.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<img src="img/icon48.png"/>
|
||||||
|
<span>Custom Web</span>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<form id="optionsForm">
|
||||||
|
<section id="storage-settings">
|
||||||
|
<h2>Storage</h2>
|
||||||
|
|
||||||
|
<label for="webdav-address">WebDAV Address</label>
|
||||||
|
<input id="webdav-address" type="text" placeholder="https://example.com:1234/CustomWeb/"></input>
|
||||||
|
|
||||||
|
<label for="webdav-username">WebDAV Username</label>
|
||||||
|
<input id="webdav-username" type="text" placeholder="username"></input>
|
||||||
|
|
||||||
|
<label for="webdav-password">WebDAV Password</label>
|
||||||
|
<input id="webdav-password" type="password" placeholder="********"></input>
|
||||||
|
|
||||||
|
<input id="webdav-push-all" type="button" value="Push all"></input>
|
||||||
|
<input id="webdav-pull-all" type="button" value="Pull all"></input>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<script src="js/storage.js"></script>
|
||||||
|
<script src="js/file.js"></script>
|
||||||
|
<script src="js/webdav.js"></script>
|
||||||
|
<script src="js/settings.js"></script>
|
||||||
|
</html>
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue