(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 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 = doc => ({ create: name => 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'; const utils = require('./utils'); const split = utils.split; const 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 */ const wrapClass = node => { const res = { add: function (el) { if (!this.has(el)) { const classes = split(node.className); classes.push(el); node.className = classes.join(' '); } return this; }, delete: el => { const classes = split(node.className), pos = classes.indexOf(el); if (pos > -1) { classes.splice(pos, 1); node.className = classes.join(' '); return true; } return false; }, has: el => split(node.className).indexOf(el) !== -1, clear: () => node.className = '', get size() { return split(node.className).length; }, keys: () => iterateArray(split(node.className)), values: () => iterateArray(split(node.className)), entries: () => iterateArray(split(node.className).map(el => [el, el])), forEach: function (callback, thisArg) { for (let cls of this) { callback.call(thisArg, cls, cls, this); } } }; res[Symbol.iterator] = res.values; return res; }; module.exports = wrapClass; },{"./utils":5}],3:[function(require,module,exports){ 'use strict'; const wrapClass = require('./class'); const wrapStyle = require('./style'); const 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 */ const unwrap = node => (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 */ const wrapList = list => { const length = list.length; let result = []; for (let i = 0; i < length; i += 1) { result.push(wrapNode(list.item(i))); } return Object.assign(result, { on: (evts, handler) => { result.forEach(node => node.on(evts, handler)); }, off: (evts, handler) => { result.forEach(node => node.off(evts, handler)); } }); }; /** * Create an object of shortcuts to manipulate * given node more easily * * @param {Node} Input node * @return {Object} DOM shortcuts */ const wrapNode = node => { if (node === null || typeof node !== 'object') { return null; } return { node, // search among children find: query => wrapNode(node.querySelector(query)), findAll: query => wrapList(node.querySelectorAll(query)), // access node's relative tree (parent, children, siblings) equal: el => 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: el => !!(unwrap(el).compareDocumentPosition(node) & 2), follows: el => !!(unwrap(el).compareDocumentPosition(node) & 4), contains: el => !!(unwrap(el).compareDocumentPosition(node) & 8), contained: el => !!(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: attr => node.getAttribute(attr), setAttr: (attr, value) => node.setAttribute(attr, value), // place an element in the DOM tree append: subnode => node.appendChild(unwrap(subnode)), attach: parent => unwrap(parent).appendChild(node), 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: (evts, handler) => { split(evts).forEach(evt => { node.addEventListener(evt, handler); }); }, off: (evts, handler) => { split(evts).forEach(evt => { node.removeEventListener(evt, handler); }); } }; }; module.exports = wrapNode; },{"./class":2,"./style":4,"./utils":5}],4:[function(require,module,exports){ 'use strict'; const 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 */ const wrapStyle = node => { const res = { set: function (prop, value) { node.style.setProperty(prop, value); return this; }, delete: prop => node.style.removeProperty(prop) !== '', has: prop => [].slice.call(node.style).indexOf(prop) > -1, get: prop => { const result = node.style.getPropertyValue(prop); if (result.trim() === '') { return undefined; } return result; }, clear: () => { const length = node.style.length; for (let i = 0; i < length; i += 1) { node.style.removeProperty(node.style[i]); } }, get size() { return node.style.length; }, keys: () => iterateArray([].slice.call(node.style)), values: () => iterateArray([].slice.call(node.style).map( prop => node.style.getPropertyValue(prop))), entries: () => iterateArray([].slice.call(node.style).map( prop => [prop, node.style.getPropertyValue(prop)])), forEach: function (callback, thisArg) { for (let cls of this) { callback.call(thisArg, cls, cls, this); } } }; res[Symbol.iterator] = res.values; return res; }; module.exports = wrapStyle; },{"./utils":5}],5:[function(require,module,exports){ 'use strict'; const whitespace = /\s+/g; /** * Split a list of whitespace separated tokens, * excluding empty ones * * @param {string} str Input string * @return {Array} Split tokens */ exports.split = str => str.split(whitespace).filter(el => el.trim().length); /** * Create an iterator on an array * * @param {Array} arr Array to iterate on * @return {Object} Iterator for given array */ exports.iterateArray = (arr) => { let next = 0; return { next: () => next < arr.length ? {value: arr[next++], done: false} : {done: true} }; }; },{}],6:[function(require,module,exports){ 'use strict'; var _theDom = require('the-dom'); var _utils = require('./utils'); var _html = (0, _theDom.html)(document); var body = _html.body; var create = _html.create; var content = body.find('#content'); var plotting = body.find('#plotting'); var ctx = plotting.node.getContext('2d'); var padding = 40; // padding between the canvas edges and the points var image = undefined, width = undefined, height = undefined; var lastUpdate = -Infinity; /** * Create a fractal of given width, height, based on * a polygon of given amount of vertices, using the * chaos game applied with given fraction * * @param {number} width Fractal width * @param {number} height Fractal height * @param {number} fraction Fraction to use * @param {Array} colors Color of each vertex * @return {ImageData} Generated pixel data */ var chaos = function chaos(width, height, fraction, colors) { var cx = Math.floor(width / 2); var cy = Math.floor(height / 2); var radius = Math.min(cx, cy); var count = colors.length; var vertices = []; var angleStep = 2 * Math.PI / count; var initialAngle = undefined; // creating 0-width image data will throw an error if (width <= 0 || height <= 0) { return ctx.createImageData(1, 1); } var image = ctx.createImageData(width, height); var data = image.data; // we will rotate around an inscribed circle to calculate // the vertices' positions. We adapt the initial angle so // that usual polygons look better if (count === 3) { initialAngle = -Math.PI / 2; } else if (count === 4) { initialAngle = Math.PI / 4; } else { initialAngle = 0; } for (var i = 0; i < count; i += 1) { var current = angleStep * i + initialAngle; vertices.push([Math.floor(Math.cos(current) * radius + cx), Math.floor(Math.sin(current) * radius + cy)]); } // 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 = 200000; var drop = 1000; while (iterations--) { var vertexNumber = (0, _utils.randomNumber)(0, count); var vertex = vertices[vertexNumber], color = colors[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] * width + point[0]) * 4; data[i] = color[0]; data[i + 1] = color[1]; data[i + 2] = color[2]; data[i + 3] = 255; } else { drop--; } } return image; }; /** * Render the scene, recalculating the points * positions if they need to * * @return {null} */ var render = function render() { // only recalculate every 16.67 ms if (+new Date() - lastUpdate > 16.67) { image = chaos(width - 2 * padding, height - 2 * padding, 1 / 2, [[255, 0, 0], [0, 255, 0], [0, 0, 255]]); lastUpdate = +new Date(); } ctx.clearRect(0, 0, width, height); ctx.putImageData(image, padding, padding); }; /** * Resize the canvas to fit the new * window size and redraw the scene * * @return {null} */ var resize = function resize() { width = content.node.clientWidth; height = content.node.clientHeight; plotting.setAttr('width', width); plotting.setAttr('height', height); render(); }; window.onresize = resize; resize(); },{"./utils":7,"the-dom":1}],7:[function(require,module,exports){ 'use strict' /** * 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 */ ; Object.defineProperty(exports, "__esModule", { value: true }); var randomNumber = exports.randomNumber = function randomNumber(min, max) { return Math.floor(Math.random() * (max - min)) + min; }; /** * Generate a random color * * @return {Array} RGB components */ var randomColor = exports.randomColor = function randomColor() { var color = []; for (var i = 0; i < 3; i++) { color.push(Math.round(Math.random().toFixed(2) * 255)); } return color; }; /** * Convert a decimal number to its hexadecimal representation * * @param {number} input Number to be converted * @return {string} Number representation */ var hex = function hex(input) { var hex = parseInt(input, 10).toString(16); return hex.length === 1 ? '0' + hex : hex; }; /** * Convert a RGB color to its hexadecimal representation * * @param {Array} color RGB color * @return {string} Hex representation */ var rgbToHex = exports.rgbToHex = function rgbToHex(color) { return '#' + hex(color[0]) + hex(color[1]) + hex(color[2]); }; },{}]},{},[6]);