templateLoader.js

9.54 KB
12/10/2025 04:50
JS
templateLoader.js
/**
 * Template Loader Service - บริการสำหรับโหลดเทมเพลตจากแหล่งต่างๆ
 */
(function(global) {
  'use strict';

  /**
   * คลาส TemplateLoader
   * บริการสำหรับโหลดเทมเพลตจากแหล่งต่างๆ
   */
  class TemplateLoader {
    constructor(editor) {
      this.editor = editor;
      this.cache = {};
      this.loadingPromises = {};
    }

    /**
     * โหลดเทมเพลตจาก URL
     * @param {string} url - URL ของเทมเพลต
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนเนื้อหาเทมเพลต
     */
    loadFromURL(url, options = {}) {
      // ตรวจสอบแคช
      if (this.cache[url]) {
        return Promise.resolve(this.cache[url]);
      }

      // ตรวจสอบว่ากำลังโหลดอยู่หรือไม่
      if (this.loadingPromises[url]) {
        return this.loadingPromises[url];
      }

      // สร้าง Promise สำหรับการโหลด
      const promise = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        xhr.open('GET', url, true);

        xhr.onload = () => {
          if (xhr.status === 200) {
            const template = xhr.responseText;

            // เก็บในแคช
            this.cache[url] = template;

            // ลบจากรายการที่กำลังโหลด
            delete this.loadingPromises[url];

            // ส่งเหตุการณ์
            this.editor.emit('template:loaded', {url, template});

            resolve(template);
          } else {
            reject(new Error(`Failed to load template: ${xhr.status} ${xhr.statusText}`));
          }
        };

        xhr.onerror = () => {
          reject(new Error(`Network error while loading template: ${url}`));
        };

        xhr.send();
      });

      // เก็บ Promise ที่กำลังโหลด
      this.loadingPromises[url] = promise;

      return promise;
    }

    /**
     * โหลดเทมเพลตจากอิลิเมนต์
     * @param {string} selector - ตัวเลือก CSS ของอิลิเมนต์เทมเพลต
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนเนื้อหาเทมเพลต
     */
    loadFromElement(selector, options = {}) {
      return new Promise((resolve, reject) => {
        try {
          const element = document.querySelector(selector);

          if (!element) {
            reject(new Error(`Template element not found: ${selector}`));
            return;
          }

          const template = element.innerHTML;

          // ส่งเหตุการณ์
          this.editor.emit('template:loaded', {selector, template});

          resolve(template);
        } catch (error) {
          reject(error);
        }
      });
    }

    /**
     * โหลดเทมเพลตจากข้อความ
     * @param {string} template - ข้อความเทมเพลต
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนเนื้อหาเทมเพลต
     */
    loadFromString(template, options = {}) {
      return new Promise((resolve) => {
        // ส่งเหตุการณ์
        this.editor.emit('template:loaded', {template});

        resolve(template);
      });
    }

    /**
     * โหลดเทมเพลตจาก localStorage
     * @param {string} key - คีย์ใน localStorage
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนเนื้อหาเทมเพลต
     */
    loadFromStorage(key, options = {}) {
      return new Promise((resolve, reject) => {
        try {
          const template = localStorage.getItem(key);

          if (!template) {
            reject(new Error(`Template not found in storage: ${key}`));
            return;
          }

          // ส่งเหตุการณ์
          this.editor.emit('template:loaded', {key, template});

          resolve(template);
        } catch (error) {
          reject(error);
        }
      });
    }

    /**
     * โหลดเทมเพลตจาก JSON
     * @param {string} url - URL ของไฟล์ JSON
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนเนื้อหาเทมเพลต
     */
    loadFromJSON(url, options = {}) {
      // ตรวจสอบแคช
      if (this.cache[url]) {
        return Promise.resolve(this.cache[url]);
      }

      // ตรวจสอบว่ากำลังโหลดอยู่หรือไม่
      if (this.loadingPromises[url]) {
        return this.loadingPromises[url];
      }

      // สร้าง Promise สำหรับการโหลด
      const promise = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        xhr.open('GET', url, true);
        xhr.responseType = 'json';

        xhr.onload = () => {
          if (xhr.status === 200) {
            const data = xhr.response;

            // แปลง JSON เป็น HTML
            let template = '';

            if (data.html) {
              template = data.html;
            } else if (data.template) {
              template = data.template;
            } else {
              // ถ้าไม่มีฟิลด์ html หรือ template ให้ใช้ทั้งหมด
              template = JSON.stringify(data);
            }

            // เก็บในแคช
            this.cache[url] = template;

            // ลบจากรายการที่กำลังโหลด
            delete this.loadingPromises[url];

            // ส่งเหตุการณ์
            this.editor.emit('template:loaded', {url, template, data});

            resolve(template);
          } else {
            reject(new Error(`Failed to load JSON template: ${xhr.status} ${xhr.statusText}`));
          }
        };

        xhr.onerror = () => {
          reject(new Error(`Network error while loading JSON template: ${url}`));
        };

        xhr.send();
      });

      // เก็บ Promise ที่กำลังโหลด
      this.loadingPromises[url] = promise;

      return promise;
    }

    /**
     * โหลดเทมเพลตหลายรายการพร้อมกัน
     * @param {Array} sources - รายการแหล่งที่จะโหลด
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {Promise} Promise ที่ส่งคืนอาร์เรย์ของเทมเพลต
     */
    loadMultiple(sources, options = {}) {
      const promises = sources.map(source => {
        if (typeof source === 'string') {
          // ถ้าเป็นสตริง ให้โหลดจาก URL
          return this.loadFromURL(source, options);
        } else if (source.url) {
          // ถ้าเป็นออบเจกต์ที่มี url
          return this.loadFromURL(source.url, options);
        } else if (source.selector) {
          // ถ้าเป็นออบเจกต์ที่มี selector
          return this.loadFromElement(source.selector, options);
        } else if (source.key) {
          // ถ้าเป็นออบเจกต์ที่มี key
          return this.loadFromStorage(source.key, options);
        } else if (source.template) {
          // ถ้าเป็นออบเจกต์ที่มี template
          return this.loadFromString(source.template, options);
        } else {
          // ถ้าไม่ตรงกับเงื่อนไขใดๆ ให้ส่ง Promise ที่ reject
          return Promise.reject(new Error('Invalid source format'));
        }
      });

      return Promise.all(promises);
    }

    /**
     * ล้างแคช
     * @param {string} url - URL ที่จะลบจากแคช (optional)
     */
    clearCache(url = null) {
      if (url) {
        delete this.cache[url];
      } else {
        this.cache = {};
      }
    }

    /**
     * ตรวจสอบว่ามีในแคชหรือไม่
     * @param {string} url - URL ที่จะตรวจสอบ
     * @returns {boolean} มีในแคชหรือไม่
     */
    isInCache(url) {
      return !!this.cache[url];
    }

    /**
     * รับข้อมูลจากแคช
     * @param {string} url - URL ที่จะรับข้อมูล
     * @returns {string} ข้อมูลในแคช
     */
    getFromCache(url) {
      return this.cache[url] || null;
    }

    /**
     * เพิ่มข้อมูลในแคช
     * @param {string} url - URL
     * @param {string} template - เนื้อหาเทมเพลต
     */
    addToCache(url, template) {
      this.cache[url] = template;
    }
  }

  // เปิดเผยคลาส TemplateLoader ทั่วโลก
  global.TemplateLoader = TemplateLoader;

})(typeof window !== 'undefined' ? window : this);