Merge pull request #2054 from maxberger/master
UI: More reorganization of code, support browser back/forward buttons!
This commit is contained in:
@@ -126,3 +126,24 @@ def test_navigation_refresh_button(
|
|||||||
page.click('#logoutview a[data-name="refresh"]')
|
page.click('#logoutview a[data-name="refresh"]')
|
||||||
# It shows LoadingScene briefly then back to CollectionsScene
|
# It shows LoadingScene briefly then back to CollectionsScene
|
||||||
expect(page.locator("#collectionsscene")).to_be_visible()
|
expect(page.locator("#collectionsscene")).to_be_visible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_navigation_browser_history(
|
||||||
|
context: BrowserContext, page: Page, radicale_server: str, config: Config
|
||||||
|
) -> None:
|
||||||
|
login(page, radicale_server, config, context=context)
|
||||||
|
expect(page.locator("#collectionsscene")).to_be_visible()
|
||||||
|
|
||||||
|
# Navigate forward inside the SPA to CreateEditCollectionScene
|
||||||
|
page.click('a[data-name="new"]')
|
||||||
|
expect(page.locator("#createcollectionscene")).to_be_visible()
|
||||||
|
|
||||||
|
# Emulate browser Back button
|
||||||
|
page.go_back()
|
||||||
|
expect(page.locator("#createcollectionscene")).to_be_hidden()
|
||||||
|
expect(page.locator("#collectionsscene")).to_be_visible()
|
||||||
|
|
||||||
|
# Emulate browser Forward button
|
||||||
|
page.go_forward()
|
||||||
|
expect(page.locator("#collectionsscene")).to_be_hidden()
|
||||||
|
expect(page.locator("#createcollectionscene")).to_be_visible()
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ main {
|
|||||||
#collectionsscene article p {
|
#collectionsscene article p {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
max-height: 130px;
|
max-height: 130px;
|
||||||
overflow: overlay;
|
overflow: auto;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +476,8 @@ a.blue:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.inline {
|
button.inline {
|
||||||
padding-inline: 1px;
|
padding-left: 1px;
|
||||||
|
padding-right: 1px;
|
||||||
min-width: 2em;
|
min-width: 2em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,13 +127,13 @@
|
|||||||
<option value="WEBCAL">webcal</option>
|
<option value="WEBCAL">webcal</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="displayname">Title:</label>
|
<label for="displayname">Title:</label>
|
||||||
<input data-name="displayname" type="text">
|
<input data-name="displayname" id="displayname" type="text">
|
||||||
<label for="description">Description:</label>
|
<label for="description">Description:</label>
|
||||||
<input data-name="description" type="text">
|
<input data-name="description" id="description" type="text">
|
||||||
<label for="source">Source:</label>
|
<label for="source">Source:</label>
|
||||||
<input data-name="source" type="url">
|
<input data-name="source" id="source" type="url">
|
||||||
<label for="color">Color:</label>
|
<label for="color">Color:</label>
|
||||||
<input data-name="color" type="color">
|
<input data-name="color" id="color" type="color">
|
||||||
<br>
|
<br>
|
||||||
<span class="error hidden" data-name="error"></span>
|
<span class="error hidden" data-name="error"></span>
|
||||||
<br>
|
<br>
|
||||||
@@ -309,15 +309,15 @@
|
|||||||
<option value="WEBCAL">Webcal</option>
|
<option value="WEBCAL">Webcal</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="href">HREF:</label>
|
<label for="href">HREF:</label>
|
||||||
<input data-name="href" type="text">
|
<input data-name="href" id="href" type="text">
|
||||||
<label for="displayname">Title:</label>
|
<label for="displayname">Title:</label>
|
||||||
<input data-name="displayname" type="text">
|
<input data-name="displayname" id="create_displayname" type="text">
|
||||||
<label for="description">Description:</label>
|
<label for="description">Description:</label>
|
||||||
<input data-name="description" type="text">
|
<input data-name="description" id="create_description" type="text">
|
||||||
<label for="source">Source:</label>
|
<label for="source">Source:</label>
|
||||||
<input data-name="source" type="url">
|
<input data-name="source" id="create_source" type="url">
|
||||||
<label for="color">Color:</label>
|
<label for="color">Color:</label>
|
||||||
<input data-name="color" type="color">
|
<input data-name="color" id="create_color" type="color">
|
||||||
<br>
|
<br>
|
||||||
<span class="error" data-name="error"></span>
|
<span class="error" data-name="error"></span>
|
||||||
<br>
|
<br>
|
||||||
@@ -342,9 +342,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<form>
|
<form>
|
||||||
<label for="uploadfile">File:</label>
|
<label for="uploadfile">File:</label>
|
||||||
<input data-name="uploadfile" type="file" accept=".ics, .vcf" multiple>
|
<input data-name="uploadfile" id="uploadfile" type="file" accept=".ics, .vcf" multiple>
|
||||||
<label for="href">HREF:</label>
|
<label for="upload_href">HREF:</label>
|
||||||
<input data-name="href" type="text">
|
<input data-name="href" id="upload_href" type="text">
|
||||||
<small data-name="hreflimitmsg" class="hidden">You can only specify the HREF if you upload 1 file.</small>
|
<small data-name="hreflimitmsg" class="hidden">You can only specify the HREF if you upload 1 file.</small>
|
||||||
<button type="submit" class="green" data-name="submit">Upload</button>
|
<button type="submit" class="green" data-name="submit">Upload</button>
|
||||||
<button type="button" class="red" data-name="close">Close</button>
|
<button type="button" class="red" data-name="close">Close</button>
|
||||||
|
|||||||
@@ -45,19 +45,25 @@ export class CollectionsScene {
|
|||||||
* scene is popped.
|
* scene is popped.
|
||||||
*/
|
*/
|
||||||
constructor(user, password, principal_collection, onerror) {
|
constructor(user, password, principal_collection, onerror) {
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("collectionsscene");
|
this._user = user;
|
||||||
/** @type {HTMLElement} */ let template = get_element(html_scene, "[data-name=collectiontemplate]");
|
this._password = password;
|
||||||
/** @type {HTMLElement} */ let new_btn = get_element(html_scene, "[data-name=new]");
|
this._principal_collection = principal_collection;
|
||||||
/** @type {HTMLElement} */ let upload_btn = get_element(html_scene, "[data-name=upload]");
|
this._onerror = onerror;
|
||||||
/** @type {HTMLElement} */ let incomingshares_btn = get_element(html_scene, "[data-name=incomingshares]");
|
|
||||||
/** @type {HTMLElement} */ let error_div = get_element(html_scene, "[data-name=collectionsscene_error]");
|
|
||||||
|
|
||||||
/** @type {Array<HTMLElement>} */ let nodes = [];
|
this._html_scene = get_element_by_id("collectionsscene");
|
||||||
let errorHandler = new ErrorHandler(error_div);
|
this._template = get_element(this._html_scene, "[data-name=collectiontemplate]");
|
||||||
|
this._new_btn = get_element(this._html_scene, "[data-name=new]");
|
||||||
|
this._upload_btn = get_element(this._html_scene, "[data-name=upload]");
|
||||||
|
this._incomingshares_btn = get_element(this._html_scene, "[data-name=incomingshares]");
|
||||||
|
this._error_div = get_element(this._html_scene, "[data-name=collectionsscene_error]");
|
||||||
|
|
||||||
function onnew() {
|
/** @type {Array<HTMLElement>} */ this._nodes = [];
|
||||||
|
this._errorHandler = new ErrorHandler(this._error_div);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onnew() {
|
||||||
try {
|
try {
|
||||||
let create_collection_scene = new CreateEditCollectionScene(user, password, principal_collection);
|
let create_collection_scene = new CreateEditCollectionScene(this._user, this._password, this._principal_collection);
|
||||||
push_scene(create_collection_scene);
|
push_scene(create_collection_scene);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -65,9 +71,9 @@ export class CollectionsScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onupload() {
|
_onupload() {
|
||||||
try {
|
try {
|
||||||
let upload_scene = new UploadCollectionScene(user, password, principal_collection);
|
let upload_scene = new UploadCollectionScene(this._user, this._password, this._principal_collection);
|
||||||
push_scene(upload_scene);
|
push_scene(upload_scene);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -75,9 +81,9 @@ export class CollectionsScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onincomingshares() {
|
_onincomingshares() {
|
||||||
try {
|
try {
|
||||||
let incoming_sharing_scene = new IncomingSharingScene(user, password);
|
let incoming_sharing_scene = new IncomingSharingScene(this._user, this._password);
|
||||||
push_scene(incoming_sharing_scene);
|
push_scene(incoming_sharing_scene);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -88,9 +94,9 @@ export class CollectionsScene {
|
|||||||
/**
|
/**
|
||||||
* @param {Collection} collection
|
* @param {Collection} collection
|
||||||
*/
|
*/
|
||||||
function onedit(collection) {
|
_onedit(collection) {
|
||||||
try {
|
try {
|
||||||
let edit_collection_scene = new CreateEditCollectionScene(user, password, collection);
|
let edit_collection_scene = new CreateEditCollectionScene(this._user, this._password, collection);
|
||||||
push_scene(edit_collection_scene);
|
push_scene(edit_collection_scene);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -101,9 +107,9 @@ export class CollectionsScene {
|
|||||||
/**
|
/**
|
||||||
* @param {Collection} collection
|
* @param {Collection} collection
|
||||||
*/
|
*/
|
||||||
function onshare(collection) {
|
_onshare(collection) {
|
||||||
try {
|
try {
|
||||||
let share_collection_scene = new ShareCollectionScene(user, password, collection);
|
let share_collection_scene = new ShareCollectionScene(this._user, this._password, collection);
|
||||||
push_scene(share_collection_scene);
|
push_scene(share_collection_scene);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -114,10 +120,10 @@ export class CollectionsScene {
|
|||||||
/**
|
/**
|
||||||
* @param {Collection} collection
|
* @param {Collection} collection
|
||||||
*/
|
*/
|
||||||
function ondelete(collection) {
|
_ondelete(collection) {
|
||||||
try {
|
try {
|
||||||
let delete_collection_scene = new DeleteConfirmationScene(
|
let delete_collection_scene = new DeleteConfirmationScene(
|
||||||
user, password, "Delete Collection", collection, collection.displayname || collection.href,
|
this._user, this._password, "Delete Collection", collection, collection.displayname || collection.href,
|
||||||
delete_collection, true
|
delete_collection, true
|
||||||
);
|
);
|
||||||
push_scene(delete_collection_scene);
|
push_scene(delete_collection_scene);
|
||||||
@@ -132,26 +138,26 @@ export class CollectionsScene {
|
|||||||
* @param {import("../api/sharing.js").Share[]} shares
|
* @param {import("../api/sharing.js").Share[]} shares
|
||||||
* @param {boolean} clear_error
|
* @param {boolean} clear_error
|
||||||
*/
|
*/
|
||||||
function show_collections(collections, shares, clear_error) {
|
_show_collections(collections, shares, clear_error) {
|
||||||
/** @type {HTMLElement} */ let navBar = get_element(document, "#logoutview");
|
/** @type {HTMLElement} */ let navBar = get_element(document, "#logoutview");
|
||||||
let heightOfNavBar = navBar.offsetHeight + "px";
|
let heightOfNavBar = navBar.offsetHeight + "px";
|
||||||
html_scene.style.marginTop = heightOfNavBar;
|
this._html_scene.style.marginTop = heightOfNavBar;
|
||||||
html_scene.style.height = "calc(100vh - " + heightOfNavBar + ")";
|
this._html_scene.style.height = "calc(100vh - " + heightOfNavBar + ")";
|
||||||
|
|
||||||
if (clear_error) {
|
if (clear_error) {
|
||||||
errorHandler.clearError();
|
this._errorHandler.clearError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear old nodes
|
// Clear old nodes
|
||||||
nodes.forEach(function (node) {
|
this._nodes.forEach(function (node) {
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes = [];
|
this._nodes = [];
|
||||||
|
|
||||||
collections.forEach(function (/** @type {Collection} */ collection) {
|
collections.forEach((/** @type {Collection} */ collection) => {
|
||||||
/** @type {HTMLElement} */ let node = /** @type {HTMLElement} */(template.cloneNode(true));
|
/** @type {HTMLElement} */ let node = /** @type {HTMLElement} */(this._template.cloneNode(true));
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
/** @type {HTMLElement} */ let title_form = get_element(node, "[data-name=title]");
|
/** @type {HTMLElement} */ let title_form = get_element(node, "[data-name=title]");
|
||||||
/** @type {HTMLElement} */ let description_form = get_element(node, "[data-name=description]");
|
/** @type {HTMLElement} */ let description_form = get_element(node, "[data-name=description]");
|
||||||
@@ -186,7 +192,7 @@ export class CollectionsScene {
|
|||||||
s => (s.ShareType === "map") &&
|
s => (s.ShareType === "map") &&
|
||||||
(s.PathOrToken || "").replace(/\/+$/, "") === (collection.href || "").replace(/\/+$/, ""));
|
(s.PathOrToken || "").replace(/\/+$/, "") === (collection.href || "").replace(/\/+$/, ""));
|
||||||
if (share) {
|
if (share) {
|
||||||
if (share.Owner !== user) {
|
if (share.Owner !== this._user) {
|
||||||
share_info.classList.remove("hidden");
|
share_info.classList.remove("hidden");
|
||||||
get_element(node, "[data-name=shared-by-owner]").textContent = share.Owner;
|
get_element(node, "[data-name=shared-by-owner]").textContent = share.Owner;
|
||||||
} else {
|
} else {
|
||||||
@@ -222,9 +228,9 @@ export class CollectionsScene {
|
|||||||
let href = SERVER + collection.href;
|
let href = SERVER + collection.href;
|
||||||
url_form.value = href;
|
url_form.value = href;
|
||||||
download_btn.href = href;
|
download_btn.href = href;
|
||||||
download_btn.onclick = function (event) {
|
download_btn.onclick = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let auth = get_auth_header(user, password);
|
let auth = get_auth_header(this._user, this._password);
|
||||||
let headers = auth ? {
|
let headers = auth ? {
|
||||||
'Authorization': auth
|
'Authorization': auth
|
||||||
} : undefined;
|
} : undefined;
|
||||||
@@ -242,8 +248,8 @@ export class CollectionsScene {
|
|||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
})["catch"](function (error) {
|
})["catch"]((error) => {
|
||||||
errorHandler.setError(error.message);
|
this._errorHandler.setError(error.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (collection.type == CollectionType.WEBCAL) {
|
if (collection.type == CollectionType.WEBCAL) {
|
||||||
@@ -251,44 +257,47 @@ export class CollectionsScene {
|
|||||||
download_btn.parentElement.classList.add("hidden");
|
download_btn.parentElement.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete_btn.onclick = function () { return ondelete(collection); };
|
delete_btn.onclick = () => { return this._ondelete(collection); };
|
||||||
edit_btn.onclick = function () { return onedit(collection); };
|
edit_btn.onclick = () => { return this._onedit(collection); };
|
||||||
share_btn.onclick = function () { return onshare(collection); };
|
share_btn.onclick = () => { return this._onshare(collection); };
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
nodes.push(node);
|
this._nodes.push(node);
|
||||||
if (template.parentNode) {
|
if (this._template.parentNode) {
|
||||||
template.parentNode.insertBefore(node, template);
|
this._template.parentNode.insertBefore(node, this._template);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.errorwrapper = function (/** @type {string} */ error) {
|
_errorwrapper(/** @type {string} */ error) {
|
||||||
errorHandler.setError(error);
|
this._errorHandler.setError(error);
|
||||||
if (onerror) onerror(error);
|
if (this._onerror) this._onerror(error);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
new_btn.onclick = onnew;
|
this._new_btn.onclick = () => this._onnew();
|
||||||
upload_btn.onclick = onupload;
|
this._upload_btn.onclick = () => this._onupload();
|
||||||
incomingshares_btn.onclick = onincomingshares;
|
this._incomingshares_btn.onclick = () => this._onincomingshares();
|
||||||
collectionsCache.getChildCollections(user, password, principal_collection, this.errorwrapper, show_collections);
|
collectionsCache.getChildCollections(this._user, this._password, this._principal_collection, (e) => this._errorwrapper(e), (c, s, ce) => this._show_collections(c, s, ce));
|
||||||
collectionsCache.getServerFeatures(user, password, this.errorwrapper, maybe_enable_sharing_options);
|
collectionsCache.getServerFeatures(this._user, this._password, (e) => this._errorwrapper(e), maybe_enable_sharing_options);
|
||||||
};
|
}
|
||||||
this.hide = function () {
|
|
||||||
html_scene.classList.add("hidden");
|
hide() {
|
||||||
new_btn.onclick = null;
|
this._html_scene.classList.add("hidden");
|
||||||
upload_btn.onclick = null;
|
this._new_btn.onclick = null;
|
||||||
incomingshares_btn.onclick = null;
|
this._upload_btn.onclick = null;
|
||||||
|
this._incomingshares_btn.onclick = null;
|
||||||
// remove collection
|
// remove collection
|
||||||
nodes.forEach(function (node) {
|
this._nodes.forEach(function (node) {
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes = [];
|
this._nodes = [];
|
||||||
};
|
|
||||||
this.release = function () {
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release() {
|
||||||
|
}
|
||||||
|
|
||||||
|
is_transient() { return false; }
|
||||||
}
|
}
|
||||||
@@ -41,50 +41,53 @@ export class CreateEditCollectionScene {
|
|||||||
* Otherwise the collection will be edited.
|
* Otherwise the collection will be edited.
|
||||||
*/
|
*/
|
||||||
constructor(user, password, collection) {
|
constructor(user, password, collection) {
|
||||||
let edit = collection.type !== CollectionType.PRINCIPAL;
|
this._user = user;
|
||||||
let html_scene = get_element_by_id(edit ? "editcollectionscene" : "createcollectionscene");
|
this._password = password;
|
||||||
/** @type {?HTMLElement} */ let title_form = edit ? get_element(html_scene, "[data-name=title]") : null;
|
this._collection = collection;
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
|
this._edit = collection.type !== CollectionType.PRINCIPAL;
|
||||||
/** @type {?HTMLInputElement} */ let href_form = edit ? null : /** @type {HTMLInputElement} */(get_element(html_scene, "[data-name=href]"));
|
|
||||||
/** @type {HTMLInputElement} */ let displayname_form = /** @type {HTMLInputElement} */(get_element(html_scene, "[data-name=displayname]"));
|
|
||||||
/** @type {HTMLInputElement} */ let description_form = /** @type {HTMLInputElement} */(get_element(html_scene, "[data-name=description]"));
|
|
||||||
/** @type {HTMLInputElement} */ let source_form = /** @type {HTMLInputElement} */(get_element(html_scene, "[data-name=source]"));
|
|
||||||
/** @type {HTMLElement} */ let source_label = get_element(html_scene, "label[for=source]");
|
|
||||||
/** @type {HTMLSelectElement} */ let type_form = /** @type {HTMLSelectElement} */(get_element(html_scene, "[data-name=type]"));
|
|
||||||
/** @type {HTMLInputElement} */ let color_form = /** @type {HTMLInputElement} */(get_element(html_scene, "[data-name=color]"));
|
|
||||||
/** @type {HTMLElement} */ let submit_btn = get_element(html_scene, "[data-name=submit]");
|
|
||||||
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
|
|
||||||
|
|
||||||
|
this._html_scene = get_element_by_id(this._edit ? "editcollectionscene" : "createcollectionscene");
|
||||||
|
this._title_form = this._edit ? get_element(this._html_scene, "[data-name=title]") : null;
|
||||||
|
this._error_form = get_element(this._html_scene, "[data-name=error]");
|
||||||
|
this._href_form = this._edit ? null : /** @type {HTMLInputElement} */(get_element(this._html_scene, "[data-name=href]"));
|
||||||
|
this._displayname_form = /** @type {HTMLInputElement} */(get_element(this._html_scene, "[data-name=displayname]"));
|
||||||
|
this._description_form = /** @type {HTMLInputElement} */(get_element(this._html_scene, "[data-name=description]"));
|
||||||
|
this._source_form = /** @type {HTMLInputElement} */(get_element(this._html_scene, "[data-name=source]"));
|
||||||
|
this._source_label = get_element(this._html_scene, "label[for$=source]");
|
||||||
|
this._type_form = /** @type {HTMLSelectElement} */(get_element(this._html_scene, "[data-name=type]"));
|
||||||
|
this._color_form = /** @type {HTMLInputElement} */(get_element(this._html_scene, "[data-name=color]"));
|
||||||
|
this._submit_btn = get_element(this._html_scene, "[data-name=submit]");
|
||||||
|
this._cancel_btn = get_element(this._html_scene, "[data-name=cancel]");
|
||||||
|
|
||||||
/** @type {?XMLHttpRequest} */ let create_edit_req = null;
|
/** @type {?XMLHttpRequest} */ this._create_edit_req = null;
|
||||||
/** @type {?HTMLSelectElement} */ let saved_type_form = null;
|
/** @type {?HTMLSelectElement} */ this._saved_type_form = null;
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
let validator = new FormValidator(errorHandler);
|
this._validator = new FormValidator(this._errorHandler);
|
||||||
|
|
||||||
if (!edit && href_form) {
|
if (!this._edit && this._href_form) {
|
||||||
validator.addValidator(href_form, validate_href(href_form, "HREF"));
|
this._validator.addValidator(this._href_form, validate_href(this._href_form, "HREF"));
|
||||||
}
|
}
|
||||||
validator.addValidator(color_form, validate_color(color_form, "Color"));
|
this._validator.addValidator(this._color_form, validate_color(this._color_form, "Color"));
|
||||||
|
|
||||||
let href = edit ? collection.href : collection.href + random_uuid() + "/";
|
this._href = this._edit ? collection.href : collection.href + random_uuid() + "/";
|
||||||
let displayname = edit ? collection.displayname : "";
|
this._displayname = this._edit ? collection.displayname : "";
|
||||||
let description = edit ? collection.description : "";
|
this._description = this._edit ? collection.description : "";
|
||||||
let source = edit ? collection.source : "";
|
this._source = this._edit ? collection.source : "";
|
||||||
let type = edit ? collection.type : CollectionType.CALENDAR_JOURNAL_TASKS;
|
this._type = this._edit ? collection.type : CollectionType.CALENDAR_JOURNAL_TASKS;
|
||||||
let color = edit && collection.color ? collection.color : "#" + random_hex(6);
|
this._color = this._edit && collection.color ? collection.color : "#" + random_hex(6);
|
||||||
|
|
||||||
if (!edit && href_form) {
|
if (!this._edit && this._href_form) {
|
||||||
href_form.addEventListener("input", onCleanHREFinput);
|
this._href_form.addEventListener("input", onCleanHREFinput);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_invalid_types() {
|
_remove_invalid_types() {
|
||||||
if (!edit) {
|
if (!this._edit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/** @type {HTMLOptionsCollection} */ let options = type_form.options;
|
/** @type {HTMLOptionsCollection} */ let options = this._type_form.options;
|
||||||
// remove all options that are not supersets
|
let valid_type_options = CollectionType.valid_options_for_type(this._type);
|
||||||
let valid_type_options = CollectionType.valid_options_for_type(type);
|
|
||||||
for (let i = options.length - 1; i >= 0; i--) {
|
for (let i = options.length - 1; i >= 0; i--) {
|
||||||
if (valid_type_options.indexOf(options[i].value) < 0) {
|
if (valid_type_options.indexOf(options[i].value) < 0) {
|
||||||
options.remove(i);
|
options.remove(i);
|
||||||
@@ -92,40 +95,40 @@ export class CreateEditCollectionScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_form() {
|
_read_form() {
|
||||||
if (!edit && href_form) {
|
if (!this._edit && this._href_form) {
|
||||||
cleanHREFinput(href_form);
|
cleanHREFinput(this._href_form);
|
||||||
let newhreftxtvalue = href_form.value.trim().toLowerCase();
|
let newhreftxtvalue = this._href_form.value.trim().toLowerCase();
|
||||||
href = collection.href + newhreftxtvalue + "/";
|
this._href = this._collection.href + newhreftxtvalue + "/";
|
||||||
}
|
}
|
||||||
displayname = displayname_form.value;
|
this._displayname = this._displayname_form.value;
|
||||||
description = description_form.value;
|
this._description = this._description_form.value;
|
||||||
source = source_form.value;
|
this._source = this._source_form.value;
|
||||||
type = type_form.value;
|
this._type = this._type_form.value;
|
||||||
color = color_form.value;
|
this._color = this._color_form.value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fill_form() {
|
_fill_form() {
|
||||||
if (!edit && href_form) {
|
if (!this._edit && this._href_form) {
|
||||||
href_form.value = random_uuid();
|
this._href_form.value = random_uuid();
|
||||||
}
|
}
|
||||||
displayname_form.value = displayname;
|
this._displayname_form.value = this._displayname;
|
||||||
description_form.value = description;
|
this._description_form.value = this._description;
|
||||||
source_form.value = source;
|
this._source_form.value = this._source;
|
||||||
type_form.value = type;
|
this._type_form.value = this._type;
|
||||||
color_form.value = color;
|
this._color_form.value = this._color;
|
||||||
onTypeChange(null);
|
this._onTypeChange(null);
|
||||||
type_form.addEventListener("change", onTypeChange);
|
this._type_form.addEventListener("change", (e) => this._onTypeChange(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onsubmit() {
|
_onsubmit() {
|
||||||
try {
|
try {
|
||||||
if (!validator.validate()) {
|
if (!this._validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
read_form();
|
this._read_form();
|
||||||
let sane_color = color.trim();
|
let sane_color = this._color.trim();
|
||||||
if (sane_color) {
|
if (sane_color) {
|
||||||
/** @type {?RegExpExecArray} */ let match = COLOR_RE.exec(sane_color);
|
/** @type {?RegExpExecArray} */ let match = COLOR_RE.exec(sane_color);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -134,24 +137,24 @@ export class CreateEditCollectionScene {
|
|||||||
}
|
}
|
||||||
let loading_scene = new LoadingScene();
|
let loading_scene = new LoadingScene();
|
||||||
push_scene(loading_scene);
|
push_scene(loading_scene);
|
||||||
let collection = new Collection(href, type, displayname, description, sane_color, 0, 0, source);
|
let collection = new Collection(this._href, this._type, this._displayname, this._description, sane_color, 0, 0, this._source);
|
||||||
let callback = function (/** @type {?string} */ error1) {
|
let callback = (/** @type {?string} */ error1) => {
|
||||||
if (!is_current_scene(loading_scene)) {
|
if (!is_current_scene(loading_scene)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
create_edit_req = null;
|
this._create_edit_req = null;
|
||||||
if (error1) {
|
if (error1) {
|
||||||
errorHandler.setError(error1);
|
this._errorHandler.setError(error1);
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} else {
|
} else {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
pop_to_parent();
|
pop_to_parent();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (edit) {
|
if (this._edit) {
|
||||||
create_edit_req = edit_collection(user, password, collection, callback);
|
this._create_edit_req = edit_collection(this._user, this._password, collection, callback);
|
||||||
} else {
|
} else {
|
||||||
create_edit_req = create_collection(user, password, collection, callback);
|
this._create_edit_req = create_collection(this._user, this._password, collection, callback);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -159,7 +162,7 @@ export class CreateEditCollectionScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function oncancel() {
|
_oncancel() {
|
||||||
try {
|
try {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -171,51 +174,54 @@ export class CreateEditCollectionScene {
|
|||||||
/**
|
/**
|
||||||
* @param {?Event} _e
|
* @param {?Event} _e
|
||||||
*/
|
*/
|
||||||
function onTypeChange(_e) {
|
_onTypeChange(_e) {
|
||||||
if (type_form.value == CollectionType.WEBCAL) {
|
if (this._type_form.value == CollectionType.WEBCAL) {
|
||||||
source_label.classList.remove("hidden");
|
this._source_label.classList.remove("hidden");
|
||||||
source_form.classList.remove("hidden");
|
this._source_form.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
source_label.classList.add("hidden");
|
this._source_label.classList.add("hidden");
|
||||||
source_form.classList.add("hidden");
|
this._source_form.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
this.release();
|
this.release();
|
||||||
// Clone type_form because it's impossible to hide options without removing them
|
// Clone type_form because it's impossible to hide options without removing them
|
||||||
saved_type_form = type_form;
|
this._saved_type_form = this._type_form;
|
||||||
type_form = /** @type {HTMLSelectElement} */ (type_form.cloneNode(true));
|
this._type_form = /** @type {HTMLSelectElement} */ (this._type_form.cloneNode(true));
|
||||||
if (saved_type_form.parentNode) {
|
if (this._saved_type_form.parentNode) {
|
||||||
saved_type_form.parentNode.replaceChild(type_form, saved_type_form);
|
this._saved_type_form.parentNode.replaceChild(this._type_form, this._saved_type_form);
|
||||||
}
|
}
|
||||||
remove_invalid_types();
|
this._remove_invalid_types();
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
if (edit && title_form) {
|
if (this._edit && this._title_form) {
|
||||||
title_form.textContent = collection.displayname || collection.href;
|
this._title_form.textContent = this._collection.displayname || this._collection.href;
|
||||||
}
|
}
|
||||||
fill_form();
|
this._fill_form();
|
||||||
submit_btn.onclick = onsubmit;
|
this._submit_btn.onclick = () => this._onsubmit();
|
||||||
cancel_btn.onclick = oncancel;
|
this._cancel_btn.onclick = () => this._oncancel();
|
||||||
validator.validate();
|
this._validator.validate();
|
||||||
};
|
}
|
||||||
this.hide = function () {
|
|
||||||
read_form();
|
hide() {
|
||||||
html_scene.classList.add("hidden");
|
this._read_form();
|
||||||
|
this._html_scene.classList.add("hidden");
|
||||||
// restore type_form
|
// restore type_form
|
||||||
if (type_form.parentNode && saved_type_form) {
|
if (this._type_form.parentNode && this._saved_type_form) {
|
||||||
type_form.parentNode.replaceChild(saved_type_form, type_form);
|
this._type_form.parentNode.replaceChild(this._saved_type_form, this._type_form);
|
||||||
type_form = saved_type_form;
|
this._type_form = this._saved_type_form;
|
||||||
}
|
}
|
||||||
saved_type_form = null;
|
this._saved_type_form = null;
|
||||||
submit_btn.onclick = null;
|
this._submit_btn.onclick = null;
|
||||||
cancel_btn.onclick = null;
|
this._cancel_btn.onclick = null;
|
||||||
};
|
}
|
||||||
this.release = function () {
|
|
||||||
if (create_edit_req !== null) {
|
is_transient() { return false; }
|
||||||
create_edit_req.abort();
|
|
||||||
create_edit_req = null;
|
release() {
|
||||||
|
if (this._create_edit_req !== null) {
|
||||||
|
this._create_edit_req.abort();
|
||||||
|
this._create_edit_req = null;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,82 +39,87 @@ export class CreateEditShareScene {
|
|||||||
* @param {Share} [share] If provided, the scene will be in edit mode.
|
* @param {Share} [share] If provided, the scene will be in edit mode.
|
||||||
*/
|
*/
|
||||||
constructor(user, password, collection, shareType, share) {
|
constructor(user, password, collection, shareType, share) {
|
||||||
let self = this;
|
this._user = user;
|
||||||
let edit = !!share;
|
this._password = password;
|
||||||
let pathMapped = collection.href;
|
this._collection = collection;
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("newshare");
|
this._shareType = shareType;
|
||||||
/** @type {HTMLFormElement} */ let form = /** @type {HTMLFormElement} */ (get_element(html_scene, "form"));
|
this._share = share;
|
||||||
/** @type {HTMLElement} */ let sharemapfields = get_element(html_scene, "[data-name=sharemapfields]");
|
this._edit = !!share;
|
||||||
/** @type {HTMLInputElement} */ let shareuser_input = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=shareuser]"));
|
this._pathMapped = collection.href;
|
||||||
/** @type {HTMLInputElement} */ let sharehref_input = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=sharehref]"));
|
|
||||||
/** @type {HTMLInputElement} */ let enabled_checkbox = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=enabled]"));
|
|
||||||
/** @type {HTMLInputElement} */ let hidden_checkbox = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=hidden]"));
|
|
||||||
let permissions_ro_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_ro"));
|
|
||||||
let permissions_rw_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_rw"));
|
|
||||||
/** @type {HTMLDetailsElement} */ let conversions_details = /** @type {HTMLDetailsElement} */ (get_element(html_scene, "[data-name=conversions]"));
|
|
||||||
/** @type {HTMLElement} */ let conversions_container = get_element(html_scene, "[data-name=conversions_container]");
|
|
||||||
|
|
||||||
/** @type {HTMLDetailsElement} */ let properties_fieldset = /** @type {HTMLDetailsElement} */ (get_element(html_scene, "[data-name=properties_override]"));
|
this._html_scene = get_element_by_id("newshare");
|
||||||
/** @type {HTMLInputElement} */ let displayname_override_enabled = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=displayname_override_enabled]"));
|
this._form = /** @type {HTMLFormElement} */ (get_element(this._html_scene, "form"));
|
||||||
/** @type {HTMLInputElement} */ let displayname_override_input = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=displayname_override]"));
|
this._sharemapfields = get_element(this._html_scene, "[data-name=sharemapfields]");
|
||||||
/** @type {HTMLInputElement} */ let description_override_enabled = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=description_override_enabled]"));
|
this._shareuser_input = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=shareuser]"));
|
||||||
/** @type {HTMLInputElement} */ let description_override_input = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=description_override]"));
|
this._sharehref_input = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=sharehref]"));
|
||||||
/** @type {HTMLInputElement} */ let color_override_enabled = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=color_override_enabled]"));
|
this._enabled_checkbox = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=enabled]"));
|
||||||
/** @type {HTMLInputElement} */ let color_override_input = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=color_override]"));
|
this._hidden_checkbox = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=hidden]"));
|
||||||
|
this._permissions_ro_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_ro"));
|
||||||
|
this._permissions_rw_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_rw"));
|
||||||
|
this._conversions_details = /** @type {HTMLDetailsElement} */ (get_element(this._html_scene, "[data-name=conversions]"));
|
||||||
|
this._conversions_container = get_element(this._html_scene, "[data-name=conversions_container]");
|
||||||
|
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
|
this._properties_fieldset = /** @type {HTMLDetailsElement} */ (get_element(this._html_scene, "[data-name=properties_override]"));
|
||||||
/** @type {HTMLElement} */ let submit_btn = get_element(html_scene, "[data-name=submit]");
|
this._displayname_override_enabled = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=displayname_override_enabled]"));
|
||||||
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
|
this._displayname_override_input = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=displayname_override]"));
|
||||||
|
this._description_override_enabled = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=description_override_enabled]"));
|
||||||
|
this._description_override_input = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=description_override]"));
|
||||||
|
this._color_override_enabled = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=color_override_enabled]"));
|
||||||
|
this._color_override_input = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=color_override]"));
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._error_form = get_element(this._html_scene, "[data-name=error]");
|
||||||
let map_validator = new FormValidator(errorHandler);
|
this._submit_btn = get_element(this._html_scene, "[data-name=submit]");
|
||||||
|
this._cancel_btn = get_element(this._html_scene, "[data-name=cancel]");
|
||||||
|
|
||||||
map_validator.addValidator(shareuser_input, function () {
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
let conversion = get_selected_conversion();
|
this._map_validator = new FormValidator(this._errorHandler);
|
||||||
if (conversion === "bday") {
|
|
||||||
return validate_non_empty(shareuser_input, "Share User")();
|
this._map_validator.addValidator(this._shareuser_input, () => {
|
||||||
|
let conversion = this._get_selected_conversion();
|
||||||
|
if (conversion != "none") {
|
||||||
|
return validate_non_empty(this._shareuser_input, "Share User")();
|
||||||
} else {
|
} else {
|
||||||
return validate_not_empty_or_equals(shareuser_input, user, "Share User")();
|
return validate_not_empty_or_equals(this._shareuser_input, user, "Share User")();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
map_validator.addValidator(sharehref_input, validate_href(sharehref_input, "Share Href"));
|
this._map_validator.addValidator(this._sharehref_input, validate_href(this._sharehref_input, "Share Href"));
|
||||||
|
|
||||||
sharehref_input.addEventListener("input", onCleanHREFinput);
|
this._sharehref_input.addEventListener("input", onCleanHREFinput);
|
||||||
|
|
||||||
displayname_override_enabled.onchange = function () {
|
this._displayname_override_enabled.onchange = () => {
|
||||||
displayname_override_input.disabled = !displayname_override_enabled.checked;
|
this._displayname_override_input.disabled = !this._displayname_override_enabled.checked;
|
||||||
};
|
};
|
||||||
description_override_enabled.onchange = function () {
|
this._description_override_enabled.onchange = () => {
|
||||||
description_override_input.disabled = !description_override_enabled.checked;
|
this._description_override_input.disabled = !this._description_override_enabled.checked;
|
||||||
};
|
};
|
||||||
color_override_enabled.onchange = function () {
|
this._color_override_enabled.onchange = () => {
|
||||||
color_override_input.disabled = !color_override_enabled.checked;
|
this._color_override_input.disabled = !this._color_override_enabled.checked;
|
||||||
};
|
};
|
||||||
function get_selected_conversion() {
|
}
|
||||||
|
|
||||||
|
_get_selected_conversion() {
|
||||||
/** @type {HTMLInputElement | null} */
|
/** @type {HTMLInputElement | null} */
|
||||||
let checked = conversions_container.querySelector("input[name=conversion]:checked");
|
let checked = this._conversions_container.querySelector("input[name=conversion]:checked");
|
||||||
return checked ? checked.value : "none";
|
return checked ? checked.value : "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_conversion_change() {
|
_on_conversion_change() {
|
||||||
let conversion = get_selected_conversion();
|
let conversion = this._get_selected_conversion();
|
||||||
if (conversion === "bday") {
|
if (conversion != "none") {
|
||||||
permissions_ro_radio.checked = true;
|
this._permissions_ro_radio.disabled = true;
|
||||||
permissions_rw_radio.checked = false;
|
this._permissions_rw_radio.disabled = true;
|
||||||
permissions_ro_radio.disabled = true;
|
this._enabled_checkbox.checked = true;
|
||||||
permissions_rw_radio.disabled = true;
|
this._hidden_checkbox.checked = false;
|
||||||
enabled_checkbox.checked = true;
|
|
||||||
hidden_checkbox.checked = false;
|
|
||||||
} else {
|
} else {
|
||||||
permissions_ro_radio.disabled = false;
|
this._permissions_ro_radio.disabled = false;
|
||||||
permissions_rw_radio.disabled = false;
|
this._permissions_rw_radio.disabled = false;
|
||||||
}
|
}
|
||||||
if (shareType === "map") {
|
if (this._shareType === "map") {
|
||||||
map_validator.validate();
|
this._map_validator.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function oncancel() {
|
_oncancel() {
|
||||||
try {
|
try {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -123,44 +128,44 @@ export class CreateEditShareScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onsubmit() {
|
_onsubmit() {
|
||||||
try {
|
try {
|
||||||
if (shareType === "map") {
|
if (this._shareType === "map") {
|
||||||
if (!map_validator.validate()) {
|
if (!this._map_validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let conversion = get_selected_conversion();
|
let conversion = this._get_selected_conversion();
|
||||||
let is_birthday = conversion === "bday";
|
let is_conversion = conversion != "none";
|
||||||
let enabled_by_owner = is_birthday ? true : enabled_checkbox.checked;
|
let enabled_by_owner = is_conversion ? true : this._enabled_checkbox.checked;
|
||||||
let hidden_by_owner = is_birthday ? false : hidden_checkbox.checked;
|
let hidden_by_owner = is_conversion ? false : this._hidden_checkbox.checked;
|
||||||
let permissions = is_birthday ? "r" : (permissions_rw_radio.checked ? "rw" : "r");
|
let permissions = is_conversion ? "r" : (this._permissions_rw_radio.checked ? "rw" : "r");
|
||||||
/** @type {string} */ let conversion_value = conversion;
|
/** @type {string} */ let conversion_value = conversion;
|
||||||
|
|
||||||
/** @type {Object<string, string>} */ let properties = {};
|
/** @type {Object<string, string>} */ let properties = {};
|
||||||
if (displayname_override_enabled.checked) {
|
if (this._displayname_override_enabled.checked) {
|
||||||
let key = get_property_key(collection.type, "DISPLAYNAME");
|
let key = get_property_key(this._collection.type, "DISPLAYNAME");
|
||||||
if (key) properties[key] = displayname_override_input.value;
|
if (key) properties[key] = this._displayname_override_input.value;
|
||||||
}
|
}
|
||||||
if (description_override_enabled.checked) {
|
if (this._description_override_enabled.checked) {
|
||||||
let key = get_property_key(collection.type, "DESCRIPTION");
|
let key = get_property_key(this._collection.type, "DESCRIPTION");
|
||||||
if (key) properties[key] = description_override_input.value;
|
if (key) properties[key] = this._description_override_input.value;
|
||||||
}
|
}
|
||||||
if (color_override_enabled.checked) {
|
if (this._color_override_enabled.checked) {
|
||||||
let key = get_property_key(collection.type, "COLOR");
|
let key = get_property_key(this._collection.type, "COLOR");
|
||||||
if (key) properties[key] = color_override_input.value + (color_override_input.value ? "ff" : "");
|
if (key) properties[key] = this._color_override_input.value + (this._color_override_input.value ? "ff" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
let callback = function (/** @type {?string} */ error) {
|
let callback = (/** @type {?string} */ error) => {
|
||||||
if (!is_current_scene(self)) {
|
if (!is_current_scene(this)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
errorHandler.setError(error);
|
this._errorHandler.setError(error);
|
||||||
} else {
|
} else {
|
||||||
// On any share-to-self (currently only bday conversion), invalidate the
|
// On any share-to-self (currently only bday conversion), invalidate the
|
||||||
// collections cache so the virtual calendar appears immediately.
|
// collections cache so the virtual calendar appears immediately.
|
||||||
if (shareuser_input.value === user) {
|
if (this._shareuser_input.value === this._user) {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
}
|
}
|
||||||
pop_scene();
|
pop_scene();
|
||||||
@@ -168,30 +173,30 @@ export class CreateEditShareScene {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let new_share = new Share({
|
let new_share = new Share({
|
||||||
ShareType: shareType,
|
ShareType: this._shareType,
|
||||||
PathMapped: pathMapped,
|
PathMapped: this._pathMapped,
|
||||||
Permissions: permissions,
|
Permissions: permissions,
|
||||||
EnabledByOwner: enabled_by_owner,
|
EnabledByOwner: enabled_by_owner,
|
||||||
EnabledByUser: (edit && share) ? share.EnabledByUser : null,
|
EnabledByUser: (this._edit && this._share) ? this._share.EnabledByUser : null,
|
||||||
HiddenByOwner: hidden_by_owner,
|
HiddenByOwner: hidden_by_owner,
|
||||||
HiddenByUser: (edit && share) ? share.HiddenByUser : null,
|
HiddenByUser: (this._edit && this._share) ? this._share.HiddenByUser : null,
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
User: (edit && share) ? share.User : shareuser_input.value,
|
User: (this._edit && this._share) ? this._share.User : this._shareuser_input.value,
|
||||||
PathOrToken: (edit && share) ? share.PathOrToken : (shareType === "map" ? "/" + shareuser_input.value + "/" + sharehref_input.value + "/" : ""),
|
PathOrToken: (this._edit && this._share) ? this._share.PathOrToken : (this._shareType === "map" ? "/" + this._shareuser_input.value + "/" + this._sharehref_input.value + "/" : ""),
|
||||||
Conversion: conversion_value,
|
Conversion: conversion_value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (edit) {
|
if (this._edit) {
|
||||||
if (shareType === "map") {
|
if (this._shareType === "map") {
|
||||||
update_share_by_map(user, password, new_share, callback);
|
update_share_by_map(this._user, this._password, new_share, callback);
|
||||||
} else {
|
} else {
|
||||||
update_share_by_token(user, password, new_share, callback);
|
update_share_by_token(this._user, this._password, new_share, callback);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (shareType === "map") {
|
if (this._shareType === "map") {
|
||||||
add_share_by_map(user, password, new_share, callback);
|
add_share_by_map(this._user, this._password, new_share, callback);
|
||||||
} else {
|
} else {
|
||||||
add_share_by_token(user, password, new_share, callback);
|
add_share_by_token(this._user, this._password, new_share, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -200,32 +205,32 @@ export class CreateEditShareScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
this.release();
|
this.release();
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
html_scene.querySelectorAll("details").forEach(function (details) {
|
this._html_scene.querySelectorAll("details").forEach(function (details) {
|
||||||
if (details.dataset.name !== "properties_override") {
|
if (details.dataset.name !== "properties_override") {
|
||||||
details.open = true;
|
details.open = true;
|
||||||
} else {
|
} else {
|
||||||
details.open = false;
|
details.open = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cancel_btn.onclick = oncancel;
|
this._cancel_btn.onclick = () => this._oncancel();
|
||||||
form.onsubmit = onsubmit;
|
this._form.onsubmit = () => this._onsubmit();
|
||||||
|
|
||||||
/** @type {HTMLHeadingElement} */ let title = /** @type {HTMLHeadingElement} */ (get_element(html_scene, "h1"));
|
/** @type {HTMLHeadingElement} */ let title = /** @type {HTMLHeadingElement} */ (get_element(this._html_scene, "h1"));
|
||||||
title.textContent = edit ? "Edit Share" : "New Share";
|
title.textContent = this._edit ? "Edit Share" : "New Share";
|
||||||
submit_btn.textContent = edit ? "Save" : "Create";
|
this._submit_btn.textContent = this._edit ? "Save" : "Create";
|
||||||
|
|
||||||
shareuser_input.value = (edit && share) ? share.User : "";
|
this._shareuser_input.value = (this._edit && this._share) ? this._share.User : "";
|
||||||
shareuser_input.disabled = edit;
|
this._shareuser_input.disabled = this._edit;
|
||||||
enabled_checkbox.checked = (edit && share && share.EnabledByOwner !== null) ? share.EnabledByOwner : true;
|
this._enabled_checkbox.checked = (this._edit && this._share && this._share.EnabledByOwner !== null) ? this._share.EnabledByOwner : true;
|
||||||
hidden_checkbox.checked = (edit && share && share.HiddenByOwner !== null) ? share.HiddenByOwner : false;
|
this._hidden_checkbox.checked = (this._edit && this._share && this._share.HiddenByOwner !== null) ? this._share.HiddenByOwner : false;
|
||||||
|
|
||||||
let initial_conversion = (edit && share && share.Conversion) ? share.Conversion : "none";
|
let initial_conversion = (this._edit && this._share && this._share.Conversion) ? this._share.Conversion : "none";
|
||||||
|
|
||||||
collectionsCache.getServerFeatures(user, password, errorHandler.setError, (features) => {
|
collectionsCache.getServerFeatures(this._user, this._password, this._errorHandler.setError, (features) => {
|
||||||
conversions_container.innerHTML = "";
|
this._conversions_container.innerHTML = "";
|
||||||
let supported = (features.sharing && features.sharing.SupportedConversions) || [];
|
let supported = (features.sharing && features.sharing.SupportedConversions) || [];
|
||||||
if (supported.length > 0) {
|
if (supported.length > 0) {
|
||||||
let options = ["none", ...supported.filter(opt => opt !== "none")];
|
let options = ["none", ...supported.filter(opt => opt !== "none")];
|
||||||
@@ -237,7 +242,7 @@ export class CreateEditShareScene {
|
|||||||
radio.value = opt;
|
radio.value = opt;
|
||||||
radio.id = id;
|
radio.id = id;
|
||||||
radio.checked = (opt === initial_conversion);
|
radio.checked = (opt === initial_conversion);
|
||||||
radio.onchange = on_conversion_change;
|
radio.onchange = () => this._on_conversion_change();
|
||||||
|
|
||||||
let label = document.createElement("label");
|
let label = document.createElement("label");
|
||||||
label.htmlFor = id;
|
label.htmlFor = id;
|
||||||
@@ -247,44 +252,44 @@ export class CreateEditShareScene {
|
|||||||
label.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
|
label.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
conversions_container.appendChild(radio);
|
this._conversions_container.appendChild(radio);
|
||||||
conversions_container.appendChild(label);
|
this._conversions_container.appendChild(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
let is_addressbook = collection.type === CollectionType.ADDRESSBOOK;
|
let is_addressbook = this._collection.type === CollectionType.ADDRESSBOOK;
|
||||||
if (is_addressbook) {
|
if (is_addressbook) {
|
||||||
conversions_details.classList.remove("hidden");
|
this._conversions_details.classList.remove("hidden");
|
||||||
conversions_details.open = true;
|
this._conversions_details.open = true;
|
||||||
} else {
|
} else {
|
||||||
conversions_details.classList.add("hidden");
|
this._conversions_details.classList.add("hidden");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conversions_details.classList.add("hidden");
|
this._conversions_details.classList.add("hidden");
|
||||||
}
|
}
|
||||||
on_conversion_change();
|
this._on_conversion_change();
|
||||||
});
|
});
|
||||||
|
|
||||||
let displayname = collection.displayname || "";
|
let displayname = this._collection.displayname || "";
|
||||||
let description = collection.description || "";
|
let description = this._collection.description || "";
|
||||||
let color = collection.color || "#ffffff";
|
let color = this._collection.color || "#ffffff";
|
||||||
let displayname_override_enabled_value = false;
|
let displayname_override_enabled_value = false;
|
||||||
let description_override_enabled_value = false;
|
let description_override_enabled_value = false;
|
||||||
let color_override_enabled_value = false;
|
let color_override_enabled_value = false;
|
||||||
|
|
||||||
if (edit && share && share.Properties) {
|
if (this._edit && this._share && this._share.Properties) {
|
||||||
let displayname_key = get_property_key(collection.type, "DISPLAYNAME");
|
let displayname_key = get_property_key(this._collection.type, "DISPLAYNAME");
|
||||||
if (displayname_key && share.Properties[displayname_key] !== undefined) {
|
if (displayname_key && this._share.Properties[displayname_key] !== undefined) {
|
||||||
displayname = share.Properties[displayname_key];
|
displayname = this._share.Properties[displayname_key];
|
||||||
displayname_override_enabled_value = true;
|
displayname_override_enabled_value = true;
|
||||||
}
|
}
|
||||||
let description_key = get_property_key(collection.type, "DESCRIPTION");
|
let description_key = get_property_key(this._collection.type, "DESCRIPTION");
|
||||||
if (description_key && share.Properties[description_key] !== undefined) {
|
if (description_key && this._share.Properties[description_key] !== undefined) {
|
||||||
description = share.Properties[description_key];
|
description = this._share.Properties[description_key];
|
||||||
description_override_enabled_value = true;
|
description_override_enabled_value = true;
|
||||||
}
|
}
|
||||||
let color_key = get_property_key(collection.type, "COLOR");
|
let color_key = get_property_key(this._collection.type, "COLOR");
|
||||||
if (color_key && share.Properties[color_key] !== undefined) {
|
if (color_key && this._share.Properties[color_key] !== undefined) {
|
||||||
color = share.Properties[color_key];
|
color = this._share.Properties[color_key];
|
||||||
if (color.length === 9 && color.endsWith("ff")) {
|
if (color.length === 9 && color.endsWith("ff")) {
|
||||||
color = color.substring(0, 7);
|
color = color.substring(0, 7);
|
||||||
}
|
}
|
||||||
@@ -292,78 +297,79 @@ export class CreateEditShareScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
displayname_override_enabled.checked = displayname_override_enabled_value;
|
this._displayname_override_enabled.checked = displayname_override_enabled_value;
|
||||||
displayname_override_input.value = displayname;
|
this._displayname_override_input.value = displayname;
|
||||||
displayname_override_input.disabled = !displayname_override_enabled_value;
|
this._displayname_override_input.disabled = !displayname_override_enabled_value;
|
||||||
|
|
||||||
description_override_enabled.checked = description_override_enabled_value;
|
this._description_override_enabled.checked = description_override_enabled_value;
|
||||||
description_override_input.value = description;
|
this._description_override_input.value = description;
|
||||||
description_override_input.disabled = !description_override_enabled_value;
|
this._description_override_input.disabled = !description_override_enabled_value;
|
||||||
|
|
||||||
color_override_enabled.checked = color_override_enabled_value;
|
this._color_override_enabled.checked = color_override_enabled_value;
|
||||||
color_override_input.value = color;
|
this._color_override_input.value = color;
|
||||||
color_override_input.disabled = !color_override_enabled_value;
|
this._color_override_input.disabled = !color_override_enabled_value;
|
||||||
|
|
||||||
properties_fieldset.classList.remove("hidden");
|
this._properties_fieldset.classList.remove("hidden");
|
||||||
if (displayname_override_enabled_value || description_override_enabled_value || color_override_enabled_value) {
|
if (displayname_override_enabled_value || description_override_enabled_value || color_override_enabled_value) {
|
||||||
properties_fieldset.open = true;
|
this._properties_fieldset.open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayname_key = get_property_key(collection.type, "DISPLAYNAME");
|
let displayname_key = get_property_key(this._collection.type, "DISPLAYNAME");
|
||||||
if (!displayname_key) {
|
if (!displayname_key) {
|
||||||
if (displayname_override_enabled.parentElement) {
|
if (this._displayname_override_enabled.parentElement) {
|
||||||
displayname_override_enabled.parentElement.classList.add("hidden");
|
this._displayname_override_enabled.parentElement.classList.add("hidden");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (displayname_override_enabled.parentElement) {
|
if (this._displayname_override_enabled.parentElement) {
|
||||||
displayname_override_enabled.parentElement.classList.remove("hidden");
|
this._displayname_override_enabled.parentElement.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let description_key = get_property_key(collection.type, "DESCRIPTION");
|
let description_key = get_property_key(this._collection.type, "DESCRIPTION");
|
||||||
if (!description_key) {
|
if (!description_key) {
|
||||||
if (description_override_enabled.parentElement) {
|
if (this._description_override_enabled.parentElement) {
|
||||||
description_override_enabled.parentElement.classList.add("hidden");
|
this._description_override_enabled.parentElement.classList.add("hidden");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (description_override_enabled.parentElement) {
|
if (this._description_override_enabled.parentElement) {
|
||||||
description_override_enabled.parentElement.classList.remove("hidden");
|
this._description_override_enabled.parentElement.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let color_key = get_property_key(collection.type, "COLOR");
|
let color_key = get_property_key(this._collection.type, "COLOR");
|
||||||
if (!color_key) {
|
if (!color_key) {
|
||||||
if (color_override_enabled.parentElement) {
|
if (this._color_override_enabled.parentElement) {
|
||||||
color_override_enabled.parentElement.classList.add("hidden");
|
this._color_override_enabled.parentElement.classList.add("hidden");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (color_override_enabled.parentElement) {
|
if (this._color_override_enabled.parentElement) {
|
||||||
color_override_enabled.parentElement.classList.remove("hidden");
|
this._color_override_enabled.parentElement.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shareType === "map") {
|
if (this._shareType === "map") {
|
||||||
if (edit && share) {
|
if (this._edit && this._share) {
|
||||||
sharehref_input.value = share.PathOrToken.split("/").filter(Boolean).pop() || "";
|
this._sharehref_input.value = this._share.PathOrToken.split("/").filter(Boolean).pop() || "";
|
||||||
} else {
|
} else {
|
||||||
sharehref_input.value = random_uuid();
|
this._sharehref_input.value = random_uuid();
|
||||||
}
|
}
|
||||||
sharehref_input.disabled = edit;
|
this._sharehref_input.disabled = this._edit;
|
||||||
sharemapfields.classList.remove("hidden");
|
this._sharemapfields.classList.remove("hidden");
|
||||||
map_validator.validate();
|
this._map_validator.validate();
|
||||||
} else {
|
} else {
|
||||||
sharehref_input.value = "";
|
this._sharehref_input.value = "";
|
||||||
sharemapfields.classList.add("hidden");
|
this._sharemapfields.classList.add("hidden");
|
||||||
errorHandler.clearError();
|
this._errorHandler.clearError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
this.hide = function () {
|
hide() {
|
||||||
html_scene.classList.add("hidden");
|
this._html_scene.classList.add("hidden");
|
||||||
cancel_btn.onclick = null;
|
this._cancel_btn.onclick = null;
|
||||||
form.onsubmit = null;
|
this._form.onsubmit = null;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.release = function () {
|
release() {
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_transient() { return false; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,57 +42,65 @@ export class DeleteConfirmationScene {
|
|||||||
* @param {function} [on_success]
|
* @param {function} [on_success]
|
||||||
*/
|
*/
|
||||||
constructor(user, password, header_title, item, item_title, delete_action, needsconfirmation, on_success) {
|
constructor(user, password, header_title, item, item_title, delete_action, needsconfirmation, on_success) {
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("deleteconfirmationscene");
|
this._user = user;
|
||||||
/** @type {HTMLElement} */ let header_html = get_element(html_scene, "[data-name=headertitle]");
|
this._password = password;
|
||||||
|
this._item = item;
|
||||||
|
this._item_title = item_title;
|
||||||
|
this._delete_action = delete_action;
|
||||||
|
this._needsconfirmation = needsconfirmation;
|
||||||
|
this._on_success = on_success;
|
||||||
|
|
||||||
|
this._html_scene = get_element_by_id("deleteconfirmationscene");
|
||||||
|
/** @type {HTMLElement} */ let header_html = get_element(this._html_scene, "[data-name=headertitle]");
|
||||||
if (header_html) header_html.textContent = header_title;
|
if (header_html) header_html.textContent = header_title;
|
||||||
/** @type {HTMLElement} */ let title_form = get_element(html_scene, "[data-name=title]");
|
this._title_form = get_element(this._html_scene, "[data-name=title]");
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
|
this._error_form = get_element(this._html_scene, "[data-name=error]");
|
||||||
/** @type {HTMLElement} */ let confirmation_prompt = get_element(html_scene, "[data-name=confirmationprompt]");
|
this._confirmation_prompt = get_element(this._html_scene, "[data-name=confirmationprompt]");
|
||||||
/** @type {HTMLInputElement} */ let confirmation_txt = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=confirmationtxt]"));
|
this._confirmation_txt = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=confirmationtxt]"));
|
||||||
/** @type {HTMLElement} */ let delete_confirmation_lbl = get_element(html_scene, "[data-name=deleteconfirmationtext]");
|
this._delete_confirmation_lbl = get_element(this._html_scene, "[data-name=deleteconfirmationtext]");
|
||||||
/** @type {HTMLElement} */ let delete_btn = get_element(html_scene, "[data-name=delete]");
|
this._delete_btn = get_element(this._html_scene, "[data-name=delete]");
|
||||||
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
|
this._cancel_btn = get_element(this._html_scene, "[data-name=cancel]");
|
||||||
|
|
||||||
if (needsconfirmation) {
|
if (needsconfirmation) {
|
||||||
delete_confirmation_lbl.innerHTML = DELETE_CONFIRMATION_TEXT;
|
this._delete_confirmation_lbl.innerHTML = DELETE_CONFIRMATION_TEXT;
|
||||||
confirmation_txt.value = "";
|
this._confirmation_txt.value = "";
|
||||||
confirmation_txt.addEventListener("keydown", onkeydown);
|
this._confirmation_txt.addEventListener("keydown", (e) => this._onkeydown(e));
|
||||||
confirmation_prompt.classList.remove("hidden");
|
this._confirmation_prompt.classList.remove("hidden");
|
||||||
confirmation_txt.classList.remove("hidden");
|
this._confirmation_txt.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
confirmation_prompt.classList.add("hidden");
|
this._confirmation_prompt.classList.add("hidden");
|
||||||
confirmation_txt.classList.add("hidden");
|
this._confirmation_txt.classList.add("hidden");
|
||||||
confirmation_txt.removeEventListener("keydown", onkeydown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {?XMLHttpRequest} */ let delete_req = null;
|
/** @type {?XMLHttpRequest} */ this._delete_req = null;
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
let validator = new FormValidator(errorHandler);
|
this._validator = new FormValidator(this._errorHandler);
|
||||||
|
|
||||||
if (needsconfirmation) {
|
if (needsconfirmation) {
|
||||||
validator.addValidator(confirmation_txt, validate_equals(confirmation_txt, DELETE_CONFIRMATION_TEXT, "confirmation"));
|
this._validator.addValidator(this._confirmation_txt, validate_equals(this._confirmation_txt, DELETE_CONFIRMATION_TEXT, "confirmation"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ondelete() {
|
_ondelete() {
|
||||||
if (needsconfirmation && !validator.validate()) {
|
if (this._needsconfirmation && !this._validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let loading_scene = new LoadingScene();
|
let loading_scene = new LoadingScene();
|
||||||
push_scene(loading_scene);
|
push_scene(loading_scene);
|
||||||
delete_req = delete_action(user, password, item, function (/** @type {?string} */ error1) {
|
this._delete_req = this._delete_action(this._user, this._password, this._item, (/** @type {?string} */ error1) => {
|
||||||
if (!is_current_scene(loading_scene)) {
|
if (!is_current_scene(loading_scene)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete_req = null;
|
this._delete_req = null;
|
||||||
if (error1) {
|
if (error1) {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
errorHandler.setError(error1);
|
this._errorHandler.setError(error1);
|
||||||
} else {
|
} else {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
if (on_success) {
|
if (this._on_success) {
|
||||||
on_success();
|
this._on_success();
|
||||||
} else {
|
} else {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
pop_scene();
|
pop_scene();
|
||||||
@@ -105,7 +113,7 @@ export class DeleteConfirmationScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function oncancel() {
|
_oncancel() {
|
||||||
try {
|
try {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -114,31 +122,35 @@ export class DeleteConfirmationScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onkeydown(/** @type {KeyboardEvent}*/ event) {
|
/** @param {KeyboardEvent} event */
|
||||||
|
_onkeydown(event) {
|
||||||
if (event.code !== "Enter") {
|
if (event.code !== "Enter") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ondelete();
|
this._ondelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
this.release();
|
this.release();
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
title_form.textContent = item_title;
|
this._title_form.textContent = this._item_title;
|
||||||
delete_btn.onclick = ondelete;
|
this._delete_btn.onclick = () => this._ondelete();
|
||||||
cancel_btn.onclick = oncancel;
|
this._cancel_btn.onclick = () => this._oncancel();
|
||||||
validator.validate();
|
this._validator.validate();
|
||||||
};
|
}
|
||||||
this.hide = function () {
|
|
||||||
html_scene.classList.add("hidden");
|
hide() {
|
||||||
cancel_btn.onclick = null;
|
this._html_scene.classList.add("hidden");
|
||||||
delete_btn.onclick = null;
|
this._cancel_btn.onclick = null;
|
||||||
};
|
this._delete_btn.onclick = null;
|
||||||
this.release = function () {
|
}
|
||||||
if (delete_req !== null) {
|
|
||||||
delete_req.abort();
|
is_transient() { return false; }
|
||||||
delete_req = null;
|
|
||||||
|
release() {
|
||||||
|
if (this._delete_req !== null) {
|
||||||
|
this._delete_req.abort();
|
||||||
|
this._delete_req = null;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,31 +35,30 @@ export class IncomingSharingScene {
|
|||||||
* @param {?string} password
|
* @param {?string} password
|
||||||
*/
|
*/
|
||||||
constructor(user, password) {
|
constructor(user, password) {
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("incomingsharingscene");
|
this._user = user;
|
||||||
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
|
this._password = password;
|
||||||
/** @type {HTMLElement} */ let error_element = get_element(html_scene, "[data-name=error]");
|
|
||||||
/** @type {HTMLElement} */ let tbody = get_element(html_scene, "tbody[data-name=incomingsharesbody]");
|
|
||||||
/** @type {HTMLElement} */ let template = get_element(tbody, "[data-name=incomingsharerowtemplate]");
|
|
||||||
/** @type {HTMLElement} */ let table = get_element(html_scene, "table");
|
|
||||||
/** @type {HTMLElement} */ let noshares_message = get_element(html_scene, "[data-name=nosharesmessage]");
|
|
||||||
|
|
||||||
let error_handler = new ErrorHandler(error_element);
|
this._html_scene = get_element_by_id("incomingsharingscene");
|
||||||
|
this._cancel_btn = get_element(this._html_scene, "[data-name=cancel]");
|
||||||
|
this._error_element = get_element(this._html_scene, "[data-name=error]");
|
||||||
|
this._tbody = get_element(this._html_scene, "tbody[data-name=incomingsharesbody]");
|
||||||
|
this._template = get_element(this._tbody, "[data-name=incomingsharerowtemplate]");
|
||||||
|
this._table = get_element(this._html_scene, "table");
|
||||||
|
this._noshares_message = get_element(this._html_scene, "[data-name=nosharesmessage]");
|
||||||
|
|
||||||
/** @type {Array<HTMLElement>} */ let nodes = [];
|
this._error_handler = new ErrorHandler(this._error_element);
|
||||||
|
|
||||||
function on_cancel() {
|
/** @type {Array<HTMLElement>} */ this._nodes = [];
|
||||||
pop_scene();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../api/sharing.js").Share} share
|
* @param {import("../api/sharing.js").Share} share
|
||||||
* @param {HTMLElement} node
|
* @param {HTMLElement} node
|
||||||
*/
|
*/
|
||||||
function toggle_share(share, node) {
|
_toggle_share(share, node) {
|
||||||
let enabled_cb = /** @type {HTMLInputElement} */ (node.querySelector("[data-name=enabled]"));
|
let enabled_cb = /** @type {HTMLInputElement} */ (node.querySelector("[data-name=enabled]"));
|
||||||
let shown_cb = /** @type {HTMLInputElement} */ (node.querySelector("[data-name=shown]"));
|
let shown_cb = /** @type {HTMLInputElement} */ (node.querySelector("[data-name=shown]"));
|
||||||
|
|
||||||
// disable checkboxes while updating
|
|
||||||
enabled_cb.disabled = true;
|
enabled_cb.disabled = true;
|
||||||
shown_cb.disabled = true;
|
shown_cb.disabled = true;
|
||||||
|
|
||||||
@@ -68,14 +67,14 @@ export class IncomingSharingScene {
|
|||||||
|
|
||||||
share.EnabledByUser = enabled_cb.checked;
|
share.EnabledByUser = enabled_cb.checked;
|
||||||
share.HiddenByUser = !shown_cb.checked;
|
share.HiddenByUser = !shown_cb.checked;
|
||||||
error_handler.clearError();
|
this._error_handler.clearError();
|
||||||
|
|
||||||
update_incoming_share(user, password, share, function (error) {
|
update_incoming_share(this._user, this._password, share, (error) => {
|
||||||
enabled_cb.disabled = false;
|
enabled_cb.disabled = false;
|
||||||
shown_cb.disabled = !share.EnabledByUser;
|
shown_cb.disabled = !share.EnabledByUser;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
error_handler.setError(error);
|
this._error_handler.setError(error);
|
||||||
enabled_cb.checked = old_enabled;
|
enabled_cb.checked = old_enabled;
|
||||||
shown_cb.checked = !old_hidden;
|
shown_cb.checked = !old_hidden;
|
||||||
shown_cb.disabled = !old_enabled;
|
shown_cb.disabled = !old_enabled;
|
||||||
@@ -88,30 +87,30 @@ export class IncomingSharingScene {
|
|||||||
/**
|
/**
|
||||||
* @param {import("../api/sharing.js").Share[]} shares
|
* @param {import("../api/sharing.js").Share[]} shares
|
||||||
*/
|
*/
|
||||||
function render_shares(shares) {
|
_render_shares(shares) {
|
||||||
// clear old nodes
|
// clear old nodes
|
||||||
nodes.forEach(function (node) {
|
this._nodes.forEach(function (node) {
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes = [];
|
this._nodes = [];
|
||||||
|
|
||||||
let prefix = "/" + user + "/";
|
let prefix = "/" + this._user + "/";
|
||||||
let filtered_shares = shares.filter(
|
let filtered_shares = shares.filter(
|
||||||
share => (share.ShareType === "map")
|
share => (share.ShareType === "map")
|
||||||
&& share.PathOrToken.startsWith(prefix));
|
&& share.PathOrToken.startsWith(prefix));
|
||||||
|
|
||||||
if (filtered_shares.length === 0) {
|
if (filtered_shares.length === 0) {
|
||||||
table.classList.add("hidden");
|
this._table.classList.add("hidden");
|
||||||
noshares_message.classList.remove("hidden");
|
this._noshares_message.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
table.classList.remove("hidden");
|
this._table.classList.remove("hidden");
|
||||||
noshares_message.classList.add("hidden");
|
this._noshares_message.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered_shares.forEach(function (share) {
|
filtered_shares.forEach((share) => {
|
||||||
let node = /** @type {HTMLElement} */(template.cloneNode(true));
|
let node = /** @type {HTMLElement} */(this._template.cloneNode(true));
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
|
|
||||||
let pathortoken = /** @type {HTMLInputElement} */ (get_element(node, "[data-name=pathortoken]"));
|
let pathortoken = /** @type {HTMLInputElement} */ (get_element(node, "[data-name=pathortoken]"));
|
||||||
@@ -136,35 +135,36 @@ export class IncomingSharingScene {
|
|||||||
shown_cb.checked = shown;
|
shown_cb.checked = shown;
|
||||||
shown_cb.disabled = !enabled;
|
shown_cb.disabled = !enabled;
|
||||||
|
|
||||||
enabled_cb.onchange = function () { toggle_share(share, node); };
|
enabled_cb.onchange = () => { this._toggle_share(share, node); };
|
||||||
shown_cb.onchange = function () { toggle_share(share, node); };
|
shown_cb.onchange = () => { this._toggle_share(share, node); };
|
||||||
|
|
||||||
nodes.push(node);
|
this._nodes.push(node);
|
||||||
tbody.appendChild(node);
|
this._tbody.appendChild(node);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
cancel_btn.onclick = on_cancel;
|
this._cancel_btn.onclick = () => pop_scene();
|
||||||
error_handler.clearError();
|
this._error_handler.clearError();
|
||||||
collectionsCache.getIncomingShares(user, password, error_handler.setError, render_shares);
|
collectionsCache.getIncomingShares(this._user, this._password, this._error_handler.setError, (shares) => this._render_shares(shares));
|
||||||
};
|
}
|
||||||
|
|
||||||
this.hide = function () {
|
hide() {
|
||||||
html_scene.classList.add("hidden");
|
this._html_scene.classList.add("hidden");
|
||||||
cancel_btn.onclick = null;
|
this._cancel_btn.onclick = null;
|
||||||
error_handler.clearError();
|
this._error_handler.clearError();
|
||||||
};
|
}
|
||||||
|
|
||||||
this.release = function () {
|
is_transient() { return false; }
|
||||||
error_handler.clearError();
|
|
||||||
nodes.forEach(function (node) {
|
release() {
|
||||||
|
this._error_handler.clearError();
|
||||||
|
this._nodes.forEach(function (node) {
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes = [];
|
this._nodes = [];
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ export class LoadingScene {
|
|||||||
this.html_scene.classList.add("hidden");
|
this.html_scene.classList.add("hidden");
|
||||||
}
|
}
|
||||||
release() { }
|
release() { }
|
||||||
|
is_transient() { return true; }
|
||||||
}
|
}
|
||||||
@@ -31,113 +31,112 @@ import { LoadingScene } from "./LoadingScene.js";
|
|||||||
import { Scene, is_current_scene, pop_scene, pop_to_root, push_scene, replace_scene } from "./scene_manager.js";
|
import { Scene, is_current_scene, pop_scene, pop_to_root, push_scene, replace_scene } from "./scene_manager.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
|
||||||
* @implements {Scene}
|
* @implements {Scene}
|
||||||
*/
|
*/
|
||||||
export class LoginScene {
|
export class LoginScene {
|
||||||
constructor() {
|
constructor() {
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("loginscene");
|
this._html_scene = get_element_by_id("loginscene");
|
||||||
/** @type {HTMLElement} */ let form = get_element(html_scene, "[data-name=form]");
|
this._form = get_element(this._html_scene, "[data-name=form]");
|
||||||
/** @type {HTMLInputElement} */ let user_form = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=user]"));
|
this._user_form = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=user]"));
|
||||||
/** @type {HTMLInputElement} */ let password_form = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=password]"));
|
this._password_form = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=password]"));
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
|
this._error_form = get_element(this._html_scene, "[data-name=error]");
|
||||||
/** @type {HTMLElement} */ let logout_view = get_element_by_id("logoutview");
|
this._logout_view = get_element_by_id("logoutview");
|
||||||
/** @type {HTMLElement} */ let logout_user_form = get_element(logout_view, "[data-name=user]");
|
this._logout_user_form = get_element(this._logout_view, "[data-name=user]");
|
||||||
/** @type {HTMLElement} */ let logout_btn = get_element(logout_view, "[data-name=logout]");
|
this._logout_btn = get_element(this._logout_view, "[data-name=logout]");
|
||||||
/** @type {HTMLElement} */ let refresh_btn = get_element(logout_view, "[data-name=refresh]");
|
this._refresh_btn = get_element(this._logout_view, "[data-name=refresh]");
|
||||||
|
|
||||||
let user = "";
|
this._user = "";
|
||||||
/** @type {?XMLHttpRequest} */ let principal_req = null;
|
/** @type {?XMLHttpRequest} */ this._principal_req = null;
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
let validator = new FormValidator(errorHandler);
|
this._validator = new FormValidator(this._errorHandler);
|
||||||
validator.addValidator(user_form, validate_non_empty(user_form, "Username"));
|
this._validator.addValidator(this._user_form, validate_non_empty(this._user_form, "Username"));
|
||||||
|
|
||||||
function read_form() {
|
|
||||||
user = user_form.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fill_form() {
|
_read_form() {
|
||||||
user_form.value = user;
|
this._user = this._user_form.value;
|
||||||
password_form.value = "";
|
}
|
||||||
|
|
||||||
|
_fill_form() {
|
||||||
|
this._user_form.value = this._user;
|
||||||
|
this._password_form.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} p_user
|
* @param {string} p_user
|
||||||
* @param {?string} p_password
|
* @param {?string} p_password
|
||||||
*/
|
*/
|
||||||
function perform_login(p_user, p_password) {
|
_perform_login(p_user, p_password) {
|
||||||
user = p_user;
|
this._user = p_user;
|
||||||
fill_form();
|
this._fill_form();
|
||||||
// setup logout
|
// setup logout
|
||||||
logout_view.classList.remove("hidden");
|
this._logout_view.classList.remove("hidden");
|
||||||
if (p_password === null) {
|
if (p_password === null) {
|
||||||
logout_btn.classList.add("hidden");
|
this._logout_btn.classList.add("hidden");
|
||||||
} else {
|
} else {
|
||||||
logout_btn.classList.remove("hidden");
|
this._logout_btn.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
logout_btn.onclick = onlogout;
|
this._logout_btn.onclick = () => this._onlogout();
|
||||||
refresh_btn.onclick = refresh;
|
this._refresh_btn.onclick = () => this._refresh();
|
||||||
logout_user_form.textContent = user + "'s Collections";
|
this._logout_user_form.textContent = this._user + "'s Collections";
|
||||||
// Fetch principal
|
// Fetch principal
|
||||||
let loading_scene = new LoadingScene();
|
let loading_scene = new LoadingScene();
|
||||||
push_scene(loading_scene);
|
push_scene(loading_scene);
|
||||||
principal_req = get_principal(user, p_password, function (/** @type {?Collection} */ principal_collection, error1) {
|
this._principal_req = get_principal(this._user, p_password, (/** @type {?Collection} */ principal_collection, error1) => {
|
||||||
if (!is_current_scene(loading_scene)) {
|
if (!is_current_scene(loading_scene)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
principal_req = null;
|
this._principal_req = null;
|
||||||
if (error1) {
|
if (error1) {
|
||||||
errorHandler.setError(error1);
|
this._errorHandler.setError(error1);
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} else if (principal_collection) {
|
} else if (principal_collection) {
|
||||||
// show collections
|
// show collections
|
||||||
let saved_user = user;
|
let saved_user = this._user;
|
||||||
user = "";
|
this._user = "";
|
||||||
let collections_scene = new CollectionsScene(
|
let collections_scene = new CollectionsScene(
|
||||||
saved_user, p_password, principal_collection, function (/** @type {?string} */ error1) {
|
saved_user, p_password, principal_collection, (/** @type {?string} */ error1) => {
|
||||||
errorHandler.setError(error1);
|
this._errorHandler.setError(error1);
|
||||||
user = saved_user;
|
this._user = saved_user;
|
||||||
});
|
});
|
||||||
replace_scene(collections_scene);
|
replace_scene(collections_scene);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onlogin() {
|
_onlogin() {
|
||||||
try {
|
try {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
read_form();
|
this._read_form();
|
||||||
let password = password_form.value;
|
let password = this._password_form.value;
|
||||||
if (!validator.validate()) {
|
if (!this._validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
perform_login(user, password);
|
this._perform_login(this._user, password);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let onlogout = function () {
|
_onlogout() {
|
||||||
try {
|
try {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
user = "";
|
this._user = "";
|
||||||
pop_to_root();
|
pop_to_root();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function remove_logout() {
|
|
||||||
logout_view.classList.add("hidden");
|
|
||||||
logout_btn.onclick = null;
|
|
||||||
refresh_btn.onclick = null;
|
|
||||||
logout_user_form.textContent = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
_remove_logout() {
|
||||||
|
this._logout_view.classList.add("hidden");
|
||||||
|
this._logout_btn.onclick = null;
|
||||||
|
this._refresh_btn.onclick = null;
|
||||||
|
this._logout_user_form.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
_refresh() {
|
||||||
// The easiest way to refresh is to push a LoadingScene onto the stack and then pop it
|
// The easiest way to refresh is to push a LoadingScene onto the stack and then pop it
|
||||||
// forcing the scene below it, the Collections Scene to refresh itself.
|
// forcing the scene below it, the Collections Scene to refresh itself.
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
@@ -145,12 +144,12 @@ export class LoginScene {
|
|||||||
pop_scene();
|
pop_scene();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
remove_logout();
|
this._remove_logout();
|
||||||
fill_form();
|
this._fill_form();
|
||||||
form.onsubmit = onlogin;
|
this._form.onsubmit = () => this._onlogin();
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
user_form.focus();
|
this._user_form.focus();
|
||||||
|
|
||||||
// Probe for existing authentication (e.g. X-Remote-User)
|
// Probe for existing authentication (e.g. X-Remote-User)
|
||||||
// Use fetch with credentials: 'omit' to avoid browser login prompt on 401
|
// Use fetch with credentials: 'omit' to avoid browser login prompt on 401
|
||||||
@@ -158,10 +157,10 @@ export class LoginScene {
|
|||||||
method: 'PROPFIND',
|
method: 'PROPFIND',
|
||||||
headers: { 'Depth': '0' },
|
headers: { 'Depth': '0' },
|
||||||
credentials: 'omit'
|
credentials: 'omit'
|
||||||
}).then(function (response) {
|
}).then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Authenticated! Now it's safe to call get_principal
|
// Authenticated! Now it's safe to call get_principal
|
||||||
get_principal(null, null, function (/** @type {?Collection} */ principal_collection, error) {
|
get_principal(null, null, (/** @type {?Collection} */ principal_collection, error) => {
|
||||||
if (!error && principal_collection) {
|
if (!error && principal_collection) {
|
||||||
let authenticated_user = principal_collection.displayname;
|
let authenticated_user = principal_collection.displayname;
|
||||||
if (!authenticated_user) {
|
if (!authenticated_user) {
|
||||||
@@ -170,26 +169,29 @@ export class LoginScene {
|
|||||||
authenticated_user = href.substring(href.lastIndexOf("/") + 1);
|
authenticated_user = href.substring(href.lastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perform_login(authenticated_user, null);
|
this._perform_login(authenticated_user, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})["catch"](function () {
|
})["catch"](function () {
|
||||||
// Ignore error: we are not authenticated or something else went wrong
|
// Ignore error: we are not authenticated or something else went wrong
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
this.hide = function () {
|
|
||||||
read_form();
|
hide() {
|
||||||
html_scene.classList.add("hidden");
|
this._read_form();
|
||||||
form.onsubmit = null;
|
this._html_scene.classList.add("hidden");
|
||||||
};
|
this._form.onsubmit = null;
|
||||||
this.release = function () {
|
}
|
||||||
|
|
||||||
|
is_transient() { return false; }
|
||||||
|
|
||||||
|
release() {
|
||||||
// cancel pending requests
|
// cancel pending requests
|
||||||
if (principal_req !== null) {
|
if (this._principal_req !== null) {
|
||||||
principal_req.abort();
|
this._principal_req.abort();
|
||||||
principal_req = null;
|
this._principal_req = null;
|
||||||
}
|
}
|
||||||
remove_logout();
|
this._remove_logout();
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,21 +43,24 @@ export class ShareCollectionScene {
|
|||||||
* @param {Collection} collection The collection on which to edit sharing setting. Must exist.
|
* @param {Collection} collection The collection on which to edit sharing setting. Must exist.
|
||||||
*/
|
*/
|
||||||
constructor(user, password, collection) {
|
constructor(user, password, collection) {
|
||||||
|
this._user = user;
|
||||||
|
this._password = password;
|
||||||
|
this._collection = collection;
|
||||||
|
|
||||||
let html_scene = get_element_by_id("sharecollectionscene");
|
this._html_scene = get_element_by_id("sharecollectionscene");
|
||||||
|
this._cancel_btn = get_element(this._html_scene, "[data-name=cancel]");
|
||||||
|
this._share_by_token_btn = get_element(this._html_scene, "button[data-name=sharebytoken]");
|
||||||
|
this._share_by_map_btn = get_element(this._html_scene, "button[data-name=sharebymap]");
|
||||||
|
this._share_by_token_div = get_element(this._html_scene, "div[data-name=sharebytoken]");
|
||||||
|
this._share_by_map_div = get_element(this._html_scene, "div[data-name=sharebymap]");
|
||||||
|
this._error_form = get_element(this._html_scene, "[data-name=error]");
|
||||||
|
|
||||||
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
/** @type {HTMLElement} */ let share_by_token_btn = get_element(html_scene, "button[data-name=sharebytoken]");
|
|
||||||
/** @type {HTMLElement} */ let share_by_map_btn = get_element(html_scene, "button[data-name=sharebymap]");
|
|
||||||
/** @type {HTMLElement} */ let share_by_token_div = get_element(html_scene, "div[data-name=sharebytoken]");
|
|
||||||
/** @type {HTMLElement} */ let share_by_map_div = get_element(html_scene, "div[data-name=sharebymap]");
|
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
|
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._title = get_element(this._html_scene, "[data-name=title]");
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {HTMLElement} */ let title = get_element(html_scene, "[data-name=title]");
|
_oncancel() {
|
||||||
|
|
||||||
function oncancel() {
|
|
||||||
try {
|
try {
|
||||||
pop_scene();
|
pop_scene();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -66,67 +69,70 @@ export class ShareCollectionScene {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onsharebytoken() {
|
_onsharebytoken() {
|
||||||
let create_edit_share_scene = new CreateEditShareScene(user, password, collection, "token");
|
let create_edit_share_scene = new CreateEditShareScene(this._user, this._password, this._collection, "token");
|
||||||
push_scene(create_edit_share_scene);
|
push_scene(create_edit_share_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onsharebymap() {
|
_onsharebymap() {
|
||||||
let create_edit_share_scene = new CreateEditShareScene(user, password, collection, "map");
|
let create_edit_share_scene = new CreateEditShareScene(this._user, this._password, this._collection, "map");
|
||||||
push_scene(create_edit_share_scene);
|
push_scene(create_edit_share_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
this.release();
|
this.release();
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
html_scene.querySelectorAll("details").forEach(function (details) {
|
this._html_scene.querySelectorAll("details").forEach(function (details) {
|
||||||
details.open = true;
|
details.open = true;
|
||||||
});
|
});
|
||||||
cancel_btn.onclick = oncancel;
|
this._cancel_btn.onclick = () => this._oncancel();
|
||||||
|
|
||||||
collectionsCache.getServerFeatures(user, password, errorHandler.setError, (features) => {
|
collectionsCache.getServerFeatures(this._user, this._password, this._errorHandler.setError, (features) => {
|
||||||
if (features.sharing && features.sharing.PermittedCreateCollectionByToken) {
|
if (features.sharing && features.sharing.PermittedCreateCollectionByToken) {
|
||||||
if (share_by_token_btn) {
|
if (this._share_by_token_btn) {
|
||||||
share_by_token_btn.classList.remove("hidden");
|
this._share_by_token_btn.classList.remove("hidden");
|
||||||
share_by_token_btn.onclick = onsharebytoken;
|
this._share_by_token_btn.onclick = () => this._onsharebytoken();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (share_by_token_btn) share_by_token_btn.classList.add("hidden");
|
if (this._share_by_token_btn) this._share_by_token_btn.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.sharing && features.sharing.FeatureEnabledCollectionByToken) {
|
if (features.sharing && features.sharing.FeatureEnabledCollectionByToken) {
|
||||||
if (share_by_token_div) share_by_token_div.classList.remove("hidden");
|
if (this._share_by_token_div) this._share_by_token_div.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
if (share_by_token_div) share_by_token_div.classList.add("hidden");
|
if (this._share_by_token_div) this._share_by_token_div.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.sharing && features.sharing.PermittedCreateCollectionByMap) {
|
if (features.sharing && features.sharing.PermittedCreateCollectionByMap) {
|
||||||
if (share_by_map_btn) {
|
if (this._share_by_map_btn) {
|
||||||
share_by_map_btn.classList.remove("hidden");
|
this._share_by_map_btn.classList.remove("hidden");
|
||||||
share_by_map_btn.onclick = onsharebymap;
|
this._share_by_map_btn.onclick = () => this._onsharebymap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (share_by_map_btn) share_by_map_btn.classList.add("hidden");
|
if (this._share_by_map_btn) this._share_by_map_btn.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.sharing && features.sharing.FeatureEnabledCollectionByMap) {
|
if (features.sharing && features.sharing.FeatureEnabledCollectionByMap) {
|
||||||
if (share_by_map_div) share_by_map_div.classList.remove("hidden");
|
if (this._share_by_map_div) this._share_by_map_div.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
if (share_by_map_div) share_by_map_div.classList.add("hidden");
|
if (this._share_by_map_div) this._share_by_map_div.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
title.textContent = collection.displayname || collection.href;
|
this._title.textContent = this._collection.displayname || this._collection.href;
|
||||||
update_share_list(user, password, collection, errorHandler);
|
update_share_list(this._user, this._password, this._collection, this._errorHandler);
|
||||||
};
|
|
||||||
this.hide = function () {
|
|
||||||
html_scene.classList.add("hidden");
|
|
||||||
cancel_btn.onclick = null;
|
|
||||||
};
|
|
||||||
this.release = function () {
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this._html_scene.classList.add("hidden");
|
||||||
|
this._cancel_btn.onclick = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
release() {
|
||||||
|
}
|
||||||
|
|
||||||
|
is_transient() { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,96 +37,101 @@ export class UploadCollectionScene {
|
|||||||
* @param {Collection} collection parent collection
|
* @param {Collection} collection parent collection
|
||||||
*/
|
*/
|
||||||
constructor(user, password, collection) {
|
constructor(user, password, collection) {
|
||||||
/** @type {HTMLElement} */ let html_scene = get_element_by_id("uploadcollectionscene");
|
this._user = user;
|
||||||
/** @type {HTMLElement} */ let template = get_element(html_scene, "[data-name=filetemplate]");
|
this._password = password;
|
||||||
/** @type {HTMLElement} */ let upload_btn = get_element(html_scene, "[data-name=submit]");
|
this._collection = collection;
|
||||||
/** @type {HTMLElement} */ let close_btn = get_element(html_scene, "[data-name=close]");
|
|
||||||
/** @type {HTMLInputElement} */ let uploadfile_form = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=uploadfile]"));
|
|
||||||
/** @type {HTMLElement} */ let uploadfile_lbl = get_element(html_scene, "label[for=uploadfile]");
|
|
||||||
/** @type {HTMLInputElement} */ let href_form = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=href]"));
|
|
||||||
/** @type {HTMLElement} */ let href_label = get_element(html_scene, "label[for=href]");
|
|
||||||
/** @type {HTMLElement} */ let hreflimitmsg_html = get_element(html_scene, "[data-name=hreflimitmsg]");
|
|
||||||
/** @type {HTMLElement} */ let pending_html = get_element(html_scene, "[data-name=pending]");
|
|
||||||
/** @type {HTMLElement} */ let error_form = get_element(html_scene, ":scope > span[data-name=error]");
|
|
||||||
|
|
||||||
let files = uploadfile_form.files;
|
this._html_scene = get_element_by_id("uploadcollectionscene");
|
||||||
href_form.addEventListener("input", onCleanHREFinput);
|
this._template = get_element(this._html_scene, "[data-name=filetemplate]");
|
||||||
upload_btn.onclick = upload_start;
|
this._upload_btn = get_element(this._html_scene, "[data-name=submit]");
|
||||||
uploadfile_form.onchange = onfileschange;
|
this._close_btn = get_element(this._html_scene, "[data-name=close]");
|
||||||
|
this._uploadfile_form = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=uploadfile]"));
|
||||||
|
this._uploadfile_lbl = get_element(this._html_scene, "label[for=uploadfile]");
|
||||||
|
this._href_form = /** @type {HTMLInputElement} */ (get_element(this._html_scene, "[data-name=href]"));
|
||||||
|
this._href_label = get_element(this._html_scene, "label[for$=href]");
|
||||||
|
this._hreflimitmsg_html = get_element(this._html_scene, "[data-name=hreflimitmsg]");
|
||||||
|
this._pending_html = get_element(this._html_scene, "[data-name=pending]");
|
||||||
|
this._error_form = get_element(this._html_scene, ":scope > span[data-name=error]");
|
||||||
|
|
||||||
href_form.value = "";
|
this._files = this._uploadfile_form.files;
|
||||||
let href = "";
|
this._href_form.addEventListener("input", onCleanHREFinput);
|
||||||
|
this._upload_btn.onclick = () => this._upload_start();
|
||||||
|
this._uploadfile_form.onchange = () => this._onfileschange();
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
this._href_form.value = "";
|
||||||
let validator = new FormValidator(errorHandler);
|
this._href = "";
|
||||||
|
|
||||||
validator.addValidator(href_form, validate_href(href_form, "HREF"));
|
this._errorHandler = new ErrorHandler(this._error_form);
|
||||||
validator.addValidator(uploadfile_form, validate_files(uploadfile_form, "file"));
|
this._validator = new FormValidator(this._errorHandler);
|
||||||
|
|
||||||
/** @type {?XMLHttpRequest} */ let upload_req = null;
|
this._validator.addValidator(this._href_form, validate_href(this._href_form, "HREF"));
|
||||||
/** @type {Array<?string>} */ let results = [];
|
this._validator.addValidator(this._uploadfile_form, validate_files(this._uploadfile_form, "file"));
|
||||||
/** @type {?Array<HTMLElement>} */ let nodes = null;
|
|
||||||
|
|
||||||
function upload_start() {
|
/** @type {?XMLHttpRequest} */ this._upload_req = null;
|
||||||
|
/** @type {Array<?string>} */ this._results = [];
|
||||||
|
/** @type {?Array<HTMLElement>} */ this._nodes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_upload_start() {
|
||||||
try {
|
try {
|
||||||
if (!validator.validate()) {
|
if (!this._validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!files) {
|
if (!this._files) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
read_form();
|
this._read_form();
|
||||||
uploadfile_form.classList.add("hidden");
|
this._uploadfile_form.classList.add("hidden");
|
||||||
uploadfile_lbl.classList.add("hidden");
|
this._uploadfile_lbl.classList.add("hidden");
|
||||||
href_form.classList.add("hidden");
|
this._href_form.classList.add("hidden");
|
||||||
href_label.classList.add("hidden");
|
this._href_label.classList.add("hidden");
|
||||||
hreflimitmsg_html.classList.add("hidden");
|
this._hreflimitmsg_html.classList.add("hidden");
|
||||||
upload_btn.classList.add("hidden");
|
this._upload_btn.classList.add("hidden");
|
||||||
close_btn.classList.add("hidden");
|
this._close_btn.classList.add("hidden");
|
||||||
|
|
||||||
pending_html.classList.remove("hidden");
|
this._pending_html.classList.remove("hidden");
|
||||||
|
|
||||||
nodes = [];
|
this._nodes = [];
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < this._files.length; i++) {
|
||||||
let file = files[i];
|
let file = this._files[i];
|
||||||
let node = /** @type {HTMLElement} */ (template.cloneNode(true));
|
let node = /** @type {HTMLElement} */ (this._template.cloneNode(true));
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
let name_form = get_element(node, "[data-name=name]");
|
let name_form = get_element(node, "[data-name=name]");
|
||||||
name_form.textContent = file.name;
|
name_form.textContent = file.name;
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
nodes.push(node);
|
this._nodes.push(node);
|
||||||
updateFileStatus(i);
|
this._updateFileStatus(i);
|
||||||
if (template.parentNode) {
|
if (this._template.parentNode) {
|
||||||
template.parentNode.insertBefore(node, template);
|
this._template.parentNode.insertBefore(node, this._template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
upload_next();
|
this._upload_next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function upload_next() {
|
_upload_next() {
|
||||||
if (!files) {
|
if (!this._files) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (files.length === results.length) {
|
if (this._files.length === this._results.length) {
|
||||||
pending_html.classList.add("hidden");
|
this._pending_html.classList.add("hidden");
|
||||||
close_btn.classList.remove("hidden");
|
this._close_btn.classList.remove("hidden");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
let file = files[results.length];
|
let file = this._files[this._results.length];
|
||||||
if (files.length > 1 || href.length == 0) {
|
if (this._files.length > 1 || this._href.length == 0) {
|
||||||
href = random_uuid();
|
this._href = random_uuid();
|
||||||
}
|
}
|
||||||
let upload_href = collection.href + href + "/";
|
let upload_href = this._collection.href + this._href + "/";
|
||||||
upload_req = upload_collection(user, password, upload_href, file, function (result) {
|
this._upload_req = upload_collection(this._user, this._password, upload_href, file, (result) => {
|
||||||
upload_req = null;
|
this._upload_req = null;
|
||||||
results.push(result);
|
this._results.push(result);
|
||||||
updateFileStatus(results.length - 1);
|
this._updateFileStatus(this._results.length - 1);
|
||||||
upload_next();
|
this._upload_next();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -134,9 +139,9 @@ export class UploadCollectionScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onclose() {
|
_onclose() {
|
||||||
try {
|
try {
|
||||||
if (results.length > 0) {
|
if (this._results.length > 0) {
|
||||||
collectionsCache.invalidate();
|
collectionsCache.invalidate();
|
||||||
}
|
}
|
||||||
pop_scene();
|
pop_scene();
|
||||||
@@ -149,17 +154,17 @@ export class UploadCollectionScene {
|
|||||||
/**
|
/**
|
||||||
* @param {number} i
|
* @param {number} i
|
||||||
*/
|
*/
|
||||||
function updateFileStatus(i) {
|
_updateFileStatus(i) {
|
||||||
if (nodes === null) {
|
if (this._nodes === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/** @type {HTMLElement} */ let file_success_form = get_element(nodes[i], "[data-name=success]");
|
/** @type {HTMLElement} */ let file_success_form = get_element(this._nodes[i], "[data-name=success]");
|
||||||
/** @type {HTMLElement} */ let file_error_form = get_element(nodes[i], "[data-name=error]");
|
/** @type {HTMLElement} */ let file_error_form = get_element(this._nodes[i], "[data-name=error]");
|
||||||
if (results.length > i) {
|
if (this._results.length > i) {
|
||||||
if (results[i]) {
|
if (this._results[i]) {
|
||||||
file_success_form.classList.add("hidden");
|
file_success_form.classList.add("hidden");
|
||||||
file_error_form.textContent = "Error: " + results[i];
|
file_error_form.textContent = "Error: " + this._results[i];
|
||||||
error_form.classList.remove("hidden");
|
this._error_form.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
file_success_form.classList.remove("hidden");
|
file_success_form.classList.remove("hidden");
|
||||||
file_error_form.classList.add("hidden");
|
file_error_form.classList.add("hidden");
|
||||||
@@ -170,68 +175,70 @@ export class UploadCollectionScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_form() {
|
_read_form() {
|
||||||
cleanHREFinput(href_form);
|
cleanHREFinput(this._href_form);
|
||||||
href = href_form.value.trim().toLowerCase();
|
this._href = this._href_form.value.trim().toLowerCase();
|
||||||
files = uploadfile_form.files;
|
this._files = this._uploadfile_form.files;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onfileschange() {
|
_onfileschange() {
|
||||||
files = uploadfile_form.files;
|
this._files = this._uploadfile_form.files;
|
||||||
if (files && files.length > 1) {
|
if (this._files && this._files.length > 1) {
|
||||||
hreflimitmsg_html.classList.remove("hidden");
|
this._hreflimitmsg_html.classList.remove("hidden");
|
||||||
href_form.classList.add("hidden");
|
this._href_form.classList.add("hidden");
|
||||||
href_label.classList.add("hidden");
|
this._href_label.classList.add("hidden");
|
||||||
href_form.value = random_uuid(); // fake HREF, will be replaced on upload
|
this._href_form.value = random_uuid(); // fake HREF, will be replaced on upload
|
||||||
} else {
|
} else {
|
||||||
hreflimitmsg_html.classList.add("hidden");
|
this._hreflimitmsg_html.classList.add("hidden");
|
||||||
href_form.classList.remove("hidden");
|
this._href_form.classList.remove("hidden");
|
||||||
href_label.classList.remove("hidden");
|
this._href_label.classList.remove("hidden");
|
||||||
if (files && files.length > 0) {
|
if (this._files && this._files.length > 0) {
|
||||||
href_form.value = files[0].name.replace(/\.(ics|vcf)$/, '');
|
this._href_form.value = this._files[0].name.replace(/\.(ics|vcf)$/, '');
|
||||||
} else {
|
} else {
|
||||||
href_form.value = "";
|
this._href_form.value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.show = function () {
|
show() {
|
||||||
html_scene.classList.remove("hidden");
|
this._html_scene.classList.remove("hidden");
|
||||||
close_btn.onclick = onclose;
|
this._close_btn.onclick = () => this._onclose();
|
||||||
};
|
}
|
||||||
|
|
||||||
this.hide = function () {
|
hide() {
|
||||||
html_scene.classList.add("hidden");
|
this._html_scene.classList.add("hidden");
|
||||||
close_btn.classList.remove("hidden");
|
this._close_btn.classList.remove("hidden");
|
||||||
upload_btn.classList.remove("hidden");
|
this._upload_btn.classList.remove("hidden");
|
||||||
uploadfile_form.classList.remove("hidden");
|
this._uploadfile_form.classList.remove("hidden");
|
||||||
uploadfile_lbl.classList.remove("hidden");
|
this._uploadfile_lbl.classList.remove("hidden");
|
||||||
href_form.classList.remove("hidden");
|
this._href_form.classList.remove("hidden");
|
||||||
href_label.classList.remove("hidden");
|
this._href_label.classList.remove("hidden");
|
||||||
hreflimitmsg_html.classList.add("hidden");
|
this._hreflimitmsg_html.classList.add("hidden");
|
||||||
pending_html.classList.add("hidden");
|
this._pending_html.classList.add("hidden");
|
||||||
errorHandler.clearError();
|
this._errorHandler.clearError();
|
||||||
close_btn.onclick = null;
|
this._close_btn.onclick = null;
|
||||||
upload_btn.onclick = null;
|
this._upload_btn.onclick = null;
|
||||||
href_form.value = "";
|
this._href_form.value = "";
|
||||||
uploadfile_form.value = "";
|
this._uploadfile_form.value = "";
|
||||||
if (nodes == null) {
|
if (this._nodes == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nodes.forEach(function (node) {
|
this._nodes.forEach(function (node) {
|
||||||
if (node.parentNode) {
|
if (node.parentNode) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes = null;
|
this._nodes = null;
|
||||||
};
|
}
|
||||||
this.release = function () {
|
|
||||||
if (upload_req !== null) {
|
is_transient() { return false; }
|
||||||
upload_req.abort();
|
|
||||||
upload_req = null;
|
release() {
|
||||||
|
if (this._upload_req !== null) {
|
||||||
|
this._upload_req.abort();
|
||||||
|
this._upload_req = null;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,11 @@ export class Scene {
|
|||||||
* Scene is removed from scene stack.
|
* Scene is removed from scene stack.
|
||||||
*/
|
*/
|
||||||
release() { }
|
release() { }
|
||||||
|
/**
|
||||||
|
* Whether the scene should be excluded from browser history.
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
is_transient() { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +49,87 @@ export class Scene {
|
|||||||
*/
|
*/
|
||||||
let scene_stack = [];
|
let scene_stack = [];
|
||||||
|
|
||||||
|
/** @type {Array<Array<Scene>>} */
|
||||||
|
let history_array = [];
|
||||||
|
let current_history_index = -1;
|
||||||
|
let is_navigating_history = false;
|
||||||
|
|
||||||
|
function record_history() {
|
||||||
|
if (is_navigating_history) return;
|
||||||
|
|
||||||
|
let current_scene = scene_stack.length > 0 ? scene_stack[scene_stack.length - 1] : null;
|
||||||
|
if (current_scene && current_scene.is_transient && current_scene.is_transient()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare with current history to avoid duplicates
|
||||||
|
if (history_array.length > 0 && current_history_index >= 0) {
|
||||||
|
let last_stack = history_array[current_history_index];
|
||||||
|
if (last_stack && last_stack.length === scene_stack.length) {
|
||||||
|
let is_identical = true;
|
||||||
|
for (let i = 0; i < scene_stack.length; i++) {
|
||||||
|
if (last_stack[i] !== scene_stack[i]) {
|
||||||
|
is_identical = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_identical) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
history_array.splice(current_history_index + 1);
|
||||||
|
history_array.push(scene_stack.slice());
|
||||||
|
current_history_index++;
|
||||||
|
|
||||||
|
// Check if this is the first history entry we are recording
|
||||||
|
if (typeof window !== "undefined" && window.history) {
|
||||||
|
if (current_history_index === 0) {
|
||||||
|
history.replaceState({ history_index: current_history_index }, '');
|
||||||
|
} else {
|
||||||
|
history.pushState({ history_index: current_history_index }, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== "undefined" && window.history) {
|
||||||
|
if (!history.state || typeof history.state.history_index !== "number") {
|
||||||
|
current_history_index = -1;
|
||||||
|
} else {
|
||||||
|
// If there's an existing state but we just loaded, we don't have the history_array memory.
|
||||||
|
// We'll reset it. The user will be redirected to the root if they go back out of bounds.
|
||||||
|
current_history_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("popstate", (event) => {
|
||||||
|
if (!event.state || typeof event.state.history_index !== "number") return;
|
||||||
|
let new_index = event.state.history_index;
|
||||||
|
|
||||||
|
if (new_index >= 0 && new_index < history_array.length) {
|
||||||
|
is_navigating_history = true;
|
||||||
|
try {
|
||||||
|
let new_stack = history_array[new_index];
|
||||||
|
|
||||||
|
if (scene_stack.length > 0) {
|
||||||
|
scene_stack[scene_stack.length - 1].hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
scene_stack = new_stack.slice();
|
||||||
|
|
||||||
|
if (scene_stack.length > 0) {
|
||||||
|
scene_stack[scene_stack.length - 1].show();
|
||||||
|
}
|
||||||
|
|
||||||
|
current_history_index = new_index;
|
||||||
|
} finally {
|
||||||
|
is_navigating_history = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Out of bounds (e.g., from a previous session after a reload)
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push scene onto stack.
|
* Push scene onto stack.
|
||||||
* @param {Scene} scene
|
* @param {Scene} scene
|
||||||
@@ -54,6 +140,7 @@ export function push_scene(scene) {
|
|||||||
}
|
}
|
||||||
scene_stack.push(scene);
|
scene_stack.push(scene);
|
||||||
scene.show();
|
scene.show();
|
||||||
|
record_history();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,6 +167,7 @@ export function replace_scene(scene) {
|
|||||||
}
|
}
|
||||||
scene_stack.push(scene);
|
scene_stack.push(scene);
|
||||||
scene.show();
|
scene.show();
|
||||||
|
record_history();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +182,7 @@ export function pop_scene() {
|
|||||||
if (scene_stack.length >= 1) {
|
if (scene_stack.length >= 1) {
|
||||||
scene_stack[scene_stack.length - 1].show();
|
scene_stack[scene_stack.length - 1].show();
|
||||||
}
|
}
|
||||||
|
record_history();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,6 +201,7 @@ export function pop_to_parent() {
|
|||||||
if (scene_stack.length >= 1) {
|
if (scene_stack.length >= 1) {
|
||||||
scene_stack[scene_stack.length - 1].show();
|
scene_stack[scene_stack.length - 1].show();
|
||||||
}
|
}
|
||||||
|
record_history();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,6 +221,7 @@ export function pop_to_root() {
|
|||||||
if (scene_stack.length === 1) {
|
if (scene_stack.length === 1) {
|
||||||
scene_stack[0].show(); // Ensure the root scene is visible
|
scene_stack[0].show(); // Ensure the root scene is visible
|
||||||
}
|
}
|
||||||
|
record_history();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function displayPermissionsOrConversion(conversion, permissions, node) {
|
|||||||
const roElement = get_element(node, "[data-name=ro]");
|
const roElement = get_element(node, "[data-name=ro]");
|
||||||
const rwElement = get_element(node, "[data-name=rw]");
|
const rwElement = get_element(node, "[data-name=rw]");
|
||||||
let fixedConversion = (conversion || "").toLowerCase();
|
let fixedConversion = (conversion || "").toLowerCase();
|
||||||
if (fixedConversion === "bday") {
|
if (fixedConversion != "none" && fixedConversion != "") {
|
||||||
conversionElement.classList.remove("hidden");
|
conversionElement.classList.remove("hidden");
|
||||||
roElement.classList.add("hidden");
|
roElement.classList.add("hidden");
|
||||||
rwElement.classList.add("hidden");
|
rwElement.classList.add("hidden");
|
||||||
|
|||||||
Reference in New Issue
Block a user