Repository: franciscop/superdom.js
Branch: master
Commit: 0b7ccde86f99
Files: 28
Total size: 55.5 KB
Directory structure:
gitextract_8hbc1cxk/
├── .gitignore
├── CNAME
├── Contributing.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── __tests__/
│ ├── class.test.js
│ ├── other.test.js
│ └── plugin.test.js
├── bower.json
├── index.html
├── package.json
├── src/
│ ├── plugins/
│ │ ├── attributes.js
│ │ ├── attributes.test.js
│ │ ├── helpers.js
│ │ ├── navigate.js
│ │ ├── navigate.test.js
│ │ ├── nodes.js
│ │ ├── nodes.test.js
│ │ ├── selectors.js
│ │ └── selectors.test.js
│ ├── superdom.js
│ └── superdom.test.js
├── superdom.js
└── web/
├── buildings.xcf
├── prism.css
├── prism.js
└── style.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.git
node_modules
npm-debug.log
.DS_Store
================================================
FILE: CNAME
================================================
superdom.site
================================================
FILE: Contributing.md
================================================
# Contributing
## API
There's a powerful API that is used internally. It might get documented and released in a future version, check the uses on /src/plugins if you are interested in developing another property for Superdom.
================================================
FILE: Gruntfile.js
================================================
// This builds the library itself
module.exports = function (grunt) {
// Configuration
grunt.initConfig({
concat: {
main: {
options: {
process: function (src, file) {
return /test\.js/.test(file) ? '' : src;
}
},
files: {
'superdom.js': ['src/superdom.js', 'src/plugins/*.js'],
// 'documentation.md': ['src/readme.md', 'src/plugins/**/readme.md']
}
}
},
usebanner: {
options: {
position: 'top',
banner: '/* Superdom.js v' + grunt.file.readJSON('package.json').version + ' by Francisco Presencia - MIT - https://github.com/franciscop/superdom.js */',
linebreak: true
},
files: {
src: [ './superdom.min.js' ]
}
},
// Super simple minifier that works with ES6
copy: {
main: {
src: 'superdom.js',
dest: 'superdom.min.js',
options: {
process: function (content, srcpath) {
return content
.replace(/\/\/[^\n]+/g, '')
.replace(/\/\*[^*]+\*\/\s+/, '')
.replace(/\n[^\S]*/g, ' ')
.replace(/\s?\=\>\s?/g, '=>')
.replace(/\s?\{\s?/g, '{')
.replace(/\s?\}\s?/g, '}')
.replace(/\s?\(\s?/g, '(')
.replace(/\s?\)\s?/g, ')')
.replace(/\s?\=\s?/g, '=')
.replace(/\s?\:\s?/g, ':')
.replace(/\s?\+\s?/g, '+')
.replace(/\s?\,\s?/g, ',')
.replace(/\s?\;\s?/g, ';')
.replace(/;}/g, '}')
;
}
}
}
},
semistandard: {
app: {
src: [
'src/**.js', '!src/**.test.js'
]
}
},
run: {
test: {
cmd: '/home/francisco/.nvm/versions/node/v7.2.1/bin/jest'
}
},
watch: {
scripts: {
files: [
'package.js', // To bump versions
'Gruntfile.js',
'src/**/*.js',
'__tests__/**/*'
],
tasks: ['default'],
options: {
spawn: false,
livereload: true
}
}
},
bytesize: {
all: {
src: [
'superdom.min.js'
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-semistandard');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-banner');
grunt.loadNpmTasks('grunt-bytesize');
grunt.loadNpmTasks('grunt-run');
grunt.registerTask('build', ['concat', 'copy', 'usebanner', 'bytesize']);
grunt.registerTask('test', ['semistandard', 'run:test']);
grunt.registerTask('default', ['build', 'test']);
};
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Francisco Presencia
Permission 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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE 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.
================================================
FILE: README.md
================================================
# Superdom
You have `dom`. It has all the DOM virtually within it. Use that power:
```js
// Fetch all the page links
let links = dom.a.href;
// Links open in a new tab
dom.a.target = '_blank';
```
> Only for [modern browsers](http://caniuse.com/#feat=proxy)
### Getting started
Simply use the CDN via unpkg.com:
```html
<script src="https://unpkg.com/superdom@1"></script>
```
Or use [npm](https://www.npmjs.com/package/superdom) or bower:
```
npm|bower install superdom --save
```
## Select
It always returns **an array with the matched elements**. Get all the elements that match the selector:
```js
// Simple element selector into an array
let allLinks = dom.a;
// Loop straight on the selection
dom.a.forEach(link => { ... });
// Combined selector
let importantLinks = dom['a.important'];
```
There are also some predetermined elements, such as `id`, `class` and `attr`:
```js
// Select HTML Elements by id:
let main = dom.id.main;
// by class:
let buttons = dom.class.button;
// or by attribute:
let targeted = dom.attr.target;
let targeted = dom.attr['target="_blank"'];
```
## Generate
Use it as a function or a tagged template literal to generate DOM fragments:
```js
// Not a typo; tagged template literals
let link = dom`<a href="https://google.com/">Google</a>`;
// It is the same as
let link = dom('<a href="https://google.com/">Google</a>');
```
## Delete elements
Delete a piece of the DOM
```js
// Delete all of the elements with the class .google
delete dom.class.google; // Is this an ad-block rule?
```
## Attributes
You can easily manipulate attributes right from the `dom` node. There are some aliases that share the syntax of the attributes such as `html` and `text` (aliases for `innerHTML` and `textContent`). There are others that travel through the dom such as `parent` (alias for parentNode) and `children`. Finally, `class` behaves differently as explained below.
### Get attributes
The fetching will always **return an array** with the element for each of the matched nodes (or undefined if not there):
```js
// Retrieve all the urls from the page
let urls = dom.a.href; // #attr-list
// ['https://google.com', 'https://facebook.com/', ...]
// Get an array of the h2 contents (alias of innerHTML)
let h2s = dom.h2.html; // #attr-alias
// ['Level 2 header', 'Another level 2 header', ...]
// Get whether any of the attributes has the value "_blank"
let hasBlank = dom.class.cta.target._blank; // #attr-value
// true/false
```
You also use these:
- html (alias of `innerHTML`): retrieve a list of the htmls
- text (alias of `textContent`): retrieve a list of the htmls
- parent (alias of `parentNode`): travel up one level
- children: travel down one level
### Set attributes
```js
// Set target="_blank" to all links
dom.a.target = '_blank'; // #attr-set
```
```js
dom.class.tableofcontents.html = `
<ul class="tableofcontents">
${dom.h2.map(h2 => `
<li>
<a href="#${h2.id}">
${h2.innerHTML}
</a>
</li>
`).join('')}
</ul>
`;
```
### Remove an attribute
To delete an attribute use the `delete` keyword:
```js
// Remove all urls from the page
delete dom.a.href;
// Remove all ids
delete dom.a.id;
```
## Classes
It provides an easy way to manipulate the classes.
### Get classes
To retrieve whether a particular class is present or not:
```js
// Get an array with true/false for a single class
let isTest = dom.a.class.test; // #class-one
```
For a general method to retrieve all classes you can do:
```js
// Get a list of the classes of each matched element
let arrays = dom.a.class; // #class-arrays
// [['important'], ['button', 'cta'], ...]
// If you want a plain list with all of the classes:
let flatten = dom.a.class._flat; // #class-flat
// ['important', 'button', 'cta', ...]
// And if you just want an string with space-separated classes:
let text = dom.a.class._text; // #class-text
// 'important button cta ...'
```
### Add a class
```js
// Add the class 'test' (different ways)
dom.a.class.test = true; // #class-make-true
dom.a.class = 'test'; // #class-push
```
### Remove a class
```js
// Remove the class 'test'
dom.a.class.test = false; // #class-make-false
```
## Manipulate
Did we say it returns a simple array?
```js
dom.a.forEach(link => link.innerHTML = 'I am a link');
```
But what an interesting array it is; indeed we are also proxy'ing it so you can manipulate its sub-elements straight from the selector:
```js
// Replace all of the link's html with 'I am a link'
dom.a.html = 'I am a link';
```
Of course we might want to manipulate them dynamically depending on the current value. Just pass it a function:
```js
// Append ' ^_^' to all of the links in the page
dom.a.html = html => html + ' ^_^';
// Same as this:
dom.a.forEach(link => link.innerHTML = link.innerHTML + ' ^_^');
```
> Note: this won't work `dom.a.html += ' ^_^';` for more than 1 match (for reasons)
Or get into genetics to manipulate the attributes:
```js
dom.a.attr.target = '_blank';
// Only to external sites:
let isOwnPage = el => /^https?\:\/\/mypage\.com/.test(el.getAttribute('href'));
dom.a.attr.target = (prev, i, element) => isOwnPage(element) ? '' : '_blank';
```
## Events
You can also handle and trigger events:
```js
// Handle click events for all <a>
dom.a.on.click = e => ...;
// Trigger click event for all <a>
dom.a.trigger.click;
```
## Testing
We are using Jest as a Grunt task for testing. Install Jest and run in the terminal:
```bash
grunt watch
```
================================================
FILE: __tests__/class.test.js
================================================
const dom = require('../superdom');
beforeEach(() => {
document.body.className = 'bla blu';
});
afterEach(() => {
document.body.className = '';
});
it("#class-arrays: should be an array", () => {
expect(typeof dom.body.class).toBe('object');
expect(dom.body.class instanceof Array).toBe(true);
});
it("#class-arrays: retrieves a deep list of classes", () => {
expect(dom.body.class).toHaveLength(1);
expect(dom.body.class[0]).toHaveLength(2);
expect(dom.body.class[0][0]).toEqual('bla');
expect(dom.body.class[0][1]).toEqual('blu');
});
it("#class-flat: retrieves a simple list of classes", () => {
expect(dom.body.class._flat).toHaveLength(2);
expect(dom.body.class._flat[0]).toBe('bla');
expect(dom.body.class._flat[1]).toBe('blu');
});
it("#class-text: retrieves a text list of classes", () => {
expect(dom.body.class._text).toBe('bla blu');
});
it("#class-one: can check for a particular class", () => {
expect(dom.body.class.bla).toBe(true);
expect(dom.body.class.nonexisting).toBe(false);
});
it("removes the attribute and all classes", () => {
delete dom.body.class;
expect(document.body.getAttribute('class')).toBe(null);
expect(document.body.className).toBe('');
});
// Add a class
it("#class-push: can add a single class with equal", () => {
document.body.className = '';
dom.body.class = 'bla';
expect(document.body.className).toBe('bla');
});
it("#class-push: it adds it to the end", () => {
dom.body.class = 'blo';
expect(document.body.className).toBe('bla blu blo');
});
it("#class-push: it does not overwrite others", () => {
dom.body.class = 'bla';
expect(document.body.className).toBe('bla blu');
});
it("#class-make-true: can add a single class with true", () => {
document.body.className = '';
dom.body.class.bla = true;
expect(document.body.className).toBe('bla');
});
it("#class-make-true: it's added to the end", () => {
dom.body.class.blo = true;
expect(document.body.className).toBe('bla blu blo');
});
it("#class-make-true: it does not overwrite others", () => {
dom.body.class.bla = true;
expect(document.body.className).toBe('bla blu');
});
// Delete a class
it("#class-make-false: can remove a single class with false ", () => {
document.body.className = 'bla';
dom.body.class.bla = false;
expect(document.body.className).toBe('');
});
it("can remove a single class with delete", () => {
document.body.className = 'bla';
delete dom.body.class.bla;
expect(document.body.className).toBe('');
});
it("does not remove other classes", () => {
document.body.className = 'bla blu blo';
delete dom.body.class.blu;
expect(document.body.className).toBe('bla blo');
});
================================================
FILE: __tests__/other.test.js
================================================
// Mutted so far until future migration
it('works', () => {});
// describe("dom[selector][attribute]", function() {
//
// afterEach(function(){
// base.attr({ bla: false, blu: false, blo: false });
// });
//
// it('#attr-each alias of forEach', () => {
// dom.a.each = (node, i, all) => {
// //console.log("Parts", node, i, all);
// expect(node instanceof Element).to.equal(true, 'An element');
// expect(typeof i).to.equal('number', 'A number');
// expect(all instanceof Array).to.equal(true, 'An array');
// };
// });
//
// it("#attr-list fake attr is empty", function() {
// console.log("AAA", typeof dom.id.base.bla[0], "BBB");
// expect(typeof dom.id.base.bla[0]).to.equal('undefined');
// expect(dom.id.base.bla instanceof Array).to.equal(true);
// });
//
// it("#attr-list retrieve a list of hrefs", function(){
// expect(str(dom['#base a'].href)).to.deep.equal(str(['#1', '#2', '#3']));
// expect(dom['#base a'].href[0]).to.equal('#1');
// });
//
// it("#attr-list retrieve a list of names", function(){
// var names = ['name', 'email', 'other', 'options'];
// expect(str(dom['#base form [name]'].name)).to.deep.equal(str(names));
// expect(dom['#base form [name]'].name[0]).to.equal('name');
// });
//
// it("#attr-value checks if any attribute has the value", function(){
// expect(dom['#base form [name]'].name.email).to.equal(true);
// expect(dom['#base form [name]'].name.nonexisting).to.equal(false);
// expect(dom['#base form [name]'].name['email']).to.equal(true);
// expect(dom['#base form [name]'].name['nonexisting']).to.equal(false);
// });
//
// it("#attr-alias get the text of .insides", function(){
// expect(str(dom.class.insides.text)).to.deep.equal(str(['a thing']));
// expect(dom.class.insides.text[0]).to.deep.equal('a thing');
// });
//
// it("#attr-alias can change the html of .insides", function(){
// dom.class.insides = '<div class="insides">I am inside</div>';
// expect($('.insides').html()).to.equal('I am inside');
// dom.class.insides = el => '<div class="insides">me too</div>';
// expect($('.insides').html()).to.equal('me too');
// });
//
// it("#attr-alias travel one level up", function(){
// expect(dom.id.base.parent.id[0]).to.equal('demo');
// });
//
// it("#attr-alias there's a limit up", function(){
// expect(dom.body.parent[0].nodeName).to.equal('HTML');
// expect(dom.html.parent[0]).to.equal();
// expect(typeof dom.html.parent[0]).to.equal('undefined');
// });
//
// it("#attr-alias go down a level", function(){
// expect(dom.html.children.length).to.equal(2);
// expect(dom.id.demo.children.id[0]).to.equal('base');
// });
//
// it("#attr-set set a new attribute", function(){
// dom.id.demo['data-value'] = 5;
// expect($('#demo').attr('data-value')).to.equal('5');
// });
//
// it("#attr-set change an attribute", function(){
// dom.id.demo['data-value'] = 3;
// expect($('#demo').attr('data-value')).to.equal('3');
// });
// });
================================================
FILE: __tests__/plugin.test.js
================================================
// Create a new plugin
let dom = require('../superdom');
dom.api.nodes.beetlejuice = {
get: node => 'beetlejuice',
set: (cb, node) => node.innerHTML = 'beetlejuice'
};
describe('plugin creation', () => {
it('can use the getter', () => {
expect(dom.body.beetlejuice[0]).toBe('beetlejuice');
});
it('can use the setter', () => {
dom.body.beetlejuice = 'beetlejuice';
expect(document.body.innerHTML).toBe('beetlejuice');
document.body.innerHTML = '';
});
});
================================================
FILE: bower.json
================================================
{
"name": "superdom",
"version": "0.9.0",
"description": "Better and simpler DOM manipulation",
"main": "superdom.min.js",
"authors": [
"Francisco Presencia Fandos <publicfrancisco@hotmail.com>"
],
"license": "MIT",
"keywords": [
"dom",
"manipulation",
"edit",
"add",
"remove",
"generate"
],
"homepage": "https://github.com/franciscop/superdom.js",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Superdom</title>
<meta name='viewport', content='width=device-width, initial-scale=1'>
<meta name="keywords" content="js, library, dom, superdom, manipulate">
<meta name="description" content="A lightweight DOM manipulation library">
<meta property="og:image" content="http://superdom.site/web/screenshot.png">
<meta property="og:url" content="http://superdom.site">
<meta property="og:title" content="Superdom">
<meta property="og:description" content="A lightweight DOM manipulation library">
<link href="https://unpkg.com/paperdocs@1/paperdocs.min.css" rel="stylesheet">
<link rel="stylesheet" href="/web/style.css" />
</head>
<body>
<main>
<div class="flex">
<div class="full half-900">
<h1>Superdom<sub>.js</sub></h1>
<p>Manipulate the DOM like it's <span id="year">2018</span></p>
<a href="https://unpkg.com/superdom@1" target="_blank">CDN</a>
<a href="https://github.com/franciscop/superdom/blob/master/README.md" target="_blank">Documentation</a>
<a href="https://github.com/franciscop/superdom" target="_blank">Github</a>
</div>
<div class="full half-900">
<pre class="language-js"><code class=" language-js"><span class="token comment" spellcheck="true">// Fetch all of the link's urls</span>
<span class="token keyword">let</span> links <span class="token operator">=</span> dom<span class="token punctuation">.</span>a<span class="token punctuation">.</span>href<span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">// Set the targets to a new page</span>
dom<span class="token punctuation">.</span>a<span class="token punctuation">.</span>target <span class="token operator">=</span> <span class="token string">'_blank'</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">// Manipulate the buttons without loops</span>
dom<span class="token punctuation">.</span>class<span class="token punctuation">.</span>cta<span class="token punctuation">.</span>html <span class="token operator">=</span> <span class="token string">'Click me!'</span><span class="token punctuation">;</span></code></pre>
</div>
</div>
</main>
<article class="loading" data-src="README.md"></article>
<script src="https://unpkg.com/paperdocs@1/paperdocs.min.js"></script>
<script src="https://unpkg.com/superdom@1"></script>
<script>
dom.id.year = (new Date()).getFullYear();
</script>
</body>
</html>
================================================
FILE: package.json
================================================
{
"name": "superdom",
"version": "1.3.0",
"description": "Better and simpler DOM manipulation",
"main": "superdom.min.js",
"dependencies": {},
"devDependencies": {
"grunt": "^1.0.1",
"grunt-banner": "^0.6.0",
"grunt-bytesize": "^0.2.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-run": "^0.6.0",
"grunt-semistandard": "^1.0.6",
"jest": "^17.0.3"
},
"scripts": {
"test": "jest"
},
"repository": {
"type": "git",
"url": "git+https://github.com/franciscop/superdom.js.git"
},
"keywords": [
"dom",
"manipulation",
"edit",
"add",
"remove",
"generate"
],
"author": "Francisco Presencia Fandos <public@francisco.io>",
"license": "MIT",
"bugs": {
"url": "https://github.com/franciscop/superdom.js/issues"
},
"homepage": "https://github.com/franciscop/superdom.js#readme"
}
================================================
FILE: src/plugins/attributes.js
================================================
// An attribute handles derivatives from Nodes so it normally has its
// corresponding dom.api.nodes. Example:
// dom.api.nodes.class => dom.a.class;
// dom.api.attributes.class => dom.a.class.test = true;
// If it has no dom.api.nodes, then the default getAttribute() will be used
dom.api.attributes = {};
dom.api.attributes.class = {
// dom.a.class.bla = false; dom.a.class.bla = ['a', 'b'] => true;
set: (cb, key, node, i, all) => {
let val = cb(node.classList.contains(key), i, all);
node.classList[val ? 'add' : 'remove'](key);
},
del: (key, node) => {
node.classList.remove(key);
}
};
// dom.a.on.click = function(){}
dom.api.attributes.on = (cb, key, node) => {
dom.api.helpers.args(key).forEach(event => node.addEventListener(event, cb));
};
// dom.a.handle.click = function(){}
dom.api.attributes.handle = (cb, key, node) => {
dom.api.helpers.args(key).forEach(event => node.addEventListener(event, (...args) => {
args[0].preventDefault();
cb(...args);
}));
};
dom.api.attributes.trigger = {
get: (prevVal, key, node) => {
// Accept different types of event names
dom.api.helpers.args(key).forEach(event => {
let ev = new CustomEvent(event, { bubbles: true, cancelable: true });
node.dispatchEvent(ev);
});
}
};
================================================
FILE: src/plugins/attributes.test.js
================================================
// Test the attributes:
// let links = dom.a.href; dom.a.href = '...'; delete dom.a.href;
const dom = require('../../superdom');
let initial = '<a>Link</a>';
let link = 'https://example.com/'
beforeEach(() => {
document.body.innerHTML = '<a target="_blank">Link</a><p>Lorem <strong>ipsum</strong></p>';
});
afterEach(() => {
document.body.innerHTML = '';
});
it('Can set an attribute of one element', () => {
dom.a.href = link;
expect(dom.a[0].getAttribute('href')).toBe(link);
});
it('handles non-existing elements just fine', () => {
expect(dom.a.bla[0]).toBe('');
});
it('Can set an attribute of all matched', () => {
document.body.innerHTML = '<a>Link1</a><a>Link2</a>';
dom.a.href = link;
expect(dom.a[0].getAttribute('href')).toBe(link);
expect(dom.a[1].getAttribute('href')).toBe(link);
});
it('Can set the attribute dynamically', () => {
document.body.innerHTML = '<a>Link1</a><a>Link2</a>';
dom.a.href = link;
dom.a.href = (prev, i) => prev + i;
expect(dom.a[0].getAttribute('href')).toBe(link + '0');
expect(dom.a[1].getAttribute('href')).toBe(link + '1');
});
it('receives the correct parameters', () => {
dom.a.target = (prev, i, all) => {
expect(prev).toBe('_blank');
expect(i).toBe(0);
expect(all).toHaveLength(1);
return 'invalid';
}
dom.a.target = prev => expect(prev).toBe('invalid');
});
it('Only sets the attribute where it should', () => {
document.body.innerHTML = '<a class="cta">Link1</a><a>Link2</a>';
dom.class.cta.target = '_blank';
expect(dom.class.cta[0].getAttribute('target')).toBe('_blank');
expect(dom['a:not(.cta)'][0].getAttribute('target')).not.toBe('_blank');
});
// Delete
it('Does not actually remove the attribute, just make it empty', () => {
dom.a.target = '';
expect(dom.a[0].getAttribute('target')).toBe('');
});
it('Delete an attribute', () => {
delete dom.a.target;
expect(dom.a.target[0]).toBe('');
expect(dom.a[0].getAttribute('target')).toBe(null);
});
// EVENTS
it('can handle events', done => {
dom.a.on.click = e => {
expect(e.target.nodeName).toBe('A');
done();
}
dom.a.trigger.click;
});
it('can handle multiple events', done => {
document.body.innerHTML = '<a href="">Hello</a>';
let counter = 0;
dom.a.on['click, hover'] = e => {
counter++;
expect(e.target.nodeName).toBe('A');
if (counter === 2) {
done();
} else {
dom.a.trigger.hover;
}
}
dom.a.trigger.click;
});
================================================
FILE: src/plugins/helpers.js
================================================
dom.api.helpers = {};
dom.api.helpers.args = val => val.split(/[\s,]+/);
================================================
FILE: src/plugins/navigate.js
================================================
// Change the current node selection
dom.api.navigate = {};
dom.api.navigate.parent = node => node.parentNode || false;
dom.api.navigate.children = node => node.children;
================================================
FILE: src/plugins/navigate.test.js
================================================
// DOM navigation. Parents, children, etc
const dom = require('../../superdom');
beforeEach(() => {
document.body.innerHTML = '<ul class="list"><li><a></a></li></ul>';
});
afterEach(() => {
document.body.innerHTML = '';
});
describe('DOM navigation', () => {
it('can retrieve the parent', () => {
expect(dom.li.parent[0].nodeName).toBe('UL');
});
it('can retrieve the parent parent', () => {
expect(dom.a.parent.parent[0].nodeName).toBe('UL');
});
it('keeps the special attrs with get', () => {
expect(dom.a.parent.parent.class._text).toBe('list');
});
it('keeps the special attrs with set', () => {
dom.a.parent.parent.class = 'good';
expect(dom.ul.class._text).toBe('list good');
});
it('keeps the special attrs with delete', () => {
dom.a.parent.parent.class = 'good';
delete dom.a.parent.parent.class.list;
expect(dom.ul.class._text).toBe('good');
});
});
================================================
FILE: src/plugins/nodes.js
================================================
// dom.a.each = a => {}
dom.api.nodes = {};
dom.api.nodes.each = (cb, node, i, all) => cb(node, i, all);
dom.api.nodes.html = {
get: node => node.innerHTML,
set: (cb, node, i, all) => node.innerHTML = cb(node.innerHTML, i, all) || '',
del: node => node.innerHTML = ''
};
dom.api.nodes.text = {
get: node => node.textContent,
set: (cb, node, i, all) => node.textContent = cb(node.textContent, i, all) || '',
del: node => node.textContent = ''
};
// dom.a.class; dom.a.class = classes => newClasses; delete dom.a.class
dom.api.nodes.class = {
get: node => Array.from(node.classList),
set: (cb, node, i, all) => {
let val = cb(Array.from(node.classList), i, all);
val = typeof val === 'string' ? dom.api.helpers.args(val) : val;
val.forEach(one => node.classList.add(one));
}
};
================================================
FILE: src/plugins/nodes.test.js
================================================
const dom = require('../../superdom');
const initial = '<p>Hello <strong>world</strong></p>';
beforeEach(() => {
document.body.innerHTML = initial;
});
afterEach(() => {
document.body.innerHTML = '';
});
it('can read html', () => {
expect(dom.body.html[0]).toBe(initial);
});
it('can write html', () => {
dom.body.html = 'Hello world';
expect(document.body.innerHTML).toBe('Hello world');
});
it('can append to html by setting a cb', () => {
dom.body.html = html => html + 'abc';
expect(document.body.innerHTML).toBe(initial + 'abc');
});
it('passes correct arguments to the setter', () => {
dom.body.html = (html, i, all) => {
expect(html).toBe(initial);
expect(i).toBe(0);
expect(all).toHaveLength(1);
expect(all[0]).toBe(document.body);
}
});
it('can delete it', () => {
delete dom.body.html;
expect(document.body.innerHTML).toBe('');
});
================================================
FILE: src/plugins/selectors.js
================================================
// Selector-level extensible
// Choose which method to use
dom.api.selectors = (sel = []) => typeof sel === 'string'
? /^\s*</.test(sel)
? dom.api.selectors.generate(sel)
: [...(document.querySelectorAll(sel))]
: sel instanceof Element
? [sel]
// Allows for array of non-elements to be turned into a simple array
: [...sel].map(el => dom(el)).reduce((all, one) => all.concat(one), []);
// The one to generate a chunk of html
dom.api.selectors.generate = html => {
let type = /^\s*<t(h|r|d)/.test(html) ? 'table' : 'div';
let cont = document.createElement(type);
cont.innerHTML = html.replace(/^\s*/, '').replace(/\s*$/, '');
return [...cont.childNodes];
};
// Some custom selectors:
dom.api.selectors.id = name => dom[`#${name}`];
dom.api.selectors.class = name => dom[`.${name}`];
dom.api.selectors.attr = name => dom[`[${name}]`];
================================================
FILE: src/plugins/selectors.test.js
================================================
const dom = require('../../superdom');
document.body.innerHTML = `
<div id="demo" class="demo">
<ul><li><a href="" target="_blank">Item</a></li></ul>
</div>
`;
it("can select by class", function() {
expect(dom.class.demo.length).toBe(1);
});
it("can select by tag", function() {
expect(dom.body.length).toBe(1);
});
it("can select by id", function() {
expect(dom.id.demo.length).toBe(1);
});
it("selects by id with function", function() {
expect(dom.id('demo').length).toBe(1);
expect(dom.id('demo').id.demo).toBe(true); // Doesn't screw the chain
});
it("can select by id", function() {
expect(dom.attr.target.length).toBe(1);
});
it("can select by attribute", function() {
expect(dom.attr['target="_blank"'].length).toBe(1);
});
it("wraps elements", function() {
let el = dom.id.demo[0];
expect(dom(el).length).toBe(1);
expect(dom(el, el).length).toBe(2);
});
it("wraps an array of els", function() {
let el = dom.id.demo[0];
expect(dom([el]).length).toBe(1);
expect(dom([el, el]).length).toBe(2);
expect(dom([el, [el, [el]]]).length).toBe(3);
});
it("can select with css", function() {
expect(dom['[id="demo"]'].length).toBe(1);
expect(dom['.demo ul'].length).toBe(1);
});
it("can select as a function", function() {
expect(dom('#demo').length).toBe(1);
expect(dom('.demo').length).toBe(1);
expect(dom('.demo ul').length).toBe(1);
});
it("can generate some html", function() {
expect(dom('<div class="demo">Hello</div>')[0].nodeName).toBe('DIV');
expect(dom`<div class="demo">Hello</div>`[0].nodeName).toBe('DIV');
expect(dom('<div class="demo">Hello</div>').class._text).toBe('demo');
expect(dom`<div class="demo">Hello</div>`.class._text).toBe('demo');
});
================================================
FILE: src/superdom.js
================================================
let dom = (function nodeSelector () {
// Convert a function into a property selector
// It converts "(a, b) => [a, b]" but keeps "a => a"
let DOM = (...sel) => DOM.api.array(DOM.api.selectors(sel.length <= 1 ? sel[0] : sel));
// The second-level SELECTOR
// dom.class(X); dom.class.X; dom.class.X = 5; delete.dom.class.X
// This is NOT matched though: dom.a.X
const derivated = (selector, orig) => {
// Allow for a function to be used as getter
return new Proxy(sel => DOM(selector(sel)), {
get: (orig, name) => {
return selector(name);
}, set: (orig, name, value) => {
DOM[selector(name)] = value;
return true;
}, deleteProperty: (orig, name) => {
delete DOM[selector(name)];
return true;
}
});
};
// First level SELECTOR
// dom.button || dom.class
let getter = (orig, key) => {
if (key in orig) return orig[key];
// Allow extending the API
if (key in orig.api.selectors) {
return derivated(orig.api.selectors[key], orig);
}
return orig.api.array(orig.api.selectors(key));
};
let setter = (orig, key, value) => {
let cb = DOM.api.fn(value, true);
DOM[key].each(node => node.parentNode.replaceChild(cb(node)[0], node));
};
let deletter = (base, key) => {
DOM[key].forEach(n => n.remove());
return true;
};
DOM.api = {};
// CANNOT SIMPLIFY TO "return new Proxy()" => ERROR WTF?
DOM = new Proxy(DOM, {
get: getter,
set: setter,
deleteProperty: deletter
});
return DOM;
})();
// Second level selector
// let a = dom.a.href; dom.a.href = '...'; delete dom.a.href;
dom = (function Attributes (DOM) {
// Obtain a callback from the attribute passed, whatever the type
DOM.api.fn = (value, parse = false) => {
let cb = node => parse ? DOM(value) : value;
if (value instanceof Function) cb = value;
return cb;
};
// Returns something that is not a list of nodes, so keep them in reference
DOM.api.proxify = (proxify, nodes, key) => {
proxify._ = { ref: nodes, attr: key };
return DOM.api.values(proxify);
};
DOM.api.array = nodes => {
let getter = (orig, key) => {
// Array original function: dom.a.map()
if (key in orig) {
return orig[key];
}
// Nodes API function: dom.a.class, dom.a.on
if (key in DOM.api.nodes) {
let nodeCb = DOM.api.nodes[key];
if (nodeCb.get) nodeCb = nodeCb.get;
let newNodes = nodes.map((nodes, i, all) => nodeCb(nodes, i, all));
return DOM.api.proxify(newNodes, nodes, key);
}
// Navigation API function: dom.a.parent
if (key in DOM.api.navigate) {
let cb = DOM.api.navigate[key];
// Make it into a simple array if an array of arrays was returned
let newNodes = nodes.map(cb).reduce((all, one) => {
return all.concat(one);
}, []).filter(n => n);
return DOM.api.array(newNodes);
}
// Defaults to the attribute: dom.a.href
let newNodes = nodes.map(node => node.getAttribute(key) || '');
return DOM.api.proxify(newNodes, nodes, key);
};
// Setting the array; convert to fn and then proceed
let setter = (orig, key, value) => {
let cb = DOM.api.fn(value);
let nodeCb = DOM.api.nodes[key];
if (nodeCb) {
if (nodeCb.set) nodeCb = nodeCb.set;
orig.map((node, i, all) => nodeCb(cb, node, i, all));
return true;
}
if (value instanceof Function) {
cb = (node, i, orig) => value(node.getAttribute(key) || '', i, orig);
} else {
cb = node => value;
}
orig.forEach((node, i, orig) => node.setAttribute(key, cb(node, i, orig) || ''));
};
let deletter = (orig, key) => {
let cb = el => el.removeAttribute(key);
if (DOM.api.nodes[key] && DOM.api.nodes[key].del) {
cb = DOM.api.nodes[key].del;
}
orig.forEach(cb);
return true;
};
return new Proxy(nodes, {
get: getter,
set: setter,
deleteProperty: deletter
});
};
return DOM;
})(dom);
// Derivated attribute (when it was Nodes and not Navigate)
// let a = dom.a.class.demo; dom.a.class.demo = true; delete dom.a.class.demo
dom = (function Values (DOM) {
let specialAttrs = {
_flat: lists => [...new Set([].concat.apply([], lists))],
_text: lists => [...new Set([].concat.apply([], lists))].join(' ')
};
DOM.api.values = attributes => {
// dom.a.href._blank; dom.a.class.bla
let getter = (orig, key) => {
if (key in orig || typeof orig[key] !== 'undefined') {
return orig[key];
}
let nodes = orig._.ref;
let cb = DOM.api.attributes[orig._.attr];
if (cb && cb.get) {
cb = cb.get;
orig.map((attr, i, all) => cb(attr, key, nodes[i], i, all));
}
if (key in specialAttrs) {
return specialAttrs[key](orig);
}
// TODO: personalized attr (such as in parent)
return specialAttrs._flat(orig).includes(key);
};
// dom.a.class.bla = false; dom.a.href._blank = false;
let setter = (orig, key, value) => {
let nodes = orig._.ref;
let attrCb = DOM.api.attributes[orig._.attr];
if (attrCb) {
if (attrCb.set) attrCb = attrCb.set;
orig.map((attr, i, all) => attrCb(DOM.api.fn(value), key, nodes[i], i, all));
}
return true;
};
let deletter = (orig, key) => {
let nodes = orig._.ref;
let attrCb = DOM.api.attributes[orig._.attr].del;
if (attrCb) {
orig.map((attr, i, all) => attrCb(key, nodes[i], i, all));
}
return true;
};
return new Proxy(attributes, {
get: getter,
set: setter,
deleteProperty: deletter
});
};
return DOM;
})(dom);
if (typeof module !== 'undefined') {
module.exports = dom;
}
================================================
FILE: src/superdom.test.js
================================================
const dom = require('../superdom');
// Testing the main file
it("should be defined", () => {
expect(!!dom).toBe(true);
});
it("should be a function", () => {
expect(typeof dom).toBe("function");
});
it("can accept no argument", () => {
expect(dom() instanceof Array).toBe(true);
expect(dom()).toHaveLength(0);
});
================================================
FILE: superdom.js
================================================
let dom = (function nodeSelector () {
// Convert a function into a property selector
// It converts "(a, b) => [a, b]" but keeps "a => a"
let DOM = (...sel) => DOM.api.array(DOM.api.selectors(sel.length <= 1 ? sel[0] : sel));
// The second-level SELECTOR
// dom.class(X); dom.class.X; dom.class.X = 5; delete.dom.class.X
// This is NOT matched though: dom.a.X
const derivated = (selector, orig) => {
// Allow for a function to be used as getter
return new Proxy(sel => DOM(selector(sel)), {
get: (orig, name) => {
return selector(name);
}, set: (orig, name, value) => {
DOM[selector(name)] = value;
return true;
}, deleteProperty: (orig, name) => {
delete DOM[selector(name)];
return true;
}
});
};
// First level SELECTOR
// dom.button || dom.class
let getter = (orig, key) => {
if (key in orig) return orig[key];
// Allow extending the API
if (key in orig.api.selectors) {
return derivated(orig.api.selectors[key], orig);
}
return orig.api.array(orig.api.selectors(key));
};
let setter = (orig, key, value) => {
let cb = DOM.api.fn(value, true);
DOM[key].each(node => node.parentNode.replaceChild(cb(node)[0], node));
};
let deletter = (base, key) => {
DOM[key].forEach(n => n.remove());
return true;
};
DOM.api = {};
// CANNOT SIMPLIFY TO "return new Proxy()" => ERROR WTF?
DOM = new Proxy(DOM, {
get: getter,
set: setter,
deleteProperty: deletter
});
return DOM;
})();
// Second level selector
// let a = dom.a.href; dom.a.href = '...'; delete dom.a.href;
dom = (function Attributes (DOM) {
// Obtain a callback from the attribute passed, whatever the type
DOM.api.fn = (value, parse = false) => {
let cb = node => parse ? DOM(value) : value;
if (value instanceof Function) cb = value;
return cb;
};
// Returns something that is not a list of nodes, so keep them in reference
DOM.api.proxify = (proxify, nodes, key) => {
proxify._ = { ref: nodes, attr: key };
return DOM.api.values(proxify);
};
DOM.api.array = nodes => {
let getter = (orig, key) => {
// Array original function: dom.a.map()
if (key in orig) {
return orig[key];
}
// Nodes API function: dom.a.class, dom.a.on
if (key in DOM.api.nodes) {
let nodeCb = DOM.api.nodes[key];
if (nodeCb.get) nodeCb = nodeCb.get;
let newNodes = nodes.map((nodes, i, all) => nodeCb(nodes, i, all));
return DOM.api.proxify(newNodes, nodes, key);
}
// Navigation API function: dom.a.parent
if (key in DOM.api.navigate) {
let cb = DOM.api.navigate[key];
// Make it into a simple array if an array of arrays was returned
let newNodes = nodes.map(cb).reduce((all, one) => {
return all.concat(one);
}, []).filter(n => n);
return DOM.api.array(newNodes);
}
// Defaults to the attribute: dom.a.href
let newNodes = nodes.map(node => node.getAttribute(key) || '');
return DOM.api.proxify(newNodes, nodes, key);
};
// Setting the array; convert to fn and then proceed
let setter = (orig, key, value) => {
let cb = DOM.api.fn(value);
let nodeCb = DOM.api.nodes[key];
if (nodeCb) {
if (nodeCb.set) nodeCb = nodeCb.set;
orig.map((node, i, all) => nodeCb(cb, node, i, all));
return true;
}
if (value instanceof Function) {
cb = (node, i, orig) => value(node.getAttribute(key) || '', i, orig);
} else {
cb = node => value;
}
orig.forEach((node, i, orig) => node.setAttribute(key, cb(node, i, orig) || ''));
};
let deletter = (orig, key) => {
let cb = el => el.removeAttribute(key);
if (DOM.api.nodes[key] && DOM.api.nodes[key].del) {
cb = DOM.api.nodes[key].del;
}
orig.forEach(cb);
return true;
};
return new Proxy(nodes, {
get: getter,
set: setter,
deleteProperty: deletter
});
};
return DOM;
})(dom);
// Derivated attribute (when it was Nodes and not Navigate)
// let a = dom.a.class.demo; dom.a.class.demo = true; delete dom.a.class.demo
dom = (function Values (DOM) {
let specialAttrs = {
_flat: lists => [...new Set([].concat.apply([], lists))],
_text: lists => [...new Set([].concat.apply([], lists))].join(' ')
};
DOM.api.values = attributes => {
// dom.a.href._blank; dom.a.class.bla
let getter = (orig, key) => {
if (key in orig || typeof orig[key] !== 'undefined') {
return orig[key];
}
let nodes = orig._.ref;
let cb = DOM.api.attributes[orig._.attr];
if (cb && cb.get) {
cb = cb.get;
orig.map((attr, i, all) => cb(attr, key, nodes[i], i, all));
}
if (key in specialAttrs) {
return specialAttrs[key](orig);
}
// TODO: personalized attr (such as in parent)
return specialAttrs._flat(orig).includes(key);
};
// dom.a.class.bla = false; dom.a.href._blank = false;
let setter = (orig, key, value) => {
let nodes = orig._.ref;
let attrCb = DOM.api.attributes[orig._.attr];
if (attrCb) {
if (attrCb.set) attrCb = attrCb.set;
orig.map((attr, i, all) => attrCb(DOM.api.fn(value), key, nodes[i], i, all));
}
return true;
};
let deletter = (orig, key) => {
let nodes = orig._.ref;
let attrCb = DOM.api.attributes[orig._.attr].del;
if (attrCb) {
orig.map((attr, i, all) => attrCb(key, nodes[i], i, all));
}
return true;
};
return new Proxy(attributes, {
get: getter,
set: setter,
deleteProperty: deletter
});
};
return DOM;
})(dom);
if (typeof module !== 'undefined') {
module.exports = dom;
}
// An attribute handles derivatives from Nodes so it normally has its
// corresponding dom.api.nodes. Example:
// dom.api.nodes.class => dom.a.class;
// dom.api.attributes.class => dom.a.class.test = true;
// If it has no dom.api.nodes, then the default getAttribute() will be used
dom.api.attributes = {};
dom.api.attributes.class = {
// dom.a.class.bla = false; dom.a.class.bla = ['a', 'b'] => true;
set: (cb, key, node, i, all) => {
let val = cb(node.classList.contains(key), i, all);
node.classList[val ? 'add' : 'remove'](key);
},
del: (key, node) => {
node.classList.remove(key);
}
};
// dom.a.on.click = function(){}
dom.api.attributes.on = (cb, key, node) => {
dom.api.helpers.args(key).forEach(event => node.addEventListener(event, cb));
};
// dom.a.handle.click = function(){}
dom.api.attributes.handle = (cb, key, node) => {
dom.api.helpers.args(key).forEach(event => node.addEventListener(event, (...args) => {
args[0].preventDefault();
cb(...args);
}));
};
dom.api.attributes.trigger = {
get: (prevVal, key, node) => {
// Accept different types of event names
dom.api.helpers.args(key).forEach(event => {
let ev = new CustomEvent(event, { bubbles: true, cancelable: true });
node.dispatchEvent(ev);
});
}
};
dom.api.helpers = {};
dom.api.helpers.args = val => val.split(/[\s,]+/);
// Change the current node selection
dom.api.navigate = {};
dom.api.navigate.parent = node => node.parentNode || false;
dom.api.navigate.children = node => node.children;
// dom.a.each = a => {}
dom.api.nodes = {};
dom.api.nodes.each = (cb, node, i, all) => cb(node, i, all);
dom.api.nodes.html = {
get: node => node.innerHTML,
set: (cb, node, i, all) => node.innerHTML = cb(node.innerHTML, i, all) || '',
del: node => node.innerHTML = ''
};
dom.api.nodes.text = {
get: node => node.textContent,
set: (cb, node, i, all) => node.textContent = cb(node.textContent, i, all) || '',
del: node => node.textContent = ''
};
// dom.a.class; dom.a.class = classes => newClasses; delete dom.a.class
dom.api.nodes.class = {
get: node => Array.from(node.classList),
set: (cb, node, i, all) => {
let val = cb(Array.from(node.classList), i, all);
val = typeof val === 'string' ? dom.api.helpers.args(val) : val;
val.forEach(one => node.classList.add(one));
}
};
// Selector-level extensible
// Choose which method to use
dom.api.selectors = (sel = []) => typeof sel === 'string'
? /^\s*</.test(sel)
? dom.api.selectors.generate(sel)
: [...(document.querySelectorAll(sel))]
: sel instanceof Element
? [sel]
// Allows for array of non-elements to be turned into a simple array
: [...sel].map(el => dom(el)).reduce((all, one) => all.concat(one), []);
// The one to generate a chunk of html
dom.api.selectors.generate = html => {
let type = /^\s*<t(h|r|d)/.test(html) ? 'table' : 'div';
let cont = document.createElement(type);
cont.innerHTML = html.replace(/^\s*/, '').replace(/\s*$/, '');
return [...cont.childNodes];
};
// Some custom selectors:
dom.api.selectors.id = name => dom[`#${name}`];
dom.api.selectors.class = name => dom[`.${name}`];
dom.api.selectors.attr = name => dom[`[${name}]`];
================================================
FILE: web/prism.css
================================================
/* Prism */
/* http://prismjs.com/download.html?themes=prism&languages=markup+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
pre[class*="language-"] {
margin-top: 0;
padding: .5em .8em;
font-size: 1.2em;
line-height: 1.3;
}
================================================
FILE: web/prism.js
================================================
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=t.util.clone(e[r]));return a;case"Array":return e.map&&e.map(function(e){return t.util.clone(e)})}return e}},languages:{extend:function(e,n){var a=t.util.clone(t.languages[e]);for(var r in n)a[r]=n[r];return a},insertBefore:function(e,n,a,r){r=r||t.languages;var l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==n)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return t.languages.DFS(t.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},DFS:function(e,n,a,r){r=r||{};for(var l in e)e.hasOwnProperty(l)&&(n.call(e,l,e[l],a||l),"Object"!==t.util.type(e[l])||r[e[l]]?"Array"!==t.util.type(e[l])||r[e[l]]||(r[e[l]]=!0,t.languages.DFS(e[l],n,l,r)):(r[e[l]]=!0,t.languages.DFS(e[l],n,null,r)))}},plugins:{},highlightAll:function(e,n){for(var a,r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'),l=0;a=r[l++];)t.highlightElement(a,e===!0,n)},highlightElement:function(n,a,r){for(var l,i,o=n;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=t.languages[l]),n.className=n.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=n.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=n.textContent,u={element:n,language:l,grammar:i,code:s};if(!s||!i)return t.hooks.run("complete",u),void 0;if(t.hooks.run("before-highlight",u),a&&_self.Worker){var g=new Worker(t.filename);g.onmessage=function(e){u.highlightedCode=e.data,t.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),t.hooks.run("after-highlight",u),t.hooks.run("complete",u)},g.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=t.highlight(u.code,u.grammar,u.language),t.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(n),t.hooks.run("after-highlight",u),t.hooks.run("complete",u)},highlight:function(e,a,r){var l=t.tokenize(e,a);return n.stringify(t.util.encode(l),r)},tokenize:function(e,n){var a=t.Token,r=[e],l=n.rest;if(l){for(var i in l)n[i]=l[i];delete n.rest}e:for(var i in n)if(n.hasOwnProperty(i)&&n[i]){var o=n[i];o="Array"===t.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],g=u.inside,c=!!u.lookbehind,f=0,h=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var d=r[p];if(r.length>e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?[\w\W]+?\?>/,doctype:/<!DOCTYPE[\w\W]+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<style[\w\W]*?>)[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<script[\w\W]*?>)[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript;
================================================
FILE: web/style.css
================================================
article {
margin: 80vh auto 20px;
width: calc(100% - 40px);
max-width: 900px
}
article, pre {
box-shadow: .1em .1em .1em rgba(17, 17, 17, .2), 0 0 .1em rgba(17, 17, 17, .2);
}
code[class*="language-"], pre[class*="language-"] {
text-shadow: none;
}
article h1 {
line-height: 1;
padding-bottom: 20px;
}
article h2 {
margin: -40px 0 0;
}
body {
margin: 0 auto;
width: 100%;
height: 100%;
background-color: #e4fcfe;
background-image: url('/web/buildings.jpg');
background-size: cover;
background-position: center center;
}
@media all and (max-width: 900px) {
.no-scroll {
overflow-y: auto;
}
}
@media all and (max-width: 700px) {
body {
background-image: none;
}
}
main {
position: fixed;
width: calc(100% - 40px);
max-width: 800px;
margin: 0 auto;
/*color: #fff;*/
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
h1 {
padding: 0;
}
main .flex {
align-items: center;
}
main p {
margin: 3px 0 5px;
}
main a {
display: inline-block;
color: #333;
border: 2px solid transparent;
padding: 3px 8px;
border-bottom: 2px solid #333;
margin-right: 10px;
}
main a:hover,
main a:focus {
outline: none;
border: 2px solid #333;
}
main a:active {
color: #333;
}
gitextract_8hbc1cxk/
├── .gitignore
├── CNAME
├── Contributing.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── __tests__/
│ ├── class.test.js
│ ├── other.test.js
│ └── plugin.test.js
├── bower.json
├── index.html
├── package.json
├── src/
│ ├── plugins/
│ │ ├── attributes.js
│ │ ├── attributes.test.js
│ │ ├── helpers.js
│ │ ├── navigate.js
│ │ ├── navigate.test.js
│ │ ├── nodes.js
│ │ ├── nodes.test.js
│ │ ├── selectors.js
│ │ └── selectors.test.js
│ ├── superdom.js
│ └── superdom.test.js
├── superdom.js
└── web/
├── buildings.xcf
├── prism.css
├── prism.js
└── style.css
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (61K chars).
[
{
"path": ".gitignore",
"chars": 42,
"preview": ".git\nnode_modules\nnpm-debug.log\n.DS_Store\n"
},
{
"path": "CNAME",
"chars": 14,
"preview": "superdom.site\n"
},
{
"path": "Contributing.md",
"chars": 228,
"preview": "# Contributing\n\n## API\n\nThere's a powerful API that is used internally. It might get documented and released in a future"
},
{
"path": "Gruntfile.js",
"chars": 2762,
"preview": "// This builds the library itself\nmodule.exports = function (grunt) {\n // Configuration\n grunt.initConfig({\n concat"
},
{
"path": "LICENSE",
"chars": 1086,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Francisco Presencia\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 5598,
"preview": "# Superdom\n\nYou have `dom`. It has all the DOM virtually within it. Use that power:\n\n```js\n// Fetch all the page links\nl"
},
{
"path": "__tests__/class.test.js",
"chars": 2688,
"preview": "const dom = require('../superdom');\n\nbeforeEach(() => {\n document.body.className = 'bla blu';\n});\n\nafterEach(() => {\n "
},
{
"path": "__tests__/other.test.js",
"chars": 3093,
"preview": "// Mutted so far until future migration\nit('works', () => {});\n\n\n\n// describe(\"dom[selector][attribute]\", function() {\n/"
},
{
"path": "__tests__/plugin.test.js",
"chars": 487,
"preview": "// Create a new plugin\nlet dom = require('../superdom');\n\ndom.api.nodes.beetlejuice = {\n get: node => 'beetlejuice',\n "
},
{
"path": "bower.json",
"chars": 498,
"preview": "{\n \"name\": \"superdom\",\n \"version\": \"0.9.0\",\n \"description\": \"Better and simpler DOM manipulation\",\n \"main\": \"superdo"
},
{
"path": "index.html",
"chars": 2611,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset='utf-8'>\n <title>Superdom</title>\n <meta name='viewport', conten"
},
{
"path": "package.json",
"chars": 978,
"preview": "{\n \"name\": \"superdom\",\n \"version\": \"1.3.0\",\n \"description\": \"Better and simpler DOM manipulation\",\n \"main\": \"superdo"
},
{
"path": "src/plugins/attributes.js",
"chars": 1293,
"preview": "// An attribute handles derivatives from Nodes so it normally has its\n// corresponding dom.api.nodes. Example:\n// dom."
},
{
"path": "src/plugins/attributes.test.js",
"chars": 2466,
"preview": "// Test the attributes:\n// let links = dom.a.href; dom.a.href = '...'; delete dom.a.href;\nconst dom = require('../../sup"
},
{
"path": "src/plugins/helpers.js",
"chars": 73,
"preview": "dom.api.helpers = {};\ndom.api.helpers.args = val => val.split(/[\\s,]+/);\n"
},
{
"path": "src/plugins/navigate.js",
"chars": 171,
"preview": "// Change the current node selection\ndom.api.navigate = {};\ndom.api.navigate.parent = node => node.parentNode || false;\n"
},
{
"path": "src/plugins/navigate.test.js",
"chars": 923,
"preview": "// DOM navigation. Parents, children, etc\nconst dom = require('../../superdom');\n\nbeforeEach(() => {\n document.body.inn"
},
{
"path": "src/plugins/nodes.js",
"chars": 809,
"preview": "// dom.a.each = a => {}\ndom.api.nodes = {};\ndom.api.nodes.each = (cb, node, i, all) => cb(node, i, all);\n\ndom.api.nodes."
},
{
"path": "src/plugins/nodes.test.js",
"chars": 888,
"preview": "const dom = require('../../superdom');\n\nconst initial = '<p>Hello <strong>world</strong></p>';\n\nbeforeEach(() => {\n doc"
},
{
"path": "src/plugins/selectors.js",
"chars": 868,
"preview": "// Selector-level extensible\n\n// Choose which method to use\ndom.api.selectors = (sel = []) => typeof sel === 'string'\n "
},
{
"path": "src/plugins/selectors.test.js",
"chars": 1729,
"preview": "const dom = require('../../superdom');\n\ndocument.body.innerHTML = `\n <div id=\"demo\" class=\"demo\">\n <ul><li><a href=\""
},
{
"path": "src/superdom.js",
"chars": 5859,
"preview": "let dom = (function nodeSelector () {\n // Convert a function into a property selector\n // It converts \"(a, b) => [a, b"
},
{
"path": "src/superdom.test.js",
"chars": 325,
"preview": "const dom = require('../superdom');\n\n// Testing the main file\nit(\"should be defined\", () => {\n expect(!!dom).toBe(true)"
},
{
"path": "superdom.js",
"chars": 9082,
"preview": "let dom = (function nodeSelector () {\n // Convert a function into a property selector\n // It converts \"(a, b) => [a, b"
},
{
"path": "web/prism.css",
"chars": 2394,
"preview": "\n/* Prism */\n/* http://prismjs.com/download.html?themes=prism&languages=markup+clike+javascript */\n/**\n * prism.js defau"
},
{
"path": "web/prism.js",
"chars": 8589,
"preview": "var _self=\"undefined\"!=typeof window?window:\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?sel"
},
{
"path": "web/style.css",
"chars": 1254,
"preview": "article {\n margin: 80vh auto 20px;\n width: calc(100% - 40px);\n max-width: 900px\n}\n\narticle, pre {\n box-shadow: .1em "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the franciscop/superdom.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (55.5 KB), approximately 16.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.