diff --git a/src/com/flashartofwar/fcss/stylesheets/FStyleSheet.as b/src/com/flashartofwar/fcss/stylesheets/FStyleSheet.as new file mode 100644 index 0000000..2e9b3fa --- /dev/null +++ b/src/com/flashartofwar/fcss/stylesheets/FStyleSheet.as @@ -0,0 +1 @@ + /** *
Original Author: jessefreeman
*Class File: StyleSheet.as
* *Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions:
* *The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software.
* *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE.
* *Licensed under The MIT License
*Redistributions of files must retain the above copyright notice.
* *Revisions
* 1.0 Initial version Aug 28, 2009
Class constructor.
*/ public function FStyleSheet() { super(); } /** *Check to see if style exists.
* * @param styleName * @return * */ public function hasStyle(name:String):Boolean { return (_styleNames.indexOf(name) == -1) ? false : true; } /** *Returns a list of related styles.
* @param styleName * @return * */ public function relatedStyle(name:String):Array { return relatedstyleIndex[name].slice(); } /** * * @return * */ public function toString():String { return cssText; } /** * * */ public function clear():void { cssText = ""; cachedstyles.length = 0; _styleNames.length = 0; } /** * * @param cssTest * */ public function parseCSS(cssTest:String, useCSSTidy:Boolean = true):void { cachedstyles.length = 0; cssText = useCSSTidy ? tidy(cssTest) : cssTest; indexCSS(cssText); // Force @variables to cache var style:Style = getStyle("@variables"); } /** *This looks up a style and returns an object. To help support style * inheritance you can also pass in an comma delimited string and have * the list merged into one style based on the order of the list. The * first item being lower all the way up to the last in the list.
* * @param styleName * @return * */ public function getStyle(... styleNames):Style { // Split styles and get the total related classes var total:Number = styleNames.length; var baseProperties:Style = createEmptyStyle(); // Loop through styles and merges them into a single style. for (var i:Number = 0; i < total; i++) { if (hasStyle(styleNames[i])) { var currentPropertiesID:String = styleNames[i]; var tempProperties:Style = styleLookup(currentPropertiesID); baseProperties.merge(tempProperties); } } // Returns megred style return baseProperties; } /** * * @param styleName * @param propertystyle * */ public function newStyle(name:String, style:Style):void { if (_styleNames.indexOf(name) == -1) _styleNames.push(name); if(style.styleName != name) style.styleName = name; cachedstyles[name] = style; } /** *Creates a style sheet string from supplied style names. You can combine * Properties into a larger style sheet by separating styles by a comma. * If no styleName is provided the entire set of styles will be included * in the new styleSheet.
* *Its important to note that this is incredibly expensive to perform * on large CamoStyeSheets. To avoid this, make sure you pass in only * the styles you need.
* * @param styleName comma separated list of styles that will be used * to create a style sheet string. * @return String style sheet from passed in styles * */ public function clone(... styleNames):FStyleSheet { var tempStyleSheet:FStyleSheet = new FStyleSheet(); var total:Number = styleNames.length; if (total == 0) { tempStyleSheet.parseCSS(cssText.toString()); } else { var tempCSSText:String = ""; //TODO right now this just combines styles into one string, need to add support for style inheritance. // Loop through styles and merges them into a single style. for (var i:Number = 0; i < total; i++) { if ((hasStyle(styleNames[i]) && (styleNames[i] is String))) { tempCSSText += getStyle(styleNames[i]).toString(); } } // Strip classes from a styles tempCSSText = tempCSSText.replace(FIND_A_HREF_CLASS, "a"); tempStyleSheet.parseCSS(tempCSSText, false); } return tempStyleSheet; } /** * * @return * */ protected function createEmptyStyle():Style { return new Style(); } /** * * @param cssTest * @return * */ protected function tidy(cssTest:String):String { return CSSTidyUtil.tidy(cssTest); } /** *Does a preliminary run through of the css styles and indexes them. * This is only done once and to helps speed up the retrieval of the raw * css style cssText. Its important to note that this index is only for * the style and its string content. Any CSS props inside of the * style are not parsed here. This is simply used as a lookup table * for the raw cssText.
* * @param css * */ protected function indexCSS(css:String):void { // Use RegEx to get all css blocks - anything inside of { } along with the style (name) var blocks:Array = css.match(CSS_BLOCKS); // get reference of the total blocks found to speed up for i loop var total:Number = blocks.length; // Loop through all styles, get the style name and its cssText, then store it in the index for (var i:int = 0; i < total; i++) { parseStyleBlock(blocks[i]); } } /** *This splits up a CSS style from its cssText. By doing the split on * the "{" we can assume anything in index 0 of the array is the style's * name, and the rest is the css props. Also we cut off the last character * (in this case the trailing "}") to make sure we get a clean reference * to the css properties inside.
* * @param cssClass * @return * */ protected function parseStyleBlock(cssClass:String):void { var splitBlock:Array = cssClass.split("{"); var related:Array = String(splitBlock[0]).split(" "); var styleName:String = related.pop(); var indexOfColon:Number = styleName.indexOf(":"); if (indexOfColon != -1) { var pseudoBasePropertiesID:String = styleName.substring(0, indexOfColon); related.push(pseudoBasePropertiesID); } var styleBlock:String = String(splitBlock[1]).substr(0, String(splitBlock[1]).length - 1); if (_styleNames.indexOf(styleName) != -1) { styleIndex[styleName] = String(styleIndex[styleName]).concat(styleBlock); clearCachedClass(styleName); } else { _styleNames.push(styleName); styleIndex[styleName] = styleBlock; } // Save out array of left over classes if (!relatedstyleIndex[styleName]) { relatedstyleIndex[styleName] = related; } } /** * * @param styleName */ protected function clearCachedClass(styleName:String):void { if (cachedstyles[styleName]) { delete cachedstyles[styleName]; } } /** *This goes through a style's name and looks for any related * classes separated by a space. With this we can keep an index of related * classes based on the class's id as a key. This lets us quickly * reference any related class names on the fly without having to re-loop * through the css cssText.
* * @param classes * @param index * @return * */ protected function relatedClasses(classes:String):String { // Split out names of classes var related:Array = classes.split(" "); var classID:String = related.pop(); var indexOfColon:Number = classID.indexOf(":"); if (indexOfColon != -1) { var pseudoBasePropertiesID:String = classID.substring(0, indexOfColon); related.push(pseudoBasePropertiesID); } // Save out array of left over classes relatedstyleIndex[classID] = related; // Return what was found return classID; } /** *Core lookup function and is responsible for parsing out css * styles, finding related classes, and putting them all together * into a clean Properties.
* *The first step is to look for a cached version of the class already * requested. If that does not exist it looks up the class id from the * cssIndex. Once the cssText is found it can start looping through related * classes to build a base css object for the desired class to inherit * then override. Once this is done, it is cached in the cachedProperties * dictionary and the created object is returned.
* * @param styleName * @return * */ protected function styleLookup(styleName:String):Style { var tempProperties:Style = (cachedstyles[styleName]) ? cachedstyles[styleName] : null; if (!tempProperties) { tempProperties = createEmptyStyle(); if (hasStyle(styleName)) { // Begin CSS lookup var styleData:String = styleIndex[styleName]; var subjectProperties:Style = convertStringListToProperties(styleData); var ancestors:Array = relatedstyleIndex[styleName]; var totalAncestors:Number = ancestors.length; var ancestorProperties:Style; for (var i:int = 0; i < totalAncestors; i++) { ancestorProperties = styleLookup(ancestors[i]); tempProperties.merge(ancestorProperties); } tempProperties.merge(subjectProperties); tempProperties.styleName = styleName; newStyle(styleName, tempProperties); } } return tempProperties.clone() as Style; } /** *This function converts a String list to an object. Used to help * split up complex, string lists form css cssText.
* * @param cssText * @param propDelim * @param listDelim */ protected function convertStringListToProperties(cssText:String, propDelim:String = ":", listDelim:String = ";"):Style { // Start - Test for Variables if(cachedstyles["@variables"]) { // If we have a cached variable style run it through the replacer cssText = replaceVaribales(cssText, cachedstyles["@variables"]); } // End - Test for Variables var tempObject:Style = createEmptyStyle(); var list:Array = cssText.split(listDelim); var total:int = list.length; var i:int; // Loop through properties for (i = 0; i < total; i++) { var delimLocation:Number = list[i].indexOf(propDelim); var prop:String = cleanUpProp(list[i].slice(0, delimLocation)); if (prop != "") { var value:String = cleanUpValue(prop, list[i].slice(delimLocation + 1)); tempObject[prop] = value; } } return tempObject; } /** * * @param prop * @return * */ protected function cleanUpProp(prop:String):String { return prop; } /** * * @param prop * @param value * @return * */ protected function cleanUpValue(prop:String, value:String):String { switch (prop) { case STYLE_SHEET: case STYLE_SHEET_CAMEL: var styleNames:Array = value.split(","); return buildStyleSheetString.apply(null, styleNames); break; default: return value; break; } } /** * * @param styleNames * @return * */ protected function buildStyleSheetString(... styleNames):String { var cssTest:String = clone.apply(null, styleNames).toString(); return cssTest; } /** * This util will take a string, search for var(tokens) and replace them * with the values in the supplied object. * * @param text - text you would like to search for tokens * @param paramObj - an object to use to find and replace tokens with * @return * */ protected function replaceVaribales(text:String, paramObj: Object) : String{ return text.replace(CSS_VAR_PATTERN,function():*{return paramObj[arguments[1]];}); } } } \ No newline at end of file diff --git a/src/com/flashartofwar/fcss/stylesheets/StyleSheet.as b/src/com/flashartofwar/fcss/stylesheets/StyleSheet.as deleted file mode 100644 index a237bfa..0000000 --- a/src/com/flashartofwar/fcss/stylesheets/StyleSheet.as +++ /dev/null @@ -1 +0,0 @@ - /** *Original Author: jessefreeman
*Class File: StyleSheet.as
* *Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions:
* *The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software.
* *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE.
* *Licensed under The MIT License
*Redistributions of files must retain the above copyright notice.
* *Revisions
* 1.0 Initial version Aug 28, 2009
Class constructor.
*/ public function StyleSheet() { super(); } /** *Check to see if style exists.
* * @param styleName * @return * */ public function hasStyle(name:String):Boolean { return (_styleNames.indexOf(name) == -1) ? false : true; } /** *Returns a list of related styles.
* @param styleName * @return * */ public function relatedStyle(name:String):Array { return relatedstyleIndex[name].slice(); } /** * * @return * */ public function toString():String { return cssText; } /** * * */ public function clear():void { cssText = ""; cachedstyles.length = 0; _styleNames.length = 0; } /** * * @param cssTest * */ public function parseCSS(cssTest:String, useCSSTidy:Boolean = true):void { cachedstyles.length = 0; cssText = useCSSTidy ? tidy(cssTest) : cssTest; indexCSS(cssText); // Force @variables to cache var style:Style = getStyle("@variables"); } /** *This looks up a style and returns an object. To help support style * inheritance you can also pass in an comma delimited string and have * the list merged into one style based on the order of the list. The * first item being lower all the way up to the last in the list.
* * @param styleName * @return * */ public function getStyle(... styleNames):Style { // Split styles and get the total related classes var total:Number = styleNames.length; var baseProperties:Style = createEmptyStyle(); // Loop through styles and merges them into a single style. for (var i:Number = 0; i < total; i++) { if (hasStyle(styleNames[i])) { var currentPropertiesID:String = styleNames[i]; var tempProperties:Style = styleLookup(currentPropertiesID); baseProperties.merge(tempProperties); } } // Returns megred style return baseProperties; } /** * * @param styleName * @param propertystyle * */ public function newStyle(name:String, style:Style):void { if (_styleNames.indexOf(name) == -1) _styleNames.push(name); if(style.styleName != name) style.styleName = name; cachedstyles[name] = style; } /** *Creates a style sheet string from supplied style names. You can combine * Properties into a larger style sheet by separating styles by a comma. * If no styleName is provided the entire set of styles will be included * in the new styleSheet.
* *Its important to note that this is incredibly expensive to perform * on large CamoStyeSheets. To avoid this, make sure you pass in only * the styles you need.
* * @param styleName comma separated list of styles that will be used * to create a style sheet string. * @return String style sheet from passed in styles * */ public function clone(... styleNames):StyleSheet { var tempStyleSheet:StyleSheet = new StyleSheet(); var total:Number = styleNames.length; if (total == 0) { tempStyleSheet.parseCSS(cssText.toString()); } else { var tempCSSText:String = ""; //TODO right now this just combines styles into one string, need to add support for style inheritance. // Loop through styles and merges them into a single style. for (var i:Number = 0; i < total; i++) { if ((hasStyle(styleNames[i]) && (styleNames[i] is String))) { tempCSSText += getStyle(styleNames[i]).toString(); } } // Strip classes from a styles tempCSSText = tempCSSText.replace(FIND_A_HREF_CLASS, "a"); tempStyleSheet.parseCSS(tempCSSText, false); } return tempStyleSheet; } /** * * @return * */ protected function createEmptyStyle():Style { return new Style(); } /** * * @param cssTest * @return * */ protected function tidy(cssTest:String):String { return CSSTidyUtil.tidy(cssTest); } /** *Does a preliminary run through of the css styles and indexes them. * This is only done once and to helps speed up the retrieval of the raw * css style cssText. Its important to note that this index is only for * the style and its string content. Any CSS props inside of the * style are not parsed here. This is simply used as a lookup table * for the raw cssText.
* * @param css * */ protected function indexCSS(css:String):void { // Use RegEx to get all css blocks - anything inside of { } along with the style (name) var blocks:Array = css.match(CSS_BLOCKS); // get reference of the total blocks found to speed up for i loop var total:Number = blocks.length; // Loop through all styles, get the style name and its cssText, then store it in the index for (var i:int = 0; i < total; i++) { parseStyleBlock(blocks[i]); } } /** *This splits up a CSS style from its cssText. By doing the split on * the "{" we can assume anything in index 0 of the array is the style's * name, and the rest is the css props. Also we cut off the last character * (in this case the trailing "}") to make sure we get a clean reference * to the css properties inside.
* * @param cssClass * @return * */ protected function parseStyleBlock(cssClass:String):void { var splitBlock:Array = cssClass.split("{"); var related:Array = String(splitBlock[0]).split(" "); var styleName:String = related.pop(); var indexOfColon:Number = styleName.indexOf(":"); if (indexOfColon != -1) { var pseudoBasePropertiesID:String = styleName.substring(0, indexOfColon); related.push(pseudoBasePropertiesID); } var styleBlock:String = String(splitBlock[1]).substr(0, String(splitBlock[1]).length - 1); if (_styleNames.indexOf(styleName) != -1) { styleIndex[styleName] = String(styleIndex[styleName]).concat(styleBlock); clearCachedClass(styleName); } else { _styleNames.push(styleName); styleIndex[styleName] = styleBlock; } // Save out array of left over classes if (!relatedstyleIndex[styleName]) { relatedstyleIndex[styleName] = related; } } /** * * @param styleName */ protected function clearCachedClass(styleName:String):void { if (cachedstyles[styleName]) { delete cachedstyles[styleName]; } } /** *This goes through a style's name and looks for any related * classes separated by a space. With this we can keep an index of related * classes based on the class's id as a key. This lets us quickly * reference any related class names on the fly without having to re-loop * through the css cssText.
* * @param classes * @param index * @return * */ protected function relatedClasses(classes:String):String { // Split out names of classes var related:Array = classes.split(" "); var classID:String = related.pop(); var indexOfColon:Number = classID.indexOf(":"); if (indexOfColon != -1) { var pseudoBasePropertiesID:String = classID.substring(0, indexOfColon); related.push(pseudoBasePropertiesID); } // Save out array of left over classes relatedstyleIndex[classID] = related; // Return what was found return classID; } /** *Core lookup function and is responsible for parsing out css * styles, finding related classes, and putting them all together * into a clean Properties.
* *The first step is to look for a cached version of the class already * requested. If that does not exist it looks up the class id from the * cssIndex. Once the cssText is found it can start looping through related * classes to build a base css object for the desired class to inherit * then override. Once this is done, it is cached in the cachedProperties * dictionary and the created object is returned.
* * @param styleName * @return * */ protected function styleLookup(styleName:String):Style { var tempProperties:Style = (cachedstyles[styleName]) ? cachedstyles[styleName] : null; if (!tempProperties) { tempProperties = createEmptyStyle(); if (hasStyle(styleName)) { // Begin CSS lookup var styleData:String = styleIndex[styleName]; var subjectProperties:Style = convertStringListToProperties(styleData); var ancestors:Array = relatedstyleIndex[styleName]; var totalAncestors:Number = ancestors.length; var ancestorProperties:Style; for (var i:int = 0; i < totalAncestors; i++) { ancestorProperties = styleLookup(ancestors[i]); tempProperties.merge(ancestorProperties); } tempProperties.merge(subjectProperties); tempProperties.styleName = styleName; newStyle(styleName, tempProperties); } } return tempProperties.clone() as Style; } /** *This function converts a String list to an object. Used to help * split up complex, string lists form css cssText.
* * @param cssText * @param propDelim * @param listDelim */ protected function convertStringListToProperties(cssText:String, propDelim:String = ":", listDelim:String = ";"):Style { // Start - Test for Variables if(cachedstyles["@variables"]) { // If we have a cached variable style run it through the replacer cssText = replaceVaribales(cssText, cachedstyles["@variables"]); } // End - Test for Variables var tempObject:Style = createEmptyStyle(); var list:Array = cssText.split(listDelim); var total:int = list.length; var i:int; // Loop through properties for (i = 0; i < total; i++) { var delimLocation:Number = list[i].indexOf(propDelim); var prop:String = cleanUpProp(list[i].slice(0, delimLocation)); if (prop != "") { var value:String = cleanUpValue(prop, list[i].slice(delimLocation + 1)); tempObject[prop] = value; } } return tempObject; } /** * * @param prop * @return * */ protected function cleanUpProp(prop:String):String { return prop; } /** * * @param prop * @param value * @return * */ protected function cleanUpValue(prop:String, value:String):String { switch (prop) { case STYLE_SHEET: case STYLE_SHEET_CAMEL: var styleNames:Array = value.split(","); return buildStyleSheetString.apply(null, styleNames); break; default: return value; break; } } /** * * @param styleNames * @return * */ protected function buildStyleSheetString(... styleNames):String { var cssTest:String = clone.apply(null, styleNames).toString(); return cssTest; } /** * This util will take a string, search for var(tokens) and replace them * with the values in the supplied object. * * @param text - text you would like to search for tokens * @param paramObj - an object to use to find and replace tokens with * @return * */ protected function replaceVaribales(text:String, paramObj: Object) : String{ return text.replace(CSS_VAR_PATTERN,function():*{return paramObj[arguments[1]];}); } } } \ No newline at end of file diff --git a/src/com/flashartofwar/fcss/stylesheets/StyleSheetCollection.as b/src/com/flashartofwar/fcss/stylesheets/StyleSheetCollection.as index de9970f..dfa280b 100644 --- a/src/com/flashartofwar/fcss/stylesheets/StyleSheetCollection.as +++ b/src/com/flashartofwar/fcss/stylesheets/StyleSheetCollection.as @@ -78,7 +78,7 @@ package com.flashartofwar.fcss.stylesheets public function get baseStyleSheet():IStyleSheet { if (!styleSheets[baseStyleSheetName]) - addStyleSheet(baseStyleSheetName, new StyleSheet()); + addStyleSheet(baseStyleSheetName, new FStyleSheet()); return styleSheets[baseStyleSheetName]; } @@ -148,7 +148,7 @@ package com.flashartofwar.fcss.stylesheets */ public function parseCSS(CSSText:String, compressText:Boolean = true):void { - var styleSheet:StyleSheet = new StyleSheet(); + var styleSheet:FStyleSheet = new FStyleSheet(); styleSheet.parseCSS(CSSText, compressText); addStyleSheet(defaultSheetName + (totalStyleSheets + 1), styleSheet); @@ -172,7 +172,7 @@ package com.flashartofwar.fcss.stylesheets */ public function removeStyleSheet(id:String):IStyleSheet { - var styleSheet:StyleSheet = styleSheets[id]; + var styleSheet:FStyleSheet = styleSheets[id]; delete styleSheets[id]; _totalSheets--; return styleSheet; diff --git a/src/com/flashartofwar/fcss/utils/CSSTidyUtil.as b/src/com/flashartofwar/fcss/utils/CSSTidyUtil.as index efe191d..0e86207 100644 --- a/src/com/flashartofwar/fcss/utils/CSSTidyUtil.as +++ b/src/com/flashartofwar/fcss/utils/CSSTidyUtil.as @@ -16,6 +16,10 @@ package com.flashartofwar.fcss.utils { protected static const COMPRESS_CSS:RegExp = /\s*([@{}:;,]|\)\s|\s\()\s*|\/\*([^*\\\\]|\*(?!\/))+\*\/|[\n\r\t]|(px)|(%)/g; + protected static const OPENING_BRACKET:RegExp = /({)/g; + protected static const SINGLE_CSS_LINE:RegExp = /(;)/g; + protected static const CSS_PROPERTY_DIVIDER:RegExp = /(:)/g; + protected static const CLOSING_BRACKET:RegExp = /\t(})/g; /** *This uses regex to remove spaces, breaks, "px" and other items
@@ -29,6 +33,15 @@ package com.flashartofwar.fcss.utils
{
return cssText.replace(COMPRESS_CSS, "$1");
}
+
+ public static function format(cssText:String):String
+ {
+ var unTidyText:String = cssText.replace(OPENING_BRACKET, "\n$1\n\t");
+ unTidyText = unTidyText.replace(SINGLE_CSS_LINE, "$1\n\t");
+ unTidyText = unTidyText.replace(CSS_PROPERTY_DIVIDER, " $1 ");
+ unTidyText = unTidyText.replace(CLOSING_BRACKET, "$1\n\n");
+ return unTidyText;
+ }
}
}
diff --git a/tools/FCSSEditor/src/FCSSEditor-app.xml b/tools/FCSSEditor/src/FCSSEditor-app.xml
new file mode 100644
index 0000000..ff64f97
--- /dev/null
+++ b/tools/FCSSEditor/src/FCSSEditor-app.xml
@@ -0,0 +1,135 @@
+
+