diff --git a/src/language/CSSUtils.js b/src/language/CSSUtils.js index a331e798008..e94349c5036 100644 --- a/src/language/CSSUtils.js +++ b/src/language/CSSUtils.js @@ -22,7 +22,7 @@ */ -/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ +/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, regexp: true */ /*global define, $, CodeMirror, _parseRuleList: true */ // JSLint Note: _parseRuleList() is cyclical dependency, not a global function. @@ -1115,10 +1115,62 @@ define(function (require, exports, module) { return _stripAtRules(selector); } + // removes css comments from the content + function _removeComments(_content) { + return _content.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\//g, ''); + } + + // removes strings from the content + function _removeStrings(_content) { + return _content.replace(/[^\\]\"(.*)[^\\]\"|[^\\]\'(.*)[^\\]\'+/g, ''); + } + + /** + * Reduces the style sheet by removing comments and strings + * so that the content can be parsed using a regular expression + * @param {!String} content to reduce + * @return {String} reduced content + */ + function reduceStyleSheetForRegExParsing(content) { + return _removeStrings(_removeComments(content)); + } + + /** + * Extracts all named flow instances + * @param {!String} text to extract from + * @return {?Array.} array of unique flow names + */ + function extractAllNamedFlows(text) { + var namedFlowRegEx = /(?:flow\-(into|from)\:[ \t\n\r]*)([a-z0-9_\-]+)(?:[ \t\n\r]*;)/gi, + result = [], + names = {}, + thisMatch; + + // Reduce the content so that matches + // inside strings and comments are ignored + text = reduceStyleSheetForRegExParsing(text); + + // Find the first match + thisMatch = namedFlowRegEx.exec(text); + + // Iterate over the matches and add them to result + while (thisMatch) { + var thisName = thisMatch[2]; + if (!names.hasOwnProperty(thisName)) { + names[thisName] = result.push(thisName); + } + thisMatch = namedFlowRegEx.exec(text); + } + + return result; + } + exports._findAllMatchingSelectorsInText = _findAllMatchingSelectorsInText; // For testing only exports.findMatchingRules = findMatchingRules; exports.extractAllSelectors = extractAllSelectors; + exports.extractAllNamedFlows = extractAllNamedFlows; exports.findSelectorAtDocumentPos = findSelectorAtDocumentPos; + exports.reduceStyleSheetForRegExParsing = reduceStyleSheetForRegExParsing; exports.SELECTOR = SELECTOR; exports.PROP_NAME = PROP_NAME; diff --git a/test/spec/CSSUtils-test-files/regions.css b/test/spec/CSSUtils-test-files/regions.css new file mode 100644 index 00000000000..7c07a9fd64e --- /dev/null +++ b/test/spec/CSSUtils-test-files/regions.css @@ -0,0 +1,77 @@ +/* basic tests */ +article.content { + flow-into: main; +} + +section.layout > div { + flow-from: main; +} + + +#jeff.content { + flow-into: jeff; +} + +#jeff.layout > div { + flow-from: jeff; +} + +/* exclude matches inside comments tests */ + +/* +p.content { + flow-into: carter; +} + +p.layout > div { + flow-from: carter; +} +*/ + +/* exclude matches inside strings tests */ + +div { + content: "/* p.content { flow-into: carter; } p.layout > div { flow-from: carter; } */"; +} + + +div { + content: "html.content { flow-into: dexter; } html.layout > div { flow-from: dexter; }"; +} + + +/* +div.content { + content: "flow-into: martin;"; +} + +div.layout > div { + content: "flow-from: martin;"; +} +*/ + +/* multi-line property tests */ + +#randy.content { + flow-into: + randy + ; +} + +#randy.layout > div { + flow-from: randy; +} + +/* test to exclude duplicates */ +#yin.content { + flow-into: jeff; +} + +#yin.layout > div { + flow-from: jeff; +} + +/* flow-from only tests */ +#raymond.layout > div { + flow-from: lim; +} diff --git a/test/spec/CSSUtils-test.js b/test/spec/CSSUtils-test.js index f8df31facfc..d88859ff995 100644 --- a/test/spec/CSSUtils-test.js +++ b/test/spec/CSSUtils-test.js @@ -41,7 +41,8 @@ define(function (require, exports, module) { offsetsCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/offsets.css"), bootstrapCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/bootstrap.css"), escapesCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/escaped-identifiers.css"), - embeddedHtmlFileEntry = new NativeFileSystem.FileEntry(testPath + "/embedded.html"); + embeddedHtmlFileEntry = new NativeFileSystem.FileEntry(testPath + "/embedded.html"), + cssRegionsFileEntry = new NativeFileSystem.FileEntry(testPath + "/regions.css"); var contextTestCss = require("text!spec/CSSUtils-test-files/contexts.css"), selectorPositionsTestCss = require("text!spec/CSSUtils-test-files/selector-positions.css"); @@ -1922,4 +1923,20 @@ define(function (require, exports, module) { }); }); }); + + describe("CSS Regions", function () { + beforeEach(function () { + init(this, cssRegionsFileEntry); + }); + + it("should find named flows", function () { + var namedFlows = CSSUtils.extractAllNamedFlows(this.fileContent); + expect(namedFlows.length).toBe(4); + expect(namedFlows[0]).toBe("main"); + expect(namedFlows[1]).toBe("jeff"); + expect(namedFlows[2]).toBe("randy"); + expect(namedFlows[3]).toBe("lim"); + }); + + }); });