From db6f6a9b2b930b042f92340ab26d1d6d6dd9b9b3 Mon Sep 17 00:00:00 2001 From: RTa-technology Date: Wed, 1 Nov 2023 01:23:15 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E7=B0=A1=E5=8D=98=E3=81=AA=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 18 +++++++++- src/script/include.ts | 82 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/script/include.ts diff --git a/src/main.ts b/src/main.ts index a7c8a19..fa27299 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,8 @@ import css from './css/wikidot.css'; import init from './css/init.css'; import collapsible from './css/collapsible.css'; +import { TextWikiParseInclude, Wiki } from './script/include'; + import { handleDOMContentLoaded @@ -15,4 +17,18 @@ document.querySelector("head > style#innercss")!.innerHTML = css; document.querySelector("head > style#collapsible")!.innerHTML = collapsible; document.querySelector("head > style#init")!.innerHTML = init; // Event listeners... -document.addEventListener('DOMContentLoaded', handleDOMContentLoaded); \ No newline at end of file +document.addEventListener('DOMContentLoaded', handleDOMContentLoaded); + + + + + +const wiki: Wiki = { + source: '\n[[include self]]\n', + vars: {} +}; + +console.log("Source before parsing: \n", wiki.source); // 'Some text before \n[[include some-page]]\n and some text after.' を出力 +const parser = new TextWikiParseInclude(wiki); +parser.parse(); +console.log(wiki.source); // 'Some text before This is some page content. and some text after.' を出力 \ No newline at end of file diff --git a/src/script/include.ts b/src/script/include.ts new file mode 100644 index 0000000..9985167 --- /dev/null +++ b/src/script/include.ts @@ -0,0 +1,82 @@ +interface Wiki { + source: string; + vars: Record; +} + +class Page { + constructor(private pageId: number, private source: string) { } + + getPageId(): number { + return this.pageId; + } + + getSource(): string { + return this.source; + } +} + +export class TextWikiParseInclude { + private conf = { base: '/path/to/scripts/' }; + private regex = /^\[\[include ([a-zA-Z0-9\s\-:]+?)(\s+.*?)?(?:\]\])$/ims; + + constructor(private wiki: Wiki) { } + + parse(): void { + let level = 0; + let oldSource; + do { + oldSource = this.wiki.source; + // console.log("this.wiki.source.match(this.regex)", this.wiki.source.match(this.regex)); + + this.wiki.source = this.wiki.source.replace(this.regex, (match, ...matches) => this.process(matches)); + level++; + } while (oldSource !== this.wiki.source && level < 5); + } + + private process(matches: string[]): string { + const [pageName, subs] = matches; + const cleanedPageName = this.toUnixName(pageName.trim()); + + const page = this.getPageFromDb(cleanedPageName); + // console.log("page", page); + + if (!page) { + const output = `\n\n[[div class="error-block"]]\nPage to be included ${cleanedPageName} cannot be found!\n[[/div]]\n\n`; + this.wiki.vars.inclusionsNotExist = { ...this.wiki.vars.inclusionsNotExist, [cleanedPageName]: cleanedPageName }; + return output; + } + + let output = page.getSource(); + // console.log("output", output); + + if (subs) { + const subsArray = subs.split('|'); + for (const sub of subsArray) { + const [varName, value] = sub.split('=').map(s => s.trim()); + if (value && varName && /^[a-z0-9\-_]+$/i.test(varName)) { + output = output.replace(new RegExp(`\{\$${varName}\}`, 'g'), value); + } + } + } + + this.wiki.vars.inclusions = { ...this.wiki.vars.inclusions, [page.getPageId()]: page.getPageId() }; + return `${output}`; + } + + private toUnixName(name: string): string { + return name.replace(/\s+/g, '_').toLowerCase(); + } + + private getPageFromDb(pageName: string): Page | null { + if (pageName === 'some-page') { + return new Page(1, 'This is some page content.'); + } + if (pageName === 'some-inc-page') { + return new Page(2, '[[include some-page]]\n[[include some-page]]\n[[include some-page]]\nthis is 3times inc page content.'); + } + if (pageName === 'self') { + return new Page(1, '[[include self]]'); + } + return null; + } +} \ No newline at end of file From 3a6204e1e57e649202af1514ab1aa126cc79953a Mon Sep 17 00:00:00 2001 From: RTa-technology Date: Wed, 1 Nov 2023 02:10:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E4=B8=80=E6=97=A6=E3=81=AE=E5=8B=95?= =?UTF-8?q?=E4=BD=9C=E7=A2=BA=E8=AA=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 22 +++++++++-------- src/script/eventHandlers.ts | 17 +++++++++++++ src/script/include.ts | 48 ++++++++++++++++++------------------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/main.ts b/src/main.ts index fa27299..9b8266f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,7 @@ import css from './css/wikidot.css'; import init from './css/init.css'; import collapsible from './css/collapsible.css'; -import { TextWikiParseInclude, Wiki } from './script/include'; +import { TextWikiParseInclude } from './script/include'; import { @@ -22,13 +22,15 @@ document.addEventListener('DOMContentLoaded', handleDOMContentLoaded); +// const wiki: Wiki = { +// source: '[[include component:topsubtitle |TITLE=test]]', +// vars: {} +// }; -const wiki: Wiki = { - source: '\n[[include self]]\n', - vars: {} -}; - -console.log("Source before parsing: \n", wiki.source); // 'Some text before \n[[include some-page]]\n and some text after.' を出力 -const parser = new TextWikiParseInclude(wiki); -parser.parse(); -console.log(wiki.source); // 'Some text before This is some page content. and some text after.' を出力 \ No newline at end of file +// console.log("Source before parsing: \n", wiki.source); +// const parser = new TextWikiParseInclude(wiki); +// parser.parse().then(() => { +// console.log("Source after parsing: \n", wiki.source); +// }).catch(error => { +// console.error("Parsing failed with error: ", error); +// }); diff --git a/src/script/eventHandlers.ts b/src/script/eventHandlers.ts index 675db71..5f897fa 100644 --- a/src/script/eventHandlers.ts +++ b/src/script/eventHandlers.ts @@ -7,6 +7,7 @@ import { generateShortId, getOrCreateUserShortId, getCurrentPageShortId, encryptSha256, setCookie, getCookie } from './utils'; +import { TextWikiParseInclude } from './include'; import { @@ -159,6 +160,22 @@ const handleEditpageInput = debounce((event) => { const storageKey = shortid ? `FtmlStorage[${shortid}]` : 'FtmlStorage'; localStorage.setItem(storageKey, JSON.stringify(FtmlStorageItem)); + // const WPInc = { + // source: value, + // vars: {} + // }; + // const parser = new TextWikiParseInclude(WPInc); + // parser.parse().then(() => { + // console.log("Source after parsing: \n", WPInc.source); + // ftml.postMessage({ value: WPInc.source, type }); + // }).catch(error => { + // console.error("Parsing failed with error: ", error); + // }); + + // [WIP] includeの処理を行う + // include元のソースを保持する。(GASの実行制限を考慮して、include元のソースを保持する必要がある) + // include元を取得するためのボタンを用意する(5秒おきに取得するのは非効率的) + ftml.postMessage({ value, type }); }, 1000); diff --git a/src/script/include.ts b/src/script/include.ts index 9985167..80e9c2d 100644 --- a/src/script/include.ts +++ b/src/script/include.ts @@ -1,12 +1,14 @@ +import { getDataFromGAS } from "./helper"; + interface Wiki { source: string; vars: Record; } class Page { - constructor(private pageId: number, private source: string) { } + constructor(private pageId: string, private source: string) { } - getPageId(): number { + getPageId(): string { return this.pageId; } @@ -21,25 +23,27 @@ export class TextWikiParseInclude { constructor(private wiki: Wiki) { } - parse(): void { + async parse(): Promise { let level = 0; let oldSource; do { oldSource = this.wiki.source; - // console.log("this.wiki.source.match(this.regex)", this.wiki.source.match(this.regex)); - - this.wiki.source = this.wiki.source.replace(this.regex, (match, ...matches) => this.process(matches)); + const matches = this.regex.exec(this.wiki.source); + if (matches) { + const output = await this.process(matches.slice(1)); + this.wiki.source = this.wiki.source.replace(this.regex, output); + } level++; - } while (oldSource !== this.wiki.source && level < 5); + } while (oldSource !== this.wiki.source && level <= 10); } - private process(matches: string[]): string { + + private async process(matches: string[]): Promise { const [pageName, subs] = matches; const cleanedPageName = this.toUnixName(pageName.trim()); - const page = this.getPageFromDb(cleanedPageName); - // console.log("page", page); + const page = await this.getPageFromDb(cleanedPageName); if (!page) { const output = `\n\n[[div class="error-block"]]\nPage to be included ${cleanedPageName} cannot be found!\n[[/div]]\n\n`; this.wiki.vars.inclusionsNotExist = { ...this.wiki.vars.inclusionsNotExist, [cleanedPageName]: cleanedPageName }; @@ -47,14 +51,12 @@ export class TextWikiParseInclude { } let output = page.getSource(); - // console.log("output", output); - if (subs) { const subsArray = subs.split('|'); for (const sub of subsArray) { const [varName, value] = sub.split('=').map(s => s.trim()); if (value && varName && /^[a-z0-9\-_]+$/i.test(varName)) { - output = output.replace(new RegExp(`\{\$${varName}\}`, 'g'), value); + output = output.replace(new RegExp(`\{\\$${varName}\}`, 'g'), value); } } } @@ -64,19 +66,17 @@ export class TextWikiParseInclude { } private toUnixName(name: string): string { - return name.replace(/\s+/g, '_').toLowerCase(); + // return name.replace(/\s+/g, '_').toLowerCase(); + return name.replace(/\s+/g, '_'); } - private getPageFromDb(pageName: string): Page | null { - if (pageName === 'some-page') { - return new Page(1, 'This is some page content.'); - } - if (pageName === 'some-inc-page') { - return new Page(2, '[[include some-page]]\n[[include some-page]]\n[[include some-page]]\nthis is 3times inc page content.'); - } - if (pageName === 'self') { - return new Page(1, '[[include self]]'); + private async getPageFromDb(pageName: string): Promise { + try { + const data = await getDataFromGAS(pageName); + return new Page(data.data.shortId, data.data.source); + } catch (error) { + console.error('Failed to get page from DB:', error); + return null; } - return null; } } \ No newline at end of file From 0c33c92027a7d6a24c168689d32ca16f9bea55f4 Mon Sep 17 00:00:00 2001 From: RTa-technology Date: Wed, 1 Nov 2023 14:24:58 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20include=E3=81=AE=E5=B0=8E?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 18 -------- src/script/eventHandlers.ts | 27 +++++++++-- src/script/include.ts | 91 ++++++++++++++++++++++++++++++++++--- 3 files changed, 108 insertions(+), 28 deletions(-) diff --git a/src/main.ts b/src/main.ts index 9b8266f..310c574 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,8 +5,6 @@ import css from './css/wikidot.css'; import init from './css/init.css'; import collapsible from './css/collapsible.css'; -import { TextWikiParseInclude } from './script/include'; - import { handleDOMContentLoaded @@ -18,19 +16,3 @@ document.querySelector("head > style#collapsible")!.innerHTML = collapsible; document.querySelector("head > style#init")!.innerHTML = init; // Event listeners... document.addEventListener('DOMContentLoaded', handleDOMContentLoaded); - - - - -// const wiki: Wiki = { -// source: '[[include component:topsubtitle |TITLE=test]]', -// vars: {} -// }; - -// console.log("Source before parsing: \n", wiki.source); -// const parser = new TextWikiParseInclude(wiki); -// parser.parse().then(() => { -// console.log("Source after parsing: \n", wiki.source); -// }).catch(error => { -// console.error("Parsing failed with error: ", error); -// }); diff --git a/src/script/eventHandlers.ts b/src/script/eventHandlers.ts index 5f897fa..5e9faf9 100644 --- a/src/script/eventHandlers.ts +++ b/src/script/eventHandlers.ts @@ -7,7 +7,7 @@ import { generateShortId, getOrCreateUserShortId, getCurrentPageShortId, encryptSha256, setCookie, getCookie } from './utils'; -import { TextWikiParseInclude } from './include'; +import { TextWikiParseInclude } from "./include"; import { @@ -174,9 +174,28 @@ const handleEditpageInput = debounce((event) => { // [WIP] includeの処理を行う // include元のソースを保持する。(GASの実行制限を考慮して、include元のソースを保持する必要がある) - // include元を取得するためのボタンを用意する(5秒おきに取得するのは非効率的) + // include元のページを配列で持っておいて、その中身に変更があった場合は、include元のソースを増えたものだけ取得しに行く - ftml.postMessage({ value, type }); + + const wiki = { + source: editpageField.value, + vars: {} + }; + + // console.log("Source before parsing: \n", wiki.source); + const parser = new TextWikiParseInclude(wiki); + + // onEditでthis.wiki.sourceを更新する。editpageFieldが更新されたらonEditにeventを渡す。 + // editpageField.addEventListener('input', parser.onEdit.bind(parser)); + parser.onEdit(event).then(() => { + // console.log("Source after parsing: \n", wiki.source); + ftml.postMessage({ value: wiki.source, type }); + } + ).catch(error => { + console.error("Parsing failed with error: ", error); + }); + + // ftml.postMessage({ value, type }); }, 1000); @@ -298,7 +317,7 @@ const handleShareButtonClick = async () => { } - + console.debug('Sending data to GAS:', dataToSend); diff --git a/src/script/include.ts b/src/script/include.ts index 80e9c2d..2eaada4 100644 --- a/src/script/include.ts +++ b/src/script/include.ts @@ -20,8 +20,11 @@ class Page { export class TextWikiParseInclude { private conf = { base: '/path/to/scripts/' }; private regex = /^\[\[include ([a-zA-Z0-9\s\-:]+?)(\s+.*?)?(?:\]\])$/ims; + private includedPages: string[] = []; - constructor(private wiki: Wiki) { } + constructor(private wiki: Wiki) { + this.updateIncludedPages(); + } async parse(): Promise { let level = 0; @@ -35,15 +38,21 @@ export class TextWikiParseInclude { } level++; } while (oldSource !== this.wiki.source && level <= 10); + this.saveIncludedPagesToLocalStorage(); } - private async process(matches: string[]): Promise { const [pageName, subs] = matches; const cleanedPageName = this.toUnixName(pageName.trim()); + // const page = await this.getPageFromDb(cleanedPageName); + const cachedPages = this.getCachedPages(); + // console.log('cachedPages:', cachedPages); + // cachedPagesにpageNameがあれば、pageIdとsourceを取得 + // なければ、GASから取得 + const cachedPage = cachedPages[pageName]; + const page = cachedPage ? new Page(cachedPage.pageId, cachedPage.source) : await this.getPageFromDb(cleanedPageName); - const page = await this.getPageFromDb(cleanedPageName); if (!page) { const output = `\n\n[[div class="error-block"]]\nPage to be included ${cleanedPageName} cannot be found!\n[[/div]]\n\n`; this.wiki.vars.inclusionsNotExist = { ...this.wiki.vars.inclusionsNotExist, [cleanedPageName]: cleanedPageName }; @@ -51,7 +60,7 @@ export class TextWikiParseInclude { } let output = page.getSource(); - if (subs) { + if (subs && output) { const subsArray = subs.split('|'); for (const sub of subsArray) { const [varName, value] = sub.split('=').map(s => s.trim()); @@ -66,7 +75,6 @@ export class TextWikiParseInclude { } private toUnixName(name: string): string { - // return name.replace(/\s+/g, '_').toLowerCase(); return name.replace(/\s+/g, '_'); } @@ -79,4 +87,75 @@ export class TextWikiParseInclude { return null; } } -} \ No newline at end of file + + private updateIncludedPages() { + const regex = /\[\[include ([a-zA-Z0-9\s\-:]+?)(\s+.*?)?\]\]/g; + let match; + while ((match = regex.exec(this.wiki.source)) !== null) { + // this.includedPages.push(match[1].trim()); //重複を削除したい + const pageName = match[1].trim(); + if (!this.includedPages.includes(pageName)) { + this.includedPages.push(pageName); + } + } + } + + private saveIncludedPagesToLocalStorage() { + localStorage.setItem('includedPages', JSON.stringify(this.includedPages)); + } + + static loadIncludedPagesFromLocalStorage(): string[] { + const savedData = localStorage.getItem('includedPages'); + if (savedData) { + const savedPages = JSON.parse(savedData); + if (Array.isArray(savedPages)) { + return savedPages; + } + } + return []; + } + + async onEdit(event: Event) { + const source = (event.target as HTMLTextAreaElement).value; + // console.log('Source changed:', source); + this.wiki.source = source; // クラス内のwiki.sourceを更新 + this.updateIncludedPages(); // 引数なしで呼び出し + await this.checkForNewIncludes(); + this.saveIncludedPagesToLocalStorage(); + + // paese()を呼び出し、wiki.sourceを更新 + await this.parse(); + } + + private async checkForNewIncludes() { + const cachedPages = this.getCachedPages(); + const newIncludes = this.includedPages.filter(page => !cachedPages[page]); + if (newIncludes.length > 0) { + await this.fetchPagesFromGAS(newIncludes); + } + } + + private async fetchPagesFromGAS(pages: string[]) { + for (const page of pages) { + try { + const data = await getDataFromGAS(page); + this.cachePage(page, data); + } catch (error) { + console.error('Failed to fetch page:', page, error); + } + } + } + + private cachePage(pageName: string, data: any) { + const cachedPages = this.getCachedPages(); + cachedPages[pageName] = { pageId: data.data.shortId, source: data.data.source }; // オブジェクトをそのまま保存 + localStorage.setItem('cachedPages', JSON.stringify(cachedPages)); // オブジェクトを文字列に変換して保存 + } + + private getCachedPages(): Record { + const savedData = localStorage.getItem('cachedPages'); + return savedData ? JSON.parse(savedData) : {}; + } + +} +