diff --git a/lib/flipcss.js b/lib/flipcss.js index 69233f0..64efe23 100644 --- a/lib/flipcss.js +++ b/lib/flipcss.js @@ -13,32 +13,57 @@ var flipcss = { * (?!y) -> not followed by y. * @private */ - _ruleMatchIgnorePattern: "(?![^\n]*/\\*\\s*!" - + "(direction-ignore|rtl-only|ltr-only)" - + ")", + _ruleMatchIgnorePattern: "(?![^\n]*/\\*\\s*!" + + "(direction-ignore|rtl-only|ltr-only)" + + ")",   /** - * Swap two words in a string + * Swap two words in a string. Used to swap "left" and "right", and + * vice versa. This function is pretty naive, and just swaps every instance + * of the words that it comes across, with some exceptions: + * - Words where word1 or word2 are parts of other words (like "copyright") + * are left alone. Words like "margin-left" are not. + * - You can set some flags to leave the words to be swapped unchanged + * in some contexts. * * @private * @param {String} string String to process - * @param {String} word1 Word to swap with word2 - * @param {String} word2 Word to swap with word1 + * @param {String} word1 Word to swap with word2 (alphanumeric chars only) + * @param {String} word2 Word to swap with word1 (alphanumeric chars only) + * @param {Boolean} flipFilenames * @returns {String} Processed string. - * - * NOTE: This function is stupid; word1 and word2 are expected to be - * alphanumeric characters, but no checking is performed, so it may - * break on other strings. */ - _swapWords: function(string, word1, word2) { - // Matches one of the given words, nothing else (i.e. the rest of the - // rule is ignored), except ignore pattern. - // Captures the found word. - var pattern = new RegExp( - "([^a-z0-9])(" + word1 + "|" + word2 + ")([^a-z0-9])" - + this._ruleMatchIgnorePattern, "g"); + _swapWords: function(string, word1, word2, flipFilenames, flipSelectors) { + // Regexp parts + var rp = []; + + // Should only match whole words, or words separated by e.g. a hyphen + // ("margin-left" would be matched, but not "copyright"). Thus, we + // need to check for alphanumeric chars right before and right + // after the word: + var noAlphaNum = "([^a-z0-9])"; + rp.push(noAlphaNum); // Charactes not allowed right before word + + // The two words to swap + rp.push("(" + word1 + "|" + word2 + ")"); + + // If filenames are not to be flipped: + if (!flipFilenames) { + rp.push("(?!.*\\))"); // Not inside parantheses (e.g. "url()") + }  +/* if (!flipSelectors) { + rp.push("(?!(.*\\s){)"); // Not inside parantheses (e.g. "url()") + }*/ + + rp.push(noAlphaNum); // Alphanumerics not allowed right before word + + // Ignore pattern, in case this specific rule is to be ignored. + rp.push(this._ruleMatchIgnorePattern); + + // Do replace + var pattern = new RegExp(rp.join(""), "g"); return string.replace(pattern, function(_, pre, word, post) { return pre + (word === word1 ? word2 : word1) + post; }); @@ -272,13 +297,16 @@ module.exports = { * @param {Boolean} flipPseudo Flip :before and :after. * @returns {String} Flipped CSS. */ - flip: function(string, warnings, flipPseudo) { + flip: function(string, warnings, flipPseudo, flipFilenames) { if (!warnings) { warnings = false; } if (!flipPseudo) { flipPseudo = false; } + if (!flipFilenames) { + flipFilenames = false; + }  // Output warnings: if (warnings) { @@ -294,7 +322,7 @@ module.exports = { string = flipcss._cleanLineFeeds(string);  // Do processing - string = flipcss._swapWords(string, "left", "right"); + string = flipcss._swapWords(string, "left", "right", flipFilenames); string = flipcss._swapValues(string); string = flipcss._swapBackgroundPosition(string); string = flipcss._swapBeforeAfter(string, flipPseudo); diff --git a/test/test.js b/test/test.js index 799f48b..2658adf 100644 --- a/test/test.js +++ b/test/test.js @@ -33,6 +33,17 @@ buster.assertions.add("flipsTo", { });   +buster.assertions.add("flipsFilename", { + assert: function (input, expectedOutput) { + this.output = lib.flip(input, false, false, true); + return this.output === expectedOutput; + }, + assertMessage: "Expected \"${0}\" to flip to \"${1}\", got \"${output}\".", + refuteMessage: "Expected \"${0}\" to not flip to \"${1}\"," + + " got \"${output}\"." +}); + + buster.assertions.add("flipsPseudo", { assert: function (input, expectedOutput) { this.output = lib.flip(input, false, true); @@ -120,15 +131,22 @@ buster.testCase("CSS word swapper", { assert.flipsTo(".copyright { float: right; }", ".copyright { float: left; }"); // "rights.png" Should not be changed (subword) - assert.flipsTo("background: url('rights.png')", - "background: url('rights.png')"); - // "arrow-left.png" Should be changed (subword) - assert.flipsTo("background: url('arrow-left.png')", - "background: url('arrow-right.png')"); + assert.flipsTo("/* rights */')", + "/* rights */')"); // "pull-right" should be changed (subword), and float should be changed assert.flipsTo(".pull-right { float: right; }", ".pull-left { float: left; }"); }, + "does not swap filenames by default": function() { + assert.flipsTo("background: url('arrow-left.png')", + "background: url('arrow-left.png')"); + }, + "swaps filenames when asked to": function() { + assert.flipsFilename("background: url('arrow-left.png')", + "background: url('arrow-right.png')"); + assert.flipsFilename("background: url('left-imgs/arrow.png')", + "background: url('right-imgs/arrow.png')"); + }, "leaves ignored rules alone": function() { // Basic case: Nothing should change. assert.flipsTo(".foo { clear: left; /* !direction-ignore */ }",