From 869b4badbf844b4cc46667721fea4bb2069d571e Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 5 Jan 2014 19:23:31 -0800 Subject: [PATCH 1/8] Add a `hasColor` function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given a string, such as `”background-color: #000000;”`, this method will return the tinycolor object for the string. Otherwise, it returns `false`. --- test/test.js | 7 +++++++ tinycolor.js | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/test/test.js b/test/test.js index 315b55c..6176ff4 100644 --- a/test/test.js +++ b/test/test.js @@ -433,6 +433,13 @@ test("Readability", function () { equal (tinycolor.mostReadable("#f00", ["#d00", "#0d0"]).toHexString(), "#00dd00", "pick most readable color"); }); +test("HasColor", function () { + var blackHex = tinycolor.fromRatio({ r: 0, g: 0, b: 0 }, {format: "hex"}); + + equal (tinycolor.hasColor("background-color: #000000;"), blackHex.toString()); + equal (tinycolor.hasColor("body {"), false); +}); + test("Filters", function () { equal (tinycolor("red").toFilter(), "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffff0000,endColorstr=#ffff0000)"); diff --git a/tinycolor.js b/tinycolor.js index a37833c..f6ac4b9 100644 --- a/tinycolor.js +++ b/tinycolor.js @@ -606,6 +606,26 @@ tinycolor.mostReadable = function(baseColor, colorList) { }; +// `hasColor` +// Given a string, determines if it has a valid color. +// Returns the matched object if it does, false otherwise. +tinycolor.hasColor = function(line) { + var joinedMatcherRegExp = [], match = null; + + for (var match in matchers) { + if (matchers.hasOwnProperty(match)) { + joinedMatcherRegExp.push(String(matchers[match]).replace(/\^|\$|\//g, '')); + } + } + + joinedMatcherRegExp = new RegExp(joinedMatcherRegExp.join("|"), "i"); + if (match = joinedMatcherRegExp.test(line)) { + return tinycolor(match); + } + + return false; +}; + // Big List of Colors // ------------------ // From 1a144b6cf93c6f6f521000141dd886f92102bfc3 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Fri, 10 Jan 2014 16:14:58 -0800 Subject: [PATCH 2/8] Update scanForColors to return an array This supports reading and returning multiple values, given any block of text. --- test/test.js | 18 ++++++++++++++---- tinycolor.js | 40 +++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/test/test.js b/test/test.js index 6176ff4..46829ae 100644 --- a/test/test.js +++ b/test/test.js @@ -433,11 +433,21 @@ test("Readability", function () { equal (tinycolor.mostReadable("#f00", ["#d00", "#0d0"]).toHexString(), "#00dd00", "pick most readable color"); }); -test("HasColor", function () { - var blackHex = tinycolor.fromRatio({ r: 0, g: 0, b: 0 }, {format: "hex"}); +test("ScanForColors", function () { + var blackHex = tinycolor.fromRatio({ r: 0, g: 0, b: 0 }, {format: "hex"}), + single_line_single_result = "background-color: #000000;", + results = tinycolor.scanForColors(single_line_single_result); + equal (results.length, 1); + equal (results[0], blackHex.toString()); - equal (tinycolor.hasColor("background-color: #000000;"), blackHex.toString()); - equal (tinycolor.hasColor("body {"), false); + var single_line_multiple_results = "{ content: '#000'; background: orange; }"; + results = tinycolor.scanForColors(single_line_multiple_results); + equal (results.length, 2); + + equal (results[0], blackHex.toString()); + equal (results[1], "orange"); + + equal (tinycolor.scanForColors("body {").length, 0); }); test("Filters", function () { diff --git a/tinycolor.js b/tinycolor.js index f6ac4b9..abebea8 100644 --- a/tinycolor.js +++ b/tinycolor.js @@ -605,25 +605,43 @@ tinycolor.mostReadable = function(baseColor, colorList) { return bestColor; }; - -// `hasColor` +// `scanForColors` // Given a string, determines if it has a valid color. -// Returns the matched object if it does, false otherwise. -tinycolor.hasColor = function(line) { - var joinedMatcherRegExp = [], match = null; +// Returns an array of matched objects if it does, or an empty array otherwise. +tinycolor.scanForColors = function(text) { + var joinedMatcherRegExp = [], results = [], match = null, lines = text.split("\n"); + + for (name in names) { + if (names.hasOwnProperty(name)) { + joinedMatcherRegExp.push(name); + } + } - for (var match in matchers) { + // construct a giant regexp of the color matchers + for (match in matchers) { if (matchers.hasOwnProperty(match)) { - joinedMatcherRegExp.push(String(matchers[match]).replace(/\^|\$|\//g, '')); + var matchAsString = String(matchers[match]); + if (/^hex/.test(match)) + matchAsString = matchAsString + "\\b"; + + joinedMatcherRegExp.push(matchAsString.replace(/\^|\$|\//g, '')); } } - joinedMatcherRegExp = new RegExp(joinedMatcherRegExp.join("|"), "i"); - if (match = joinedMatcherRegExp.test(line)) { - return tinycolor(match); + joinedMatcherRegExp = new RegExp(joinedMatcherRegExp.join("|"), "gi"); + + // for each line, try to find a match. if it's found, turn it into a tinycolor + // and append it to the results + for (var l = 0, size = lines.length; l < size; l++) { + match = lines[l].match(joinedMatcherRegExp); + if (match == null) + continue; + while (match.length > 0) { + results.push(tinycolor(match.shift())); + } } - return false; + return results; }; // Big List of Colors From 641da8fe7d5e1dc768e2f867b28aa4f384b591c4 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Fri, 10 Jan 2014 16:27:11 -0800 Subject: [PATCH 3/8] Store regexp construction on first use --- tinycolor.js | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/tinycolor.js b/tinycolor.js index abebea8..73fae38 100644 --- a/tinycolor.js +++ b/tinycolor.js @@ -605,36 +605,44 @@ tinycolor.mostReadable = function(baseColor, colorList) { return bestColor; }; -// `scanForColors` -// Given a string, determines if it has a valid color. -// Returns an array of matched objects if it does, or an empty array otherwise. -tinycolor.scanForColors = function(text) { - var joinedMatcherRegExp = [], results = [], match = null, lines = text.split("\n"); +tinycolor.constructMatcherRegExp = function() { + if (tinycolor.joinedMatcherRegExp !== undefined) + return tinycolor.joinedMatcherRegExp; - for (name in names) { - if (names.hasOwnProperty(name)) { - joinedMatcherRegExp.push(name); - } + var joinedMatcherRegExp = [], name = null, match = null; + + for (name in names) { + if (names.hasOwnProperty(name)) { + joinedMatcherRegExp.push(name); } + } - // construct a giant regexp of the color matchers - for (match in matchers) { - if (matchers.hasOwnProperty(match)) { - var matchAsString = String(matchers[match]); - if (/^hex/.test(match)) - matchAsString = matchAsString + "\\b"; + // construct a giant regexp of the color matchers + for (match in matchers) { + if (matchers.hasOwnProperty(match)) { + var matchAsString = String(matchers[match]); + if (/^hex/.test(match)) + matchAsString = matchAsString + "\\b"; - joinedMatcherRegExp.push(matchAsString.replace(/\^|\$|\//g, '')); - } + joinedMatcherRegExp.push(matchAsString.replace(/\^|\$|\//g, '')); } + } - joinedMatcherRegExp = new RegExp(joinedMatcherRegExp.join("|"), "gi"); + this.joinedMatcherRegExp = new RegExp(joinedMatcherRegExp.join("|"), "gi"); + return this.joinedMatcherRegExp; +}; + +// `scanForColors` +// Given a string, determines if it has a valid color. +// Returns an array of matched objects if it does, or an empty array otherwise. +tinycolor.scanForColors = function(text) { + var joinedMatcherRegExp = this.constructMatcherRegExp(), results = [], match = null, lines = text.split("\n"); // for each line, try to find a match. if it's found, turn it into a tinycolor // and append it to the results for (var l = 0, size = lines.length; l < size; l++) { match = lines[l].match(joinedMatcherRegExp); - if (match == null) + if (match === null) continue; while (match.length > 0) { results.push(tinycolor(match.shift())); From d0493a265590284d5d64b36437dcbcc918046b93 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Fri, 10 Jan 2014 16:37:39 -0800 Subject: [PATCH 4/8] Add tests for multiple lines --- test/test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test.js b/test/test.js index 46829ae..f0681f6 100644 --- a/test/test.js +++ b/test/test.js @@ -447,6 +447,17 @@ test("ScanForColors", function () { equal (results[0], blackHex.toString()); equal (results[1], "orange"); + var multiple_lines_single_results = "body { \n font-size: 12px;\n\n text-color: rgba (255, 0, 0, .5) }"; + results = tinycolor.scanForColors(multiple_lines_single_results); + equal (results.length, 1); + equal (results[0].toRgbString(), "rgba(255, 0, 0, 0.5)"); + + var multiple_lines_multiple_results = "body { \n background-color: hsl 0 1.0 0.5;\n\n text-color: rgba (255, 0, 0, .5) }"; + results = tinycolor.scanForColors(multiple_lines_multiple_results); + equal (results.length, 2); + equal (results[0].toHslString(), "hsl(0, 100%, 50%)"); + equal (results[1].toRgbString(), "rgba(255, 0, 0, 0.5)"); + equal (tinycolor.scanForColors("body {").length, 0); }); From fdccf76d39b54273a1feb167fa9227772706aa3f Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Fri, 21 Feb 2014 13:20:41 -0800 Subject: [PATCH 5/8] Add word boundaries to protect against aggressive word matches --- tinycolor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tinycolor.js b/tinycolor.js index 73fae38..c71fbc7 100644 --- a/tinycolor.js +++ b/tinycolor.js @@ -613,7 +613,8 @@ tinycolor.constructMatcherRegExp = function() { for (name in names) { if (names.hasOwnProperty(name)) { - joinedMatcherRegExp.push(name); + // "\\b" prevents words like imporTANt + joinedMatcherRegExp.push("\\b" + name + "\\b"); } } From 41d153933fb77ac0fd55a865976ede9efc088f92 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Mon, 24 Feb 2014 14:07:20 -0800 Subject: [PATCH 6/8] Always match word boundaries --- tinycolor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinycolor.js b/tinycolor.js index c71fbc7..b5c8943 100644 --- a/tinycolor.js +++ b/tinycolor.js @@ -623,9 +623,9 @@ tinycolor.constructMatcherRegExp = function() { if (matchers.hasOwnProperty(match)) { var matchAsString = String(matchers[match]); if (/^hex/.test(match)) - matchAsString = matchAsString + "\\b"; + matchAsString = "#" + matchAsString; - joinedMatcherRegExp.push(matchAsString.replace(/\^|\$|\//g, '')); + joinedMatcherRegExp.push("(?:\\s*|\\b)" + matchAsString.replace(/\^|\$|\//g, '') + "\\b"); } } From c5ffa5cf84fbd943f48687f42e76be25a0bfaff0 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Mon, 24 Feb 2014 14:07:31 -0800 Subject: [PATCH 7/8] Add more (negative) tests --- test/test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test.js b/test/test.js index f0681f6..fbd6e4f 100644 --- a/test/test.js +++ b/test/test.js @@ -459,6 +459,18 @@ test("ScanForColors", function () { equal (results[1].toRgbString(), "rgba(255, 0, 0, 0.5)"); equal (tinycolor.scanForColors("body {").length, 0); + + var negativeMatchThreeHex = "background-position: 100% 50%;", + results = tinycolor.scanForColors(negativeMatchThreeHex); + equal (results.length, 0); + + var positiveMatchThreeHex = "background-position: #100% 50%;", + results = tinycolor.scanForColors(positiveMatchThreeHex); + equal (results.length, 1); + + var negativeMatchString = "margin: 0 !important;", + results = tinycolor.scanForColors(negativeMatchString); + equal (results.length, 0); }); test("Filters", function () { From c46f11da9bb2471182799c198aa4e6da40e30f13 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Mon, 24 Feb 2014 14:10:35 -0800 Subject: [PATCH 8/8] Protect against `#fade` --- test/test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test.js b/test/test.js index fbd6e4f..e12acf0 100644 --- a/test/test.js +++ b/test/test.js @@ -471,6 +471,10 @@ test("ScanForColors", function () { var negativeMatchString = "margin: 0 !important;", results = tinycolor.scanForColors(negativeMatchString); equal (results.length, 0); + + var negativeMatchFourHex = "color: #fade;", + results = tinycolor.scanForColors(negativeMatchFourHex); + equal (results.length, 0); }); test("Filters", function () {