├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── cjs └── index.js ├── esm └── index.js ├── index.js ├── min.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .DS_Store 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | img/ 3 | node_modules/ 4 | test/ 5 | _config.yml 6 | .DS_Store 7 | .gitignore 8 | .travis.yml 9 | package-lock.json 10 | rollup.config.js 11 | es.config.js 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | git: 5 | depth: 1 6 | branches: 7 | only: 8 | - master 9 | - /^greenkeeper/.*$/ 10 | after_success: 11 | - "npm run coveralls" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019, Andrea Giammarchi, @WebReflection 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qs2el 2 | 3 | **Social Media Photo by [Matheus Frade](https://unsplash.com/@matheusfrade) on [Unsplash](https://unsplash.com/)** 4 | 5 | [![Build Status](https://travis-ci.com/WebReflection/qs2el.svg?branch=master)](https://travis-ci.com/WebReflection/qs2el) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/qs2el/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/qs2el?branch=master) ![WebReflection status](https://offline.report/status/webreflection.svg) 6 | 7 | 8 | Create an element from a query selector string. 9 | 10 | ```js 11 | // as function 12 | const a = qs2el('a#home.special[data-page=home]'); 13 | 14 | // or as literal 15 | const button = qs2el`button${'.primary'}[disabled]`; 16 | ``` 17 | 18 | The exported function also have an `.escape(value)` to safely parse text that might contain `[` or `]` chars. 19 | 20 | ```js 21 | const value = 'this [might] be an issue'; 22 | const div = qs2el`div[data-blob=${qs2el.escape(value)}]`; 23 | 24 | div.dataset.blob === value; // true 25 | ``` 26 | -------------------------------------------------------------------------------- /cjs/index.js: -------------------------------------------------------------------------------- 1 | var qs2el = (function (fromCharCode, toCharCode) { 2 | 3 | /*! (c) Andrea Giammarchi - ISC */ 4 | 5 | qs2el.escape = escape; 6 | 7 | return qs2el; 8 | 9 | function asChar(_, $) { 10 | return fromCharCode($); 11 | } 12 | 13 | function asEntity($) { 14 | return '&#' + toCharCode[$] + ';'; 15 | } 16 | 17 | function qs2el(selector) { 18 | return selector2element.apply( 19 | null, 20 | /^([a-z0-9-]+)?(#[a-z0-9_-]+)?((?:\.[a-z0-9_-]+)+)?(\[[\s\S]*\])?$/.exec( 21 | typeof selector === 'string' ? 22 | selector : 23 | join.apply(null, arguments) 24 | ) 25 | ); 26 | } 27 | 28 | function escape(value) { 29 | return value.replace(/[[\]]/g, asEntity); 30 | } 31 | 32 | function join(template) { 33 | for (var 34 | out = [template[0]], 35 | i = 1; i < arguments.length; i++ 36 | ) { 37 | out.push(arguments[i], template[i]); 38 | } 39 | return out.join(''); 40 | } 41 | 42 | function selector2element(_, tag, id, classes, attributes) { 43 | var el = document.createElement(tag || 'div'); 44 | if (id) 45 | el.id = id.slice(1); 46 | if (classes) 47 | el.className = classes.replace(/\./g, ' ').slice(1); 48 | if (attributes) { 49 | var re = /\[([\s\S]*?)\]/g; 50 | var match; 51 | while (match = re.exec(attributes)) { 52 | var all = match[1]; 53 | var i = all.indexOf('='); 54 | var key = i < 0 ? all : all.slice(0, i); 55 | var value = i < 0 ? true : unescape(unquote(all.slice(i + 1))); 56 | if (key === 'class') 57 | key = 'className'; 58 | if (key in el) 59 | el[key] = value; 60 | else if (key) 61 | el.setAttribute(key, i < 0 ? '' : value); 62 | } 63 | } 64 | return el; 65 | } 66 | 67 | function unescape(value) { 68 | return value.replace(/&#(91|93);/g, asChar); 69 | } 70 | 71 | function unquote(value) { 72 | return value.replace(/^("|')?([\s\S]*)\1$/, '$2'); 73 | } 74 | 75 | }(String.fromCharCode, {'[': 91, ']': 93})); 76 | module.exports = qs2el; 77 | -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | var qs2el = (function (fromCharCode, toCharCode) { 2 | 3 | /*! (c) Andrea Giammarchi - ISC */ 4 | 5 | qs2el.escape = escape; 6 | 7 | return qs2el; 8 | 9 | function asChar(_, $) { 10 | return fromCharCode($); 11 | } 12 | 13 | function asEntity($) { 14 | return '&#' + toCharCode[$] + ';'; 15 | } 16 | 17 | function qs2el(selector) { 18 | return selector2element.apply( 19 | null, 20 | /^([a-z0-9-]+)?(#[a-z0-9_-]+)?((?:\.[a-z0-9_-]+)+)?(\[[\s\S]*\])?$/.exec( 21 | typeof selector === 'string' ? 22 | selector : 23 | join.apply(null, arguments) 24 | ) 25 | ); 26 | } 27 | 28 | function escape(value) { 29 | return value.replace(/[[\]]/g, asEntity); 30 | } 31 | 32 | function join(template) { 33 | for (var 34 | out = [template[0]], 35 | i = 1; i < arguments.length; i++ 36 | ) { 37 | out.push(arguments[i], template[i]); 38 | } 39 | return out.join(''); 40 | } 41 | 42 | function selector2element(_, tag, id, classes, attributes) { 43 | var el = document.createElement(tag || 'div'); 44 | if (id) 45 | el.id = id.slice(1); 46 | if (classes) 47 | el.className = classes.replace(/\./g, ' ').slice(1); 48 | if (attributes) { 49 | var re = /\[([\s\S]*?)\]/g; 50 | var match; 51 | while (match = re.exec(attributes)) { 52 | var all = match[1]; 53 | var i = all.indexOf('='); 54 | var key = i < 0 ? all : all.slice(0, i); 55 | var value = i < 0 ? true : unescape(unquote(all.slice(i + 1))); 56 | if (key === 'class') 57 | key = 'className'; 58 | if (key in el) 59 | el[key] = value; 60 | else if (key) 61 | el.setAttribute(key, i < 0 ? '' : value); 62 | } 63 | } 64 | return el; 65 | } 66 | 67 | function unescape(value) { 68 | return value.replace(/&#(91|93);/g, asChar); 69 | } 70 | 71 | function unquote(value) { 72 | return value.replace(/^("|')?([\s\S]*)\1$/, '$2'); 73 | } 74 | 75 | }(String.fromCharCode, {'[': 91, ']': 93})); 76 | export default qs2el; 77 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var qs2el = (function (fromCharCode, toCharCode) { 2 | 3 | /*! (c) Andrea Giammarchi - ISC */ 4 | 5 | qs2el.escape = escape; 6 | 7 | return qs2el; 8 | 9 | function asChar(_, $) { 10 | return fromCharCode($); 11 | } 12 | 13 | function asEntity($) { 14 | return '&#' + toCharCode[$] + ';'; 15 | } 16 | 17 | function qs2el(selector) { 18 | return selector2element.apply( 19 | null, 20 | /^([a-z0-9-]+)?(#[a-z0-9_-]+)?((?:\.[a-z0-9_-]+)+)?(\[[\s\S]*\])?$/.exec( 21 | typeof selector === 'string' ? 22 | selector : 23 | join.apply(null, arguments) 24 | ) 25 | ); 26 | } 27 | 28 | function escape(value) { 29 | return value.replace(/[[\]]/g, asEntity); 30 | } 31 | 32 | function join(template) { 33 | for (var 34 | out = [template[0]], 35 | i = 1; i < arguments.length; i++ 36 | ) { 37 | out.push(arguments[i], template[i]); 38 | } 39 | return out.join(''); 40 | } 41 | 42 | function selector2element(_, tag, id, classes, attributes) { 43 | var el = document.createElement(tag || 'div'); 44 | if (id) 45 | el.id = id.slice(1); 46 | if (classes) 47 | el.className = classes.replace(/\./g, ' ').slice(1); 48 | if (attributes) { 49 | var re = /\[([\s\S]*?)\]/g; 50 | var match; 51 | while (match = re.exec(attributes)) { 52 | var all = match[1]; 53 | var i = all.indexOf('='); 54 | var key = i < 0 ? all : all.slice(0, i); 55 | var value = i < 0 ? true : unescape(unquote(all.slice(i + 1))); 56 | if (key === 'class') 57 | key = 'className'; 58 | if (key in el) 59 | el[key] = value; 60 | else if (key) 61 | el.setAttribute(key, i < 0 ? '' : value); 62 | } 63 | } 64 | return el; 65 | } 66 | 67 | function unescape(value) { 68 | return value.replace(/&#(91|93);/g, asChar); 69 | } 70 | 71 | function unquote(value) { 72 | return value.replace(/^("|')?([\s\S]*)\1$/, '$2'); 73 | } 74 | 75 | }(String.fromCharCode, {'[': 91, ']': 93})); 76 | -------------------------------------------------------------------------------- /min.js: -------------------------------------------------------------------------------- 1 | /*! (c) Andrea Giammarchi - ISC */ 2 | var qs2el=function(n,r){return e.escape=function(e){return e.replace(/[[\]]/g,t)},e;function g(e,r){return n(r)}function t(e){return"&#"+r[e]+";"}function e(e){return function(e,r,n,t,a){var c=document.createElement(r||"div");n&&(c.id=n.slice(1));t&&(c.className=t.replace(/\./g," ").slice(1));if(a)for(var i,l=/\[([\s\S]*?)\]/g;i=l.exec(a);){var u=i[1],s=u.indexOf("="),o=s<0?u:u.slice(0,s),f=s<0||(p=u.slice(s+1),p.replace(/^("|')?([\s\S]*)\1$/,"$2").replace(/&#(91|93);/g,g));"class"===o&&(o="className"),o in c?c[o]=f:o&&c.setAttribute(o,s<0?"":f)}var p;return c}.apply(null,/^([a-z0-9-]+)?(#[a-z0-9_-]+)?((?:\.[a-z0-9_-]+)+)?(\[[\s\S]*\])?$/.exec("string"==typeof e?e:function(e){for(var r=[e[0]],n=1;n> cjs/index.js", 9 | "coveralls": "cat ./coverage/lcov.info | coveralls", 10 | "esm": "cp index.js esm/ && echo 'export default qs2el;' >> esm/index.js", 11 | "min": "echo '/*! (c) Andrea Giammarchi - ISC */' > min.js && uglifyjs index.js -c -m >> min.js", 12 | "size": "cat index.js | wc -c;cat min.js | wc -c;gzip -c9 min.js | wc -c", 13 | "test": "istanbul cover test/index.js" 14 | }, 15 | "keywords": [ 16 | "query", 17 | "selector", 18 | "element", 19 | "transform" 20 | ], 21 | "author": "Andrea Giammarchi", 22 | "license": "ISC", 23 | "devDependencies": { 24 | "basichtml": "^2.1.4", 25 | "coveralls": "^3.0.9", 26 | "istanbul": "^0.4.5", 27 | "uglify-js": "^3.8.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('basichtml').init(); 2 | 3 | const qs2el = require('../cjs'); 4 | 5 | let el = qs2el('a#id.class[attribute=value]'); 6 | 7 | console.assert(el.id === 'id', 'id as function'); 8 | console.assert(el.className === 'class', 'class as function'); 9 | console.assert(el.getAttribute('attribute') === 'value', 'attribute as function'); 10 | 11 | el = qs2el`a#${'id'}${'.class'}${'[attribute=value]'}`; 12 | console.assert(el.id === 'id', 'id as literal'); 13 | console.assert(el.className === 'class', 'class as literal'); 14 | console.assert(el.getAttribute('attribute') === 'value', 'attribute as literal'); 15 | 16 | el = qs2el('a[class=test]'); 17 | console.assert(el.className === 'test', 'class as attribute'); 18 | 19 | el = qs2el('button[disabled]'); 20 | console.assert(el.hasAttribute('disabled'), '[attribute]'); 21 | 22 | el = qs2el`button[attr=${qs2el.escape('[test]')}]`; 23 | console.assert(el.getAttribute('attr') === '[test]', '[attribute=[test]]'); 24 | 25 | el = qs2el('#div'); 26 | console.assert(el.nodeName.toLowerCase() === 'div', 'div as default tag'); 27 | console.assert(el.id === 'div', 'the rest works too'); 28 | 29 | el = qs2el`button[a=b][][c=d]`; 30 | console.assert(el.outerHTML.toLowerCase() === '', 'empty attributes skipped'); --------------------------------------------------------------------------------