git.fiddlerwoaroof.com
Raw Blame History
/**
 * This file is part of JIM <https://github.com/fiddlerwoaroof/jim/> (c) 2016 Edward Langley
 *
 * See the COPYING document in the root of the source distribution for licensing information.
 */
PresentationType = (function () {
  var presentationTypes = {};

  // TODO: think through selector better: right now, I think we assume that it
  //       is a class selector and things will break if it isn't we should,
  //       however, probably let it be more general.
  function PresentationType(name, selector) {
    if (this instanceof PresentationType) {
      this.name = name;
      if (selector === undefined) {
        selector = '.'+this.name;
      }
      this.selector = selector;
      this.receivers = [];
    } else {
      var result = presentationTypes[name];
      if (result === undefined) {
        result = new PresentationType(name);
        presentationTypes[result.name] = result;
      }
      return result;
    }
  }

  PresentationType.prototype = {
    presentationsTypes: presentationTypes,

    isOfType(name) {
      var type = presentationTypes[name];
      //console.log(name);
      //console.log(type);
      return type.isPrototypeOf(this);
    },

    addReceiver(receiver) {
      this.receivers.push(receiver);
      return this.receivers.length - 1;
    },

    removeReceiver(el) {
      var elIdx = this.receivers.indexOf(el);
      var result = elIdx !== -1;
      if (result) {
        this.receivers.splice(elIdx,1);
      }
      return result;
    },

    notifyReceivers(theEvent, self) {
      if (self === undefined) {
        self = this;
      }

      var outerArgs = Array.prototype.slice.call(arguments);
      outerArgs.unshift(theEvent, self, self.value);

      this.receivers.forEach((function (theReceiver) {
        theReceiver.receive.apply(theReceiver, outerArgs);
      }));

      var parent = Object.getPrototypeOf(this);
      if (parent instanceof PresentationType && parent.receivers !== undefined && parent.receivers !== []) {
        parent.notifyReceivers(theEvent, self);
      }
    },

    selectAllTags() {
      return Array.prototype.slice.call(
        document.querySelectorAll(this.selector)
      );
    },

    selectAll() {
      var allTags = this.selectAllTags();
      return Array.prototype.map.call(allTags, function (el) {
        return el.ptObject;
      });
    },

    bindTags() {
      var els = this.selectAllTags().filter(function (theEl) {
        return theEl.ptObject === undefined;
      });

      els.forEach(this.bindEl, this);

      return this;
    },

    bindEl(theEl) {
      var content = theEl.textContent.trim();
      var value = theEl.value;

      var bindTypes = theEl.dataset.bind;
      if (bindTypes !== undefined) {
        bindTypes = bindTypes.split(',');
      } else {
        bindTypes = ['click'];
      }

      //console.log('bt',bindTypes);

      if (! theEl.hasAttribute('value') ) {
        var tmpVal = theEl.dataset.value;
        value = tmpVal === undefined ? content : theEl.dataset.value;
      }

      return this.wrapTag(theEl, value, bindTypes, content);
    },

    wrapTag(tag, value, bindTypes, content) {
      var result = Object.assign(
        Object.create(this), {
          tag: tag,
          value: value,
          display: content,
        }
      );
      tag.ptObject = result;

      this.classes.forEach(function (cls) {
        tag.classList.add(cls);
      });

      bindTypes.forEach(function (event) {
        //console.log(event);
        tag.addEventListener(event, result, false);
      });

      if (result.validate()) {
        return result;
      } else {
        this.doError(result);
      }
    },

    get classes() {
      var current = this,
          classes = new Set();
      while (current instanceof PresentationType) {
        if (current.name !== undefined) {
          classes.add(current.name);
        }
        current = Object.getPrototypeOf(current);
      }

      return classes;
    },

    extend(name, props) {
      if (this.children === undefined) {
        this.children = [];
      }

      if (props === undefined) {
        props = {};
      }

      props.name = name;
      if (props.selector === undefined) {
        props.selector = '.' + name;
      }
      props.receivers = [];
      var newType = Object.assign(Object.create(this), props);
      presentationTypes[name] = newType;
      this.children.push(newType);
      return newType;
    },

    makeTag(value, tagName, bindTypes, content) {
      var newTag = document.createElement(tagName);

      this.classes.forEach(function (cls) { newTag.classList.add(cls); });

      if (bindTypes === undefined) {
        bindTypes = [];
      } else if (!(bindTypes instanceof Array)) {
        content = bindTypes;
        bindTypes = [];
      }

      if (content === undefined) {
        content = value;
      }

      newTag.textContent = content;
      return this.wrapTag(newTag, value, bindTypes, content);
    },

    handleEvent(theEvent) {
      //console.log('handling: ',theEvent);
      var handler = this[theEvent.type];
      var result = true;
      if (handler !== undefined) {
        result = handler.bind(this)(theEvent);
      }
      this.notifyReceivers(theEvent);

      return result;
    },

    doError(object) {
      return;
    },

    validate() {
      return true;
    },

    toggleAll() {
      var els = this.selectAllTags();

      els.forEach(function (el) {
        el.classList.toggle('activated');
      });
    },

    deactivateAll() {
      var els = this.selectAllTags();

      els.forEach(function (el) {
        el.classList.remove('activated');
      });
    },

    activateAll() {
      this.bindTags();
      var els = this.selectAllTags();

      els.forEach(function (el) {
        el.classList.add('activated');
      });

      return els;
    },


  };

  var typeType = PresentationType('type');
  typeType.generateTags = function (tagName) {
    if (tagName === undefined) {
      tagName = 'span';
    }

    return Object.keys(presentationTypes).map(
      function (type) {
        var tag = document.createElement(tagName);
        return this.wrapTag(tag, type, ['click'], type);
      }, this
    );
  };
  return PresentationType;
})();