├── .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 | [](https://travis-ci.com/WebReflection/qs2el) [](https://coveralls.io/github/WebReflection/qs2el?branch=master) 
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');
--------------------------------------------------------------------------------