{
"version": 3,
"sources": ["../build/lib/lw-expr-parser.js", "../build/lib/lw-event-bus.js", "../build/lib/lw-element.js", "../build/components/root/ast.js", "../build/components/root/root.js", "../build/components/terminal/ast.js", "../build/components/terminal/terminal.js", "../build/components/header/ast.js", "../build/components/header/header.js", "../build/components/footer/ast.js", "../build/components/footer/footer.js", "../build/components/section-primary/ast.js", "../build/components/section-primary/section-primary.js", "../build/components/sections/code-01/ast.js", "../build/components/sections/code-01/code-01.js", "../build/components/sections/code-02/ast.js", "../build/components/sections/code-02/code-02.js", "../build/components/sections/code-03/ast.js", "../build/components/sections/code-03/code-03.js", "../build/components/sections/code-04/ast.js", "../build/components/sections/code-04/code-04.js", "../build/components/sections/code-05/ast.js", "../build/components/sections/code-05/code-05.js", "../build/components/sections/code-06/ast.js", "../build/components/sections/code-06/code-06.js", "../build/components/sections/code-06/parent/ast.js", "../build/components/sections/code-06/parent/parent.js", "../build/components/sections/code-06/child/ast.js", "../build/components/sections/code-06/child/child.js", "../build/components/sections/code-07/ast.js", "../build/components/sections/code-07/code-07.js", "../build/components/sections/code-08/ast.js", "../build/components/sections/code-08/code-08.js", "../build/components/sections/code-09/ast.js", "../build/components/sections/code-09/code-09.js", "../build/components/sections/code-10/ast.js", "../build/components/sections/code-10/code-10.js", "../build/components/sections/code-11/ast.js", "../build/components/sections/code-11/code-11.js", "../build/components/sections/code-12/ast.js", "../build/components/sections/code-12/code-12.js", "../build/components/sections/code-12/pub/ast.js", "../build/components/sections/code-12/pub/pub.js", "../build/components/sections/code-12/sub/ast.js", "../build/components/sections/code-12/sub/sub.js", "../build/components/sections/code-13/ast.js", "../build/components/sections/code-13/code-13.js", "../build/components/sections/commands/ast.js", "../build/components/sections/commands/commands.js", "../build/components/sections/howtos/ast.js", "../build/components/sections/howtos/howtos.js", "../build/components/sections/code-14/ast.js", "../build/components/sections/code-14/code-14.js", "../build/components/sections/code-14/dest-red/ast.js", "../build/components/sections/code-14/dest-red/dest-red.js", "../build/components/sections/code-14/dest-blue/ast.js", "../build/components/sections/code-14/dest-blue/dest-blue.js", "../build/components/sections/code-15/ast.js", "../build/components/sections/code-15/code-15.js", "../build/components/sections/code-15/parent/ast.js", "../build/components/sections/code-15/parent/parent.js", "../build/components/sections/code-15/child/ast.js", "../build/components/sections/code-15/child/child.js", "../build/components/sections/code-16/ast.js", "../build/components/sections/code-16/code-16.js", "../build/components/sections/code-16/parent/ast.js", "../build/components/sections/code-16/parent/parent.js", "../build/components/sections/code-16/child/ast.js", "../build/components/sections/code-16/child/child.js", "../build/components/sections/code-00/ast.js", "../build/components/sections/code-00/code-00.js"],
"sourcesContent": ["const binaryOperations = {\n '==': (a, b) => a == b,\n '!=': (a, b) => a != b,\n '===': (a, b) => a === b,\n '!==': (a, b) => a !== b,\n '<': (a, b) => a < b,\n '<=': (a, b) => a <= b,\n '>': (a, b) => a > b,\n '>=': (a, b) => a >= b,\n '<<': (a, b) => a << b,\n '>>': (a, b) => a >> b,\n '>>>': (a, b) => a >>> b,\n '+': (a, b) => a + b,\n '-': (a, b) => a - b,\n '*': (a, b) => a * b,\n '/': (a, b) => a / b,\n '%': (a, b) => a % b,\n '**': (a, b) => a ** b,\n '|': (a, b) => a | b,\n '^': (a, b) => a ^ b,\n '&': (a, b) => a & b,\n 'in': (a, b) => a in b,\n 'instanceof': (a, b) => a instanceof b,\n // '|>': (a, b) => a |> b,\n};\n\nconst assignmentOperations = {\n '=': (c, a, b) => { c[a] = b; },\n '+=': (c, a, b) => { c[a] += b; },\n '-=': (c, a, b) => { c[a] -= b; },\n '*=': (c, a, b) => { c[a] *= b; },\n '/=': (c, a, b) => { c[a] /= b; },\n '%=': (c, a, b) => { c[a] %= b; },\n '**=': (c, a, b) => { c[a] **= b; },\n '&&=': (c, a, b) => { c[a] &&= b; },\n '??=': (c, a, b) => { c[a] ??= b; },\n '||=': (c, a, b) => { c[a] ||= b; },\n '>>=': (c, a, b) => { c[a] >>= b; },\n '>>>=': (c, a, b) => { c[a] >>>= b; },\n '<<=': (c, a, b) => { c[a] <<= b; },\n '&=': (c, a, b) => { c[a] &= b; },\n '|=': (c, a, b) => { c[a] |= b; },\n '^=': (c, a, b) => { c[a] ^= b; },\n};\n\nconst logicalOperators = {\n '||': (a, b) => a || b,\n '&&': (a, b) => a && b,\n '??': (a, b) => a ?? b,\n};\n\nconst unaryOperators = {\n '-': a => -a,\n '+': a => +a,\n '!': a => !a,\n '~': a => ~a,\n 'typeof': a => typeof a,\n 'void': a => void a,\n // 'delete': a => delete a,\n 'throw': a => { throw a; },\n};\n\nconst updateOperators = (operator, prefix) => {\n if (operator === '++') {\n return (c, a) => prefix ? ++c[a] : c[a]++;\n } else if (operator === '--') {\n return (c, a) => prefix ? --c[a] : c[a]--;\n }\n};\n\nconst callFunction = (node, context) => {\n const callee = evalNode(node.callee, context);\n if (node.callee.type === 'OptionalMemberExpression' && (callee === void 0 || callee === null)) {\n return void 0;\n }\n const args = [];\n node.arguments.map(argument => {\n if (argument.type === 'SpreadElement') {\n args.push(...evalNode(argument, context));\n } else {\n args.push(evalNode(argument, context));\n }\n });\n return callee(...args);\n};\n\nconst nodeHandlers = {\n 'NumericLiteral': (node, context) => node.value,\n 'StringLiteral': (node, context) => node.value,\n 'BooleanLiteral': (node, context) => node.value,\n 'NullLiteral': (node, context) => null,\n\n 'RegExpLiteral': (node, context) => new RegExp(node.pattern, node.flags),\n\n 'ExpressionStatement': (node, context) => evalNode(node.expression, context),\n 'BinaryExpression': (node, context) => binaryOperations[node.operator](evalNode(node.left, context), evalNode(node.right, context)),\n 'AssignmentExpression': (node, context) => {\n const immediateCtx = immediateContext(node.left, context);\n assignmentOperations[node.operator](immediateCtx, node.left.name, evalNode(node.right, context));\n },\n 'LogicalExpression': (node, context) => logicalOperators[node.operator](evalNode(node.left, context), evalNode(node.right, context)),\n 'UnaryExpression': (node, context) => unaryOperators[node.operator](evalNode(node.argument, context)),\n 'UpdateExpression': (node, context) => {\n const immediateCtx = immediateContext(node.argument, context);\n updateOperators(node.operator, node.prefix)(immediateCtx, node.argument.name, evalNode(node.argument, context));\n },\n 'ConditionalExpression': (node, context) => {\n const test = evalNode(node.test, context);\n const consequent = evalNode(node.consequent, context);\n const alternate = evalNode(node.alternate, context);\n return test ? consequent : alternate;\n },\n 'MemberExpression': (node, context) => {\n const object = evalNode(node.object, context);\n const member = node.computed ? object[evalNode(node.property, context)] : object[node.property.name];\n if (typeof member === 'function') {\n return member.bind(object);\n }\n return member;\n },\n 'OptionalMemberExpression': (node, context) => {\n const object = evalNode(node.object, context);\n if (object === void 0 || object === null) {\n return void 0;\n }\n const member = node.computed ? (object[evalNode(node.property, context)]) : (object[node.property.name]);\n if (typeof member === 'function') {\n return member.bind(object);\n }\n return member;\n },\n\n 'ArrayExpression': (node, context) => {\n const arr = [];\n node.elements.map(elem => {\n if (elem.type === 'SpreadElement') {\n arr.push(...evalNode(elem, context));\n } else {\n arr.push(evalNode(elem, context));\n }\n });\n return arr;\n },\n 'ObjectExpression': (node, context) => node.properties.reduce((acc, prop) => ({ ...acc, ...evalNode(prop, context) }), {}),\n 'ObjectProperty': (node, context) => ({ [evalNode(node.key, context)]: evalNode(node.value, context) }),\n 'SpreadElement': (node, context) => evalNode(node.argument, context),\n\n 'Identifier': (node, context) => {\n if (Array.isArray(context)) {\n const hitContext = context.find(contextObj => node.name in contextObj);\n return hitContext ? hitContext[node.name] : undefined;\n } else if (typeof context === 'object') {\n return context[node.name];\n }\n },\n 'ThisExpression': (node, context) => {\n if (Array.isArray(context)) {\n const hitContext = context.find(contextObj => 'this' in contextObj);\n return hitContext ? hitContext['this'] : undefined;\n } else if (typeof context === 'object') {\n return context['this'];\n }\n },\n\n 'CallExpression': (node, context) => callFunction(node, context),\n 'OptionalCallExpression': (node, context) => callFunction(node, context),\n 'NewExpression': (node, context) => callFunction(node, context),\n\n 'Directive': (node, context) => evalNode(node.value, context),\n 'DirectiveLiteral': (node, context) => node.value,\n};\n\nconst immediateContext = (node, context) => {\n if (Array.isArray(context)) {\n if (context.length === 0) {\n return null;\n }\n const qualifiedContext = context.filter(contextObj => !(('$event' in contextObj && '$node' in contextObj) || 'this' in contextObj));\n return context.find(contextObj => node.name in contextObj) ?? qualifiedContext[0];\n } else if (typeof context === 'object') {\n return context;\n }\n}\n\nconst evalNode = (node, context) => nodeHandlers[node.type](node, context);\n\nconst evaluate = (ast, context = {}, loc = {}) => {\n try {\n return ast.map(astNode => evalNode(astNode, context));\n } catch (e) {\n throw { error: e.message, location: loc, ast, context };\n }\n};\n\nexport { evaluate };\n\n// module.exports = { evaluate };\n// const parser = require('@babel/parser');\n// const ast = parser.parse(\"name?.toUpperCase()\").program.body;\n// console.log(ast);\n// const result = evaluate(JSON.parse(JSON.stringify(ast)), { name: 'hello' });\n// console.log(result);", "class LWEvent {\n constructor(eventName, data) {\n this.eventName = eventName;\n this.data = data;\n }\n}\n\nclass LWEventListener {\n static key = 0;\n constructor(eventName, callback) {\n this.eventName = eventName;\n this.callback = callback;\n this.key = ++LWEventListener.key;\n }\n}\n\n// const listeners = {\n// event0: {\n// key0: listener0,\n// key1: listener1,\n// },\n// event0: {},\n// event0: {},\n// };\n\nexport default class LWEventBus {\n constructor() {\n this.listeners = {};\n }\n\n addEventListener(eventName, callback) {\n const listener = new LWEventListener(eventName, callback);\n this.listeners[listener.eventName] = this.listeners[listener.eventName] || {};\n const events = this.listeners[listener.eventName];\n events[listener.key] = listener;\n return listener;\n }\n\n removeEventListener(listener) {\n if (this.listeners[listener.eventName]) {\n delete this.listeners[listener.eventName][listener.key];\n }\n }\n\n dispatchEvent(eventName, data = null) {\n if (this.listeners[eventName]) {\n Object.values(this.listeners[eventName]).forEach(listener => {\n setTimeout(() => {\n listener.callback.call(void 0, new LWEvent(eventName, data));\n });\n });\n }\n }\n}", "import * as parser from './lw-expr-parser.js';\nimport LWEventBus from './lw-event-bus.js';\n\nglobalThis.leanweb = globalThis.leanweb ?? {\n componentsListeningOnUrlChanges: [],\n eventBus: new LWEventBus(),\n updateComponents(...tagNames) {\n if (tagNames?.length) {\n tagNames.forEach(tagName => {\n leanweb.eventBus.dispatchEvent(tagName);\n });\n } else {\n leanweb.eventBus.dispatchEvent('update');\n }\n },\n\n set urlHash(hash) {\n location.hash = hash;\n },\n\n get urlHash() {\n return location.hash;\n },\n\n set urlHashPath(hashPath) {\n const s = this.urlHash.split('?');\n if (s.length === 1) {\n this.urlHash = hashPath;\n } else if (s.length > 1) {\n this.urlHash = hashPath + '?' + s[1];\n }\n },\n\n get urlHashPath() {\n return this.urlHash.split('?')[0];\n },\n\n set urlHashParams(hashParams) {\n if (!hashParams) {\n return;\n }\n\n const paramArray = [];\n Object.keys(hashParams).forEach(key => {\n const value = hashParams[key];\n if (Array.isArray(value)) {\n value.forEach(v => {\n paramArray.push(key + '=' + encodeURIComponent(v));\n });\n } else {\n paramArray.push(key + '=' + encodeURIComponent(value));\n }\n });\n this.urlHash = this.urlHashPath + '?' + paramArray.join('&');\n },\n\n get urlHashParams() {\n const ret = {};\n const s = this.urlHash.split('?');\n if (s.length > 1) {\n const p = new URLSearchParams(s[1]);\n p.forEach((v, k) => {\n if (ret[k] === undefined) {\n ret[k] = v;\n } else if (Array.isArray(ret[k])) {\n ret[k].push(v);\n } else {\n ret[k] = [ret[k], v];\n }\n });\n }\n return ret;\n }\n};\n\nglobalThis.addEventListener('hashchange', () => {\n leanweb.componentsListeningOnUrlChanges.forEach(component => {\n setTimeout(() => {\n component?.urlHashChanged?.call(component);\n component?.update?.call(component);\n });\n });\n}, false);\n\nconst hasMethod = (obj, name) => {\n const desc = Object.getOwnPropertyDescriptor(obj, name);\n return !!desc && typeof desc.value === 'function';\n}\n\nconst nextAllSiblings = (el, selector) => {\n const siblings = [];\n while (el = el.nextSibling) {\n if (el.nodeType === Node.ELEMENT_NODE && (!selector || el.matches(selector))) {\n siblings.push(el);\n }\n }\n return siblings;\n};\n\nexport default class LWElement extends HTMLElement {\n constructor(ast) {\n super();\n this.ast = ast;\n\n leanweb.runtimeVersion = ast.runtimeVersion;\n leanweb.builderVersion = ast.builderVersion;\n\n const node = document.createElement('template');\n node.innerHTML = `${ast.html}`;\n this.attachShadow({ mode: 'open' }).appendChild(node.content);\n\n this._bindMethods();\n setTimeout(() => {\n this.update(this.shadowRoot);\n setTimeout(() => {\n this.domReady?.call(this);\n });\n });\n\n if (this.urlHashChanged && typeof this.urlHashChanged === 'function') {\n leanweb.componentsListeningOnUrlChanges.push(this);\n }\n\n leanweb.eventBus.addEventListener('update', _ => {\n this.update();\n });\n\n leanweb.eventBus.addEventListener(ast.componentFullName, _ => {\n this.update();\n });\n }\n\n _getNodeContext(node) {\n const contextNode = node.closest('[lw-context]');\n return contextNode?.['lw-context'] ?? [{ 'this': this }, this, globalThis];\n }\n\n update(rootNode = this.shadowRoot) {\n if (rootNode !== this.shadowRoot) {\n if (rootNode.hasAttribute('lw-elem')) {\n if (rootNode.hasAttribute('lw-elem-bind')) {\n this._bindModels(rootNode);\n this._bindEvents(rootNode);\n this._bindInputs(rootNode);\n }\n if (rootNode.hasAttribute('lw-if')) {\n this.updateIf(rootNode);\n }\n if (!rootNode.hasAttribute('lw-false')) {\n this.updateEval(rootNode);\n this.updateClass(rootNode);\n this.updateBind(rootNode);\n this.updateModel(rootNode);\n if (rootNode.hasAttribute('lw-for')) {\n this.updateFor(rootNode);\n }\n }\n }\n }\n const treeWalker = document.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT, {\n acceptNode: node => {\n if (node.hasAttribute('lw-elem')) {\n if (node.hasAttribute('lw-for')) {\n this.updateFor(node);\n return NodeFilter.FILTER_REJECT;\n }\n if (node.hasAttribute('lw-for-parent')) {\n return NodeFilter.FILTER_REJECT;\n }\n if (node.hasAttribute('lw-elem-bind')) {\n this._bindModels(node);\n this._bindEvents(node);\n this._bindInputs(node);\n }\n if (node.hasAttribute('lw-if')) {\n this.updateIf(node);\n }\n if (node.hasAttribute('lw-false')) {\n return NodeFilter.FILTER_REJECT;\n }\n this.updateEval(node);\n this.updateClass(node);\n this.updateBind(node);\n this.updateModel(node);\n }\n return NodeFilter.FILTER_ACCEPT;\n }\n });\n while (treeWalker.nextNode()) { }\n }\n\n _bindMethods() {\n const methodNames = ['update'];\n const proto = Object.getPrototypeOf(this);\n methodNames.push(...Object.getOwnPropertyNames(proto).filter(name => hasMethod(proto, name)));\n methodNames.push(...Object.getOwnPropertyNames(this).filter(name => hasMethod(this, name)));\n methodNames.filter(name => name !== 'constructor').forEach(name => {\n this[name] = this[name].bind(this);\n });\n }\n\n // properties:\n // lw_input_bound: boolean\n _bindInputs(inputNode) {\n if (inputNode['lw_input_bound']) {\n return;\n }\n inputNode['lw_input_bound'] = true;\n for (const attr of inputNode.attributes) {\n const attrName = attr.name;\n const attrValue = attr.value;\n if (attrName.startsWith('lw-input:')) {\n const interpolation = this.ast[attrValue];\n const context = this._getNodeContext(inputNode);\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n inputNode[interpolation.lwValue] = parsed[0];\n }\n }\n inputNode?.inputReady?.call(this);\n inputNode?.update?.call(this);\n }\n\n // properties:\n // lw_event_bound: boolean\n _bindEvents(eventNode) {\n if (eventNode['lw_event_bound']) {\n return;\n }\n eventNode['lw_event_bound'] = true;\n const me = this;\n for (const attr of eventNode.attributes) {\n const attrName = attr.name;\n const attrValue = attr.value;\n if (attrName.startsWith('lw-on:')) {\n const interpolation = this.ast[attrValue];\n interpolation.lwValue.split(',').forEach(eventType => {\n eventNode.addEventListener(eventType.trim(), (event => {\n const context = this._getNodeContext(eventNode);\n const eventContext = { '$event': event, '$node': eventNode };\n const parsed = parser.evaluate(interpolation.ast, [eventContext, ...context], interpolation.loc);\n const promises = parsed.filter(p => typeof p?.then === 'function' && typeof p?.finally === 'function');\n if (parsed.length > promises.length) {\n me.update();\n }\n promises.forEach(p => {\n p?.finally(() => {\n me.update();\n });\n });\n }).bind(me));\n });\n }\n }\n }\n\n // properties:\n // lw_model_bound: boolean\n _bindModels(modelNode) {\n const key = modelNode.getAttribute('lw-model');\n if (!key) {\n return;\n }\n if (modelNode['lw_model_bound']) {\n return;\n }\n modelNode['lw_model_bound'] = true;\n const interpolation = this.ast[key];\n modelNode.addEventListener('input', (event => {\n const context = this._getNodeContext(modelNode);\n const astModel = interpolation.ast[0].expression;\n let object;\n let propertyExpr;\n if (astModel.type === 'MemberExpression') {\n // . false and [] true\n propertyExpr = astModel.computed ? parser.evaluate([astModel.property], context, interpolation.loc)[0] : astModel.property.name;\n object = parser.evaluate([astModel.object], context, interpolation.loc)[0];\n } else if (astModel.type === 'Identifier') {\n object = this;\n propertyExpr = astModel.name;\n }\n\n if (modelNode.type === 'number' || modelNode.type === 'range') {\n // set do_not_update mark for cases when user inputs 0.01, 0.0 will not be evaluated prematurely\n modelNode.do_not_update = true;\n object[propertyExpr] = modelNode.value * 1;\n } else if (modelNode.type === 'checkbox') {\n if (Array.isArray(object[propertyExpr])) {\n if (modelNode.checked) {\n object[propertyExpr].push(modelNode.value);\n } else {\n const index = object[propertyExpr].indexOf(modelNode.value);\n if (index > -1) {\n object[propertyExpr].splice(index, 1);\n }\n }\n } else {\n object[propertyExpr] = modelNode.checked;\n }\n } else if (modelNode.type === 'select-multiple') {\n if (!Array.isArray(object[propertyExpr])) {\n object[propertyExpr] = [];\n }\n object[propertyExpr].length = 0;\n for (let i = 0; i < modelNode.options.length; ++i) {\n const option = modelNode.options[i];\n if (option.selected) {\n object[propertyExpr].push(option.value);\n }\n }\n } else {\n object[propertyExpr] = modelNode.value;\n }\n this.update();\n delete modelNode.do_not_update;\n }).bind(this));\n }\n\n updateModel(modelNode) {\n if (modelNode.do_not_update && modelNode.type === 'number') {\n return;\n }\n const key = modelNode.getAttribute('lw-model');\n if (!key) {\n return;\n }\n const context = this._getNodeContext(modelNode);\n const interpolation = this.ast[key];\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n if (modelNode.type === 'checkbox') {\n if (Array.isArray(parsed[0])) {\n modelNode.checked = parsed[0].includes?.(modelNode.value);\n } else {\n modelNode.checked = !!parsed[0];\n }\n } else if (modelNode.type === 'radio') {\n modelNode.checked = parsed[0] === modelNode.value;\n } else if (modelNode.type === 'select-multiple') {\n for (let i = 0; i < modelNode.options.length; ++i) {\n const option = modelNode.options[i];\n if (parsed[0]) {\n option.selected = parsed[0].includes(option.value);\n }\n }\n } else {\n const newValue = parsed[0] ?? '';\n if (modelNode.value !== newValue) {\n modelNode.value = newValue;\n }\n }\n }\n\n // attribute: lw: astKey\n // property: lw-eval-value-$key\n updateEval(evalNode) {\n const key = evalNode.getAttribute('lw');\n if (!key) {\n return;\n }\n const context = this._getNodeContext(evalNode);\n const interpolation = this.ast[key];\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n if (evalNode['lw-eval-value-' + key] !== parsed[0] || typeof parsed[0] === 'object') {\n evalNode['lw-eval-value-' + key] = parsed[0];\n evalNode.innerText = parsed[0] ?? '';\n }\n }\n\n // attribute: lw-if: astKey\n // lw-false: '' (if false)\n updateIf(ifNode) {\n const key = ifNode.getAttribute('lw-if');\n if (!key) {\n return;\n }\n const context = this._getNodeContext(ifNode);\n const interpolation = this.ast[key];\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n\n const hasLwFalse = ifNode.hasAttribute('lw-false');\n if (parsed[0] !== false && parsed[0] !== undefined && parsed[0] !== null) {\n hasLwFalse && ifNode.removeAttribute('lw-false');\n setTimeout(() => {\n ifNode.turnedOn?.call(ifNode);\n });\n } else {\n !hasLwFalse && ifNode.setAttribute('lw-false', '');\n setTimeout(() => {\n ifNode.turnedOff?.call(ifNode);\n });\n }\n }\n\n // attribute: lw-class: astKey\n updateClass(classNode) {\n const context = this._getNodeContext(classNode);\n for (const attr of classNode.attributes) {\n const attrName = attr.name;\n const attrValue = attr.value;\n if (attrName.startsWith('lw-class:')) {\n const interpolation = this.ast[attrValue];\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n\n if (!parsed[0]) {\n classNode.classList.remove(interpolation.lwValue);\n } else {\n classNode.classList.add(interpolation.lwValue);\n }\n }\n }\n }\n\n updateBind(bindNode) {\n const context = this._getNodeContext(bindNode);\n for (const attr of bindNode.attributes) {\n const attrName = attr.name;\n const attrValue = attr.value;\n if (attrName.startsWith('lw-bind:')) {\n const interpolation = this.ast[attrValue];\n const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);\n\n if (interpolation.lwValue === 'class') {\n const initClass = bindNode.getAttribute('lw-init-class');\n if (!parsed[0]) {\n bindNode.classList.remove(parsed[0]);\n } else {\n bindNode.classList = initClass + ' ' + parsed[0];\n }\n } else {\n if (parsed[0] !== false && parsed[0] !== undefined && parsed[0] !== null) {\n bindNode.setAttribute(interpolation.lwValue, parsed[0]);\n } else {\n bindNode.removeAttribute(interpolation.lwValue);\n }\n }\n }\n }\n }\n\n // parent attribytes:\n // lw-for: $astKey\n\n // child attributes:\n // lw-context: ''\n // lw-for-parent: $astKey\n\n // child propery:\n // lw-context: localContext\n updateFor(forNode) {\n const key = forNode.getAttribute('lw-for');\n if (!key) {\n return;\n }\n const context = this._getNodeContext(forNode);\n const interpolation = this.ast[key];\n const items = parser.evaluate(interpolation.astItems, context, interpolation.loc)[0] ?? [];\n const rendered = nextAllSiblings(forNode, `[lw-for-parent=\"${key}\"]`);\n for (let i = items.length; i < rendered.length; ++i) {\n rendered[i].remove();\n }\n\n let currentNode = forNode;\n items.forEach((item, index) => {\n let node;\n if (rendered.length > index) {\n node = rendered[index];\n } else {\n node = forNode.cloneNode(true);\n node.removeAttribute('lw-for');\n // node.removeAttribute('lw-elem');\n node.setAttribute('lw-for-parent', key);\n node.setAttribute('lw-context', '');\n currentNode.insertAdjacentElement('afterend', node);\n }\n if (item && typeof item === 'object') {\n item.getDom = () => node;\n }\n currentNode = node;\n const itemContext = { [interpolation.itemExpr]: item };\n if (interpolation.indexExpr) {\n itemContext[interpolation.indexExpr] = index;\n }\n\n node['lw-context'] = [itemContext, ...context];\n this.update(node);\n });\n }\n}", "export default {\"html\":\"
Builds framework agnostic web components
DOM first, no magic
Lean, 4kb runtime gzipped
\\n \\n Hello <span lw>world</span>!\\n <span lw>this instanceof HTMLElement</span>\\n\\n
\\n class extends LWElement {\\n world = 'Leanweb';\\n }\\n\\n
\\n \\n Radius: \\n <input type='number' lw-model='r'>\\n Area: \\n <span lw>Math.PI * r * r</span>\\n\\n
\\n class extends LWElement {\\n r = 1;\\n }\\n\\n \\n\\n
\\n I <span lw-if='love'>\u2764\uFE0F</span> Leanweb!\\n\\n
\\n class extends LWElement {\\n love = true;\\n }\\n\\n
\\n <div lw lw-for='item, $index in items'>$index+': '+item</div>\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n }\\n\\n
You could access DOM nodes for each element in a lw-for loop by calling elem.getDom() as long as typeof elem evaluates object.
\",\"css\":\"@import \\\"global-styles.css\\\";\\n:host {\\n display: block;\\n padding: 20px 0;\\n border-top: 1px solid tomato;\\n}\\n\\n[lw-false],[lw-for]{display:none !important;}\\n\",\"componentFullName\":\"leanweb-app-sections-code-02\",\"runtimeVersion\":\"3.0.4\",\"builderVersion\":\"3.0.4\"};", "import LWElement from './../../../lib/lw-element.js';\nimport ast from './ast.js';\n\nimport hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/es/core.min.js';\nimport javascript from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/es/languages/javascript.min.js';\nimport xml from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/es/languages/xml.min.js';\nimport css from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/es/languages/css.min.js';\n\n\ncustomElements.define('leanweb-app-sections-code-02',\n class extends LWElement { // LWElement extends HTMLElement\n constructor() {\n super(ast);\n \n hljs.registerLanguage('javascript', javascript);\n hljs.registerLanguage('html', xml);\n hljs.registerLanguage('css', css);\n this.shadowRoot.querySelectorAll('pre code').forEach((block) => {\n hljs.highlightElement(block);\n });\n }\n\n items = ['one', 'two', 'three'];\n }\n);\n", "export default {\"145\":{\"ast\":[{\"type\":\"ExpressionStatement\",\"expression\":{\"type\":\"Identifier\",\"name\":\"name\"}}],\"loc\":{\"startLine\":15,\"endLine\":15}},\"146\":{\"ast\":[{\"type\":\"ExpressionStatement\",\"expression\":{\"type\":\"CallExpression\",\"callee\":{\"type\":\"Identifier\",\"name\":\"resetName\"},\"arguments\":[{\"type\":\"Identifier\",\"name\":\"$event\"},{\"type\":\"Identifier\",\"name\":\"$node\"}]}}],\"loc\":{\"startLine\":15,\"endLine\":15},\"lwType\":\"lw-on\",\"lwValue\":\"click\"},\"147\":{\"ast\":[{\"type\":\"ExpressionStatement\",\"expression\":{\"type\":\"Identifier\",\"name\":\"name\"}}],\"loc\":{\"startLine\":15,\"endLine\":15}},\"html\":\"\\n <input type='text' lw-model='name'>\\n <button lw-on:click='resetName($event, $node)'> Reset Name </button>\\n <!-- You could bind multiple events -->\\n <!-- <button lw-on:click,mouseout='resetName($event, $node)'> Reset Name </button> -->\\n <span lw>name</span>\\n\\n
\\n class extends LWElement {\\n resetName(event, node) {\\n this.name = 'Leanweb';\\n }\\n }\\n\\n
\\n <div lw \\n lw-for='item, $index in items' \\n lw-class:active='isActive($index)' \\n lw-on:click='setActive($index)'>\\n item\\n </div>\\n\\n
\\n .active {\\n color: red;\\n }\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n setActive(index) {\\n this.activeIndex = index;\\n }\\n isActive(index) {\\n return index === (this.activeIndex ?? 1);\\n }\\n }\\n\\n
\\n <img lw-bind:src='imgSrc' lw-bind:width='imageWidth' lw-bind:title='title'>\\n\\n
\\n class extends LWElement {\\n imgSrc = '/resources/images/arizona.jpg';\\n imageWidth = 400;\\n title = 'Beautiful Arizona';\\n }\\n
The following example shows how parent and child components share data by passing data with lw-input:.
\\n <div class='parent'>\\n <span>Parent</span>\\n <span lw>item?.quantity</span>\\n <button lw-on:click='update()'>Update Quantity</button>\\n <demo-child lw-input:item='item'></demo-child>\\n </div>\\n\\n
\\n class extends LWElement {\\n item = {\\n name: 'Banana',\\n quantity: 1,\\n };\\n }\\n\\n
\\n <div class='child'>\\n <span>Child</span>\\n <span lw>item?.quantity</span>\\n <div class='buttons'>\\n <button lw-on:click='addOne()'>Add One</button>\\n <button lw-on:click='removeOne()'>Remove One</button>\\n </div>\\n </div>\\n\\n
\\n class extends LWElement {\\n addOne() {\\n this.item.quantity += 1;\\n }\\n\\n removeOne() {\\n this.item.quantity -= 1;\\n }\\n }\\n\\n
\\n <button lw-on:click='toggleCheckboxes()'>Toggle Checkboxes</button>\\n <div lw-for='item, $index in items'>\\n <input type='checkbox' lw-bind:value='item' lw-model='checkedValues'> \\n <span lw>item</span>\\n </div>\\n <span lw>checkedValues</span>\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n toggleCheckboxes() {\\n if (this.checkedValues.length) {\\n this.checkedValues.length = 0;\\n } else {\\n this.checkedValues = [...this.items];\\n }\\n }\\n checkedValues = [];\\n }\\n\\n
\\n <button lw-on:click='toggleCheckbox()'>Toggle Checkbox</button>\\n <div>\\n <input type='checkbox' lw-model='checked'>\\n <span lw>checked</span>\\n </div>\\n\\n
\\n class extends LWElement {\\n checked = false;\\n toggleCheckbox() {\\n this.checked = !this.checked;\\n }\\n }\\n\\n
\\n <button lw-on:click='chooseTwo()'>Choose Two</button>\\n <div lw-for='item, $index in items'>\\n <input type='radio' lw-bind:value='item' lw-model='picked'>\\n <span lw>item</span>\\n </div>\\n <span lw>picked</span>\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n chooseTwo() {\\n this.picked = 'two';\\n }\\n }\\n\\n
\\n <button lw-on:click='selectTwo()'>Select Two</button>\\n <select lw-model='selectedOption'>\\n <option lw lw-for='item, $index in items'>item</option>\\n </select>\\n <span lw> selectedOption </span>\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n selectTwo() {\\n this.selectedOption = 'two';\\n }\\n selectedOption;\\n }\\n\\n
\\n <button lw-on:click='toggleAllOptions()'>Toggle All</button>\\n <div lw> selectedOptions </div>\\n <select lw-model='selectedOptions' multiple>\\n <option lw lw-for='item, $index in items'>item</option>\\n </select>\\n\\n
\\n class extends LWElement {\\n items = ['one', 'two', 'three'];\\n toggleAllOptions() {\\n if (this.selectedOptions.length) {\\n this.selectedOptions.length = 0;\\n } else {\\n this.selectedOptions = [...this.items];\\n }\\n }\\n selectedOptions = [];\\n }\\n\\n
\\n <button lw-on:click='selectRange50()'>Select Range 50</button> <br>\\n <input type='range' lw-model='selectedRange'>\\n <span lw>selectedRange</span>\\n\\n
\\n class extends LWElement {\\n selectRange50() {\\n this.selectedRange = 50;\\n }\\n selectedRange = 10;\\n }\\n\\n
\\n <div class='pub'>\\n <span>Time Publisher</span>\\n <span lw>time</span>\\n </div>\\n\\n
\\n class extends LWElement {\\n constructor() {\\n super(ast);\\n setInterval(() => {\\n this.time = new Date(Date.now()).toLocaleString();\\n leanweb.eventBus.dispatchEvent('time', this.time);\\n this.update();\\n }, 1000);\\n }\\n }\\n\\n
\\n <div class='sub'>\\n <span>Time Subscriber</span>\\n <span lw>time</span>\\n <div class='buttons'>\\n <button lw-bind:disabled='subscribed' lw-on:click='sub()'>Subscribe Time</button>\\n <button lw-bind:disabled='!subscribed' lw-on:click='unsub()'>Unsubscribe Time</button>\\n </div>\\n </div>\\n\\n
\\n class extends LWElement {\\n sub() {\\n this.listener = leanweb.eventBus.addEventListener('time', event => {\\n this.time = event.data;\\n this.update();\\n });\\n this.subscribed = true;\\n }\\n\\n unsub() {\\n leanweb.eventBus.removeEventListener(this.listener);\\n this.subscribed = false;\\n }\\n }\\n\\n
\\n <button lw-on:click='reset()'>Reset</button>\\n <div lw-for='row in array2d'>\\n <span lw-for='cell, $i in row'>\\n <!-- cell is primitive type which is passed by value, -->\\n <!-- so lw-model uses row[$i] which is passed by reference -->\\n <input type='number' lw-model='row[$i]'>\\n <span lw>cell</span>\\n </span>\\n </div>\\n\\n
\\n class extends LWElement {\\n array2d = [\\n [1, 2, 3],\\n [4, 5, 6],\\n [7, 8, 9],\\n ];\\n\\n reset() {\\n this.array2d.forEach(a => a.fill(0));\\n }\\n }\\n\\n
Run this command in an empty directory to create a ready to go web component project.
Start a dev server and monitor for any source code changes.
Build your project and get the output ready for production.
Upgrade project runtime after Leanweb tools upgraded.
Get help information for more commands.
\",\"css\":\"@import \\\"global-styles.css\\\";\\n:host {\\n display: block;\\n border-top: 1px solid tomato;\\n}\\n\\nh2 {\\n font-weight: 500;\\n /* font-size: 28px; */\\n}\\n[lw-false],[lw-for]{display:none !important;}\\n\",\"componentFullName\":\"leanweb-app-sections-commands\",\"runtimeVersion\":\"3.0.4\",\"builderVersion\":\"3.0.4\"};", "import LWElement from './../../../lib/lw-element.js';\nimport ast from './ast.js';\n\ncustomElements.define('leanweb-app-sections-commands',\n class extends LWElement { // LWElement extends HTMLElement\n constructor() {\n super(ast);\n }\n\n // derived from LWElement\n // domReady() {\n // console.log('Dom is ready');\n // }\n\n // inputReady() {\n // console.log('input is ready');\n // }\n\n // derived from HTMLElement\n // connectedCallback() {\n // console.log(this.isConnected);\n // console.log('Element added to page.');\n // }\n\n // disconnectedCallback() {\n // console.log('Element removed from page.');\n // }\n\n // adoptedCallback() {\n // console.log('Element moved to new page.');\n // }\n\n // static get observedAttributes() {\n // return [];\n // }\n\n // attributeChangedCallback(name, oldValue, newValue) {\n // console.log(name, oldValue, newValue);\n // }\n }\n);\n", "export default {\"html\":\"\\n document.querySelector('demo-root').shadowRoot.querySelector('demo-login').update();\\n \\n
This is to update the DOM of the demo-login component enclosed by demo-root component.
\\n leanweb.eventBus.dispatchEvent('update');\\n \\n
All instances of LWElement listen on update event to update their DOMs. Therefore, simply dispatching the update event will trigger DOMs of all components to update themselves. Only changed part will get updated.
\\n <demo-login lw-if='leanweb.urlHash==='#/login''></demo-login>\\n <demo-signup lw-if='leanweb.urlHash==='#/signup''></demo-signup>\\n <demo-dashboard lw-if='leanweb.urlHash==='#/dashboard''></demo-dashboard>\\n
\\n customElements.define('demo-root',\\n class extends LWElement { \\n constructor() {\\n super(ast);\\n }\\n\\n // If urlHashChanged() is defined as a function, it will be called \\n // whenever the urlHash changes. This could be useful to update the \\n // DOM on component routing.\\n urlHashChanged() {\\n // Called when url hash changes.\\n }\\n }\\n
Under src/ directory, there is an env.js file and an env/ directory. lw s will use src/env.js, and lw s prod will use src/env/prod.js. You can create additional env files in the src/env/ directory as you need. Other than lw s, lw di and lw e support the same env parameters.
If post-dist file is present in the project root directory, it will be called after lw dist is done. This could be useful to copy dist/* to somewhere else.
host=0.0.0.0 lw s to listen on all all IPv4 local host addresses.
host=:: lw s to listen on all all local host addresses.
port=9999 lw s to listen on port 9999.
https=./https.js lw s to use https.js to configure https server.
\\n const fs = require(\\\"fs\\\");\\n\\n module.exports = {\\n cert: fs.readFileSync(__dirname + \\\"/localhost.pem\\\"),\\n key: fs.readFileSync(__dirname + \\\"/localhost.key.pem\\\"),\\n passphrase: \\\"12345\\\"\\n };\\n
noopen=1 lw s to not open a browser.
verbose=1 lw di to show verbose dist output.
It is important to understand that you are responsible to update the DOM of any components that needs to be updated. However, an LWElement's DOM gets updated when:
You might need to update local or other components DOM when:
\\n leanweb.updateComponents();\\n\\n
\\n leanweb.updateComponents('demo-root', 'demo-login');\\n\\n
\\n <div class='buttons'>\\n <button lw-on:click='updateAll()'>Update All</button>\\n <button lw-on:click='updateRed()'>Update Red</button>\\n <button lw-on:click='updateBlue()'>Update Blue</button>\\n </div>\\n\\n <div class='red'>\\n <demo-dest-red></demo-dest-red>\\n <demo-dest-red></demo-dest-red>\\n </div>\\n <div class='blue'>\\n <demo-dest-blue></demo-dest-blue>\\n <demo-dest-blue></demo-dest-blue>\\n </div>\\n\\n
\\n class extends LWElement {\\n updateAll() {\\n // update all components\\n leanweb.updateComponents();\\n\\n // update components by exact tag names\\n // leanweb.updateComponents('demo-dest-red', 'demo-dest-blue');\\n }\\n\\n updateRed() {\\n leanweb.updateComponents('demo-dest-red');\\n }\\n\\n updateBlue() {\\n leanweb.updateComponents('demo-dest-blue');\\n }\\n }\\n\\n
\\n <div lw>time ?? '-'</div>\\n\\n
\\n customElements.define('demo-dest-red',\\n class extends LWElement {\\n constructor() {\\n super(ast);\\n setInterval(() => {\\n this.time = new Date(Date.now()).toLocaleString();\\n }, 1000);\\n }\\n }\\n }\\n\\n
\\n <div lw>time ?? '-'</div>\\n\\n
\\n customElements.define('demo-dest-blue',\\n class extends LWElement {\\n constructor() {\\n super(ast);\\n setInterval(() => {\\n this.time = new Date(Date.now()).toLocaleString();\\n }, 1000);\\n }\\n }\\n }\\n\\n
\\n <div class='parent'>\\n <span>Parent</span>\\n <span lw>quantity</span>\\n <demo-child lw-on:add='onAdd($event)' </demo-child>\\n </div>\\n\\n
\\n class extends LWElement {\\n quantity = 0;\\n \\n onAdd(event) {\\n this.quantity += event.detail;\\n }\\n }\\n\\n
\\n <div class='child'>\\n <span>Child</span>\\n <div class='buttons'>\\n <button lw-on:click='add(1)'>Add One</button>\\n <button lw-on:click='add(-1)'>Remove One</button>\\n </div>\\n </div>\\n\\n
\\n class extends LWElement {\\n add(by) {\\n this.dispatchEvent(new CustomEvent('add', {\\n detail: by\\n }));\\n }\\n }\\n\\n
\\n <div class='parent'>\\n <span>Parent</span>\\n <span lw>quantity</span>\\n <demo-child lw-input:parent='this' </demo-child>\\n </div>\\n\\n
\\n class extends LWElement {\\n quantity = 0;\\n \\n add(by) {\\n this.quantity += by;\\n this.update();\\n }\\n }\\n\\n
\\n <div class='child'>\\n <span>Child</span>\\n <div class='buttons'>\\n <button lw-on:click='add(1)'>Add One</button>\\n <button lw-on:click='add(-1)'>Remove One</button>\\n </div>\\n </div>\\n\\n
\\n class extends LWElement {\\n add(by) {\\n this.parent.add(by);\\n }\\n }\\n\\n
\\n <div>\\n Hello <span lw>world</span>!\\n </div>\\n <div>\\n I <span lw>this instanceof HTMLElement ? '\u2764\uFE0F' : '\uD83D\uDC94'</span> Leanweb!\\n </div>\\n\\n
\\n class extends LWElement {\\n world = 'Leanweb';\\n }\\n\\n