All files / src/editor render.js

0% Statements 0/122
0% Branches 0/1
0% Functions 0/1
0% Lines 0/122

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123                                                                                                                                                                                                                                                     
let events = [];

export const Render = {
  registerEvent(node, type, callback, options) {
    let nodes = [node];
    if (node.synthetic && node.getEventNodes) {
      nodes = node.getEventNodes();
    }
    for (let i = 0; i < nodes.length; i++) {
      nodes[i].addEventListener(type, callback, options);
      events = events.concat({ node: nodes[i], type, callback, options });
    }
  },
  deregisterEvents(node) {
    events = events.filter((t) => {
      let old = t.node;
      if (t.node.synthetic) { old = t.node.element; }
      if (t.node === node || old === node || node.contains(old)) {
        old.removeEventListener(t.type, t.callback, t.options);
        return false;
      }
      return true;
    });
  },
  moveEvents(a, b) {
    events = events.map((t) => {
      if (t.node === a) {
        a.removeEventListener(t.type, t.callback, t.options);
        b.addEventListener(t.type, t.callback, t.options);
        return { node: b, type: t.type, callback: t.callback, options: t.options };
      }
      return t;
    });
  },
  syntheticToEl(newEl) {
    // Deal with synthetic elements / strings
    if (typeof newEl === 'object' && newEl.synthetic) {
      return newEl.element;
    } if (typeof newEl === 'string') {
      return document.createTextNode(newEl);
    }
    return newEl;
  },
  renderToEl(p, c) {
    if (c == null) { c = []; } // eslint-disable-line no-param-reassign
    if (p == null) {
      console.warn('Parent undefined. Parent:', p, 'Child:', c); // eslint-disable-line no-console
      return;
    }
    if (c instanceof HTMLCollection || c instanceof NodeList) {
      c = Array.prototype.slice.call(c); // eslint-disable-line no-param-reassign
    }
    if (!(c instanceof Array)) {
      c = [c]; // eslint-disable-line no-param-reassign
    }
    let i;
    const children = Array.prototype.slice.call(p.childNodes); // Convert to array
    for (i = 0; i < children.length; i++) {
      const currentNode = children[i];
      // Remove excess children
      if (i >= c.length) {
        p.removeChild(currentNode);
        continue;
      }

      // Deal with synthetic elements / strings
      const newEl = Render.syntheticToEl(c[i]);

      // Update node
      if (currentNode.nodeValue == null
        && currentNode.nodeName === newEl.nodeName
        && currentNode.nodeType === newEl.nodeType
        && newEl.nodeName !== 'CANVAS') {
        const attrs = Array.prototype.slice.call(currentNode.attributes);
        const attrNames = attrs.map((t) => t.name);
        const newAttrs = Array.prototype.slice.call(newEl.attributes);
        // Remove unneeded attributes
        for (let j = 0; j < attrs.length; j++) {
          if (attrNames.includes(attrs[j].name)) { continue; }
          currentNode.removeAttribute(attrs[j].name);
        }
        // Set new/updated attributes
        for (let j = 0; j < newAttrs.length; j++) {
          currentNode.setAttribute(newAttrs[j].name, newAttrs[j].value);
        }

        // Copy over some input stuff
        if (!!newEl.checked !== !!currentNode.checked) {
          currentNode.checked = newEl.checked;
        }
        if (newEl.value !== currentNode.value) {
          currentNode.value = newEl.value;
        }
        if (newEl.type !== currentNode.type) {
          currentNode.type = newEl.type;
        }
        if (newEl.disabled !== currentNode.disabled) {
          currentNode.disabled = newEl.disabled;
        }
        if (newEl.className !== currentNode.className) {
          currentNode.className = newEl.className;
        }
        // Register / deregister events
        Render.deregisterEvents(currentNode);
        Render.moveEvents(newEl, currentNode);
        // Set innerHTML the same way
        Render.renderToEl(currentNode, newEl.childNodes);
        Render.deregisterEvents(newEl);
      } else { // Replace node
        Render.deregisterEvents(currentNode);
        currentNode.replaceWith(newEl);
      }
    }

    // Add extra children
    while (i < c.length) {
      const newEl = Render.syntheticToEl(c[i]);
      p.appendChild(newEl);
      i++;
    }
  },
};