30/05/2025 commit

This commit is contained in:
2025-05-30 13:04:37 +07:00
parent c75d9b72cf
commit 23f5f51329
7 changed files with 248 additions and 64 deletions

View File

@ -5,7 +5,7 @@ namespace VinFontApp.Controllers
{
public class TypeFontsController : Controller
{
[PageInfor("2001", "Type Font")]
[PageInfor("2001", "Type Font", "/TypeFont/*")]
[Layout(LayoutOptions.Custom, "Async1")]
public async Task<IActionResult> Index(string? id)
{

View File

@ -7,7 +7,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta name="idPage" content="@ViewData["PageID"]" pageName="Vin Font" layName="@ViewData["LayoutName"]" />
<meta name="idPage" content="@ViewData["PageID"]" pageName="Vin Font" layName="@ViewData["LayoutName"]" flexPage="@ViewData["FlexPage"]"/>
<title></title>
<link rel="icon" type="image/x-icon" href="@Url.AbsoluteContent("~/images/logo/icon.png")" />
<link rel="preload" as="script" href="@Url.AbsoluteContent("~/js/libs/js-core.js")">

View File

@ -49,9 +49,9 @@
</div>
</div>
</div>
<div class="c_filter c-3 c-m-2 c-x-1">
<button class="btn btn-second d-f j-c-center a-i-center"><i class="atg a-2x atg-filter-list-light mr-3"></i> Filter</button>
</div>
<div class="c_filter c-3 c-m-2 c-x-1">
<button class="btn btn-second d-f j-c-center a-i-center"><i class="atg a-2x atg-filter-list-light mr-3"></i> Filter</button>
</div>
</div>
</div>
</div>

View File

@ -68,7 +68,7 @@
<ItemGroup>
<None Update="Keys\app\VFApp.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Keys\db\Certificate.pfx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

View File

@ -64,9 +64,6 @@ class Async1Layout extends ALayout {
</div>
</div>
<div class="cfull">
</div>
</div>`;
}
_createFHeader() {

View File

@ -104,13 +104,18 @@ window.getOS = function () {
return os;
}
window.GetAbsoluteURL = function (relativeURL) {
window.GetAbsoluteURL = function(relativeURL) {
if (relativeURL.startsWith(window.location.origin)) {
return relativeURL;
}
return window.location.origin + relativeURL;
}
window.getPathFromUrl = function(url) {
const parsed = new URL(url);
return parsed.pathname + parsed.search + parsed.hash;
}
window.GetEventType = function () {
if (isTouchAvailable) {
return "touchend";
@ -656,15 +661,23 @@ class AApp extends window.AObject {
this.isRedirectPage = true;
this.callLoadPage(window.GetAbsoluteURL(t));
}
callLoadPage(url) {
this.cachePage.get(url, ((result) => {
if (result) {
this.setContentPage(result, url); // Set content page từ cache
} else {
console.log("connect new");
this.getPage(url); // Load mới nếu chưa có trong cache
}
}).bind(this));
callLoadPage(url, flex = false) {
if (flex) {
url = window.getPathFromUrl(url);
this.cachePage.searchFlexPage(url, ((result) => {
if (result) {
}
}).bind(this));
} else {
this.cachePage.get(url, ((result) => {
if (result) {
this.setContentPage(result, url); // Set content page từ cache
} else {
this.getPage(url); // Load mới nếu chưa có trong cache
}
}).bind(this));
}
}
loadContentPage(content) {
const tpl = document.createElement('template');
@ -721,6 +734,7 @@ class AApp extends window.AObject {
title: title,
idPage: idPage,
lName: o.LayoutName,
flexPageID: o.FlexPageId,
doc: doc,
dynamicF: dF ? dF.getAttribute("dynamic") : null
};
@ -827,8 +841,12 @@ class CacheManager {
this.layoutMap = new Map();
this.readyCallbacks = [];
this.isReady = false;
this.flexPages = new FlexPageTrie();
this.countFP = 0;
this._loadIndex();
window.addEventListener("beforeunload", (function (e) {
}).bind(this));
}
@ -845,25 +863,29 @@ class CacheManager {
}
_loadIndex() {
this.storage.getAllKeys(((keys) => {
let pending = 0;
keys.forEach(((key) => {
if (!key.startsWith(`${this.storage.prefix}|`)) return;
this.storage.getAllKeys(((keys, type) => {
if (type == "cache") {
let pending = 0;
keys.forEach(((key) => {
if (!key.startsWith(`${this.storage.prefix}|`)) return;
const shortKey = key.replace(`${this.storage.prefix}|`, "");
const [type, id] = shortKey.split("|");
const shortKey = key.replace(`${this.storage.prefix}|`, "");
const [type, id] = shortKey.split("|");
if (type === "pI") {
pending++;
this.storage.get(key, (data) => {
if (data) this.pageMap.set(id, data);
if (--pending === 0) this._finishInit();
}, true);
} else if (type === "layout") {
this.layoutMap.set(id, true);
}
}).bind(this));
if (pending === 0) this._finishInit(); // Trường hợp không có key nào là "pI"
if (type === "pI") {
pending++;
this.storage.get(key, (data) => {
if (data) this.pageMap.set(id, data);
if (--pending === 0) this._finishInit();
}, true);
} else if (type === "layout") {
this.layoutMap.set(id, true);
}
}).bind(this));
if (pending === 0) this._finishInit(); // Trường hợp không có key nào là "pI"
} else {
}
}).bind(this));
}
@ -904,7 +926,14 @@ class CacheManager {
});
});
}
searchFlexPage(url, callback) {
this._onReady(() => {
callback(this.flexPages.search(url));
});
}
genShortID() {
return Date.now().toString(36) + Math.random().toString(36).substring(2, 5);
}
// Lưu dữ liệu page hoặc layout vào cache
set(id, obj, type = "page", callback) {
this._onReady(() => {
@ -915,19 +944,34 @@ class CacheManager {
html: obj.html,
doc: obj.doc.innerHTML
};
// Lưu dữ liệu htmlData vào storage
this.storage.set(key, htmlData, function () { });
const info = {
title: obj.title,
idPage: obj.idPage,
lName: obj.lName,
dynamicF: obj.dynamicF
};
// Lưu dữ liệu htmlData vào storage
if (obj.flexPageID !== "None") {
const idN = this.genShortID();
this.flexPages.insert(obj.flexPageID, { "id": idN, "info": info });
this.storage.set(idN, htmlData, function () { }, type = "flex");
if (this.countFP > 5) {
this.countFP = 0
}
console.log(this.flexPages);
} else {
this.storage.set(key, htmlData, function () { });
// Lưu thông tin trang vào pageMap và storage
this.pageMap.set(id, info);
this.storage.set(`pI|${id}`, info, function () { });
}
} else if (type == "flex") {
// Lưu thông tin trang vào pageMap và storage
this.pageMap.set(id, info);
this.storage.set(`pI|${id}`, info, function () { });
} else {
const layout = {
Content: obj.Content,
@ -962,7 +1006,13 @@ class CacheStorage {
_initDB() {
const req = indexedDB.open(`${this.prefix}_db`, 1);
req.onupgradeneeded = (e) => {
const store = e.target.result.createObjectStore("cache", { keyPath: "key" });
let store;
if (!e.target.result.objectStoreNames.contains("cache")) {
store = e.target.result.createObjectStore("cache", { keyPath: "key" });
}
if (!e.target.result.objectStoreNames.contains("fpCache")) {
e.target.result.createObjectStore("fpCache", { keyPath: "key" });
}
try {
store.add({ id: 1, value: "test data" });
store.delete("1");
@ -971,6 +1021,7 @@ class CacheStorage {
this.db = null;
this.useIndexedDB = false;
}
};
req.onsuccess = (event) => {
this.db = req.result;
@ -996,14 +1047,19 @@ class CacheStorage {
else this.queue.push(fn);
}
get(key, callback, f=false) {
get(key, callback, f = false, type = "cache") {
this._onReady(() => {
if (this.db) {
const tx = this.db.transaction("cache", "readonly");
const store = tx.objectStore("cache");
const req = store.get(f ? key : this._key(key));
let tx, store, req;
if (type === "flex") {
tx = this.db.transaction("fpCache", "readonly");
store = tx.objectStore("fpCache");
} else {
tx = this.db.transaction("cache", "readonly");
store = tx.objectStore("cache");
}
req = store.get(f ? key : this._key(key));
req.onsuccess = () => callback(req.result ? req.result.value : null);
req.onerror = () => callback(null);
} else {
try {
const raw = localStorage.getItem(this._key(key));
@ -1018,21 +1074,35 @@ class CacheStorage {
getAllKeys(callback) {
this._onReady(() => {
if (this.db) {
const tx = this.db.transaction("cache", "readonly");
const store = tx.objectStore("cache");
const req = store.getAllKeys();
req.onsuccess = () => {
callback(req.result);
let tx = this.db.transaction("cache", "readonly");
let store = tx.objectStore("cache");
let req = store.getAllKeys();
req.onsuccess = (evt) => {
callback(evt.target.result, "cache");
};
tx = this.db.transaction("fpCache", "readonly");
store = tx.objectStore("fpCache");
req = store.getAllKeys();
req.onsuccess = (evt) => {
callback(evt.target.result, "flex");
};
}
});
}
set(key, data, callback = () => { }) {
set(key, data, callback = () => { }, type="cache") {
this._onReady(() => {
if (this.db) {
const tx = this.db.transaction("cache", "readwrite");
const store = tx.objectStore("cache");
store.put({ key: this._key(key), value: data, ts: Date.now() });
let tx, store, req;
if (type === "flex") {
tx = this.db.transaction("fpCache", "readwrite");
store = tx.objectStore("fpCache");
store.put({ key: this._key(key), value: data, ts: Date.now() });
} else {
tx = this.db.transaction("cache", "readwrite");
store = tx.objectStore("cache");
store.put({ key: this._key(key), value: data, ts: Date.now() });
}
tx.oncomplete = () => callback(null);
tx.onerror = (e) => callback(e);
} else {
@ -1048,9 +1118,8 @@ class CacheStorage {
});
}
remove(key) {
remove(key, type = "cache") {
const fullKey = this._key(key);
localStorage.removeItem(fullKey);
if (this.db) {
try {
const tx = this.db.transaction("cache", "readwrite");
@ -1076,6 +1145,124 @@ class CacheStorage {
}
}
}
class TrieNode {
constructor() {
this.children = new Map(); // segment -> TrieNode
this.paramChild = null; // node chứa ':param'
this.wildcardChild = null; // node chứa '*'
this.paramName = null; // tên param nếu là param node
this.layout = null;
}
}
class FlexPageTrie {
constructor() {
this.root = new TrieNode();
}
insert(path, layout) {
const segments = path.split('/').filter(Boolean);
let node = this.root;
for (const seg of segments) {
if (seg === '*') {
if (!node.wildcardChild) node.wildcardChild = new TrieNode();
node = node.wildcardChild;
} else if (seg.startsWith(':')) {
if (!node.paramChild) node.paramChild = new TrieNode();
node.paramChild.paramName = seg.slice(1);
node = node.paramChild;
} else {
if (!node.children.has(seg)) node.children.set(seg, new TrieNode());
node = node.children.get(seg);
}
}
node.layout = layout;
}
search(url) {
const segments = url.split('/').filter(Boolean);
const result = this._searchRecursive(this.root, segments, 0, {});
return result || null;
}
_searchRecursive(node, segments, index, params) {
if (!node) return null;
if (index === segments.length) {
if (node.layout) {
return { layout: node.layout, params };
}
return null;
}
const seg = segments[index];
// Ưu tiên match chính xác
if (node.children.has(seg)) {
const res = this._searchRecursive(node.children.get(seg), segments, index + 1, { ...params });
if (res) return res;
}
// Match dynamic :param
if (node.paramChild) {
const newParams = { ...params };
newParams[node.paramChild.paramName] = seg;
const res = this._searchRecursive(node.paramChild, segments, index + 1, newParams);
if (res) return res;
}
// Match wildcard *
if (node.wildcardChild) {
const res = this._searchRecursive(node.wildcardChild, segments, index + 1, { ...params });
if (res) return res;
}
return null;
}
_toJSON(node) {
const obj = {
layout: node.layout,
paramName: node.paramName,
children: {},
paramChild: node.paramChild ? this._toJSON(node.paramChild) : null,
wildcardChild: node.wildcardChild ? this._toJSON(node.wildcardChild) : null
};
for (const [key, child] of node.children.entries()) {
obj.children[key] = this._toJSON(child);
}
return obj;
}
toJSON() {
return this._toJSON(this.root);
}
_fromJSON(data) {
const node = new TrieNode();
node.layout = data.layout;
node.paramName = data.paramName;
for (const key in data.children) {
node.children.set(key, this._fromJSON(data.children[key]));
}
if (data.paramChild) {
node.paramChild = this._fromJSON(data.paramChild);
}
if (data.wildcardChild) {
node.wildcardChild = this._fromJSON(data.wildcardChild);
}
return node;
}
fromJSON(data) {
this.root = this._fromJSON(data);
}
}
window.removeStopCollapsed = function () {
if (window.dropdown != null && window.dropdown.currentE != null) {