mustache_templates-8.x-1.0-beta4/js/sync.js

js/sync.js
/**
 * @file
 * DOM content synchronization with Mustache templates.
 *
 * Using Mustache.js by
 *   Copyright (c) 2009 Chris Wanstrath (Ruby)
 *   Copyright (c) 2010-2014 Jan Lehnardt (JavaScript)
 *   Copyright (c) 2010-2015 The mustache.js community
 * Using morphdom by
 *   Copyright (c) Patrick Steele-Idem <pnidem@gmail.com> (psteeleidem.com)
 */

window.mustacheSync = mustacheSync || {items: [], templates: []};

(function (Drupal, sync, Mustache) {
  sync.registry = sync.registry || {
    items: [],
    pending: [],
    templates: {},
    providers: [],
    listeners: {},
    evald: {},
    magic: {}
  };

  sync.___internals = sync.___internals || {
    subset: function (data, select) {
      var subset = data;
      var slength = select.length;
      var skey;
      var i;
      for (i = 0; i < slength; i++) {
        skey = select[i];
        if (subset.hasOwnProperty(skey)) {
          subset = subset[skey];
        }
        else {
          return false;
        }
      }
      return subset;
    },
    isEmpty: function (obj) {
      var k;
      if (!(typeof obj === 'object') || obj === null) {
        return !obj;
      }
      if (Array.isArray(obj)) {
        return !obj.length;
      }
      for (k in obj) {
        if (obj.hasOwnProperty(k)) {
          return false;
        }
      }
      return true;
    },
    trigger: function (target, type, canBubble, cancelable, detail) {
      var event = document.createEvent('CustomEvent');
      if (typeof detail === 'undefined') {
        detail = null;
      }
      event.initCustomEvent(type, canBubble, cancelable, detail);
      target.dispatchEvent(event);
    },
    item: {
      update: function (period, force_fetch, no_repeat) {
        var provider = this.provider;
        var targetElement = this.element || null;
        var internals = sync.___internals;
        var fetch = internals.item.fetch;
        var render = internals.item.render;
        var trigger = internals.trigger;
        var buffer = {size: 0};
        if (typeof period === 'undefined') {
          period = this.period;
        }
        if (no_repeat !== true) {
          no_repeat = false;
        }

        if (targetElement) {
          targetElement.classList.add('syncing');
          targetElement.classList.remove('synced');
          trigger(targetElement, 'mustacheSyncBegin', true, false, this);
        }

        if (provider) {
          if (provider.faulty === true) {
            if (period > 0) {
              // Retry when period is set.
              this.sync(this.delay + 1000, period, false, true, no_repeat);
            }
            return;
          }
          if (provider.fetching === true) {
            // Retry.
            this.sync(this.delay + 10, period, false, true, no_repeat);
            return;
          }
          if ((provider.fetched === null) || (force_fetch === true) || ((period > 0) && (period - 1 < Date.now() - provider.fetched)) || ((typeof this.max_age === 'number') && (this.max_age < Date.now() - provider.fetched))) {
            fetch.call(this, period, no_repeat);
            return;
          }
        }

        render.call(this);

        if (!no_repeat) {
          // Repeat or sync next one.
          buffer.next = this.next();
          if (buffer.next) {
            buffer.next.sync(period, period);
          }
        }
      },
      fetch: function (period, no_repeat) {
        var provider = this.provider;
        var fetch = sync.___internals.item.fetch;
        var render = sync.___internals.item.render;
        var isEmpty = sync.___internals.isEmpty;
        var request = new XMLHttpRequest();

        provider.fetching = true;
        if (typeof period === 'undefined') {
          period = this.period;
        }
        if (no_repeat !== true) {
          no_repeat = false;
        }

        request.open('GET', provider.url, true);
        request.onload = function () {
          var data = false;
          var next = false;
          if (request.status === 304) {
            data = !isEmpty(provider.latest) ? provider.latest : this.data || false;
            provider.previousFetched = provider.fetched || null;
            provider.fetched = Date.now();
          }
          else if (request.status >= 200 && request.status < 400) {
            try {
              data = JSON.parse(request.responseText);
              provider.previous = provider.latest || null;
              provider.previousFetched = provider.fetched || null;
              provider.latest = data;
              provider.fetched = Date.now();
            }
            catch (e) {
              data = false;
            }
          }
          else if (request.status >= 500) {
            request.onerror();
            return;
          }
          provider.fetching = false;
          provider.faulty = false;
          if (provider !== this.provider) {
            return;
          }
          if (this.increment === null || !isEmpty(data)) {
            this.previousData = this.data || this.previousData || false;
            if (!(typeof data === 'object') || Array.isArray(data) || !data) {
              this.data = data;
            }
            else if (typeof this.previousData === 'object' && this.previousData) {
              this.data = Object.assign({}, this.previousData, data);
            }
            else {
              this.data = Object.assign({}, data);
            }
            render.call(this);
          }
          if (!no_repeat) {
            if (this.increment !== null && this.increment.loop && isEmpty(data)) {
              next = this.next(0);
            }
            else {
              next = this.next();
            }
            if (next) {
              next.sync(period, period);
            }
          }
        }.bind(this);
        request.onerror = function () {
          provider.fetching = false;
          provider.faulty = true;
          if (period > 0) {
            // Retry when period is set.
            setTimeout(fetch.bind(this, period, no_repeat), period + 5000);
          }
        }.bind(this);

        request.send();
      },
      done: function () {
        if (this.increment !== null) {
          if (this.increment.i + 1 >= this.increment.max && !this.increment.loop) {
            return true;
          }
        }
        return this.limit === 0 || (this.period === 0 && this.limit < -1);
      },
      next: function (index) {
        if (this.done()) {
          return false;
        }
        if (this.increment !== null) {
          if (typeof index === 'number') {
            this.increment.i = index;
            this.increment.value = this.increment.offset + (index * this.increment.step);
          }
          else if (this.increment.i + 1 >= this.increment.max) {
            if (this.increment.loop) {
              this.increment.i = 0;
              this.increment.value = this.increment.offset;
            }
          }
          else {
            this.increment.i++;
            this.increment.value += this.increment.step;
          }
          if (this.provider !== null) {
            this.provider = this.provider.rebuild(this.increment.key, this.increment.value);
          }
          else {
            this.data[this.increment.key] = this.increment.value;
          }
        }
        return this;
      },
      prepare: function (rendered, buffer) {
        return rendered;
      },
      finish: function (rendered, buffer) {
        return rendered;
      },
      render: function () {
        var rendered = false;
        var buffer = {
          template: sync.registry.templates[this.template],
          data: this.data
        };
        var targetElement = this.element || null;
        var adjacent = this.adjacent;
        var internals = sync.___internals;
        var subset = internals.subset;
        var trigger = internals.trigger;
        var prepare = internals.item.prepare;
        var finish = internals.item.finish;
        var magic = Object.assign({}, sync.registry.magic);
        var execInner = this.eval;
        var behaviors = targetElement && this.behaviors;
        for (var k in magic) {
          if (magic.hasOwnProperty(k)) {
            magic[k] = magic[k].call(this, buffer);
          }
        }

        if (this.select && buffer.data) {
          buffer.data = subset(buffer.data, this.select);
        }
        if (buffer.data === false) {
          if (targetElement) {
            targetElement.classList.remove('syncing');
            targetElement.classList.remove('synced');
            targetElement.classList.add('not-synced');
            targetElement.classList.add('error');
          }
          return false;
        }
        if (buffer.data === null) {
          buffer.data = {};
        }
        if (typeof buffer.data === 'object') {
          if (Array.isArray(buffer.data)) {
            buffer.data = {___l: [Object.assign([], buffer.data)]};
            buffer.template = '{{#___l}}' + buffer.template + '{{/___l}}'
          }
          buffer.data = Object.assign({}, buffer.data, magic);
          if (this.previousData) {
            buffer.data.previous = this.previousData;
            delete buffer.data.previous.previous;
          }
        }
        try {
          rendered = prepare.call(this, rendered, buffer);
          rendered = Mustache.render(buffer.template, buffer.data, sync.registry.templates);
          rendered = finish.call(this, rendered, buffer);
        }
        catch (e) {
          if (e.retry && e.component) {
            for (buffer.ri = 0; buffer.ri < e.retry.length; buffer.ri++) {
              buffer.retry = e.retry[buffer.ri];
              buffer.retry.retries = buffer.retry.retries ? buffer.retry.retries + 1 : 1;
              if (this === buffer.retry) {
                setTimeout(sync.___internals.item.render.bind(this), buffer.retry.retries * 100);
              }
              else {
                buffer.retry.sync(buffer.retry.retries * 100, 0, false, true, true);
              }
            }
            return rendered;
          }
          if (e.abort) {
            return rendered;
          }
          throw e;
        }
        behaviors && Drupal.detachBehaviors(targetElement);
        if (this.morph || execInner) {
          buffer.fragment = document.createElement('div');
          buffer.fragment.innerHTML = rendered;
        }
        if (execInner) {
          buffer.scripts = buffer.fragment.querySelectorAll('script');
          buffer.eval = [];
          for (buffer.i = 0; buffer.i < buffer.scripts.length; buffer.i++) {
            buffer.script = buffer.scripts[buffer.i];
            buffer.script.remove();
            if (buffer.script.hasAttribute('src')) {
              if (sync.registry.evald.hasOwnProperty(buffer.script.getAttribute('src'))) {
                continue;
              }
              sync.registry.evald[buffer.script.getAttribute('src')] = true;
            }
            if (buffer.script.hasAttribute('id')) {
              if (sync.registry.evald.hasOwnProperty(buffer.script.getAttribute('id'))) {
                continue;
              }
              sync.registry.evald[buffer.script.getAttribute('id')] = true;
            }
            buffer.eval_script = document.createElement('script');
            for (buffer.k = 0; buffer.k < buffer.script.attributes.length; buffer.k++) {
              buffer.eval_script.setAttribute(buffer.script.attributes[buffer.k].name, buffer.script.attributes[buffer.k].value);
            }
            buffer.eval_script.innerHTML = buffer.script.innerHTML;
            buffer.eval.push(buffer.eval_script);
          }
          rendered = buffer.fragment.innerHTML;
        }
        if (targetElement) {
          if (this.morph) {
            morphdom(targetElement, buffer.fragment, this.morph);
          }
          else {
            switch (adjacent) {
              case 'beforebegin':
              case 'afterbegin':
              case 'beforeend':
              case 'afterend':
                targetElement.insertAdjacentHTML(adjacent, rendered);
                break;
              default:
                targetElement.innerHTML = rendered;
            }
          }
          targetElement.classList.add('synced');
          targetElement.classList.remove('syncing');
          targetElement.classList.remove('not-synced');
          targetElement.classList.remove('error');
        }
        if (execInner) {
          for (buffer.i = 0; buffer.i < buffer.eval.length; buffer.i++) {
            document.head.appendChild(buffer.eval[buffer.i]);
            buffer.eval[buffer.i].remove();
          }
        }
        buffer = null;
        targetElement && trigger(targetElement, 'mustacheSyncFinish', true, false, this);
        behaviors && Drupal.attachBehaviors(targetElement);
        return rendered;
      },
      init: function () {
        var internals = sync.___internals.item;
        var getProvider = sync.___internals.provider.get;

        if (this.initialized === true) {
          // Already initialized, aborting.
          return;
        }

        // Initialize the provider, if given.
        if (typeof this.url === 'string') {
          this.provider = getProvider(this.url);
          this.data = this.data || {};
        }
        else if (typeof this.data === 'string') {
          this.provider = getProvider(this.data);
          this.data = {};
        }
        else {
          this.data = this.data || {};
          this.provider = null;
        }

        // Initialize the increment, if given.
        this.increment = this.increment || null;
        if (this.increment !== null) {
          this.increment.offset = this.increment.offset || 0;
          this.increment.key = this.increment.key || 'page';
          this.increment.value = this.increment.offset;
          this.increment.step = this.increment.step || 1;
          this.increment.max = this.increment.max || -1;
          this.increment.i = 0;
          this.increment.loop = this.increment.loop || true;
          if (this.provider !== null) {
            this.provider = this.provider.rebuild(this.increment.key, this.increment.value);
          }
          else {
            this.data[this.increment.key] = this.increment.value;
          }
        }

        this.listen = this.listen || internals.listen.bind(this);
        this.ready = this.ready || internals.ready.bind(this);
        this.sync = this.sync || internals.sync.bind(this);
        this.done = this.done || internals.done.bind(this);
        this.next = this.next || internals.next.bind(this);

        this.delay = this.delay || 0;
        this.period = this.period || 0;
        this.limit = this.limit || -1;
        this.trigger = this.trigger || null;
        this.adjacent = this.adjacent || null;
        this.eval = (this.eval === true) || false;
        this.behaviors = (this.behaviors === true) || (this.eval && this.behaviors !== false) || false;
        if (this.adjacent) {
          this.morph = false;
        }
        if (this.morph === true) {
          this.morph = {childrenOnly: true};
        }

        this.started = false;
        this.triggered = false;
        this.initialized = true;
      },
      ready: function () {
        if (!this.element && this.into) {
          this.element = document.querySelector(this.into);
        }
        if (this.element === null) {
          return false;
        }
        if (this.morph && !morphdom) {
          // Morphing DOM trees require morphdom.
          return false;
        }
        return sync.registry.templates.hasOwnProperty(this.template);
      },
      start: function () {
        if (!this.ready() || this.started === true) {
          return;
        }
        this.started = true;

        if (this.trigger === null) {
          // Synchronize immediately, or at least after a specified delay.
          this.sync(this.delay, this.period);
        }
        else {
          // Listen and synchronize by the specified triggers.
          this.listen(this.trigger);
        }
      },
      listen: function (triggers) {
        var listeners = sync.registry.listeners;
        var trigger;
        var selector;
        var event;
        var limit;
        var group;
        var subscribers;
        var i;

        // Add this item as a subscriber for each specified triggering element.
        for (i = 0; i < triggers.length; i++) {
          trigger = triggers[i];
          selector = trigger[0];
          event = trigger[1];
          limit = trigger[2];
          if (!listeners.hasOwnProperty(selector)) {
            listeners[selector] = {
              elements: [],
              events: {}
            };
          }
          group = listeners[selector];
          if (!group.events.hasOwnProperty(event)) {
            group.events[event] = {
              subscribers: []
            };
            group.events[event].triggered = function () {
              var i;
              var subscriber;
              var item;
              var length = this.subscribers.length;

              for (i = 0; i < length; i++) {
                subscriber = this.subscribers[i];
                item = subscriber.item;
                if (subscriber.limit === 0) {
                  continue;
                }
                subscriber.limit--;
                if (item.triggered === true) {
                  if (item.limit < 0) {
                    item.limit = -1;
                  }
                  item = item.next();
                  if (item) {
                    subscriber.item = item;
                    item.sync(item.delay, 0, !item.max_age, false, true);
                  }
                }
                else {
                  item.triggered = true;
                  item.sync(item.delay, item.period, !item.max_age);
                }
              }
            }.bind(group.events[event]);
          }
          subscribers = group.events[event].subscribers;
          subscribers.push({item: this, limit: limit});
        }
      },
      sync: function (delay, period, force_fetch, ignore_limit, no_repeat) {
        if (!this.ready()) {
          return;
        }
        if (ignore_limit !== true) {
          if (this.done()) {
            return;
          }
          this.limit--;
        }
        if (typeof delay === 'undefined') {
          delay = this.delay;
        }
        if (typeof period === 'undefined') {
          period = this.period;
        }
        if (force_fetch !== true) {
          force_fetch = false;
        }
        if (no_repeat !== true) {
          no_repeat = false;
        }
        setTimeout(sync.___internals.item.update.bind(this, period, force_fetch, no_repeat), delay);
      }
    },
    provider: {
      init: function (url) {
        var internals = sync.___internals.provider;
        var instance = {
          url: url,
          latest: {},
          fetched: null,
          fetching: false,
          faulty: false
        };
        instance.getParts = internals.parts.bind(instance);
        instance.getParams = internals.params.bind(instance);
        instance.rebuild = internals.rebuild.bind(instance);
        return instance;
      },
      get: function (url) {
        var providers = sync.registry.providers;
        var buffer = {size: providers.length};
        for (buffer.i = 0; buffer.i < buffer.size; buffer.i++) {
          if (providers[buffer.i].url === url) {
            return providers[buffer.i];
          }
        }
        while (buffer.size > 9) {
          providers.shift();
          buffer.size--;
        }
        buffer.provider = sync.___internals.provider.init(url);
        providers.push(buffer.provider);
        return buffer.provider;
      },
      rebuild: function (key, val) {
        var getProvider = sync.___internals.provider.get;
        var buffer = {isDifferent: false, params: {}, flat: []};
        var newParams = key;
        if (typeof key !== 'object') {
          newParams = {};
          newParams[key] = val;
        }

        this.getParams();
        for (buffer.key in this.params) {
          if (this.params.hasOwnProperty(buffer.key) && !newParams.hasOwnProperty(buffer.key)) {
            buffer.val = this.params[buffer.key];
            buffer.flat.push(buffer.key + '=' + buffer.val);
            buffer.params[buffer.key] = buffer.val;
          }
        }
        for (buffer.key in newParams) {
          if (!newParams.hasOwnProperty(buffer.key)) {
            continue;
          }
          buffer.val = newParams[buffer.key];
          if (this.params.hasOwnProperty(buffer.key)) {
            if ((typeof this.params[buffer.key] === 'string') && (typeof buffer.val === 'number')) {
              this.params[buffer.key] = parseInt(this.params[buffer.key]);
            }
            if (this.params[buffer.key] !== buffer.val) {
              buffer.isDifferent = true;
            }
          }
          else {
            buffer.isDifferent = true;
          }
          buffer.flat.push(buffer.key + '=' + buffer.val);
          buffer.params[buffer.key] = buffer.val;
        }
        if (!buffer.isDifferent) {
          return getProvider(this.url);
        }

        buffer.parts = document.createElement('a');
        buffer.parts.href = this.url;
        buffer.parts.search = buffer.flat.join('&');

        buffer.provider = getProvider(buffer.parts.href);
        buffer.provider.parts = buffer.parts;
        buffer.provider.params = buffer.params;
        return buffer.provider;
      },
      parts: function () {
        if (!this.hasOwnProperty('parts')) {
          // Extract and attach the url parts.
          this.parts = document.createElement('a');
          this.parts.href = this.url;
        }
        return this.parts;
      },
      params: function () {
        var buffer;
        if (!this.hasOwnProperty('params')) {
          // Extract and attach the query parameters.
          this.params = {};
          buffer = {search: this.getParts().search};
          buffer.search = buffer.search.substring(1);
          if (buffer.search.length === 0) {
            // No params given, abort extracting.
            return this.params;
          }
          buffer.queries = buffer.search.split('&');
          buffer.ql = buffer.queries.length;
          for (buffer.i = 0; buffer.i < buffer.ql; buffer.i++) {
            buffer.current = buffer.queries[buffer.i].split('=');
            if (buffer.current[0].length === 0) {
              continue;
            }
            if (buffer.current.length === 2) {
              this.params[buffer.current[0]] = buffer.current[1];
            }
            else {
              this.params[buffer.current[0]] = '';
            }
          }
        }
        return this.params;
      }
    }
  };

  sync.now = sync.now || function () {
    var registry = sync.registry;
    var init = sync.___internals.item.init;
    var start = sync.___internals.item.start;
    var item;
    var template;
    var i;

    i = sync.templates.length;
    while (i > 0) {
      i--;
      template = sync.templates.shift();
      registry.templates[template.name] = template.content;
    }

    i = registry.pending.length;
    while (i > 0) {
      i--;
      item = registry.pending.shift();
      if (item.ready()) {
        start.call(item);
      }
      else {
        registry.pending.push(item);
      }
    }

    i = sync.items.length;
    while (i > 0) {
      i--;
      item = sync.items.shift();
      init.call(item);
      registry.items.push(item);
      if (item.ready()) {
        start.call(item);
      }
      else {
        registry.pending.push(item);
      }
    }
  };
  sync.refresh = sync.refresh || function (dom) {
    var listeners = sync.registry.listeners;
    var selector;
    var group;
    var new_elements;
    var event_name;
    var event_item;
    var i;
    var targetElement;
    var length;
    var k;
    var length_k;

    sync.now();

    if (typeof dom === 'undefined') {
      dom = document;
    }
    else if (dom === null) {
      return;
    }

    // Collect all triggering elements,
    // and register the corresponding event listeners.
    for (selector in listeners) {
      if (!listeners.hasOwnProperty(selector)) {
        continue;
      }
      group = listeners[selector];

      new_elements = dom.querySelectorAll(selector);
      length = new_elements.length;
      for (i = 0; i < length; i++) {
        targetElement = new_elements[i];
        if (group.elements.indexOf(targetElement) < 0) {
          group.elements.push(targetElement);
        }
        for (event_name in group.events) {
          if (!group.events.hasOwnProperty(event_name)) {
            continue;
          }
          event_item = group.events[event_name];

          length_k = group.elements.length;
          for (k = 0; k < length_k; k++) {
            group.elements[k].addEventListener(event_name, event_item.triggered, false);
          }
        }
      }
    }
  };
}(Drupal, mustacheSync, Mustache));

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc