lightview 1.4.10-b → 1.5.1-b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lightview.js CHANGED
@@ -85,7 +85,7 @@ const {observe} = (() => {
85
85
  return observer;
86
86
  }
87
87
  const coerce = (value, toType) => {
88
- if (value + "" === "null" || value + "" === "undefined") return value;
88
+ if (value + "" === "null" || value + "" === "undefined" || toType==="any") return value;
89
89
  const type = typeof (value);
90
90
  if (type === toType) return value;
91
91
  if (toType === "number") return parseFloat(value + "");
@@ -103,7 +103,7 @@ const {observe} = (() => {
103
103
  if (toType === "string") return value + "";
104
104
  const isfunction = typeof (toType) === "function";
105
105
  if ((toType === "object" || isfunction)) {
106
- if (type === "object") {
106
+ if (type === "object" && isfunction) {
107
107
  if (value instanceof toType) return value;
108
108
  }
109
109
  if (type === "string") {
@@ -164,6 +164,9 @@ const {observe} = (() => {
164
164
  return JSON.stringify([...target]);
165
165
  }
166
166
  }
167
+ if(target instanceof Date) {
168
+ return Reflect.get(target,property);
169
+ }
167
170
  let value = target[property];
168
171
  const type = typeof (value);
169
172
  if (CURRENTOBSERVER && typeof (property) !== "symbol" && type !== "function") {
@@ -220,12 +223,14 @@ const {observe} = (() => {
220
223
  const createVarsProxy = (vars, component, constructor) => {
221
224
  return new Proxy(vars, {
222
225
  get(target, property) {
226
+ if(target instanceof Date) {
227
+ return Reflect.get(target,property);
228
+ }
223
229
  let {value} = target[property] || {};
224
230
  if (typeof (value) === "function") return value.bind(target);
225
231
  return value;
226
232
  },
227
233
  set(target, property, newValue) {
228
- //if(newValue && typeof(newValue)==="object" && newValue instanceof Promise) newValue = await newValue;
229
234
  const event = new VariableEvent({variableName: property, value: newValue});
230
235
  if (target[property] === undefined) {
231
236
  target[property] = {type: "any", value: newValue}; // should we allow this, do first to prevent loops
@@ -233,11 +238,13 @@ const {observe} = (() => {
233
238
  if (event.defaultPrevented) delete target[property].value;
234
239
  return true;
235
240
  }
236
- const {type, value, shared, exported, constant, reactive, remote} = target[property];
241
+ const variable = target[property],
242
+ {type, value, shared, exported, constant, reactive, remote} = variable;
237
243
  if (constant) throw new TypeError(`${property}:${type} is a constant`);
244
+ newValue = type.validate ? type.validate(newValue,target[property]) : coerce(newValue,type);
238
245
  const newtype = typeof (newValue),
239
246
  typetype = typeof (type);
240
- if (newValue == null || type === "any" || newtype === type || (typetype === "function" && newValue && newtype === "object" && newValue instanceof type)) {
247
+ if (newValue == null || type === "any" || (newtype === type && typetype==="string") || (typetype === "function" && (newValue && newtype === "object" && newValue instanceof type) || variable.validityState?.valid)) {
241
248
  if (value !== newValue) {
242
249
  event.oldValue = value;
243
250
  target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
@@ -245,7 +252,7 @@ const {observe} = (() => {
245
252
  if (event.defaultPrevented) {
246
253
  target[property].value = value;
247
254
  } else if(remote) {
248
- handleRemote({variable:target[property],remote,reactive},true);
255
+ handleRemote({variable,remote,reactive},true);
249
256
  }
250
257
  }
251
258
  return true;
@@ -261,22 +268,21 @@ const {observe} = (() => {
261
268
  });
262
269
  }
263
270
  const createObserver = (domNode, framed) => {
264
- const observer = new MutationObserver((mutations) => {
271
+ const mobserver = new MutationObserver((mutations) => {
265
272
  mutations.forEach((mutation) => {
273
+ const target = mutation.target;
266
274
  if (mutation.type === "attributes") {
267
275
  //if (framed) debugger;
268
276
  const name = mutation.attributeName,
269
- target = mutation.target,
270
277
  value = target.getAttribute(name);
271
278
  if (framed && name === "message" && target instanceof IFrameElement) {
272
- if (value) console.log("message", value);
279
+ //if (value) console.log("message", value);
273
280
  target.removeAttribute(name);
274
281
  target.dispatchEvent(new CustomEvent("message", {detail: JSON.parse(value)}))
275
282
  }
276
283
  if (target.observedAttributes && target.observedAttributes.includes(name)) {
277
284
  if (value !== mutation.oldValue) {
278
- target.setValue(name, value);
279
- if (target.attributeChangedCallback) target.attributeChangedCallback(name, value, mutation.oldValue);
285
+ target.setVariableValue(name, value);
280
286
  }
281
287
  }
282
288
  } else if (mutation.type === "childList") {
@@ -286,11 +292,13 @@ const {observe} = (() => {
286
292
  for (const target of mutation.addedNodes) {
287
293
  if (target.connectedCallback) target.connectedCallback();
288
294
  }
295
+ } else if(mutation.type === "characterData") {
296
+ if(target.characterDataMutationCallback) target.characterDataMutationCallback(target,mutation.oldValue,target.textContent);
289
297
  }
290
298
  });
291
299
  });
292
- observer.observe(domNode, {subtree: true, childList: true});
293
- return observer;
300
+ mobserver.observe(domNode, {subtree: true, childList: true});
301
+ return mobserver;
294
302
  }
295
303
  const querySelectorAll = (node, selector) => {
296
304
  const nodes = [...node.querySelectorAll(selector)],
@@ -313,17 +321,17 @@ const {observe} = (() => {
313
321
  nodes.push(node);
314
322
  } else if (node.nodeType === Node.ELEMENT_NODE) {
315
323
  let skip, pushed;
316
- [...node.attributes].forEach((attr) => {
317
- if (attr.value.includes("${")) {
318
- attr.template ||= attr.value;
319
- pushed = true;
320
- nodes.push(node);
321
- } else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
322
- skip = attr.name.includes("l-for:");
323
- pushed = true;
324
- nodes.push(node)
325
- }
326
- })
324
+ [...node.attributes].forEach((attr) => {
325
+ if (attr.value.includes("${")) {
326
+ attr.template ||= attr.value;
327
+ pushed = true;
328
+ nodes.push(node);
329
+ } else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
330
+ skip = attr.name.includes("l-for:");
331
+ pushed = true;
332
+ nodes.push(node)
333
+ }
334
+ })
327
335
  if (!pushed && node.getAttribute("type") === "radio") nodes.push(node);
328
336
  if (!skip && !node.shadowRoot) nodes.push(...getNodes(node));
329
337
  }
@@ -339,11 +347,12 @@ const {observe} = (() => {
339
347
  try {
340
348
  let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
341
349
  value = node.nodeType === Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
342
- if (type === "string") return value;
350
+ if (type === "string") return value==="undefined" ? undefined : value;
343
351
  node.nodeValue = value == "null" || value == "undefined" ? "" : value;
344
352
  } catch (e) {
345
- console.warn(e);
353
+ //console.warn(e);
346
354
  if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
355
+ return undefined;
347
356
  }
348
357
  }
349
358
  return node?.nodeValue;
@@ -376,16 +385,18 @@ const {observe} = (() => {
376
385
  if (bound.has(input)) return;
377
386
  bound.add(input);
378
387
  const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
379
- type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
380
- deflt = input.getAttribute("default");
388
+ type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype);
381
389
  value ||= input.getAttribute("value");
382
390
  let variable = component.vars[variableName] || {type};
383
391
  if (type !== variable.type) {
384
392
  if (variable.type === "any" || variable.type === "unknown") variable.type = type;
385
393
  else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
386
394
  }
387
- component.variables({[variableName]: type});
388
- if(inputtype!=="radio") component.setValue(variableName, value);
395
+ component.variables({[variableName]: type},{reactive:true});
396
+ if(inputtype!=="radio") {
397
+ if(value.includes("${")) input.attributes.value.value = "";
398
+ else component.setVariableValue(variableName, coerce(value,type));
399
+ }
389
400
  let eventname = "change";
390
401
  if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
391
402
  eventname = "input";
@@ -415,11 +426,6 @@ const {observe} = (() => {
415
426
  }
416
427
  }
417
428
  let reserved = {
418
- any: {value: "any", constant: true},
419
- boolean: {value: "boolean", constant: true},
420
- string: {value: "string", constant: true},
421
- number: {value: "number", constant: true},
422
- object: {value: "object", constant: true},
423
429
  observed: {value: true, constant: true},
424
430
  reactive: {value: true, constant: true},
425
431
  shared: {value: true, constant: true},
@@ -431,7 +437,9 @@ const {observe} = (() => {
431
437
  const instances = new Set(),
432
438
  dom = domElementNode.tagName === "TEMPLATE"
433
439
  ? domElementNode.content.cloneNode(true)
434
- : domElementNode.cloneNode(true);
440
+ : domElementNode.cloneNode(true),
441
+ observedAttributes = [];
442
+ observedAttributes.add = function(name) { observedAttributes.includes(name) || observedAttributes.push(name); }
435
443
  if (domElementNode.tagName === "TEMPLATE") domElementNode = domElementNode.cloneNode(true);
436
444
  return class CustomElement extends HTMLElement {
437
445
  static get instances() {
@@ -441,23 +449,20 @@ const {observe} = (() => {
441
449
  constructor() {
442
450
  super();
443
451
  instances.add(this);
444
- observer ||= createObserver(this, framed);
445
452
  const currentComponent = this,
446
453
  shadow = this.attachShadow({mode: "open"}),
447
454
  eventlisteners = {};
455
+ // needs to be local to the instance
456
+ Object.defineProperty(this,"changeListener",{value:
457
+ function({variableName, value}) {
458
+ if (currentComponent.changeListener.targets.has(variableName)) {
459
+ value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
460
+ if (value == null) removeComponentAttribute(currentComponent, variableName);
461
+ else setComponentAttribute(currentComponent, variableName, value);
462
+ }
463
+ }});
448
464
  this.vars = {
449
465
  ...reserved,
450
- changeListener: {
451
- value: ({variableName, value}) => {
452
- if (currentComponent.vars.changeListener.value.targets.has(variableName)) {
453
- value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
454
- if (value == null) removeComponentAttribute(this, variableName);
455
- else setComponentAttribute(this, variableName, value);
456
- }
457
- },
458
- type: "function",
459
- constant: true
460
- },
461
466
  addEventListener: {
462
467
  value: (eventName, listener) => {
463
468
  const listeners = eventlisteners[eventName] ||= new Set();
@@ -470,9 +475,10 @@ const {observe} = (() => {
470
475
  constant: true
471
476
  },
472
477
  postEvent: {
473
- value: (eventName, event) => {
478
+ value: (eventName, event={}) => {
474
479
  //event = {...event}
475
480
  event.type = eventName;
481
+ event.target = currentComponent;
476
482
  eventlisteners[eventName]?.forEach((f) => f(event));
477
483
  },
478
484
  type: "function",
@@ -482,8 +488,8 @@ const {observe} = (() => {
482
488
  };
483
489
  this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
484
490
  this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
485
- this.vars.changeListener.value.targets = new Set();
486
- this.varsProxy.addEventListener("change", this.varsProxy.changeListener);
491
+ this.changeListener.targets = new Set();
492
+ this.varsProxy.addEventListener("change", this.changeListener);
487
493
  if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported: true});
488
494
  ["getElementById", "querySelector", "querySelectorAll"]
489
495
  .forEach((fname) => {
@@ -514,27 +520,39 @@ const {observe} = (() => {
514
520
  shadow = ctx.shadowRoot;
515
521
  for (const attr of this.defaultAttributes) this.hasAttribute(attr.name) || this.setAttribute(attr.name, attr.value);
516
522
  const scripts = shadow.querySelectorAll("script"),
517
- promises = [];
518
- for (const script of scripts) {
523
+ promises = [],
524
+ scriptpromises = [];
525
+ for (const script of [...scripts]) {
519
526
  if (script.attributes.src?.value?.includes("/lightview.js")) continue;
520
- if (script.className !== "lightview" && !((script.getAttribute("type") || "").includes("lightview/"))) continue;
521
- const scriptid = Math.random() + "",
522
- currentScript = document.createElement("script");
527
+ const currentScript = document.createElement("script");
528
+ if (script.className !== "lightview" && !((script.attributes.type?.value || "").includes("lightview/"))) {
529
+ for (const attr of script.attributes) currentScript.setAttribute(attr.name,attr.value);
530
+ scriptpromises.push(new Promise((resolve) => {
531
+ currentScript.onload = () => resolve();
532
+ }))
533
+ shadow.appendChild(currentScript);
534
+ currentScript.remove();
535
+ continue;
536
+ };
537
+ const scriptid = Math.random() + "";
523
538
  for (const attr of script.attributes) {
524
539
  currentScript.setAttribute(attr.name, attr.name === "type" ? attr.value.replace("lightview/", "") : attr.value);
525
540
  }
526
541
  currentScript.classList.remove("lightview");
527
542
  const text = script.innerHTML.replaceAll(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "$1").replaceAll(/\r?\n/g, "");
528
- currentScript.innerHTML = `Object.getPrototypeOf(async function(){}).constructor('if(window["${scriptid}"]?.ctx) { with(window["${scriptid}"].ctx) { ${text}; } window["${scriptid}"](); }')(); `;
543
+ currentScript.innerHTML = `Object.getPrototypeOf(async function(){}).constructor('if(window["${scriptid}"]?.ctx) { const ctx = window["${scriptid}"].ctx; { with(ctx) { ${text}; } } }')().then(() => window["${scriptid}"]()); `;
529
544
  let resolver;
530
- promises.push(new Promise((resolve) => resolver = resolve));
531
545
  window[scriptid] = () => {
532
546
  delete window[scriptid];
533
547
  currentScript.remove();
534
548
  resolver();
535
549
  }
536
550
  window[scriptid].ctx = ctx.varsProxy;
537
- ctx.appendChild(currentScript);
551
+ promises.push(new Promise((resolve) => {
552
+ resolver = resolve;
553
+ // wait for all regular scripts to load first
554
+ Promise.all(scriptpromises).then(() => shadow.appendChild(currentScript));
555
+ }));
538
556
  }
539
557
  Promise.all(promises).then(() => {
540
558
  const nodes = getNodes(ctx);
@@ -543,70 +561,68 @@ const {observe} = (() => {
543
561
  render(!!node.template, () => resolveNodeOrText(node, this))
544
562
  } else if (node.nodeType === Node.ELEMENT_NODE) {
545
563
  // resolve the value before all else;
546
- const attr = node.attributes.value;
547
- if (attr && attr.template) {
548
- render(!!attr.template, () => {
549
- const value = resolveNodeOrText(attr, this),
564
+ const attr = node.attributes.value,
565
+ template = attr?.template;
566
+ if (attr && template) {
567
+ //render(!!template, () => {
568
+ let value = resolveNodeOrText(attr, this),
550
569
  eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
551
- if (node.attributes.value) {
552
- const template = attr.template;
570
+ const template = attr.template;
571
+ if (template) {
553
572
  if (/\$\{[a-zA-z_]+\}/g.test(template)) {
554
573
  const name = template.substring(2, template.length - 1);
555
- bindInput(node, name, this, value);
556
- }
557
- }
558
- if (eltype === "checkbox") {
559
- if (coerce(value, "boolean") === true) {
560
- node.setAttribute("checked", "");
561
- node.checked = true;
562
- } else {
563
- node.removeAttribute("checked");
564
- node.checked = false;
565
- }
566
- const vname = resolveNodeOrText(node.attributes.name, ctx);
567
- if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
568
- }
569
- if (node.tagName === "SELECT") {
570
- let values = [value];
571
- if (node.hasAttribute("multiple")) values = coerce(value, Array);
572
- [...node.querySelectorAll("option")].forEach(async (option) => {
573
- if (option.hasAttribute("value")) {
574
- if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
575
- option.setAttribute("selected", "");
576
- option.selected = true;
577
- }
578
- } else if (values.includes(resolveNodeOrText(option.innerText, ctx))) {
579
- option.setAttribute("selected", "");
580
- option.selected = true;
574
+ if(!this.vars[name] || this.vars[name].reactive) {
575
+ bindInput(node, name, this, value);
581
576
  }
582
- })
583
- }
584
- });
585
- }
586
- [...node.attributes].forEach(async (attr) => {
587
- if (attr.name === "value" && attr.template) return;
588
- const {name, value} = attr;
589
- if (name === "type") {
590
- if (value === "radio" && node.attributes.name) {
591
- const name = resolveNodeOrText(node.attributes.name, ctx);
592
- for (const vname of this.getVariableNames()) {
593
- if (vname === name) {
594
- render(true, () => {
595
- const name = resolveNodeOrText(node.attributes.name, ctx),
596
- varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
597
- if (node.attributes.value && varvalue == resolveNodeOrText(node.attributes.value, ctx)) {
577
+ }
578
+ observe(() => {
579
+ const value = resolveNodeOrText(template, ctx);
580
+ if(value!==undefined) {
581
+ if (eltype === "checkbox") {
582
+ if (coerce(value, "boolean") === true) {
598
583
  node.setAttribute("checked", "");
599
584
  node.checked = true;
600
585
  } else {
601
586
  node.removeAttribute("checked");
602
587
  node.checked = false;
603
588
  }
604
- });
605
- bindInput(node, name, ctx);
606
- break;
589
+ } else if (node.tagName === "SELECT") {
590
+ let values = [value];
591
+ if (node.hasAttribute("multiple")) values = coerce(value, Array);
592
+ [...node.querySelectorAll("option")].forEach(async (option) => {
593
+ if (option.hasAttribute("value")) {
594
+ if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
595
+ option.setAttribute("selected", "");
596
+ option.selected = true;
597
+ }
598
+ } else if (values.includes(resolveNodeOrText(option.innerText, ctx))) {
599
+ option.setAttribute("selected", "");
600
+ option.selected = true;
601
+ }
602
+ })
603
+ } else if (eltype!=="radio") {
604
+ attr.value = value;
605
+ }
607
606
  }
608
- }
607
+ });
609
608
  }
609
+ }
610
+ [...node.attributes].forEach(async (attr) => {
611
+ if (attr.name === "value" && attr.template) return;
612
+ const {name, value} = attr,
613
+ vname = node.attributes.name?.value;
614
+ if (name === "type" && value=="radio" && vname) {
615
+ bindInput(node, vname, this);
616
+ render(true, () => {
617
+ const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
618
+ if (node.attributes.value.value == varvalue) {
619
+ node.setAttribute("checked", "");
620
+ node.checked = true;
621
+ } else {
622
+ node.removeAttribute("checked");
623
+ node.checked = false;
624
+ }
625
+ });
610
626
  }
611
627
 
612
628
  const [type, ...params] = name.split(":");
@@ -667,42 +683,33 @@ const {observe} = (() => {
667
683
  else node.appendChild(parsed.body.firstChild);
668
684
  }
669
685
  })
670
- } else if (attr.template) {
671
- render(!!attr.template, () => resolveNodeOrText(attr, this));
686
+ } else {
687
+ render(!!attr.template, () => {
688
+ resolveNodeOrText(attr, this);
689
+ })
672
690
  }
673
691
  })
674
692
  }
675
693
  })
676
694
  shadow.normalize();
677
- observer.observe(ctx, {attributeOldValue: true});
678
- if (ctx.hasOwnProperty("connectedCallback")) ctx.connectedCallback();
695
+ observer ||= createObserver(ctx, framed);
696
+ observer.observe(ctx, {attributeOldValue: true, subtree:true, characterData:true, characterDataOldValue:true});
697
+ ctx.vars.postEvent.value("connected");
679
698
  })
680
699
  }
681
-
682
- adopted(value) {
683
- this.adoptedCallback = value;
684
- //Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
700
+ adoptedCallback(callback) {
701
+ this.vars.postEvent.value("adopted");
702
+ super.adoptedCallback();
685
703
  }
686
-
687
- connected(callback) {
688
- this.connectedCallback = callback;
689
- //Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
704
+ disconnectedCallback() {
705
+ this.vars.postEvent.value("disconnected");
706
+ super.disconnectedCallback();
690
707
  }
691
-
692
- attributeChanged(callback) {
693
- this.attributeChangedCallback = callback;
694
- //Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
708
+ get observedAttributes() {
709
+ return CustomElement.observedAttributes;
695
710
  }
696
-
697
- disconnected(callback) {
698
- Object.defineProperty(this, "disconnectedCallback", {
699
- configurable: true,
700
- writable: true,
701
- value: () => {
702
- value();
703
- super.disconnectedCallback(callback);
704
- }
705
- });
711
+ static get observedAttributes() {
712
+ return observedAttributes;
706
713
  }
707
714
 
708
715
  getVariableNames() {
@@ -711,17 +718,17 @@ const {observe} = (() => {
711
718
  })
712
719
  }
713
720
 
714
- setValue(variableName, value, {coerceTo = typeof (value)} = {}) {
721
+ setVariableValue(variableName, value, {coerceTo = typeof (value)} = {}) {
715
722
  if (!this.isConnected) {
716
723
  instances.delete(this);
717
724
  return false;
718
725
  }
719
726
  let {type} = this.vars[variableName] || {};
720
727
  if (type) {
721
- value = coerce(value, type);
722
728
  if (this.varsProxy[variableName] !== value) {
723
729
  const variable = this.vars[variableName];
724
730
  if (variable.shared) {
731
+ value = type.validate ? type.validate(value,variable) : coerce(value,coerceTo);
725
732
  const event = new VariableEvent({
726
733
  variableName: variableName,
727
734
  value: value,
@@ -740,7 +747,7 @@ const {observe} = (() => {
740
747
  return false;
741
748
  }
742
749
 
743
- getValue(variableName) {
750
+ getVariableValue(variableName) {
744
751
  return this.vars[variableName]?.value;
745
752
  }
746
753
 
@@ -754,6 +761,7 @@ const {observe} = (() => {
754
761
  variable.value = this.hasAttribute(key) ? coerce(this.getAttribute(key), variable.type) : variable.value;
755
762
  variable.observed = observed;
756
763
  variable.imported = imported;
764
+ if(variable.observed) this.observedAttributes.add(key);
757
765
  }
758
766
  if (reactive) {
759
767
  variable.reactive = true;
@@ -762,21 +770,20 @@ const {observe} = (() => {
762
770
  if (shared) {
763
771
  variable.shared = true;
764
772
  addEventListener("change", ({variableName, value}) => {
765
- if (this.vars[variableName]?.shared) {
766
- this.siblings.forEach((instance) => instance.setValue(variableName, value))
767
- }
773
+ if (this.vars[variableName]?.shared) this.siblings.forEach((instance) => instance.setVariableValue(variableName, value))
768
774
  })
769
775
  }
770
776
  if (exported) {
771
777
  variable.exported = true;
772
778
  // in case the export goes up to an iframe
773
779
  if (variable.value != null) setComponentAttribute(this, key, variable.value);
774
- this.vars.changeListener.value.targets.add(key);
780
+ this.changeListener.targets.add(key);
775
781
  }
776
782
  if (remote) {
777
783
  variable.remote = remote;
778
784
  handleRemote({variable, remote, reactive});
779
785
  }
786
+ if(type.validate) type.validate(undefined,variable);
780
787
  });
781
788
  }
782
789
  return Object.entries(this.vars)
@@ -820,7 +827,7 @@ const {observe} = (() => {
820
827
  let remotevalue;
821
828
  if (type === "string") {
822
829
  const href = new URL(remote,window.location.href).href;
823
- remotevalue = patch({target,property,value,oldValue},href);
830
+ remotevalue = patch({target,property,value,oldValue},href,variable);
824
831
  } else if(remote && type==="object") {
825
832
  let href;
826
833
  if(remote.path) href = new URL(remote.path,window.location.href).href;
@@ -828,21 +835,17 @@ const {observe} = (() => {
828
835
  if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
829
836
  remote.patch = patch;
830
837
  }
831
- remotevalue = remote.patch({target,property,value,oldValue},href);
838
+ remotevalue = remote.patch({target,property,value,oldValue},href,variable);
832
839
  }
833
840
  if(remotevalue) {
834
841
  await remotevalue.then((newjson) => {
835
842
  if (newjson && typeof (newjson) === "object" && reactive) {
836
843
  const target = variable.value?.__reactorProxyTarget__ ? json : variable.value;
837
844
  Object.entries(newjson).forEach(([key,newValue]) => {
838
- if(target[key]!==newValue) {
839
- target[key] = newValue;
840
- }
845
+ if(target[key]!==newValue) target[key] = newValue;
841
846
  })
842
847
  Object.keys(target).forEach((key) => {
843
- if(!(key in newjson)) {
844
- delete target[key];
845
- }
848
+ if(!(key in newjson)) delete target[key];
846
849
  });
847
850
  if(variable.value?.__reactorProxyTarget__) {
848
851
  const dependents = variable.value.__dependents__,
@@ -863,7 +866,7 @@ const {observe} = (() => {
863
866
  })
864
867
  }
865
868
 
866
- const patch = ({target,property,value,oldValue},href) => {
869
+ const patch = ({target,property,value,oldValue},href,variable) => {
867
870
  return fetch(href, {
868
871
  method: "PATCH",
869
872
  body: JSON.stringify({property,value,oldValue}),
@@ -875,14 +878,14 @@ const {observe} = (() => {
875
878
  })
876
879
  }
877
880
 
878
- const get = ({variable,remote,reactive},path) => {
879
- return fetch(path)
881
+ const get = (href,variable) => {
882
+ return fetch(href)
880
883
  .then((response) => {
881
884
  if (response.status < 400) return response.json();
882
885
  })
883
886
  }
884
887
 
885
- const put = ({variable,remote,reactive},href) => {
888
+ const put = (href,variable) => {
886
889
  return fetch(href, {
887
890
  method: "PUT",
888
891
  body: JSON.stringify(variable.value),
@@ -890,9 +893,7 @@ const {observe} = (() => {
890
893
  "Content-Type": "application/json"
891
894
  }
892
895
  }).then((response) => {
893
- if (response.status === 200) {
894
- return response.json();
895
- }
896
+ if (response.status === 200) return response.json();
896
897
  })
897
898
  }
898
899
 
@@ -902,8 +903,8 @@ const {observe} = (() => {
902
903
  if (type === "string") {
903
904
  const href = new URL(remote,window.location.href).href;
904
905
  value = (doput
905
- ? put({variable, remote, reactive},href)
906
- : get({variable, remote, reactive},href));
906
+ ? put(href,variable)
907
+ : get(href,variable));
907
908
  if(variable.value===undefined) variable.value = value;
908
909
  } else if (remote && type === "object") {
909
910
  let href;
@@ -914,8 +915,8 @@ const {observe} = (() => {
914
915
  if(!remote.put) remote.put = put;
915
916
  }
916
917
  value = (doput
917
- ? remote.put({variable, remote, reactive},href)
918
- : remote.get({variable, remote, reactive},href));
918
+ ? remote.put(href,variable)
919
+ : remote.get(href,variable));
919
920
  if(remote.ttl && !doput && !remote.intervalId) {
920
921
  remote.intervalId = setInterval(async () => {
921
922
  await handleRemote({variable, remote, reactive});
@@ -928,7 +929,7 @@ const {observe} = (() => {
928
929
  if (json && typeof (json) === "object" && reactive) {
929
930
  return remoteProxy({json, variable,remote, reactive})
930
931
  } else {
931
- return json;
932
+ return json;
932
933
  }
933
934
  })
934
935
  }
@@ -937,11 +938,8 @@ const {observe} = (() => {
937
938
  const createComponent = (name, node, {framed, observer} = {}) => {
938
939
  let ctor = customElements.get(name);
939
940
  if (ctor) {
940
- if (framed && !ctor.lightviewFramed) {
941
- ctor.lightviewFramed = true;
942
- } else {
943
- console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
944
- }
941
+ if (framed && !ctor.lightviewFramed) ctor.lightviewFramed = true;
942
+ else console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
945
943
  return ctor;
946
944
  }
947
945
  ctor = createClass(node, {observer, framed});
@@ -951,7 +949,6 @@ const {observe} = (() => {
951
949
  }
952
950
  Lightview.customElements = new Map();
953
951
  Lightview.createComponent = createComponent;
954
- //Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
955
952
  const importLink = async (link, observer) => {
956
953
  const url = (new URL(link.getAttribute("href"), window.location.href)),
957
954
  as = link.getAttribute("as") || getNameFromPath(url.pathname);
@@ -1046,21 +1043,19 @@ const {observe} = (() => {
1046
1043
  postMessage({type: "setAttribute", argsList: ["height", height + 20]});
1047
1044
  }
1048
1045
  resize();
1049
- onresize(document.body, () => {
1050
- resize();
1051
- });
1046
+ onresize(document.body, () => resize());
1052
1047
  return
1053
1048
  }
1054
1049
  if (type === "setAttribute") {
1055
1050
  const [name, value] = [...argsList],
1056
1051
  variable = document.body.vars[name];
1057
- if (variable && variable.imported) document.body.setValue(name, value);
1052
+ if (variable && variable.imported) document.body.setVariableValue(name, value);
1058
1053
  return;
1059
1054
  }
1060
1055
  if (type === "removeAttribute") {
1061
1056
  const [name] = argsList[0],
1062
1057
  variable = document.body.vars[name];
1063
- if (variable && variable.imported) document.body.setValue(name, undefined);
1058
+ if (variable && variable.imported) document.body.setVariableValue(name, undefined);
1064
1059
 
1065
1060
  }
1066
1061
  });
@@ -1098,9 +1093,7 @@ const {observe} = (() => {
1098
1093
  }
1099
1094
  if (type === "setAttribute") {
1100
1095
  const [name, value] = [...argsList];
1101
- if (iframe.getAttribute(name) !== value + "") {
1102
- iframe.setAttribute(name, value);
1103
- }
1096
+ if (iframe.getAttribute(name) !== value + "") iframe.setAttribute(name, value);
1104
1097
  return;
1105
1098
  }
1106
1099
  if (type === "removeAttribute") {