687 lines
23 KiB
JavaScript
687 lines
23 KiB
JavaScript
"use strict";
|
|
|
|
function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; }
|
|
|
|
(function e(t, n, r) {
|
|
function s(o, u) {
|
|
if (!n[o]) {
|
|
if (!t[o]) {
|
|
var a = typeof require == "function" && require;if (!u && a) return a(o, !0);if (i) return i(o, !0);var f = new Error("Cannot find module '" + o + "'");throw f.code = "MODULE_NOT_FOUND", f;
|
|
}var l = n[o] = { exports: {} };t[o][0].call(l.exports, function (e) {
|
|
var n = t[o][1][e];return s(n ? n : e);
|
|
}, l, l.exports, e, t, n, r);
|
|
}return n[o].exports;
|
|
}var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) {
|
|
s(r[o]);
|
|
}return s;
|
|
})({ 1: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var wrapNode = require('./lib/node');
|
|
|
|
/**
|
|
* Import given node into `the-dom`
|
|
*
|
|
* @param {Node} node Node to import
|
|
* @return {Object} Wrapped node
|
|
*/
|
|
exports.import = function (node) {
|
|
return wrapNode(node);
|
|
};
|
|
|
|
/**
|
|
* Import an HTML document into `the-dom`
|
|
*
|
|
* @param {HTMLDocument} doc Document to import
|
|
* @return {Object} A hash with doctype, body, head, html props
|
|
*/
|
|
exports.html = function (doc) {
|
|
return {
|
|
create: function create(name) {
|
|
return doc.createElement(name);
|
|
},
|
|
body: wrapNode(doc.body),
|
|
head: wrapNode(doc.head),
|
|
html: wrapNode(doc.documentElement),
|
|
doctype: wrapNode(doc.doctype)
|
|
};
|
|
};
|
|
}, { "./lib/node": 3 }], 2: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var utils = require('./utils');
|
|
|
|
var split = utils.split;
|
|
var iterateArray = utils.iterateArray;
|
|
|
|
/**
|
|
* Create an object to manipulate given node's CSS classes
|
|
*
|
|
* @param {HTMLElement} node Input element
|
|
* @see `Set` documentation for behaviour information
|
|
* @return {Object} Set-like object
|
|
*/
|
|
var wrapClass = function wrapClass(node) {
|
|
var res = {
|
|
add: function add(el) {
|
|
if (!this.has(el)) {
|
|
var classes = split(node.className);
|
|
|
|
classes.push(el);
|
|
node.className = classes.join(' ');
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
delete: function _delete(el) {
|
|
var classes = split(node.className),
|
|
pos = classes.indexOf(el);
|
|
|
|
if (pos > -1) {
|
|
classes.splice(pos, 1);
|
|
node.className = classes.join(' ');
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
has: function has(el) {
|
|
return split(node.className).indexOf(el) !== -1;
|
|
},
|
|
clear: function clear() {
|
|
return node.className = '';
|
|
},
|
|
|
|
get size() {
|
|
return split(node.className).length;
|
|
},
|
|
|
|
keys: function keys() {
|
|
return iterateArray(split(node.className));
|
|
},
|
|
values: function values() {
|
|
return iterateArray(split(node.className));
|
|
},
|
|
entries: function entries() {
|
|
return iterateArray(split(node.className).map(function (el) {
|
|
return [el, el];
|
|
}));
|
|
},
|
|
|
|
forEach: function forEach(callback, thisArg) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = this[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var cls = _step.value;
|
|
|
|
callback.call(thisArg, cls, cls, this);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
res[Symbol.iterator] = res.values;
|
|
return res;
|
|
};
|
|
|
|
module.exports = wrapClass;
|
|
}, { "./utils": 5 }], 3: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var wrapClass = require('./class');
|
|
var wrapStyle = require('./style');
|
|
var split = require('./utils').split;
|
|
|
|
/**
|
|
* Ensure a node is not wrapped before using it in native methods
|
|
*
|
|
* @param {Node|Object} node A node, wrapped or not
|
|
* @return {Node} Unwrapped node
|
|
*/
|
|
var unwrap = function unwrap(node) {
|
|
return (typeof node === "undefined" ? "undefined" : _typeof(node)) !== 'object' || node === null || !node.node ? node : node.node;
|
|
};
|
|
|
|
/**
|
|
* Turn a NodeList/HTMLCollection into an array
|
|
* for easy manipulation
|
|
*
|
|
* @param {NodeList|HTMLCollection} list Input collection
|
|
* @return {Array} Wrapping array
|
|
*/
|
|
var wrapList = function wrapList(list) {
|
|
var length = list.length;
|
|
var result = [];
|
|
|
|
for (var i = 0; i < length; i += 1) {
|
|
result.push(wrapNode(list.item(i)));
|
|
}
|
|
|
|
return Object.assign(result, {
|
|
on: function on(evts, handler) {
|
|
result.forEach(function (node) {
|
|
return node.on(evts, handler);
|
|
});
|
|
},
|
|
|
|
off: function off(evts, handler) {
|
|
result.forEach(function (node) {
|
|
return node.off(evts, handler);
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Create an object of shortcuts to manipulate
|
|
* given node more easily
|
|
*
|
|
* @param {Node} Input node
|
|
* @return {Object} DOM shortcuts
|
|
*/
|
|
var wrapNode = function wrapNode(node) {
|
|
if (node === null || (typeof node === "undefined" ? "undefined" : _typeof(node)) !== 'object') {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
node: node,
|
|
|
|
// search among children
|
|
find: function find(query) {
|
|
return wrapNode(node.querySelector(query));
|
|
},
|
|
findAll: function findAll(query) {
|
|
return wrapList(node.querySelectorAll(query));
|
|
},
|
|
|
|
// access node's relative tree (parent, children, siblings)
|
|
equal: function equal(el) {
|
|
return unwrap(el) === node;
|
|
},
|
|
|
|
get following() {
|
|
return wrapNode(node.nextElementSibling);
|
|
},
|
|
|
|
get preceding() {
|
|
return wrapNode(node.previousElementSibling);
|
|
},
|
|
|
|
get parent() {
|
|
return wrapNode(node.parentNode);
|
|
},
|
|
|
|
get children() {
|
|
return wrapList(node.children);
|
|
},
|
|
|
|
// check relative positions
|
|
precedes: function precedes(el) {
|
|
return !!(unwrap(el).compareDocumentPosition(node) & 2);
|
|
},
|
|
follows: function follows(el) {
|
|
return !!(unwrap(el).compareDocumentPosition(node) & 4);
|
|
},
|
|
contains: function contains(el) {
|
|
return !!(unwrap(el).compareDocumentPosition(node) & 8);
|
|
},
|
|
contained: function contained(el) {
|
|
return !!(unwrap(el).compareDocumentPosition(node) & 16);
|
|
},
|
|
|
|
// get and set element attributes
|
|
get name() {
|
|
return node.tagName.toLowerCase().trim();
|
|
},
|
|
|
|
get type() {
|
|
switch (node.nodeType) {
|
|
case 1:
|
|
return 'element';
|
|
case 3:
|
|
return 'text';
|
|
case 7:
|
|
return 'processing-instruction';
|
|
case 8:
|
|
return 'comment';
|
|
case 9:
|
|
return 'document';
|
|
case 10:
|
|
return 'document-type';
|
|
case 11:
|
|
return 'document-fragment';
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
|
|
getAttr: function getAttr(attr) {
|
|
return node.getAttribute(attr);
|
|
},
|
|
setAttr: function setAttr(attr, value) {
|
|
return node.setAttribute(attr, value);
|
|
},
|
|
|
|
// place an element in the DOM tree
|
|
append: function append(subnode) {
|
|
return node.appendChild(unwrap(subnode));
|
|
},
|
|
attach: function attach(parent) {
|
|
return unwrap(parent).appendChild(node);
|
|
},
|
|
remove: function remove(child) {
|
|
if (child) {
|
|
node.removeChild(unwrap(child));
|
|
return;
|
|
}
|
|
|
|
node.parentNode.removeChild(node);
|
|
},
|
|
|
|
// manipulate element's CSS (see wrapClass, wrapStyle)
|
|
class: wrapClass(node),
|
|
style: wrapStyle(node),
|
|
|
|
// change an element's content
|
|
get text() {
|
|
return node.textContent;
|
|
},
|
|
|
|
set text(val) {
|
|
node.textContent = val;
|
|
},
|
|
|
|
get html() {
|
|
return node.innerHTML;
|
|
},
|
|
|
|
set html(val) {
|
|
node.innerHTML = val;
|
|
},
|
|
|
|
// listen to events
|
|
on: function on(evts, handler) {
|
|
split(evts).forEach(function (evt) {
|
|
node.addEventListener(evt, handler);
|
|
});
|
|
},
|
|
|
|
off: function off(evts, handler) {
|
|
split(evts).forEach(function (evt) {
|
|
node.removeEventListener(evt, handler);
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
module.exports = wrapNode;
|
|
}, { "./class": 2, "./style": 4, "./utils": 5 }], 4: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var iterateArray = require('./utils').iterateArray;
|
|
|
|
/**
|
|
* Create an object to manipulate given node's CSS styles
|
|
*
|
|
* @param {HTMLElement} node Input element
|
|
* @see `Map` documentation for behaviour information
|
|
* @return {Object} Map-like object
|
|
*/
|
|
var wrapStyle = function wrapStyle(node) {
|
|
var res = {
|
|
set: function set(prop, value) {
|
|
node.style.setProperty(prop, value);
|
|
return this;
|
|
},
|
|
|
|
delete: function _delete(prop) {
|
|
return node.style.removeProperty(prop) !== '';
|
|
},
|
|
has: function has(prop) {
|
|
return [].slice.call(node.style).indexOf(prop) > -1;
|
|
},
|
|
|
|
get: function get(prop) {
|
|
var result = node.style.getPropertyValue(prop);
|
|
|
|
if (result.trim() === '') {
|
|
return undefined;
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
clear: function clear() {
|
|
var length = node.style.length;
|
|
|
|
for (var i = 0; i < length; i += 1) {
|
|
node.style.removeProperty(node.style[i]);
|
|
}
|
|
},
|
|
|
|
get size() {
|
|
return node.style.length;
|
|
},
|
|
|
|
keys: function keys() {
|
|
return iterateArray([].slice.call(node.style));
|
|
},
|
|
values: function values() {
|
|
return iterateArray([].slice.call(node.style).map(function (prop) {
|
|
return node.style.getPropertyValue(prop);
|
|
}));
|
|
},
|
|
entries: function entries() {
|
|
return iterateArray([].slice.call(node.style).map(function (prop) {
|
|
return [prop, node.style.getPropertyValue(prop)];
|
|
}));
|
|
},
|
|
|
|
forEach: function forEach(callback, thisArg) {
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = this[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var cls = _step2.value;
|
|
|
|
callback.call(thisArg, cls, cls, this);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
res[Symbol.iterator] = res.values;
|
|
return res;
|
|
};
|
|
|
|
module.exports = wrapStyle;
|
|
}, { "./utils": 5 }], 5: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var whitespace = /\s+/g;
|
|
|
|
/**
|
|
* Split a list of whitespace separated tokens,
|
|
* excluding empty ones
|
|
*
|
|
* @param {string} str Input string
|
|
* @return {Array} Split tokens
|
|
*/
|
|
exports.split = function (str) {
|
|
return str.split(whitespace).filter(function (el) {
|
|
return el.trim().length;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Create an iterator on an array
|
|
*
|
|
* @param {Array} arr Array to iterate on
|
|
* @return {Object} Iterator for given array
|
|
*/
|
|
exports.iterateArray = function (arr) {
|
|
var _next = 0;
|
|
|
|
return {
|
|
next: function next() {
|
|
return _next < arr.length ? { value: arr[_next++], done: false } : { done: true };
|
|
}
|
|
};
|
|
};
|
|
}, {}], 6: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.applyChaos = exports.scaleVertices = exports.createRegularVertices = undefined;
|
|
|
|
var _utils = require('./utils');
|
|
|
|
/**
|
|
* Calculate the position of a regular polygon's vertices
|
|
* inside a 2 x 2 squared centered on the origin
|
|
*
|
|
* @param {number} count Vertices amount
|
|
* @return {Array<Array>} Array of points representing the vertices
|
|
*/
|
|
var createRegularVertices = exports.createRegularVertices = function createRegularVertices(count) {
|
|
var step = 2 * Math.PI / count;
|
|
var initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1));
|
|
var result = [];
|
|
|
|
for (var i = 0; i < count; i += 1) {
|
|
var current = step * i + initial;
|
|
|
|
result.push([Math.cos(current), Math.sin(current)]);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Scale the vertices so that they fit in given bounding rectangle
|
|
*
|
|
* @param {number} width Bounding rectangle width
|
|
* @param {number} height Bounding rectangle height
|
|
* @param {Array<Array>} vertices Vertices to scale
|
|
* @return {Array<Array>} Scaled vertices
|
|
*/
|
|
var scaleVertices = exports.scaleVertices = function scaleVertices(width, height, vertices) {
|
|
var centerX = Math.floor(width / 2);
|
|
var centerY = Math.floor(height / 2);
|
|
var radius = Math.min(centerX, centerY);
|
|
|
|
return vertices.map(function (vertex) {
|
|
return [vertex[0] * radius + centerX, vertex[1] * radius + centerY];
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Apply the chaos game algorithm in a polygon
|
|
* of given vertices, with given fraction
|
|
*
|
|
* @param {ImageData} image Image to write on Data to amend
|
|
* @param {number} fraction Fraction to use
|
|
* @param {Array} vertices List of vertices of the bounding polygon
|
|
* @return {null}
|
|
*/
|
|
var applyChaos = exports.applyChaos = function applyChaos(image, fraction, vertices) {
|
|
var count = vertices.length,
|
|
imageWidth = image.width;
|
|
|
|
// now we apply the chaos algorithm:
|
|
// for any point, the next point is a `fraction` of the
|
|
// distance between it and a random vertex
|
|
var point = vertices[0];
|
|
var iterations = Math.floor(500 * imageWidth * fraction);
|
|
var drop = Math.floor(iterations / 200);
|
|
|
|
while (iterations--) {
|
|
var vertexNumber = (0, _utils.getRandomNumber)(0, count);
|
|
var vertex = vertices[vertexNumber],
|
|
color = (0, _utils.getColor)(vertexNumber);
|
|
|
|
point = [Math.floor((point[0] - vertex[0]) * fraction + vertex[0]), Math.floor((point[1] - vertex[1]) * fraction + vertex[1])];
|
|
|
|
// skip the first 1000 points
|
|
if (drop === 0) {
|
|
var i = (point[1] * imageWidth + point[0]) * 4;
|
|
|
|
image.data[i] = color[0];
|
|
image.data[i + 1] = color[1];
|
|
image.data[i + 2] = color[2];
|
|
image.data[i + 3] = 255;
|
|
} else {
|
|
drop--;
|
|
}
|
|
}
|
|
};
|
|
}, { "./utils": 8 }], 7: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
var _theDom = require('the-dom');
|
|
|
|
var _utils = require('./utils');
|
|
|
|
var _chaos = require('./chaos');
|
|
|
|
var _html = (0, _theDom.html)(document);
|
|
|
|
var body = _html.body;
|
|
|
|
var content = body.find('#content');
|
|
var verticesRange = body.find('#vertices');
|
|
var fractionRange = body.find('#fraction');
|
|
|
|
var plotting = body.find('#plotting').node;
|
|
var ctx = plotting.getContext('2d');
|
|
|
|
var padding = 40; // padding between the canvas edges and the points
|
|
var width = undefined,
|
|
height = undefined,
|
|
vertices = undefined;
|
|
|
|
/**
|
|
* Re-render the scene from scratch
|
|
*
|
|
* @return {null}
|
|
*/
|
|
var render = function render() {
|
|
var fraction = 1 / parseFloat(fractionRange.node.value);
|
|
var scaledVerts = (0, _chaos.scaleVertices)(width, height, vertices);
|
|
|
|
plotting.width = width + 2 * padding;
|
|
plotting.height = height + 2 * padding;
|
|
|
|
// do not plot (very) small sizes
|
|
if (width < 1) {
|
|
return;
|
|
}
|
|
|
|
// draw the polygon
|
|
ctx.strokeStyle = '#aaa';
|
|
ctx.lineWidth = 1;
|
|
|
|
ctx.beginPath();
|
|
|
|
for (var i = 0; i < vertices.length; i += 1) {
|
|
ctx.lineTo(scaledVerts[i][0] + padding, scaledVerts[i][1] + padding);
|
|
}
|
|
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
|
|
// draw the vertices
|
|
for (var i = 0; i < vertices.length; i += 1) {
|
|
ctx.beginPath();
|
|
ctx.fillStyle = 'rgb(' + (0, _utils.getColor)(i).join(', ') + ')';
|
|
|
|
ctx.arc(scaledVerts[i][0] + padding, scaledVerts[i][1] + padding, 4, 0, Math.PI * 2);
|
|
|
|
ctx.fill();
|
|
}
|
|
|
|
// do the chaos game
|
|
var image = ctx.getImageData(padding, padding, width, height);
|
|
|
|
(0, _chaos.applyChaos)(image, fraction, scaledVerts);
|
|
ctx.putImageData(image, padding, padding);
|
|
};
|
|
|
|
/**
|
|
* Update the scene when the window has been resized
|
|
*
|
|
* @return {null}
|
|
*/
|
|
var resize = function resize() {
|
|
width = content.node.clientWidth - 2 * padding;
|
|
height = content.node.clientHeight - 2 * padding;
|
|
|
|
render();
|
|
};
|
|
|
|
/**
|
|
* Create new vertices
|
|
*/
|
|
verticesRange.on('input', function () {
|
|
vertices = (0, _chaos.createRegularVertices)(parseInt(verticesRange.node.value, 10));
|
|
|
|
render();
|
|
});
|
|
|
|
window.onresize = resize;
|
|
fractionRange.on('input', render);
|
|
|
|
vertices = (0, _chaos.createRegularVertices)(3);
|
|
resize();
|
|
}, { "./chaos": 6, "./utils": 8, "the-dom": 1 }], 8: [function (require, module, exports) {
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
var colors = ['#F44336', '#2196F3', '#4CAF50', '#F9A825', '#E91E63', '#00838F'].map(function (color) {
|
|
return color.match(/[A-F0-9]{2}/g).map(function (component) {
|
|
return parseInt(component, 16);
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Get a random whole number
|
|
*
|
|
* @param {number} min Minimal value for the number
|
|
* @param {number} max Maximal value for the number (excluded)
|
|
* @return {number} Random number
|
|
*/
|
|
var getRandomNumber = exports.getRandomNumber = function getRandomNumber(min, max) {
|
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
};
|
|
|
|
/**
|
|
* Get a color at given index. For any given
|
|
* index, the same color will always be returned
|
|
*
|
|
* @param {number} index Color index
|
|
* @return {Array} RGB components
|
|
*/
|
|
var getColor = exports.getColor = function getColor(index) {
|
|
return colors[index % colors.length];
|
|
};
|
|
}, {}] }, {}, [7]);
|
|
|