All files core.js

89.83% Statements 106/118
82.35% Branches 56/68
70.58% Functions 12/17
90.74% Lines 98/108

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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219                                          39x   240x   240x 2x 2x 1x   1x   2x       240x 10x 10x 10x       240x     240x             240x 261x 261x 261x     261x 261x 261x 261x 261x   240x 261x 261x 261x 261x 259x 2x 1x   1x   261x 261x       240x 1x 1x   240x 259x           240x 240x 240x 235x   5x         240x 240x       240x 1x 1x   239x 239x     239x 233x   6x 6x 6x     239x         239x 239x     239x 239x 239x                           239x 239x 239x 239x 239x     239x 239x 3x       239x 239x 3x   236x   2x   236x       239x 239x 239x   239x     6x 6x   6x 6x 6x 3x     3x 3x 3x         3x 3x 3x               239x 6x     239x       239x 239x   240x  
'use strict';
 
import "core-js/modules/es.array.index-of.js";
import "core-js/modules/es.array.is-array.js";
import "core-js/modules/es.array.iterator.js";
import "core-js/modules/es.array.slice.js";
import "core-js/modules/es.function.name.js";
import "core-js/modules/es.map.js";
import "core-js/modules/es.object.assign.js";
import "core-js/modules/es.object.to-string.js";
import "core-js/modules/es.string.iterator.js";
import "core-js/modules/es.string.trim.js";
import "core-js/modules/web.dom-collections.iterator.js";
import { Idiomorph } from 'idiomorph/dist/idiomorph.esm.js';
import { updateTextNodePlaceholders } from './placeholders';
import { directives } from './directives';
import { filters } from './filters';
import { processNode } from './processing';
import { helpers } from './utils';
import { fetchRequest, axiosRequest } from './requests';
import { logError } from './logger';
var defaultRequestHeaders = {};
export function AppBlock(config) {
  var _this = this;
  // Sets or Updates the data and then calls render()
  this.setData = function (newData) {
    var replaceData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    if (replaceData) {
      this.data = newData;
    } else {
      Object.assign(this.data, newData);
    }
    this.render();
  };
 
  // Resets to the default state. Handy before making a request.
  this.resetState = function () {
    this.state.loading = false;
    this.state.error = false;
    this.state.success = false;
  };
 
  // Requests
  this.axiosRequest = function (options, callbacks, delay) {
    return axiosRequest(_this, options, callbacks, delay);
  };
  this.fetchRequest = function (url, options, callbacks, delay) {
    return fetchRequest(_this, url, options, callbacks, delay);
  };
 
  // Render ============================================================================================================
  // This is the heart of an AppBlock. This is where all placeholders and directives get evaluated based on our
  // data, and content gets updated.
  this.prepareTmpDom = function () {
    var comp = this;
    var cache = new Map(); // Per-render ephemeral cache
    var tmpDOM = comp.template.cloneNode(true);
 
    // Wrap in a div to handle directives on root elements
    var wrapper = document.createElement('div');
    wrapper.appendChild(tmpDOM);
    processNode(comp, wrapper, cache);
    updateTextNodePlaceholders(comp, wrapper, null, cache);
    return wrapper;
  };
  this.render = function (callback) {
    var comp = this;
    if (comp.methods.beforeRender instanceof Function) comp.methods.beforeRender(comp);
    var tmpDOM = this.prepareTmpDom();
    if (comp.renderEngine === 'Idiomorph') {
      comp.idiomorphRender(tmpDOM);
    } else if (comp.renderEngine === 'plain') {
      comp.plainRender(tmpDOM);
    } else {
      logError(comp, "".concat(comp.renderEngine, " renderEngine does not exist."));
    }
    if (comp.methods.afterRender instanceof Function) comp.methods.afterRender(comp);
    if (callback instanceof Function) callback();
  };
 
  // Render engines
  this.plainRender = function (tmpDOM) {
    this.el.innerHTML = '';
    this.el.appendChild(tmpDOM);
  };
  this.idiomorphRender = function (tmpDOM) {
    Idiomorph.morph(this.el, tmpDOM, {
      morphStyle: 'innerHTML'
    });
  };
 
  // Initialization ====================================================================================================
  this.Init = function () {
    var comp = this;
    if (config.name) {
      comp.name = config.name;
    } else {
      comp.name = "AppBlock";
    }
 
    // Initialize all the properties and update them from the config if they are included, or exit if no
    // config is provided.
    if (config !== undefined) {
      Iif (config.el === undefined) {
        logError(comp, "el is empty. Please assign a DOM element to el.");
        return;
      }
      if (config.el === null) {
        logError(comp, "The element you assigned to el is not present.");
        return;
      }
      comp.el = config.el;
      comp.renderEngine = config.renderEngine ? config.renderEngine : "Idiomorph";
 
      // Get or create a document fragment with all the app's contents and pass it to the template.
      if (config.template) {
        comp.template = config.template.content;
      } else {
        comp.template = document.createDocumentFragment();
        while (comp.el.firstChild) {
          comp.template.appendChild(comp.el.firstChild);
        }
      }
      comp.state = {
        loading: false,
        error: false,
        success: false
      };
      comp.data = {};
      if (config.data instanceof Object) comp.data = config.data;
 
      // A set of helper functions.
      comp.utils = helpers;
      comp.utils['comp'] = comp;
      comp.methods = {
        Parent: comp,
        isLoading: function isLoading(thisApp) {
          return thisApp.state.loading;
        },
        isSuccessful: function isSuccessful(thisApp) {
          return thisApp.state.success;
        },
        hasError: function hasError(thisApp) {
          return thisApp.state.error;
        },
        beforeRender: function beforeRender(thisApp) {},
        afterRender: function afterRender(thisApp) {}
      };
      if (config.methods instanceof Object) Object.assign(comp.methods, config.methods);
      comp.directives = directives;
      if (config.directives instanceof Object) Object.assign(comp.directives, config.directives);
      comp.filters = filters;
      if (config.filters instanceof Object) Object.assign(comp.filters, config.filters);
 
      // Expression evaluation built-ins allow-list. Expect an array of strings (default: empty, no built-ins allowed).
      comp.allowBuiltins = [];
      if (Array.isArray(config.allowBuiltins)) {
        comp.allowBuiltins = config.allowBuiltins;
      }
 
      // Placeholder delimiters configuration. Expect an array of two non-empty strings.
      var defaultDelimiters = ['{', '}'];
      if (Array.isArray(config.delimiters) && config.delimiters.length === 2 && typeof config.delimiters[0] === 'string' && typeof config.delimiters[1] === 'string' && config.delimiters[0].length > 0 && config.delimiters[1].length > 0) {
        comp.delimiters = config.delimiters;
      } else {
        if (config.delimiters !== undefined) {
          // Developer provided an invalid configuration — log and fallback to default
          logError(comp, 'Invalid `delimiters` config provided. Falling back to default [`{`,`}`].');
        }
        comp.delimiters = defaultDelimiters;
      }
 
      // Event handling ------------------------------------------------------------------------------------------------
      comp.events = {};
      Eif (config.events instanceof Object) {
        Object.assign(comp.events, config.events);
        // Add event listeners to :el for each event
        var _loop = function _loop(ev) {
          // Events are in this form "<eventName> <cssSelector>" where the selector may contain spaces.
          // Split only on the first space so the remainder is treated as a full selector string.
          var firstSpace = ev.indexOf(' ');
          Iif (firstSpace === -1) return 1; // continue
          // invalid key
          var eventName = ev.slice(0, firstSpace);
          var eventSelector = ev.slice(firstSpace + 1).trim();
          comp.el.addEventListener(eventName, function (e) {
            var target = e.target || e.srcElement;
            // Use closest to test whether the event originated from within a matching element.
            // Ensure the matched element is inside this AppBlock's root.
            var matched = null;
            try {
              Eif (target && target.closest) matched = target.closest(eventSelector);
            } catch (err) {
              // If the selector is invalid, skip handling to avoid breaking the app.
              matched = null;
            }
            Eif (matched && comp.el.contains(matched)) {
              try {
                comp.events[ev](e, matched);
              } catch (err) {
                // swallow handler errors to avoid breaking other handlers
                logError(comp, err && err.message ? err.message : String(err));
              }
            }
          });
        };
        for (var ev in comp.events) {
          Iif (_loop(ev)) continue;
        }
      }
      comp.events['Parent'] = comp;
    } else E{
      return false;
    }
    comp.render();
    return comp;
  };
  return this.Init();
}