30/05/2025 commit
This commit is contained in:
@ -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)
|
||||
{
|
||||
|
@ -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")">
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -64,9 +64,6 @@ class Async1Layout extends ALayout {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfull">
|
||||
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
_createFHeader() {
|
||||
|
0
VinFontApp/wwwroot/js/libs/CacheManager.js
Normal file
0
VinFontApp/wwwroot/js/libs/CacheManager.js
Normal 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;
|
||||
|
||||
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"
|
||||
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("|");
|
||||
|
||||
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,20 +944,35 @@ 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 {
|
||||
|
||||
// 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") {
|
||||
|
||||
} else {
|
||||
const layout = {
|
||||
Content: obj.Content,
|
||||
Scripts: obj.Scripts
|
||||
@ -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) {
|
||||
|
Reference in New Issue
Block a user