diff options
Diffstat (limited to 'ext/js')
| -rw-r--r-- | ext/js/data/anki-note-builder.js | 88 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer-frame-api.js | 16 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer-frame-main.js | 3 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer-proxy.js | 5 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer.js | 37 | 
5 files changed, 142 insertions, 7 deletions
| diff --git a/ext/js/data/anki-note-builder.js b/ext/js/data/anki-note-builder.js index 38f1eb16..b84b2eda 100644 --- a/ext/js/data/anki-note-builder.js +++ b/ext/js/data/anki-note-builder.js @@ -24,6 +24,8 @@ class AnkiNoteBuilder {      constructor() {          this._markerPattern = AnkiUtil.cloneFieldMarkerPattern(true);          this._templateRenderer = new TemplateRendererProxy(); +        this._batchedRequests = []; +        this._batchedRequestsQueued = false;      }      async createNote({ @@ -113,7 +115,7 @@ class AnkiNoteBuilder {      async _formatField(field, commonData, template, errors=null) {          return await this._stringReplaceAsync(field, this._markerPattern, async (g0, marker) => {              try { -                return await this._renderTemplate(template, marker, commonData); +                return await this._renderTemplateBatched(template, commonData, marker);              } catch (e) {                  if (Array.isArray(errors)) {                      const error = new Error(`Template render error for {${marker}}`); @@ -143,4 +145,88 @@ class AnkiNoteBuilder {      async _renderTemplate(template, marker, commonData) {          return await this._templateRenderer.render(template, {marker, commonData}, 'ankiNote');      } + +    _getBatchedTemplateGroup(template) { +        for (const item of this._batchedRequests) { +            if (item.template === template) { +                return item; +            } +        } + +        const result = {template, commonDataRequestsMap: new Map()}; +        this._batchedRequests.push(result); +        return result; +    } + +    _renderTemplateBatched(template, commonData, marker) { +        const {promise, resolve, reject} = deferPromise(); +        const {commonDataRequestsMap} = this._getBatchedTemplateGroup(template); +        let requests = commonDataRequestsMap.get(commonData); +        if (typeof requests === 'undefined') { +            requests = []; +            commonDataRequestsMap.set(commonData, requests); +        } +        requests.push({resolve, reject, marker}); +        this._runBatchedRequestsDelayed(); +        return promise; +    } + +    _runBatchedRequestsDelayed() { +        if (this._batchedRequestsQueued) { return; } +        this._batchedRequestsQueued = true; +        Promise.resolve().then(() => { +            this._batchedRequestsQueued = false; +            this._runBatchedRequests(); +        }); +    } + +    _runBatchedRequests() { +        if (this._batchedRequests.length === 0) { return; } + +        const allRequests = []; +        const items = []; +        for (const {template, commonDataRequestsMap} of this._batchedRequests) { +            const templateItems = []; +            for (const [commonData, requests] of commonDataRequestsMap.entries()) { +                const datas = []; +                for (const {marker} of requests) { +                    datas.push(marker); +                } +                allRequests.push(...requests); +                templateItems.push({type: 'ankiNote', commonData, datas}); +            } +            items.push({template, templateItems}); +        } + +        this._batchedRequests.length = 0; + +        this._resolveBatchedRequests(items, allRequests); +    } + +    async _resolveBatchedRequests(items, requests) { +        let responses; +        try { +            responses = await this._templateRenderer.renderMulti(items); +        } catch (e) { +            for (const {reject} of requests) { +                reject(e); +            } +            return; +        } + +        for (let i = 0, ii = requests.length; i < ii; ++i) { +            const request = requests[i]; +            try { +                const response = responses[i]; +                const {error} = response; +                if (typeof error !== 'undefined') { +                    throw deserializeError(error); +                } else { +                    request.resolve(response.result); +                } +            } catch (e) { +                request.reject(e); +            } +        } +    }  } diff --git a/ext/js/templates/template-renderer-frame-api.js b/ext/js/templates/template-renderer-frame-api.js index 104e357b..dd6be517 100644 --- a/ext/js/templates/template-renderer-frame-api.js +++ b/ext/js/templates/template-renderer-frame-api.js @@ -20,6 +20,7 @@ class TemplateRendererFrameApi {          this._templateRenderer = templateRenderer;          this._windowMessageHandlers = new Map([              ['render', {async: false, handler: this._onRender.bind(this)}], +            ['renderMulti', {async: false, handler: this._onRenderMulti.bind(this)}],              ['getModifiedData', {async: false, handler: this._onGetModifiedData.bind(this)}]          ]);      } @@ -58,6 +59,10 @@ class TemplateRendererFrameApi {          return this._templateRenderer.render(template, data, type);      } +    _onRenderMulti({items}) { +        return this._serializeMulti(this._templateRenderer.renderMulti(items)); +    } +      _onGetModifiedData({data, type}) {          const result = this._templateRenderer.getModifiedData(data, type);          return this._clone(result); @@ -82,6 +87,17 @@ class TemplateRendererFrameApi {          };      } +    _serializeMulti(array) { +        for (let i = 0, ii = array.length; i < ii; ++i) { +            const value = array[i]; +            const {error} = value; +            if (typeof error !== 'undefined') { +                value.error = this._serializeError(error); +            } +        } +        return array; +    } +      _clone(value) {          return JSON.parse(JSON.stringify(value));      } diff --git a/ext/js/templates/template-renderer-frame-main.js b/ext/js/templates/template-renderer-frame-main.js index 40ecf308..3d61295d 100644 --- a/ext/js/templates/template-renderer-frame-main.js +++ b/ext/js/templates/template-renderer-frame-main.js @@ -27,7 +27,8 @@      const templateRenderer = new TemplateRenderer(japaneseUtil);      const ankiNoteDataCreator = new AnkiNoteDataCreator(japaneseUtil);      templateRenderer.registerDataType('ankiNote', { -        modifier: ({marker, commonData}) => ankiNoteDataCreator.create(marker, commonData) +        modifier: ({marker, commonData}) => ankiNoteDataCreator.create(marker, commonData), +        composeData: (marker, commonData) => ({marker, commonData})      });      const templateRendererFrameApi = new TemplateRendererFrameApi(templateRenderer);      templateRendererFrameApi.prepare(); diff --git a/ext/js/templates/template-renderer-proxy.js b/ext/js/templates/template-renderer-proxy.js index aba45e6c..f35239cb 100644 --- a/ext/js/templates/template-renderer-proxy.js +++ b/ext/js/templates/template-renderer-proxy.js @@ -30,6 +30,11 @@ class TemplateRendererProxy {          return await this._invoke('render', {template, data, type});      } +    async renderMulti(items) { +        await this._prepareFrame(); +        return await this._invoke('renderMulti', {items}); +    } +      async getModifiedData(data, type) {          await this._prepareFrame();          return await this._invoke('getModifiedData', {data, type}); diff --git a/ext/js/templates/template-renderer.js b/ext/js/templates/template-renderer.js index 5441528c..ed9cc41c 100644 --- a/ext/js/templates/template-renderer.js +++ b/ext/js/templates/template-renderer.js @@ -29,18 +29,39 @@ class TemplateRenderer {          this._dataTypes = new Map();      } -    registerDataType(name, {modifier=null}) { -        this._dataTypes.set(name, {modifier}); +    registerDataType(name, {modifier=null, composeData=null}) { +        this._dataTypes.set(name, {modifier, composeData});      }      render(template, data, type) {          const instance = this._getTemplateInstance(template); -        data = this._getModifiedData(data, type); +        data = this._getModifiedData(data, void 0, type);          return this._renderTemplate(instance, data);      } +    renderMulti(items) { +        const results = []; +        for (const {template, templateItems} of items) { +            const instance = this._getTemplateInstance(template); +            for (const {type, commonData, datas} of templateItems) { +                for (let data of datas) { +                    let result; +                    try { +                        data = this._getModifiedData(data, commonData, type); +                        result = this._renderTemplate(instance, data); +                        result = {result}; +                    } catch (error) { +                        result = {error}; +                    } +                    results.push(result); +                } +            } +        } +        return results; +    } +      getModifiedData(data, type) { -        return this._getModifiedData(data, type); +        return this._getModifiedData(data, void 0, type);      }      // Private @@ -70,10 +91,16 @@ class TemplateRenderer {          }      } -    _getModifiedData(data, type) { +    _getModifiedData(data, commonData, type) {          if (typeof type === 'string') {              const typeInfo = this._dataTypes.get(type);              if (typeof typeInfo !== 'undefined') { +                if (typeof commonData !== 'undefined') { +                    const {composeData} = typeInfo; +                    if (typeof composeData === 'function') { +                        data = composeData(data, commonData); +                    } +                }                  const {modifier} = typeInfo;                  if (typeof modifier === 'function') {                      data = modifier(data); |