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
```
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`Google`;
// It is the same as
let link = dom('Google');
```
## 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 = `
// Fetch all of the link's urlslet links = dom.a.href;// Set the targets to a new page
dom.a.target ='_blank';// Manipulate the buttons without loops
dom.class.cta.html ='Click me!';
================================================
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 ",
"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 = 'Link';
let link = 'https://example.com/'
beforeEach(() => {
document.body.innerHTML = 'Link
Lorem ipsum
';
});
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 = 'Link1Link2';
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 = 'Link1Link2';
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 = 'Link1Link2';
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 = 'Hello';
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 = '
';
});
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 = '
Hello world
';
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* dom(el)).reduce((all, one) => all.concat(one), []);
// The one to generate a chunk of html
dom.api.selectors.generate = html => {
let type = /^\s* 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 = `