//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package flashx.textLayout.property { import flash.text.engine.TabAlignment; import flashx.textLayout.debug.assert; import flashx.textLayout.formats.FormatValue; import flashx.textLayout.formats.TabStopFormat; [ExcludeClass] /** Property for tab stops. Extends ArrayProperty; setter takes a string representation of tab stops in addition to an array. @private */ public class TabStopsProperty extends ArrayProperty { public function TabStopsProperty(nameValue:String, defaultValue:Array, inherited:Boolean, category:String) { super (nameValue, defaultValue, inherited, category, TabStopFormat); } /** Helper function when setting the property */ public override function setHelper(currVal:*,newVal:*):* { // null is a real value - DO NOT map to undefined like the others // null, undefined and INHERIT are all valid for arrays if (newVal == null || newVal == FormatValue.INHERIT) return newVal; // Accepts either an array or a string representation var tabStops:Array = newVal as Array; if (tabStops) { if (!checkArrayTypes(tabStops)) { Property.errorHandler(this,newVal); return currVal; } } else { var valString:String = newVal as String; if (!valString) { Property.errorHandler(this,newVal); return currVal; } // Parse the string representation and create an equivalent array tabStops = new Array(); // Replace escape sequences (\ followed by a space or \) with placeholder strings // that can't naturally occur in the passed-in string valString = valString.replace(_escapeBackslashRegex, _backslashPlaceHolder); valString = valString.replace(_escapeSpaceRegex, _spacePlaceHolder); _tabStopRegex.lastIndex = 0; do { var result:Object = _tabStopRegex.exec(valString); if (!result) break; // no more matches var tabStop:TabStopFormat = new TabStopFormat(); switch (result[1].toLowerCase()) { case "s": case "": // START is the default tabStop.alignment = TabAlignment.START; break; case "c": tabStop.alignment = TabAlignment.CENTER; break; case "e": tabStop.alignment = TabAlignment.END; break; case "d": tabStop.alignment = TabAlignment.DECIMAL; break; } var position:Number = Number(result[2]); if (isNaN(position)) { Property.errorHandler(this,newVal); return currVal; } tabStop.position = position; if (tabStop.alignment == TabAlignment.DECIMAL) { if (result[3] == "") tabStop.decimalAlignmentToken = "."; //default else { // strip the leading vertical bar and restore \ and space characters where intended tabStop.decimalAlignmentToken = result[3].slice(1).replace(_backslashPlaceholderRegex, "\\"); tabStop.decimalAlignmentToken = tabStop.decimalAlignmentToken.replace(_spacePlaceholderRegex, " "); } } else if (result[3] != "") { Property.errorHandler(this,newVal); return currVal; // if alignment is not decimal, the alignment token is not allowed } tabStops.push(tabStop); } while (true); } return tabStops.sort(compareTabStopFormats); } /** @private */ public override function toXMLString(val:Object):String { var str:String = ""; var tabStops:Array = val as Array; for each (var tabStop:TabStopFormat in tabStops) { if (str.length) str += " "; switch (tabStop.alignment) { case TabAlignment.START: str += "s"; break; case TabAlignment.CENTER: str += "c"; break; case TabAlignment.END: str += "e"; break; case TabAlignment.DECIMAL: str += "d"; break; } str += tabStop.position.toString(); if (tabStop.alignment == TabAlignment.DECIMAL) { var escapedAlignmentToken:String = tabStop.decimalAlignmentToken.replace(_backslashRegex, "\\\\"); escapedAlignmentToken = escapedAlignmentToken.replace(_spaceRegex, "\\ "); str += "|" + escapedAlignmentToken; } } return str; } private static function compareTabStopFormats(a:TabStopFormat, b:TabStopFormat):Number { return a.position == b.position ? 0 : a.position < b.position ? -1 : 1; } // Alignment type: [sScCeEdD]? - Atmost 1 occurance of one of s/c/e/d (or upper-case equivalents) // Position: [^| ]+ - At least character which is not a space or | (further validation is done by the Number constructor) // Alignment token and separator:(|[^ ]*)? - Atmost one occurance of a | followed by 0 or more non-space characters // Delimiter: ( |$) - A space or end-of-string private static const _tabStopRegex:RegExp = /([sScCeEdD]?)([^| ]+)(|[^ ]*)?( |$)/g; private static const _escapeBackslashRegex:RegExp = /\\\\/g; private static const _escapeSpaceRegex:RegExp = /\\ /g; private static const _backslashRegex:RegExp = /\\/g; private static const _spaceRegex:RegExp = / /g; private static const _backslashPlaceholderRegex:RegExp = /\uE000/g; private static const _spacePlaceholderRegex:RegExp = /\uE001/g; // Replace escape sequences (\ followed by a space or \) with placeholder strings // containing characters from Unicode private use area (won't naturally occur in the passed-in string) private static const _backslashPlaceHolder:String = String.fromCharCode(0xE000); private static const _spacePlaceHolder:String = String.fromCharCode(0xE001); } }