package/package.json000644 000765 000024 0000001054 12500306713013012 0ustar00000000 000000 { "name": "nth-check", "version": "1.0.1", "description": "performant nth-check parser & compiler", "main": "index.js", "scripts": { "test": "node test" }, "repository": { "type": "git", "url": "https://github.com/fb55/nth-check" }, "keywords": [ "nth-child", "nth", "css" ], "author": "Felix Boehm ", "license": "BSD", "bugs": { "url": "https://github.com/fb55/nth-check/issues" }, "homepage": "https://github.com/fb55/nth-check", "dependencies": { "boolbase": "~1.0.0" } } package/README.md000644 000765 000024 0000002650 12500306527012011 0ustar00000000 000000 #nth-check [![Build Status](https://travis-ci.org/fb55/nth-check.png)](https://travis-ci.org/fb55/nth-check) A performant nth-check parser & compiler. ###About This module can be used to parse & compile nth-checks, as they are found in CSS 3's `nth-child()` and `nth-last-of-type()`. `nth-check` focusses on speed, providing optimized functions for different kinds of nth-child formulas, while still following the [spec](http://www.w3.org/TR/css3-selectors/#nth-child-pseudo). ###API ```js var nthCheck = require("nth-check"); ``` #####`nthCheck(formula)` First parses, then compiles the formula. #####`nthCheck.parse(formula)` Parses the expression, throws a `SyntaxError` if it fails, otherwise returns an array containing two elements. __Example:__ ```js nthCheck.parse("2n+3") //[2, 3] ``` #####`nthCheck.compile([a, b])` Takes an array with two elements (as returned by `.parse`) and returns a highly optimized function. If the formula doesn't match any elements, it returns [`boolbase`](https://github.com/fb55/boolbase)'s `falseFunc`, otherwise, a function accepting an _index_ is returned, which returns whether or not a passed _index_ matches the formula. (Note: The spec starts counting at `1`, the returned function at `0`). __Example:__ ```js var check = nthCheck.compile([2, 3]); check(0) //false check(1) //false check(2) //true check(3) //false check(4) //true check(5) //false check(6) //true ``` --- License: BSD package/compile.js000644 000765 000024 0000002155 12500306527012520 0ustar00000000 000000 module.exports = compile; var BaseFuncs = require("boolbase"), trueFunc = BaseFuncs.trueFunc, falseFunc = BaseFuncs.falseFunc; /* returns a function that checks if an elements index matches the given rule highly optimized to return the fastest solution */ function compile(parsed){ var a = parsed[0], b = parsed[1] - 1; //when b <= 0, a*n won't be possible for any matches when a < 0 //besides, the specification says that no element is matched when a and b are 0 if(b < 0 && a <= 0) return falseFunc; //when a is in the range -1..1, it matches any element (so only b is checked) if(a ===-1) return function(pos){ return pos <= b; }; if(a === 0) return function(pos){ return pos === b; }; //when b <= 0 and a === 1, they match any element if(a === 1) return b < 0 ? trueFunc : function(pos){ return pos >= b; }; //when a > 0, modulo can be used to check if there is a match var bMod = b % a; if(bMod < 0) bMod += a; if(a > 1){ return function(pos){ return pos >= b && pos % a === bMod; }; } a *= -1; //make `a` positive return function(pos){ return pos <= b && pos % a === bMod; }; }package/index.js000644 000765 000024 0000000334 12500306527012174 0ustar00000000 000000 var parse = require("./parse.js"), compile = require("./compile.js"); module.exports = function nthCheck(formula){ return compile(parse(formula)); }; module.exports.parse = parse; module.exports.compile = compile;package/parse.js000644 000765 000024 0000001507 12500306611012174 0ustar00000000 000000 module.exports = parse; //following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo //[ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? var re_nthElement = /^([+\-]?\d*n)?\s*(?:([+\-]?)\s*(\d+))?$/; /* parses a nth-check formula, returns an array of two numbers */ function parse(formula){ formula = formula.trim().toLowerCase(); if(formula === "even"){ return [2, 0]; } else if(formula === "odd"){ return [2, 1]; } else { var parsed = formula.match(re_nthElement); if(!parsed){ throw new SyntaxError("n-th rule couldn't be parsed ('" + formula + "')"); } var a; if(parsed[1]){ a = parseInt(parsed[1], 10); if(isNaN(a)){ if(parsed[1].charAt(0) === "-") a = -1; else a = 1; } } else a = 0; return [ a, parsed[3] ? parseInt((parsed[2] || "") + parsed[3], 10) : 0 ]; } } package/test.js000644 000765 000024 0000004233 12500306641012043 0ustar00000000 000000 var nthCheck = require("./"), assert = require("assert"); var invalid = ["-", "asdf", "2n+-0", "2+0", "- 1n", "-1 n"]; function parseInvalid(){ invalid.forEach(function(formula){ assert.throws(function(){ nthCheck.parse(formula); }, SyntaxError, formula ); }); } var valid = { "1": [ 0, 1 ], "2": [ 0, 2 ], "3": [ 0, 3 ], "5": [ 0, 5 ], " 1 ": [ 0, 1 ], " 5 ": [ 0, 5 ], "+2n + 1": [ 2, 1 ], "-1": [ 0, -1 ], "-1n + 3": [ -1, 3 ], "-1n+3": [ -1, 3 ], "-n+2": [ -1, 2 ], "-n+3": [ -1, 3 ], "0n+3": [ 0, 3 ], "1n": [ 1, 0 ], "1n+0": [ 1, 0 ], "2n": [ 2, 0 ], "2n + 1": [ 2, 1 ], "2n+1": [ 2, 1 ], "3n": [ 3, 0 ], "3n+0": [ 3, 0 ], "3n+1": [ 3, 1 ], "3n+2": [ 3, 2 ], "3n+3": [ 3, 3 ], "3n-1": [ 3, -1 ], "3n-2": [ 3, -2 ], "3n-3": [ 3, -3 ], even: [ 2, 0 ], n: [ 1, 0 ], "n+2": [ 1, 2 ], odd: [ 2, 1 ], //surprisingly, neither sizzle, qwery or nwmatcher cover these cases "-4n+13": [-4, 13], "-2n + 12": [-2, 12] }; function parseValid(){ Object.keys(valid).forEach(function(formula){ assert.deepEqual(nthCheck.parse(formula), valid[formula], formula); }); } function testValid(){ Object.keys(valid).forEach(function(formula){ testFormula(valid[formula], formula); }); } var valArray = Array.apply(null, Array(2e3)).map(function(_, i){return i;}); function testFormula(formula, name){ var filtered = valArray.filter(nthCheck.compile(formula)), iterated = stupidNth(formula); try { assert.deepEqual(filtered, iterated, name); } catch(e){ e.expected = JSON.stringify(iterated) + " " + name; e.actual = JSON.stringify(filtered) + " " + name; throw e; } } function stupidNth(formula, limit){ var a = formula[0], b = formula[1]; if(a === 0 && b > 0) return [b - 1]; //taken from qwery return valArray.filter(function(val){ for(var i = b, l = valArray.length; ((a > 0) ? (i <= l) : (i >= 1)); i += a){ if(val === valArray[i - 1]) return true; } }); } process.stdout.write("- parser"); process.stdout.write("\n - parse invalid:\t"); parseInvalid(); process.stdout.write("X\n - parse valid:\t"); parseValid(); process.stdout.write("X\n- check values: \t"); testValid(); process.stdout.write("X\n"); package/.travis.yml000644 000765 000024 0000000067 12500306527012643 0ustar00000000 000000 language: node_js node_js: - 0.8 - "0.10" - 0.11