Comparing version 4.1.0 to 4.2.0
@@ -9,4 +9,6 @@ { | ||
"no-implicit-coercion": 0, | ||
"no-lonely-if": 0 | ||
"no-lonely-if": 0, | ||
"no-var": 0, | ||
"comma-dangle": [2, "never"] | ||
} | ||
} |
{ | ||
"name": "tartan", | ||
"version": "4.1.0", | ||
"version": "4.2.0", | ||
"description": "This library allows to parse tartan threadcount.", | ||
@@ -37,8 +37,8 @@ "keywords": [ | ||
"dependencies": { | ||
"lodash": "^4.16.4" | ||
"lodash": "^4.17.2" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"eslint": "^3.9.1", | ||
"eslint-config-google": "^0.6.0", | ||
"eslint": "^3.11.1", | ||
"eslint-config-google": "^0.7.1", | ||
"json-loader": "^0.5.4", | ||
@@ -45,0 +45,0 @@ "mocha": "^3.1.2", |
'use strict'; | ||
var _ = require('lodash'); | ||
var autodetectSource = require('./source/autodetect'); | ||
var tokenize = require('./tokenize'); | ||
var utils = require('../utils'); | ||
var defaults = require('../defaults'); | ||
@@ -10,5 +13,40 @@ var defaultOptions = { | ||
buildSyntaxTree: null, | ||
foreseeLimit: 1 | ||
foreseeLimit: 1, | ||
getSourceMeta: function(source) { | ||
return _.omit(source, [ | ||
'warp', 'weft', 'threadcount', 'sett', 'palette', 'colors' | ||
]); | ||
}, | ||
// Used only if `source` is an object (or JSON) and has different | ||
// warp and weft, and `buildSyntaxTree` is not specified | ||
warpAndWeftSeparator: defaults.warpAndWeftSeparator | ||
}; | ||
function chooseNonEmptyString(values) { | ||
var result = _.filter(values, function(value) { | ||
return _.isString(value) && (value.length > 0); | ||
}); | ||
return result.length > 0 ? _.first(result) : ''; | ||
} | ||
function chooseRootBlock(values) { | ||
var result = _.filter(values, function(value) { | ||
return _.isObject(value) && value.isBlock && value.isRoot && | ||
_.isArrayLike(value.items); | ||
}); | ||
return result.length > 0 ? _.first(result) : utils.node.newRootBlock([]); | ||
} | ||
function parse(parsers, source, options) { | ||
var context = tokenize(source, parsers, options); | ||
var result = context.parse(); | ||
if (_.isFunction(options.processTokens)) { | ||
result = options.processTokens(result); | ||
} | ||
if (_.isFunction(options.buildSyntaxTree)) { | ||
result = options.buildSyntaxTree(result); | ||
} | ||
return result; | ||
} | ||
function factory(parsers, options) { | ||
@@ -18,10 +56,73 @@ options = _.extend({}, defaultOptions, options); | ||
return function(source) { | ||
var context = tokenize(source, parsers, options); | ||
var result = context.parse(); | ||
if (_.isFunction(options.processTokens)) { | ||
result = options.processTokens(result); | ||
source = autodetectSource(source); | ||
var result; | ||
if (_.isString(source.warp) || _.isString(source.weft)) { | ||
var warp = _.trim(chooseNonEmptyString([source.warp, source.weft])); | ||
var weft = _.trim(chooseNonEmptyString([source.weft, source.warp])); | ||
var warpIsSameAsWeft = warp == weft; | ||
warp = parse(parsers, warp, options); | ||
if (warpIsSameAsWeft) { | ||
// Create AST with same warp and weft; use meta from warp; | ||
// do not use palette | ||
result = { | ||
meta: _.extend({}, warp.meta), | ||
warp: chooseRootBlock([warp.warp, warp.weft]) | ||
}; | ||
result.weft = result.warp; | ||
} else { | ||
weft = parse(parsers, weft, options); | ||
if (_.isArrayLike(warp) && _.isArrayLike(weft)) { | ||
// Merge tokens together; | ||
result = _.concat(warp, | ||
utils.token.newLiteral(options.warpAndWeftSeparator), | ||
weft); | ||
} else | ||
if (_.isObject(warp) && _.isObject(weft)) { | ||
// Create AST with different warp and weft; merge meta; | ||
// do not use palette | ||
result = { | ||
meta: _.extend({}, warp.meta, weft.meta), | ||
warp: chooseRootBlock([warp.warp, warp.weft]), | ||
weft: chooseRootBlock([weft.warp, weft.weft]) | ||
}; | ||
} | ||
} | ||
} else | ||
if (_.isString(source.threadcount) || _.isString(source.sett)) { | ||
// Try to parse entire threadcount | ||
var threadcount = chooseNonEmptyString([source.threadcount, source.sett]); | ||
result = parse(parsers, threadcount, options); | ||
} else { | ||
// Create empty result | ||
result = parse(parsers, '', options); | ||
} | ||
if (_.isFunction(options.buildSyntaxTree)) { | ||
result = options.buildSyntaxTree(result); | ||
if (_.isString(source.palette) || _.isString(source.colors)) { | ||
// Try to add palette - as tokens or as color map | ||
var palette = chooseNonEmptyString([source.palette, source.colors]); | ||
palette = parse(parsers, palette, options); | ||
if (_.isArrayLike(result)) { | ||
if (_.isArrayLike(palette)) { | ||
result = _.concat(palette, result); | ||
} | ||
} else | ||
if (_.isObject(result)) { | ||
if (_.isObject(palette)) { | ||
result.colors = palette.colors; | ||
result.meta = _.extend({}, palette.meta, result.meta); | ||
} | ||
} | ||
} | ||
if (_.isObject(result)) { | ||
result.colors = _.extend({}, result.colors); | ||
if (_.isFunction(options.getSourceMeta)) { | ||
result.meta = _.extend({}, result.meta, options.getSourceMeta(source)); | ||
} else { | ||
result.meta = _.extend({}, result.meta); | ||
} | ||
} | ||
return result; | ||
@@ -33,2 +134,4 @@ }; | ||
module.exports.source = require('./source'); | ||
module.exports.color = require('./token/color'); | ||
@@ -35,0 +138,0 @@ module.exports.stripe = require('./token/stripe'); |
@@ -9,4 +9,17 @@ 'use strict'; | ||
weave: defaults.weave.serge, | ||
zoom: 1, | ||
defaultColors: null, | ||
transformSyntaxTree: null | ||
transformSyntaxTree: null, | ||
hooks: { | ||
// Called after renderer fills up all options; it's a good place | ||
// to modify options object | ||
configure: function(options) {}, | ||
// `stage`: `false` on before-action and `true` on after-action | ||
// `options` can mutate here; `context` is new for each repaint but | ||
// the same for each call during single repaint | ||
clear: function(context, options, stage) {}, | ||
render: function(context, options, stage) {}, | ||
renderWarp: function(context, options, stage) {}, | ||
renderWeft: function(context, options, stage) {} | ||
} | ||
}; | ||
@@ -25,2 +38,3 @@ | ||
var pattern = options.warp.pattern; | ||
var zoom = options.zoom; | ||
var i; | ||
@@ -34,7 +48,7 @@ var first; | ||
item = pattern[i]; | ||
if (x + item[1] > 0) { | ||
if (x + item[1] * zoom > 0) { | ||
first = i; | ||
break; | ||
} | ||
x += item[1]; | ||
x += item[1] * zoom; | ||
} | ||
@@ -46,4 +60,4 @@ | ||
context.fillStyle = item[0]; | ||
context.fillRect(x, 0, item[1], options.height); | ||
x += item[1]; | ||
context.fillRect(x, 0, item[1] * zoom, options.height); | ||
x += item[1] * zoom; | ||
if (x >= options.width) { | ||
@@ -65,8 +79,9 @@ break; | ||
var j; | ||
var zoom = options.zoom; | ||
var first; | ||
var item; | ||
var y = options.offset.y; | ||
var offsetX = options.offset.x; | ||
var offsetY = options.offset.y; | ||
var n = _.sum(options.weave); | ||
var offsetX = (options.offset.x) % n; | ||
var offsetY = (options.offset.y) % n; | ||
var offset; | ||
@@ -77,10 +92,12 @@ | ||
item = pattern[i]; | ||
if (y + item[1] > 0) { | ||
if (y + item[1] * zoom > 0) { | ||
first = i; | ||
break; | ||
} | ||
y += item[1]; | ||
y += item[1] * zoom; | ||
} | ||
context.setLineDash(options.weave); | ||
context.setLineDash(_.map(options.weave, function(value) { | ||
return value * zoom; | ||
})); | ||
@@ -94,7 +111,7 @@ while (y <= options.height) { | ||
j = y < 0 ? 0 : y; | ||
y += item[1]; | ||
y += item[1] * zoom; | ||
for (j; j < y; j++) { | ||
// Correct offset of each line relating to global (0, 0) point | ||
offset = n - (j - offsetY) % n + 1; | ||
offset = (offset - offsetX) % n; | ||
offset = n - Math.floor((j - offsetY) / zoom) % n; | ||
offset = offset * zoom - offsetX; | ||
context.lineDashOffset = offset; | ||
@@ -126,3 +143,6 @@ | ||
var items = _.isObject(node) && node.isBlock ? node.items : []; | ||
var pattern = utils.sett.compile(items, colors, defaultColors); | ||
var pattern = []; | ||
if (items.length > 0) { | ||
pattern = utils.sett.compile(items, colors, defaultColors); | ||
} | ||
var metrics = utils.sett.getPatternMetrics(pattern, weave); | ||
@@ -137,3 +157,3 @@ | ||
function prepareOffset(offset, warp, weft) { | ||
function prepareOffset(offset, warp, weft, zoom) { | ||
var x = 0; | ||
@@ -149,10 +169,10 @@ var y = 0; | ||
// avoid numeric overflows | ||
x %= warp.lengthOfCycle; | ||
x %= (warp.lengthOfCycle * zoom); | ||
if (x > 0) { | ||
x -= warp.lengthOfCycle; | ||
x -= (warp.lengthOfCycle * zoom); | ||
} | ||
y %= weft.lengthOfCycle; | ||
y %= (weft.lengthOfCycle * zoom); | ||
if (y > 0) { | ||
y -= weft.lengthOfCycle; | ||
y -= (weft.lengthOfCycle * zoom); | ||
} | ||
@@ -190,2 +210,7 @@ | ||
renderEmpty.metrics = getMetrics(defaultOptions.weave, | ||
preparePattern(null, defaultOptions.weave, {}, {}), | ||
preparePattern(null, defaultOptions.weave, {}, {}) | ||
); | ||
function factory(sett, options) { | ||
@@ -196,3 +221,14 @@ if (!_.isObject(sett)) { | ||
options = _.extend({}, defaultOptions, options); | ||
options = _.merge({}, defaultOptions, options); | ||
// Validate hooks | ||
var hooks = options.hooks; | ||
if (!_.isObject(options.hooks)) { | ||
hooks = options.hooks = {}; | ||
} | ||
_.each(defaultOptions.hooks, function(value, key) { | ||
if (!_.isFunction(hooks[key])) { | ||
hooks[key] = defaultOptions.hooks[key]; | ||
} | ||
}); | ||
if (_.isFunction(options.transformSyntaxTree)) { | ||
@@ -202,2 +238,7 @@ sett = options.transformSyntaxTree(sett); | ||
var zoom = parseInt(options.zoom, 10) || 0; | ||
if (zoom < 1) { | ||
zoom = 1; | ||
} | ||
var warpIsSameAsWeft = sett.weft === sett.warp; | ||
@@ -215,3 +256,3 @@ | ||
if ((warp.length == 0) && (weft.length == 0)) { | ||
if ((warp.lengthOfPattern == 0) && (weft.lengthOfPattern == 0)) { | ||
return renderEmpty; | ||
@@ -223,3 +264,3 @@ } | ||
offset = repeat ? prepareOffset(offset, warp, weft) : {x: 0, y: 0}; | ||
offset = repeat ? prepareOffset(offset, warp, weft, zoom) : {x: 0, y: 0}; | ||
@@ -230,13 +271,28 @@ var options = { | ||
weave: weave, | ||
zoom: zoom, | ||
width: Math.ceil(parseFloat(canvas.width) || 0), | ||
height: Math.ceil(parseFloat(canvas.height) || 0), | ||
offset: offset, | ||
repeat: repeat | ||
repeat: repeat, | ||
canvas: canvas | ||
}; | ||
hooks.configure(canvas, options); | ||
if ((options.width > 0) && (options.height > 0)) { | ||
var context = canvas.getContext('2d'); | ||
hooks.render(context, options, false); | ||
hooks.clear(context, options, false); | ||
options = clearCanvas(context, options); | ||
hooks.clear(context, options, true); | ||
hooks.renderWarp(context, options, false); | ||
renderWarp(context, options); | ||
hooks.renderWarp(context, options, true); | ||
hooks.renderWeft(context, options, false); | ||
renderWeft(context, options); | ||
hooks.renderWeft(context, options, true); | ||
hooks.render(context, options, true); | ||
} | ||
@@ -253,1 +309,10 @@ | ||
module.exports = factory; | ||
// Define some properties for `factory()` function | ||
Object.defineProperty(module.exports, 'id', { | ||
enumerable: true, | ||
value: 'canvas' | ||
}); | ||
Object.defineProperty(module.exports, 'name', { | ||
enumerable: true, | ||
value: 'Simple' | ||
}); |
'use strict'; | ||
module.exports.canvas = require('./canvas'); | ||
module.exports.houseOfTartan = require('./house-of-tartan'); | ||
module.exports.format = require('./format'); |
@@ -50,3 +50,11 @@ 'use strict'; | ||
transform.flatten(), | ||
transform.fold() | ||
transform.fold({ | ||
allowRootReorder: false, | ||
allowNestedBlocks: false, | ||
maxFoldLevels: 2, | ||
minBlockSize: 3, | ||
greedy: false, | ||
allowSplitStripe: false, | ||
processExistingBlocks: false | ||
}) | ||
]); | ||
@@ -53,0 +61,0 @@ |
@@ -6,2 +6,3 @@ 'use strict'; | ||
var render = require('../../render'); | ||
var transform = require('../../transform'); | ||
@@ -47,2 +48,15 @@ var defaultOptions = { | ||
options.transformSyntaxTree = transform([ | ||
options.transformSyntaxTree, | ||
transform.fold({ | ||
allowRootReorder: false, | ||
allowNestedBlocks: false, | ||
maxFoldLevels: 2, | ||
minBlockSize: 3, | ||
greedy: false, | ||
allowSplitStripe: false, | ||
processExistingBlocks: false | ||
}) | ||
]); | ||
options.join = function(components) { | ||
@@ -49,0 +63,0 @@ var parts = []; |
@@ -50,3 +50,11 @@ 'use strict'; | ||
transform.flatten(), | ||
transform.fold() | ||
transform.fold({ | ||
allowRootReorder: false, | ||
allowNestedBlocks: false, | ||
maxFoldLevels: 2, | ||
minBlockSize: 3, | ||
greedy: false, | ||
allowSplitStripe: false, | ||
processExistingBlocks: false | ||
}) | ||
]); | ||
@@ -53,0 +61,0 @@ |
@@ -62,3 +62,11 @@ 'use strict'; | ||
transform.flatten(), | ||
transform.fold() | ||
transform.fold({ | ||
allowRootReorder: false, | ||
allowNestedBlocks: false, | ||
maxFoldLevels: 2, | ||
minBlockSize: 3, | ||
greedy: false, | ||
allowSplitStripe: false, | ||
processExistingBlocks: false | ||
}) | ||
]); | ||
@@ -65,0 +73,0 @@ |
'use strict'; | ||
var _ = require('lodash'); | ||
var utils = require('../utils'); | ||
var defaultOptions = { | ||
// treat root block as infinite - may detect non-obvious folds | ||
allowRootReorder: true, | ||
// Enables extended mode - search of sub-blocks | ||
allowNestedBlocks: false, // fold only root | ||
// Next options are applicable only to extended mode: | ||
maxFoldLevels: 2, // fold root and up to 2 nested levels; 0 - unlimited | ||
// detected blocks should contain at least 3 stripes when folded | ||
minBlockSize: 3, | ||
// Example: R10 K20 Y2 G2 Y2 K20 R10 | ||
// Non-greedy algorithm will capture only [R10 K20 Y2 G2] | ||
// Greedy will also produce R10 [K20 Y2 G2] R10 | ||
greedy: false, // Capture only longest sequence | ||
// R15 K10 Y2 K10 R10 => R5 R10 K10 Y2 K10 R10 => R5 [R10 K10 Y2] | ||
allowSplitStripe: true, | ||
// If sett already contains some nested blocks - try to fold them too | ||
processExistingBlocks: true, | ||
// Evaluation function - should return a number that will be used to | ||
// compare blocks and choose the best variant | ||
calculateNodeWeight: utils.node.calculateNodeWeight | ||
}; | ||
function processTokens(root, options) { | ||
if (root.reflect || (root.items.length % 2 != 0)) { | ||
return root; | ||
} | ||
function tryFoldBlock(items) { | ||
// Smallest reflective sett contains 3 stripes in threadcount or | ||
// 4 stripes when unfolded, i.e. R/10 K2 Y/2 => R10 K2 Y2 K2; | ||
// 5 stripes when unfolded, i.e. R/10 K2 Y/2 => R10 K2 Y2 K2 R10; | ||
// R/10 K/2 => R10 K2 | ||
if (root.items.length < 4) { | ||
return root; | ||
if ((items.length < 5) || (items.length % 2 != 1)) { | ||
return; | ||
} | ||
var result = []; | ||
var i = 1; | ||
var j = root.items.length - 1; | ||
var left; | ||
var right; | ||
result.push(root.items[0]); | ||
var result = []; | ||
var i = 0; | ||
var j = items.length - 1; | ||
while (true) { | ||
left = root.items[i]; | ||
right = root.items[j]; | ||
if (left.isStripe && right.isStripe) { | ||
var isSameColor = left.name == right.name; | ||
var isSameCount = left.count == right.count; | ||
if (isSameColor && isSameCount) { | ||
result.push(left); | ||
if (i == j) { | ||
break; | ||
} | ||
i++; | ||
j--; | ||
continue; | ||
left = items[i]; | ||
right = items[j]; | ||
if (utils.node.isSameNode(left, right)) { | ||
result.push(left); | ||
if (i == j) { | ||
break; | ||
} | ||
i++; | ||
j--; | ||
continue; | ||
} | ||
result = null; | ||
break; | ||
return; | ||
} | ||
if (result) { | ||
root = _.clone(root); | ||
root.items = result; | ||
root.reflect = true; | ||
return result; | ||
} | ||
function foldRootBlock(root, options, results) { | ||
if (root.reflect || (root.items.length == 0)) { | ||
return; | ||
} | ||
return root; | ||
var items = _.concat(root.items, root.items[0]); | ||
var resultItems = tryFoldBlock(items); | ||
if (_.isArray(resultItems)) { | ||
var result = _.clone(root); | ||
result.items = resultItems; | ||
result.reflect = true; | ||
results.push({ | ||
node: result, | ||
hash: utils.node.calculateNodeHash(result), | ||
weight: options.calculateNodeWeight(result) | ||
}); | ||
} | ||
} | ||
function findRootBlockVariants(root, options) { | ||
var results = [root]; | ||
if (root.reflect || (root.items.length == 0) || !options.allowRootReorder) { | ||
return results; | ||
} | ||
var i; | ||
var items = _.clone(root.items); | ||
for (i = 0; i < items.length - 1; i++) { | ||
// Move first node to the end | ||
var temp = items[0]; | ||
items.splice(0, 1); | ||
items.push(temp); | ||
var result = _.clone(root); | ||
result.items = _.clone(items); | ||
results.push(result); | ||
} | ||
return results; | ||
} | ||
function findAllPossibleVariants(items, options, results, level) { | ||
results.push(items); | ||
if (level <= 0) { | ||
return; | ||
} | ||
if (items.length >= options.minBlockSize * 2 - 1) { | ||
var from = options.minBlockSize - 1; | ||
var to = items.length - options.minBlockSize; | ||
for (var i = from; i <= to; i++) { | ||
tryFindNestedBlocks(i, items, _.extend({}, options, { | ||
allowSplitStripe: false | ||
}), results, level - 1); | ||
if (options.allowSplitStripe) { | ||
tryFindNestedBlocks(i, items, _.extend({}, options, { | ||
allowSplitStripe: true | ||
}), results, level - 1); | ||
} | ||
} | ||
} | ||
} | ||
function processNestedVariants(items, left, right, middle, appendToPrefix, | ||
prependToSuffix, options, results, level) { | ||
if (middle.length < options.minBlockSize) { | ||
return; | ||
} | ||
var prefix = items.slice(0, left >= 0 ? left + 1 : 0); | ||
if (appendToPrefix) { | ||
prefix.push(appendToPrefix); | ||
} | ||
var middleVariants = []; | ||
findAllPossibleVariants(middle, options, middleVariants, level); | ||
var suffix = items.slice(right, items.length); | ||
if (prependToSuffix) { | ||
suffix.splice(0, 0, prependToSuffix); | ||
} | ||
var suffixVariants = []; | ||
findAllPossibleVariants(suffix, options, suffixVariants, level); | ||
_.each(suffixVariants, function(variant) { | ||
_.each(middleVariants, function(middle) { | ||
results.push(_.concat(prefix, | ||
utils.node.newBlock(middle, true), | ||
variant)); | ||
}); | ||
}); | ||
} | ||
function tryFindNestedBlocks(index, items, options, results, level) { | ||
var left; | ||
var right; | ||
left = index - 1; | ||
right = index + 1; | ||
var appendToPrefix = null; | ||
var prependToSuffix = null; | ||
var middle = [items[index]]; | ||
var processLast = false; | ||
while ((left >= 0) && (right < items.length)) { | ||
if (utils.node.isSameNode(items[left], items[right])) { | ||
middle.splice(0, 0, items[left]); | ||
} else | ||
if ( | ||
options.allowSplitStripe && items[left].isStripe && | ||
items[right].isStripe && (items[left].name == items[right].name)) { | ||
var diff = items[left].count - items[right].count; | ||
if (diff > 0) { | ||
appendToPrefix = utils.node.newStripe({ | ||
name: items[left].name, | ||
count: Math.abs(diff) | ||
}); | ||
} else { | ||
prependToSuffix = utils.node.newStripe({ | ||
name: items[left].name, | ||
count: Math.abs(diff) | ||
}); | ||
} | ||
var node = _.clone(items[left]); | ||
node.count = Math.min(items[left].count, items[right].count); | ||
middle.splice(0, 0, node); | ||
left--; | ||
right++; | ||
processLast = true; | ||
break; | ||
} else { | ||
processLast = true; | ||
break; | ||
} | ||
left--; | ||
right++; | ||
if (options.greedy) { | ||
processNestedVariants(items, left, right, middle, | ||
appendToPrefix, prependToSuffix, options, results, level); | ||
} | ||
} | ||
if (processLast || !options.greedy) { | ||
processNestedVariants(items, left, right, middle, | ||
appendToPrefix, prependToSuffix, options, results, level); | ||
} | ||
} | ||
function findNestedBlocks(block, options, results) { | ||
if (block.items.length < options.minBlockSize * 2 - 1) { | ||
// This block cannot be folded to contain `minBlockSize` stripes as it | ||
// is too small | ||
return; | ||
} | ||
var variants = []; | ||
findAllPossibleVariants(block.items, options, variants, | ||
options.maxFoldLevels); | ||
_.each(variants, function(variant) { | ||
var result = _.clone(block); | ||
result.items = variant; | ||
results.push({ | ||
node: result, | ||
hash: utils.node.calculateNodeHash(result), | ||
weight: options.calculateNodeWeight(result) | ||
}); | ||
}); | ||
} | ||
function processExistingBlocks(root, options, results) { | ||
results.push(root); | ||
if ( | ||
options.allowNestedBlocks && options.processExistingBlocks && | ||
options.maxFoldLevels > 1 | ||
) { | ||
var prefixes = []; | ||
var suffix = []; | ||
var modifiedOptions = _.clone(options); | ||
// Nested blocks are not real roots, so do not use extended algorithm | ||
modifiedOptions.allowRootReorder = false; | ||
// We already drilled down one level | ||
modifiedOptions.maxFoldLevels -= 1; | ||
_.each(root.items, function(item) { | ||
if (item.isBlock) { | ||
// Calculate variants of item | ||
item = _.clone(item); | ||
item.isRoot = true; | ||
var variants = processTokens(item, modifiedOptions, true); | ||
// Merge previous prefixes, variants of current block and suffix | ||
var temp = prefixes; | ||
prefixes = []; | ||
_.each(variants, function(variant) { | ||
variant = _.clone(variant.node); | ||
variant.isRoot = false; | ||
if (temp.length > 0) { | ||
_.each(temp, function(prefix) { | ||
prefixes.push(_.concat(prefix, suffix, variant)); | ||
}); | ||
} else { | ||
prefixes.push(_.concat(suffix, variant)); | ||
} | ||
}); | ||
// Suffix is already merged, reset it | ||
suffix = []; | ||
} else { | ||
suffix.push(item); | ||
} | ||
}); | ||
_.each(prefixes, function(prefix) { | ||
var result = _.clone(root); | ||
result.items = _.concat(prefix, suffix); | ||
results.push(result); | ||
}); | ||
} | ||
} | ||
function processTokens(root, options, doNotLog) { | ||
var variants = [{ | ||
node: root, | ||
hash: utils.node.calculateNodeHash(root), | ||
weight: utils.node.calculateNodeWeight(root) | ||
}]; | ||
var rootVariants = []; | ||
processExistingBlocks(root, options, rootVariants); | ||
var excludeHashes = []; | ||
_.each(rootVariants, function(root) { | ||
var rootVariants = findRootBlockVariants(root, options); | ||
// Exclude non-folded modified roots | ||
_.each(_.drop(rootVariants), function(root) { | ||
excludeHashes.push({hash: utils.node.calculateNodeHash(root)}); | ||
}); | ||
_.each(rootVariants, function(root) { | ||
foldRootBlock(root, options, variants); | ||
if (options.allowNestedBlocks) { | ||
findNestedBlocks(root, options, variants); | ||
} | ||
}); | ||
}); | ||
return _.chain(variants) | ||
.differenceBy(excludeHashes, function(item) { | ||
return item.hash; | ||
}) | ||
.uniqBy(function(item) { | ||
return item.hash; | ||
}) | ||
.sortBy(function(item) { | ||
return item.weight; | ||
}) | ||
.each(function(item) { | ||
if (!doNotLog) { | ||
// Debug code, let it be here for now | ||
console.log(item.weight.toFixed(4), | ||
item.hash | ||
.replace(/\[R\*[0-9]+\/R[PF]:/g, '') | ||
.replace(/B\*[0-9]+\/R[PF]:/g, '') | ||
.replace(/]$/g, '') | ||
.replace(/[0-9]+/g, '') | ||
); | ||
} | ||
}) | ||
.value(); | ||
} | ||
function transform(sett, options) { | ||
@@ -59,12 +352,16 @@ var result = _.clone(sett); | ||
if (_.isObject(sett.warp)) { | ||
result.warp = processTokens(sett.warp, options); | ||
result.warpVariants = processTokens(sett.warp, options, true); | ||
} | ||
if (_.isObject(sett.weft)) { | ||
if (warpIsSameAsWeft) { | ||
result.weft = result.warp; | ||
result.weftVariants = result.warpVariants; | ||
} else { | ||
result.weft = processTokens(sett.weft, options); | ||
result.weftVariants = processTokens(sett.weft, options, true); | ||
} | ||
} | ||
// Take the best variant, but keep other too | ||
result.warp = _.first(result.warpVariants).node; | ||
result.weft = _.first(result.weftVariants).node; | ||
return result; | ||
@@ -75,2 +372,8 @@ } | ||
options = _.extend({}, defaultOptions, options); | ||
if (options.minBlockSize < 1) { | ||
options.minBlockSize = 1; | ||
} | ||
if (options.maxFoldLevels <= 0) { | ||
options.maxFoldLevels = 200000000; // Just a huge number | ||
} | ||
return function(sett) { | ||
@@ -77,0 +380,0 @@ return transform(sett, options); |
@@ -29,4 +29,119 @@ 'use strict'; | ||
function parseRepeat(value) { | ||
var result = parseInt(value, 10) || 0; | ||
return result > 1 ? result : 1; | ||
} | ||
function calculateNodeHash(node) { | ||
if (!_.isObject(node)) { | ||
return ''; | ||
} | ||
if (node.isStripe && (node.count > 0)) { | ||
return node.name + node.count * parseRepeat(node.repeat); | ||
} | ||
if (node.isBlock) { | ||
if (_.isArray(node.items) && (node.items.length > 0)) { | ||
return '[' + (node.isRoot ? 'R' : 'B') + | ||
'*' + parseRepeat(node.repeat) + | ||
'/' + (node.reflect ? 'RF' : 'RP') + ':' + | ||
_.chain(node.items) | ||
.map(calculateNodeHash) | ||
.join('') | ||
.value() + | ||
']'; | ||
} | ||
} | ||
return ''; | ||
} | ||
function calculateNodeWeight(node, returnRawWeight) { | ||
var stripeCount = 0; | ||
var blockCount = 0; | ||
if (_.isObject(node)) { | ||
if (node.isStripe) { | ||
stripeCount++; | ||
} | ||
if (node.isBlock && _.isArray(node.items) && (node.items.length > 0)) { | ||
if (!node.isRoot) { | ||
blockCount++; | ||
} | ||
var multiplier = node.reflect ? 2 : 1; | ||
_.each(node.items, function(item) { | ||
if (_.isObject(item)) { | ||
if (item.isBlock) { | ||
var nested = calculateNodeWeight(item, true); | ||
blockCount += nested.blocks * multiplier; | ||
} | ||
if (item.isStripe && node.isRoot && !node.reflect) { | ||
// Calculate only stripes in root if it is not reflected | ||
stripeCount++; | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
if (returnRawWeight) { | ||
return {blocks: blockCount, stripes: stripeCount}; | ||
} | ||
if ((blockCount == 0) && (stripeCount == 0)) { | ||
return node.isRoot ? 0 : Number.MAX_VALUE; | ||
} | ||
return Math.sqrt(blockCount * blockCount + stripeCount * stripeCount); | ||
} | ||
function isSameNode(left, right) { | ||
if (!_.isObject(left) || (!_.isObject(right))) { | ||
return false; | ||
} | ||
if (left.isStripe && right.isStripe) { | ||
return (left.name == right.name) && ( | ||
left.count * parseRepeat(left.repeat) == | ||
right.count * parseRepeat(right.repeat) | ||
); | ||
} | ||
if (left.isBlock && right.isBlock) { | ||
// Both should be root or not | ||
if (left.isRoot != right.isRoot) { | ||
return false; | ||
} | ||
// Both should be reflected or not | ||
if (left.reflect != right.reflect) { | ||
return false; | ||
} | ||
// Both should be repeated equal times | ||
if (parseRepeat(left.repeat) != parseRepeat(right.repeat)) { | ||
return false; | ||
} | ||
// Both should have same count of items | ||
if (!_.isArray(left.items) || !_.isArray(right.items)) { | ||
return false; | ||
} | ||
if (left.items.length != right.items.length) { | ||
return false; | ||
} | ||
// Complex case: compare all items | ||
var result = true; | ||
_.each(left.items, function(leftItem, index) { | ||
var rightItem = right.items[index]; | ||
if (!isSameNode(leftItem, rightItem)) { | ||
result = false; | ||
return false; // Break | ||
} | ||
}); | ||
return result; | ||
} | ||
return false; | ||
} | ||
module.exports.newStripe = newStripe; | ||
module.exports.newBlock = newBlock; | ||
module.exports.newRootBlock = newRootBlock; | ||
module.exports.isSameNode = isSameNode; | ||
module.exports.calculateNodeHash = calculateNodeHash; | ||
module.exports.calculateNodeWeight = calculateNodeWeight; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3319883
76
24141
Updatedlodash@^4.17.2