')
- },
- quote: {
- icon: '“ ”',
- title: 'Quote',
- result: () => exec(formatBlock, '
')
- },
- olist: {
- icon: '#',
- title: 'Ordered List',
- result: () => exec('insertOrderedList')
- },
- ulist: {
- icon: '•',
- title: 'Unordered List',
- result: () => exec('insertUnorderedList')
- },
- code: {
- icon: '</>',
- title: 'Code',
- result: () => exec(formatBlock, '')
- },
- line: {
- icon: '―',
- title: 'Horizontal Line',
- result: () => exec('insertHorizontalRule')
- },
- link: {
- icon: '🔗',
- title: 'Link',
- result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
- },
- image: {
- icon: '📷',
- title: 'Image',
- result: () => {
- navigator.clipboard.readText().then(url => exec('insertImage', url))
- exec('enableObjectResizing')
- }
- }
- }
-
- const defaultClasses = {
- actionbar: 'pell-actionbar',
- button: 'pell-button',
- content: 'pell-content',
- selected: 'pell-button-selected'
- }
-
- const init = settings => {
- const actions = settings.actions
- ? (
- settings.actions.map(action => {
- if (typeof action === 'string') return defaultActions[action]
- else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }
- return action
- })
- )
- : Object.keys(defaultActions).map(action => defaultActions[action])
-
- const classes = { ...defaultClasses, ...settings.classes }
-
- const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'
-
- const actionbar = createElement('div')
- actionbar.className = classes.actionbar
- appendChild(settings.element, actionbar)
-
- const content = settings.element.content = createElement('div')
- content.contentEditable = true
- content.className = classes.content
- content.oninput = ({ target: { firstChild } }) => {
- if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)
- else if (content.innerHTML === '
') content.innerHTML = ''
- settings.onChange(content.innerHTML)
- }
- content.onkeydown = event => {
- if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
- setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0)
- }
- }
- appendChild(settings.element, content)
-
- actions.forEach(action => {
- const button = createElement('button')
- button.className = classes.button
- button.innerHTML = action.icon
- button.title = action.title
- button.setAttribute('type', 'button')
- button.onclick = () => action.result() && content.focus()
-
- if (action.state) {
- const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected)
- addEventListener(content, 'keyup', handler)
- addEventListener(content, 'mouseup', handler)
- addEventListener(button, 'click', handler)
- }
-
- appendChild(actionbar, button)
- })
-
- if (settings.styleWithCSS) exec('styleWithCSS')
- exec(defaultParagraphSeparatorString, defaultParagraphSeparator)
-
- return settings.element
- }
-
- return {exec, init}
-
-})));
\ No newline at end of file
diff --git a/libs/pell.min.js b/libs/pell.min.js
new file mode 100644
index 00000000..c5be57e5
--- /dev/null
+++ b/libs/pell.min.js
@@ -0,0 +1,2 @@
+// https://github.com/jaredreich/pell, MIT License
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Pell=t()}(this,function(){"use strict";const e=(e,t,n)=>e.addEventListener(t,n),t=(e,t)=>e.appendChild(t),n=e=>document.createElement(e),i=e=>document.queryCommandState(e),o=(e,t=null)=>document.execCommand(e,!1,t),l={bold:{icon:"B",title:"Bold",state:()=>i("bold"),result:()=>o("bold")},italic:{icon:"I",title:"Italic",state:()=>i("italic"),result:()=>o("italic")},underline:{icon:"U",title:"Underline",state:()=>i("underline"),result:()=>o("underline")},strikethrough:{icon:"S",title:"Strike-through",state:()=>i("strikeThrough"),result:()=>o("strikeThrough")},heading1:{icon:"H1",title:"Heading 1",result:()=>o("formatBlock","")},heading2:{icon:"H2",title:"Heading 2",result:()=>o("formatBlock","")},paragraph:{icon:"¶",title:"Paragraph",result:()=>o("formatBlock","
")},quote:{icon:"“ ”",title:"Quote",result:()=>o("formatBlock","
")},olist:{icon:"#",title:"Ordered List",result:()=>o("insertOrderedList")},ulist:{icon:"•",title:"Unordered List",result:()=>o("insertUnorderedList")},code:{icon:"</>",title:"Code",result:()=>o("formatBlock","")},line:{icon:"―",title:"Horizontal Line",result:()=>o("insertHorizontalRule")},link:{icon:"🔗",title:"Link",result:()=>navigator.clipboard.readText().then(e=>o("createLink",e))},image:{icon:"📷",title:"Image",result:()=>{navigator.clipboard.readText().then(e=>o("insertImage",e)),o("enableObjectResizing")}}},r={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"};return{exec:o,init:i=>{const a=i.actions?i.actions.map(e=>"string"==typeof e?l[e]:l[e.name]?{...l[e.name],...e}:e):Object.keys(l).map(e=>l[e]),s={...r,...i.classes},c=i.defaultParagraphSeparator||"div",u=n("div");u.className=s.actionbar,t(i.element,u);const d=i.element.content=n("div");return d.contentEditable=!0,d.className=s.content,d.oninput=(({target:{firstChild:e}})=>{e&&3===e.nodeType?o("formatBlock",`<${c}>`):"
"===d.innerHTML&&(d.innerHTML=""),i.onChange(d.innerHTML)}),d.onkeydown=(e=>{"Enter"===e.key&&"blockquote"===(e=>document.queryCommandValue(e))("formatBlock")&&setTimeout(()=>o("formatBlock",`<${c}>`),0)}),t(i.element,d),a.forEach(i=>{const o=n("button");if(o.className=s.button,o.innerHTML=i.icon,o.title=i.title,o.setAttribute("type","button"),o.onclick=(()=>i.result()&&d.focus()),i.state){const t=()=>o.classList[i.state()?"add":"remove"](s.selected);e(d,"keyup",t),e(d,"mouseup",t),e(o,"click",t)}t(u,o)}),i.styleWithCSS&&o("styleWithCSS"),o("defaultParagraphSeparator",c),i.element}}});
\ No newline at end of file
diff --git a/libs/rgbquant.js b/libs/rgbquant.js
deleted file mode 100644
index bc0bd1b9..00000000
--- a/libs/rgbquant.js
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
-* Copyright (c) 2015, Leon Sorokin
-* All rights reserved. (MIT Licensed)
-*
-* RgbQuant.js - an image quantization lib
-*/
-
-(function(){
- function RgbQuant(opts) {
- opts = opts || {};
-
- // 1 = by global population, 2 = subregion population threshold
- this.method = opts.method || 2;
- // desired final palette size
- this.colors = opts.colors || 256;
- // # of highest-frequency colors to start with for palette reduction
- this.initColors = opts.initColors || 4096;
- // color-distance threshold for initial reduction pass
- this.initDist = opts.initDist || 0.01;
- // subsequent passes threshold
- this.distIncr = opts.distIncr || 0.005;
- // palette grouping
- this.hueGroups = opts.hueGroups || 10;
- this.satGroups = opts.satGroups || 10;
- this.lumGroups = opts.lumGroups || 10;
- // if > 0, enables hues stats and min-color retention per group
- this.minHueCols = opts.minHueCols || 0;
- // HueStats instance
- this.hueStats = this.minHueCols ? new HueStats(this.hueGroups, this.minHueCols) : null;
-
- // subregion partitioning box size
- this.boxSize = opts.boxSize || [64,64];
- // number of same pixels required within box for histogram inclusion
- this.boxPxls = opts.boxPxls || 2;
- // palette locked indicator
- this.palLocked = false;
- // palette sort order
-// this.sortPal = ['hue-','lum-','sat-'];
-
- // dithering/error diffusion kernel name
- this.dithKern = opts.dithKern || null;
- // dither serpentine pattern
- this.dithSerp = opts.dithSerp || false;
- // minimum color difference (0-1) needed to dither
- this.dithDelta = opts.dithDelta || 0;
-
- // accumulated histogram
- this.histogram = {};
- // palette - rgb triplets
- this.idxrgb = opts.palette ? opts.palette.slice(0) : [];
- // palette - int32 vals
- this.idxi32 = [];
- // reverse lookup {i32:idx}
- this.i32idx = {};
- // {i32:rgb}
- this.i32rgb = {};
- // enable color caching (also incurs overhead of cache misses and cache building)
- this.useCache = opts.useCache !== false;
- // min color occurance count needed to qualify for caching
- this.cacheFreq = opts.cacheFreq || 10;
- // allows pre-defined palettes to be re-indexed (enabling palette compacting and sorting)
- this.reIndex = opts.reIndex || this.idxrgb.length == 0;
- // selection of color-distance equation
- this.colorDist = opts.colorDist == "manhattan" ? distManhattan : distEuclidean;
-
- // if pre-defined palette, build lookups
- if (this.idxrgb.length > 0) {
- var self = this;
- this.idxrgb.forEach(function(rgb, i) {
- var i32 = (
- (255 << 24) | // alpha
- (rgb[2] << 16) | // blue
- (rgb[1] << 8) | // green
- rgb[0] // red
- ) >>> 0;
-
- self.idxi32[i] = i32;
- self.i32idx[i32] = i;
- self.i32rgb[i32] = rgb;
- });
- }
- }
-
- // gathers histogram info
- RgbQuant.prototype.sample = function sample(img, width) {
- if (this.palLocked)
- throw "Cannot sample additional images, palette already assembled.";
-
- var data = getImageData(img, width);
-
- switch (this.method) {
- case 1: this.colorStats1D(data.buf32); break;
- case 2: this.colorStats2D(data.buf32, data.width); break;
- }
- };
-
- // image quantizer
- // todo: memoize colors here also
- // @retType: 1 - Uint8Array (default), 2 - Indexed array, 3 - Match @img type (unimplemented, todo)
- RgbQuant.prototype.reduce = function reduce(img, retType, dithKern, dithSerp) {
- if (!this.palLocked)
- this.buildPal();
-
- dithKern = dithKern || this.dithKern;
- dithSerp = typeof dithSerp != "undefined" ? dithSerp : this.dithSerp;
-
- retType = retType || 1;
-
- // reduce w/dither
- if (dithKern)
- var out32 = this.dither(img, dithKern, dithSerp);
- else {
- var data = getImageData(img),
- buf32 = data.buf32,
- len = buf32.length,
- out32 = new Uint32Array(len);
-
- for (var i = 0; i < len; i++) {
- var i32 = buf32[i];
- out32[i] = this.nearestColor(i32);
- }
- }
-
- if (retType == 1)
- return new Uint8Array(out32.buffer);
-
- if (retType == 2) {
- var out = [],
- len = out32.length;
-
- for (var i = 0; i < len; i++) {
- var i32 = out32[i];
- out[i] = this.i32idx[i32];
- }
-
- return out;
- }
- };
-
- // adapted from http://jsbin.com/iXofIji/2/edit by PAEz
- RgbQuant.prototype.dither = function(img, kernel, serpentine) {
- // http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/
- var kernels = {
- FloydSteinberg: [
- [7 / 16, 1, 0],
- [3 / 16, -1, 1],
- [5 / 16, 0, 1],
- [1 / 16, 1, 1]
- ],
- FalseFloydSteinberg: [
- [3 / 8, 1, 0],
- [3 / 8, 0, 1],
- [2 / 8, 1, 1]
- ],
- Stucki: [
- [8 / 42, 1, 0],
- [4 / 42, 2, 0],
- [2 / 42, -2, 1],
- [4 / 42, -1, 1],
- [8 / 42, 0, 1],
- [4 / 42, 1, 1],
- [2 / 42, 2, 1],
- [1 / 42, -2, 2],
- [2 / 42, -1, 2],
- [4 / 42, 0, 2],
- [2 / 42, 1, 2],
- [1 / 42, 2, 2]
- ],
- Atkinson: [
- [1 / 8, 1, 0],
- [1 / 8, 2, 0],
- [1 / 8, -1, 1],
- [1 / 8, 0, 1],
- [1 / 8, 1, 1],
- [1 / 8, 0, 2]
- ],
- Jarvis: [ // Jarvis, Judice, and Ninke / JJN?
- [7 / 48, 1, 0],
- [5 / 48, 2, 0],
- [3 / 48, -2, 1],
- [5 / 48, -1, 1],
- [7 / 48, 0, 1],
- [5 / 48, 1, 1],
- [3 / 48, 2, 1],
- [1 / 48, -2, 2],
- [3 / 48, -1, 2],
- [5 / 48, 0, 2],
- [3 / 48, 1, 2],
- [1 / 48, 2, 2]
- ],
- Burkes: [
- [8 / 32, 1, 0],
- [4 / 32, 2, 0],
- [2 / 32, -2, 1],
- [4 / 32, -1, 1],
- [8 / 32, 0, 1],
- [4 / 32, 1, 1],
- [2 / 32, 2, 1],
- ],
- Sierra: [
- [5 / 32, 1, 0],
- [3 / 32, 2, 0],
- [2 / 32, -2, 1],
- [4 / 32, -1, 1],
- [5 / 32, 0, 1],
- [4 / 32, 1, 1],
- [2 / 32, 2, 1],
- [2 / 32, -1, 2],
- [3 / 32, 0, 2],
- [2 / 32, 1, 2],
- ],
- TwoSierra: [
- [4 / 16, 1, 0],
- [3 / 16, 2, 0],
- [1 / 16, -2, 1],
- [2 / 16, -1, 1],
- [3 / 16, 0, 1],
- [2 / 16, 1, 1],
- [1 / 16, 2, 1],
- ],
- SierraLite: [
- [2 / 4, 1, 0],
- [1 / 4, -1, 1],
- [1 / 4, 0, 1],
- ],
- };
-
- if (!kernel || !kernels[kernel]) {
- throw 'Unknown dithering kernel: ' + kernel;
- }
-
- var ds = kernels[kernel];
-
- var data = getImageData(img),
-// buf8 = data.buf8,
- buf32 = data.buf32,
- width = data.width,
- height = data.height,
- len = buf32.length;
-
- var dir = serpentine ? -1 : 1;
-
- for (var y = 0; y < height; y++) {
- if (serpentine)
- dir = dir * -1;
-
- var lni = y * width;
-
- for (var x = (dir == 1 ? 0 : width - 1), xend = (dir == 1 ? width : 0); x !== xend; x += dir) {
- // Image pixel
- var idx = lni + x,
- i32 = buf32[idx],
- r1 = (i32 & 0xff),
- g1 = (i32 & 0xff00) >> 8,
- b1 = (i32 & 0xff0000) >> 16;
-
- // Reduced pixel
- var i32x = this.nearestColor(i32),
- r2 = (i32x & 0xff),
- g2 = (i32x & 0xff00) >> 8,
- b2 = (i32x & 0xff0000) >> 16;
-
- buf32[idx] =
- (255 << 24) | // alpha
- (b2 << 16) | // blue
- (g2 << 8) | // green
- r2;
-
- // dithering strength
- if (this.dithDelta) {
- var dist = this.colorDist([r1, g1, b1], [r2, g2, b2]);
- if (dist < this.dithDelta)
- continue;
- }
-
- // Component distance
- var er = r1 - r2,
- eg = g1 - g2,
- eb = b1 - b2;
-
- for (var i = (dir == 1 ? 0 : ds.length - 1), end = (dir == 1 ? ds.length : 0); i !== end; i += dir) {
- var x1 = ds[i][1] * dir,
- y1 = ds[i][2];
-
- var lni2 = y1 * width;
-
- if (x1 + x >= 0 && x1 + x < width && y1 + y >= 0 && y1 + y < height) {
- var d = ds[i][0];
- var idx2 = idx + (lni2 + x1);
-
- var r3 = (buf32[idx2] & 0xff),
- g3 = (buf32[idx2] & 0xff00) >> 8,
- b3 = (buf32[idx2] & 0xff0000) >> 16;
-
- var r4 = Math.max(0, Math.min(255, r3 + er * d)),
- g4 = Math.max(0, Math.min(255, g3 + eg * d)),
- b4 = Math.max(0, Math.min(255, b3 + eb * d));
-
- buf32[idx2] =
- (255 << 24) | // alpha
- (b4 << 16) | // blue
- (g4 << 8) | // green
- r4; // red
- }
- }
- }
- }
-
- return buf32;
- };
-
- // reduces histogram to palette, remaps & memoizes reduced colors
- RgbQuant.prototype.buildPal = function buildPal(noSort) {
- if (this.palLocked || this.idxrgb.length > 0 && this.idxrgb.length <= this.colors) return;
-
- var histG = this.histogram,
- sorted = sortedHashKeys(histG, true);
-
- if (sorted.length == 0)
- throw "Nothing has been sampled, palette cannot be built.";
-
- switch (this.method) {
- case 1:
- var cols = this.initColors,
- last = sorted[cols - 1],
- freq = histG[last];
-
- var idxi32 = sorted.slice(0, cols);
-
- // add any cut off colors with same freq as last
- var pos = cols, len = sorted.length;
- while (pos < len && histG[sorted[pos]] == freq)
- idxi32.push(sorted[pos++]);
-
- // inject min huegroup colors
- if (this.hueStats)
- this.hueStats.inject(idxi32);
-
- break;
- case 2:
- var idxi32 = sorted;
- break;
- }
-
- // int32-ify values
- idxi32 = idxi32.map(function(v){return +v;});
-
- this.reducePal(idxi32);
-
- if (!noSort && this.reIndex)
- this.sortPal();
-
- // build cache of top histogram colors
- if (this.useCache)
- this.cacheHistogram(idxi32);
-
- this.palLocked = true;
- };
-
- RgbQuant.prototype.palette = function palette(tuples, noSort) {
- this.buildPal(noSort);
- return tuples ? this.idxrgb : new Uint8Array((new Uint32Array(this.idxi32)).buffer);
- };
-
- RgbQuant.prototype.prunePal = function prunePal(keep) {
- var i32;
-
- for (var j = 0; j < this.idxrgb.length; j++) {
- if (!keep[j]) {
- i32 = this.idxi32[j];
- this.idxrgb[j] = null;
- this.idxi32[j] = null;
- delete this.i32idx[i32];
- }
- }
-
- // compact
- if (this.reIndex) {
- var idxrgb = [],
- idxi32 = [],
- i32idx = {};
-
- for (var j = 0, i = 0; j < this.idxrgb.length; j++) {
- if (this.idxrgb[j]) {
- i32 = this.idxi32[j];
- idxrgb[i] = this.idxrgb[j];
- i32idx[i32] = i;
- idxi32[i] = i32;
- i++;
- }
- }
-
- this.idxrgb = idxrgb;
- this.idxi32 = idxi32;
- this.i32idx = i32idx;
- }
- };
-
- // reduces similar colors from an importance-sorted Uint32 rgba array
- RgbQuant.prototype.reducePal = function reducePal(idxi32) {
- // if pre-defined palette's length exceeds target
- if (this.idxrgb.length > this.colors) {
- // quantize histogram to existing palette
- var len = idxi32.length, keep = {}, uniques = 0, idx, pruned = false;
-
- for (var i = 0; i < len; i++) {
- // palette length reached, unset all remaining colors (sparse palette)
- if (uniques == this.colors && !pruned) {
- this.prunePal(keep);
- pruned = true;
- }
-
- idx = this.nearestIndex(idxi32[i]);
-
- if (uniques < this.colors && !keep[idx]) {
- keep[idx] = true;
- uniques++;
- }
- }
-
- if (!pruned) {
- this.prunePal(keep);
- pruned = true;
- }
- }
- // reduce histogram to create initial palette
- else {
- // build full rgb palette
- var idxrgb = idxi32.map(function(i32) {
- return [
- (i32 & 0xff),
- (i32 & 0xff00) >> 8,
- (i32 & 0xff0000) >> 16,
- ];
- });
-
- var len = idxrgb.length,
- palLen = len,
- thold = this.initDist;
-
- // palette already at or below desired length
- if (palLen > this.colors) {
- while (palLen > this.colors) {
- var memDist = [];
-
- // iterate palette
- for (var i = 0; i < len; i++) {
- var pxi = idxrgb[i], i32i = idxi32[i];
- if (!pxi) continue;
-
- for (var j = i + 1; j < len; j++) {
- var pxj = idxrgb[j], i32j = idxi32[j];
- if (!pxj) continue;
-
- var dist = this.colorDist(pxi, pxj);
-
- if (dist < thold) {
- // store index,rgb,dist
- memDist.push([j, pxj, i32j, dist]);
-
- // kill squashed value
- delete(idxrgb[j]);
- palLen--;
- }
- }
- }
-
- // palette reduction pass
- // console.log("palette length: " + palLen);
-
- // if palette is still much larger than target, increment by larger initDist
- thold += (palLen > this.colors * 3) ? this.initDist : this.distIncr;
- }
-
- // if palette is over-reduced, re-add removed colors with largest distances from last round
- if (palLen < this.colors) {
- // sort descending
- sort.call(memDist, function(a,b) {
- return b[3] - a[3];
- });
-
- var k = 0;
- while (palLen < this.colors) {
- // re-inject rgb into final palette
- idxrgb[memDist[k][0]] = memDist[k][1];
-
- palLen++;
- k++;
- }
- }
- }
-
- var len = idxrgb.length;
- for (var i = 0; i < len; i++) {
- if (!idxrgb[i]) continue;
-
- this.idxrgb.push(idxrgb[i]);
- this.idxi32.push(idxi32[i]);
-
- this.i32idx[idxi32[i]] = this.idxi32.length - 1;
- this.i32rgb[idxi32[i]] = idxrgb[i];
- }
- }
- };
-
- // global top-population
- RgbQuant.prototype.colorStats1D = function colorStats1D(buf32) {
- var histG = this.histogram,
- num = 0, col,
- len = buf32.length;
-
- for (var i = 0; i < len; i++) {
- col = buf32[i];
-
- // skip transparent
- if ((col & 0xff000000) >> 24 == 0) continue;
-
- // collect hue stats
- if (this.hueStats)
- this.hueStats.check(col);
-
- if (col in histG)
- histG[col]++;
- else
- histG[col] = 1;
- }
- };
-
- // population threshold within subregions
- // FIXME: this can over-reduce (few/no colors same?), need a way to keep
- // important colors that dont ever reach local thresholds (gradients?)
- RgbQuant.prototype.colorStats2D = function colorStats2D(buf32, width) {
- var boxW = this.boxSize[0],
- boxH = this.boxSize[1],
- area = boxW * boxH,
- boxes = makeBoxes(width, buf32.length / width, boxW, boxH),
- histG = this.histogram,
- self = this;
-
- boxes.forEach(function(box) {
- var effc = Math.max(Math.round((box.w * box.h) / area) * self.boxPxls, 2),
- histL = {}, col;
-
- iterBox(box, width, function(i) {
- col = buf32[i];
-
- // skip transparent
- if ((col & 0xff000000) >> 24 == 0) return;
-
- // collect hue stats
- if (self.hueStats)
- self.hueStats.check(col);
-
- if (col in histG)
- histG[col]++;
- else if (col in histL) {
- if (++histL[col] >= effc)
- histG[col] = histL[col];
- }
- else
- histL[col] = 1;
- });
- });
-
- if (this.hueStats)
- this.hueStats.inject(histG);
- };
-
- // TODO: group very low lum and very high lum colors
- // TODO: pass custom sort order
- RgbQuant.prototype.sortPal = function sortPal() {
- var self = this;
-
- this.idxi32.sort(function(a,b) {
- var idxA = self.i32idx[a],
- idxB = self.i32idx[b],
- rgbA = self.idxrgb[idxA],
- rgbB = self.idxrgb[idxB];
-
- var hslA = rgb2hsl(rgbA[0],rgbA[1],rgbA[2]),
- hslB = rgb2hsl(rgbB[0],rgbB[1],rgbB[2]);
-
- // sort all grays + whites together
- var hueA = (rgbA[0] == rgbA[1] && rgbA[1] == rgbA[2]) ? -1 : hueGroup(hslA.h, self.hueGroups);
- var hueB = (rgbB[0] == rgbB[1] && rgbB[1] == rgbB[2]) ? -1 : hueGroup(hslB.h, self.hueGroups);
-
- var hueDiff = hueB - hueA;
- if (hueDiff) return -hueDiff;
-
- var lumDiff = lumGroup(+hslB.l.toFixed(2)) - lumGroup(+hslA.l.toFixed(2));
- if (lumDiff) return -lumDiff;
-
- var satDiff = satGroup(+hslB.s.toFixed(2)) - satGroup(+hslA.s.toFixed(2));
- if (satDiff) return -satDiff;
- });
-
- // sync idxrgb & i32idx
- this.idxi32.forEach(function(i32, i) {
- self.idxrgb[i] = self.i32rgb[i32];
- self.i32idx[i32] = i;
- });
- };
-
- // TOTRY: use HUSL - http://boronine.com/husl/
- RgbQuant.prototype.nearestColor = function nearestColor(i32) {
- var idx = this.nearestIndex(i32);
- return idx === null ? 0 : this.idxi32[idx];
- };
-
- // TOTRY: use HUSL - http://boronine.com/husl/
- RgbQuant.prototype.nearestIndex = function nearestIndex(i32) {
- // alpha 0 returns null index
- if ((i32 & 0xff000000) >> 24 == 0)
- return null;
-
- if (this.useCache && (""+i32) in this.i32idx)
- return this.i32idx[i32];
-
- var min = 1000,
- idx,
- rgb = [
- (i32 & 0xff),
- (i32 & 0xff00) >> 8,
- (i32 & 0xff0000) >> 16,
- ],
- len = this.idxrgb.length;
-
- for (var i = 0; i < len; i++) {
- if (!this.idxrgb[i]) continue; // sparse palettes
-
- var dist = this.colorDist(rgb, this.idxrgb[i]);
-
- if (dist < min) {
- min = dist;
- idx = i;
- }
- }
-
- return idx;
- };
-
- RgbQuant.prototype.cacheHistogram = function cacheHistogram(idxi32) {
- for (var i = 0, i32 = idxi32[i]; i < idxi32.length && this.histogram[i32] >= this.cacheFreq; i32 = idxi32[i++])
- this.i32idx[i32] = this.nearestIndex(i32);
- };
-
- function HueStats(numGroups, minCols) {
- this.numGroups = numGroups;
- this.minCols = minCols;
- this.stats = {};
-
- for (var i = -1; i < numGroups; i++)
- this.stats[i] = {num: 0, cols: []};
-
- this.groupsFull = 0;
- }
-
- HueStats.prototype.check = function checkHue(i32) {
- if (this.groupsFull == this.numGroups + 1)
- this.check = function() {return;};
-
- var r = (i32 & 0xff),
- g = (i32 & 0xff00) >> 8,
- b = (i32 & 0xff0000) >> 16,
- hg = (r == g && g == b) ? -1 : hueGroup(rgb2hsl(r,g,b).h, this.numGroups),
- gr = this.stats[hg],
- min = this.minCols;
-
- gr.num++;
-
- if (gr.num > min)
- return;
- if (gr.num == min)
- this.groupsFull++;
-
- if (gr.num <= min)
- this.stats[hg].cols.push(i32);
- };
-
- HueStats.prototype.inject = function injectHues(histG) {
- for (var i = -1; i < this.numGroups; i++) {
- if (this.stats[i].num <= this.minCols) {
- switch (typeOf(histG)) {
- case "Array":
- this.stats[i].cols.forEach(function(col){
- if (histG.indexOf(col) == -1)
- histG.push(col);
- });
- break;
- case "Object":
- this.stats[i].cols.forEach(function(col){
- if (!histG[col])
- histG[col] = 1;
- else
- histG[col]++;
- });
- break;
- }
- }
- }
- };
-
- // Rec. 709 (sRGB) luma coef
- var Pr = .2126,
- Pg = .7152,
- Pb = .0722;
-
- // http://alienryderflex.com/hsp.html
- function rgb2lum(r,g,b) {
- return Math.sqrt(
- Pr * r*r +
- Pg * g*g +
- Pb * b*b
- );
- }
-
- var rd = 255,
- gd = 255,
- bd = 255;
-
- var euclMax = Math.sqrt(Pr*rd*rd + Pg*gd*gd + Pb*bd*bd);
- // perceptual Euclidean color distance
- function distEuclidean(rgb0, rgb1) {
- var rd = rgb1[0]-rgb0[0],
- gd = rgb1[1]-rgb0[1],
- bd = rgb1[2]-rgb0[2];
-
- return Math.sqrt(Pr*rd*rd + Pg*gd*gd + Pb*bd*bd) / euclMax;
- }
-
- var manhMax = Pr*rd + Pg*gd + Pb*bd;
- // perceptual Manhattan color distance
- function distManhattan(rgb0, rgb1) {
- var rd = Math.abs(rgb1[0]-rgb0[0]),
- gd = Math.abs(rgb1[1]-rgb0[1]),
- bd = Math.abs(rgb1[2]-rgb0[2]);
-
- return (Pr*rd + Pg*gd + Pb*bd) / manhMax;
- }
-
- // http://rgb2hsl.nichabi.com/javascript-function.php
- function rgb2hsl(r, g, b) {
- var max, min, h, s, l, d;
- r /= 255;
- g /= 255;
- b /= 255;
- max = Math.max(r, g, b);
- min = Math.min(r, g, b);
- l = (max + min) / 2;
- if (max == min) {
- h = s = 0;
- } else {
- d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch (max) {
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break
- }
- h /= 6;
- }
-// h = Math.floor(h * 360)
-// s = Math.floor(s * 100)
-// l = Math.floor(l * 100)
- return {
- h: h,
- s: s,
- l: rgb2lum(r,g,b),
- };
- }
-
- function hueGroup(hue, segs) {
- var seg = 1/segs,
- haf = seg/2;
-
- if (hue >= 1 - haf || hue <= haf)
- return 0;
-
- for (var i = 1; i < segs; i++) {
- var mid = i*seg;
- if (hue >= mid - haf && hue <= mid + haf)
- return i;
- }
- }
-
- function satGroup(sat) {
- return sat;
- }
-
- function lumGroup(lum) {
- return lum;
- }
-
- function typeOf(val) {
- return Object.prototype.toString.call(val).slice(8,-1);
- }
-
- var sort = isArrSortStable() ? Array.prototype.sort : stableSort;
-
- // must be used via stableSort.call(arr, fn)
- function stableSort(fn) {
- var type = typeOf(this[0]);
-
- if (type == "Number" || type == "String") {
- var ord = {}, len = this.length, val;
-
- for (var i = 0; i < len; i++) {
- val = this[i];
- if (ord[val] || ord[val] === 0) continue;
- ord[val] = i;
- }
-
- return this.sort(function(a,b) {
- return fn(a,b) || ord[a] - ord[b];
- });
- }
- else {
- var ord = this.map(function(v){return v});
-
- return this.sort(function(a,b) {
- return fn(a,b) || ord.indexOf(a) - ord.indexOf(b);
- });
- }
- }
-
- // test if js engine's Array#sort implementation is stable
- function isArrSortStable() {
- var str = "abcdefghijklmnopqrstuvwxyz";
-
- return "xyzvwtursopqmnklhijfgdeabc" == str.split("").sort(function(a,b) {
- return ~~(str.indexOf(b)/2.3) - ~~(str.indexOf(a)/2.3);
- }).join("");
- }
-
- // returns uniform pixel data from various img
- // TODO?: if array is passed, createimagedata, createlement canvas? take a pxlen?
- function getImageData(img, width) {
- var can, ctx, imgd, buf8, buf32, height;
-
- switch (typeOf(img)) {
- case "HTMLImageElement":
- can = document.createElement("canvas");
- can.width = img.naturalWidth;
- can.height = img.naturalHeight;
- ctx = can.getContext("2d");
- ctx.drawImage(img,0,0);
- case "Canvas":
- case "HTMLCanvasElement":
- can = can || img;
- ctx = ctx || can.getContext("2d");
- case "CanvasRenderingContext2D":
- ctx = ctx || img;
- can = can || ctx.canvas;
- imgd = ctx.getImageData(0, 0, can.width, can.height);
- case "ImageData":
- imgd = imgd || img;
- width = imgd.width;
- if (typeOf(imgd.data) == "CanvasPixelArray")
- buf8 = new Uint8Array(imgd.data);
- else
- buf8 = imgd.data;
- case "Array":
- case "CanvasPixelArray":
- buf8 = buf8 || new Uint8Array(img);
- case "Uint8Array":
- case "Uint8ClampedArray":
- buf8 = buf8 || img;
- buf32 = new Uint32Array(buf8.buffer);
- case "Uint32Array":
- buf32 = buf32 || img;
- buf8 = buf8 || new Uint8Array(buf32.buffer);
- width = width || buf32.length;
- height = buf32.length / width;
- }
-
- return {
- can: can,
- ctx: ctx,
- imgd: imgd,
- buf8: buf8,
- buf32: buf32,
- width: width,
- height: height,
- };
- }
-
- // partitions a rect of wid x hgt into
- // array of bboxes of w0 x h0 (or less)
- function makeBoxes(wid, hgt, w0, h0) {
- var wnum = ~~(wid/w0), wrem = wid%w0,
- hnum = ~~(hgt/h0), hrem = hgt%h0,
- xend = wid-wrem, yend = hgt-hrem;
-
- var bxs = [];
- for (var y = 0; y < hgt; y += h0)
- for (var x = 0; x < wid; x += w0)
- bxs.push({x:x, y:y, w:(x==xend?wrem:w0), h:(y==yend?hrem:h0)});
-
- return bxs;
- }
-
- // iterates @bbox within a parent rect of width @wid; calls @fn, passing index within parent
- function iterBox(bbox, wid, fn) {
- var b = bbox,
- i0 = b.y * wid + b.x,
- i1 = (b.y + b.h - 1) * wid + (b.x + b.w - 1),
- cnt = 0, incr = wid - b.w + 1, i = i0;
-
- do {
- fn.call(this, i);
- i += (++cnt % b.w == 0) ? incr : 1;
- } while (i <= i1);
- }
-
- // returns array of hash keys sorted by their values
- function sortedHashKeys(obj, desc) {
- var keys = [];
-
- for (var key in obj)
- keys.push(key);
-
- return sort.call(keys, function(a,b) {
- return desc ? obj[b] - obj[a] : obj[a] - obj[b];
- });
- }
-
- // expose
- this.RgbQuant = RgbQuant;
-
- // expose to commonJS
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = RgbQuant;
- }
-
-}).call(this);
\ No newline at end of file
diff --git a/libs/rgbquant.min.js b/libs/rgbquant.min.js
new file mode 100644
index 00000000..1cc0104e
--- /dev/null
+++ b/libs/rgbquant.min.js
@@ -0,0 +1,2 @@
+// © 2015, Leon Sorokin, MIT
+(function(){function t(t){var s;t=t||{},this.method=t.method||2,this.colors=t.colors||256,this.initColors=t.initColors||4096,this.initDist=t.initDist||.01,this.distIncr=t.distIncr||.005,this.hueGroups=t.hueGroups||10,this.satGroups=t.satGroups||10,this.lumGroups=t.lumGroups||10,this.minHueCols=t.minHueCols||0,this.hueStats=this.minHueCols?new i(this.hueGroups,this.minHueCols):null,this.boxSize=t.boxSize||[64,64],this.boxPxls=t.boxPxls||2,this.palLocked=!1,this.dithKern=t.dithKern||null,this.dithSerp=t.dithSerp||!1,this.dithDelta=t.dithDelta||0,this.histogram={},this.idxrgb=t.palette?t.palette.slice(0):[],this.idxi32=[],this.i32idx={},this.i32rgb={},this.useCache=!1!==t.useCache,this.cacheFreq=t.cacheFreq||10,this.reIndex=t.reIndex||0==this.idxrgb.length,this.colorDist="manhattan"==t.colorDist?n:r,0>>0;s.idxi32[i]=r,s.i32idx[r]=i,s.i32rgb[r]=t})}function i(t,i){this.numGroups=t,this.minCols=i,this.stats={};for(var r=-1;r>8,b=(16711680&x)>>16,m=this.nearestColor(x),v=255&m,x=(65280&m)>>8,m=(16711680&m)>>16;if(h[f]=255<<24|m<<16|x<<8|v,this.dithDelta)if(this.colorDist([p,g,b],[v,x,m])>8,A=(16711680&h[D])>>16,I=Math.max(0,Math.min(255,I+y*M)),P=Math.max(0,Math.min(255,P+w*M)),M=Math.max(0,Math.min(255,A+S*M)),h[D]=255<<24|M<<16|P<<8|I)}}}return h},t.prototype.buildPal=function(t){if(!(this.palLocked||0this.colors){for(var i,r=t.length,s={},e=0,h=!1,n=0;n>8,(16711680&t)>>16]}),o=r=a.length,u=this.initDist;if(o>this.colors){for(;o>this.colors;){for(var l=[],n=0;n3*this.colors?this.initDist:this.distIncr}if(o>24!=0&&(this.hueStats&&this.hueStats.check(i),i in r?r[i]++:r[i]=1)},t.prototype.colorStats2D=function(e,h){var t=this.boxSize[0],i=this.boxSize[1],n=t*i,i=function(t,i,r,s){for(var e=t%r,h=i%s,n=t-e,a=i-h,o=[],u=0;u>24!=0&&(o.hueStats&&o.hueStats.check(i),i in a?a[i]++:i in s?++s[i]>=r&&(a[i]=s[i]):s[i]=1)})}),this.hueStats&&this.hueStats.inject(a)},t.prototype.sortPal=function(){var e=this;this.idxi32.sort(function(t,i){var r=e.i32idx[t],s=e.i32idx[i],t=e.idxrgb[r],i=e.idxrgb[s],r=a(t[0],t[1],t[2]),s=a(i[0],i[1],i[2]),t=t[0]==t[1]&&t[1]==t[2]?-1:c(r.h,e.hueGroups),t=(i[0]==i[1]&&i[1]==i[2]?-1:c(s.h,e.hueGroups))-t;if(t)return-t;t=+s.l.toFixed(2)-+r.l.toFixed(2);if(t)return-t;r=+s.s.toFixed(2)-+r.s.toFixed(2);return r?-r:void 0}),this.idxi32.forEach(function(t,i){e.idxrgb[i]=e.i32rgb[t],e.i32idx[t]=i})},t.prototype.nearestColor=function(t){t=this.nearestIndex(t);return null===t?0:this.idxi32[t]},t.prototype.nearestIndex=function(t){if((4278190080&t)>>24==0)return null;if(this.useCache&&""+t in this.i32idx)return this.i32idx[t];for(var i,r,s=1e3,e=[255&t,(65280&t)>>8,(16711680&t)>>16],h=this.idxrgb.length,n=0;n=this.cacheFreq;r=t[i++])this.i32idx[r]=this.nearestIndex(r)},i.prototype.check=function(t){this.groupsFull==this.numGroups+1&&(this.check=function(){});var i=255&t,r=(65280&t)>>8,s=(16711680&t)>>16,i=i==r&&r==s?-1:c(a(i,r,s).h,this.numGroups),r=this.stats[i],s=this.minCols;r.num++,r.num>s||(r.num==s&&this.groupsFull++,r.num<=s&&this.stats[i].cols.push(t))},i.prototype.inject=function(i){for(var t=-1;t>>=1;return(n+t)/r};return m.int32=function(){return 0|p.g(4)},m.quick=function(){return p.g(4)/4294967296},m.double=m,q(s(p.S),a),(t.pass||e||function(n,r,t,e){return e&&(e.S&&o(e,p),n.state=function(){return o(p,{})}),t?(b[g]=n,r):n})(m,f,"global"in t?t.global:this==b,t.state)}function n(n){var r,t=n.length,u=this,e=0,o=u.i=u.j=0,i=u.S=[];for(t||(n=[t++]);e {
s.provinces = [];
if (!s.i || s.removed) return;
diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js
index c54ce994..3fcf8170 100644
--- a/modules/cultures-generator.js
+++ b/modules/cultures-generator.js
@@ -388,14 +388,9 @@
if (cells.s[e] > 0) cells.culture[e] = c; // assign culture to populated cell
cost[e] = totalCost;
queue.queue({e, p:totalCost, c});
-
- //debug.append("text").attr("x", (cells.p[n][0]+cells.p[e][0])/2 - 1).attr("y", (cells.p[n][1]+cells.p[e][1])/2 - 1).text(rn(totalCost-p)).attr("font-size", .8);
- //const points = [cells.p[n][0], cells.p[n][1], (cells.p[n][0]+cells.p[e][0])/2, (cells.p[n][1]+cells.p[e][1])/2, cells.p[e][0], cells.p[e][1]];
- //debug.append("polyline").attr("points", points.toString()).attr("marker-mid", "url(#arrow)").attr("opacity", .6);
}
});
}
- //debug.selectAll(".text").data(cost).enter().append("text").attr("x", (d, e) => cells.p[e][0]-1).attr("y", (d, e) => cells.p[e][1]-1).text(d => d ? rn(d) : "").attr("font-size", 2);
TIME && console.timeEnd('expandCultures');
}
diff --git a/modules/river-generator.js b/modules/river-generator.js
index 69e3c571..e0c5cdbe 100644
--- a/modules/river-generator.js
+++ b/modules/river-generator.js
@@ -6,7 +6,7 @@
const generate = function(changeHeights = true) {
TIME && console.time('generateRivers');
- Math.seedrandom(seed);
+ Math.random = aleaPRNG(seed);
const cells = pack.cells, p = cells.p, features = pack.features;
// build distance field in cells from water (cells.t)
@@ -250,7 +250,7 @@
const specify = function() {
if (!pack.rivers.length) return;
- Math.seedrandom(seed);
+ Math.random = aleaPRNG(seed);
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
const smallType = {"Creek":9, "River":3, "Brook":3, "Stream":1}; // weighted small river types
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 1018e2b6..8cf4a2c4 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -475,7 +475,7 @@ function toggleIce(event) {
function drawIce() {
const cells = grid.cells, vertices = grid.vertices, n = cells.i.length, temp = cells.temp, h = cells.h;
const used = new Uint8Array(cells.i.length);
- Math.seedrandom(seed);
+ Math.random = aleaPRNG(seed);
const shieldMin = -6; // max temp to form ice shield (glacier)
const icebergMax = 2; // max temp to form an iceberg
diff --git a/modules/ui/options.js b/modules/ui/options.js
index 9efbfaa8..50473d62 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -389,7 +389,7 @@ function applyStoredOptions() {
// randomize options if randomization is allowed (not locked or options='default')
function randomizeOptions() {
- Math.seedrandom(seed); // reset seed to initial one
+ Math.random = aleaPRNG(seed); // reset seed to initial one
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
// 'Options' settings
diff --git a/modules/ui/tools.js b/modules/ui/tools.js
index 6c648024..d41fbc91 100644
--- a/modules/ui/tools.js
+++ b/modules/ui/tools.js
@@ -97,7 +97,8 @@ function recalculatePopulation() {
}
function regenerateStates() {
- Math.seedrandom(Math.floor(Math.random() * 1e9)); // new random seed
+ const localSeed = Math.floor(Math.random() * 1e9); // new random seed
+ Math.random = aleaPRNG(localSeed);
const burgs = pack.burgs.filter(b => b.i && !b.removed);
if (!burgs.length) {
tip("No burgs to generate states. Please create burgs first", false, "error");