diff options
| author | bozo.kopic <bozo.kopic@gmail.com> | 2019-04-08 22:47:10 +0200 |
|---|---|---|
| committer | bozo.kopic <bozo.kopic@gmail.com> | 2019-04-08 22:47:10 +0200 |
| commit | f53913389fa435d26307075bf5dab3675b1f17c4 (patch) | |
| tree | f70d5fd95070b6f1f5f427d2d43da7f77d000efa /src_js | |
| parent | 07dae145e814856c8f38f407d91a033b233c081d (diff) | |
dependencies update & minor fixes
Diffstat (limited to 'src_js')
| -rw-r--r-- | src_js/opcut/common.js | 17 | ||||
| -rw-r--r-- | src_js/opcut/fs.js | 144 | ||||
| -rw-r--r-- | src_js/opcut/future.js | 2 | ||||
| -rw-r--r-- | src_js/opcut/main.js | 1 | ||||
| -rw-r--r-- | src_js/opcut/renderer.js | 360 | ||||
| -rw-r--r-- | src_js/opcut/util.js | 968 | ||||
| -rw-r--r-- | src_js/opcut/validators.js | 126 | ||||
| -rw-r--r-- | src_js/opcut/vt.js | 4 |
8 files changed, 836 insertions, 786 deletions
diff --git a/src_js/opcut/common.js b/src_js/opcut/common.js index 48ba8d0..1a8562c 100644 --- a/src_js/opcut/common.js +++ b/src_js/opcut/common.js @@ -65,8 +65,7 @@ function createValidateRequest() { if (name in panels) { throw 'Duplicate panel name ' + name; } - panels[name] = {width: width, - height: height}; + panels[name] = {width: width, height: height}; } } if (u.equals(panels, {})) @@ -90,9 +89,11 @@ function createValidateRequest() { if (name in items) { throw 'Duplicate item name ' + name; } - items[name] = {width: width, - height: height, - can_rotate: item.can_rotate}; + items[name] = { + width: width, + height: height, + can_rotate: item.can_rotate + }; } } if (u.equals(items, {})) @@ -105,7 +106,7 @@ function createValidateRequest() { panels: panels, items: items } - } + }; } @@ -123,12 +124,12 @@ function parseCalculateResponse(msg) { function parseGenerateOutputResponse(msg, output_type) { - if (msg.data) { + if (output_type == 'PDF' && msg.data) { const fileName = 'output.pdf'; fs.saveB64Data(msg.data, fileName); } else { showNotification('Error generating output', 'error'); - }; + } } diff --git a/src_js/opcut/fs.js b/src_js/opcut/fs.js index f96485b..e748181 100644 --- a/src_js/opcut/fs.js +++ b/src_js/opcut/fs.js @@ -1,72 +1,72 @@ -import h from 'hyperscript';
-import FileSaver from 'file-saver';
-
-import * as u from 'opcut/util';
-import * as ev from 'opcut/ev';
-
-
-export function loadText(ext) {
- const el = h('input', {
- style: 'display: none',
- type: 'file',
- accept: ext});
- const promise = new Promise(resolve => {
- ev.on(el, 'change', evt => {
- const file = u.get(['files', 0], evt.target);
- if (!file)
- return;
- const fileReader = new FileReader();
- fileReader.onload = () => {
- const data = fileReader.result;
- resolve(data);
- };
- fileReader.readAsText(file);
- });
- el.click();
- });
- return promise;
-}
-
-
-export function saveText(text, fileName) {
- const blob = stringToBlob(text);
- FileSaver.saveAs(blob, fileName);
-}
-
-
-export function saveB64Data(b64Data, fileName) {
- const blob = b64ToBlob(b64Data);
- FileSaver.saveAs(blob, fileName);
-}
-
-
-function stringToBlob(strData, contentType) {
- contentType = contentType || '';
- return new Blob([strData], {type: contentType});
-}
-
-
-// http://stackoverflow.com/a/16245768
-function b64ToBlob(b64Data, contentType, sliceSize) {
- contentType = contentType || '';
- sliceSize = sliceSize || 512;
-
- var byteCharacters = atob(b64Data);
- var byteArrays = [];
-
- for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
- var slice = byteCharacters.slice(offset, offset + sliceSize);
-
- var byteNumbers = new Array(slice.length);
- for (var i = 0; i < slice.length; i++) {
- byteNumbers[i] = slice.charCodeAt(i);
- }
-
- var byteArray = new Uint8Array(byteNumbers);
-
- byteArrays.push(byteArray);
- }
-
- var blob = new Blob(byteArrays, {type: contentType});
- return blob;
-}
+import h from 'hyperscript'; +import FileSaver from 'file-saver'; + +import * as u from 'opcut/util'; +import * as ev from 'opcut/ev'; + + +export function loadText(ext) { + const el = h('input', { + style: 'display: none', + type: 'file', + accept: ext}); + const promise = new Promise(resolve => { + ev.on(el, 'change', evt => { + const file = u.get(['files', 0], evt.target); + if (!file) + return; + const fileReader = new FileReader(); + fileReader.onload = () => { + const data = fileReader.result; + resolve(data); + }; + fileReader.readAsText(file); + }); + el.click(); + }); + return promise; +} + + +export function saveText(text, fileName) { + const blob = stringToBlob(text); + FileSaver.saveAs(blob, fileName); +} + + +export function saveB64Data(b64Data, fileName) { + const blob = b64ToBlob(b64Data); + FileSaver.saveAs(blob, fileName); +} + + +function stringToBlob(strData, contentType) { + contentType = contentType || ''; + return new Blob([strData], {type: contentType}); +} + + +// http://stackoverflow.com/a/16245768 +function b64ToBlob(b64Data, contentType, sliceSize) { + contentType = contentType || ''; + sliceSize = sliceSize || 512; + + var byteCharacters = atob(b64Data); + var byteArrays = []; + + for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { + var slice = byteCharacters.slice(offset, offset + sliceSize); + + var byteNumbers = new Array(slice.length); + for (var i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + } + + var blob = new Blob(byteArrays, {type: contentType}); + return blob; +} diff --git a/src_js/opcut/future.js b/src_js/opcut/future.js index a435e89..22d4b1f 100644 --- a/src_js/opcut/future.js +++ b/src_js/opcut/future.js @@ -1,3 +1,5 @@ +/** @module opcut/future */ + export function create() { let data = { diff --git a/src_js/opcut/main.js b/src_js/opcut/main.js index 7014798..a6ea938 100644 --- a/src_js/opcut/main.js +++ b/src_js/opcut/main.js @@ -15,3 +15,4 @@ function main() { ev.on(window, 'load', main); window.r = r; +window.u = u; diff --git a/src_js/opcut/renderer.js b/src_js/opcut/renderer.js index 451d6cf..378b3c0 100644 --- a/src_js/opcut/renderer.js +++ b/src_js/opcut/renderer.js @@ -1,180 +1,180 @@ -/** @module opcut/renderer2 */
-
-import * as snabbdom from 'snabbdom/es/snabbdom';
-import snabbdomAttributes from 'snabbdom/es/modules/attributes';
-import snabbdomClass from 'snabbdom/es/modules/class';
-import snabbdomProps from 'snabbdom/es/modules/props';
-// import snabbdomStyle from 'snabbdom/es/modules/style';
-import snabbdomDataset from 'snabbdom/es/modules/dataset';
-import snabbdomEvent from 'snabbdom/es/modules/eventlisteners';
-
-import * as u from 'opcut/util';
-import * as ev from 'opcut/ev';
-
-
-const patch = snabbdom.init([
- snabbdomAttributes,
- snabbdomClass,
- snabbdomProps,
- // snabbdomStyle,
- snabbdomDataset,
- snabbdomEvent
-]);
-
-
-function vhFromArray(node) {
- if (!node)
- return [];
- if (u.isString(node))
- return node;
- if (!u.isArray(node))
- throw 'Invalid node structure';
- if (node.length < 1)
- return [];
- if (typeof node[0] != 'string')
- return node.map(vhFromArray);
- const hasData = node.length > 1 && u.isObject(node[1]);
- const children = u.pipe(
- u.map(vhFromArray),
- u.flatten,
- Array.from
- )(node.slice(hasData ? 2 : 1));
- const result = hasData ?
- snabbdom.h(node[0], node[1], children) :
- snabbdom.h(node[0], children);
- return result;
-}
-
-/**
- * Virtual DOM renderer
- */
-export class Renderer {
-
- /**
- * Calls `init` method
- * @param {HTMLElement} [el=document.body]
- * @param {Any} [initState=null]
- * @param {Function} [vtCb=null]
- * @param {Number} [maxFps=30]
- */
- constructor(el, initState, vtCb, maxFps) {
- this.init(el, initState, vtCb, maxFps);
- }
-
- /**
- * Initialize renderer
- * @param {HTMLElement} [el=document.body]
- * @param {Any} [initState=null]
- * @param {Function} [vtCb=null]
- * @param {Number} [maxFps=30]
- * @return {Promise}
- */
- init(el, initState, vtCb, maxFps) {
- this._state = null;
- this._changes = [];
- this._promise = null;
- this._timeout = null;
- this._lastRender = null;
- this._vtCb = vtCb;
- this._maxFps = u.isNumber(maxFps) ? maxFps : 30;
- this._vNode = el || document.querySelector('body');
- if (initState)
- return this.change(_ => initState);
- return new Promise(resolve => { resolve(); });
- }
-
- /**
- * Get current state value referenced by `paths`
- * @param {...Path} paths
- * @return {Any}
- */
- get(...paths) {
- return u.get(paths, this._state);
- }
-
- /**
- * Change current state value referenced by `path`
- * @param {Path} path
- * @param {Any} value
- * @return {Promise}
- */
- set(path, value) {
- if (arguments.length < 2) {
- value = path;
- path = [];
- }
- return this.change(path, _ => value);
- }
-
- /**
- * Change current state value referenced by `path`
- * @param {Path} path
- * @param {Function} cb
- * @return {Promise}
- */
- change(path, cb) {
- if (arguments.length < 2) {
- cb = path;
- path = [];
- }
- this._changes.push([path, cb]);
- if (this._promise)
- return this._promise;
- this._promise = new Promise((resolve, reject) => {
- setTimeout(() => {
- try {
- this._change();
- } catch(e) {
- this._promise = null;
- reject(e);
- throw e;
- }
- this._promise = null;
- resolve();
- }, 0);
- });
- return this._promise;
- }
-
- _change() {
- let change = false;
- while (this._changes.length > 0) {
- const [path, cb] = this._changes.shift();
- const view = u.get(path);
- const oldState = this._state;
- this._state = u.change(path, cb, this._state);
- if (this._state && u.equals(view(oldState),
- view(this._state)))
- continue;
- change = true;
- if (!this._vtCb || this._timeout)
- continue;
- const delay = (!this._lastRender || !this._maxFps ?
- 0 :
- (1000 / this._maxFps) -
- (performance.now() - this._lastRender));
- this._timeout = setTimeout(() => {
- this._timeout = null;
- this._lastRender = performance.now();
- const vNode = vhFromArray(this._vtCb(this));
- patch(this._vNode, vNode);
- this._vNode = vNode;
- ev.fire(this, 'render', [this._state]);
- }, (delay > 0 ? delay : 0));
- }
- if (change)
- ev.fire(this, 'change', [this._state]);
- }
-
-}
-// Renderer.prototype.set = u.curry(Renderer.prototype.set);
-// Renderer.prototype.change = u.curry(Renderer.prototype.change);
-
-
-/**
- * Default renderer
- * @static
- * @type {Renderer}
- */
-const defaultRenderer = new Renderer();
-export default defaultRenderer;
+/** @module opcut/renderer2 */ + +import * as snabbdom from 'snabbdom/es/snabbdom'; +import snabbdomAttributes from 'snabbdom/es/modules/attributes'; +import snabbdomClass from 'snabbdom/es/modules/class'; +import snabbdomProps from 'snabbdom/es/modules/props'; +// import snabbdomStyle from 'snabbdom/es/modules/style'; +import snabbdomDataset from 'snabbdom/es/modules/dataset'; +import snabbdomEvent from 'snabbdom/es/modules/eventlisteners'; + +import * as u from 'opcut/util'; +import * as ev from 'opcut/ev'; + + +const patch = snabbdom.init([ + snabbdomAttributes, + snabbdomClass, + snabbdomProps, + // snabbdomStyle, + snabbdomDataset, + snabbdomEvent +]); + + +function vhFromArray(node) { + if (!node) + return []; + if (u.isString(node)) + return node; + if (!u.isArray(node)) + throw 'Invalid node structure'; + if (node.length < 1) + return []; + if (typeof node[0] != 'string') + return node.map(vhFromArray); + const hasData = node.length > 1 && u.isObject(node[1]); + const children = u.pipe( + u.map(vhFromArray), + u.flatten, + Array.from + )(node.slice(hasData ? 2 : 1)); + const result = hasData ? + snabbdom.h(node[0], node[1], children) : + snabbdom.h(node[0], children); + return result; +} + +/** + * Virtual DOM renderer + */ +export class Renderer { + + /** + * Calls `init` method + * @param {HTMLElement} [el=document.body] + * @param {Any} [initState=null] + * @param {Function} [vtCb=null] + * @param {Number} [maxFps=30] + */ + constructor(el, initState, vtCb, maxFps) { + this.init(el, initState, vtCb, maxFps); + } + + /** + * Initialize renderer + * @param {HTMLElement} [el=document.body] + * @param {Any} [initState=null] + * @param {Function} [vtCb=null] + * @param {Number} [maxFps=30] + * @return {Promise} + */ + init(el, initState, vtCb, maxFps) { + this._state = null; + this._changes = []; + this._promise = null; + this._timeout = null; + this._lastRender = null; + this._vtCb = vtCb; + this._maxFps = u.isNumber(maxFps) ? maxFps : 30; + this._vNode = el || document.querySelector('body'); + if (initState) + return this.change(_ => initState); + return new Promise(resolve => { resolve(); }); + } + + /** + * Get current state value referenced by `paths` + * @param {...Path} paths + * @return {Any} + */ + get(...paths) { + return u.get(paths, this._state); + } + + /** + * Change current state value referenced by `path` + * @param {Path} path + * @param {Any} value + * @return {Promise} + */ + set(path, value) { + if (arguments.length < 2) { + value = path; + path = []; + } + return this.change(path, _ => value); + } + + /** + * Change current state value referenced by `path` + * @param {Path} path + * @param {Function} cb + * @return {Promise} + */ + change(path, cb) { + if (arguments.length < 2) { + cb = path; + path = []; + } + this._changes.push([path, cb]); + if (this._promise) + return this._promise; + this._promise = new Promise((resolve, reject) => { + setTimeout(() => { + try { + this._change(); + } catch(e) { + this._promise = null; + reject(e); + throw e; + } + this._promise = null; + resolve(); + }, 0); + }); + return this._promise; + } + + _change() { + let change = false; + while (this._changes.length > 0) { + const [path, cb] = this._changes.shift(); + const view = u.get(path); + const oldState = this._state; + this._state = u.change(path, cb, this._state); + if (this._state && u.equals(view(oldState), + view(this._state))) + continue; + change = true; + if (!this._vtCb || this._timeout) + continue; + const delay = (!this._lastRender || !this._maxFps ? + 0 : + (1000 / this._maxFps) - + (performance.now() - this._lastRender)); + this._timeout = setTimeout(() => { + this._timeout = null; + this._lastRender = performance.now(); + const vNode = vhFromArray(this._vtCb(this)); + patch(this._vNode, vNode); + this._vNode = vNode; + ev.fire(this, 'render', [this._state]); + }, (delay > 0 ? delay : 0)); + } + if (change) + ev.fire(this, 'change', [this._state]); + } + +} +// Renderer.prototype.set = u.curry(Renderer.prototype.set); +// Renderer.prototype.change = u.curry(Renderer.prototype.change); + + +/** + * Default renderer + * @static + * @type {Renderer} + */ +const defaultRenderer = new Renderer(); +export default defaultRenderer; diff --git a/src_js/opcut/util.js b/src_js/opcut/util.js index 6f5526a..a7aa166 100644 --- a/src_js/opcut/util.js +++ b/src_js/opcut/util.js @@ -1,461 +1,507 @@ -/** @module opcut/util */
-
-/**
- * Identity function
- * @function
- * @param {Any} obj input object
- * @return {Any} same object as input
- */
-export const identity = obj => obj;
-
-/**
- * Check if value is Array (wrapper for Array.isArray)
- * @function
- * @param {Any} arr input object
- * @return {Boolean}
- */
-export const isArray = Array.isArray;
-
-/**
- * Check if value is Object (not `true` from Array or `null`)
- * @function
- * @param {Any} obj input object
- * @return {Boolean}
- */
-export const isObject = obj => obj !== null &&
- typeof(obj) == 'object' &&
- !isArray(obj);
-
-/**
- * Check if value is number
- * @function
- * @param {Any} n input object
- * @return {Boolean}
- */
-export const isNumber = n => typeof(n) == 'number';
-
-/**
- * Check if value is integer
- * @function
- * @param {Any} n input object
- * @type {Boolean}
- */
-export const isInteger = Number.isInteger;
-
-/**
- * Check if value is string
- * @function
- * @param {Any} str input object
- * @type {Boolean}
- */
-export const isString = str => typeof(str) == 'string';
-
-/**
- * Strictly parse integer from string
- * @param {String} value
- * @return {Number}
- */
-export function strictParseInt(value) {
- if (/^(-|\+)?([0-9]+)$/.test(value))
- return Number(value);
- return NaN;
-}
-
-/**
- * Strictly parse float from string
- * @param {String} value
- * @return {Number}
- */
-export function strictParseFloat(value) {
- if (/^(-|\+)?([0-9]+(\.[0-9]+)?)$/.test(value))
- return Number(value);
- return NaN;
-}
-
-/**
- * Create new deep copy of input value
- * @param {Any} value
- * @return {Any} copy of value
- */
-export function clone(obj) {
- if (isArray(obj))
- return Array.from(obj, clone);
- if (isObject(obj)) {
- let ret = {};
- for (let i in obj)
- ret[i] = clone(obj[i]);
- return ret;
- }
- return obj;
-}
-
-/**
- * Combine two arrays in single array of pairs
- * @param {Array<Any>} arr1
- * @param {Array<Any>} arr2
- * @return {Array<Array<Any>>}
- */
-export function zip(arr1, arr2) {
- return Array.from((function*() {
- for (let i = 0; i < arr1.length || i < arr2.length; ++i)
- yield [arr1[i], arr2[i]];
- })());
-}
-
-/**
- * Convert object to array of key, value pairs
- * @param {Object} obj
- * @return {Array<Array>}
- */
-export function toPairs(obj) {
- return Object.entries(obj);
-}
-
-/**
- * Convert array of key, value pairs to object
- * @param {Array<Array>} arr
- * @return {Object}
- */
-export function fromPairs(arr) {
- let ret = {};
- for (let [k, v] of arr)
- ret[k] = v;
- return ret;
-}
-
-/**
- * Flatten nested arrays
- * @param {Array} arr
- * @return {Generator}
- */
-export function* flatten(arr) {
- if (isArray(arr)) {
- for (let i of arr)
- if (isArray(i))
- yield* flatten(i);
- else
- yield i;
- } else {
- yield arr;
- }
-}
-
-/**
- * Pipe function calls (functional composition with reversed order)
- * @param {...Function} fns functions
- * @return {Function}
- */
-export function pipe(...fns) {
- if (fns.length < 1)
- throw 'no functions';
- return function (...args) {
- let ret = fns[0].apply(this, args);
- for (let fn of fns.slice(1))
- ret = fn(ret);
- return ret;
- };
-}
-
-/**
- * Curry function with fixed arguments lenth
- * @param {Function} fn
- * @return {Function}
- */
-export function curry(fn) {
- let wrapper = function(oldArgs) {
- return function(...args) {
- args = oldArgs.concat(args);
- if (args.length >= fn.length)
- return fn(...args);
- return wrapper(args);
- };
- };
- return wrapper([]);
-}
-
-/**
- * Deep object equality
- * (curried function)
- * @function
- * @param {Any} x
- * @param {Any} y
- * @return {Boolean}
- */
-export const equals = curry((x, y) => {
- if (x === y)
- return true;
- if (typeof(x) != 'object' ||
- typeof(y) != 'object' ||
- x === null ||
- y === null)
- return false;
- if (Array.isArray(x) || Array.isArray(y)) {
- if (!Array.isArray(x) || !Array.isArray(y) || x.length != y.length)
- return false;
- }
- for (let i in x)
- if (!equals(x[i], y[i]))
- return false;
- for (let i in y)
- if (!equals(x[i], y[i]))
- return false;
- return true;
-});
-
-/**
- * Get value from `obj` referenced by `path`
- * (curried function)
- * @function
- * @param {Path} path
- * @param {Any} obj
- * @return {Any}
- */
-export const get = curry((path, obj) => {
- let ret = obj;
- for (let i of flatten(path)) {
- if (ret === null || typeof(ret) != 'object')
- return undefined;
- ret = ret[i];
- }
- return ret;
-});
-
-/**
- * Change `obj` by appling function `fn` to value referenced by `path`
- * (curried function)
- * @function
- * @param {Path} path
- * @param {Function} fn
- * @param {Any} obj
- * @return {Any} changed `obj`
- */
-export const change = curry((path, fn, obj) => {
- function _change(path, obj) {
- if (isInteger(path[0])) {
- obj = (isArray(obj) ? Array.from(obj) : []);
- } else if (isString(path[0])) {
- obj = (isObject(obj) ? Object.assign({}, obj) : {});
- } else {
- throw 'invalid path';
- }
- if (path.length > 1) {
- obj[path[0]] = _change(path.slice(1), obj[path[0]]);
- } else {
- obj[path[0]] = fn(obj[path[0]]);
- }
- return obj;
- }
- path = Array.from(flatten(path));
- if (path.length < 1)
- return fn(obj);
- return _change(path, obj);
-});
-
-/**
- * Change `obj` by setting value referenced by `path` to `val`
- * (curried function)
- * @function
- * @param {Path} path
- * @param {Any} val
- * @param {Any} obj
- * @return {Any} changed `obj`
- */
-export const set = curry((path, val, obj) => change(path, _ => val, obj));
-
-/**
- * Change `obj` by omitting value referenced by `path`
- * (curried function)
- * @function
- * @param {Path} path
- * @param {Any} obj
- * @return {Any} changed `obj`
- */
-export const omit = curry((path, obj) => {
- function _omit(path, obj) {
- if (isInteger(path[0])) {
- obj = (isArray(obj) ? Array.from(obj) : []);
- } else if (isString(path[0])) {
- obj = (isObject(obj) ? Object.assign({}, obj) : {});
- } else {
- throw 'invalid path';
- }
- if (path.length > 1) {
- obj[path[0]] = _omit(path.slice(1), obj[path[0]]);
- } else if (isInteger(path[0])) {
- obj.splice(path[0], 1);
- } else {
- delete obj[path[0]];
- }
- return obj;
- }
- path = Array.from(flatten(path));
- if (path.length < 1)
- return undefined;
- return _omit(path, obj);
-});
-
-/**
- * Sort `arr` by with comparison function `fn`
- * (curried function)
- * @function
- * @param {Function} fn
- * @param {Array} arr
- * @return {Array} sorted `arr`
- */
-export const sortBy = curry((fn, arr) => Array.from(arr).sort((x, y) => {
- let xVal = fn(x);
- let yVal = fn(y);
- if (xVal < yVal)
- return -1;
- if (xVal > yVal)
- return 1;
- return 0;
-}));
-
-/**
- * Create object which is subset `obj` containing only properties defined by
- * `arr`
- * (curried function)
- * @function
- * @param {Array} arr
- * @param {Object} obj
- * @return {Object} subset of `obj`
- */
-export const pick = curry((arr, obj) => {
- const ret = {};
- for (let i of arr)
- if (i in obj)
- ret[i] = obj[i];
- return ret;
-});
-
-/**
- * Change `arr` by appling function `fn` to it's elements
- * (curried function)
- * @function
- * @param {Function} fn
- * @param {Array} arr
- * @return {Array} modified `arr`
- */
-export const map = curry((fn, arr) => isArray(arr) ?
- arr.map(fn) :
- pipe(toPairs,
- x => x.map(([k, v]) => [k, fn(v)]),
- fromPairs)(arr));
-
-/**
- * Change `arr` to contain only elements fow which function `fn` returns `true`
- * (curried function)
- * @function
- * @param {Function} fn
- * @param {Array} arr
- * @return {Array} filtered `arr`
- */
-export const filter = curry((fn, arr) => arr.filter(fn));
-
-/**
- * Append `val` to end of `arr`
- * (curried function)
- * @function
- * @param {Any} val
- * @param {Array} arr
- * @return {Array} `arr` with appended `val`
- */
-export const append = curry((val, arr) => arr.concat([val]));
-
-/**
- * Reduce `arr` values by appling function `fn`
- * (curried function)
- * @function
- * @param {Function} fn
- * @param {Any} val initial accumulator value
- * @param {Array} arr
- * @return {Any} reduced value
- */
-export const reduce = curry((fn, val, arr) => arr.reduce(fn, val));
-
-/**
- * Merge two objects
- * (curried function)
- * @function
- * @param {Object} obj1
- * @param {Object} obj2
- * @return {Object} combined `obj1` and `obj2`
- */
-export const merge = curry((obj1, obj2) => Object.assign({}, obj1, obj2));
-
-/**
- * Merge multiple objects
- * (curried function)
- * @function
- * @param {...Object} objs
- * @return {Object} combined `objs`
- */
-export const mergeAll = reduce(merge, {});
-
-/**
- * Find element in `arr` for which function `fn` returns `true`
- * (curried function)
- * @function
- * @param {Function} fn
- * @param {Array} arr
- * @return {Any}
- */
-export const find = curry((fn, arr) => arr.find(fn));
-
-/**
- * Concatenate two arrays
- * (curried function)
- * @function
- * @param {Array} arr1
- * @param {Array} arr2
- * @return {Array} concatenated `arr1` and `arr2`
- */
-export const concat = curry((arr1, arr2) => arr1.concat(arr2));
-
-/**
- * Check if `arr` contains `val`
- * (curried function)
- * @function
- * @param {Any} val
- * @param {Array} arr
- * @return {Boolean}
- */
-export const contains = curry((val, arr) => arr.includes(val));
-
-/**
- * Insert `val` into `arr` on index `idx`
- * (curried function)
- * @function
- * @param {Number} idx
- * @param {Any} val
- * @param {Array} arr
- * @return {Array}
- */
-// TODO: Array.from(arr).splice(idx, 0, val) not working?
-export const insert = curry((idx, val, arr) =>
- arr.slice(0, idx).concat([val], arr.slice(idx)));
-
-/**
- * Create promise that resolves in `t` milliseconds
- * @param {Number} t
- * @return {Promise}
- */
-export function sleep(t) {
- return new Promise(resolve => {
- setTimeout(() => { resolve(); }, t);
- });
-}
-
-/**
- * Delay function call `fn(...args)` for `t` milliseconds
- * @param {Function} fn
- * @param {Number} [t=0]
- * @param {...Any} args
- * @return {Promise}
- */
-export function delay(fn, t, ...args) {
- return new Promise(resolve => {
- setTimeout(() => { resolve(fn(...args)); }, t || 0);
- });
-}
+/** @module opcut/util */ + +/** + * Identity function + * @function + * @param {Any} obj input object + * @return {Any} same object as input + */ +export const identity = obj => obj; + +/** + * Check if value is Array (wrapper for Array.isArray) + * @function + * @param {Any} arr input object + * @return {Boolean} + */ +export const isArray = Array.isArray; + +/** + * Check if value is Object (not `true` from Array or `null`) + * @function + * @param {Any} obj input object + * @return {Boolean} + */ +export const isObject = obj => obj !== null && + typeof(obj) == 'object' && + !isArray(obj); + +/** + * Check if value is number + * @function + * @param {Any} n input object + * @return {Boolean} + */ +export const isNumber = n => typeof(n) == 'number'; + +/** + * Check if value is integer + * @function + * @param {Any} n input object + * @type {Boolean} + */ +export const isInteger = Number.isInteger; + +/** + * Check if value is string + * @function + * @param {Any} str input object + * @type {Boolean} + */ +export const isString = str => typeof(str) == 'string'; + +/** + * Strictly parse integer from string + * @param {String} value + * @return {Number} + */ +export function strictParseInt(value) { + if (/^(-|\+)?([0-9]+)$/.test(value)) + return Number(value); + return NaN; +} + +/** + * Strictly parse float from string + * @param {String} value + * @return {Number} + */ +export function strictParseFloat(value) { + if (/^(-|\+)?([0-9]+(\.[0-9]+)?)$/.test(value)) + return Number(value); + return NaN; +} + +/** + * Create new deep copy of input value + * @param {Any} value + * @return {Any} copy of value + */ +export function clone(obj) { + if (isArray(obj)) + return Array.from(obj, clone); + if (isObject(obj)) { + let ret = {}; + for (let i in obj) + ret[i] = clone(obj[i]); + return ret; + } + return obj; +} + +/** + * Combine two arrays in single array of pairs + * @param {Array<Any>} arr1 + * @param {Array<Any>} arr2 + * @return {Array<Array<Any>>} + */ +export function zip(arr1, arr2) { + return Array.from((function*() { + for (let i = 0; i < arr1.length || i < arr2.length; ++i) + yield [arr1[i], arr2[i]]; + })()); +} + +/** + * Convert object to array of key, value pairs + * @param {Object} obj + * @return {Array<Array>} + */ +export function toPairs(obj) { + return Object.entries(obj); +} + +/** + * Convert array of key, value pairs to object + * @param {Array<Array>} arr + * @return {Object} + */ +export function fromPairs(arr) { + let ret = {}; + for (let [k, v] of arr) + ret[k] = v; + return ret; +} + +/** + * Flatten nested arrays + * @param {Array} arr + * @return {Generator} + */ +export function* flatten(arr) { + if (isArray(arr)) { + for (let i of arr) + if (isArray(i)) + yield* flatten(i); + else + yield i; + } else { + yield arr; + } +} + +/** + * Pipe function calls (functional composition with reversed order) + * @param {...Function} fns functions + * @return {Function} + */ +export function pipe(...fns) { + if (fns.length < 1) + throw 'no functions'; + return function (...args) { + let ret = fns[0].apply(this, args); + for (let fn of fns.slice(1)) + ret = fn(ret); + return ret; + }; +} + +/** + * Curry function with fixed arguments lenth + * @param {Function} fn + * @return {Function} + */ +export function curry(fn) { + let wrapper = function(oldArgs) { + return function(...args) { + args = oldArgs.concat(args); + if (args.length >= fn.length) + return fn(...args); + return wrapper(args); + }; + }; + return wrapper([]); +} + +/** + * Deep object equality + * (curried function) + * @function + * @param {Any} x + * @param {Any} y + * @return {Boolean} + */ +export const equals = curry((x, y) => { + if (x === y) + return true; + if (typeof(x) != 'object' || + typeof(y) != 'object' || + x === null || + y === null) + return false; + if (Array.isArray(x) || Array.isArray(y)) { + if (!Array.isArray(x) || !Array.isArray(y) || x.length != y.length) + return false; + } + for (let i in x) + if (!equals(x[i], y[i])) + return false; + for (let i in y) + if (!equals(x[i], y[i])) + return false; + return true; +}); + +/** + * Get value from `obj` referenced by `path` + * (curried function) + * @function + * @param {Path} path + * @param {Any} obj + * @return {Any} + */ +export const get = curry((path, obj) => { + let ret = obj; + for (let i of flatten(path)) { + if (ret === null || typeof(ret) != 'object') + return undefined; + ret = ret[i]; + } + return ret; +}); + +/** + * Change `obj` by appling function `fn` to value referenced by `path` + * (curried function) + * @function + * @param {Path} path + * @param {Function} fn + * @param {Any} obj + * @return {Any} changed `obj` + */ +export const change = curry((path, fn, obj) => { + function _change(path, obj) { + if (isInteger(path[0])) { + obj = (isArray(obj) ? Array.from(obj) : []); + } else if (isString(path[0])) { + obj = (isObject(obj) ? Object.assign({}, obj) : {}); + } else { + throw 'invalid path'; + } + if (path.length > 1) { + obj[path[0]] = _change(path.slice(1), obj[path[0]]); + } else { + obj[path[0]] = fn(obj[path[0]]); + } + return obj; + } + path = Array.from(flatten(path)); + if (path.length < 1) + return fn(obj); + return _change(path, obj); +}); + +/** + * Change `obj` by setting value referenced by `path` to `val` + * (curried function) + * @function + * @param {Path} path + * @param {Any} val + * @param {Any} obj + * @return {Any} changed `obj` + */ +export const set = curry((path, val, obj) => change(path, _ => val, obj)); + +/** + * Change `obj` by omitting value referenced by `path` + * (curried function) + * @function + * @param {Path} path + * @param {Any} obj + * @return {Any} changed `obj` + */ +export const omit = curry((path, obj) => { + function _omit(path, obj) { + if (isInteger(path[0])) { + obj = (isArray(obj) ? Array.from(obj) : []); + } else if (isString(path[0])) { + obj = (isObject(obj) ? Object.assign({}, obj) : {}); + } else { + throw 'invalid path'; + } + if (path.length > 1) { + obj[path[0]] = _omit(path.slice(1), obj[path[0]]); + } else if (isInteger(path[0])) { + obj.splice(path[0], 1); + } else { + delete obj[path[0]]; + } + return obj; + } + path = Array.from(flatten(path)); + if (path.length < 1) + return undefined; + return _omit(path, obj); +}); + +/** + * Sort `arr` by with comparison function `fn` + * (curried function) + * @function + * @param {Function} fn + * @param {Array} arr + * @return {Array} sorted `arr` + */ +export const sortBy = curry((fn, arr) => Array.from(arr).sort((x, y) => { + let xVal = fn(x); + let yVal = fn(y); + if (xVal < yVal) + return -1; + if (xVal > yVal) + return 1; + return 0; +})); + +/** + * Create object which is subset `obj` containing only properties defined by + * `arr` + * (curried function) + * @function + * @param {Array} arr + * @param {Object} obj + * @return {Object} subset of `obj` + */ +export const pick = curry((arr, obj) => { + const ret = {}; + for (let i of arr) + if (i in obj) + ret[i] = obj[i]; + return ret; +}); + +/** + * Change `arr` by appling function `fn` to it's elements + * (curried function) + * @function + * @param {Function} fn + * @param {Array} arr + * @return {Array} modified `arr` + */ +export const map = curry((fn, arr) => isArray(arr) ? + arr.map(fn) : + pipe(toPairs, + x => x.map(([k, v]) => [k, fn(v)]), + fromPairs)(arr)); + +/** + * Change `arr` to contain only elements for which function `fn` returns `true` + * (curried function) + * @function + * @param {Function} fn + * @param {Array} arr + * @return {Array} filtered `arr` + */ +export const filter = curry((fn, arr) => arr.filter(fn)); + +/** + * Append `val` to end of `arr` + * (curried function) + * @function + * @param {Any} val + * @param {Array} arr + * @return {Array} `arr` with appended `val` + */ +export const append = curry((val, arr) => arr.concat([val])); + +/** + * Reduce `arr` values by appling function `fn` + * (curried function) + * @function + * @param {Function} fn + * @param {Any} val initial accumulator value + * @param {Array} arr + * @return {Any} reduced value + */ +export const reduce = curry((fn, val, arr) => arr.reduce(fn, val)); + +/** + * Merge two objects + * (curried function) + * @function + * @param {Object} obj1 + * @param {Object} obj2 + * @return {Object} combined `obj1` and `obj2` + */ +export const merge = curry((obj1, obj2) => Object.assign({}, obj1, obj2)); + +/** + * Merge multiple objects + * (curried function) + * @function + * @param {...Object} objs + * @return {Object} combined `objs` + */ +export const mergeAll = reduce(merge, {}); + +/** + * Find element in `arr` for which function `fn` returns `true` + * (curried function) + * @function + * @param {Function} fn + * @param {Array} arr + * @return {Any} + */ +export const find = curry((fn, arr) => arr.find(fn)); + +/** + * Concatenate two arrays + * (curried function) + * @function + * @param {Array} arr1 + * @param {Array} arr2 + * @return {Array} concatenated `arr1` and `arr2` + */ +export const concat = curry((arr1, arr2) => arr1.concat(arr2)); + +/** + * Create union of two arrays using `equals` to check equality + * (curried function) + * @function + * @param {Array} arr1 + * @param {Array} arr2 + * @return {Array} union of `arr1` and `arr2` + */ +export const union = curry((arr1, arr2) => { + return reduce((acc, val) => { + if (!find(equals(val), arr1)) + acc = append(val, acc); + return acc; + }, arr1, arr2); +}); + +/** + * Check if `arr` contains `val` + * (curried function) + * @function + * @param {Any} val + * @param {Array} arr + * @return {Boolean} + */ +export const contains = curry((val, arr) => arr.includes(val)); + +/** + * Insert `val` into `arr` on index `idx` + * (curried function) + * @function + * @param {Number} idx + * @param {Any} val + * @param {Array} arr + * @return {Array} + */ +// TODO: Array.from(arr).splice(idx, 0, val) not working? +export const insert = curry((idx, val, arr) => + arr.slice(0, idx).concat([val], arr.slice(idx))); + +/** + * Reverse array + * @param {Array} arr + * @return {Array} + */ +export function reverse(arr) { + return Array.from(arr).reverse(); +} + +/** + * Array length + * @param {Array} arr + * @return {Number} + */ +export function length(arr) { + return arr.length; +} + +/** + * Create promise that resolves in `t` milliseconds + * @param {Number} t + * @return {Promise} + */ +export function sleep(t) { + return new Promise(resolve => { + setTimeout(() => { resolve(); }, t); + }); +} + + +export const slice = curry((begin, end, arr) => arr.slice(begin, end)); + +export function inc(x) { + return x + 1; +} + +export function dec(x) { + return x - 1; +} + + +/** + * Delay function call `fn(...args)` for `t` milliseconds + * @param {Function} fn + * @param {Number} [t=0] + * @param {...Any} args + * @return {Promise} + */ +export function delay(fn, t, ...args) { + return new Promise(resolve => { + setTimeout(() => { resolve(fn(...args)); }, t || 0); + }); +} diff --git a/src_js/opcut/validators.js b/src_js/opcut/validators.js index 713e73b..1d2ad32 100644 --- a/src_js/opcut/validators.js +++ b/src_js/opcut/validators.js @@ -1,63 +1,63 @@ -import * as u from 'opcut/util';
-
-
-export function notEmptyValidator(value) {
- if (!value)
- return 'invalid value';
-}
-
-
-export function floatValidator(value) {
- const floatValue = u.strictParseFloat(value);
- if (!Number.isFinite(floatValue))
- return 'not valid number';
-}
-
-
-export function integerValidator(value) {
- const intValue = u.strictParseInt(value);
- if (!Number.isFinite(intValue))
- return 'not valid number';
-}
-
-
-export function tcpPortValidator(value) {
- const intValue = u.strictParseInt(value);
- if (!Number.isFinite(intValue) || intValue < 0 || intValue > 0xFFFF)
- return 'not valid TCP port';
-}
-
-
-export function createChainValidator(...validators) {
- return value => {
- for (let validator of validators) {
- let result = validator(value);
- if (result)
- return result;
- }
- };
-}
-
-
-export function createUniqueValidator() {
- const values = new Set();
- return value => {
- if (values.has(value))
- return 'duplicate value';
- values.add(value);
- };
-}
-
-
-export function dimensionValidator(value) {
- const floatValue = u.strictParseFloat(value);
- if (!Number.isFinite(floatValue) || floatValue <= 0)
- return 'not valid dimension';
-}
-
-
-export function quantityValidator(value) {
- const intValue = u.strictParseInt(value);
- if (!Number.isFinite(intValue) || intValue < 1)
- return 'not valid quantity';
-}
+import * as u from 'opcut/util'; + + +export function notEmptyValidator(value) { + if (!value) + return 'invalid value'; +} + + +export function floatValidator(value) { + const floatValue = u.strictParseFloat(value); + if (!Number.isFinite(floatValue)) + return 'not valid number'; +} + + +export function integerValidator(value) { + const intValue = u.strictParseInt(value); + if (!Number.isFinite(intValue)) + return 'not valid number'; +} + + +export function tcpPortValidator(value) { + const intValue = u.strictParseInt(value); + if (!Number.isFinite(intValue) || intValue < 0 || intValue > 0xFFFF) + return 'not valid TCP port'; +} + + +export function createChainValidator(...validators) { + return value => { + for (let validator of validators) { + let result = validator(value); + if (result) + return result; + } + }; +} + + +export function createUniqueValidator() { + const values = new Set(); + return value => { + if (values.has(value)) + return 'duplicate value'; + values.add(value); + }; +} + + +export function dimensionValidator(value) { + const floatValue = u.strictParseFloat(value); + if (!Number.isFinite(floatValue) || floatValue <= 0) + return 'not valid dimension'; +} + + +export function quantityValidator(value) { + const intValue = u.strictParseInt(value); + if (!Number.isFinite(intValue) || intValue < 1) + return 'not valid quantity'; +} diff --git a/src_js/opcut/vt.js b/src_js/opcut/vt.js index c423234..bbfd792 100644 --- a/src_js/opcut/vt.js +++ b/src_js/opcut/vt.js @@ -208,7 +208,7 @@ function centerPanel() { const itemColor = 'rgb(250,250,250)'; const selectedItemColor = 'rgb(200,140,140)'; const unusedColor = 'rgb(238,238,238)'; - const fontSize = String(panel.height * 0.02); + const fontSize = String(Math.max(panel.height, panel.width) * 0.02); return ['div.center-panel', ['svg', { attrs: { @@ -278,7 +278,7 @@ function centerPanel() { on: { click: () => r.set(['selected', 'item'], used.item) }}, - used.item + used.item + (used.rotate ? ' \u293E' : '') ]; }) ] |
