fix some bugs. need to continue bug testing.
This commit is contained in:
parent
0853c2b42c
commit
6b8374cc84
|
@ -47,6 +47,7 @@ class Clusterize {
|
|||
#element_observer = null;
|
||||
#element_observer_timer = null;
|
||||
#pointer_events_set = false;
|
||||
#on_scroll_bound;
|
||||
|
||||
constructor(args) {
|
||||
for (const option of Object.keys(this.options)) {
|
||||
|
@ -56,16 +57,16 @@ class Clusterize {
|
|||
}
|
||||
|
||||
if (isNullOrUndefined(this.options.callbacks.initData)) {
|
||||
this.options.callbacks.initData = this.initDataDefaultCallback;
|
||||
this.options.callbacks.initData = this.initDataDefaultCallback.bind(this);
|
||||
}
|
||||
if (isNullOrUndefined(this.options.callbacks.fetchData)) {
|
||||
this.options.callbacks.fetchData = this.fetchDataDefaultCallback;
|
||||
this.options.callbacks.fetchData = this.fetchDataDefaultCallback.bind(this);
|
||||
}
|
||||
if (isNullOrUndefined(this.options.callbacks.sortData)) {
|
||||
this.options.callbacks.sortData = this.sortDataDefaultCallback;
|
||||
this.options.callbacks.sortData = this.sortDataDefaultCallback.bind(this);
|
||||
}
|
||||
if (isNullOrUndefined(this.options.callbacks.filterData)) {
|
||||
this.options.callbacks.filterData = this.filterDataDefaultCallback;
|
||||
this.options.callbacks.filterData = this.filterDataDefaultCallback.bind(this);
|
||||
}
|
||||
|
||||
// detect ie9 and lower
|
||||
|
@ -96,6 +97,8 @@ class Clusterize {
|
|||
this.#scroll_top = this.scroll_elem.scrollTop;
|
||||
|
||||
this.#max_items = args.max_items;
|
||||
|
||||
this.#on_scroll_bound = this.#onScroll.bind(this);
|
||||
}
|
||||
|
||||
// ==== PUBLIC FUNCTIONS ====
|
||||
|
@ -114,7 +117,7 @@ class Clusterize {
|
|||
await this.#insertToDOM();
|
||||
this.scroll_elem.scrollTop = this.#scroll_top;
|
||||
|
||||
this.#setupEvent("scroll", this.scroll_elem, this.#onScroll);
|
||||
this.#setupEvent("scroll", this.scroll_elem, this.#on_scroll_bound);
|
||||
this.#setupElementObservers();
|
||||
this.#setupResizeObservers();
|
||||
|
||||
|
@ -129,12 +132,13 @@ class Clusterize {
|
|||
this.#html(this.#generateEmptyRow().join(""));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#teardownEvent("scroll", this.scroll_elem, this.#onScroll);
|
||||
destroy() {
|
||||
this.#teardownEvent("scroll", this.scroll_elem, this.#on_scroll_bound);
|
||||
this.#teardownElementObservers();
|
||||
this.#teardownResizeObservers();
|
||||
this.clear();
|
||||
|
||||
this.#html(this.#generateEmptyRow().join(""));
|
||||
|
||||
this.setup_has_run = false;
|
||||
}
|
||||
|
||||
|
@ -210,7 +214,7 @@ class Clusterize {
|
|||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
return await this.options.callbacks.initData.call(this);
|
||||
return await this.options.callbacks.initData();
|
||||
}
|
||||
|
||||
fetchDataDefaultCallback() {
|
||||
|
@ -221,7 +225,7 @@ class Clusterize {
|
|||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
return await this.options.callbacks.fetchData.call(this, idx_start, idx_end);
|
||||
return await this.options.callbacks.fetchData(idx_start, idx_end);
|
||||
}
|
||||
|
||||
sortDataDefaultCallback() {
|
||||
|
@ -236,7 +240,7 @@ class Clusterize {
|
|||
this.#fixElementReferences();
|
||||
|
||||
// Sort is applied to the filtered data.
|
||||
await this.options.callbacks.sortData.call(this);
|
||||
await this.options.callbacks.sortData();
|
||||
this.#recalculateDims();
|
||||
await this.#insertToDOM();
|
||||
}
|
||||
|
@ -251,7 +255,7 @@ class Clusterize {
|
|||
}
|
||||
|
||||
// Filter is applied to entire dataset.
|
||||
const max_items = await this.options.callbacks.filterData.call(this);
|
||||
const max_items = await this.options.callbacks.filterData();
|
||||
await this.setMaxItems(max_items);
|
||||
}
|
||||
|
||||
|
@ -287,10 +291,8 @@ class Clusterize {
|
|||
}
|
||||
|
||||
// Get the first element that isn't one of our placeholder rows.
|
||||
const node = this.content_elem.querySelector(
|
||||
`${this.options.tag}:not(clusterize-extra-row):not(clusterize-no-data)`
|
||||
);
|
||||
if (!isElement(node)) {
|
||||
const node = this.content_elem.querySelector(":scope > :not(.clusterize-extra-row,.clusterize-no-data)");
|
||||
if (!isElementLogError(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -307,10 +309,13 @@ class Clusterize {
|
|||
|
||||
// Update rows in block to match the number of elements that can fit in the view.
|
||||
const content_padding = getComputedPaddingDims(this.content_elem);
|
||||
let content_gap = parseFloat(getComputedProperty(this.content_elem, "gap"));
|
||||
if (isNumber(content_gap)) {
|
||||
this.options.item_width += content_gap;
|
||||
this.options.item_height += content_gap;
|
||||
const column_gap = parseFloat(getComputedProperty(this.content_elem, "column-gap"));
|
||||
const row_gap = parseFloat(getComputedProperty(this.content_elem, "row-gap"));
|
||||
if (isNumber(column_gap)) {
|
||||
this.options.item_width += column_gap;
|
||||
}
|
||||
if (isNumber(row_gap)) {
|
||||
this.options.item_height += row_gap;
|
||||
}
|
||||
|
||||
const inner_width = this.scroll_elem.clientWidth - content_padding.width;
|
||||
|
@ -594,17 +599,17 @@ class Clusterize {
|
|||
|
||||
#setupEvent(type, elem, listener) {
|
||||
if (elem.addEventListener) {
|
||||
return elem.addEventListener(type, event => listener.call(this), false);
|
||||
return elem.addEventListener(type, listener, false);
|
||||
} else {
|
||||
return elem.attachEvent(`on${type}`, event => listener.call(this));
|
||||
return elem.attachEvent(`on${type}`, listener);
|
||||
}
|
||||
}
|
||||
|
||||
#teardownEvent(type, elem, listener) {
|
||||
if (elem.removeEventListener) {
|
||||
return elem.removeEventListener(type, event => listener.call(this), false);
|
||||
return elem.removeEventListener(type, listener, false);
|
||||
} else {
|
||||
return elem.detachEvent(`on${type}`, event => listener.call(this));
|
||||
return elem.detachEvent(`on${type}`, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ var globalPopupInner = null;
|
|||
const storedPopupIds = {};
|
||||
const extraPageUserMetadataEditors = {};
|
||||
const extra_networks_tabs = {};
|
||||
// A flag used by the `waitForBool` promise to determine when we first load Ui Options.
|
||||
/** Boolean flags used along with utils.js::waitForBool(). */
|
||||
// Set true when extraNetworksSetup completes.
|
||||
const extra_networks_setup_complete = {state: false};
|
||||
// Set true when we first load the UI options.
|
||||
const initialUiOptionsLoaded = {state: false};
|
||||
|
||||
class ExtraNetworksTab {
|
||||
|
@ -32,6 +35,9 @@ class ExtraNetworksTab {
|
|||
txt_prompt_elem;
|
||||
txt_neg_prompt_elem;
|
||||
active_prompt_elem;
|
||||
sort_mode_str = "";
|
||||
sort_dir_str = "";
|
||||
filter_str = "";
|
||||
show_prompt = true;
|
||||
show_neg_prompt = true;
|
||||
compact_prompt_en = false;
|
||||
|
@ -70,6 +76,15 @@ class ExtraNetworksTab {
|
|||
await this.setupTreeList();
|
||||
await this.setupCardsList();
|
||||
|
||||
const sort_mode_elem = this.controls_elem.querySelector(".extra-network-control--sort-mode[data-selected='']");
|
||||
isElementThrowError(sort_mode_elem);
|
||||
const sort_dir_elem = this.controls_elem.querySelector(".extra-network-control--sort-dir");
|
||||
isElementThrowError(sort_dir_elem);
|
||||
|
||||
this.setSortMode(sort_mode_elem.dataset.sortMode);
|
||||
this.setSortDir(sort_dir_elem.dataset.sortDir);
|
||||
this.setFilterStr(this.txt_search_elem.value.toLowerCase());
|
||||
|
||||
this.registerPrompt();
|
||||
|
||||
if (this.container_elem.style.display === "none") {
|
||||
|
@ -79,6 +94,24 @@ class ExtraNetworksTab {
|
|||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unload();
|
||||
this.tree_list.destroy();
|
||||
this.cards_list.destroy();
|
||||
this.tree_list = null;
|
||||
this.cards_list = null;
|
||||
this.container_elem = null;
|
||||
this.controls_elem = null;
|
||||
this.txt_search_elem = null;
|
||||
this.prompt_container_elem = null;
|
||||
this.prompts_elem = null;
|
||||
this.prompt_row_elem = null;
|
||||
this.neg_prompt_row_elem = null;
|
||||
this.txt_prompt_elem = null;
|
||||
this.txt_neg_prompt_elem = null;
|
||||
this.active_prompt_elem = null;
|
||||
}
|
||||
|
||||
async registerPrompt() {
|
||||
await Promise.all([
|
||||
waitForElement(`#${this.tabname}_prompt > label > textarea`).then(elem => this.txt_prompt_elem = elem),
|
||||
|
@ -100,8 +133,8 @@ class ExtraNetworksTab {
|
|||
contentId: `${this.tabname_full}_tree_list_content_area`,
|
||||
tag: "button",
|
||||
callbacks: {
|
||||
initData: this.onInitTreeData,
|
||||
fetchData: this.onFetchTreeData,
|
||||
initData: this.onInitTreeData.bind(this),
|
||||
fetchData: this.onFetchTreeData.bind(this),
|
||||
},
|
||||
});
|
||||
await this.tree_list.setup();
|
||||
|
@ -118,13 +151,28 @@ class ExtraNetworksTab {
|
|||
contentId: `${this.tabname_full}_cards_list_content_area`,
|
||||
tag: "div",
|
||||
callbacks: {
|
||||
initData: this.onInitCardsData,
|
||||
fetchData: this.onFetchCardsData,
|
||||
initData: this.onInitCardsData.bind(this),
|
||||
fetchData: this.onFetchCardsData.bind(this),
|
||||
},
|
||||
});
|
||||
await this.cards_list.setup();
|
||||
}
|
||||
|
||||
setSortMode(sort_mode_str) {
|
||||
this.sort_mode_str = sort_mode_str;
|
||||
this.cards_list.setSortMode(this.sort_mode_str);
|
||||
}
|
||||
|
||||
setSortDir(sort_dir_str) {
|
||||
this.sort_dir_str = sort_dir_str;
|
||||
this.cards_list.setSortDir(this.sort_dir_str);
|
||||
}
|
||||
|
||||
setFilterStr(filter_str) {
|
||||
this.filter_str = filter_str;
|
||||
this.cards_list.setFilterStr(this.filter_str);
|
||||
}
|
||||
|
||||
movePrompt(show_prompt=true, show_neg_prompt=true) {
|
||||
// This function only applies when compact prompt mode is enabled.
|
||||
if (!this.compact_prompt_en) {
|
||||
|
@ -142,8 +190,8 @@ class ExtraNetworksTab {
|
|||
this.prompts_elem.classList.toggle("extra-page-prompts-active", show_neg_prompt || show_prompt);
|
||||
}
|
||||
|
||||
async refreshSingleCard(name) {
|
||||
await requestGetPromise(
|
||||
refreshSingleCard(name) {
|
||||
requestGet(
|
||||
"./sd_extra_networks/get-single-card",
|
||||
{
|
||||
tabname: this.tabname,
|
||||
|
@ -152,13 +200,7 @@ class ExtraNetworksTab {
|
|||
},
|
||||
(data) => {
|
||||
if (data && data.html) {
|
||||
const card = this.cards_list.content_elem.querySelector(`.card[data-name="${name}"]`);
|
||||
const new_div = document.createElement("div");
|
||||
new_div.innerHTML = data.html;
|
||||
const new_card = new_div.firstElementChild;
|
||||
new_card.style.display = "";
|
||||
card.parentElement.insertBefore(new_card, card);
|
||||
card.parentElement.removeChild(card);
|
||||
this.cards_list.updateCard(name, data.html);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -176,6 +218,8 @@ class ExtraNetworksTab {
|
|||
const btn_dirs_view = this.controls_elem.querySelector(".extra-network-control--dirs-view");
|
||||
const btn_tree_view = this.controls_elem.querySelector(".extra-network-control--tree-view");
|
||||
const div_dirs = this.container_elem.querySelector(".extra-network-content--dirs-view");
|
||||
// We actually want to select the tree view's column in the resize-handle-row.
|
||||
// This is what we actually show/hide, not the inner elements.
|
||||
const div_tree = this.container_elem.querySelector(
|
||||
`.extra-network-content.resize-handle-col:has(> #${this.tabname_full}_tree_list_scroll_area)`
|
||||
);
|
||||
|
@ -188,6 +232,10 @@ class ExtraNetworksTab {
|
|||
this.tree_list.enable();
|
||||
this.cards_list.enable();
|
||||
await Promise.all([this.tree_list.load(true), this.cards_list.load(true)]);
|
||||
// apply the previous sort/filter options
|
||||
this.setSortMode(this.sort_mode_str);
|
||||
this.setSortDir(this.sort_dir_str);
|
||||
this.setFilterStr(this.filter_str);
|
||||
}
|
||||
|
||||
async load(show_prompt, show_neg_prompt) {
|
||||
|
@ -207,7 +255,7 @@ class ExtraNetworksTab {
|
|||
|
||||
applyFilter() {
|
||||
// We only want to filter/sort the cards list.
|
||||
this.cards_list.setFilterStr(this.txt_search_elem.value.toLowerCase());
|
||||
this.setFilterStr(this.txt_search_elem.value.toLowerCase());
|
||||
|
||||
// If the search input has changed since selecting a button to populate it
|
||||
// then we want to disable the button that previously populated the search input.
|
||||
|
@ -224,48 +272,80 @@ class ExtraNetworksTab {
|
|||
}
|
||||
}
|
||||
|
||||
async waitForServerPageReady() {
|
||||
// We need to wait for the page to be ready before we can fetch data.
|
||||
// After starting the server, on the first load of the page, if the user
|
||||
// immediately clicks a tab, then we will try to load the card data before
|
||||
// the server has even generated it.
|
||||
// We use status 503 to indicate that the page isnt ready yet.
|
||||
while (true) {
|
||||
try {
|
||||
await requestGetPromise(
|
||||
"./sd_extra_networks/page-is-ready",
|
||||
{extra_networks_tabname: this.extra_networks_tabname},
|
||||
);
|
||||
break;
|
||||
} catch (error) {
|
||||
if (error.status === 503) {
|
||||
await new Promise(resolve => setTimeout(resolve, 250));
|
||||
} else {
|
||||
// We do not want to continue waiting if we get an unhandled error.
|
||||
throw new Error("Error checking page readiness:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onInitCardsData() {
|
||||
const res = await requestGetPromise(
|
||||
"./sd_extra_networks/init-cards-data",
|
||||
{
|
||||
tabname: this.tabname,
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
},
|
||||
await this.waitForServerPageReady();
|
||||
|
||||
return JSON.parse(
|
||||
await requestGetPromise(
|
||||
"./sd_extra_networks/init-cards-data",
|
||||
{
|
||||
tabname: this.tabname,
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
},
|
||||
)
|
||||
);
|
||||
return JSON.parse(res);
|
||||
}
|
||||
|
||||
async onInitTreeData() {
|
||||
const res = await requestGetPromise(
|
||||
"./sd_extra_networks/init-tree-data",
|
||||
{
|
||||
tabname: this.tabname,
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
},
|
||||
await this.waitForServerPageReady();
|
||||
|
||||
return JSON.parse(
|
||||
await requestGetPromise(
|
||||
"./sd_extra_networks/init-tree-data",
|
||||
{
|
||||
tabname: this.tabname,
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
},
|
||||
)
|
||||
);
|
||||
return JSON.parse(res);
|
||||
}
|
||||
|
||||
async onFetchCardsData(div_ids) {
|
||||
const res = await requestGetPromise(
|
||||
"./sd_extra_networks/fetch-cards-data",
|
||||
{
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
div_ids: div_ids,
|
||||
},
|
||||
return JSON.parse(
|
||||
await requestGetPromise(
|
||||
"./sd_extra_networks/fetch-cards-data",
|
||||
{
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
div_ids: div_ids,
|
||||
},
|
||||
)
|
||||
);
|
||||
return JSON.parse(res);
|
||||
}
|
||||
|
||||
async onFetchTreeData(div_ids) {
|
||||
const res = await requestGetPromise(
|
||||
"./sd_extra_networks/fetch-tree-data",
|
||||
{
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
div_ids: div_ids,
|
||||
},
|
||||
return JSON.parse(
|
||||
await requestGetPromise(
|
||||
"./sd_extra_networks/fetch-tree-data",
|
||||
{
|
||||
extra_networks_tabname: this.extra_networks_tabname,
|
||||
div_ids: div_ids,
|
||||
},
|
||||
)
|
||||
);
|
||||
return JSON.parse(res);
|
||||
}
|
||||
|
||||
updateSearch(text) {
|
||||
|
@ -484,27 +564,13 @@ function extraNetworksShowMetadata(text) {
|
|||
}
|
||||
|
||||
function extraNetworksRefreshSingleCard(tabname, extra_networks_tabname, name) {
|
||||
requestGet(
|
||||
"./sd_extra_networks/get-single-card",
|
||||
{tabname: tabname, extra_networks_tabname: extra_networks_tabname, name: name},
|
||||
(data) => {
|
||||
if (data && data.html) {
|
||||
const card = gradioApp().querySelector(`${tabname}_${extra_networks_tabname}_cards > .card[data-name="${name}"]`);
|
||||
const new_div = document.createElement("div");
|
||||
new_div.innerHTML = data.html;
|
||||
const new_card = new_div.firstElementChild;
|
||||
|
||||
new_card.style.display = "";
|
||||
card.parentElement.insertBefore(new_card, card);
|
||||
card.parentElement.removeChild(card);
|
||||
}
|
||||
},
|
||||
);
|
||||
const tab = extra_networks_tabs[`${tabname}_${extra_networks_tabname}`];
|
||||
tab.refreshSingleCard(name);
|
||||
}
|
||||
|
||||
async function extraNetworksRefreshTab(tabname_full) {
|
||||
/** called from python when user clicks the extra networks refresh tab button */
|
||||
extra_networks_tabs[tabname_full].refresh();
|
||||
await extra_networks_tabs[tabname_full].refresh();
|
||||
}
|
||||
|
||||
// ==== EVENT HANDLING ====
|
||||
|
@ -535,9 +601,10 @@ function extraNetworksUnrelatedTabSelected() {
|
|||
|
||||
async function extraNetworksTabSelected(tabname_full, show_prompt, show_neg_prompt) {
|
||||
/** called from python when user selects an extra networks tab */
|
||||
await waitForKeyInObject({obj: extra_networks_tabs, k: tabname_full});
|
||||
for (const [k, v] of Object.entries(extra_networks_tabs)) {
|
||||
if (k === tabname_full) {
|
||||
v.load(show_prompt=show_prompt, show_neg_prompt=show_neg_prompt);
|
||||
await v.load(show_prompt=show_prompt, show_neg_prompt=show_neg_prompt);
|
||||
} else {
|
||||
v.unload();
|
||||
}
|
||||
|
@ -563,6 +630,14 @@ function extraNetworksBtnDirsViewItemOnClick(event, tabname_full) {
|
|||
// update search input with selected button's path.
|
||||
elem.dataset.selected = "";
|
||||
txt_search_elem.value = elem.textContent.trim();
|
||||
|
||||
// Select the corresponding tree view button.
|
||||
if ("selected" in elem.dataset) {
|
||||
const tree_row = tab.container_elem.querySelector(`.tree-list-item[data-path="${elem.textContent.trim()}"]`);
|
||||
if (isElement(tree_row)) {
|
||||
tab.tree_list.onRowSelected(tree_row.dataset.divId, tree_row);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _deselect_button = (elem) => {
|
||||
|
@ -603,7 +678,7 @@ function extraNetworksControlSortModeOnClick(event, tabname_full) {
|
|||
|
||||
const sort_mode_str = event.currentTarget.dataset.sortMode.toLowerCase();
|
||||
|
||||
tab.cards_list.setSortMode(sort_mode_str);
|
||||
tab.setSortMode(sort_mode_str);
|
||||
}
|
||||
|
||||
function extraNetworksControlSortDirOnClick(event, tabname_full) {
|
||||
|
@ -624,7 +699,7 @@ function extraNetworksControlSortDirOnClick(event, tabname_full) {
|
|||
event.currentTarget.dataset.sortDir = sort_dir_str;
|
||||
event.currentTarget.setAttribute("title", `Sort ${sort_dir_str}`);
|
||||
|
||||
tab.cards_list.setSortDir(sort_dir_str);
|
||||
tab.setSortDir(sort_dir_str);
|
||||
}
|
||||
|
||||
function extraNetworksControlTreeViewOnClick(event, tabname_full) {
|
||||
|
@ -672,9 +747,6 @@ function extraNetworksControlRefreshOnClick(event, tabname_full) {
|
|||
* event handler that refreshes the page. So what this function here does
|
||||
* is it manually raises a `click` event on that button.
|
||||
*/
|
||||
// reset states
|
||||
initialUiOptionsLoaded.state = false;
|
||||
|
||||
// We want to reset all tabs lists on refresh click so that the viewing area
|
||||
// shows that it is loading new data.
|
||||
for (tab of Object.values(extra_networks_tabs)) {
|
||||
|
@ -721,6 +793,17 @@ function extraNetworksTreeDirectoryOnClick(event, btn, tabname_full) {
|
|||
} else {
|
||||
// user clicked anywhere else on the row
|
||||
tab.tree_list.onRowSelected(div_id, btn);
|
||||
// Select the corresponding dirs view button.
|
||||
if ("selected" in btn.dataset) {
|
||||
tab.container_elem.querySelectorAll(".extra-network-dirs-view-button").forEach(elem => {
|
||||
if (elem.textContent.trim() === btn.dataset.path) {
|
||||
elem.dataset.selected = "";
|
||||
} else {
|
||||
delete elem.dataset.selected;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
tab.updateSearch("selected" in btn.dataset ? btn.dataset.path : "");
|
||||
}
|
||||
}
|
||||
|
@ -839,13 +922,17 @@ async function extraNetworksSetupTab(tabname) {
|
|||
}
|
||||
|
||||
async function extraNetworksSetup() {
|
||||
extra_networks_setup_complete.state = false;
|
||||
await waitForBool(initialUiOptionsLoaded);
|
||||
|
||||
await Promise.all([
|
||||
extraNetworksSetupTab('txt2img'),
|
||||
extraNetworksSetupTab('img2img'),
|
||||
]);
|
||||
|
||||
extraNetworksSetupEventDelegators();
|
||||
|
||||
extra_networks_setup_complete.state = true;
|
||||
}
|
||||
|
||||
onUiLoaded(extraNetworksSetup);
|
||||
|
|
|
@ -10,47 +10,6 @@ class NotImplementedError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
const LRU_MAX_ITEMS = 250;
|
||||
class LRU {
|
||||
constructor(max = LRU_MAX_ITEMS) {
|
||||
this.max = max;
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
get(key) {
|
||||
key = String(key);
|
||||
let item = this.cache.get(key);
|
||||
if (!isNullOrUndefined(item)) {
|
||||
this.cache.delete(key);
|
||||
this.cache.set(key, item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
set(key, val) {
|
||||
key = String(key);
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.delete(key);
|
||||
} else if (this.cache.size === this.max) {
|
||||
this.cache.delete(this.first());
|
||||
}
|
||||
this.cache.set(key, val);
|
||||
}
|
||||
|
||||
has(key) {
|
||||
key = String(key);
|
||||
return this.cache.has(key);
|
||||
}
|
||||
|
||||
first() {
|
||||
return this.cache.keys().next().value;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtraNetworksClusterize extends Clusterize {
|
||||
data_obj = {};
|
||||
data_obj_keys_sorted = [];
|
||||
|
@ -73,7 +32,6 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
super(args);
|
||||
this.tabname = getValueThrowError(args, "tabname");
|
||||
this.extra_networks_tabname = getValueThrowError(args, "extra_networks_tabname");
|
||||
this.lru = new LRU();
|
||||
}
|
||||
|
||||
sortByDivId(data) {
|
||||
|
@ -86,7 +44,7 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
// can't use super class' sort since it relies on setup being run first.
|
||||
// but we do need to make sure to sort the new data before continuing.
|
||||
await this.setMaxItems(Object.keys(this.data_obj).length);
|
||||
await this.options.callbacks.sortData.call(this);
|
||||
await this.options.callbacks.sortData();
|
||||
}
|
||||
|
||||
async setup() {
|
||||
|
@ -94,6 +52,12 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.lru instanceof LRUCache) {
|
||||
this.lru.clear();
|
||||
} else {
|
||||
this.lru = new LRUCache();
|
||||
}
|
||||
|
||||
await this.reinitData();
|
||||
|
||||
if (this.enabled) {
|
||||
|
@ -101,6 +65,23 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.lru instanceof LRUCache) {
|
||||
this.lru.destroy();
|
||||
this.lru = null;
|
||||
}
|
||||
this.data_obj = {};
|
||||
this.data_obj_keys_sorted = [];
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.data_obj = {};
|
||||
this.data_obj_keys_sorted = [];
|
||||
this.lru.clear();
|
||||
super.clear();
|
||||
}
|
||||
|
||||
async load(force_init_data) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
|
@ -115,13 +96,6 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.data_obj = {};
|
||||
this.data_obj_keys_sorted = [];
|
||||
this.lru.clear();
|
||||
super.clear();
|
||||
}
|
||||
|
||||
setSortMode(sort_mode_str) {
|
||||
if (this.sort_mode_str === sort_mode_str) {
|
||||
return;
|
||||
|
@ -173,6 +147,9 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
}
|
||||
|
||||
async fetchDivIds(div_ids) {
|
||||
if (isNullOrUndefinedLogError(this.lru)) {
|
||||
return [];
|
||||
}
|
||||
const lru_keys = Array.from(this.lru.cache.keys());
|
||||
const cached_div_ids = div_ids.filter(x => lru_keys.includes(x));
|
||||
const missing_div_ids = div_ids.filter(x => !lru_keys.includes(x));
|
||||
|
@ -182,7 +159,7 @@ class ExtraNetworksClusterize extends Clusterize {
|
|||
if (missing_div_ids.length !== 0) {
|
||||
Object.assign(
|
||||
data,
|
||||
await this.options.callbacks.fetchData.call(this, missing_div_ids),
|
||||
await this.options.callbacks.fetchData(missing_div_ids),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -270,15 +247,17 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize {
|
|||
override = override === true;
|
||||
|
||||
if (!isNullOrUndefined(this.selected_div_id) && div_id !== this.selected_div_id) {
|
||||
const prev_elem = this.content_elem.querySelector(`[data-div-id="${this.selected_div_id}"]`);
|
||||
// deselect current selection if exists on page
|
||||
const prev_elem = this.content_elem.querySelector(`div[data-div-id="${this.selected_div_id}"]`);
|
||||
if (isElement(prev_elem)) {
|
||||
delete prev_elem.dataset.selected;
|
||||
this.data_obj[prev_elem.dataset.divId].selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
elem.toggleAttribute("data-selected");
|
||||
this.selected_div_id = "selected" in elem.dataset ? div_id : null;
|
||||
this.data_obj[elem.dataset.divId].selected = "selected" in elem.dataset;
|
||||
}
|
||||
|
||||
getMaxRowWidth() {
|
||||
|
@ -344,7 +323,7 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize {
|
|||
expanded: bool,
|
||||
}
|
||||
*/
|
||||
this.data_obj = await this.options.callbacks.initData.call(this);
|
||||
this.data_obj = await this.options.callbacks.initData();
|
||||
}
|
||||
|
||||
async fetchData(idx_start, idx_end) {
|
||||
|
@ -429,7 +408,29 @@ class ExtraNetworksClusterizeCardsList extends ExtraNetworksClusterize {
|
|||
sort_<mode>: string, (for various sort modes)
|
||||
}
|
||||
*/
|
||||
this.data_obj = await this.options.callbacks.initData.call(this);
|
||||
this.data_obj = await this.options.callbacks.initData();
|
||||
}
|
||||
|
||||
updateCard(name, new_html) {
|
||||
const parsed_html = htmlStringToElement(new_html);
|
||||
|
||||
const old_card = this.content_elem.querySelector(`.card[data-name="${name}"]`);
|
||||
if (!isElementLogError(old_card)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const div_id = old_card.dataset.divId;
|
||||
|
||||
// replace new html's data attributes with the current ones
|
||||
for (const [k, v] of Object.entries(old_card.dataset)) {
|
||||
parsed_html.dataset[k] = v;
|
||||
}
|
||||
|
||||
// replace the element in DOM with our new element
|
||||
old_card.replaceWith(parsed_html);
|
||||
|
||||
// update the internal cache with the new html
|
||||
this.lru.set(String(div_id), new_html);
|
||||
}
|
||||
|
||||
async fetchData(idx_start, idx_end) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
const LRU_CACHE_MAX_ITEMS_DEFAULT = 250;
|
||||
class LRUCache {
|
||||
/** Least Recently Used cache implementation.
|
||||
*
|
||||
* Source: https://stackoverflow.com/a/46432113
|
||||
*/
|
||||
constructor(max = LRU_CACHE_MAX_ITEMS_DEFAULT) {
|
||||
isNumberThrowError(max);
|
||||
|
||||
this.max = max;
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear();
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.cache.size;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
let item = this.cache.get(key);
|
||||
if (!isNullOrUndefined(item)) {
|
||||
this.cache.delete(key);
|
||||
this.cache.set(key, item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
set(key, val) {
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.delete(key);
|
||||
} else if (this.cache.size === this.max) {
|
||||
this.cache.delete(this.first());
|
||||
}
|
||||
this.cache.set(key, val);
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.cache.has(key);
|
||||
}
|
||||
|
||||
first() {
|
||||
return this.cache.keys().next().value;
|
||||
}
|
||||
}
|
|
@ -71,7 +71,6 @@ class TreeListItem(ListItem):
|
|||
Attributes:
|
||||
visible [bool]: Whether the item should be shown in the list.
|
||||
expanded [bool]: Whether the item children should be shown.
|
||||
selected [bool]: Whether the item is selected by user.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -79,7 +78,6 @@ class TreeListItem(ListItem):
|
|||
self.node: Optional[DirectoryTreeNode] = None
|
||||
self.visible: bool = False
|
||||
self.expanded: bool = False
|
||||
self.selected: bool = False
|
||||
|
||||
|
||||
class DirectoryTreeNode:
|
||||
|
@ -241,8 +239,23 @@ def init_cards_data(tabname: str = "", extra_networks_tabname: str = "") -> JSON
|
|||
|
||||
data = page.generate_cards_view_data(tabname)
|
||||
|
||||
if data is None:
|
||||
return JSONResponse({}, status_code=503)
|
||||
|
||||
return JSONResponse(data, status_code=200)
|
||||
|
||||
def page_is_ready(extra_networks_tabname: str = "") -> JSONResponse:
|
||||
page = get_page_by_name(extra_networks_tabname)
|
||||
|
||||
try:
|
||||
items_list = [x for x in page.list_items()]
|
||||
if len(page.items) == len(items_list):
|
||||
return JSONResponse({}, status_code=200)
|
||||
|
||||
return JSONResponse({"error": "page not ready"}, status_code=503)
|
||||
except Exception as exc:
|
||||
return JSONResponse({"error": str(exc)}, status_code=500)
|
||||
|
||||
def get_metadata(extra_networks_tabname: str = "", item: str = "") -> JSONResponse:
|
||||
try:
|
||||
page = get_page_by_name(extra_networks_tabname)
|
||||
|
@ -287,6 +300,7 @@ def add_pages_to_demo(app):
|
|||
app.add_api_route("/sd_extra_networks/init-cards-data", init_cards_data, methods=["GET"])
|
||||
app.add_api_route("/sd_extra_networks/fetch-tree-data", fetch_tree_data, methods=["GET"])
|
||||
app.add_api_route("/sd_extra_networks/fetch-cards-data", fetch_cards_data, methods=["GET"])
|
||||
app.add_api_route("/sd_extra_networks/page-is-ready", page_is_ready, methods=["GET"])
|
||||
|
||||
def quote_js(s):
|
||||
s = s.replace('\\', '\\\\')
|
||||
|
@ -593,8 +607,8 @@ class ExtraNetworksPage:
|
|||
|
||||
show_files = shared.opts.extra_networks_tree_view_show_files is True
|
||||
for div_id, node in div_id_to_node.items():
|
||||
self.tree[div_id] = TreeListItem(div_id, "")
|
||||
self.tree[div_id].node = node
|
||||
tree_item = TreeListItem(div_id, "")
|
||||
tree_item.node = node
|
||||
parent_id = None
|
||||
if node.parent is not None:
|
||||
parent_id = path_to_div_id.get(node.parent.abspath, None)
|
||||
|
@ -603,8 +617,8 @@ class ExtraNetworksPage:
|
|||
if show_files:
|
||||
dir_is_empty = node.children == []
|
||||
else:
|
||||
dir_is_empty = not any(x.item.is_dir for x in node.children)
|
||||
self.tree[div_id].html = self.build_tree_html_dict_row(
|
||||
dir_is_empty = all(not x.is_dir for x in node.children)
|
||||
tree_item.html = self.build_tree_html_dict_row(
|
||||
tabname=tabname,
|
||||
label=os.path.basename(node.abspath),
|
||||
btn_type="dir",
|
||||
|
@ -619,6 +633,7 @@ class ExtraNetworksPage:
|
|||
"data-expanded": node.parent is None, # Expand root directories
|
||||
},
|
||||
)
|
||||
self.tree[div_id] = tree_item
|
||||
else: # file
|
||||
if not show_files:
|
||||
# Don't add file if files are disabled in the options.
|
||||
|
@ -629,7 +644,7 @@ class ExtraNetworksPage:
|
|||
onclick = html.escape(f"extraNetworksCardOnClick(event, '{tabname}_{self.extra_networks_tabname}');")
|
||||
|
||||
item_name = node.item.get("name", "").strip()
|
||||
self.tree[div_id].html = self.build_tree_html_dict_row(
|
||||
tree_item.html = self.build_tree_html_dict_row(
|
||||
tabname=tabname,
|
||||
label=html.escape(item_name),
|
||||
btn_type="file",
|
||||
|
@ -648,6 +663,7 @@ class ExtraNetworksPage:
|
|||
item=node.item,
|
||||
onclick_extra=onclick,
|
||||
)
|
||||
self.tree[div_id] = tree_item
|
||||
|
||||
res = {}
|
||||
|
||||
|
@ -740,7 +756,7 @@ class ExtraNetworksPage:
|
|||
if not os.path.exists(abspath):
|
||||
continue
|
||||
self.tree_roots[abspath] = DirectoryTreeNode(os.path.dirname(abspath), abspath, None)
|
||||
self.tree_roots[abspath].build(tree_items)
|
||||
self.tree_roots[abspath].build(tree_items if shared.opts.extra_networks_tree_view_show_files else {})
|
||||
|
||||
# Generate the html for displaying directory buttons
|
||||
dirs_html = self.create_dirs_view_html(tabname)
|
||||
|
|
|
@ -16,14 +16,7 @@ def javascript_html():
|
|||
script_js = os.path.join(script_path, "script.js")
|
||||
head += f'<script type="text/javascript" src="{webpath(script_js)}"></script>\n'
|
||||
|
||||
# We want the utils.js script to be imported first since it can be used by multiple other
|
||||
# scripts. This resolves dependency issues caused by html <script> ordering.
|
||||
js_scripts = scripts.list_scripts("javascript", ".js")
|
||||
js_utils_script = [x for x in js_scripts if x.filename == "utils.js"][0]
|
||||
head += f'<script type="text/javascript" src="{webpath(js_utils_script.path)}"></script>\n'
|
||||
|
||||
# Now add all remaining .js scripts excluding the utils.js file.
|
||||
for script in [x for x in js_scripts if x.filename != "utils.js"]:
|
||||
for script in scripts.list_scripts("javascript", ".js"):
|
||||
head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
|
||||
|
||||
for script in scripts.list_scripts("javascript", ".mjs"):
|
||||
|
|
Loading…
Reference in New Issue