{"_id":"l-html","_rev":"5-82ff3270bfaacab3638ef8fdbbd1a707","name":"l-html","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"l-html","version":"1.0.0","description":"An HTML Generation Library for Javascript","main":"l.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"babel --presets es2015 src/l.js -o dist/l.js && uglifyjs dist/l.js > dist/l.min.js"},"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"keywords":["html","generation","dsl"],"author":{"name":"Adam Bertrand Berger"},"license":"MIT","bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"homepage":"https://github.com/adambertrandberger/l#readme","devDependencies":{"babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-preset-es2015":"^6.24.1","uglifyjs":"^2.4.11"},"gitHead":"d5815bf02698ccac01ce3751337c4b461033561b","_id":"l-html@1.0.0","_npmVersion":"6.2.0","_nodeVersion":"10.8.0","_npmUser":{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"},"dist":{"integrity":"sha512-IkurEyPEP573ZGWul5L4lqB69MmNrMmk1yuKPf55FMQxM6jZ+uSyEbfc+k6vYrjTSRN4obp5nWSNbEtUAFxjkQ==","shasum":"a28f136a4899fa135189751e2022438e70eef64c","tarball":"https://registry.npmjs.org/l-html/-/l-html-1.0.0.tgz","fileCount":8,"unpackedSize":224105,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4h7xCRA9TVsSAnZWagAAep8P/A0Sy9656I39iGVu9jjE\nRCPn0WXyQ5IGHAw8jGnj1/KnvOFKW/qNCla1NwE0eRNuz68facoPE+vvAr4E\nQcoEVMGBJy+w3gtikgpJABDJ6GT7NlsUHhg++PuZx1TAEUyh8QtxhyEoyyJU\nU0+wYDM0pZlD1MytO1bWPhy8jpPnU06QUeHQ49AQ9N1OlYn93Hx8xuLEUbR5\n7x21s6tSO6MjSjVZZErUSrPnhH3/0gft9cuFt8lgYu/l2BD9g0BRoq4Ahj6N\nOGfxabBIKotVp+7s/O/VkWEpYTvvu8XR53oNC/6fn8/6+ZhYG9lEkZgHqSVO\n+UEHxS1rvW5E/STVzRMB4DfFMIGtYV857qXaIHT8mcZ8zqfydhWlQQXraXRz\npTqEyUQr01QEuRX4a3B9eQO6bQbfgrHSNApHsaWdCuiWBoGTQnuGsDwJbjXb\nBSlOpe5TY6v7rTVz+24YiZ0il7PQhyDTp0OP0wNCuSipI9iTrLl1XH/Lzyb7\nNeRYBRW9FAO17RISyddb24+NJUyAhecbcMbA79v+yQyOsGqhQBRUgVyNHAs2\n5opeoiIt/SZ2TUhXhb86tq6pHTHyp3vTdtRGsSGdOvaBxmE/myWCNUGwE7uy\nOqXls8x5Q8c4A7p0uJKwnru06+NyxafqxepvWVoUfPJUbuRGvA2KoyGRYkV1\n4osI\r\n=iCkN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4DoJ2haUjjf1FlCHbcM2kXS9m64uH1m45F55i2imzWwIhAJhhNoN6P+nFUo2LR1uxCrkKSsiVMalxlqeHaI1H/eSf"}]},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-html_1.0.0_1558322928719_0.6122247796082159"},"_hasShrinkwrap":false},"1.0.1":{"name":"l-html","version":"1.0.1","description":"An HTML Generation Library for Javascript","main":"l.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"babel --presets es2015 src/l.js -o dist/l.js && uglifyjs dist/l.js > dist/l.min.js"},"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"keywords":["html","generation","dsl"],"author":{"name":"Adam Bertrand Berger"},"license":"MIT","bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"homepage":"https://github.com/adambertrandberger/l#readme","devDependencies":{"babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-preset-es2015":"^6.24.1","uglifyjs":"^2.4.11"},"gitHead":"0ad1f5b4c8562c703e3e26f723a6a79635ced22f","_id":"l-html@1.0.1","_npmVersion":"6.2.0","_nodeVersion":"10.8.0","_npmUser":{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"},"dist":{"integrity":"sha512-FAslDPDoz4MreHjgGNHrDjWrLfVY8uSv33D5LtJjuAE8T3Gk7IoBDNj66GBvsssshW2eF/wxkgQBxXcESKmyRg==","shasum":"a9e6e8c4959c380869ced98dedf4ca556ed34ae7","tarball":"https://registry.npmjs.org/l-html/-/l-html-1.0.1.tgz","fileCount":8,"unpackedSize":224099,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4h9kCRA9TVsSAnZWagAAFG8P+gJkq0cglIwRFDJcHpLl\nE9bxkmnDndMXseW+CEPwNk5sKIvkVJVvE0knIbVGzQr7XcYDUpjWwDiq8ivd\nqLWsZQIfyw2T7tmOtijicZ2LlR5K3NIlu1nNWH+BkDNw5S8LeK8iRBg6ILKW\ndQKJ5J3bOQXEKgt9tSYxXCpMxdvwNjI7ltUPZ3hpq1p2pB8CncV/df1Yurl8\n0xm0giPTVCJwMZeiXicT28Kku0B311kjJ+eWu8IcEwezraxc9hR02cnaOzmJ\nAOFZd80U8/dH0kGFv2ujKairPoQz4AR2ayObRbg2p7OUvFeFon3fSjYGj8lo\nrSqpFWBHiRUZ93bAc3EglO4cxIQQJG9Zp/nN3ax01HPjMtKjnyRBhnyRTyLs\neEgKCe3Rm8KUrCfNeSKumEtB9hW+agSevFLeip8huwRXBYjxNQaghRNyeHb8\nUT9HuD4gHK3wLccPCZoEsNZxDe/1KP+6avAhTdXZ2AiG0z9M/wBZNvoxVMtS\njntuGpDEhI3ldfMDywlbLLYK9pHsFYpZC6iWTCDRfTvz46ZZW9uqZidw/Eis\ndhLWRWSvbyeZBf0Ux3TXXdPuAAnaqUMZ7CSvc28CaMkX2ffvFf8rAYtBKUB9\ndGDyQIWVvUiiDYkdaB71xBfJmIFcr7HEjuDR4e9hRL3pZIAYIJC8nz5C+Z86\nVCAR\r\n=s6gS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCERL9Lj9j9dBqtYv7KWgkCa+uSV+uawuFAoiRJSZI1mwIgUjq0fnm1W4hMSx37lXr372ocg26/Tz4tcEVtO/jrsHY="}]},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-html_1.0.1_1558323044149_0.09643491792697279"},"_hasShrinkwrap":false},"1.0.2":{"name":"l-html","version":"1.0.2","description":"An HTML Generation Library for Javascript","main":"l.js","scripts":{"test":"npm run test-behavior","test-behavior":"mocha --compiler js:babel-core ./test/behavior/","test-performance":"mocha ./test/performance/","build":"webpack --mode=production","watch":"onchange 'src/**/*.js' -- npm run build","dev-watch":"onchange 'src/**/*.js' -- npm run dev-build","dev-build":"webpack --mode=development"},"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"keywords":["html","generation","dsl"],"author":{"name":"Adam Bertrand Berger"},"license":"MIT","bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"homepage":"https://github.com/adambertrandberger/l#readme","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/node":"^7.2.2","@babel/plugin-transform-runtime":"^7.4.4","@babel/polyfill":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/runtime":"^7.4.4","babel-loader":"^8.0.6","chai":"^4.2.0","jsdom":"^15.1.0","mocha":"^6.1.4","onchange":"^6.0.0","webpack":"^4.31.0","webpack-cli":"^3.3.2"},"gitHead":"063efab5607c36663aaca86a8c1b4ca1a5c68e5f","_id":"l-html@1.0.2","_npmVersion":"6.2.0","_nodeVersion":"10.8.0","_npmUser":{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"},"dist":{"integrity":"sha512-aWen6PkkwUTkE2Me/SPVWQ0tUj14HGQSPY8qgWD74snlcwzUXsw/BSzVfgYH5SN1r9KqZRyXa96O1oOl9zIJHQ==","shasum":"dbc30008f9798f7285bfefc9029e383eaab81827","tarball":"https://registry.npmjs.org/l-html/-/l-html-1.0.2.tgz","fileCount":22,"unpackedSize":283546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5yjVCRA9TVsSAnZWagAA15QP/3i6KfRiMbgsyA1dYryO\nFgYh3Hi42qAX+VP1pAuSGMPlvC/eP1JS7wd5oryfDpfW7847MqBaOwmiSuGK\n197fJIpe7mR/2AI26h+e3cVqvpmkvlsvfN+nJpHXbYT/6VjEfHDEK1fWW/ZU\nDbvs315ezV3dsy+4ZwVrOau96CBqfbcQgpPd/dUdCOjogp0gse7zQYrtoFzE\nySjtwiaVURAi2Jn9mgf7LHChA/8cig3Wx+bx29aSnSYdA/en9Anf96fIp1tZ\nwpufQvJwmk86zvnryVqxPZqU2EfYYTkgEMN5IH1kUP1ItCEQ5UDqLMbun/uR\nTs+EQg93QhVgFT39yu4hRwhus5iWKPx+Z1zgTb91QGNHhVC5ktl7wPv46lYb\nXlV5NJAGMUepSS0Bdzk9bPwaZuMhAUWICiqhxUakgdOnJKzzVW5CuqQjtCJE\n/FjdIlLA3cUrmFblQ4fgW/OKRIWhYjKAKkTKlp4yDzjIWdP51qVbm+zsh8cj\nYL4BqnVLX0j1Sef9gpBjBUvwhqX0rdIi4x/mDEb+EejE00NzjM1llURuLUVS\nN0D1fEt3LqrgngvL3FQDHUMh+T+wIVWiADy+nXpV3FnDN8p2On8ap0j/SFzy\nvGT6ixduxbsSRrQCJy4ao9qZm4Pn1hH1sPQ4uFeRE05AF9R9ll2DGr8h/q5B\n1AC2\r\n=1YID\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA2u3TnHBOcYD5oMrjNiJF5Z1C4kHzHmz4aCdvRWQfTEAiEApVCRU1ojC8nrTK8ig9/gJB2oNKKtMYAO91bR/BpThYE="}]},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-html_1.0.2_1558653140460_0.40887864986026967"},"_hasShrinkwrap":false},"1.0.3":{"name":"l-html","version":"1.0.3","description":"An HTML Generation Library for Javascript","main":"dist/l.js","scripts":{"test":"npm run test-behavior","test-behavior":"mocha --compiler js:babel-core ./test/behavior/","test-performance":"mocha ./test/performance/","build":"webpack --mode=production","watch":"onchange 'src/**/*.js' -- npm run build","dev-watch":"onchange 'src/**/*.js' -- npm run dev-build","dev-build":"webpack --mode=development"},"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"keywords":["html","generation","dsl"],"author":{"name":"Adam Bertrand Berger"},"license":"MIT","bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"homepage":"https://github.com/adambertrandberger/l#readme","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/node":"^7.2.2","@babel/plugin-transform-runtime":"^7.4.4","@babel/polyfill":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/runtime":"^7.4.4","babel-loader":"^8.0.6","chai":"^4.2.0","jsdom":"^15.1.0","mocha":"^6.1.4","onchange":"^6.0.0","webpack":"^4.31.0","webpack-cli":"^3.3.2"},"gitHead":"063efab5607c36663aaca86a8c1b4ca1a5c68e5f","_id":"l-html@1.0.3","_npmVersion":"6.2.0","_nodeVersion":"10.8.0","_npmUser":{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"},"dist":{"integrity":"sha512-IusGmWDi//iUDHlDmRC2t9Art/vuofozVXoLaF1SIOrgBiwryID40ISRKXYjESalfWhfaNeGxDpl+uPIv4pXNA==","shasum":"02e4967e106112720daa49a6360df3a284d1366e","tarball":"https://registry.npmjs.org/l-html/-/l-html-1.0.3.tgz","fileCount":22,"unpackedSize":283551,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5yndCRA9TVsSAnZWagAAn8QP/0hF9FhXhoSwpoxdkYI1\n9KHz0M/dNTgEWzj7wrtSQoxVV8rbMjY11+2fdMBKrBhtr8ettWFAEIfzPQlO\nL6dmAqwj78R0fKH/2fd4ORnmpZ497aYCA9ocHj7SLtqi7lnzOANgTLtMHKBD\n6kxtlvd2+kLic1lZRmdR465cDmoaRktOTKPB3sMeSEQd6gcSuTQ8LYoLvGNc\nG88Cf6GLd5nTO1FP41ys8IbTpTomHqLl2yEZ1kXDcS7SJFD9M7BbO8KVvKgN\n7X+31p5lYh1TYD5H6r+AcDmkqRqr52MN/c7jIbmx8dg0Epkbvim6nEWQel+1\nOjNYwZjm+H2CINEzq/DA3g4srZa1ZZSL9sK6y+X9taPzaY89wS6fgn6eaDck\nkWm+qiN0H6qVDsYFhnWz3Y4THGFJ7cuIOaCEFeIS10OQUV6C7Xb58+KRUJU4\nyddjq5+yAx+HSB1I1F94wxygJ8XUq6lhI+2awxGJnErKQOUgwTXAUjk/Te8J\nszQs+JHrluDnusobnBL7KmfHa8RkGCYtbqHJ/PNcPBabgEvXuxY2cqQdNPBA\nJTyK5JTZdOXcbvZLs8RIZObaCOIpkWXwDRj39ry/t+CdmhMm0R1/GB6tQFvb\nhqcDtFsD5AmujfoyxW84jrAIF2v58UGG5IqHSOQtDTDAhbff3UkDTNZ7gsXq\n+Edj\r\n=LWMY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHvb4qLMQSix4Yiqro2NpudFx2smN8hrDOgbmj1t+lMcAiB7Uo4D+SuBogR3ybY/y8Jd4SkjhvFGz6rfYgXuA5uS9g=="}]},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-html_1.0.3_1558653405007_0.30142409530786884"},"_hasShrinkwrap":false},"1.0.4":{"name":"l-html","version":"1.0.4","description":"An HTML Generation Library for Javascript","main":"dist/l.js","scripts":{"test":"npm run test-behavior","test-behavior":"mocha --compiler js:babel-core ./test/behavior/","test-performance":"mocha ./test/performance/","build":"webpack --mode=production","watch":"onchange 'src/**/*.js' -- npm run build","dev-watch":"onchange 'src/**/*.js' -- npm run dev-build","dev-build":"webpack --mode=development"},"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"keywords":["html","generation","l"],"author":{"name":"Adam Bertrand Berger"},"license":"MIT","bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"homepage":"https://github.com/adambertrandberger/l#readme","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/node":"^7.2.2","@babel/plugin-transform-runtime":"^7.4.4","@babel/polyfill":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/runtime":"^7.4.4","babel-loader":"^8.0.6","chai":"^4.2.0","jsdom":"^15.1.0","mocha":"^6.1.4","onchange":"^6.0.0","webpack":"^4.31.0","webpack-cli":"^3.3.2"},"gitHead":"9cdb8f5db089eb1c9fa786edc65b28e0303f83e3","_id":"l-html@1.0.4","_npmVersion":"6.2.0","_nodeVersion":"10.8.0","_npmUser":{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"},"dist":{"integrity":"sha512-9cDFFiNVeVlUuMhieu32GqA9mMq457kuu/c6+h5KFeDIi//MXP4GgJVxMt69KF1T5knszLbtN7kPI3kdgXOldA==","shasum":"c1675d74f54c6704369d46c655b0e31d0fcd88b9","tarball":"https://registry.npmjs.org/l-html/-/l-html-1.0.4.tgz","fileCount":26,"unpackedSize":425717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7fTFCRA9TVsSAnZWagAAXywP/2jNrW67Lj5F5NrO8FtR\nom1SB//gaDRIKorzFFh66WChCwwBEhPAkLBWOyyKAe3EfYOJXGQI26UtujnD\nch8/AM3nv1YFYZcnXgGvudTXI9XWP59jjTKibSPyZKMWT0fB9uj5huN6mb/Z\nxvE9uugf5QQBRGEDT83h0Z2hTLeAeAP9sjQYejHmCKK/YH8A5WCD9KcNwr3M\nIkd0TuzAABwWugqBhkwDknpsVBZBbmCw4OInz9ymeqen6nOBMqolXcAimvik\nxZmy+Nw13nH6YjZyoCkpTIuC0sVklz18Dcc2WQBD5qZTcSvB1iGzKuUSuXyb\nYBV/nUaFpFBBdP2O3kCtWTVCc7opIuykhTAawZ9fa+FB1WjlBU+5B6KtqC8H\n398rCmyaszDhdjzLVrZgSxQrupdWZSkcKv0M3k9ZFAH4GJyJj5cyke1ec3d0\na3zFG9bTiqRGoDmbUUT24fYR+Mpb4CW8LNQitzgnNp3UYWBPa5P3pru9NRfJ\n9iLqkGagLLP8j/5ra/HJtxiMURL8rrV46oJITItzrOKk0UUBYiydCqMdrX3V\na6v+Q35vGjtXoPKnM5CS3UDgWDZ67/8NZu+TpeZKVnawrfRtdYhlKaQVfB1G\nqPRgDuIsH1zfna0p2M052wkgcLj/jKUx5DDJ0hVQ0q+lSg052LPv/zjyiZ8D\n/VxG\r\n=oxSt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFGCa3QRv6PONk76dQjv1PIgH2ibMnOewFcGK60O0WgGAiB8d60o3FPQPTioR/lJm8GY0KNRtomTN5YDwSEQIEN75g=="}]},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-html_1.0.4_1559098564115_0.6983474447321973"},"_hasShrinkwrap":false}},"time":{"created":"2019-05-20T03:28:48.719Z","1.0.0":"2019-05-20T03:28:48.900Z","modified":"2022-05-07T14:02:54.569Z","1.0.1":"2019-05-20T03:30:44.495Z","1.0.2":"2019-05-23T23:12:20.684Z","1.0.3":"2019-05-23T23:16:45.292Z","1.0.4":"2019-05-29T02:56:04.253Z"},"maintainers":[{"name":"adambertrandberger","email":"adambertrandberger@gmail.com"}],"description":"An HTML Generation Library for Javascript","homepage":"https://github.com/adambertrandberger/l#readme","keywords":["html","generation","l"],"repository":{"type":"git","url":"git+https://github.com/adambertrandberger/l.git"},"author":{"name":"Adam Bertrand Berger"},"bugs":{"url":"https://github.com/adambertrandberger/l/issues"},"license":"MIT","readme":"<div align=\"center\">\n<img src=\"img/logo.png\" width=\"100px\"></img>\n<br>\n<a href=\"#\"><img alt=\"no dependencies\" src=\"https://img.shields.io/badge/dependencies-none-blue.svg\"></img></a>\n<a href=\"https://travis-ci.org/adambertrandberger/l\"><img alt=\"build status\" src=\"https://travis-ci.org/adambertrandberger/l.svg?branch=master\"></img></a>\n<h1>for Javascript</h1>\n</div>\n\nWhether creating DOM nodes for your client-side app, or generating HTML for a Node.js webserver, <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> makes it easy.\nFor your client-side apps, you will be free from the chains that are the DOM API. No longer will you have verbose calls like `document.createElement`\nbinding you to your keyboard. With your new found freedom you can focus on describing HTML in a natural way. For your webservers, you can use the same \nliberating abstractions offered by <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> to generate HTML.\n\nTry it out online [here](http://idiocode.com/projects/l/try.html)!\n\n<img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>:\n```javascript\nl(() => div(\nh1('Example'),\ndiv(p('First'), hr, p('Second'), span(span(span('Nesting is easy.')))),\nsection(header(h1('HTML 5 is supported too!')),\ndiv('Properties can be set', { style: { color: 'red' }}),\ndiv('Attributes can be set too!', { class: 'wow' }))\n)\n);\n```\n\nHTML:\n```html\n<div>\n<h1>Example</h1>\n<div>\n<p>First</p>\n<hr>\n<p>Second</p>\n<span><span><span>Nesting is easy.</span></span></span>\n</div>\n<section>\n<header><h1>HTML 5 is supported too!</h1></header>\n<div style=\"color: red;\">Properties can be set</div>\n<div class=\"wow\">Attributes can be set too!</div>\n</section>\n</div>\n```\n\n## Installation\n### For Browsers\nJust download `dist/l.js` and place it next to the HTML you're importing it from. Don't forget to add a script tag: \n\n```html\n<script src=\"l.js\"></script>\n```\n\n### For Node.js\n<img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> works in Node.js too! Install with `npm install l-html`, then import using `require`:\n\n```javascript\nconst l = require('l-html')\n```\n\n## Test\n<img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> has `mocha` tests that verify that the code works as expected. If you want to run the tests, run:\n\n```\ngit clone https://github.com/adambertrandberger/l\nnpm install\nnpm test\n```\n\nThis is an optional step. You should only do it if you want to verify everything works for yourself. But, reading some of these tests is a good supplement to the tutorial below. \n\n## Getting Started\nAfter importing <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> in your environment, there will be one new variable called `l` that you can use. \nEverything we go over in the following sections describes what you can do with this object.\n\n## Generating Elements\nThere are multiple ways to generate HTML through the `l` object. \nThe most straightforward way is to create a tag by calling the tag name using the dot operator on the `l` object.\n\n```javascript\nl.div()\n// <div></div>\n\nl.section()\n// <section></section>\n```\n\nThe `l` object has a function like this for every valid HTML tag.\n\nYou may be saying \"what about custom HTML elements?\". You're right, the HTML spec allows for custom tag names and so does <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>. \nIf you want to generate a custom HTML tag, you can make it by calling `l` as a function:\n\n```javascript\nl('customtag')\n// <customtag></customtag>\n\nl('burrito')\n// <burrito></burrito>\n```\n\nIn a later section we will talk about adding children to these HTML elements. You'll see examples where we can generate elements by using the value of a tag generating function like `l.div` or `l.span`:\n\n```javascript\nl.div(l.br, l.hr, l.span)\n// <div><br><hr><span></span></div>\n```\n\nSee how we used `l.br`, `l.hr`, and `l.span` without calling them like `l.br()`, `l.hr()`, and `l.span()`? This works for all HTML elements when they are children of another element.\n\n## Adding Content\nNow we can make tags, but how can we add content to them? Starting from the methods above, we can\nadd one more argument to each of these function calls. You can pass it a string to set the innerHTML:\n\n```javascript\nl.div('This is the innerHTML')\n// <div>This is the innerHTML</div>\n\nl('burrito', \"I'm in a burrito\")\n// <burrito>I'm in a burrito</burrito>\n```\n\nYou can keep adding in more strings as additional arguments too:\n\n```javascript\nl.div('First', 'Second', 'Third')\n// <div>FirstSecondThird</div>\n\nl('burrito', \"I'm\", \"in\", \"a\", \"burrito\")\n// <burrito>I'minaburrito</burrito>\n```\n\nThis will be more useful later when we learn about nesting HTML elements inside of each other.\n\n## Attributes and Properties\nYou can pass an object to configure attributes and properties of the HTML element:\n\n```javascript\nl.div({ style: { color: 'pink'}})\n// <div style=\"color: pink;\"></div>\n\nl('burrito', { id: 'special' })\n// <burrito id=\"special\"></burrito>\n\nl.span({ innerHTML: 'You can set the html like this too' })\nl.span({ html: 'You can set the html like this too' })\n// <span>You can set the html like this too</span>\n```\n\nIn the object that we passed, <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  will try to guess if the value should be set as an attribute, or a property on the node.\nIf you want to force a value to be a property or attribute explicitly you can use the reserved key `attrs` for attributes\nand `props` for properties:\n\n```javascript\nl.textarea({ value: 'My value' })\n// <textarea value=\"My value\"></textarea>\n\nl.textarea({ props: { value: 'My value' }})\n// <textarea></textarea>\n```\n\nNotice how <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  inferred that `value` was an attribute, but when told to use it as a property it will be assigned to the node \nas a property. This results in the HTML not containing the potentially long `value` attribute, but it is still shown in the HTML. \n\n\nYou can mix these two arguments (the string and object) to make the syntax more compact:\n```javascript\nl.div('This is the text', { style: { color: 'red' }})\n// <div style=\"color: red;\">This is the text</div>\n```\n\n## Children\nThere are two ways of adding children to a node. You can pass a list as one of the arguments:\n\n```javascript\nl.div([l.span, l.span])\n// <div><span></span><span></span></div>\n```\n\nOr you can add any number of additional arguments as nodes:\n\n```javascript\nl.div(l.span)\n// <div><span></span></div>\n\nl.div(l.span, l.span)\n// <div><span></span><span></span></div>\n```\n\nChildren don't have to be nodes either. You can pass strings, or numbers and they will be converted to\ntext nodes:\n\n```javascript\nl.div(l.span('a', 'b', 'c'), 'd')\n// <div><span>abc</span>d</div>\n\nl.span(1, 2, 3, l.div(4, 'five', l.div(l.div('six'))))\n// <span>123<div>4five<div><div>six</div></div></div></span>\n```\n\n### <img src=\"img/l.png\" alt=\"l\" height=\"16px\"></img>  Function\nYou know how we have been saying `l.div()` and `l.span()`? With the `l` _dot_? That isn't what the example code looks like is it? In the example code we didn't \nhave to type `l` _dot_. To get the pretty code you see in the examples, you have to use <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  functions. An <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  function is a shortcut so that you \ncan skip typing `l.` all the time. Everything works the same as before. All the arguments are the same, its just easier to type.\nThe main advantage is that you can feel like you're typing actual HTML instead of using a library.\n\n```javascript\nl(() => div(span('Feels more like writing HTML'), br, span('Pretty cool')))\n\nl.div(() => span('You use it anywhere'), () => l.br, () => l.span('Yeah'))\n```\n\nThere is a performance overhead that comes with using <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions. If you are using this to create a \nmission-critical page that would benefit from being a couple of milliseconds faster, you can skip using <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions, or even look into using `l.import`. But, everyone else will be able enjoy the\ncleaner interface.\n\nA nice trait of <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions is that they don't clobber variables in the outer or global scope:\n\n```javascript\nvar a = \"Can't touch this\";\nl(() => a('What are you talking about', { href: '#' }))\n// <a href=\"#\">What are you talking about</a>\nconsole.log(a === \"Can't touch this\");\n```\n\nSee how inside of the <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  function, `a` changed its value from being `\"Can't touch this\"` to being a function that creates an anchor tag? Even better,\nthe `a` defined in the outer scope is untouched. You can think of any tag name as a reserved keyword inside of an <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  function.\nIf you need to close a variable over an <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  function, you should make sure it isn't the name of an HTML tag, as it will not\nbe visible inside of the <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  function.\n\n#### <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> Functions and Variable Scopes\nYou might be wondering exactly which variables <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions are capable of accessing. Due to implementation problems, <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions \naren't able to keep the same properties that exist in Javascript functions. They do not capture local variables as Javascript's closures do. This means\nthe following code will _not_ work:\n\n```\n// Does NOT work:\nfunction myFunc() {\nconst thing = 12;\nconsole.log(() => div(thing));\n}\n\nmyFunc(); // throws \"ReferenceError: thing is not defined\"\n```\n\nTo look on the bright-side, this is good because it means that no local variables (variables defined in functions) will get clobbered with names of the element generators in the \n<img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions. But it would be nice to access local variables from within an <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function.\nIn the next section we will take a look at `l.with`, which is a way to pass arguments to <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions.\n\nWhile they can't access local variables, they _can_ access all global variables:\n\n```\n// DOES work\nconst thing = 12;\nfunction myFunc() {\nconsole.log(() => div(thing));\n}\n\nmyFunc(); // <div>12</div>\n```\n\nBut, if we want <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions to be able to render different HTML based off different objects, we need a way of passing\ninformation to the <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions. In the next section we will show the solution to this: `l.with`.\n\n## Passing Arguments to <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> Functions\nBecause <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> aren't closures, we need some way of passing variables into <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function if we ever want to render HTML based on some input data.\n\n`l.with` lets this happen. `l.with` is just like calling `l` as a function (`l(...)`), except it takes a required object as its first argument.\n\nAll of the fields in that object will be available inside of the <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function. Take a look:\n\n```\nconst person = { age: 23, name: 'Dave' },\n    animal = { type: 'birdy', talk: () => 'tweet' };\nl.with({ person, animal }, () => div('You are: ', person.age, ' years old.', span(animal.talk()))); // <div>You are: 23 years old<span>tweet</span></div>\n```\n\nNotice how you don't need to give the  <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function any additional parameters. The object you give `l.with` automagically includes them in the\nscope. If you only needed the \"person\" and didn't want to have to type \"person\" all the time you can just pass in the \"person\" directly too:\n\n```\nconst person = { age: 23, name: 'Dave' };\nl.with(person, () => div('You are: ', age, ' years old.')); // <div>You are: 23 years old</div>\n```\n\n## Appending to Existing Nodes\n<img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  supports a way of appending children to existing DOM nodes. When you use `l` as a function, as we have done <a href=\"#generating-elements\"><i>Generating Elements</i></a>,\nyou can pass `l` an existing DOM node, and it will append any nodes that are given after it to that node.\n\n```javascript\nl(document.body, () => div(\"I've been appended!\"))\n// <body> ... <div>I've been appended!</div></body>\n```\n\nThis works as you are building elements too:\n\n```javascript\nl(l.article, () => section(h1('First Part'), p), () => section(h1('Second Part'), p))\n```\n\n## Creating HTML strings\nNo matter if you are in a browser or Node.js, everything we have learned above will work wherever you use it. However, the output has always been DOM nodes. Having DOM nodes in Node.js isn't always useful. Most of the time\na string would be much better.\n\nOne way of getting a string from any one of these nodes is to access the `outerHTML` property:\n\n```javascript\nl(() => div(span, br)).outerHTML === '<div><span></span><br></div>'\n```\n\nThere is something rather nasty about this... Oh yeah! It is the DOM API style name. If that is a turn-off, you can use the `l.str` function instead:\n\n```javascript\nl.str(() => div(span, br)) === '<div><span></span><br></div>'\n```\n\n`l.str` is the same as `l(...)`, but it outputs a string instead of an HTML element.\n\n## Put Them All Together\nTry mixing-and-matching adding content, with attributes and properties along with adding children and see what happens.\nPut things where you think they ought to be, and see if it works. Its easy to check the output by looking at `outerHTML`.\n\n```javascript\nl.div(1, () => div(span, br, () => div, article, 'oiwjef'), 'three', l.hr, l.br, 'four')\n// <div>1<div><span></span><br><div></div><article></article>oiwjef</div>three<hr><br>four</div>\n```\n\nThat's a complicated example, but it shows how all the arguments we learned about can be nested together\nand used interchangably. <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  makes sense of whatever mess you give it. So try to give it your own mess\nand see what it gives you back.\n\n## What's Next?\nTry mixing in Javascript with <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> to render your objects for you:\n\n```javascript\nclass Profile {\nconstructor(name, age, phoneNumber) {\nthis.name = name;\nthis.age = age;\nthis.phoneNumber = phoneNumber;\n}\n\neditAge(e) {\nalert('Call me at ' + this.phoneNumber);\n}\n\nrender(parent) {\nreturn l(parent, l.with(this, () => div(\nh1(`Welcome, ${name}!`),\nul(li(`Age: ${age}`), \nli(`Phone Number: ${phoneNumber}`, { onclick: editAge })\n),\n)));\n}\n}\n```\n\nThis way you can generate an HTML page for any person's profile. No Virtual DOM needed!\n\nMake a game in Javascript, make a webserver, make visualizations, do anything you could do in normal Javascript and HTML, but enjoy it more.\n\n## A Note on the Performance of <img src=\"img/l.png\" alt=\"l\" height=\"18px\"></img> Functions\nEverytime you use an <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function, Javascript's `eval(...)` function is called. The function you supply\nis actually converted to a string, then, among other things, a bunch of `var` declarations for each tag name are prepended to that string and fed into an `eval(...)`.\n\nPopular opinion is to think that this is massively inefficient. From my tests, it is a bit inefficient, but still very usable even in production environments. Using `eval` allows the library to be even more flexible \nbecuase it can use intermediate objects for HTML elements. Down the road additional helper functions and optimizations could be written that takes advantage of this. For example, when interfacing with the DOM, there are optimizations \nfor calls which are made in succession intermediate objects allow us to be lazy about DOM node generation and allow us to do some optimizations around that. Element generator functions couldn't do this because\nthere is no way to say \"whenever a tag generator completes, and the stack is empty, convert the result to a DOM node\". Well... there is a way, but it includes using `Function.prototype.caller` which is widely\nunderstood to be on the docket for removal from most Javascript implementations.\n\nAn optimization like this can still be done manually; however,\nrequiring a manual step from the programmers though. It would look like this: `l.dom(l.div, l.span, l.div(l.div()));`. But then, the whole library would have to be used this way.\nbut I'm not willing to make users have to worry about when the intermedate HTML element objects are turned into DOM nodes. At that point, I turn to my opinion that the programmers time\nis not worth that small of an efficiency gain. This is why the option to use an `eval` based approach may be a good thing rather than a bad thing. There is no doubt about the weakened performance\nat the moment though. I did a test on my macbook, and I was able to create ~500 DOM nodes (a `div` with 500 `span` children) in 0.025 milliseconds using element generators (`l.div()` and `l.span()`).\nI tried the same experiment using <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions and was able to make 500 DOM nodes in 3.88 milliseconds. These tests were done in Google Chrome, not on Node.js.\nI assume the running times will be different on Node.js based on how a fake DOM is used in that environment. \n\nKeep in mind this is a lot of DOM nodes. 500 nodes is enough for making most web pages. When this number becomes unusable is when you are using the library for a game that requires 60FPS updates and has > 10,000 DOM nodes (since every frame must take less than 16.66ms).\nPerhaps a large n-body particle simulation would greatly benefit from skipping <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions all together and just going with element generators.\n\nThe conclusion I take away from these results is to use <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions whenever performance doesn't matter. Rendering a webpage, and even frequent re-rendering of a \nwebpage is a good use for <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions because it will save programmer time. For tasks that are extremely time sensitive (such as rendering animation frames) or for\nextremely large webpages (>10,000 DOM nodes), use element generators, or `l.import`.\n\n## But I want high performance, and <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions!\nWe can do that. But you are going to have to sacrifice for a little good ol' fashioned namespace pollution:\n\n```\nl.import();\n\ndiv(span(span()), 'it works!')\n```\n\nThere is `l.import` for this need. The function takes an object to import the element generators into (defaults to `window`), and a boolean which indicates whether to clobber existing variables or not (defaults to `false`, as in \"don't clobber\").\n\nThis isn't a horrible choice if you really like the <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function syntax, but want the performance. Although, it has potential to cause some nasty variable overriding issues. I figured I would keep it in the library for those who don't care about namespace pollution :).\n\n## On the use of `eval` instead of `with`\n\nA question I forsee from a small number of people is \"why not use the `with` statement instead of building up your own variable declarations in the string that is eval'd?\". Well, the Javascript community has effectively killed\nthe `with` statement. Javascript running in `strict mode` doesn't pass the parsing step if it finds a `with` statement. Many tools are hard coded to only use `strict mode` for\ntheir build processes. This means if I wanted to use the latest transpilers and bundlers, like babel and webpack, I couldn't use the `with` statement.\n\nI'm sure before the first question is asked most people would say \"why were you even considering using the `with` statement?!\". The dynamic nature of <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions required me to do some namespace hacking.\n\n## Related Libraries\nThe idea of generating HTML in programming languages is old. It has been (almost famously) re-invented in lisps many times. [spinneret](https://github.com/ruricolist/spinneret), for example, is a library for generating HTML5. \nProgramming languages like [prolog](http://www.swi-prolog.org/pldoc/man?section=htmlwrite), python, and c have done it too. However, these programming languages don't run directly in the browser like Javascript does. \nThose libraries are created for web servers or static page generation. No offense to server-side programming, especially because <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> supports that. But, having access to the DOM allows \nnodes to be created dynamically and for making interactive HTML. Seeing as Javascript can run in the browser like this, and be used for a webserver it is a natural fit for such a library.\n\nThe browser already has the capability of generating HTML through a builtin interface: the DOM. \nI've used the DOM enough to know how time consuming it is to use. Take a look at how messy it is to generate `<div><span>This is tedious!</span></div>` and append it to `document.body`:\n\n```javascript\nconst node = document.createElement('span')\nnode.innerHTML = 'This is tedious!'\nconst container = document.createElement('div')\ncontainer.appendChild(node)\ndocument.body.appendChild(container)\n```\n\nOthers have noticed how messy this is too and have created libraries to make using it faster for the programmer. Here are some of the libraries that came before <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>  and inspired my work:\n- [jaml](http://edspencer.github.io/jaml/) - Has the same interface as <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions, but only generates HTML strings. It seems like it was inspired from MVC style HTML templates like that of Ruby on Rails or Django.\n- [http/html_write](http://www.swi-prolog.org/pldoc/man?section=htmlwrite) - A prolog library for generating HTML. Identical interface as JAML and <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> functions.\n- [crel](https://github.com/KoryNunn/crel) - More lightweight than <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> with some interface differences (the constructor handles attributes/properties differently). Doesn't have an <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function equivalent.\n- [laconic](https://github.com/joestelmach/laconic) - Similar to crel (crel says this was its inspiration).\n- [RE:DOM](https://redom.js.org/) - Has much in common with <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img>, but doesn't have a <img src=\"img/l.png\" alt=\"l\" height=\"14px\"></img> function equivalent, and focuses more on providing features for web components and updating the DOM.\n\n## License\n\nCopyright 2019 Adam Bertrand Berger\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n## TODO:\n- Add a way to print a pretty html string\n- Rename Element to Node and Node to NodeProperties (and make a new Element class and TextNode class)\n- Make website\n- Add try-out-online page\n- Create example apps\n- why does doing `l.div` vs `l.div()` have such a large performance impact\n- figure out why Proxy isn't being used in the browser for the `l` object\n","readmeFilename":"README.md"}