+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/libs/mockolate-0.8.0.swc b/build/libs/mockolate-0.8.0.swc
old mode 100644
new mode 100755
diff --git a/build/templates/README.tmpl b/build/templates/README.tmpl
old mode 100644
new mode 100755
diff --git a/config.xml b/config.xml
new file mode 100644
index 0000000..59275f5
--- /dev/null
+++ b/config.xml
@@ -0,0 +1,33 @@
+
+
+ true
+
+ en_US
+
+ true
+ false
+ true
+ true
+
+ ${flexlib}/libs/player/10.2/playerglobal.swc
+ ${flexlib}/libs/framework.swc
+ ${flexlib}/libs/rpc.swc
+
+ true
+
+
+ Event
+
+
+
+
+
+
+ 10.2.0
+
+
+ Paul Taylor
+ TinyTLF!
+ TinyTLF!
+
+
\ No newline at end of file
diff --git a/formatting.properties b/formatting.properties
deleted file mode 100644
index 3745828..0000000
--- a/formatting.properties
+++ /dev/null
@@ -1,100 +0,0 @@
-#FlexPrettyPrintSettings
-#Sun Aug 30 00:38:09 SAST 2009
-ASRearr_UseModifierOrder_Function=true
-Actionscript.useBraceStyle=false
-ASRearr_ModifierOrder_Property=,override,public,private,protected,internal,static,dynamic,final
-ASRearr_ModifierOrder_Class=,override,public,private,protected,internal,static,dynamic,final
-ASRearr_ElementSortStaticProperties=true
-ASRearr_ElementStaticFunctionVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true,
-MXML.attrWrapMode=52
-ASRearr_UseElementStaticPropertyVisibilityOrder=true
-ASRearr_ElementSortImports=true
-ASRearr_ElementOrder=Import,Include,Namespace Definition,Default Namespace,Namespace Use,Static Property,Static Function,Constructor,Property,Function,
-Actionscript.spacesAfterComma=1
-Actionscript.advancedSpacesAroundEqualsInOptionalParameters=0
-ASRearr_ElementFunctionVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true,
-MXML.onlyFormatASIfCDATABlock=false
-Actionscript.spacesBeforeComma=0
-Actionscript.advancedSpacesInsideLiteralBraces=1
-Actionscript.wrapArrayDeclMode=4
-Flex.useTabs=true
-ASRearr_ElementSortMetatags=false
-MXML.attrsToKeepOnSameLine=4
-MXML.sortAttrMode=0
-Actionscript.wrapMethodCallMode=4
-Actionscript.spacesBeforeControlOpenParen=1
-Actionscript.spacesAfterColons=0
-Actionscript.blankLinesBeforeFunctions=1
-MXML.spacesAroundEquals=0
-ASRearr_UseModifierOrder_Class=true
-Actionscript.breakLinesBeforeComma=false
-Actionscript.spacesAroundAssignment=1
-ASRearr_ElementSortIncludes=true
-MXML.wrapIndentStyle=1001
-Actionscript.leaveExtraWhitespaceAroundVarDecls=false
-MXML.maxLineLength=200
-Actionscript.putEmptyStatementsOnNewLine=true
-Actionscript.wrapExpressionMode=4
-ASRearr_UseElementStaticFunctionVisibilityOrder=true
-Actionscript.spacesAroundColons=0
-Actionscript.advancedUseSpacesAroundEqualsInOptionalParameters=false
-Actionscript.putOpenBraceOnNewLine=true
-Actionscript.blankLinesBeforeControlStatements=0
-MXML.blankLinesBeforeTags=0
-Actionscript.dontIndentPackageItems=false
-Actionscript.keepBlankLines=true
-MXML.keepBlankLines=true
-ASRearr_UseMetatagOrder=false
-MXML.attrGroups=name\=properties|sort\=11|wrap\=54|attrs\=allowDisjointSelection,allowMultipleSelection,allowThumbOverlap,allowTrackClick,autoLayout,autoRepeat,automationName,cachePolicy,class,clipContent,condenseWhite,conversion,creationIndex,creationPolicy,currentState,data,dataDescriptor,dataProvider,dataTipFormatFunction,dayNames,defaultButton,direction,disabledDays,disabledRanges,displayedMonth,displayedYear,doubleClickEnabled,emphasized,enabled,explicitHeight,explicitMaxHeight,explicitMaxWidth,explicitMinHeight,explicitMinWidth,explicitWidth,firstDayOfWeek,focusEnabled,fontContext,height,horizontalLineScrollSize,horizontalPageScrollSize,horizontalScrollBar,horizontalScrollPolicy,horizontalScrollPosition,htmlText,icon,iconField,id,imeMode,includeInLayout,indeterminate,label,labelField,labelFunction,labelPlacement,labels,layout,lineScrollSize,listData,liveDragging,maxChars,maxHeight,maxScrollPosition,maxWidth,maxYear,maximum,measuredHeight,measuredMinHeight,measuredMinWidth,measuredWidth,menuBarItemRenderer,menuBarItems,menus,minHeight,minScrollPosition,minWidth,minYear,minimum,mode,monthNames,monthSymbol,mouseFocusEnabled,pageScrollSize,pageSize,percentHeight,percentWidth,scaleX,scaleY,scrollPosition,selectable,selectableRange,selected,selectedDate,selectedField,selectedIndex,selectedRanges,showDataTip,showRoot,showToday,sliderDataTipClass,sliderThumbClass,snapInterval,source,states,stepSize,stickyHighlighting,styleName,text,text,thumbCount,tickInterval,tickValues,toggle,toolTip,transitions,truncateToFit,validationSubField,value,value,verticalLineScrollSize,verticalPageScrollSize,verticalScrollBar,verticalScrollPolicy,verticalScrollPosition,width,x,y,yearNavigationEnabled,yearSymbol,|\nname\=events|sort\=11|wrap\=54|attrs\=add,added,activate,addedToStage,buttonDown,change,childAdd,childIndexChange,childRemove,clickHandler,clear,click,complete,contextMenu,copy,creationComplete,currentStateChange,currentStateChanging,cut,dataChange,deactivate,doubleClick,dragComplete,dragDrop,dragEnter,dragExit,dragOver,dragStart,effectEnd,effectStart,enterFrame,enterState,exitFrame,exitState,focusIn,focusOut,frameConstructed,hide,httpStatus,init,initialize,invalid,ioError,itemClick,itemRollOut,itemRollOver,keyDown,keyFocusChange,keyUp,menuHide,menuShow,middleClick,middleMouseDown,middleMouseUp,mouseDown,mouseUp,mouseOver,mouseMove,mouseOut,mouseFocusChange,mouseWheel,mouseDownOutside,mouseWheelOutside,move,nativeDragComplete,nativeDragDrop,nativeDragEnter,nativeDragExit,nativeDragOver,nativeDragStart,nativeDragUpdate,open,paste,preinitialize,progress,record,remove,removed,removedFromStage,render,resize,rightClick,rightMouseDown,rightMouseUp,rollOut,rollOver,scroll,securityError,selectAll,show,tabChildrenChange,tabEnabledChange,tabIndexChange,thumbDrag,thumbPress,thumbRelease,toolTipCreate,toolTipEnd,toolTipHide,toolTipShow,toolTipShown,toolTipStart,updateComplete,unload,valid,valueCommit,|\nname\=styles|sort\=11|wrap\=54|attrs\=backgroundAlpha,backgroundAttachment,backgroundColor,backgroundDisabledColor,backgroundImage,backgroundSize,backgroundSkin,barColor,barSkin,borderColor,borderSides,borderSkin,borderStyle,borderThickness,bottom,color,cornerRadius,dataTipOffset,dataTipPrecision,dataTipStyleName,disabledColor,disabledIcon,disabledIconColor,disabledSkin,disbledOverlayAlpha,downArrowDisabledSkin,downArrowDownSkin,downArrowOverSkin,downArrowUpSkin,downIcon,downSkin,dropShadowColor,dropShadowEnabled,errorColor,fillAlphas,fillColors,focusAlpha,focusBlendMode,focusRoundedCorners,focusSkin,focusThickness,fontAntiAliasType,fontFamily,fontGridFitType,fontSharpness,fontSize,fontStyle,fontThickness,fontWeight,fontfamily,headerColors,headerStyleName,highlightAlphas,horizontalAlign,horizontalCenter,horizontalGap,horizontalScrollBarStyleName,icon,iconColor,indeterminateMoveInterval,indeterminateSkin,itemDownSkin,itemOverSkin,itemUpSkin,kerning,labelOffset,labelStyleName,labelWidth,leading,left,letterSpacing,maskSkin,menuStyleName,nextMonthDisabledSkin,nextMonthDownSkin,nextMonthOverSkin,nextMonthSkin,nextMonthUpSkin,nextYearDisabledSkin,nextYearDownSkin,nextYearOverSkin,nextYearSkin,nextYearUpSkin,overIcon,overSkin,paddingBottom,paddingLeft,paddingRight,paddingTop,prevMonthDisabledSkin,prevMonthDownSkin,prevMonthOverSkin,prevMonthSkin,prevMonthUpSkin,prevYearDisabledSkin,prevYearDownSkin,prevYearOverSkin,prevYearSkin,prevYearUpSkin,repeatDelay,repeatInterval,right,rollOverColor,rollOverIndicatorSkin,selectedDisabledIcon,selectedDisabledSkin,selectedDownIcon,selectedDownSkin,selectedOverIcon,selectedOverSkin,selectedUpIcon,selectedUpSkin,selectionColor,selectionIndicatorSkin,shadowColor,shadowDirection,shadowDistance,showTrackHighlight,skin,slideDuration,slideEasingFunction,strokeColor,strokeWidth,textAlign,textDecoration,textIndent,textRollOverColor,textSelectedColor,themeColor,thumbDisabledSkin,thumbDownSkin,thumbIcon,thumbOffset,thumbOverSkin,thumbUpSkin,tickColor,tickLength,tickOffset,tickThickness,todayColor,todayIndicatorSkin,todayStyleName,top,tracHighlightSkin,trackColors,trackHeight,trackMargin,trackSkin,upArrowDisabledSkin,upArrowDownSkin,upArrowOverSkin,upArrowUpSkin,upIcon,upSkin,verticalAlign,verticalCenter,verticalGap,verticalScrollBarStyleName,weekDayStyleName,|\nname\=effects|sort\=11|wrap\=54|attrs\=addedEffect,completeEffect,creationCompleteEffect,focusInEffect,focusOutEffect,hideEffect,mouseDownEffect,mouseUpEffect,moveEffect,removedEffect,resizeEffect,rollOutEffect,rollOverEffect,showEffect,|\n
-ASRearr_UseModifierOrder_Property=true
-ASRearr_MoveImportsOutsideClass=true
-Actionscript.maxLineLength=200
-Actionscript.spacesInsideParens=0
-ASRearr_ElementSortProperties=true
-Actionscript.advancedSpacesInsideArrayRefBrackets=0
-Actionscript.newLineAfterBindable=true
-Actionscript.blankLinesToKeep=0
-Actionscript.keepElseIfOnSameLine=true
-Actionscript.blankLinesBeforeProperties=0
-Actionscript.spacesBeforeColons=0
-Actionscript.alwaysGenerateIndent=true
-ASRearr_UseElementPropertyVisibilityOrder=true
-Actionscript.spacesAfterLabel=1
-Actionscript.wrapMethodDeclMode=1
-Actionscript.useGlobalSpacesAroundColons=true
-ASRearr_ModifierOrder_Function=,override,public,private,protected,internal,static,dynamic,final
-ASRearr_ElementStaticPropertyVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true,
-MXML.sortExtraAttrs=false
-Actionscript.tabCountForHangingIndent=1
-ASRearr_ElementSortFunctions=true
-ASRearr_ElementPropertyVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true,
-Actionscript.wrapEmbeddedXMLMode=2
-ASRearr_UseElementOrder=true
-ASRearr_UseGlobalModifierOrder=true
-Actionscript.useGlobalSpacesInsideParens=true
-Actionscript.putCatchOnNewLine=true
-ASRearr_ElementSortNamespaces=true
-Actionscript.putElseOnNewLine=true
-MXML.spacesBeforeEmptyTagEnd=0
-ASRearr_ElementSortStaticFunctions=true
-Actionscript.wrapIndentStyle=1000
-ASRearr_MetatagOrder=ArrayElementType,Bindable,DefaultProperty,Deprecated,Effect,Embed,Event,Exclude,ExcludeClass,IconFile,Inspectable,InstanceType,NonCommittingChangeEvent,RemoteClass,Style,SWF,Transient,
-Actionscript.braceStyle=5
-ASRearr_UseElementFunctionVisibilityOrder=true
-Actionscript.spacesAroundBinarySymbolicOperator=1
-Actionscript.keepSLCommentsOnColumn1=true
-MXML.tagsCanFormat=mx\:List,fx\:List,
-MXML.sortAttrData=
-Actionscript.blankLinesBeforeClasses=1
-MXML.tagsToHaveBlankLinesAddedBeforeThem=
-MXML.useAttrsToKeepOnSameLine=false
-ASRearr_UseImportOrder=true
-MXML.blankLinesBetweenSiblingTags=0
-Actionscript.collapseSpacesForAdjacentParens=true
-Actionscript.advancedSpacesInsideArrayDeclBrackets=1
-Actionscript.advancedSpacesInsideParens=0
-MXML.tagsCannotFormat=mx\:String,fx\:String,
-MXML.attrsPerLine=1
-ASRearr_ImportOrder=adobe,com,flash,mx,
-MXML.addNewlineAfterLastAttr=false
-MXML.tagsWithASContent=.*\:add,.*\:added,.*\:activate,.*\:addedToStage,.*\:buttonDown,.*\:change,.*\:childAdd,.*\:childIndexChange,.*\:childRemove,.*\:clickHandler,.*\:clear,.*\:click,.*\:complete,.*\:contextMenu,.*\:copy,.*\:creationComplete,.*\:currentStateChange,.*\:currentStateChanging,.*\:cut,.*\:dataChange,.*\:deactivate,.*\:doubleClick,.*\:dragComplete,.*\:dragDrop,.*\:dragEnter,.*\:dragExit,.*\:dragOver,.*\:dragStart,.*\:effectEnd,.*\:effectStart,.*\:enterFrame,.*\:enterState,.*\:exitFrame,.*\:exitState,.*\:focusIn,.*\:focusOut,.*\:frameConstructed,.*\:hide,.*\:httpStatus,.*\:init,.*\:initialize,.*\:invalid,.*\:ioError,.*\:itemClick,.*\:itemRollOut,.*\:itemRollOver,.*\:keyDown,.*\:keyFocusChange,.*\:keyUp,.*\:menuHide,.*\:menuShow,.*\:middleClick,.*\:middleMouseDown,.*\:middleMouseUp,.*\:mouseDown,.*\:mouseUp,.*\:mouseOver,.*\:mouseMove,.*\:mouseOut,.*\:mouseFocusChange,.*\:mouseWheel,.*\:mouseDownOutside,.*\:mouseWheelOutside,.*\:move,.*\:nativeDragComplete,.*\:nativeDragDrop,.*\:nativeDragEnter,.*\:nativeDragExit,.*\:nativeDragOver,.*\:nativeDragStart,.*\:nativeDragUpdate,.*\:open,.*\:paste,.*\:preinitialize,.*\:progress,.*\:record,.*\:remove,.*\:removed,.*\:removedFromStage,.*\:render,.*\:resize,.*\:rightClick,.*\:rightMouseDown,.*\:rightMouseUp,.*\:rollOut,.*\:rollOver,.*\:scroll,.*\:securityError,.*\:selectAll,.*\:show,.*\:tabChildrenChange,.*\:tabEnabledChange,.*\:tabIndexChange,.*\:thumbDrag,.*\:thumbPress,.*\:thumbRelease,.*\:toolTipCreate,.*\:toolTipEnd,.*\:toolTipHide,.*\:toolTipShow,.*\:toolTipShown,.*\:toolTipStart,.*\:updateComplete,.*\:unload,.*\:valid,.*\:valueCommit,.*\:Script,
diff --git a/src/org/tinytlf/ITextEngine.as b/src/org/tinytlf/ITextEngine.as
deleted file mode 100644
index 9012695..0000000
--- a/src/org/tinytlf/ITextEngine.as
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf
-{
- import flash.display.Stage;
-
- import org.tinytlf.decor.ITextDecor;
- import org.tinytlf.interaction.ITextInteractor;
- import org.tinytlf.layout.ITextLayout;
- import org.tinytlf.layout.factory.ILayoutModelFactory;
- import org.tinytlf.styles.ITextStyler;
-
- public interface ITextEngine
- {
- function get blockFactory():ILayoutModelFactory;
- function set blockFactory(value:ILayoutModelFactory):void;
-
- function get decor():ITextDecor;
- function set decor(textDecor:ITextDecor):void;
-
- function get interactor():ITextInteractor;
- function set interactor(textInteractor:ITextInteractor):void;
-
- function get layout():ITextLayout;
- function set layout(textlayout:ITextLayout):void;
-
- function set stage(theStage:Stage):void;
-
- function get styler():ITextStyler;
- function set styler(textStyler:ITextStyler):void;
-
- function prerender(...args):void;
-
- function invalidate():void;
- function invalidateLines():void;
- function invalidateDecorations():void;
-
- function render():void;
- function renderLines():void;
- function renderDecorations():void;
- }
-}
-
diff --git a/src/org/tinytlf/TextEngine.as b/src/org/tinytlf/TextEngine.as
deleted file mode 100644
index 286dee2..0000000
--- a/src/org/tinytlf/TextEngine.as
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf
-{
- import flash.display.Stage;
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.text.engine.TextBlock;
- import flash.utils.setTimeout;
-
- import org.tinytlf.decor.*;
- import org.tinytlf.extensions.xml.layout.factory.XMLBlockFactory;
- import org.tinytlf.interaction.*;
- import org.tinytlf.layout.*;
- import org.tinytlf.layout.factory.*;
- import org.tinytlf.styles.*;
-
- public class TextEngine extends EventDispatcher implements ITextEngine
- {
- public function TextEngine(stage:Stage = null)
- {
- this.stage = stage;
- }
-
- protected var _blockFactory:ILayoutModelFactory;
-
- public function get blockFactory():ILayoutModelFactory
- {
- if(!_blockFactory)
- _blockFactory = new XMLBlockFactory();
-
- _blockFactory.engine = this;
-
- return _blockFactory;
- }
-
- public function set blockFactory(value:ILayoutModelFactory):void
- {
- if(value === _blockFactory)
- return;
-
- _blockFactory = value;
-
- blockFactory.engine = this;
- }
-
- protected var _decor:ITextDecor;
-
- public function get decor():ITextDecor
- {
- if(!_decor)
- _decor = new TextDecor();
-
- _decor.engine = this;
-
- return _decor;
- }
-
- public function set decor(textDecor:ITextDecor):void
- {
- if(textDecor == _decor)
- return;
-
- _decor = textDecor;
-
- _decor.engine = this;
- }
-
- protected var _interactor:ITextInteractor;
-
- public function get interactor():ITextInteractor
- {
- if(!_interactor)
- _interactor = new TextInteractorBase();
-
- _interactor.engine = this;
-
- return _interactor;
- }
-
- public function set interactor(textInteractor:ITextInteractor):void
- {
- if(textInteractor == _interactor)
- return;
-
- _interactor = textInteractor;
-
- _interactor.engine = this;
- }
-
- protected var _layout:ITextLayout;
-
- public function get layout():ITextLayout
- {
- if(!_layout)
- _layout = new TextLayoutBase();
-
- _layout.engine = this;
-
- return _layout;
- }
-
- public function set layout(textLayout:ITextLayout):void
- {
- if(textLayout == _layout)
- return;
-
- _layout = textLayout;
-
- _layout.engine = this;
- }
-
- protected var _stage:Stage;
-
- public function set stage(theStage:Stage):void
- {
- if(theStage === _stage)
- return;
-
- _stage = theStage;
- invalidateStage();
- }
-
- protected var _styler:ITextStyler;
-
- public function get styler():ITextStyler
- {
- if(!_styler)
- _styler = new TextStyler();
-
- _styler.engine = this;
-
- return _styler;
- }
-
- public function set styler(textStyler:ITextStyler):void
- {
- if(textStyler == _styler)
- return;
-
- _styler = textStyler;
- }
-
- protected var blocks:Vector.;
-
- public function prerender(... args):void
- {
- decor.removeAll();
- blocks = blockFactory.createBlocks(args);
- }
-
- public function invalidate():void
- {
- invalidateLines();
- invalidateDecorations();
- }
-
- protected var invalidateLinesFlag:Boolean = false;
-
- public function invalidateLines():void
- {
- if(invalidateLinesFlag)
- return;
-
- invalidateLinesFlag = true;
- invalidateStage();
- }
-
- protected var invalidateDecorationsFlag:Boolean = false;
-
- public function invalidateDecorations():void
- {
- if(invalidateDecorationsFlag)
- return;
-
- invalidateDecorationsFlag = true;
- invalidateStage();
- }
-
- protected function invalidateStage():void
- {
- if(!_stage)
- return;
-
- _stage.addEventListener(Event.RENDER, onRender);
-
- if(rendering)
- setTimeout(_stage.invalidate, 0);
- else
- _stage.invalidate();
- }
-
- protected function onRender(event:Event):void
- {
- if(!_stage)
- return;
-
- _stage.removeEventListener(Event.RENDER, onRender);
-
- render();
- }
-
- protected var rendering:Boolean = false;
-
- public function render():void
- {
- rendering = true;
-
- if(invalidateLinesFlag)
- renderLines();
- invalidateLinesFlag = false;
-
- if(invalidateDecorationsFlag)
- renderDecorations();
- invalidateDecorationsFlag = false;
-
- rendering = false;
- }
-
- public function renderLines():void
- {
- layout.clear();
- layout.render(blockFactory.blocks);
- }
-
- public function renderDecorations():void
- {
- layout.resetShapes();
- decor.render();
- }
- }
-}
-
diff --git a/src/org/tinytlf/TextField.as b/src/org/tinytlf/TextField.as
deleted file mode 100644
index f0b0306..0000000
--- a/src/org/tinytlf/TextField.as
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf
-{
- import flash.display.DisplayObject;
- import flash.display.Sprite;
- import flash.events.Event;
- import flash.events.EventPhase;
- import flash.text.engine.TextLine;
-
- import org.tinytlf.core.IStyleAware;
- import org.tinytlf.decor.decorations.BackgroundColorDecoration;
- import org.tinytlf.decor.decorations.StrikeThroughDecoration;
- import org.tinytlf.decor.decorations.UnderlineDecoration;
- import org.tinytlf.extensions.xhtml.interaction.AnchorInteractor;
- import org.tinytlf.extensions.xhtml.layout.adapter.AnchorAdapter;
- import org.tinytlf.layout.ITextContainer;
- import org.tinytlf.layout.TextContainerBase;
-
- public class TextField extends Sprite implements IStyleAware
- {
- public function TextField()
- {
- super();
-
- width = 100;
-
- hookEngine();
- }
-
- private var _height:Number = 0;
- override public function get height():Number
- {
- return _height;
- }
-
- override public function set height(value:Number):void
- {
- if(!changed(height, value))
- return;
-
- _height = value;
- container.allowedHeight = value;
- engine.invalidate();
- }
-
- private var _width:Number = 0;
- override public function get width():Number
- {
- return _width;
- }
-
- override public function set width(value:Number):void
- {
- if(!changed(width, value))
- return;
-
- _width = value;
- container.allowedWidth = value;
- engine.invalidate();
- }
-
- override public function addChild(child:DisplayObject):DisplayObject
- {
- return super.addChild(child is TextLine ? hookLine(child) : child);
- }
-
- private var _container:ITextContainer;
-
- public function get container():ITextContainer
- {
- if(!_container)
- _container = new TextContainerBase(this);
-
- return _container;
- }
-
- public function set container(textContainer:ITextContainer):void
- {
- if(textContainer == _container)
- return;
-
- _container = textContainer;
- _container.target = this;
- }
-
- private var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- if(!_engine)
- {
- _engine = new TextEngine(stage);
- _engine.layout.addContainer(container);
-
- if(!stage)
- addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
- }
-
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- private var _text:String = "";
- public function set text(value:String):void
- {
- if(!changed(_text, value))
- return;
-
- _text = value;
- engine.blockFactory.data = _text;
- engine.prerender();
- engine.invalidate();
- }
-
- public function get style():Object
- {
- return engine.styler.style;
- }
-
- public function set style(value:Object):void
- {
- engine.styler.style = value;
- }
-
- public function clearStyle(styleProp:String):Boolean
- {
- return engine.styler.clearStyle(styleProp);
- }
-
- public function getStyle(styleProp:String):*
- {
- return engine.styler.getStyle(styleProp);
- }
-
- public function setStyle(styleProp:String, newValue:*):void
- {
- engine.styler.setStyle(styleProp, newValue);
- }
-
- /**
- * @private
- * Called just before a line is added to the display list.
- */
- protected function hookLine(line:DisplayObject):DisplayObject
- {
- return line;
- }
-
- /**
- * @private
- * Called from the constructor, maps in all the properties that the engine
- * uses.
- */
- protected function hookEngine():void
- {
- //Default mapped text decorations.
- engine.decor.mapDecoration("backgroundColor", BackgroundColorDecoration);
- engine.decor.mapDecoration("underline", UnderlineDecoration);
- engine.decor.mapDecoration("strikethrough", StrikeThroughDecoration);
-
- engine.blockFactory.mapElementAdapter("a", AnchorAdapter);
-
- engine.interactor.mapMirror("a", new AnchorInteractor());
- engine.styler.mapStyle("a", {underline:true});
- }
-
- private function onAddedToStage(event:Event):void
- {
- if(event.eventPhase != EventPhase.AT_TARGET)
- return;
-
- removeEventListener(event.type, onAddedToStage);
- engine.stage = stage;
- }
-
- private function changed(oldVal:*, newVal:*):Boolean
- {
- return Boolean(newVal !== oldVal);
- }
- }
-}
-
diff --git a/src/org/tinytlf/core/IStyleAware.as b/src/org/tinytlf/core/IStyleAware.as
deleted file mode 100644
index 3786fdc..0000000
--- a/src/org/tinytlf/core/IStyleAware.as
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.core
-{
- public interface IStyleAware
- {
- function get style():Object;
- function set style(value:Object):void;
-
- function clearStyle(styleProp:String):Boolean;
- function getStyle(styleProp:String):*;
- function setStyle(styleProp:String, newValue:*):void;
- }
-}
-
diff --git a/src/org/tinytlf/core/StyleAwareActor.as b/src/org/tinytlf/core/StyleAwareActor.as
deleted file mode 100644
index 57ba775..0000000
--- a/src/org/tinytlf/core/StyleAwareActor.as
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.core
-{
- import flash.utils.Proxy;
- import flash.utils.describeType;
- import flash.utils.flash_proxy;
-
- use namespace flash_proxy;
-
- /**
- * StyleAwareActor is a useful base class for objects with sealed properties
- * but who also wish to dynamically accept and store named values.
- *
- * Since he is a Proxy implementation, he overrides the flash_proxy functions
- * for setting and retrieving data. If you are calling a sealed property on
- * StyleAwareActor or one of his subclasses, the property or function is called
- * like normal. However, if you dynamically call or set a property on him, he
- * calls his getStyle and setStyle methods instead.
- *
- * StyleAwareActor has an internal styles object on which these
- * properties and values are stored. However, you can override this
- * functionality by passing in your own implementation to store styles on. You
- * can do this by calling setStyle("styleProxy", myProxyImplem).
- * This will set the myProxyImpl instance as the new internal
- * styles storage object, as well as copy over all the key/value pairs currently
- * on the myProxyImpl instance.
- *
- * This is particularly useful if you wish to proxy together multiple
- * StyleAwareActors for something similar to CSS inheritance, or to support
- * external CSS implementations (currently Flex and F*CSS).
- */
- public class StyleAwareActor extends Proxy implements IStyleAware
- {
- public function StyleAwareActor(styleObject:Object = null)
- {
- if(!styleObject)
- return;
-
- style = styleObject;
- }
-
- public function toString():String
- {
- return style.toString();
- }
-
- private var _style:Object;
-
- public function get style():Object
- {
- return _style;
- }
-
- public function set style(value:Object):void
- {
- if(value === _style)
- return;
-
- if(!(value is String))
- {
- var proxy:Object;
- var styleProp:String;
-
- // Use value as the new styles object. This allows you to pass in
- // and use your own subclass of StyleAwareActor
- // (useful for F*CSS or Flex styles)
- if(value is IStyleAware)
- {
- proxy = styles;
- styles = value;
-
- // Copy values from the proxy styles Object to this.
- // Since here we're copying the old styles onto the replacement styles
- // Object, we have to be sure not to replace any styles that already
- // exist on the new guy.
- for(styleProp in proxy)
- if(!(styleProp in this))
- this[styleProp] = proxy[styleProp];
- }
- else
- {
- proxy = value;
- // Copy values from the proxy styles Object to this.
- for(styleProp in proxy)
- this[styleProp] = proxy[styleProp];
- }
- }
-
- _style = value;
- }
-
- protected var styles:Object = {};
-
- public function clearStyle(styleProp:String):Boolean
- {
- return styleProp in styles ? delete styles[styleProp] : false;
- }
-
- public function getStyle(styleProp:String):*
- {
- return styleProp in styles ? styles[styleProp] : null;
- }
-
- public function setStyle(styleProp:String, newValue:*):void
- {
- styles[styleProp] = newValue;
- }
-
- override flash_proxy function callProperty(name:*, ... parameters):*
- {
- if(name in this && this[name] is Function)
- return (this[name] as Function).apply(null, parameters);
-
- if(name == 'toString')
- return toString();
- }
-
- override flash_proxy function setProperty(name:*, value:*):void
- {
- if(name in propertiesMap)
- this[name] = value;
- else
- setStyle(name, value);
- }
-
- override flash_proxy function getProperty(name:*):*
- {
- if(name in this)
- return this[name];
-
- return getStyle(name);
- }
-
- override flash_proxy function hasProperty(name:*):Boolean
- {
- return propertiesMap ? name in propertiesMap : false;
- }
-
- protected var names:Array = [];
-
- override flash_proxy function nextNameIndex(index:int):int
- {
- if(index == 0)
- {
- names.length = 0;
- for(var prop:String in styles)
- names.push(prop);
- }
-
- if(index < names.length)
- return index + 1;
-
- return 0;
- }
-
- override flash_proxy function nextName(index:int):String
- {
- return names[index - 1];
- }
-
- override flash_proxy function nextValue(index:int):*
- {
- return this[names[index]];
- }
-
- generatePropertiesMap(new StyleAwareActor());
-
- private static var propertiesMap:Object;
-
- protected static function generatePropertiesMap(typeOf:*):void
- {
- propertiesMap = {};
-
- var type:XML = describeType(typeOf);
- var prop:XML;
- for each(prop in type..method)
- {
- propertiesMap[prop.@name] = true;
- }
-
- for each(prop in type..accessor.(@access == "readwrite"))
- {
- propertiesMap[prop.@name] = true;
- }
- }
- }
-}
-
diff --git a/src/org/tinytlf/decor/ITextDecor.as b/src/org/tinytlf/decor/ITextDecor.as
deleted file mode 100644
index 7cb57ed..0000000
--- a/src/org/tinytlf/decor/ITextDecor.as
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor
-{
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.ITextContainer;
-
- public interface ITextDecor
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function render():void;
-
- function removeAll():void;
-
- function decorate(element:*, styleObj:Object, layer:int = 0, container:ITextContainer = null):void;
- function undecorate(element:* = null, decorationProp:String = null):void;
-
- function mapDecoration(decorationProp:String, decorationClassOrInstance:Object):void;
- function unMapDecoration(decorationProp:String):Boolean;
- function hasDecoration(decorationProp:String):Boolean;
- function getDecoration(decorationProp:String, container:ITextContainer = null):ITextDecoration;
- }
-}
-
diff --git a/src/org/tinytlf/decor/ITextDecoration.as b/src/org/tinytlf/decor/ITextDecoration.as
deleted file mode 100644
index c8eb411..0000000
--- a/src/org/tinytlf/decor/ITextDecoration.as
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor
-{
- import flash.display.Sprite;
- import flash.events.IEventDispatcher;
- import flash.geom.Rectangle;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.core.IStyleAware;
- import org.tinytlf.layout.ITextContainer;
-
- public interface ITextDecoration extends IStyleAware, IEventDispatcher
- {
- function get containers():Vector.
- function set containers(textContainers:Vector.):void;
-
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function setup(...args):Vector.
-
- function draw(bounds:Vector., layer:int = 0):void;
- }
-}
-
diff --git a/src/org/tinytlf/decor/TextDecor.as b/src/org/tinytlf/decor/TextDecor.as
deleted file mode 100644
index 9af526f..0000000
--- a/src/org/tinytlf/decor/TextDecor.as
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor
-{
- import flash.utils.Dictionary;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.ITextContainer;
-
- public class TextDecor implements ITextDecor
- {
- public function TextDecor()
- {
- }
-
- protected var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- public function render():void
- {
- var i:int = 0;
- var n:int = layers.length;
- var layer:Dictionary;
- var element:*;
- var decorationProp:String;
- var decoration:ITextDecoration;
-
- for(; i < n; ++i)
- {
- layer = layers[i];
- for(element in layer)
- for each(decoration in layer[element])
- if(decoration)
- decoration.draw(decoration.setup(element), i);
- }
- }
-
- public function removeAll():void
- {
- for(var layer:* in layers)
- {
- for(var element:* in layers[layer])
- {
- for(var decoration:String in layers[layer][element])
- {
- delete layers[layer][element][decoration]
- }
- delete layers[layer][element];
- }
- delete layers[layer];
- }
-
- engine.invalidateDecorations();
- }
-
- protected var layers:Array = [];
-
- /**
- * Decorate dresses up an element for presentation. Based on the parameters
- * passed in, he can do many things.
- *
- * This also allows decorations to be drawn to different layers, so you can
- * have decorations that are assured to be at different Z-indicies. 0 is the
- * top most layer, with each increment from 0 -> Infinity on a 'lower' level
- * in the displayList. An object on layer 0 will draw on top of an object at
- * layer 1, etc.
- *
- * One of the simplest use cases is setting a decoration property on an
- * element with a value. So, in order to set a background color of red
- * (assuming there's a decoration mapped for 'backgroundColor'), you would call:
- * #decorate(myElement, 'backgroundColor', 0xFF0000);
- *
- * If, instead of passing a value for the decoration, you want the value to
- * be looked up through styles, you can pass a styleName or style Object with
- * key/value pairs. So, say you had this style:
- *
- * myElementOverStyle {
- * backgroundColor: #FF0000;
- * backgroundAlpha: 0.25;
- * }
- *
- * You could call this:
- * #decorate(myElement, 'backgroundColor', null, 0, 'myElementOverStyle');
- *
- * If you don't have a style defined, no worries. Just pass an object with
- * the values:
- * #decorate(myElement, 'backgroundColor', null, 0, {backgroundColor:#FF0000, backgroundAlpha:0.25});
- *
- * This sets the properties in the object as styles on the decoration.
- *
- * Alternatively, you can pass in no decoration property and no value, just a
- * styleName or a key/value Object, and create ITextDecoration
- * instances from the style declaration or values in the Object.
- *
- * I will use this style declaration for reference, but bear in mind that
- * this could just as easily be a key/value paired Object:
- * myElementStyle {
- * backgroundColor: #FF0000;
- * backgroundAlpha: 0.25;
- * underline: true;
- * underlineColor: #00FF00;
- * underlineThickness: 4;
- * }
- *
- * Typically backgroundColor and underline exist, so I will use them as an
- * example:
- *
- * We check the list of mapped decoration names against the properties that
- * exist in the style declaration.
- * We see that a 'backgroundColor' property exists, which is mapped to a
- * BackgroundColorDecoration class. So it creates a new instance of the
- * BackgroundColorDecoration class and saddles it with the styleName.
- *
- * Likewise, it would also match the typical 'underline' style up with the
- * UnderlineDecoration class. So he creates an UnderlineDecoration as well,
- * passing the styleName along as well.
- *
- * It's important to note that this, while convenient when creating multiple
- * decorations, doesn't allow you the ability of layering your content any
- * more than 'first-come, first-served.'
- *
- */
- public function decorate(element:*, styleObj:Object, layer:int = 0, container:ITextContainer = null):void
- {
- if(!element || !styleObj)
- return;
-
- //Resolve the layer business first
- var theLayer:Object = resolveLayer(layer);
- if(!(element in theLayer) || theLayer[element] == null)
- theLayer[element] = new Dictionary(true);
-
- //Don't handle parsing a styleName for now, do that in a subclass.
- if(styleObj is String)
- return;
-
- var decoration:ITextDecoration;
-
- var styleProp:String;
- for(styleProp in styleObj)
- {
- if(hasDecoration(styleProp))
- {
- decoration = ITextDecoration(theLayer[element][styleProp] = getDecoration(styleProp, container));
- for(styleProp in styleObj)
- decoration.setStyle(styleProp, styleObj[styleProp]);
- }
- }
-
- engine.invalidateDecorations();
- }
-
- /**
- * Undecorate can completely dress down the element passed in, or it can strip
- * out the decoration for a particular property, leaving the others intact.
- */
- public function undecorate(element:* = null, decorationProp:String = null):void
- {
- var i:int = layers.length - 1;
- var layer:Dictionary;
-
- for(; i >= 0; i--)
- {
- layer = Dictionary(layers[i]);
-
- if(!layer)
- continue;
-
- if(element)
- {
- if(!(element in layer) || layer[element] == null)
- continue;
-
- if(!decorationProp)
- for(var dec:String in layer[element])
- delete layer[element][dec];
- else if(decorationProp in layer[element])
- delete layer[element][decorationProp];
-
- if(isEmpty(layer[element]))
- delete layer[element];
- }
- else if(decorationProp)
- {
- for(var e:* in layer)
- {
- if(layer[e] == null)
- continue;
-
- if(decorationProp in layer[e])
- delete layer[e][decorationProp];
-
- if(isEmpty(layer[e]))
- delete layer[e];
- }
- }
- }
-
- engine.invalidateDecorations();
- }
-
- private var decorationsMap:Object = {};
-
- public function mapDecoration(styleProp:String, decorationClassOrInstance:Object):void
- {
- decorationsMap[styleProp] = decorationClassOrInstance;
- }
-
- public function unMapDecoration(styleProp:String):Boolean
- {
- if(!hasDecoration(styleProp))
- return false;
-
- return delete decorationsMap[styleProp];
- }
-
- public function hasDecoration(decorationProp:String):Boolean
- {
- return Boolean(decorationProp in decorationsMap);
- }
-
- public function getDecoration(styleProp:String, container:ITextContainer = null):ITextDecoration
- {
- if(!hasDecoration(styleProp))
- return null;
-
- var decoration:* = decorationsMap[styleProp];
- if(decoration is Class)
- decoration = ITextDecoration(new decoration());
- if(decoration is Function)
- decoration = ITextDecoration((decoration as Function)());
-
- if(!decoration)
- return null;
-
- var vec:Vector. = new Vector.();
- if(container)
- vec.push(container);
-
- ITextDecoration(decoration).containers = vec;
-
- ITextDecoration(decoration).engine = engine;
-
- return ITextDecoration(decoration);
- }
-
- protected function resolveLayer(layer:int):Dictionary
- {
- if(layer < 0)
- layer = 0;
- // Allow them to use the larger layer, but keep the Array densly populated.
- // This helps with performance, but also solves the bug where a coder really
- // does want to put something at higher level before other levels are created.
- // Originally I kept the layers within the bounds of the array, but that
- // introduced race-condition-y scanerios.
- else if(layer > layers.length)
- {
- var i:int = layers.length - 1;
- while(++i < layer)
- layers[i] = (i in layers) ? layers[i] : null;
- }
-
- if(!(layers[layer]))
- layers[layer] = new Dictionary(true);
-
- return Dictionary(layers[layer]);
- }
-
- private function isEmpty(dict:Object):Boolean
- {
- if(!dict)
- return true;
-
- for(var prop:* in dict)
- if(dict[prop])
- return false;
-
- return true;
- }
- }
-}
-
diff --git a/src/org/tinytlf/decor/TextDecoration.as b/src/org/tinytlf/decor/TextDecoration.as
deleted file mode 100644
index a837d27..0000000
--- a/src/org/tinytlf/decor/TextDecoration.as
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor
-{
- import flash.display.DisplayObject;
- import flash.display.DisplayObjectContainer;
- import flash.display.Sprite;
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.geom.Rectangle;
- import flash.text.engine.ContentElement;
- import flash.text.engine.GroupElement;
- import flash.text.engine.TextBlock;
- import flash.text.engine.TextLine;
- import flash.text.engine.TextLineMirrorRegion;
- import flash.text.engine.TextLineValidity;
- import flash.utils.Dictionary;
- import flash.utils.flash_proxy;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.core.StyleAwareActor;
- import org.tinytlf.interaction.LineEventInfo;
- import org.tinytlf.layout.ITextContainer;
-
- use namespace flash_proxy;
-
- public class TextDecoration extends StyleAwareActor implements ITextDecoration
- {
- public function TextDecoration(styleName:String = "")
- {
- super(styleName);
- }
-
- private var _containers:Vector.;
- public function get containers():Vector.
- {
- return _containers;
- }
-
- public function set containers(textContainers:Vector.):void
- {
- if(textContainers === _containers)
- return;
-
- _containers = textContainers;
- }
-
- protected var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- private var derivedParent:DisplayObjectContainer;
-
- public function setup(... args):Vector.
- {
- var bounds:Vector. = new Vector.();
-
- if(args.length <= 0)
- return bounds;
-
- var arg:* = args[0];
-
- var uiLineClass:Class = LineEventInfo.uiLineClass;
-
- if(arg is ContentElement)
- {
- var element:ContentElement = arg as ContentElement;
- var regions:Vector. = getMirrorRegions(element);
- if(regions.length <= 0)
- return bounds;
-
- var region:TextLineMirrorRegion;
- var rect:Rectangle;
- var line:DisplayObjectContainer;
-
-
- while(regions.length > 0)
- {
- region = regions.pop();
- rect = region.bounds;
-
- line = (uiLineClass && region.textLine.parent is uiLineClass) ? region.textLine.parent : region.textLine;
-
- bounds.push(new Rectangle(rect.x + line.x, rect.y + line.y, rect.width, rect.height));
- }
-
- // @TODO
- // When you specify decorations for a ContentElement, there's no way to
- // associate it with ITextContainers yet. This is a hack that grabs the
- // containers that hold the TextLines that this element is rendered with.
- // It might be better if this exists somewhere else, like if ITextLayout
- // could update Decor for any decorations that are keyed off ContentElements.
- if(containers.length == 0)
- {
- var lines:Vector. = getTextLines(element);
- var container:ITextContainer;
- while(lines.length)
- {
- container = engine.layout.getContainerForLine(lines.shift());
- if(containers.indexOf(container) == -1)
- containers.push(container);
- }
- }
- }
- else if(arg is TextLine)
- {
- var tl:TextLine = arg as TextLine;
- bounds.push(tl.getBounds(tl.parent));
- }
- else if(arg is uiLineClass)
- {
- var ui:DisplayObject = arg as DisplayObject;
- bounds.push(ui.getBounds(ui.parent));
- }
- else if(arg is Rectangle)
- bounds.push(arg);
- else if(arg is Vector.)
- bounds = bounds.concat(arg);
-
- return bounds;
- }
-
- protected var spriteMap:Dictionary = new Dictionary(true);
-
- public function draw(bounds:Vector., layer:int = 0):void
- {
- if(!containers)
- return;
-
- spriteMap = new Dictionary(true);
-
- // 1. Iterate over containers
- // 2. Iterate over bounds
- // 3. Check intersection
- // 4. possibly create sprite -- add it to container.shapes
- // 5. associate bounds with proper sprite
-
- var i:int = 0;
- var n:int = containers.length;
- var j:int = 0;
- var k:int = bounds.length;
-
- var container:ITextContainer;
- var doc:DisplayObjectContainer;
-
- for(i = 0; i < n; i++)
- {
- container = containers[i];
- doc = container.target;
-
- for(j = 0; j < k; j++)
- {
- if(bounds[j].intersects(doc.getBounds(doc)))
- {
- if(! (container in spriteMap))
- spriteMap[container] = resolveLayer(container.shapes, layer);
-
- spriteMap[bounds[j]] = spriteMap[container];
- }
- }
- }
- }
-
- private function resolveLayer(shapes:Sprite, layer:int):Sprite
- {
- if(shapes.numChildren > layer)
- return Sprite(shapes.getChildAt(layer));
-
- var sprite:Sprite;
- var i:int = shapes.numChildren - 1;
- while(++i <= layer)
- sprite = Sprite(shapes.addChildAt(new Sprite(), i));
-
- return sprite;
- }
-
- protected function getTextBlock(element:ContentElement):TextBlock
- {
- if(!element)
- return null;
-
- return element.textBlock;
- }
-
- protected function getTextLines(element:ContentElement):Vector.
- {
- var lines:Vector. = new Vector.();
- var block:TextBlock = getTextBlock(element);
- if(!block)
- return lines;
-
- var firstLine:TextLine = block.getTextLineAtCharIndex(element.textBlockBeginIndex);
- lines.push(firstLine);
-
- var lastLine:TextLine = block.getTextLineAtCharIndex(element.textBlockBeginIndex + element.rawText.length - 1);
-
- var line:TextLine = firstLine;
- while(line != lastLine)
- {
- line = line.nextLine;
- if(line == null)
- break;
- lines.push(line);
- }
-
- return lines;
- }
-
- protected function getMirrorRegions(element:ContentElement):Vector.
- {
- var regions:Vector. = new Vector.();
- var lines:Vector. = getTextLines(element);
- var region:TextLineMirrorRegion;
-
- var lineRegions:Vector.;
-
- if(element is GroupElement)
- {
- var elem:ContentElement;
- var n:int = GroupElement(element).elementCount;
- for(var i:int = 0; i < n; i++)
- {
- elem = GroupElement(element).getElementAt(i);
- regions = regions.concat(getMirrorRegions(elem));
- }
- }
-
- var line:TextLine;
-
- while(lines.length > 0)
- {
- line = lines.pop();
- if(line.validity != TextLineValidity.VALID)
- continue;
-
- lineRegions = line.mirrorRegions.concat();
- while(lineRegions.length > 0)
- {
- region = lineRegions.pop();
- if(region.element == element)
- regions.push(region);
- }
- }
-
- return regions;
- }
-
- protected function getBounds(element:ContentElement):Vector.
- {
- var bounds:Vector. = new Vector.();
- var regions:Vector.;
-
- if(element is GroupElement)
- {
- var elem:ContentElement;
- var n:int = GroupElement(element).elementCount;
- for(var i:int = 0; i < n; i++)
- {
- elem = GroupElement(element).getElementAt(i);
- regions = getMirrorRegions(elem);
- while(regions.length > 0)
- bounds.push(regions.pop().bounds);
- }
- }
- else
- {
- regions = getMirrorRegions(element);
- while(regions.length > 0)
- bounds.push(regions.pop().bounds);
- }
-
- return bounds;
- }
-
- private var dispatcher:EventDispatcher = new EventDispatcher();
-
- public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = true):void
- {
- dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
- }
-
- public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
- {
- dispatcher.removeEventListener(type, listener, useCapture);
- }
-
- public function dispatchEvent(event:Event):Boolean
- {
- return dispatcher.dispatchEvent(event);
- }
-
- public function hasEventListener(type:String):Boolean
- {
- return dispatcher.hasEventListener(type);
- }
-
- public function willTrigger(type:String):Boolean
- {
- return dispatcher.willTrigger(type);
- }
-
- //Statically generate a map of the properties in this object
- generatePropertiesMap(new TextDecoration());
- }
-}
-
diff --git a/src/org/tinytlf/decor/decorations/BackgroundColorDecoration.as b/src/org/tinytlf/decor/decorations/BackgroundColorDecoration.as
deleted file mode 100644
index e7181c2..0000000
--- a/src/org/tinytlf/decor/decorations/BackgroundColorDecoration.as
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor.decorations
-{
- import flash.display.Sprite;
- import flash.geom.Rectangle;
-
- import org.tinytlf.decor.TextDecoration;
-
- public class BackgroundColorDecoration extends TextDecoration
- {
- public function BackgroundColorDecoration(styleName:String = "")
- {
- super(styleName);
- }
-
- override public function draw(bounds:Vector., layer:int = 0):void
- {
- super.draw(bounds, layer);
-
- var rect:Rectangle;
- var parent:Sprite;
-
- while(bounds.length > 0)
- {
- rect = bounds.pop();
- parent = spriteMap[rect];
- if(!parent)
- return;
-
- parent.graphics.beginFill(getStyle("backgroundColor") || 0x000000, getStyle("backgroundAlpha") || 1);
- parent.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
- }
- }
- }
-}
-
diff --git a/src/org/tinytlf/decor/decorations/CaretDecoration.as b/src/org/tinytlf/decor/decorations/CaretDecoration.as
deleted file mode 100644
index 2defda4..0000000
--- a/src/org/tinytlf/decor/decorations/CaretDecoration.as
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor.decorations
-{
- import flash.geom.Rectangle;
- import org.tinytlf.decor.TextDecoration;
-
- public class CaretDecoration extends TextDecoration
- {
- public function CaretDecoration(styleName:String = "")
- {
- super(styleName);
- }
-
- override public function draw(bounds:Vector., layer:int = 0):void
- {
- super.draw(bounds, layer);
- }
- }
-}
-
diff --git a/src/org/tinytlf/decor/decorations/SelectionDecoration.as b/src/org/tinytlf/decor/decorations/SelectionDecoration.as
deleted file mode 100644
index 8ed26e0..0000000
--- a/src/org/tinytlf/decor/decorations/SelectionDecoration.as
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor.decorations
-{
- import flash.geom.Point;
- import flash.geom.Rectangle;
- import flash.text.engine.TextLine;
-
- public class SelectionDecoration extends BackgroundColorDecoration
- {
- public function SelectionDecoration()
- {
- super(".textSelectionStyle");
- }
-
- override public function setup(...args):Vector.
- {
- var bounds:Vector. = super.setup();
-
- if(args.length != 3)
- return bounds;
-
- var selection:Point = args[0];
- var startLine:TextLine = args[1];
- var endLine:TextLine = args[2];
-
- if(!selection || !startLine || !endLine)
- return bounds;
-
- var start:int;
- var end:int;
- var startBounds:Rectangle;
- var endBounds:Rectangle;
- var finalBounds:Rectangle;
-
- var right:Boolean = selection.x <= selection.y;
-
- // Selection within the same line
- if(startLine == endLine)
- {
- start = selection.x;
- end = selection.y;
-
- if(!right)
- start--;
-
- startBounds = startLine.getAtomBounds(startLine.getAtomIndexAtCharIndex(start));
- endBounds = startLine.getAtomBounds(startLine.getAtomIndexAtCharIndex(end));
-
- finalBounds = startBounds.union(endBounds);
- finalBounds.x += startLine.x;
- finalBounds.y += startLine.y;
-
- bounds.push(finalBounds);
- }
- // Selection between lines
- else
- {
- if(right)
- {
- start = selection.x;
- end = startLine.textBlockBeginIndex + startLine.rawTextLength - 1;
- }
- else
- {
- start = selection.x - 1;
- end = startLine.textBlockBeginIndex;
- }
-
- startBounds = startLine.getAtomBounds(startLine.getAtomIndexAtCharIndex(start));
- endBounds = startLine.getAtomBounds(startLine.getAtomIndexAtCharIndex(end));
-
- finalBounds = startBounds.union(endBounds);
- finalBounds.x += startLine.x;
- finalBounds.y += startLine.y;
-
- bounds.push(finalBounds);
-
- var line:TextLine = startLine;
- while(true)
- {
- line = right ? line.nextLine : line.previousLine;
- if(line == endLine || line == null)
- break;
-
- finalBounds = line.getBounds(line);
- finalBounds.x += line.x;
- finalBounds.y += line.y;
-
- bounds.push(finalBounds);
- }
-
- if(right)
- {
- start = endLine.textBlockBeginIndex;
- end = selection.y;
- }
- else
- {
- start = endLine.textBlockBeginIndex + endLine.rawTextLength - 1;
- end = selection.y;
- }
-
- startBounds = endLine.getAtomBounds(endLine.getAtomIndexAtCharIndex(start));
- endBounds = endLine.getAtomBounds(endLine.getAtomIndexAtCharIndex(end));
-
- finalBounds = startBounds.union(endBounds);
- finalBounds.x += endLine.x;
- finalBounds.y += endLine.y;
-
- bounds.push(finalBounds);
- }
-
- return bounds;
- }
- }
-}
-
diff --git a/src/org/tinytlf/decor/decorations/UnderlineDecoration.as b/src/org/tinytlf/decor/decorations/UnderlineDecoration.as
deleted file mode 100644
index c88d89e..0000000
--- a/src/org/tinytlf/decor/decorations/UnderlineDecoration.as
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.decor.decorations
-{
- import flash.display.Sprite;
- import flash.geom.Point;
- import flash.geom.Rectangle;
-
- import org.tinytlf.decor.TextDecoration;
-
- public class UnderlineDecoration extends TextDecoration
- {
- public function UnderlineDecoration(styleName:String = "")
- {
- super(styleName);
- }
-
- override public function draw(bounds:Vector., layer:int = 0):void
- {
- super.draw(bounds, layer);
-
- var underlineDelta:Number = Math.round((getStyle("fontSize") || 12) / 6);
- var start:Point;
- var end:Point;
- var rect:Rectangle;
- var parent:Sprite;
-
- while(bounds.length > 0)
- {
- rect = bounds.pop();
- parent = spriteMap[rect];
-
- start = new Point(rect.x, rect.y + rect.height - underlineDelta);
- end = new Point(rect.x + rect.width, rect.y + rect.height - underlineDelta);
-
- parent.graphics.lineStyle(
- getStyle("underlineThickness") || 2,
- getStyle("underlineColor") || getStyle("color") || 0x00,
- getStyle("underlineAlpha") || 1,
- getStyle("pixelHinting") || false,
- getStyle("scaleMode") || "normal",
- getStyle("caps") || null,
- getStyle("joints") || null,
- getStyle("miterLimit") || 3);
-
- parent.graphics.moveTo(start.x, start.y);
- parent.graphics.lineTo(end.x, end.y);
- parent.graphics.lineStyle(0, 0, 0);
- }
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/fcss/xhtml/core/FStyleProxy.as b/src/org/tinytlf/extensions/fcss/xhtml/core/FStyleProxy.as
deleted file mode 100644
index 0434d52..0000000
--- a/src/org/tinytlf/extensions/fcss/xhtml/core/FStyleProxy.as
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.fcss.xhtml.core
-{
- import com.flashartofwar.fcss.stylesheets.FStyleSheet;
-
- import org.tinytlf.core.StyleAwareActor;
-
- public class FStyleProxy extends StyleAwareActor
- {
- public function FStyleProxy(styleObject:Object=null)
- {
- super(styleObject);
- }
-
- public var sheet:FStyleSheet;
-
- override public function set style(value:Object):void
- {
- if(value is FStyleSheet)
- {
- sheet = FStyleSheet(value);
- return;
- }
-
- super.style = value;
- }
-
- override public function getStyle(styleProp:String):*
- {
- if(styleProp in styles)
- return styles[styleProp];
- else if(sheet)
- return sheet.getStyle(styleProp);
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStyler.as b/src/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStyler.as
deleted file mode 100644
index 7bd7b30..0000000
--- a/src/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStyler.as
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.fcss.xhtml.styles
-{
- import com.flashartofwar.fcss.styles.IStyle;
- import com.flashartofwar.fcss.stylesheets.FStyleSheet;
- import com.flashartofwar.fcss.utils.TypeHelperUtil;
-
- import flash.text.engine.*;
- import flash.utils.Dictionary;
-
- import org.tinytlf.extensions.fcss.xhtml.core.FStyleProxy;
- import org.tinytlf.styles.TextStyler;
-
- public class FCSSTextStyler extends TextStyler
- {
- override public function set style(value:Object):void
- {
- var sheet:FStyleSheet = new FStyleSheet();
- sheet.parseCSS(value as String);
- super.style = new FStyleProxy(sheet);
- }
-
- private var nodeCache:Dictionary = new Dictionary(true);
-
- override public function getElementFormat(element:*):ElementFormat
- {
- if(!(element is Array) || !(style is FStyleProxy))
- return super.getElementFormat(element);
-
- // Context is an array of XML nodes, the currently processing
- // node and its parents, along with all their attributes.
- var context:Array = (element as Array);
-
- var node:XML;
- var attributes:XMLList;
- var attr:String;
-
- var i:int = 0;
- var n:int = context.length;
- var j:int = 0;
- var k:int = 0;
-
- var className:String;
- var idName:String;
- var uniqueNodeName:String;
- var inlineStyle:String = 'a:a;';
- var inheritanceStructure:Array = ['global'];
-
- var fStyle:IStyle;
- var str:String = '';
-
- for(i = 0; i < n; i++)
- {
- node = context[i];
- attributes = node.attributes();
-
- if(!(node in nodeCache))
- {
- k = attributes.length();
- if(node.localName())
- str += node.localName();
-
- if(k > 0)
- {
- // Math.random() * one billion. reasonably safe for unique identifying...
- uniqueNodeName = ' ' + node.localName() + String(Math.round(Math.random() * 100000000000));
-
- // + "style{"; // + attributes['style'] + "}";
- for(j = 0; j < k; j++)
- {
- attr = attributes[j].name();
-
- if(attr == 'class')
- className = attributes[j];
- else if(attr == 'id')
- idName = attributes[j];
- else if(attr == 'style')
- inlineStyle += attributes[j];
- else
- inlineStyle += (attr + ": " + attributes[j] + ";");
- }
-
- if(className)
- str += " ." + className;
- if(idName)
- str += " #" + idName;
-
- str += uniqueNodeName;
-
- FStyleProxy(style).sheet.parseCSS(uniqueNodeName+ '{' + inlineStyle + '}');
- }
-
- nodeCache[node] = str;
- }
- else
- {
- str = nodeCache[node];
- }
-
- inheritanceStructure = inheritanceStructure.concat(str.split(' '));
-
- str = '';
- className = '';
- idName = '';
- uniqueNodeName = '';
- inlineStyle = 'a:a;';
- }
-
- fStyle = FStyleProxy(style).sheet.getStyle.apply(null, inheritanceStructure);
-
- var format:ElementFormat = new ElementFormat(
- new FontDescription(
- TypeHelperUtil.getType(fStyle["fontName"] || "_sans", 'string'),
- TypeHelperUtil.getType(fStyle["fontWeight"] || FontWeight.NORMAL, 'string'),
- TypeHelperUtil.getType(fStyle["fontStyle"] || FontPosture.NORMAL, 'string'),
- TypeHelperUtil.getType(fStyle["fontLookup"] || FontLookup.DEVICE, 'string'),
- TypeHelperUtil.getType(fStyle["renderingMode"] || RenderingMode.CFF, 'string'),
- TypeHelperUtil.getType(fStyle["cffHinting"] || CFFHinting.HORIZONTAL_STEM, 'string')
- ),
- TypeHelperUtil.getType(fStyle["fontSize"] || '12', 'int'),
- TypeHelperUtil.getType(fStyle["color"] || '0x0', 'uint'),
- TypeHelperUtil.getType(fStyle["fontAlpha"] || '1', 'number'),
- TypeHelperUtil.getType(fStyle["textRotation"] || TextRotation.AUTO, 'string'),
- TypeHelperUtil.getType(fStyle["dominantBaseLine"] || TextBaseline.ROMAN, 'string'),
- TypeHelperUtil.getType(fStyle["alignmentBaseLine"] || TextBaseline.USE_DOMINANT_BASELINE, 'string'),
- TypeHelperUtil.getType(fStyle["baseLineShift"] || '0.0', 'number'),
- TypeHelperUtil.getType(fStyle["kerning"] || Kerning.ON, 'string'),
- TypeHelperUtil.getType(fStyle["trackingRight"] || '0.0', 'number'),
- TypeHelperUtil.getType(fStyle["trackingLeft"] || '0.0', 'number'),
- TypeHelperUtil.getType(fStyle["locale"] || "en", 'string'),
- TypeHelperUtil.getType(fStyle["breakOpportunity"] || BreakOpportunity.AUTO, 'string'),
- TypeHelperUtil.getType(fStyle["digitCase"] || DigitCase.DEFAULT, 'string'),
- TypeHelperUtil.getType(fStyle["digitWidth"] || DigitWidth.DEFAULT, 'string'),
- TypeHelperUtil.getType(fStyle["ligatureLevel"] || LigatureLevel.COMMON, 'string'),
- TypeHelperUtil.getType(fStyle["typographicCase"] || TypographicCase.DEFAULT, 'string'));
-
- return format;
- }
- }
-}
-
-
diff --git a/src/org/tinytlf/extensions/mx/FlexFTETextArea.as b/src/org/tinytlf/extensions/mx/FlexFTETextArea.as
deleted file mode 100644
index 7074753..0000000
--- a/src/org/tinytlf/extensions/mx/FlexFTETextArea.as
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx
-{
- import flash.display.DisplayObject;
- import flash.events.Event;
- import flash.events.EventPhase;
- import flash.text.engine.TextLine;
-
- import mx.containers.Canvas;
- import mx.core.EdgeMetrics;
- import mx.core.IUIComponent;
- import mx.core.ScrollPolicy;
- import mx.core.mx_internal;
- import org.tinytlf.extensions.mx.layout.FlexTextContainer;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.decor.decorations.BackgroundColorDecoration;
- import org.tinytlf.decor.decorations.StrikeThroughDecoration;
- import org.tinytlf.decor.decorations.UnderlineDecoration;
- import org.tinytlf.layout.ITextContainer;
-
- use namespace mx_internal;
-
- public class FlexFTETextArea extends Canvas
- {
- public function FlexFTETextArea()
- {
- super();
-
- minWidth = 100;
- minHeight = 100;
- }
-
- private var _container:ITextContainer;
-
- public function get container():ITextContainer
- {
- if(!_container)
- _container = new FlexTextContainer(this);
-
- return _container;
- }
-
- public function set container(textContainer:ITextContainer):void
- {
- if(textContainer == _container)
- return;
-
- _container = textContainer;
- _container.target = this;
- }
-
- override public function set data(value:Object):void
- {
- var changed:Boolean = (value !== super.data);
- super.data = value;
- if(changed)
- {
- textNeedsRender = true;
- invalidateProperties();
- }
- }
-
- private var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- if(!_engine)
- {
- _engine = new FlexTextEngine(stage);
- _engine.layout.addContainer(container);
-
- if(!stage)
- addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
- }
-
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- /**
- * @private
- * Called just before a line is added to the display list.
- */
- protected function hookLine(line:DisplayObject):DisplayObject
- {
- return line;
- }
-
- /**
- * @private
- * Called just before rendering of the TextContainer.
- */
- protected function hookEngine():void
- {
- if(styleName is String)
- engine.styler.style = String(styleName);
-
- //Default mapped text decorations.
- engine.decor.mapDecoration("backgroundColor", BackgroundColorDecoration);
- engine.decor.mapDecoration("underline", UnderlineDecoration);
- engine.decor.mapDecoration("strikethrough", StrikeThroughDecoration);
- }
-
- protected function renderText():void
- {
- hookEngine();
- engine.prerender();
- engine.invalidate();
- engine.render();
- }
-
- override public function addChildAt(child:DisplayObject, index:int):DisplayObject
- {
- if(child is IUIComponent)
- return super.addChildAt(child is UITextLine ? hookLine(child) : child, index);
-
- return rawChildren_addChildAt(child is TextLine ? hookLine(child) : child, index);
- }
-
- protected var textNeedsRender:Boolean = false;
-
- override protected function commitProperties():void
- {
- super.commitProperties();
-
- if(!stage && textNeedsRender)
- {
- textNeedsRender = true;
- addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
- return;
- }
-
- if(textNeedsRender)
- {
- if(isSizeSpecialCase && isNaN(lastUnscaledWidth))
- {
- invalidateDisplayList();
- return;
- }
-
- // Create the TextBlocks/Render the TextLines.
- var vm:EdgeMetrics = viewMetricsAndPadding;
- var w:Number = lastUnscaledWidth - vm.left - vm.right;
- w -= (verticalScrollPolicy == ScrollPolicy.ON || (verticalScrollPolicy == ScrollPolicy.AUTO && verticalScrollBar)) ?
- verticalScrollBar.getExplicitOrMeasuredWidth() : 0;
-
- container.allowedWidth = w;
-
- engine.blockFactory.data = data;
-
- renderText();
-
- invalidateSize();
- invalidateDisplayList();
-
- textNeedsRender = false;
- }
- }
-
- protected var lastUnscaledWidth:Number = NaN;
-
- override protected function updateDisplayList(w:Number, h:Number):void
- {
- if(isSizeSpecialCase)
- {
- var firstTime:Boolean = isNaN(lastUnscaledWidth) || lastUnscaledWidth != w;
- lastUnscaledWidth = w;
- if(firstTime)
- {
- invalidateProperties();
- return;
- }
- }
-
- lastUnscaledWidth = w;
-
- if(textNeedsRender)
- invalidateProperties();
-
- super.updateDisplayList(w, h);
- }
-
- /**
- * @private
- * The cases that requires a second pass through the LayoutManager
- * are (the control is to use the percentWidth
- * but the measuredHeight) and
- * (the control is to use the parent's width minus the constraints
- * but the measuredHeight).
- */
- protected function get isSizeSpecialCase():Boolean
- {
- var left:Number = getStyle("left");
- var right:Number = getStyle("right");
-
- return (!isNaN(percentWidth) || (!isNaN(left) && !isNaN(right))) && isNaN(explicitHeight) && isNaN(percentHeight);
- }
-
- protected function onAddedToStage(event:Event):void
- {
- if(event.eventPhase != EventPhase.AT_TARGET)
- return;
-
- removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
-
- engine.stage = stage;
-
- if(textNeedsRender)
- invalidateProperties();
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/FlexTextEngine.as b/src/org/tinytlf/extensions/mx/FlexTextEngine.as
deleted file mode 100644
index af4e62d..0000000
--- a/src/org/tinytlf/extensions/mx/FlexTextEngine.as
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx
-{
- import flash.display.Stage;
-
- import org.tinytlf.extensions.mx.decor.FlexTextDecor;
- import org.tinytlf.extensions.mx.styles.FlexTextStyler;
-
- import org.tinytlf.TextEngine;
- import org.tinytlf.decor.ITextDecor;
- import org.tinytlf.styles.ITextStyler;
-
- public class FlexTextEngine extends TextEngine
- {
- public function FlexTextEngine(stage:Stage)
- {
- super(stage);
- }
-
- override public function get decor():ITextDecor
- {
- if(!_decor)
- _decor = new FlexTextDecor();
-
- _decor.engine = this;
-
- return _decor;
- }
-
- override public function get styler():ITextStyler
- {
- if(!_styler)
- _styler = new FlexTextStyler();
-
- _styler.engine = this;
-
- return _styler;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/UITextLine.as b/src/org/tinytlf/extensions/mx/UITextLine.as
deleted file mode 100644
index 5a0309f..0000000
--- a/src/org/tinytlf/extensions/mx/UITextLine.as
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx
-{
- import flash.display.Graphics;
- import flash.text.engine.TextLine;
-
- import mx.core.UIComponent;
-
- public class UITextLine extends UIComponent
- {
- public function UITextLine(line:TextLine = null)
- {
- this.line = line;
- }
-
- private var _line:TextLine;
- public function get line():TextLine
- {
- return _line;
- }
-
- public function set line(value:TextLine):void
- {
- if(value === _line)
- return;
-
- if(line)
- uninstallLine(line);
-
- _line = value;
-
- if(line)
- installLine(line);
- }
-
- protected function installLine(line:TextLine):void
- {
- width = line.width;
- line.y = height = line.height;
-
- addChild(line);
- }
-
- protected function uninstallLine(line:TextLine):void
- {
- if(contains(line))
- removeChild(line);
-
- graphics.clear();
- }
-
- override protected function updateDisplayList(w:Number, h:Number):void
- {
- super.updateDisplayList(w, h);
-
- // Draw a mouse catcher behind the TextLine so we can catch and proxy the
- // "roll" events.
- var g:Graphics = graphics;
- g.clear();
- g.beginFill(0x00, 0);
- g.drawRect(line.x, 5, w, h);
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/core/FlexStyleProxy.as b/src/org/tinytlf/extensions/mx/core/FlexStyleProxy.as
deleted file mode 100644
index 6d3803e..0000000
--- a/src/org/tinytlf/extensions/mx/core/FlexStyleProxy.as
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx.core
-{
- import mx.core.Singleton;
- import mx.styles.CSSStyleDeclaration;
- import mx.styles.IStyleManager2;
-
- import org.tinytlf.core.StyleAwareActor;
-
- public class FlexStyleProxy extends StyleAwareActor
- {
- public function FlexStyleProxy(styleObject:Object = null)
- {
- super(styleObject);
- }
-
- protected var css:CSSStyleDeclaration;
-
- override public function set style(value:Object):void
- {
- super.style = value;
-
- if(value is String)
- {
- var name:String = String(value);
- if(name.indexOf(".") != 0)
- name = "." + name;
-
- css = new CSSStyleDeclaration(name);
- for(var s:String in styles)
- css.setStyle(s, styles[s]);
- }
- }
-
- override public function getStyle(styleProp:String):*
- {
- if(styleProp in styles)
- return styles[styleProp];
- else if(css)
- return css.getStyle(styleProp);
- }
-
- override public function setStyle(styleProp:String, newValue:*):void
- {
- styles[styleProp] = newValue;
- if(css)
- css.setStyle(styleProp, newValue);
- }
-
- protected function get styleManager():IStyleManager2
- {
- return Singleton.getInstance("mx.styles::IStyleManager2") as IStyleManager2;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/decor/FlexTextDecor.as b/src/org/tinytlf/extensions/mx/decor/FlexTextDecor.as
deleted file mode 100644
index a2d8a0e..0000000
--- a/src/org/tinytlf/extensions/mx/decor/FlexTextDecor.as
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx.decor
-{
- import org.tinytlf.decor.ITextDecoration;
- import org.tinytlf.decor.TextDecor;
- import org.tinytlf.layout.ITextContainer;
- import org.tinytlf.extensions.mx.core.FlexStyleProxy;
-
- public class FlexTextDecor extends TextDecor
- {
- public function FlexTextDecor()
- {
- super();
- }
-
- override public function decorate(element:*, styleObj:Object, layer:int=0, container:ITextContainer=null):void
- {
- if(styleObj is String)
- styleObj = new FlexStyleProxy(String(styleObj));
-
- super.decorate(element, styleObj, layer, container);
- }
-
- override public function getDecoration(styleProp:String, container:ITextContainer = null):ITextDecoration
- {
- var dec:ITextDecoration = super.getDecoration(styleProp, container);
-
- //Hook this decoration into the Flex StyleManager
- if(dec)
- dec.style = new FlexStyleProxy(dec.style);
-
- return dec;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/layout/FlexTextContainer.as b/src/org/tinytlf/extensions/mx/layout/FlexTextContainer.as
deleted file mode 100644
index d34844d..0000000
--- a/src/org/tinytlf/extensions/mx/layout/FlexTextContainer.as
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx.layout
-{
- import flash.display.DisplayObject;
- import flash.display.DisplayObjectContainer;
- import flash.text.engine.TextLine;
-
- import org.tinytlf.extensions.mx.UITextLine;
-
- import org.tinytlf.layout.TextContainerBase;
-
- public class FlexTextContainer extends TextContainerBase
- {
- public function FlexTextContainer(container:DisplayObjectContainer, width:Number = NaN, height:Number = NaN)
- {
- super(container, width, height);
- }
-
- override protected function hookLine(line:TextLine):DisplayObjectContainer
- {
- return new UITextLine(line);
- }
-
- override public function hasLine(line:TextLine):Boolean
- {
- var child:DisplayObject;
- var n:int = target.numChildren;
-
- for(var i:int = 0; i < n; i++)
- {
- child = target.getChildAt(i);
-
- if(child is UITextLine && DisplayObjectContainer(child).contains(line))
- return true;
- }
-
- return false;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/mx/styles/FlexTextStyler.as b/src/org/tinytlf/extensions/mx/styles/FlexTextStyler.as
deleted file mode 100644
index 5ad79ae..0000000
--- a/src/org/tinytlf/extensions/mx/styles/FlexTextStyler.as
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.mx.styles
-{
- import flash.text.engine.*;
-
- import mx.core.Singleton;
- import mx.styles.CSSStyleDeclaration;
- import mx.styles.IStyleManager2;
-
- import org.tinytlf.extensions.mx.core.FlexStyleProxy;
- import org.tinytlf.styles.TextStyler;
-
- public class FlexTextStyler extends TextStyler
- {
- public function FlexTextStyler()
- {
- super();
- style = new FlexStyleProxy();
- }
-
- private var css:CSSStyleDeclaration;
-
- override public function set style(value:Object):void
- {
- super.style = value;
-
- if(value is String)
- {
- var name:String = String(value);
- if(name.indexOf(".") != 0)
- name = "." + name;
-
- css = new CSSStyleDeclaration(name);
-
- for(var s:String in styles)
- css.setStyle(s, styles[s]);
- }
- }
-
- override public function getElementFormat(element:*):ElementFormat
- {
- var mainStyleDeclaration:CSSStyleDeclaration = css || new CSSStyleDeclaration();
- var elementStyleDeclaration:CSSStyleDeclaration = styleManager.getStyleDeclaration(styleMap[element] || "") || new CSSStyleDeclaration();
-
- var reduceBoilerplate:Function = function(style:String, defaultValue:*):*
- {
- return (elementStyleDeclaration.getStyle(style) || mainStyleDeclaration.getStyle(style) || defaultValue);
- }
-
- return new ElementFormat(
- new FontDescription(
- reduceBoilerplate("fontFamily", "_sans"),
- reduceBoilerplate("fontWeight", FontWeight.NORMAL),
- reduceBoilerplate("fontStyle", FontPosture.NORMAL),
- reduceBoilerplate("fontLookup", FontLookup.DEVICE),
- reduceBoilerplate("renderingMode", RenderingMode.CFF),
- reduceBoilerplate("cffHinting", CFFHinting.HORIZONTAL_STEM)
- ),
- reduceBoilerplate("fontSize", 12),
- reduceBoilerplate("color", 0x0),
- reduceBoilerplate("fontAlpha", 1),
- reduceBoilerplate("textRotation", TextRotation.AUTO),
- reduceBoilerplate("dominantBaseLine", TextBaseline.ROMAN),
- reduceBoilerplate("alignmentBaseLine", TextBaseline.USE_DOMINANT_BASELINE),
- reduceBoilerplate("baseLineShift", 0.0),
- reduceBoilerplate("kerning", Kerning.ON),
- reduceBoilerplate("trackingRight", 0.0),
- reduceBoilerplate("trackingLeft", 0.0),
- reduceBoilerplate("locale", "en"),
- reduceBoilerplate("breakOpportunity", BreakOpportunity.AUTO),
- reduceBoilerplate("digitCase", DigitCase.DEFAULT),
- reduceBoilerplate("digitWidth", DigitWidth.DEFAULT),
- reduceBoilerplate("ligatureLevel", LigatureLevel.COMMON),
- reduceBoilerplate("typographicCase", TypographicCase.DEFAULT)
- );
- }
-
- protected function get styleManager():IStyleManager2
- {
- return Singleton.getInstance("mx.styles::IStyleManager2") as IStyleManager2;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/xhtml/interaction/AnchorInteractor.as b/src/org/tinytlf/extensions/xhtml/interaction/AnchorInteractor.as
deleted file mode 100644
index 7c56487..0000000
--- a/src/org/tinytlf/extensions/xhtml/interaction/AnchorInteractor.as
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.xhtml.interaction
-{
- import flash.events.IEventDispatcher;
- import flash.events.MouseEvent;
- import flash.net.URLRequest;
- import flash.net.navigateToURL;
- import flash.text.engine.ContentElement;
- import flash.ui.Mouse;
- import flash.ui.MouseCursor;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.decor.ITextDecor;
- import org.tinytlf.interaction.LineEventInfo;
- import org.tinytlf.interaction.TextDispatcherBase;
-
- public class AnchorInteractor extends TextDispatcherBase
- {
- public function AnchorInteractor(target:IEventDispatcher=null)
- {
- super(target);
- }
-
- override protected function onRollOver(event:MouseEvent):void
- {
- super.onRollOver(event);
-
- Mouse.cursor = MouseCursor.BUTTON;
- }
-
- override protected function onRollOut(event:MouseEvent):void
- {
- super.onRollOut(event);
-
- Mouse.cursor = MouseCursor.ARROW;
- }
-
- override protected function onMouseMove(event:MouseEvent):void
- {
- super.onMouseMove(event);
-
- Mouse.cursor = MouseCursor.BUTTON;
- }
-
- override protected function onClick(event:MouseEvent):void
- {
- super.onClick(event);
-
- if(!allowEvent)
- return;
-
- var info:LineEventInfo = LineEventInfo.getInfo(event, this);
-
- // If the Text Engine is in the middle of rendering lines, info will
- // be null. Don't try to mess w/ stuff while we're rendering :)
- if(!info)
- return;
-
- var engine:ITextEngine = info.engine;
- var element:ContentElement = info.element;
-
- var obj:Object = element.userData
- if(!obj || !('link' in obj))
- return;
-
- navigateToURL(new URLRequest(obj['link']), obj['target'] || '_blank');
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/xhtml/layout/adapter/AnchorAdapter.as b/src/org/tinytlf/extensions/xhtml/layout/adapter/AnchorAdapter.as
deleted file mode 100644
index 9081b1e..0000000
--- a/src/org/tinytlf/extensions/xhtml/layout/adapter/AnchorAdapter.as
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.xhtml.layout.adapter
-{
- import flash.text.engine.ContentElement;
- import flash.text.engine.GroupElement;
- import flash.text.engine.TextElement;
-
- import org.tinytlf.layout.adapter.ContentElementAdapter;
-
- public class AnchorAdapter extends ContentElementAdapter
- {
- public function AnchorAdapter()
- {
- super();
- }
-
- override public function execute(data:Object, ... context:Array):ContentElement
- {
- var element:ContentElement = super.execute.apply(null, [data].concat(context));
-
- if(!element)
- return null;
-
- var anchor:XML;
- if(context.length)
- anchor = context[0];
-
- if(anchor)
- {
- var obj:Object = {};
- obj['link'] = anchor.@href || '';
- obj['target'] = anchor.@target || '';
- element.userData = obj;
- }
-
- return element;
- }
- }
-}
-
diff --git a/src/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactory.as b/src/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactory.as
deleted file mode 100644
index 4e7c824..0000000
--- a/src/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactory.as
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.extensions.xml.layout.factory
-{
- import flash.text.engine.ContentElement;
-
- import org.tinytlf.layout.adapter.IContentElementAdapter;
- import org.tinytlf.layout.factory.AbstractLayoutModelFactory;
-
- public class XMLBlockFactory extends AbstractLayoutModelFactory
- {
- XML.ignoreWhitespace = false;
-
- override public function createElements(... args):Vector.
- {
- var elements:Vector. = super.createElements.apply(null, args);
-
- if(!(data is String) && !(data is XML))
- return elements;
-
- var xml:XML = getXML(data);
- if(!xml)
- return elements;
-
- ancestorList = [];
-
- elements.push(getElementForNode(xml));
-
- return elements;
- }
-
- protected function getXML(xmlOrString:Object):XML
- {
- try{
- return XML(xmlOrString);
- }
- catch(e:Error){
- //If we failed the first time, maybe they passed in a string like this:
- // "My string that has nodes without a root."
- try{
- return new XML("<_>" + xmlOrString + "");
- }
- catch(e:Error){
- //Do nothing now, we've failed.
- }
- }
-
- return null;
- }
-
- protected var ancestorList:Array;
-
- protected function getElementForNode(node:XML):ContentElement
- {
- if(!node)
- return null;
-
- var parentName:String = node.localName();
-
- if(ancestorList.length)
- parentName = ancestorList[ancestorList.length - 1].localName();
-
- if(node.nodeKind() != "text")
- ancestorList.push(new XML(String(node.toXMLString().match(/\<[^\/](.*?)\>/)[0]).replace('>', '/>')));
-
- var adapter:IContentElementAdapter = getElementAdapter(parentName);
- var content:ContentElement;
-
- if(node..*.length() > 1)
- {
- var elements:Vector. = new Vector.();
- for each(var child:XML in node.*)
- elements.push(getElementForNode(child));
-
- content = adapter.execute.apply(null, [elements].concat(ancestorList));
- ancestorList.pop();
- }
- else if(node..*.length() == 1 || node.nodeKind() != 'text')
- {
- adapter = getElementAdapter(node.localName());
- content = adapter.execute.apply(null, [node.text().toString()].concat(ancestorList));
- ancestorList.pop();
- }
- else
- content = adapter.execute.apply(null, [node.toString()].concat(ancestorList));
-
- return content;
- }
- }
-}
-
diff --git a/src/org/tinytlf/interaction/ITextInteractor.as b/src/org/tinytlf/interaction/ITextInteractor.as
deleted file mode 100644
index 0f0f90a..0000000
--- a/src/org/tinytlf/interaction/ITextInteractor.as
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.interaction
-{
- import org.tinytlf.ITextEngine;
-
- import flash.events.EventDispatcher;
- import flash.events.IEventDispatcher;
-
- public interface ITextInteractor extends IEventDispatcher
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- /**
- * Returns an EventDispatcher for an element name. Typically an
- * IModelAdapter calls this when he is creating a ContentElement
- * and is ready to specify an eventMirror.
- */
- function getMirror(elementName:String = ""):EventDispatcher;
-
- /**
- * Maps a mirror class or instance to an elementName.
- * @return True if the mirror was successfully mapped, False
- * if it wasn't.
- */
- function mapMirror(elementName:String, mirrorClassOrInstance:Object):void;
- /**
- * Unmaps the mirror class or instance for the given elementName.
- * @return True if the mirror was successfully unmapped, or False
- * if it wasn't.
- */
- function unMapMirror(elementName:String):Boolean;
- }
-}
-
diff --git a/src/org/tinytlf/interaction/LineEventInfo.as b/src/org/tinytlf/interaction/LineEventInfo.as
deleted file mode 100644
index debecae..0000000
--- a/src/org/tinytlf/interaction/LineEventInfo.as
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.interaction
-{
- import flash.display.DisplayObjectContainer;
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.events.MouseEvent;
- import flash.text.engine.ContentElement;
- import flash.text.engine.TextLine;
- import flash.text.engine.TextLineMirrorRegion;
- import flash.text.engine.TextLineValidity;
- import flash.utils.getDefinitionByName;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.ITextContainer;
- import org.tinytlf.utils.FTEUtil;
-
- public class LineEventInfo
- {
- public static var uiLineClass:Class = checkFlex();
-
- protected static function checkFlex():Class
- {
- try{
- return getDefinitionByName("mx.tinytlf::UITextLine") as Class;
- }
- catch(e:*){
- }
-
- return null;
- }
-
- public static function getInfo(event:Event, eventMirror:EventDispatcher = null):LineEventInfo
- {
- var target:Object = event.target;
- var canProcess:Boolean = (target is TextLine) || (uiLineClass && target is uiLineClass);
-
- if(!canProcess)
- return null;
-
- var line:TextLine = (target as TextLine) || Object(target).line;
-
- if(line.validity == TextLineValidity.INVALID)
- return null;
-
- var index:int = line.textBlockBeginIndex;
-
- if(event is MouseEvent)
- index = FTEUtil.getAtomIndexAtPoint(MouseEvent(event).stageX, MouseEvent(event).stageY, line);
-
- var element:ContentElement = FTEUtil.getContentElementAt(line.textBlock.content, index);
- var mirrorRegion:TextLineMirrorRegion = line.getMirrorRegion(eventMirror || element.eventMirror);
-
- return new LineEventInfo(line, line.userData, mirrorRegion, mirrorRegion != null ? mirrorRegion.element : element, line.parent || (target as DisplayObjectContainer));
- }
-
- public function LineEventInfo(line:TextLine, engine:ITextEngine, mirrorRegion:TextLineMirrorRegion, element:ContentElement, lineParent:DisplayObjectContainer)
- {
- _engine = engine;
- _element = element;
- _line = line;
- _mirrorRegion = mirrorRegion;
- _container = engine.layout.getContainerForLine(line);
- _lineParent = lineParent;
- }
-
- private var _container:ITextContainer;
- public function get container():ITextContainer
- {
- return _container;
- }
-
- private var _engine:ITextEngine;
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- private var _element:ContentElement;
- public function get element():ContentElement
- {
- return _element;
- }
-
- private var _lineParent:DisplayObjectContainer;
- public function get lineParent():DisplayObjectContainer
- {
- return _lineParent;
- }
-
- private var _line:TextLine;
- public function get line():TextLine
- {
- return _line;
- }
-
- private var _mirrorRegion:TextLineMirrorRegion;
- public function get mirrorRegion():TextLineMirrorRegion
- {
- return _mirrorRegion;
- }
- }
-}
-
diff --git a/src/org/tinytlf/interaction/TextDispatcherBase.as b/src/org/tinytlf/interaction/TextDispatcherBase.as
deleted file mode 100644
index 4ee6576..0000000
--- a/src/org/tinytlf/interaction/TextDispatcherBase.as
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.interaction
-{
- import flash.events.EventDispatcher;
- import flash.events.IEventDispatcher;
- import flash.events.KeyboardEvent;
- import flash.events.MouseEvent;
- import flash.geom.Point;
- import flash.utils.getTimer;
-
- import org.tinytlf.utils.FTEUtil;
-
- public class TextDispatcherBase extends EventDispatcher
- {
- public function TextDispatcherBase(target:IEventDispatcher = null)
- {
- super(target);
-
- addListeners(this);
- }
-
- public static const UP:uint = 0x0001;
- public static const OVER:uint = 0x0002;
- public static const DOWN:uint = 0x0004;
-
- protected static var mouseState:uint = UP; // | DOWN;
- protected static var mouseCoords:Point;
-
- protected function onClick(event:MouseEvent):void
- {
- mouseState = FTEUtil.updateBits(mouseState, DOWN, false);
- mouseState = FTEUtil.updateBits(mouseState, UP);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- protected function onRollOver(event:MouseEvent):void
- {
- mouseState = FTEUtil.updateBits(mouseState, OVER);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- protected function onRollOut(event:MouseEvent):void
- {
- mouseState = FTEUtil.updateBits(mouseState, OVER, false);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- protected function onMouseMove(event:MouseEvent):void
- {
- mouseState = FTEUtil.updateBits(mouseState, OVER);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- private static var doubleClickTime:int = 0;
-
- protected function onMouseDown(event:MouseEvent):void
- {
- var time:int = getTimer();
- if((time - doubleClickTime) < 500)
- mouseState = FTEUtil.updateBits(mouseState, DOWN, false);
- else
- mouseState = FTEUtil.updateBits(mouseState, DOWN);
-
- doubleClickTime = time;
-
- mouseState = FTEUtil.updateBits(mouseState, UP, false);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- protected function onMouseUp(event:MouseEvent):void
- {
- mouseState = FTEUtil.updateBits(mouseState, DOWN, false);
- mouseState = FTEUtil.updateBits(mouseState, UP);
- mouseCoords = new Point(event.stageX, event.stageY);
- }
-
- protected function onKeyDown(event:KeyboardEvent):void
- {
- }
-
- protected function onKeyUp(event:KeyboardEvent):void
- {
- }
-
- public function addListeners(target:IEventDispatcher):void
- {
- target.addEventListener(MouseEvent.MOUSE_OVER, onRollOver, false, 0, true);
- target.addEventListener(MouseEvent.MOUSE_OUT, onRollOut, false, 0, true);
-
- target.addEventListener(MouseEvent.ROLL_OVER, onRollOver, false, 0, true);
- target.addEventListener(MouseEvent.ROLL_OUT, onRollOut, false, 0, true);
-
- target.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false, 0, true);
- target.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true);
- target.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true);
- target.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);
-
- target.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 0, true);
- target.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 0, true);
- }
-
- public function removeListeners(target:IEventDispatcher):void
- {
- target.removeEventListener(MouseEvent.MOUSE_OVER, onRollOver);
- target.removeEventListener(MouseEvent.MOUSE_OUT, onRollOut);
-
- target.removeEventListener(MouseEvent.ROLL_OVER, onRollOver);
- target.removeEventListener(MouseEvent.ROLL_OUT, onRollOut);
-
- target.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
- target.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
- target.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
-
- target.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
- target.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
- }
-
- protected static var eventTime:int = 0;
-
- protected static function get allowEvent():Boolean
- {
- var time:int = getTimer();
- var ret:Boolean = (time - eventTime) > 100;
- eventTime = time;
- return ret;
- }
-
- protected static function get allowFastEvent():Boolean
- {
- var time:int = getTimer();
- var ret:Boolean = (time - eventTime) > 10;
- eventTime = time;
- return ret;
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/ITextContainer.as b/src/org/tinytlf/layout/ITextContainer.as
deleted file mode 100644
index 46bc898..0000000
--- a/src/org/tinytlf/layout/ITextContainer.as
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout
-{
- import org.tinytlf.ITextEngine;
-
- import flash.display.DisplayObjectContainer;
- import flash.display.Sprite;
- import flash.text.engine.TextBlock;
- import flash.text.engine.TextLine;
-
- public interface ITextContainer
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function get target():DisplayObjectContainer;
- function set target(textContainer:DisplayObjectContainer):void;
-
- function get shapes():Sprite;
- function set shapes(shapesContainer:Sprite):void;
-
- function get allowedWidth():Number;
- function set allowedWidth(value:Number):void;
-
- function get allowedHeight():Number;
- function set allowedHeight(value:Number):void;
-
- function get measuredWidth():Number;
- function get measuredHeight():Number;
-
- function clear():void;
- function layout(block:TextBlock, line:TextLine):TextLine;
- function resetShapes():void;
- function hasLine(line:TextLine):Boolean;
- }
-}
-
diff --git a/src/org/tinytlf/layout/ITextLayout.as b/src/org/tinytlf/layout/ITextLayout.as
deleted file mode 100644
index a4f8ecd..0000000
--- a/src/org/tinytlf/layout/ITextLayout.as
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout
-{
- import flash.text.engine.TextBlock;
- import flash.text.engine.TextLine;
-
- import org.tinytlf.ITextEngine;
-
- public interface ITextLayout
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function get containers():Vector.;
-
- function addContainer(container:ITextContainer):void;
- function removeContainer(container:ITextContainer):void;
-
- function getContainerForLine(line:TextLine):ITextContainer;
-
- function clear():void;
- function resetShapes():void;
- function render(blocks:Vector.):void;
- }
-}
-
diff --git a/src/org/tinytlf/layout/LayoutProperties.as b/src/org/tinytlf/layout/LayoutProperties.as
deleted file mode 100644
index b42d614..0000000
--- a/src/org/tinytlf/layout/LayoutProperties.as
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout
-{
- import org.tinytlf.layout.description.TextAlign;
- import org.tinytlf.layout.description.TextDirection;
- import org.tinytlf.layout.description.TextFloat;
- import org.tinytlf.layout.description.TextTransform;
-
- public class LayoutProperties
- {
- public function LayoutProperties(props:Object = null)
- {
- if(!props)
- return;
-
- for(var prop:String in props)
- {
- if(prop in this)
- this[prop] = props[prop]
- }
- }
-
- public var associatedItem:*;
-
- public var width:Number = 0;
- public var lineHeight:Number = 0;
- public var textIndent:Number = 0;
- public var paddingLeft:Number = 0;
- public var paddingRight:Number = 0;
- public var paddingBottom:Number = 0;
- public var paddingTop:Number = 0;
-
- public var textAlign:TextAlign = TextAlign.LEFT;
- public var textDirection:TextDirection = TextDirection.LTR;
- public var textTransform:TextTransform = TextTransform.NONE;
- public var float:TextFloat = TextFloat.LEFT;
- }
-}
-
diff --git a/src/org/tinytlf/layout/TextContainerBase.as b/src/org/tinytlf/layout/TextContainerBase.as
deleted file mode 100644
index 9319f4d..0000000
--- a/src/org/tinytlf/layout/TextContainerBase.as
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout
-{
- import flash.display.DisplayObjectContainer;
- import flash.display.Sprite;
- import flash.text.engine.LineJustification;
- import flash.text.engine.SpaceJustifier;
- import flash.text.engine.TextBlock;
- import flash.text.engine.TextLine;
- import flash.utils.Dictionary;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.description.TextAlign;
-
- public class TextContainerBase implements ITextContainer
- {
- public function TextContainerBase(container:DisplayObjectContainer, allowedWidth:Number = NaN, allowedHeight:Number = NaN)
- {
- this.target = container;
-
- _allowedWidth = allowedWidth;
- _allowedHeight = allowedHeight;
- }
-
- protected var _target:DisplayObjectContainer;
- public function get target():DisplayObjectContainer
- {
- return _target;
- }
-
- public function set target(doc:DisplayObjectContainer):void
- {
- if(doc == _target)
- return;
-
- _target = doc;
-
- shapes = Sprite(target.addChild(new Sprite()));
- }
-
- protected var _engine:ITextEngine;
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- private var _shapes:Sprite;
- public function get shapes():Sprite
- {
- return _shapes;
- }
-
- public function set shapes(shapesContainer:Sprite):void
- {
- if(shapesContainer === _shapes)
- return;
-
- var children:Array = prepareShapesChildren();
-
- _shapes = shapesContainer;
-
- while(children.length)
- shapes.addChild(children.shift());
- }
-
- private function prepareShapesChildren():Array
- {
- var children:Array = [];
- if (shapes)
- {
- children = createArrayOfShapesChildren();
- removeShapesFromParent();
- }
- return children;
- }
-
- private function removeShapesFromParent():void
- {
- if (shapes.parent && shapes.parent.contains(shapes))
- shapes.parent.removeChild(shapes);
- }
-
- private function createArrayOfShapesChildren():Array
- {
- var children:Array = [];
- while (shapes.numChildren)
- children.push(shapes.removeChildAt(0));
- return children;
- }
-
- protected var _allowedWidth:Number = NaN;
-
- public function get allowedWidth():Number
- {
- return _allowedWidth;
- }
-
- public function set allowedWidth(value:Number):void
- {
- _allowedWidth = value;
- }
-
- protected var _allowedHeight:Number = NaN;
-
- public function get allowedHeight():Number
- {
- return _allowedHeight;
- }
-
- public function set allowedHeight(value:Number):void
- {
- _allowedHeight = value;
- }
-
- protected var width:Number = 0;
-
- public function get measuredWidth():Number
- {
- return width;
- }
-
- protected var height:Number = 0;
-
- public function get measuredHeight():Number
- {
- return height;
- }
-
- protected var lines:Dictionary = new Dictionary(false);
-
- public function hasLine(line:TextLine):Boolean
- {
- return (line in lines);
- }
-
- public function clear():void
- {
- for(var line:* in lines)
- target.removeChild(line);
-
- height = 0;
- }
-
- public function resetShapes():void
- {
- while (shapes && shapes.numChildren)
- shapes.removeChildAt(0);
- }
-
- public function layout(block:TextBlock, line:TextLine):TextLine
- {
- var doc:DisplayObjectContainer;
- var props:LayoutProperties = getLayoutProperties(block);
-
- height += props.paddingTop;
-
- if(props.textAlign == TextAlign.JUSTIFY)
- block.textJustifier = new SpaceJustifier("en", LineJustification.ALL_BUT_LAST, true);
-
- line = createLine(block, line);
-
- while(line)
- {
- width = allowedWidth;
-
- line.userData = engine;
-
- doc = hookLine(line);
-
- height += doc.height;
-
- doc.y = height;
-
- target.addChild(doc);
-
- height += props.lineHeight;
-
- if(!isNaN(allowedHeight) && measuredHeight > allowedHeight)
- return line;
-
- line = createLine(block, line);
- }
-
- height += props.paddingBottom;
-
- return line;
- }
-
- protected function createLine(block:TextBlock, line:TextLine = null):TextLine
- {
- var props:LayoutProperties = getLayoutProperties(block);
-
- var w:Number = allowedWidth || props.width;
- var x:Number = 0;
-
- if(line == null)
- {
- w -= props.textIndent;
- x += props.textIndent;
- }
-
- w -= props.paddingLeft;
- w -= props.paddingRight;
-
- line = block.createTextLine(line, w);
-
- if(!line)
- return null;
-
- switch(props.textAlign)
- {
- case TextAlign.LEFT:
- case TextAlign.JUSTIFY:
- x += props.paddingLeft;
- break;
- case TextAlign.CENTER:
- x = (width - line.width) * 0.5;
- break;
- case TextAlign.RIGHT:
- x = width - line.width + props.paddingRight;
- break;
- }
-
- line.x = x;
-
- return line;
- }
-
- protected function hookLine(line:TextLine):DisplayObjectContainer
- {
- lines[line] = true;
- return line;
- }
-
- protected function getLayoutProperties(block:TextBlock):LayoutProperties
- {
- var data:Object = block.userData;
- if(data is LayoutProperties)
- return LayoutProperties(data);
-
- return new LayoutProperties();
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/TextLayoutBase.as b/src/org/tinytlf/layout/TextLayoutBase.as
deleted file mode 100644
index 84de2f2..0000000
--- a/src/org/tinytlf/layout/TextLayoutBase.as
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout
-{
- import flash.text.engine.TextBlock;
- import flash.text.engine.TextLine;
-
- import org.tinytlf.ITextEngine;
-
- public class TextLayoutBase implements ITextLayout
- {
- protected var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- public function clear():void
- {
- for (var i:int = 0; i < containers.length; i++)
- {
- containers[i].clear();
- }
- }
-
- public function resetShapes():void
- {
- for (var i:int = 0; i < containers.length; i++)
- {
- containers[i].resetShapes();
- }
- }
-
- public function render(blocks:Vector.):void
- {
- if(!containers || !containers.length || !blocks || !blocks.length)
- return;
-
- var blockIndex:int = 0;
- var containerIndex:int = 0;
-
- var block:TextBlock;
- var container:ITextContainer;
- var line:TextLine;
-
- while(blockIndex < blocks.length)
- {
- block = blocks[blockIndex];
- container = containers[containerIndex];
-
- line = container.layout(block, line);
-
- if(line && ++containerIndex < containers.length)
- container = containers[containerIndex];
- else if(++blockIndex < blocks.length)
- block = blocks[blockIndex];
- else
- return;
- }
- }
-
- protected var _containers:Vector. = new Vector.;
-
- public function get containers():Vector.
- {
- return _containers ? _containers.concat() : new Vector.;
- }
-
- public function addContainer(container:ITextContainer):void
- {
- if(containers.indexOf(container) != -1)
- return;
-
- _containers.push(container);
- container.engine = engine;
- }
-
- public function removeContainer(container:ITextContainer):void
- {
- var i:int = containers.indexOf(container);
- if(i == -1)
- return;
-
- _containers.splice(i, 1);
- container.engine = null;
- }
-
- public function getContainerForLine(line:TextLine):ITextContainer
- {
- var n:int = containers.length;
-
- for(var i:int = 0; i < n; i++)
- {
- if(containers[i].hasLine(line))
- return containers[i];
- }
-
- return null;
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/adapter/ContentElementAdapter.as b/src/org/tinytlf/layout/adapter/ContentElementAdapter.as
deleted file mode 100644
index 62b36d6..0000000
--- a/src/org/tinytlf/layout/adapter/ContentElementAdapter.as
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.adapter
-{
- import flash.events.EventDispatcher;
- import flash.text.engine.ContentElement;
- import flash.text.engine.ElementFormat;
- import flash.text.engine.GroupElement;
- import flash.text.engine.TextElement;
-
- import org.tinytlf.ITextEngine;
-
- public class ContentElementAdapter implements IContentElementAdapter
- {
- public function execute(data:Object, ... context:Array):ContentElement
- {
- var element:ContentElement;
-
- var name:String = "";
-
- if(context.length)
- name = context[0].localName();
-
- if(data is String)
- element = new TextElement(String(data), getElementFormat(context), getEventMirror(name));
- else if(data is Vector.)
- element = new GroupElement(Vector.(data), getElementFormat(context), getEventMirror(name));
-
- if(!element)
- return null;
-
- //Check to see if there's a styleName for this element
- var style:* = engine.styler.getMappedStyle(name);
- if(style)
- engine.decor.decorate(element, style);
-
- return element;
- }
-
- private var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine === _engine)
- return;
-
- _engine = textEngine;
- }
-
- protected function getElementFormat(context:Object):ElementFormat
- {
- // You can't render a textLine with a null ElementFormat, so return an empty one here.
- if(!_engine)
- return new ElementFormat();
-
- return engine.styler.getElementFormat(context);
- }
-
- protected function getEventMirror(forName:String):EventDispatcher
- {
- if(!_engine)
- return null;
-
- return engine.interactor.getMirror(forName);
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/adapter/IContentElementAdapter.as b/src/org/tinytlf/layout/adapter/IContentElementAdapter.as
deleted file mode 100644
index 9359281..0000000
--- a/src/org/tinytlf/layout/adapter/IContentElementAdapter.as
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.adapter
-{
- import flash.text.engine.ContentElement;
-
- import org.tinytlf.ITextEngine;
-
- public interface IContentElementAdapter
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function execute(data:Object, ...context:Array):ContentElement;
- }
-}
-
diff --git a/src/org/tinytlf/layout/adapter/ITextBlockAdapter.as b/src/org/tinytlf/layout/adapter/ITextBlockAdapter.as
deleted file mode 100644
index fe838c9..0000000
--- a/src/org/tinytlf/layout/adapter/ITextBlockAdapter.as
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.adapter
-{
- import flash.text.engine.ContentElement;
- import flash.text.engine.TextBlock;
-
- import org.tinytlf.ITextEngine;
-
- public interface ITextBlockAdapter
- {
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function execute(content:ContentElement, ...context):TextBlock;
- }
-}
-
diff --git a/src/org/tinytlf/layout/adapter/TextBlockAdapter.as b/src/org/tinytlf/layout/adapter/TextBlockAdapter.as
deleted file mode 100644
index 63cd195..0000000
--- a/src/org/tinytlf/layout/adapter/TextBlockAdapter.as
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.adapter
-{
- import flash.text.engine.ContentElement;
- import flash.text.engine.TextBlock;
-
- import org.tinytlf.ITextEngine;
-
- public class TextBlockAdapter implements ITextBlockAdapter
- {
- public function execute(content:ContentElement, ...context):TextBlock
- {
- return new TextBlock(content);
- }
-
- private var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine === _engine)
- return;
-
- _engine = textEngine;
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/description/Enum.as b/src/org/tinytlf/layout/description/Enum.as
deleted file mode 100644
index f78ded8..0000000
--- a/src/org/tinytlf/layout/description/Enum.as
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.description
-{
- public class Enum
- {
- public function Enum(identifier:String)
- {
- _id = identifier;
- }
-
- private var _id:String = "";
- public function get id():String
- {
- return _id;
- }
- }
-}
-
diff --git a/src/org/tinytlf/layout/description/TextAlign.as b/src/org/tinytlf/layout/description/TextAlign.as
deleted file mode 100644
index 7d4c0d5..0000000
--- a/src/org/tinytlf/layout/description/TextAlign.as
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.description
-{
- public class TextAlign extends Enum
- {
- public function TextAlign(identifier:String)
- {
- super(id);
- }
-
- public static const LEFT:TextAlign = new TextAlign("left");
- public static const CENTER:TextAlign = new TextAlign("center");
- public static const RIGHT:TextAlign = new TextAlign("right");
- public static const JUSTIFY:TextAlign = new TextAlign("justify");
- }
-}
-
diff --git a/src/org/tinytlf/layout/description/TextDirection.as b/src/org/tinytlf/layout/description/TextDirection.as
deleted file mode 100644
index 4f0ceb7..0000000
--- a/src/org/tinytlf/layout/description/TextDirection.as
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.description
-{
- public class TextDirection extends Enum
- {
- public function TextDirection(identifier:String)
- {
- super(identifier);
- }
-
- public static const LTR:TextDirection = new TextDirection("ltr");
- public static const RTL:TextDirection = new TextDirection("rtl");
- }
-}
-
diff --git a/src/org/tinytlf/layout/description/TextFloat.as b/src/org/tinytlf/layout/description/TextFloat.as
deleted file mode 100644
index 163c248..0000000
--- a/src/org/tinytlf/layout/description/TextFloat.as
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.description
-{
- public class TextFloat extends Enum
- {
- public function TextFloat(identifier:String)
- {
- super(identifier);
- }
-
- public static const LEFT:TextFloat = new TextFloat("left");
- public static const RIGHT:TextFloat = new TextFloat("right");
- }
-}
-
diff --git a/src/org/tinytlf/layout/description/TextTransform.as b/src/org/tinytlf/layout/description/TextTransform.as
deleted file mode 100644
index a37ab69..0000000
--- a/src/org/tinytlf/layout/description/TextTransform.as
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.description
-{
- public class TextTransform extends Enum
- {
- public function TextTransform(identifier:String)
- {
- super(identifier);
- }
-
- public static const CAPITALIZE:TextTransform = new TextTransform('capitalize');
- public static const UPPERCASE:TextTransform = new TextTransform('uppercase');
- public static const LOWERCASE:TextTransform = new TextTransform('lowercase');
- public static const NONE:TextTransform = new TextTransform('none');
- }
-}
-
diff --git a/src/org/tinytlf/layout/factory/AbstractLayoutModelFactory.as b/src/org/tinytlf/layout/factory/AbstractLayoutModelFactory.as
deleted file mode 100644
index 6053214..0000000
--- a/src/org/tinytlf/layout/factory/AbstractLayoutModelFactory.as
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.factory
-{
- import flash.text.engine.ContentElement;
- import flash.text.engine.TextBlock;
- import flash.utils.Dictionary;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.adapter.ContentElementAdapter;
- import org.tinytlf.layout.adapter.IContentElementAdapter;
- import org.tinytlf.layout.adapter.ITextBlockAdapter;
- import org.tinytlf.layout.adapter.TextBlockAdapter;
-
- public class AbstractLayoutModelFactory implements ILayoutModelFactory
- {
- public static const WHITE_SPACE:String = "whitespace";
- public static const GENERIC_TEXT:String = "text";
-
- private var _blockAdapter:ITextBlockAdapter;
-
- public function get blockAdapter():ITextBlockAdapter
- {
- if(!_blockAdapter)
- {
- _blockAdapter = new TextBlockAdapter();
- _blockAdapter.engine = engine;
- }
-
- return _blockAdapter;
- }
-
- public function set blockAdapter(adapter:ITextBlockAdapter):void
- {
- if(adapter === _blockAdapter)
- return;
-
- _blockAdapter = adapter;
- _blockAdapter.engine = engine;
- }
-
- private var _data:*;
-
- public function get data():Object
- {
- return _data;
- }
-
- public function set data(value:Object):void
- {
- if(value == _data)
- return;
-
- _data = value;
- }
-
- private var _engine:ITextEngine;
-
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- protected var _blocks:Vector. = new Vector.();
-
- public function get blocks():Vector.
- {
- return _blocks ? _blocks.concat() : new Vector.;
- }
-
- protected var _elements:Vector.;
-
- public function get elements():Vector.
- {
- return _elements ? _elements.concat() : new Vector.;
- }
-
- public function createBlocks(... args):Vector.
- {
- if(_blocks != null)
- {
- var block:TextBlock;
- while(_blocks.length > 0)
- {
- block = _blocks.pop();
- block.releaseLines(block.firstLine, block.lastLine);
- }
- }
- else
- {
- _blocks = new Vector.();
- }
-
- var elements:Vector. = createElements.apply(null, args);
-
- while(elements.length > 0)
- _blocks.push(blockAdapter.execute(elements.shift()));
-
- return _blocks;
- }
-
- public function createElements(... args):Vector.
- {
- _elements = new Vector.();
- return elements;
- }
-
- protected var elementAdapterMap:Dictionary = new Dictionary(false);
-
- public function getElementAdapter(element:*):IContentElementAdapter
- {
- var adapter:*;
-
- //Return the generic adapter if we haven't mapped any.
- if(!(element in elementAdapterMap))
- {
- adapter = new ContentElementAdapter();
- IContentElementAdapter(adapter).engine = engine;
- return adapter;
- }
-
- adapter = elementAdapterMap[element];
- if(adapter is Class)
- adapter = IContentElementAdapter(new (adapter as Class)());
- if(adapter is Function)
- adapter = IContentElementAdapter((adapter as Function)());
-
- IContentElementAdapter(adapter).engine = engine;
-
- return IContentElementAdapter(adapter);
- }
-
- public function mapElementAdapter(element:*, adapterClassOrInstance:Object):void
- {
- elementAdapterMap[element] = adapterClassOrInstance;
- }
-
- public function unMapElementAdapter(element:*):Boolean
- {
- if(!(element in elementAdapterMap))
- return false;
-
- return delete elementAdapterMap[element];
- }
- }
-}
\ No newline at end of file
diff --git a/src/org/tinytlf/layout/factory/ILayoutModelFactory.as b/src/org/tinytlf/layout/factory/ILayoutModelFactory.as
deleted file mode 100644
index da8c14c..0000000
--- a/src/org/tinytlf/layout/factory/ILayoutModelFactory.as
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.layout.factory
-{
- import flash.text.engine.ContentElement;
- import flash.text.engine.TextBlock;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.layout.adapter.IContentElementAdapter;
- import org.tinytlf.layout.adapter.ITextBlockAdapter;
-
- /**
- * Ultimately the job of BlockFactory is to generate TextBlocks for us. It just
- * so happens that generating TextBlocks requires the generation of
- * ContentElements as well. Thus this is the main model actor in tinyTLF.
- */
- public interface ILayoutModelFactory
- {
- function get blockAdapter():ITextBlockAdapter;
- function set blockAdapter(adapter:ITextBlockAdapter):void;
-
- function get data():Object;
- function set data(value:Object):void;
-
- function get engine():ITextEngine;
- function set engine(textEngine:ITextEngine):void;
-
- function get blocks():Vector.;
- function get elements():Vector.;
-
- function createBlocks(...args):Vector.;
- function createElements(...args):Vector.;
-
- function getElementAdapter(element:*):IContentElementAdapter;
- function mapElementAdapter(element:*, adapterClassOrInstance:Object):void;
- function unMapElementAdapter(element:*):Boolean;
- }
-}
-
diff --git a/src/org/tinytlf/styles/TextStyler.as b/src/org/tinytlf/styles/TextStyler.as
deleted file mode 100644
index 782e6d6..0000000
--- a/src/org/tinytlf/styles/TextStyler.as
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.styles
-{
- import flash.text.engine.BreakOpportunity;
- import flash.text.engine.CFFHinting;
- import flash.text.engine.DigitCase;
- import flash.text.engine.DigitWidth;
- import flash.text.engine.ElementFormat;
- import flash.text.engine.FontDescription;
- import flash.text.engine.FontLookup;
- import flash.text.engine.FontPosture;
- import flash.text.engine.FontWeight;
- import flash.text.engine.Kerning;
- import flash.text.engine.LigatureLevel;
- import flash.text.engine.RenderingMode;
- import flash.text.engine.TextBaseline;
- import flash.text.engine.TextRotation;
- import flash.text.engine.TypographicCase;
- import flash.utils.Dictionary;
-
- import org.tinytlf.ITextEngine;
- import org.tinytlf.core.StyleAwareActor;
-
- public class TextStyler extends StyleAwareActor implements ITextStyler
- {
- protected var _engine:ITextEngine;
- public function get engine():ITextEngine
- {
- return _engine;
- }
-
- public function set engine(textEngine:ITextEngine):void
- {
- if(textEngine == _engine)
- return;
-
- _engine = textEngine;
- }
-
- public function getElementFormat(element:*):ElementFormat
- {
- var reduceBoilerplate:Function = function(style:String, defaultValue:*):*
- {
- return(getStyle(style) || defaultValue);
- }
-
- return new ElementFormat(
- new FontDescription(
- reduceBoilerplate("fontName", "_sans"),
- reduceBoilerplate("fontWeight", FontWeight.NORMAL),
- reduceBoilerplate("fontStyle", FontPosture.NORMAL),
- reduceBoilerplate("fontLookup", FontLookup.DEVICE),
- reduceBoilerplate("renderingMode", RenderingMode.CFF),
- reduceBoilerplate("cffHinting", CFFHinting.HORIZONTAL_STEM)
- ),
- reduceBoilerplate("fontSize", 12),
- reduceBoilerplate("color", 0x0),
- reduceBoilerplate("fontAlpha", 1),
- reduceBoilerplate("textRotation", TextRotation.AUTO),
- reduceBoilerplate("dominantBaseLine", TextBaseline.ROMAN),
- reduceBoilerplate("alignmentBaseLine", TextBaseline.USE_DOMINANT_BASELINE),
- reduceBoilerplate("baseLineShift", 0.0),
- reduceBoilerplate("kerning", Kerning.ON),
- reduceBoilerplate("trackingRight", 0.0),
- reduceBoilerplate("trackingLeft", 0.0),
- reduceBoilerplate("locale", "en"),
- reduceBoilerplate("breakOpportunity", BreakOpportunity.AUTO),
- reduceBoilerplate("digitCase", DigitCase.DEFAULT),
- reduceBoilerplate("digitWidth", DigitWidth.DEFAULT),
- reduceBoilerplate("ligatureLevel", LigatureLevel.COMMON),
- reduceBoilerplate("typographicCase", TypographicCase.DEFAULT)
- );
- }
-
- protected var styleMap:Dictionary = new Dictionary(true);
-
- public function getMappedStyle(element:*):*
- {
- if(element in styleMap)
- return styleMap[element];
-
- return null;
- }
-
- public function mapStyle(element:*, value:*):void
- {
- styleMap[element] = value;
- }
-
- public function unMapStyle(element:*):Boolean
- {
- if(element in styleMap)
- return delete styleMap[element];
-
- return false;
- }
-
- //Statically generate a map of the properties in this object
- generatePropertiesMap(new TextStyler());
- }
-}
-
diff --git a/src/org/tinytlf/utils/EventProxy.as b/src/org/tinytlf/utils/EventProxy.as
deleted file mode 100644
index 500d618..0000000
--- a/src/org/tinytlf/utils/EventProxy.as
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.events.EventPhase;
- import flash.events.IEventDispatcher;
-
- public class EventProxy extends EventDispatcher
- {
- private var target:IEventDispatcher;
- private var destination:IEventDispatcher;
-
- public function EventProxy(target:IEventDispatcher, destination:IEventDispatcher)
- {
- this.target = target;
- this.destination = destination;
- }
-
- public function proxyType(type:String):Boolean
- {
- if(!target)
- return false;
-
- target.addEventListener(type, proxy);
- return true;
- }
-
- public function clearType(type:String):Boolean
- {
- if(!target || !target.hasEventListener(type))
- return false;
-
- target.removeEventListener(type, proxy);
- return true;
- }
-
- private function proxy(event:Event):void
- {
- if(!destination || event.eventPhase != EventPhase.AT_TARGET)
- return;
-
- destination.dispatchEvent(event.clone());
- }
- }
-}
-
diff --git a/src/org/tinytlf/utils/FTEUtil.as b/src/org/tinytlf/utils/FTEUtil.as
deleted file mode 100644
index bb0a03f..0000000
--- a/src/org/tinytlf/utils/FTEUtil.as
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.geom.Point;
- import flash.text.engine.ContentElement;
- import flash.text.engine.GroupElement;
- import flash.text.engine.TextLine;
- import flash.ui.Keyboard;
-
- public class FTEUtil
- {
- public static function getContentElementAt(element:ContentElement, index:int):ContentElement
- {
- var idx:int = index;
- while(element is GroupElement)
- {
- idx = index - element.textBlockBeginIndex;
- idx = (idx <= 0) ? 1 : (idx < element.rawText.length) ? idx : element.rawText.length - 1
- if(idx < Math.max(element.rawText.length, GroupElement(element).elementCount))
- element = GroupElement(element).getElementAtCharIndex(idx);
- else
- break;
- }
-
- return element;
- }
-
- public static function getAtomIndexAtPoint(stageX:Number, stageY:Number, line:TextLine):int
- {
- var atomIndex:int = line.getAtomIndexAtPoint(stageX, stageY);
- if(atomIndex == -1)
- return -1;
-
- var atomCenter:int = line.getAtomCenter(atomIndex);
- var atomIncrement:int = (line.localToGlobal(new Point(atomCenter, 0)).x <= stageX) ? 1 : 0;
-
- return line.getAtomTextBlockBeginIndex(atomIndex) + atomIncrement;
- }
-
- public static function getAtomWordBoundary(line:TextLine, element:ContentElement, atomIndex:int, left:Boolean = true):int
- {
- var adjustedAtomIndex:int = atomIndex + line.textBlockBeginIndex;
-
- var atomCode:Number = element.rawText.charCodeAt(adjustedAtomIndex);
-
- if(atomCode === Keyboard.SPACE || (!left && atomIndex == 0))
- {
- left ? --atomIndex : ++atomIndex;
- adjustedAtomIndex = atomIndex + line.textBlockBeginIndex;
- atomCode = element.rawText.charCodeAt(adjustedAtomIndex);
- }
-
- if(left)
- {
- while(!isNaN(atomCode) && atomIndex > 0 && atomCode != Keyboard.SPACE && line.getAtomGraphic(adjustedAtomIndex) == null)
- {
- atomCode = element.rawText.charCodeAt(--adjustedAtomIndex);
- atomIndex--;
- }
- }
- else
- {
- while(!isNaN(atomCode) && atomIndex < element.rawText.length && atomCode != Keyboard.SPACE && line.getAtomGraphic(adjustedAtomIndex) == null)
- {
- atomCode = element.rawText.charCodeAt(++adjustedAtomIndex);
- atomIndex++;
- }
- }
-
- return atomIndex;
- }
-
- /**
- * Returns true if all of the flags specified by flagMask are set.
- */
- public static function isBitSet(flags:uint, flagMask:uint):Boolean
- {
- return flagMask == (flags & flagMask);
- }
-
- /**
- * Sets the flags specified by flagMask according to value.
- * Returns the new bitflag.
- * flagMask can be a combination of multiple flags.
- */
- public static function updateBits(flags:uint, flagMask:uint, update:Boolean = true):uint
- {
- if(update)
- {
- if((flags & flagMask) == flagMask)
- return flags; // Nothing to change
- // Don't use ^ since flagMask could be a combination of multiple flags
- flags |= flagMask;
- }
- else
- {
- if((flags & flagMask) == 0)
- return flags; // Nothing to change
- // Don't use ^ since flagMask could be a combination of multiple flags
- flags &= ~flagMask;
- }
- return flags;
- }
- }
-}
-
diff --git a/src/org/tinytlf/utils/KeyboardUtil.as b/src/org/tinytlf/utils/KeyboardUtil.as
deleted file mode 100644
index f3240a7..0000000
--- a/src/org/tinytlf/utils/KeyboardUtil.as
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.ui.Keyboard;
-
- public class KeyboardUtil
- {
- public static function isNavigable(keyCode:int):Boolean
- {
- return(
- isArrow(keyCode) ||
- isRemover(keyCode) ||
- isWhiteSpace(keyCode) ||
- isChar(keyCode)
- );
- }
-
- public static function isChar(keyCode:int):Boolean
- {
- return (!isModifier(keyCode) && !isRemover(keyCode));
- }
-
- public static function isModifier(keyCode:int):Boolean
- {
- return(
- keyCode == Keyboard.CAPS_LOCK ||
- keyCode == Keyboard.CONTROL ||
- keyCode == Keyboard.END ||
- keyCode == Keyboard.ESCAPE ||
- keyCode == Keyboard.HOME ||
- keyCode == Keyboard.INSERT ||
- keyCode == Keyboard.PAGE_DOWN ||
- keyCode == Keyboard.PAGE_UP ||
- keyCode == Keyboard.SHIFT ||
- isFunction(keyCode) ||
- isArrow(keyCode)
- );
- }
-
- public static function isWhiteSpace(keyCode:int):Boolean
- {
- return(
- keyCode == Keyboard.NUMPAD_ENTER ||
- keyCode == Keyboard.ENTER ||
- keyCode == Keyboard.TAB ||
- keyCode == Keyboard.SPACE
- );
- }
-
- public static function isArrow(keyCode:int):Boolean
- {
- return(
- keyCode == Keyboard.UP ||
- keyCode == Keyboard.DOWN ||
- keyCode == Keyboard.LEFT ||
- keyCode == Keyboard.RIGHT
- );
- }
-
- public static function isRemover(keyCode:int):Boolean
- {
- return(keyCode == Keyboard.BACKSPACE || keyCode == Keyboard.DELETE);
- }
-
- public static function isFunction(keyCode:int):Boolean
- {
- return(
- keyCode == Keyboard.F1 ||
- keyCode == Keyboard.F2 ||
- keyCode == Keyboard.F3 ||
- keyCode == Keyboard.F4 ||
- keyCode == Keyboard.F5 ||
- keyCode == Keyboard.F6 ||
- keyCode == Keyboard.F7 ||
- keyCode == Keyboard.F8 ||
- keyCode == Keyboard.F9 ||
- keyCode == Keyboard.F10 ||
- keyCode == Keyboard.F11 ||
- keyCode == Keyboard.F12 ||
- keyCode == Keyboard.F13 ||
- keyCode == Keyboard.F14 ||
- keyCode == Keyboard.F15
- );
- }
- }
-}
-
diff --git a/src/org/tinytlf/utils/Type.as b/src/org/tinytlf/utils/Type.as
deleted file mode 100644
index 3c26eab..0000000
--- a/src/org/tinytlf/utils/Type.as
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.net.registerClassAlias;
- import flash.utils.Dictionary;
- import flash.utils.describeType;
- import flash.utils.getDefinitionByName;
- import flash.utils.getQualifiedClassName;
-
- /**
- * The static Type class contains a collection of type-specific utilities,
- * including object reflection. Reflection is described in XML, which
- * format is documented in the flash.utils package. XML
- * descriptions are cached for performance.
- *
- * @see flash.utils#describeType
- */
- public class Type
- {
- // global cache improves reflection performance significantly
- private static var typeCache:Dictionary = new Dictionary();
-
- /**
- * Evaluates whether an object or class is derived from a specific
- * data type, class or interface. The isType() method is comparable to
- * ActionScript's is operator except that it also makes
- * class to class evaluations.
- *
- * @param value The object or class to evaluate.
- * @param type The data type to check against.
- *
- * @return True if the object or class is derived from
- * the data type.
- */
- public static function isType(value:Object, type:Class):Boolean
- {
- if(!(value is Class))
- {
- return value is type;
- }
-
- if(value == type)
- {
- return true;
- }
-
- var inheritance:XMLList = describeInheritance(value);
- return Boolean(inheritance.(@type == getQualifiedClassName(type)).length() > 0);
- }
-
- /**
- * Determines the property type without accessing the property directly.
- * Evaluation is based on the property definition and not by its value.
- *
- * @param value An object or class containing the property
- * definition to evaluate.
- * @param property The name of the property to be evaluated
- *
- * @return The class definition of the property type,
- * as described by the property definition, or
- * null if no definition was found.
- */
- public static function getPropertyType(value:Object, property:String):Class
- {
- if(!(value is Class) && !(property in value))
- {
- return null;
- }
-
- // retrieve the correct property from the property list
- var propList:XMLList = describeProperties(value).(@name == property);
-
- return(propList.length() > 0) ? getDefinitionByName(propList[0].@type) as Class : null;
- }
-
- /**
- * Ensures a class has a registered alias for object serialization and
- * remoting. If the class doesn't yet have an alias it will be
- * registered with it's full qualified class name, for example:
- * flight.utils.Type. If the class has already been
- * assigned an alias then its previous registration will be honored.
- *
- * @param value The object or class to register.
- *
- * @return True if the registration was successful,
- * otherwise the object already has an alias.
- *
- * @see flash.net#registerClassAlias
- */
- public static function registerType(value:Object):Boolean
- {
- if(!(value is Class))
- {
- value = getType(value);
- }
-
- // if not already registered
- var alias:String = describeType(value).@alias;
- if(alias.length)
- {
- return false;
- }
-
- registerClassAlias(getQualifiedClassName(value).split("::").join("."), value as Class);
- return true;
- }
-
- /**
- * Primary reflection method, producing an XML description of the object
- * or class specified. Results are cached internally for performance.
- *
- * @param value The object or class to introspect.
- * @param refreshCache Forces a new description to be generated,
- * useful only when a class alias has changed.
- *
- * @return An XML description, which format is
- * documented in the flash.utils
- * package.
- *
- * @see flash.utils#describeType
- */
- public static function describeType(value:Object, refreshCache:Boolean = false):XML
- {
- if(!(value is Class))
- {
- value = getType(value);
- }
-
- if(refreshCache || typeCache[value] == null)
- {
- typeCache[value] = flash.utils.describeType(value);
- }
-
- return typeCache[value];
- }
-
- /**
- * Targeted reflection describing an object's inheritance, including
- * extended classes and implemented interfaces.
- *
- * @param value The object or class to introspect.
- *
- * @return A list of XML inheritance descriptions.
- */
- public static function describeInheritance(value:Object):XMLList
- {
- return describeType(value).factory.*.(localName() == "extendsClass" || localName() == "implementsInterface");
- }
-
- /**
- * Targeted reflection describing an object's properties, including both
- * accessor's (getter/setters) and pure properties.
- *
- * @param value The object or class to introspect.
- * @param metadataType Optional filter to return only those
- * property descritions containing the
- * specified metadata.
- *
- * @return A list of XML property descriptions.
- */
- public static function describeProperties(value:Object, metadataType:String = null):XMLList
- {
- var properties:XMLList = describeType(value).factory.*.(localName() == "accessor" || localName() == "variable");
-
- return(metadataType == null) ? properties : properties.(child("metadata").(@name == metadataType).length() > 0);
- }
-
-
- /**
- * Targeted reflection describing an object's methods.
- *
- * @param value The object or class to introspect.
- * @param metadataType Optional filter to return only those
- * method descritions containing the
- * specified metadata.
- *
- * @return A list of XML method descriptions.
- */
- public static function describeMethods(value:Object, metadataType:String = null):XMLList
- {
- var methods:XMLList = describeType(value).factory.method;
-
- return(metadataType == null) ? methods : methods.(child("metadata").(@name == metadataType).length() > 0);
- }
-
- }
-}
-
diff --git a/src/org/tinytlf/utils/getClassName.as b/src/org/tinytlf/utils/getClassName.as
deleted file mode 100644
index 3bb3665..0000000
--- a/src/org/tinytlf/utils/getClassName.as
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.utils.getQualifiedClassName;
-
- /**
- * Returns the exact class name, without package or path.
- *
- * @param value The object for which the class name is desired.
- * Any ActionScript value may be passed including
- * all ActionScript types, object instances,
- * primitive types such as uint, and class objects.
- */
- public function getClassName(value:Object):String
- {
- return getQualifiedClassName(value).split("::").pop();
- }
-}
-
diff --git a/src/org/tinytlf/utils/getType.as b/src/org/tinytlf/utils/getType.as
deleted file mode 100644
index ffebcec..0000000
--- a/src/org/tinytlf/utils/getType.as
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2010 the original author or authors
- *
- * Permission is hereby granted to use, modify, and distribute this file
- * in accordance with the terms of the license agreement accompanying it.
- */
-package org.tinytlf.utils
-{
- import flash.utils.Proxy;
- import flash.utils.getDefinitionByName;
- import flash.utils.getQualifiedClassName;
-
- /**
- * Global method for getting an objects type.
- *
- * @param value The object being evaluated. If the object is a
- * class it will be returned as the type and not
- * the type Class.
- */
- public function getType(value:Object):Class
- {
- if(value is Class)
- {
- return value as Class;
- }
- else if(value is Proxy)
- {
- return getDefinitionByName(getQualifiedClassName(value)) as Class;
- }
- else
- {
- return value.constructor as Class;
- }
- }
-}
-
diff --git a/test/org/tinytlf/TextEngineTests.as b/test/org/tinytlf/TextEngineTests.as
old mode 100644
new mode 100755
index c8e0175..5673797
--- a/test/org/tinytlf/TextEngineTests.as
+++ b/test/org/tinytlf/TextEngineTests.as
@@ -1,48 +1,44 @@
package org.tinytlf
{
-
+ import flash.display.Sprite;
import flash.display.Stage;
-
import flash.events.Event;
-
import flash.events.TimerEvent;
import flash.utils.Timer;
-
+
+ import flexunit.framework.Assert;
+
import mockolate.*;
-
+
import mx.core.UIComponent;
-
- import org.flexunit.Assert;
+
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;
- import org.tinytlf.decor.ITextDecor;
- import org.tinytlf.decor.TextDecor;
- import org.tinytlf.interaction.ITextInteractor;
- import org.tinytlf.interaction.TextInteractorBase;
- import org.tinytlf.layout.ITextLayout;
- import org.tinytlf.layout.TextLayoutBase;
- import org.tinytlf.layout.factory.AbstractLayoutModelFactory;
- import org.tinytlf.layout.factory.ILayoutModelFactory;
- import org.tinytlf.styles.ITextStyler;
- import org.tinytlf.styles.TextStyler;
-
+ import org.tinytlf.components.TextFieldEngineConfiguration;
+ import org.tinytlf.conversion.*;
+ import org.tinytlf.decor.*;
+ import org.tinytlf.interaction.*;
+ import org.tinytlf.layout.*;
+ import org.tinytlf.styles.*;
+
public class TextEngineTests
{
private var engineStage:Stage;
private var engine:TextEngine;
private var delayTimer:Timer;
-
+
[Before(async, timeout=5000)]
public function setup():void
{
engineStage = UIImpersonator.addChild(new UIComponent()).stage;
engine = new TextEngine(engineStage);
- delayTimer = new Timer(100, 1);
+ engine.configuration = new TextFieldEngineConfiguration();
+ delayTimer = new Timer(1000, 1);
Async.proceedOnEvent(this,
- prepare(ITextDecor,ILayoutModelFactory, ITextLayout),
- Event.COMPLETE);
+ prepare(ITextDecor, ITextBlockFactory, ITextLayout),
+ Event.COMPLETE);
}
-
+
[After]
public function tearDown():void
{
@@ -50,228 +46,197 @@ package org.tinytlf
engine = null;
delayTimer = null;
}
-
+
[Test]
public function text_engine_constructed():void
{
Assert.assertTrue(engine != null);
}
-
+
//--------------------------------------------------------------------------
//
// default properties tests
//
//--------------------------------------------------------------------------
-
+
[Test]
public function engine_has_default_decor():void
{
var decor:ITextDecor = engine.decor;
-
+
Assert.assertTrue(decor is TextDecor);
}
-
+
[Test]
public function engine_has_default_block_factory():void
{
- var factory:ILayoutModelFactory = engine.blockFactory;
-
- Assert.assertTrue(factory is AbstractLayoutModelFactory);
+ var factory:ITextBlockFactory = engine.blockFactory;
+
+ Assert.assertTrue(factory is TextBlockFactoryBase);
}
-
+
[Test]
public function engine_has_default_interactor():void
{
var interactor:ITextInteractor = engine.interactor;
-
+
Assert.assertTrue(interactor is TextInteractorBase);
}
-
+
[Test]
public function engine_has_default_layout():void
{
var layout:ITextLayout = engine.layout;
-
+
Assert.assertTrue(layout is TextLayoutBase);
}
-
+
[Test]
public function engine_has_default_styler():void
{
var styler:ITextStyler = engine.styler;
-
+
Assert.assertTrue(styler is TextStyler)
}
-
+
//--------------------------------------------------------------------------
//
// public method tests
//
//--------------------------------------------------------------------------
-
- //----------------------------------------------------
- // prerender
- //----------------------------------------------------
-
- [Test]
- public function test_prerender_calls_decor_remove_all():void
- {
- var decor:ITextDecor = strict(ITextDecor);
- stub(decor).method("removeAll");
- stub(decor).setter("engine");
-
- engine.decor = decor;
- engine.prerender();
-
- verify(decor).method("removeAll").once();
- }
-
- [Test]
- public function prerender_calls_block_factory_create_blocks():void
- {
- var blockFactory:ILayoutModelFactory = nice(ILayoutModelFactory);
- stub(blockFactory).method("createBlocks");
-
- engine.blockFactory = blockFactory;
- engine.prerender();
-
- verify(blockFactory).method("createBlocks").once();
- }
-
+
//----------------------------------------------------
// invalidation triggers rendering
//----------------------------------------------------
- [Test(async)]
- public function invalidate_calls_clear_on_layout_after_current_frame():void
- {
- var layout:ITextLayout = nice(ITextLayout);
- stub(layout).method("clear");
-
- engine.layout = layout;
- engine.invalidate();
-
- Async.handleEvent(this, delayTimer, TimerEvent.TIMER_COMPLETE,
- handleInvalidateCallsClearOnLayoutAfterCurrentFrame, 500, layout);
-
- delayTimer.start();
- }
-
- private function handleInvalidateCallsClearOnLayoutAfterCurrentFrame(event:Event, layout:ITextLayout):void
- {
- verify(layout).method("clear").once();
- }
-
[Test(async)]
public function invalidate_calls_render_on_layout_after_current_frame():void
{
var layout:ITextLayout = nice(ITextLayout);
stub(layout).method("render");
-
+
engine.layout = layout;
engine.invalidate();
-
+
Async.handleEvent(this, delayTimer, TimerEvent.TIMER_COMPLETE,
- handleInvalidateCallsRenderOnLayoutAfterCurrentFrame, 500, layout);
-
+ handleInvalidateCallsRenderOnLayoutAfterCurrentFrame, 1500, layout);
+
delayTimer.start();
}
-
+
private function handleInvalidateCallsRenderOnLayoutAfterCurrentFrame(event:Event, layout:ITextLayout):void
{
verify(layout).method("render").once();
}
-
+
[Test(async)]
public function invalidate_calls_render_on_decor_after_current_frame():void
{
var decor:ITextDecor = nice(ITextDecor);
stub(decor).method("render");
-
+
engine.decor = decor;
engine.invalidate();
-
+
Async.handleEvent(this, delayTimer, TimerEvent.TIMER_COMPLETE,
- handleInvalidateCallsRenderOnDecorAfterCurrentFrame, 500, decor);
-
+ handleInvalidateCallsRenderOnDecorAfterCurrentFrame, 1500, decor);
+
delayTimer.start();
}
-
+
private function handleInvalidateCallsRenderOnDecorAfterCurrentFrame(event:Event, decor:ITextDecor):void
{
verify(decor).method("render").once();
}
-
+
[Test(async)]
public function invalidate_calls_resetShapes_on_layout_after_current_frame():void
{
var layout:ITextLayout = nice(ITextLayout);
stub(layout).method("resetShapes");
-
+
engine.layout = layout;
engine.invalidate();
-
+
Async.handleEvent(this, delayTimer, TimerEvent.TIMER_COMPLETE,
- handleInvalidateCallsResetShapesOnLayoutAfterCurrentFrame, 500, layout);
-
+ handleInvalidateCallsResetShapesOnLayoutAfterCurrentFrame, 1500, layout);
+
delayTimer.start();
}
-
+
private function handleInvalidateCallsResetShapesOnLayoutAfterCurrentFrame(event:Event, layout:ITextLayout):void
{
verify(layout).method("resetShapes").once();
}
-
+
//----------------------------------------------------
// rendering
//----------------------------------------------------
-
+
[Test]
public function render_lines_renders_layout():void
{
var layout:ITextLayout = nice(ITextLayout);
stub(layout).method("render");
-
+
engine.layout = layout;
- engine.renderLines();
-
+ engine.invalidateLines();
+ engine.render();
+
verify(layout).method("render").once();
}
-
- [Test]
- public function render_lines_clears_layout():void
- {
- var layout:ITextLayout = nice(ITextLayout);
- stub(layout).method("clear");
-
- engine.layout = layout;
- engine.renderLines();
-
- verify(layout).method("clear").once();
- }
-
+
[Test]
public function render_decorations_resets_shapes_in_layout():void
{
var layout:ITextLayout = nice(ITextLayout);
stub(layout).method("resetShapes");
-
+
engine.layout = layout;
- engine.renderDecorations();
-
+ engine.invalidateDecorations();
+ engine.render();
+
verify(layout).method("resetShapes").once();
}
-
+
[Test]
public function render_decorations_renders_decor():void
{
var decor:ITextDecor = nice(ITextDecor);
stub(decor).method("render");
-
+
engine.decor = decor;
- engine.renderDecorations();
-
+ engine.invalidateDecorations();
+ engine.render();
+
verify(decor).method("render").once();
}
+
+ private function renderDummyTextInEngine():void
+ {
+ var target:Sprite = new Sprite();
+ engine.layout.addContainer(new TextContainerBase(target, 100));
+ engine.blockFactory.data = "Let's test this shit.";
+ engine.invalidate();
+ engine.render();
+ }
+
+ [Test]
+ public function selecting_text_calls_decorate():void
+ {
+ renderDummyTextInEngine();
+
+ var decor:ITextDecor = strict(ITextDecor);
+ stub(decor).method("decorate");
+ stub(decor).method("undecorate");
+ stub(decor).setter("engine");
+ stub(decor).method("hasDecoration");
+
+ engine.decor = decor;
+
+ engine.select(0, 1);
+
+ verify(decor);
+ }
}
}
diff --git a/test/org/tinytlf/extensions/fcss/FCSSTextStylerTest.as b/test/org/tinytlf/extensions/fcss/FCSSTextStylerTest.as
new file mode 100755
index 0000000..da0c050
--- /dev/null
+++ b/test/org/tinytlf/extensions/fcss/FCSSTextStylerTest.as
@@ -0,0 +1,58 @@
+package org.tinytlf.extensions.fcss
+{
+ import flash.text.engine.ElementFormat;
+
+ import org.flexunit.Assert;
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.TextEngine;
+ import org.tinytlf.styles.FCSSTextStyler;
+
+ public class FCSSTextStylerTest
+ {
+ private var engine:ITextEngine;
+ private var styler:FCSSTextStyler;
+ private var css:XML = <_>;
+
+ [Before]
+ public function setUp():void
+ {
+ engine = new TextEngine();
+ styler = new FCSSTextStyler();
+ engine.styler = styler;
+ styler.style = css.toString();
+ }
+
+ [After]
+ public function tearDown():void
+ {
+ engine = null;
+ styler = null;
+ }
+
+ [Test]
+ public function dummy():void
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactoryTests.as b/test/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactoryTests.as
new file mode 100755
index 0000000..4f73700
--- /dev/null
+++ b/test/org/tinytlf/extensions/xml/layout/factory/XMLBlockFactoryTests.as
@@ -0,0 +1,44 @@
+package org.tinytlf.extensions.xml.layout.factory
+{
+ import flash.text.engine.TextBlock;
+
+ import org.flexunit.assertThat;
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.TextEngine;
+
+ public class XMLBlockFactoryTests
+ {
+ private var engine:ITextEngine;
+
+ [Before]
+ public function setUp():void
+ {
+ engine = new TextEngine();
+ }
+
+ [After]
+ public function tearDown():void
+ {
+ }
+
+ [BeforeClass]
+ public static function setUpBeforeClass():void
+ {
+ }
+
+ [AfterClass]
+ public static function tearDownAfterClass():void
+ {
+ }
+
+ [Test]
+ public function paragraph_node_creates_one_text_block():void
+ {
+ trace('break here');
+ engine.blockFactory.data = "
Text
";
+ engine.prerender();
+ var blocks:Vector. = engine.blockFactory.blocks;
+ assertThat(blocks.length == 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStylerTest.as b/test/org/tinytlf/extensions/xml/xhtml/fcss/styles/FCSSTextStylerTest.as
old mode 100644
new mode 100755
similarity index 96%
rename from test/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStylerTest.as
rename to test/org/tinytlf/extensions/xml/xhtml/fcss/styles/FCSSTextStylerTest.as
index 85966aa..359f781
--- a/test/org/tinytlf/extensions/fcss/xhtml/styles/FCSSTextStylerTest.as
+++ b/test/org/tinytlf/extensions/xml/xhtml/fcss/styles/FCSSTextStylerTest.as
@@ -1,10 +1,11 @@
-package org.tinytlf.extensions.fcss.xhtml.styles
+package org.tinytlf.extensions.xml.xhtml.fcss.styles
{
import flash.text.engine.ElementFormat;
import org.flexunit.Assert;
import org.tinytlf.ITextEngine;
import org.tinytlf.TextEngine;
+ import org.tinytlf.extensions.styles.fcss.FCSSTextStyler;
public class FCSSTextStylerTest
{
diff --git a/test/org/tinytlf/layout/TextContainerBaseTests.as b/test/org/tinytlf/layout/TextContainerBaseTests.as
old mode 100644
new mode 100755
index e1cd833..538e429
--- a/test/org/tinytlf/layout/TextContainerBaseTests.as
+++ b/test/org/tinytlf/layout/TextContainerBaseTests.as
@@ -8,108 +8,108 @@ package org.tinytlf.layout
{
import flash.display.Sprite;
import flash.events.Event;
-
+
import mockolate.prepare;
-
+
import mx.core.UIComponent;
-
+
import org.flexunit.Assert;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;
import org.hamcrest.assertThat;
import org.hamcrest.object.*;
import org.tinytlf.ITextEngine;
-
+
public class TextContainerBaseTests
{
private var container:TextContainerBase;
private var target:UIComponent;
-
+
[Before(async, ui)]
public function setup():void
{
target = new UIComponent();
container = new TextContainerBase(target);
-
+
UIImpersonator.addChild(target);
-
+
Async.proceedOnEvent(this,
- prepare(ITextEngine),
- Event.COMPLETE);
+ prepare(ITextEngine),
+ Event.COMPLETE);
}
-
+
[After(ui)]
public function tearDown():void
{
UIImpersonator.removeAllChildren();
container = null;
}
-
+
[Test]
public function was_constructed():void
{
assertThat(container, notNullValue());
}
-
+
[Test]
public function has_target_after_construction():void
{
assertThat(container.target, strictlyEqualTo(target));
}
-
+
[Test]
public function target_was_set():void
{
var newTarget:UIComponent = new UIComponent();
-
+
container.target = newTarget;
-
+
assertThat(container.target, strictlyEqualTo(newTarget));
}
-
+
[Test]
- public function child_added_to_target_after_setting_targer():void
+ public function child_added_to_target_after_setting_target():void
{
- assertThat(target.numChildren, equalTo(1));
+ assertThat(target.numChildren, equalTo(2));
}
-
- [Test]
- public function shapes_is_target_child_after_setting_target():void
- {
- assertThat(container.shapes, strictlyEqualTo(target.getChildAt(0)));
- }
-
- [Test]
- public function children_moved_to_new_shapes_when_set():void
- {
- var shapes:Sprite = new Sprite();
-
- shapes.addChild(new Sprite());
- container.shapes = shapes;
-
- assertThat(shapes.numChildren, equalTo(1));
- }
-
- [Test]
- public function children_removed_from_existing_shapes_when_shapes_set():void
- {
- var targetShapes:Sprite = container.shapes;
- targetShapes.addChild(new Sprite());
- container.shapes = new Sprite();
-
- assertThat(targetShapes.numChildren, equalTo(0));
- }
-
+
+// [Test]
+// public function shapes_is_target_child_after_setting_target():void
+// {
+// assertThat(container.shapes, strictlyEqualTo(target.getChildAt(0)));
+// }
+//
+// [Test]
+// public function children_moved_to_new_shapes_when_set():void
+// {
+// var shapes:Sprite = new Sprite();
+//
+// shapes.addChild(new Sprite());
+// container.shapes = shapes;
+//
+// assertThat(shapes.numChildren, equalTo(1));
+// }
+//
+// [Test]
+// public function children_removed_from_existing_shapes_when_shapes_set():void
+// {
+// var targetShapes:Sprite = container.shapes;
+// targetShapes.addChild(new Sprite());
+// container.shapes = new Sprite();
+//
+// assertThat(targetShapes.numChildren, equalTo(0));
+// }
+
[Test]
public function allowed_width_default_is_NaN():void
{
- Assert.assertTrue(isNaN(container.allowedWidth));
+ Assert.assertTrue(isNaN(container.explicitWidth));
}
-
+
[Test]
public function allowed_height_default_is_NaN():void
{
- Assert.assertTrue(isNaN(container.allowedHeight));
+ Assert.assertTrue(isNaN(container.explicitHeight));
}
}
}
\ No newline at end of file
diff --git a/test/org/tinytlf/layout/TextLayoutBaseTests.as b/test/org/tinytlf/layout/TextLayoutBaseTests.as
old mode 100644
new mode 100755
index 8391b37..4a12d83
--- a/test/org/tinytlf/layout/TextLayoutBaseTests.as
+++ b/test/org/tinytlf/layout/TextLayoutBaseTests.as
@@ -7,237 +7,222 @@
package org.tinytlf.layout
{
import flash.events.Event;
-
import flash.text.engine.*;
-
+
import mockolate.*;
-
+
import org.flexunit.async.Async;
import org.hamcrest.assertThat;
import org.hamcrest.object.*;
import org.tinytlf.ITextEngine;
-
+
public class TextLayoutBaseTests
{
private var layout:TextLayoutBase;
-
+
[Before(async)]
public function setup():void
{
layout = new TextLayoutBase();
Async.proceedOnEvent(this,
- prepare(ITextEngine, ITextContainer),
- Event.COMPLETE);
+ prepare(ITextEngine, ITextContainer),
+ Event.COMPLETE);
}
-
+
[After]
public function tearDown():void
{
layout = null;
}
-
+
//----------------------------------------------------
// initialization
//----------------------------------------------------
-
+
[Test]
public function text_layout_was_constructed():void
{
assertThat(layout, notNullValue());
}
-
+
[Test]
public function engine_was_set():void
{
var engine:ITextEngine = nice(ITextEngine);
-
+
layout.engine = engine;
-
+
assertThat(layout.engine, strictlyEqualTo(engine));
}
-
+
[Test]
public function containers_initialized_to_empty_vector():void
{
assertThat(layout.containers.length, equalTo(0));
}
-
+
//----------------------------------------------------
// basic public methods
//----------------------------------------------------
-
+
[Test]
public function container_was_added():void
{
var container:ITextContainer = stubBasicTextContainer();
-
+
layout.addContainer(container);
-
+
assertThat(layout.containers.length, equalTo(1));
}
-
+
[Test]
public function container_was_removed():void
{
var container:ITextContainer = stubBasicTextContainer();
-
+
layout.addContainer(container);
layout.removeContainer(container);
-
+
assertThat(layout.containers.length, equalTo(0));
}
-
+
[Test]
public function container_was_retrieved_for_line():void
{
var block:TextBlock = createTextBlockWithSmallContent();
var line:TextLine = block.createTextLine();
var retrievedContainer:ITextContainer;
-
+
var container:ITextContainer = stubBasicTextContainer();
stub(container).method("hasLine").args(line).returns(true);
-
+
layout.addContainer(container);
retrievedContainer = layout.getContainerForLine(line);
-
+
assertThat(retrievedContainer, strictlyEqualTo(container));
}
-
- [Test]
- public function clear_calls_clear_on_containers():void
- {
- var container:ITextContainer = stubBasicTextContainer();
-
- layout.addContainer(container);
- layout.clear();
-
- verify(container).method("clear").once();
- }
-
+
[Test]
public function reset_shapes_calls_reset_shapes_on_container():void
{
var container:ITextContainer = stubBasicTextContainer();
-
+
layout.addContainer(container);
layout.resetShapes();
-
+
verify(container).method("resetShapes").once();
}
-
+
//----------------------------------------------------
// render
//----------------------------------------------------
-
+
[Test]
public function render_laid_out_single_block_in_single_container():void
{
var blocks:Vector. = new Vector.;
var block:TextBlock = new TextBlock();
-
+
var container:ITextContainer = nice(ITextContainer);
stub(container).method("layout").args(block, instanceOf(TextLine));
-
+
layout.addContainer(container);
-
+
blocks.push(block);
-
- layout.render(blocks);
-
+
+ layout.render();
+
verify(container).method("layout").once();
}
-
+
[Test]
public function render_laid_out_single_block_in_multiple_container():void
{
var blocks:Vector. = new Vector.;
var block:TextBlock = createTextBlockWithSmallContent();
- var line:TextLine = block.createTextLine();
-
+ var line:TextLine = block.createTextLine(null, 0, 0, true);
+
var container:ITextContainer = nice(ITextContainer);
- stub(container).method("layout").args(block, null).returns(line);
-
+ stub(container).method("layout").args(instanceOf(TextBlock), instanceOf(TextLine)).returns(line);
+
var container2:ITextContainer = nice(ITextContainer);
- stub(container2).method("layout").args(block, line);
-
+ stub(container2).method("layout").args(instanceOf(TextBlock), instanceOf(TextLine));
+
blocks.push(block);
-
+
layout.addContainer(container);
layout.addContainer(container2);
-
- layout.render(blocks);
-
-
+
+ layout.render();
+
verify(container2).method("layout").once();
}
-
+
[Test]
public function render_laid_out_multiple_blocks_in_single_container():void
{
var blocks:Vector. = new Vector.;
var block:TextBlock = createTextBlockWithSmallContent();
var block2:TextBlock = createTextBlockWithSmallContent();
-
+
var container:ITextContainer = nice(ITextContainer);
stub(container).method("layout").args(instanceOf(TextBlock), null);
-
+
blocks.push(block);
blocks.push(block2);
-
+
layout.addContainer(container);
-
- layout.render(blocks);
-1
-
+
+ layout.render();
+
verify(container).method("layout").args(block, null).once();
verify(container).method("layout").args(block2, null).once();
}
-
+
[Test]
public function render_laid_out_multiple_blocks_in_multiple_containers():void
{
var blocks:Vector. = new Vector.;
var block:TextBlock = createTextBlockWithSmallContent();
var block2:TextBlock = createTextBlockWithSmallContent();
- var line:TextLine = block.createTextLine();
-
+ var line:TextLine = block.createTextLine(null, 0, 0, true);
+
var container:ITextContainer = nice(ITextContainer);
- stub(container).method("layout").args(instanceOf(TextBlock), null).returns(line);
-
+ stub(container).method("layout").args(instanceOf(TextBlock), instanceOf(TextLine)).returns(line);
+
var container2:ITextContainer = nice(ITextContainer);
- stub(container2).method("layout").args(instanceOf(TextBlock), null);
-
+ stub(container2).method("layout").args(instanceOf(TextBlock), instanceOf(TextLine));
+
blocks.push(block);
blocks.push(block2);
-
+
layout.addContainer(container);
layout.addContainer(container2);
-
- layout.render(blocks);
-
-
- verify(container).method("layout").args(block, null).once();
-
+
+ layout.render();
+
+ verify(container).method("layout").args(block, line).once();
+
verify(container2).method("layout").args(block, line).once();
verify(container2).method("layout").args(block2, null).once();
}
-
+
//----------------------------------------------------
//
// test helper methods
//
//----------------------------------------------------
-
+
private function stubBasicTextContainer():ITextContainer
{
var container:ITextContainer = nice(ITextContainer);
stub(container).method("clear");
stub(container).method("resetShapes");
stub(container).setter("engine");
-
+
return container;
}
-
+
private function createTextBlockWithSmallContent():TextBlock
{
var block:TextBlock = new TextBlock();
diff --git a/test/org/tinytlf/styles/StyleAwareActorTests.as b/test/org/tinytlf/styles/StyleAwareActorTests.as
new file mode 100755
index 0000000..152a1bf
--- /dev/null
+++ b/test/org/tinytlf/styles/StyleAwareActorTests.as
@@ -0,0 +1,6 @@
+package org.tinytlf.styles
+{
+ public class StyleAwareActorTests
+ {
+ }
+}
\ No newline at end of file
diff --git a/test/org/tinytlf/suites/TinyTLFTestSuite.as b/test/org/tinytlf/suites/TinyTLFTestSuite.as
old mode 100644
new mode 100755
index 123accd..6b24e22
--- a/test/org/tinytlf/suites/TinyTLFTestSuite.as
+++ b/test/org/tinytlf/suites/TinyTLFTestSuite.as
@@ -1,7 +1,7 @@
package org.tinytlf.suites
{
import org.tinytlf.*;
- import org.tinytlf.extensions.fcss.xhtml.styles.FCSSTextStylerTest;
+ import org.tinytlf.extensions.fcss.FCSSTextStylerTest;
import org.tinytlf.layout.TextContainerBaseTests;
import org.tinytlf.layout.TextLayoutBaseTests;
diff --git a/test/TinyTLFTest.mxml b/test/tinytlfTests.mxml
old mode 100644
new mode 100755
similarity index 100%
rename from test/TinyTLFTest.mxml
rename to test/tinytlfTests.mxml
diff --git a/tinytlf-components/src/org/tinytlf/components/ComponentBase.as b/tinytlf-components/src/org/tinytlf/components/ComponentBase.as
new file mode 100755
index 0000000..c8f60df
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/ComponentBase.as
@@ -0,0 +1,59 @@
+package org.tinytlf.components
+{
+ import flash.display.Sprite;
+
+ [Exclude(name="$width", kind="property")]
+ [Exclude(name="$height", kind="property")]
+
+ public class ComponentBase extends Sprite
+ {
+ public function ComponentBase()
+ {
+ super();
+ }
+
+ private var _width:Number = 0;
+
+ override public function get width():Number
+ {
+ return _width;
+ }
+
+ override public function set width(value:Number):void
+ {
+ _width = value;
+ }
+
+ public function get $width():Number
+ {
+ return super.width;
+ }
+
+ public function set $width(value:Number):void
+ {
+ super.width = value;
+ }
+
+ private var _height:Number = 0;
+
+ override public function get height():Number
+ {
+ return _height;
+ }
+
+ override public function set height(value:Number):void
+ {
+ _height = value;
+ }
+
+ public function get $height():Number
+ {
+ return super.height;
+ }
+
+ public function set $height(value:Number):void
+ {
+ super.height = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-components/src/org/tinytlf/components/TextColumnContainer.as b/tinytlf-components/src/org/tinytlf/components/TextColumnContainer.as
new file mode 100755
index 0000000..ffe9945
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/TextColumnContainer.as
@@ -0,0 +1,281 @@
+package org.tinytlf.components
+{
+ import flash.display.*;
+ import flash.events.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.*;
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.layout.orientation.*;
+
+ [Event(name="initScrollBar", type="flash.events.Event")]
+
+ /**
+ * TextColumnContainer is a Sprite which conveniently implements
+ * ITextContainer, and composites in a TextContainerBase for the
+ * implementation. Since it's a Sprite, it's easy to use in component style
+ * layouts without also having to manage and update an external
+ * ITextContainer.
+ */
+ public class TextColumnContainer extends Sprite implements IConstraintTextContainer
+ {
+ public function TextColumnContainer()
+ {
+ super();
+
+ focusRect = false;
+
+ container = new ConstraintTextContainer(this, 100);
+ }
+
+ private var _height:Number = 0;
+
+ override public function get height():Number
+ {
+ return _height;
+ }
+
+ override public function set height(value:Number):void
+ {
+ if(height === value)
+ return;
+
+ _height = Math.max(value, 1);
+ explicitHeight = value;
+ }
+
+ private var _width:Number = 0;
+
+ override public function get width():Number
+ {
+ return _width;
+ }
+
+ override public function set width(value:Number):void
+ {
+ if(width === value)
+ return;
+
+ _width = Math.max(value, 1);
+ explicitWidth = _width;
+ }
+
+ private var container:IConstraintTextContainer;
+
+ public function get engine():ITextEngine
+ {
+ return container.engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ container.engine = textEngine;
+ }
+
+ public function get target():Sprite
+ {
+ return this;
+ }
+
+ public function set target(textContainer:Sprite):void
+ {
+ //do nothing
+ }
+
+ public function get background():Sprite
+ {
+ return container.background;
+ }
+
+ public function set background(shapesContainer:Sprite):void
+ {
+ container.background = shapesContainer;
+ }
+
+ public function get foreground():Sprite
+ {
+ return container.foreground;
+ }
+
+ public function set foreground(shapesContainer:Sprite):void
+ {
+ container.foreground = shapesContainer;
+ }
+
+ public function get lines():Sprite
+ {
+ return container.lines;
+ }
+
+ public function set lines(linesContainer:Sprite):void
+ {
+ container.lines = linesContainer;
+ }
+
+ public function get explicitWidth():Number
+ {
+ return container.explicitWidth;
+ }
+
+ public function set explicitWidth(value:Number):void
+ {
+ container.explicitWidth = value;
+ }
+
+ public function get explicitHeight():Number
+ {
+ return container.explicitHeight;
+ }
+
+ public function set explicitHeight(value:Number):void
+ {
+ container.explicitHeight = value;
+ }
+
+ public function get measuredWidth():Number
+ {
+ return container.measuredWidth;
+ }
+
+ public function set measuredWidth(value:Number):void
+ {
+ container.measuredWidth = value;
+ }
+
+ public function get measuredHeight():Number
+ {
+ return container.measuredHeight;
+ }
+
+ public function set measuredHeight(value:Number):void
+ {
+ container.measuredHeight = value;
+ }
+
+ public function get totalWidth():Number
+ {
+ return container.totalWidth;
+ }
+
+ public function set totalWidth(value:Number):void
+ {
+ container.totalWidth = value;
+ }
+
+ public function get totalHeight():Number
+ {
+ return container.totalHeight;
+ }
+
+ public function set totalHeight(value:Number):void
+ {
+ container.totalHeight = value;
+ }
+
+ public function get scrollable():Boolean
+ {
+ return container.scrollable;
+ }
+
+ public function set scrollable(value:Boolean):void
+ {
+ container.scrollable = value;
+ }
+
+ public function resetShapes():void
+ {
+ container.resetShapes();
+ }
+
+ public function preLayout():void
+ {
+ container.preLayout();
+ }
+
+ public function postLayout():void
+ {
+ container.postLayout();
+
+ if(scrollable && measuredHeight > explicitHeight)
+ {
+ dispatchEvent(new Event('initScrollBar', true, true));
+ }
+
+ drawBackground();
+ }
+
+ public function layout(block:TextBlock, line:TextLine):TextLine
+ {
+ return container.layout(block, line);
+ }
+
+ public function hasLine(line:TextLine):Boolean
+ {
+ return container.hasLine(line);
+ }
+
+ public function get majorDirection():IMajorOrientation
+ {
+ return container.majorDirection;
+ }
+
+ public function set majorDirection(delegate:IMajorOrientation):void
+ {
+ container.majorDirection = delegate;
+ }
+
+ public function get minorDirection():IMinorOrientation
+ {
+ return container.minorDirection;
+ }
+
+ public function set minorDirection(delegate:IMinorOrientation):void
+ {
+ container.minorDirection = delegate;
+ }
+
+ public function set constraintFactory(factory:IConstraintFactory):void
+ {
+ container.constraintFactory = factory;
+ }
+
+ public function get constraintFactory():IConstraintFactory
+ {
+ return container.constraintFactory;
+ }
+
+ public function get constraints():Vector.
+ {
+ return container.constraints;
+ }
+
+ public function addConstraint(constraint:ITextConstraint):void
+ {
+ container.addConstraint(constraint);
+ }
+
+ public function getConstraint(element:*):ITextConstraint
+ {
+ return container.getConstraint(element);
+ }
+
+ public function removeConstraint(constraint:ITextConstraint):void
+ {
+ container.removeConstraint(constraint);
+ }
+
+ private function drawBackground():void
+ {
+ return;
+
+ var g:Graphics = graphics;
+ g.clear();
+ g.lineStyle();
+ g.beginFill(0x00, 0);
+ g.drawRect(0, 0,
+ explicitWidth || measuredWidth || width,
+ explicitHeight || measuredHeight || height);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-components/src/org/tinytlf/components/TextField.as b/tinytlf-components/src/org/tinytlf/components/TextField.as
new file mode 100755
index 0000000..31870aa
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/TextField.as
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.components
+{
+ import com.bit101.components.VScrollBar;
+
+ import flash.display.*;
+ import flash.events.*;
+ import flash.geom.*;
+ import flash.utils.*;
+
+ import org.tinytlf.*;
+ import org.tinytlf.layout.*;
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.styles.*;
+
+ public class TextField extends ComponentBase implements IStyleAware
+ {
+ public function TextField()
+ {
+ super();
+
+ engine.layout.addContainer(ITextContainer(addChild(container = new TextColumnContainer())));
+ container.addEventListener('initScrollBar', onInitScrollbar);
+
+ width = 100;
+ configuration = new TextFieldEngineConfiguration();
+ layoutConstraintFactory = new HTMLConstraintFactory();
+ }
+
+ protected var container:TextColumnContainer;
+
+ override public function set height(value:Number):void
+ {
+ if(height === value)
+ return;
+
+ super.height = value;
+ container.height = value;
+ scrollPosition = scrollPosition;
+ }
+
+ override public function set width(value:Number):void
+ {
+ if(width === value)
+ return;
+
+ super.width = value;
+
+ if(scrollBar)
+ {
+ engine.scrollPosition = 0;
+ scrollBar.value = 0;
+ value = value - scrollBar.width - 5;
+ }
+
+ container.width = value;
+ scrollPosition = scrollPosition;
+ }
+
+ protected var _configuration:ITextEngineConfiguration;
+ public function set configuration(engineConfiguration:ITextEngineConfiguration):void
+ {
+ if(engineConfiguration === _configuration)
+ return;
+
+ //Save this configuration so we can apply it if the engine changes
+ _configuration = engineConfiguration;
+
+ engine.configuration = _configuration;
+ engine.invalidate();
+ }
+
+ private var _editable:Boolean = false;
+ public function get editable():Boolean
+ {
+ return _editable;
+ }
+
+ public function set editable(value:Boolean):void
+ {
+ if(value === editable)
+ return;
+
+ _editable = value;
+ configuration = new TextFieldEngineConfiguration(selectable, editable);
+ }
+
+ private var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ if(!_engine)
+ {
+ _engine = new TextFieldEngine(this, stage);
+
+ if(!stage)
+ {
+ addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
+ }
+ }
+
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+
+ //If we've previously set an engine configuration, re-apply it now.
+ if(_configuration)
+ {
+ _engine.configuration = _configuration;
+ }
+ }
+
+ public function get scrollable():Boolean
+ {
+ return container.scrollable;
+ }
+
+ public function set scrollable(value:Boolean):void
+ {
+ container.scrollable = value;
+ }
+
+ protected var scrollP:Number = 0;
+
+ public function get scrollPosition():Number
+ {
+ return scrollP;
+ }
+
+ public function set scrollPosition(value:Number):void
+ {
+ if(value != scrollP)
+ {
+ if(!scrollBar)
+ return;
+ scrollP = Math.min(Math.max(value, 0), scrollBar.maximum);
+ engine.scrollPosition = scrollP;
+ scrollBar.value = scrollP;
+ }
+
+ if(width && height)
+ container.scrollRect = new Rectangle(-5, scrollP, width + 5, height);
+ }
+
+ private var _selectable:Boolean = true;
+ public function get selectable():Boolean
+ {
+ return _selectable;
+ }
+
+ public function set selectable(value:Boolean):void
+ {
+ if(value === _selectable)
+ return;
+
+ _selectable = value;
+ configuration = new TextFieldEngineConfiguration(selectable, editable);
+ }
+
+ private var _text:String = "";
+ public function get text():String
+ {
+ return _text;
+ }
+
+ public function set text(value:String):void
+ {
+ if(_text === value)
+ return;
+
+ _text = value;
+ engine.blockFactory.data = _text;
+ engine.invalidate();
+ }
+
+ public function get layoutConstraintFactory():IConstraintFactory
+ {
+ return container.constraintFactory;
+ }
+
+ public function set layoutConstraintFactory(factory:IConstraintFactory):void
+ {
+ container.constraintFactory = factory;
+ engine.invalidate();
+ }
+
+ public function get style():Object
+ {
+ return engine.styler.style;
+ }
+
+ public function set style(value:Object):void
+ {
+ engine.styler.style = value;
+ }
+
+ public function clearStyle(styleProp:String):Boolean
+ {
+ return engine.styler.clearStyle(styleProp);
+ }
+
+ public function getStyle(styleProp:String):*
+ {
+ return engine.styler.getStyle(styleProp);
+ }
+
+ public function setStyle(styleProp:String, newValue:*):void
+ {
+ engine.styler.setStyle(styleProp, newValue);
+ }
+
+ public function mergeWith(object:Object):void
+ {
+ engine.styler.mergeWith(object);
+ }
+
+ public function unmergeWith(object:Object):void
+ {
+ engine.styler.unmergeWith(object);
+ }
+
+ public function applyTo(object:Object):void
+ {
+ engine.styler.applyTo(object);
+ }
+
+ public function unapplyTo(object:Object):void
+ {
+ engine.styler.unapplyTo(object);
+ }
+
+ private function onAddedToStage(event:Event):void
+ {
+ if(event.eventPhase != EventPhase.AT_TARGET)
+ return;
+
+ removeEventListener(event.type, onAddedToStage);
+ engine.stage = stage;
+
+ initialize();
+ }
+
+ public function initialize():void
+ {
+ }
+
+ private function onInitScrollbar(event:Event):void
+ {
+ event.stopPropagation();
+ initScrollBar();
+ }
+
+ private var scrollBar:VScrollBar;
+
+ protected function initScrollBar():void
+ {
+ if(!scrollBar)
+ {
+ scrollBar = new VScrollBar(this, 0, 0, onScrollChange);
+ addChild(scrollBar);
+ scrollBar.lineSize = 5;
+ scrollBar.pageSize = 15;
+ scrollBar.height = height;
+ scrollBar.y = 0;
+ scrollBar.minimum = 0;
+
+ container.width = width - scrollBar.width - 10;
+ container.scrollRect = new Rectangle(-5, 0, width + 5, height);
+
+ setTimeout(engine.invalidate, 10);
+ }
+
+ var totalHeight:Number = container.totalHeight;
+
+ scrollBar.x = width - scrollBar.width;
+ scrollBar.maximum = totalHeight - height;
+ scrollBar.setThumbPercent(height / totalHeight);
+ }
+
+ protected function onScrollChange(event:Event = null):void
+ {
+ if(!scrollBar)
+ return;
+
+ scrollPosition = scrollBar.value;
+ }
+ }
+}
+import flash.display.Stage;
+
+import org.tinytlf.TextEngine;
+import org.tinytlf.components.TextField;
+
+internal class TextFieldEngine extends TextEngine
+{
+ public function TextFieldEngine(textField:TextField, stage:Stage)
+ {
+ tf = textField;
+
+ super(stage);
+ }
+ private var tf:TextField;
+
+ override public function get scrollPosition():Number
+ {
+ return tf.scrollPosition;
+ }
+
+ override public function set scrollPosition(value:Number):void
+ {
+ if(value === tf.scrollPosition)
+ {
+ super.scrollPosition = value;
+ return;
+ }
+
+ tf.scrollPosition = value;
+ }
+}
diff --git a/tinytlf-components/src/org/tinytlf/components/TextFieldEngineConfiguration.as b/tinytlf-components/src/org/tinytlf/components/TextFieldEngineConfiguration.as
new file mode 100755
index 0000000..b968139
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/TextFieldEngineConfiguration.as
@@ -0,0 +1,163 @@
+package org.tinytlf.components
+{
+ import org.tinytlf.*;
+ import org.tinytlf.behaviors.*;
+ import org.tinytlf.conversion.*;
+ import org.tinytlf.decor.*;
+ import org.tinytlf.decor.selection.*;
+ import org.tinytlf.gestures.*;
+ import org.tinytlf.interaction.*;
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.styles.*;
+
+ public class TextFieldEngineConfiguration implements ITextEngineConfiguration
+ {
+ public function TextFieldEngineConfiguration(selectable:Boolean = true, editable:Boolean = false)
+ {
+ this.selectable = selectable;
+ this.editable = editable;
+ }
+
+ public var selectable:Boolean = true;
+ public var editable:Boolean = false;
+
+ public function configure(engine:ITextEngine):void
+ {
+ engine.interactor = new CascadingTextInteractor();
+ engine.styler = new FCSSTextStyler();
+ engine.blockFactory = new XMLEditableBlockFactory();
+
+ mapDecorations(engine);
+ mapEventMirrors(engine);
+ mapGestures(engine);
+ applyGestures(engine);
+ mapElementAdapters(engine);
+ mapStyles(engine);
+ }
+
+ protected function mapDecorations(engine:ITextEngine):void
+ {
+ var decor:ITextDecor = engine.decor;
+
+ if (!decor.hasDecoration("backgroundColor"))
+ decor.mapDecoration("backgroundColor", BackgroundColorDecoration);
+
+ if (!decor.hasDecoration("bullet"))
+ decor.mapDecoration("bullet", BulletDecoration);
+
+ if (!decor.hasDecoration("horizontalRule"))
+ decor.mapDecoration("horizontalRule", HorizontalRuleDecoration);
+
+ if (!decor.hasDecoration("selection"))
+ decor.mapDecoration("selection", StandardSelectionDecoration);
+
+ if (!decor.hasDecoration("underline"))
+ decor.mapDecoration("underline", UnderlineDecoration);
+
+ if (!decor.hasDecoration("strikethrough"))
+ decor.mapDecoration("strikethrough", StrikeThroughDecoration);
+
+ if (!decor.hasDecoration("caret"))
+ decor.mapDecoration("caret", CaretDecoration);
+
+ if (!decor.hasDecoration("popup"))
+ decor.mapDecoration("popup", PopupDecoration);
+
+ if (!decor.hasDecoration("border"))
+ decor.mapDecoration("border", BorderDecoration);
+
+ if (!selectable)
+ decor.unMapDecoration("selection");
+
+ if (!editable)
+ decor.unMapDecoration("caret");
+ }
+
+ protected function mapEventMirrors(engine:ITextEngine):void
+ {
+ if (!engine.interactor.hasMirror("a"))
+ engine.interactor.mapMirror("a", AnchorMirror);
+ }
+
+ protected function mapGestures(engine:ITextEngine):void
+ {
+ var interactor:IGestureInteractor = IGestureInteractor(engine.interactor);
+
+ interactor.removeAllGestures();
+
+ var focus:FocusBehavior = new FocusBehavior();
+ var mouseClick:MouseClickGesture = new MouseClickGesture();
+ interactor.addGesture(mouseClick, focus);
+
+ if (selectable)
+ {
+ var iBeam:IBeamBehavior = new IBeamBehavior();
+ var scroll:ScrollBehavior = new ScrollBehavior();
+ var mouseCharSelect:CharacterSelectionBehavior = new CharacterSelectionBehavior();
+ var mouseWordSelect:WordSelectionBehavior = new WordSelectionBehavior();
+ var paragraphSelect:ParagraphSelectionBehavior = new ParagraphSelectionBehavior();
+
+ var mouseOver:MouseOverGesture = new MouseOverGesture();
+ var mouseOut:MouseOutGesture = new MouseOutGesture();
+ var mouseDoubleDown:MouseDoubleDownGesture = new MouseDoubleDownGesture();
+ var mouseTripleDown:MouseTripleDownGesture = new MouseTripleDownGesture();
+ var mouseWheel:MouseWheelGesture = new MouseWheelGesture();
+
+ interactor.addGesture(mouseOver, iBeam);
+ interactor.addGesture(mouseOut, iBeam);
+ interactor.addGesture(mouseClick, mouseCharSelect, scroll);
+ interactor.addGesture(mouseDoubleDown, mouseWordSelect, scroll);
+ interactor.addGesture(mouseTripleDown, paragraphSelect, scroll);
+ interactor.addGesture(mouseWheel, scroll);
+
+ if (editable)
+ {
+ mouseTripleDown.removeBehavior(paragraphSelect);
+ mouseTripleDown.addBehavior(new LineSelectionBehavior());
+ }
+ }
+ }
+
+ protected function applyGestures(engine:ITextEngine):void
+ {
+// var containers:Vector. = engine.layout.containers;
+// containers.forEach(function(container:ITextContainer, ...args):void{
+// engine.interactor.getMirror(container);
+// });
+ }
+
+ protected function mapElementAdapters(engine:ITextEngine):void
+ {
+ var factory:ITextBlockFactory = engine.blockFactory;
+
+ if (!factory.hasElementFactory('ul'))
+ factory.mapElementFactory('ul', HTMLListAdapter);
+
+ if (!factory.hasElementFactory('li'))
+ factory.mapElementFactory('li', HTMLListItemAdapter);
+
+ if (!factory.hasElementFactory('br'))
+ factory.mapElementFactory('br', HTMLLineBreakAdapter);
+
+ if (!factory.hasElementFactory('colbr'))
+ factory.mapElementFactory('colbr', HTMLColumnBreakAdapter);
+
+ if (!factory.hasElementFactory('img'))
+ factory.mapElementFactory('img', HTMLImageAdapter);
+
+ if (!factory.hasElementFactory('hr'))
+ factory.mapElementFactory('hr', HTMLHorizontalRuleAdapter);
+ }
+
+ protected function mapStyles(engine:ITextEngine):void
+ {
+ var styler:ITextStyler = engine.styler;
+
+ if(!styler.getStyle('selectionColor'))
+ styler.setStyle('selectionColor', 0x0068FC);
+ if(!styler.getStyle('selectionAlpha'))
+ styler.setStyle('selectionAlpha', 0.28);
+ }
+ }
+}
+
diff --git a/tinytlf-components/src/org/tinytlf/components/cs4/FlashTextField.as b/tinytlf-components/src/org/tinytlf/components/cs4/FlashTextField.as
new file mode 100755
index 0000000..2084b6a
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/cs4/FlashTextField.as
@@ -0,0 +1,41 @@
+package org.tinytlf.components.cs4
+{
+ import flash.geom.Transform;
+
+ import org.tinytlf.components.TextField;
+
+ public class FlashTextField extends TextField
+ {
+ public function FlashTextField()
+ {
+ super();
+
+// if($width > 0)
+// {
+// width = $width;
+// $width = 100;
+// }
+// if($height > 0)
+// {
+// height = $height;
+// $height = 100;
+// }
+
+ removeChildAt(0);
+ graphics.clear();
+ }
+
+ public function setSize(w:Number, h:Number):void
+ {
+ width = w;
+ height = h;
+ draw();
+ }
+
+ public function draw():void
+ {
+ engine.invalidate();
+ engine.render();
+ }
+ }
+}
diff --git a/tinytlf-components/src/org/tinytlf/components/cs4/TinytlfTextField.fla b/tinytlf-components/src/org/tinytlf/components/cs4/TinytlfTextField.fla
new file mode 100755
index 0000000..773b5b2
Binary files /dev/null and b/tinytlf-components/src/org/tinytlf/components/cs4/TinytlfTextField.fla differ
diff --git a/tinytlf-components/src/org/tinytlf/components/spark/RichTextField.as b/tinytlf-components/src/org/tinytlf/components/spark/RichTextField.as
new file mode 100755
index 0000000..9928e89
--- /dev/null
+++ b/tinytlf-components/src/org/tinytlf/components/spark/RichTextField.as
@@ -0,0 +1,212 @@
+package org.tinytlf.components.spark
+{
+ import flash.events.Event;
+ import flash.events.EventPhase;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.ITextEngineConfiguration;
+ import org.tinytlf.TextEngine;
+ import org.tinytlf.components.TextColumnContainer;
+ import org.tinytlf.components.TextFieldEngineConfiguration;
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.styles.IStyleAware;
+
+ import spark.components.supportClasses.GroupBase;
+
+ public class RichTextField extends GroupBase
+ /* implements IStyleAware */ //can't do this because Flex's clearStyle is retarded.
+ {
+ public function RichTextField()
+ {
+ super();
+
+ engine.layout.addContainer(ITextContainer(addChild(container = new TextColumnContainer())));
+
+ measuredWidth = 100;
+ configuration = new TextFieldEngineConfiguration(true, false);
+ }
+
+ override public function set verticalScrollPosition(value:Number):void
+ {
+ super.verticalScrollPosition = value;
+ engine.scrollPosition = value;
+ }
+
+ protected var _configuration:ITextEngineConfiguration;
+ public function set configuration(engineConfiguration:ITextEngineConfiguration):void
+ {
+ if(engineConfiguration === _configuration)
+ return;
+
+ //Save this configuration so we can apply it if the engine changes
+ _configuration = engineConfiguration;
+
+ engine.configuration = _configuration;
+ engine.invalidate();
+ invalidateDisplayList();
+ }
+
+ private var container:TextColumnContainer;
+
+ private var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ if(!_engine)
+ {
+ _engine = new RichTextFieldEngine(this, stage);
+
+ if(!stage)
+ {
+ addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
+ }
+ }
+
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+
+ //If we've previously set an engine configuration, re-apply it now.
+ if(_configuration)
+ {
+ _engine.configuration = _configuration;
+ }
+ }
+
+ private var _selectable:Boolean = true;
+ public function get selectable():Boolean
+ {
+ return _selectable;
+ }
+
+ public function set selectable(value:Boolean):void
+ {
+ if(value === _selectable)
+ return;
+
+ _selectable = value;
+ configuration = new TextFieldEngineConfiguration(selectable, false);
+ }
+
+ private var _text:String = "";
+ public function get text():String
+ {
+ return _text;
+ }
+
+ public function set text(value:String):void
+ {
+ if(_text === value)
+ return;
+
+ _text = value;
+ engine.blockFactory.data = _text;
+ engine.invalidate();
+ invalidateDisplayList();
+ }
+
+ private function onAddedToStage(event:Event):void
+ {
+ if(event.eventPhase != EventPhase.AT_TARGET)
+ return;
+
+ removeEventListener(event.type, onAddedToStage);
+ engine.stage = stage;
+ }
+
+ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+ {
+ super.updateDisplayList(unscaledWidth, unscaledHeight);
+
+ container.width = unscaledWidth;
+ container.height = unscaledHeight;
+
+ engine.invalidate();
+ engine.render();
+
+ setContentSize(unscaledWidth, container.totalHeight);
+ }
+
+ override public function get contentHeight():Number
+ {
+ return super.contentHeight;
+ }
+
+ public function get style():Object
+ {
+ return engine.styler.style;
+ }
+
+ public function set style(value:Object):void
+ {
+ engine.styler.style = value;
+ }
+
+ override public function getStyle(styleProp:String):*
+ {
+ return engine.styler.getStyle(styleProp);
+ }
+
+ override public function setStyle(styleProp:String, newValue:*):void
+ {
+ super.setStyle(styleProp, newValue);
+
+ engine.styler.setStyle(styleProp, newValue);
+ }
+
+ public function merge(merge:Object):void
+ {
+ engine.styler.mergeWith(merge);
+ }
+
+ public function applyTo(object:Object):void
+ {
+ engine.styler.applyTo(object);
+ }
+
+ override public function stylesInitialized():void
+ {
+ super.stylesInitialized();
+
+ merge(this);
+ }
+ }
+}
+
+import flash.display.Stage;
+
+import org.tinytlf.TextEngine;
+import org.tinytlf.components.spark.RichTextField;
+
+internal class RichTextFieldEngine extends TextEngine
+{
+ public function RichTextFieldEngine(textField:RichTextField, stage:Stage)
+ {
+ tf = textField;
+
+ super(stage);
+ }
+ private var tf:RichTextField;
+
+ override public function get scrollPosition():Number
+ {
+ return tf.verticalScrollPosition;
+ }
+
+ override public function set scrollPosition(value:Number):void
+ {
+ if(value === tf.verticalScrollPosition)
+ {
+ super.scrollPosition = value;
+ return;
+ }
+
+ tf.verticalScrollPosition = value;
+ }
+}
diff --git a/tinytlf-core/src/org/tinytlf/ITextEngine.as b/tinytlf-core/src/org/tinytlf/ITextEngine.as
new file mode 100755
index 0000000..d6c4e30
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/ITextEngine.as
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf
+{
+ import flash.display.Stage;
+ import flash.geom.Point;
+
+ import org.tinytlf.conversion.ITextBlockFactory;
+ import org.tinytlf.decor.ITextDecor;
+ import org.tinytlf.interaction.ITextInteractor;
+ import org.tinytlf.layout.ITextLayout;
+ import org.tinytlf.styles.ITextStyler;
+
+ /**
+ *
The ITextEngine is the facade which unifies the
+ * subsystems of tinytlf. Since tinytlf is relies on external definitions
+ * for implementation, the engine provides access to the decor,
+ * interactor, styler, and layout.
+ * ITextEngine supports external configuration through the
+ * ITextEngineConfiguration interface. To apply a
+ * configuration, write a class that implements
+ * ITextEngineConfiguration and pass an instance into the
+ * configuration setter of ITextEngine.
+ *
The ITextEngine is also responsible for invalidation,
+ * selection, and the TextBlocks that make up a tinytlf text
+ * field.
+ *
+ * @see org.tinytlf.decor.ITextDecor
+ * @see org.tinytlf.interaction.ITextInteractor
+ * @see org.tinytlf.layout.ITextLayout
+ * @see org.tinytlf.styles.ITextStyler
+ * @see org.tinytlf.ITextEngineConfiguration
+ */
+ public interface ITextEngine
+ {
+ /**
+ * The ITextBlockFactory for tinytlf. This object accepts arbitrary data
+ * and converts it into Flash Text Engine ContentElements and TextBlocks.
+ */
+ function get blockFactory():ITextBlockFactory;
+ function set blockFactory(value:ITextBlockFactory):void;
+
+ /**
+ * The index of the selection caret for the engine.
+ */
+ function get caretIndex():int;
+ function set caretIndex(index:int):void;
+
+ /**
+ * A setter which applies an ITextEngineConfiguration for
+ * this engine. ITextEngineConfiguration is meant to
+ * externally map all the properties for this engine's member maps
+ * (decoration renderers and properties on ITextDecor,
+ * event mirrors, gestures, and behaviors on ITextInteractor,
+ * IContentElementAdapters for elements parsed by
+ * ITextLayout's ITextBlockFactory member, and
+ * style definitions for the ITextStyler.
+ */
+ function set configuration(engineConfiguration:ITextEngineConfiguration):void;
+
+ /**
+ * The ITextDecor instance for the engine.
+ *
+ * @see org.tinytlf.decor.ITextDecor
+ */
+ function get decor():ITextDecor;
+ function set decor(textDecor:ITextDecor):void;
+
+ /**
+ * The ITextInteractor instance for the engine.
+ *
+ * @see org.tinytlf.interaction.ITextInteractor
+ */
+ function get interactor():ITextInteractor;
+ function set interactor(textInteractor:ITextInteractor):void;
+
+ /**
+ * The ITextLayout instance for the engine.
+ *
+ * @see org.tinytlf.layout.ITextLayout
+ */
+ function get layout():ITextLayout;
+ function set layout(textlayout:ITextLayout):void;
+
+ /**
+ * The scroll position of the engine. This value is in pixels. Defined
+ * centrally here, but usually only needed during layout.
+ */
+ function get scrollPosition():Number;
+ function set scrollPosition(value:Number):void;
+
+ /**
+ * A point whose x property represents the engine's
+ * selection startIndex and y property represents
+ * the engine's selection endIndex.
+ */
+ function get selection():Point;
+
+ /**
+ * ITextEngine invalidates by calling invalidate()
+ * on Flash's Stage singleton, then validates when the Event.RENDER
+ * event is dispatched.
+ */
+ function set stage(theStage:Stage):void;
+
+ /**
+ * The ITextStyler instance for the engine.
+ *
+ * @see org.tinytlf.styles.ITextStyler
+ */
+ function get styler():ITextStyler;
+ function set styler(textStyler:ITextStyler):void;
+
+ /**
+ * Draws a selection decoration around text.
+ */
+ function select(startIndex:Number = NaN, endIndex:Number = NaN):void;
+
+ /**
+ * Invalidates the lines, decorations, and optionally the data for
+ * re-draw or parsing on the next screen refresh.
+ *
+ * @see org.tinytlf.layout.ITextLayout
+ * @see org.tinytlf.decor.ITextDecoration
+ * @see org.tinytlf.layout.model.ILayoutModelFactory
+ */
+ function invalidate():void;
+
+ /**
+ * An optimization to invalidate only the lines for re-draw on the next
+ * screen refresh. This runs ITextLayout's
+ * render routine, which should be optimized to only render
+ * the lines which the FTE's TextBlock has marked as
+ * invalid.
+ *
+ * @see org.tinytlf.layout.ITextLayout
+ */
+ function invalidateLines():void;
+
+ /**
+ * An optimization to invalidate only the decorations for re-draw on the
+ * next screen refresh. This runs the ITextDecor's
+ * render routine for rendering
+ * ITextDecorations.
+ *
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function invalidateDecorations():void;
+
+ /**
+ * Renders the lines and decorations if they've been invalidated.
+ */
+ function render():void;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/ITextEngineConfiguration.as b/tinytlf-core/src/org/tinytlf/ITextEngineConfiguration.as
new file mode 100755
index 0000000..6c8c882
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/ITextEngineConfiguration.as
@@ -0,0 +1,16 @@
+package org.tinytlf
+{
+ /**
+ * An interface that defines a method for configuring the default
+ * mappings for ITextEngine's member maps. You can pass an instance
+ * of a class which implements this interface to the ITextEngine's
+ * configuration property, and ITextEngine calls
+ * configure.
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ public interface ITextEngineConfiguration
+ {
+ function configure(engine:ITextEngine):void;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/TextEngine.as b/tinytlf-core/src/org/tinytlf/TextEngine.as
new file mode 100755
index 0000000..7e779b5
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/TextEngine.as
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf
+{
+ import flash.display.Stage;
+ import flash.events.*;
+ import flash.geom.*;
+ import flash.text.engine.*;
+ import flash.utils.*;
+
+ import org.tinytlf.analytics.*;
+ import org.tinytlf.conversion.*;
+ import org.tinytlf.decor.*;
+ import org.tinytlf.interaction.*;
+ import org.tinytlf.layout.*;
+ import org.tinytlf.styles.*;
+
+ /**
+ * @see org.tinytlf.ITextEngine
+ */
+ public class TextEngine implements ITextEngine
+ {
+ public function TextEngine(stage:Stage = null)
+ {
+ this.stage = stage;
+ }
+
+ protected var _blockFactory:ITextBlockFactory;
+
+ public function get blockFactory():ITextBlockFactory
+ {
+ if(!_blockFactory)
+ _blockFactory = new TextBlockFactoryBase();
+
+ return _blockFactory;
+ }
+
+ public function set blockFactory(value:ITextBlockFactory):void
+ {
+ if(value === _blockFactory)
+ return;
+
+ _blockFactory = value;
+
+ _blockFactory.engine = this;
+ }
+
+ public function set configuration(engineConfiguration:ITextEngineConfiguration):void
+ {
+ engineConfiguration.configure(this);
+ }
+
+ protected var _decor:ITextDecor;
+
+ public function get decor():ITextDecor
+ {
+ if(!_decor)
+ decor = new TextDecor();
+
+ return _decor;
+ }
+
+ public function set decor(textDecor:ITextDecor):void
+ {
+ if(textDecor == _decor)
+ return;
+
+ _decor = textDecor;
+
+ _decor.engine = this;
+ }
+
+ protected var _interactor:ITextInteractor;
+
+ public function get interactor():ITextInteractor
+ {
+ if(!_interactor)
+ interactor = new TextInteractorBase();
+
+ return _interactor;
+ }
+
+ public function set interactor(textInteractor:ITextInteractor):void
+ {
+ if(textInteractor == _interactor)
+ return;
+
+ _interactor = textInteractor;
+
+ _interactor.engine = this;
+ }
+
+ protected var _layout:ITextLayout;
+
+ public function get layout():ITextLayout
+ {
+ if(!_layout)
+ layout = new TextLayoutBase();
+
+ return _layout;
+ }
+
+ public function set layout(textLayout:ITextLayout):void
+ {
+ if(textLayout == _layout)
+ return;
+
+ _layout = textLayout;
+
+ _layout.engine = this;
+ }
+
+ protected var _stage:Stage;
+
+ public function set stage(theStage:Stage):void
+ {
+ if(theStage === _stage)
+ return;
+
+ _stage = theStage;
+ invalidateStage();
+ }
+
+ protected var _styler:ITextStyler;
+
+ public function get styler():ITextStyler
+ {
+ if(!_styler)
+ styler = new TextStyler();
+
+ return _styler;
+ }
+
+ public function set styler(textStyler:ITextStyler):void
+ {
+ if(textStyler == _styler)
+ return;
+
+ _styler = textStyler;
+
+ _styler.engine = this;
+ }
+
+ private var _caretIndex:int = 0;
+
+ // A unique identifier for the caret index during
+ // decoration, since ints are passed by value.
+ private const caretWrapper:Object = {caretIndex:0};
+
+ public function get caretIndex():int
+ {
+ return _caretIndex;
+ }
+
+ public function set caretIndex(index:int):void
+ {
+ if(index === _caretIndex)
+ return;
+
+ _caretIndex = Math.max(Math.min(index, blockFactory.contentVirtualizer.size), 0);
+
+ //Don't draw the caretIndex if we don't have a caret decoration.
+ if(!decor.hasDecoration('caret'))
+ return;
+
+ caretWrapper.caretIndex = _caretIndex;
+
+ decor.decorate(caretWrapper,
+ {
+ caret:true,
+ selectionColor:styler.getStyle('caretColor'),
+ selectionAlpha:styler.getStyle('caretAlpha')
+ },
+ TextDecor.CARET_LAYER, null, true);
+
+ invalidateDecorations();
+ }
+
+ private var _scrollPosition:Number = 0;
+
+ public function get scrollPosition():Number
+ {
+ return _scrollPosition;
+ }
+
+ public function set scrollPosition(value:Number):void
+ {
+ if(value === _scrollPosition)
+ return;
+
+ if(rendering)
+ return;
+
+ _scrollPosition = Math.min(Math.max(value, 0), layout.textBlockVirtualizer.size);
+ invalidate();
+ }
+
+ private var _selection:Point = new Point(NaN, NaN);
+
+ public function get selection():Point
+ {
+ return _selection;
+ }
+
+ public function select(startIndex:Number = NaN, endIndex:Number = NaN):void
+ {
+ //super fast inline isNaN checks
+ if(startIndex != startIndex || endIndex != endIndex)
+ {
+ selection.x = NaN;
+ selection.y = NaN;
+ decor.undecorate(selection, "selection");
+ return;
+ }
+
+ var temp:Point = new Point(startIndex, endIndex);
+
+ // Normalize the inputs.
+ startIndex = Math.max(Math.min(temp.x, temp.y), 0);
+ endIndex = Math.max(Math.max(temp.x, temp.y), 0);
+
+ if(startIndex == selection.x && endIndex == selection.y)
+ return;
+
+ selection.x = startIndex;
+ selection.y = endIndex;
+
+ //Don't draw selection if we don't have a selection decoration.
+ if(!decor.hasDecoration('selection'))
+ return;
+
+ decor.decorate(selection,
+ {
+ selection:true,
+ selectionColor:styler.getStyle('selectionColor'),
+ selectionAlpha:styler.getStyle('selectionAlpha')
+ },
+ TextDecor.SELECTION_LAYER, null, true);
+
+ invalidateDecorations();
+ }
+
+ public function invalidate():void
+ {
+ invalidateLines();
+ invalidateDecorations();
+ }
+
+ protected var invalidateLinesFlag:Boolean = false;
+
+ public function invalidateLines():void
+ {
+ if(invalidateLinesFlag)
+ return;
+
+ invalidateLinesFlag = true;
+ invalidateStage();
+ }
+
+ protected var invalidateDecorationsFlag:Boolean = false;
+
+ public function invalidateDecorations():void
+ {
+ if(invalidateDecorationsFlag)
+ return;
+
+ invalidateDecorationsFlag = true;
+ invalidateStage();
+ }
+
+ protected function invalidateStage():void
+ {
+ if(!_stage)
+ return;
+
+ _stage.addEventListener(Event.ENTER_FRAME, onRender);
+ _stage.addEventListener(Event.RENDER, onRender);
+ _stage.invalidate();
+ }
+
+ protected function onRender(event:Event):void
+ {
+ if(!_stage)
+ return;
+
+ _stage.removeEventListener(Event.ENTER_FRAME, onRender);
+ _stage.removeEventListener(Event.RENDER, onRender);
+ render();
+ }
+
+ protected var rendering:Boolean = false;
+
+ public function render():void
+ {
+ if(_stage)
+ {
+ _stage.removeEventListener(Event.ENTER_FRAME, onRender);
+ _stage.removeEventListener(Event.RENDER, onRender);
+ }
+
+ rendering = true;
+
+ if(invalidateLinesFlag)
+ renderLines();
+ invalidateLinesFlag = false;
+
+ if(invalidateDecorationsFlag)
+ renderDecorations();
+ invalidateDecorationsFlag = false;
+
+ rendering = false;
+ }
+
+ protected var blocks:Vector.;
+
+ protected function renderLines():void
+ {
+ // If we have selection decorations and are re-rendering the lines,
+ // re-render the decorations so they don't get out of sync.
+ if(selection.x == selection.x && selection.y == selection.y)
+ invalidateDecorationsFlag = true;
+
+ blockFactory.preRender();
+ layout.render();
+ }
+
+ protected function renderDecorations():void
+ {
+ layout.resetShapes();
+ decor.render();
+ }
+ }
+}
diff --git a/tinytlf-core/src/org/tinytlf/analytics/IVirtualizer.as b/tinytlf-core/src/org/tinytlf/analytics/IVirtualizer.as
new file mode 100644
index 0000000..db3652a
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/analytics/IVirtualizer.as
@@ -0,0 +1,43 @@
+package org.tinytlf.analytics
+{
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.ITextEngine;
+
+ public interface IVirtualizer
+ {
+ /**
+ * Reference to the central ITextEngine facade for this
+ * decor.
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ function get size():int;
+ function get length():int;
+
+ function get items():Dictionary;
+
+ function clear():void;
+
+ function enqueue(item:*, size:int):*;
+ function enqueueAt(item:*, index:int, size:int):*;
+
+ function dequeue(item:*):*;
+ function dequeueAt(index:int):*;
+ function dequeueAtPosition(position:int):*;
+
+ function getItemStart(item:*):int;
+ function getItemEnd(item:*):int;
+ function getItemSize(item:*):int;
+
+ function getItemIndex(item:*):int;
+
+ function getIndexFromPosition(position:int):int;
+
+ function getItemFromPosition(position:int):*;
+ function getItemFromIndex(index:int):*;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/analytics/SparseArray.as b/tinytlf-core/src/org/tinytlf/analytics/SparseArray.as
new file mode 100755
index 0000000..6eb374f
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/analytics/SparseArray.as
@@ -0,0 +1,883 @@
+// This is a modified version of the
+// spark.layouts.supportClasses.LinearLayoutVector class from the Flex 4
+// framework. The dependencies on Flex 4 have been removed, and it has been
+// modified for general use. In tinytlf, I use it to enqueue TextBlocks
+// for efficient scrolling. Enjoy.
+package org.tinytlf.analytics
+{
+ /**
+ * A sparse array of sizes that represent items in a dimension.
+ *
+ * Provides efficient support for finding the cumulative distance to
+ * the start/end of an item along the axis, and similarly for finding the
+ * index of the item at a particular distance.
+ *
+ * Default size is used for items whose size hasn't been specified.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public final class SparseArray
+ {
+ // Assumption: vector elements (sizes) will typically be set in
+ // small ranges that reflect localized scrolling. Allocate vector
+ // elements in blocks of BLOCK_SIZE, which must be a power of 2.
+ // BLOCK_SHIFT is the power of 2 and BLOCK_MASK masks off as many
+ // low order bits. The blockTable contains all of the allocated
+ // blocks and has length/BLOCK_SIZE elements which are allocated lazily.
+ internal static const BLOCK_SIZE:uint = 128;
+ internal static const BLOCK_SHIFT:uint = 7;
+ internal static const BLOCK_MASK:uint = 0x7F;
+
+ private const blockTable:Vector. = new Vector.(0, false);
+
+ // Sorted Vector of intervals for the pending removes, in descending order,
+ // for example [7, 5, 3, 1] for the removes at 7, 6, 5, 3, 2, 1
+ private var pendingRemoves:Vector. = null;
+
+ // Sorted Vector of intervals for the pending inserts, in ascending order,
+ // for example [1, 3, 5, 7] for the inserts at 1, 2, 3, 5, 6, 7
+ private var pendingInserts:Vector. = null;
+
+ // What the length will be after any pending changes are flushed.
+ private var pendingLength:int = -1;
+
+ public function SparseArray()
+ {
+ super();
+ }
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Properties
+ //
+ //--------------------------------------------------------------------------
+
+
+ //----------------------------------
+ // length
+ //----------------------------------
+
+ private var _length:uint = 0;
+
+ /**
+ * The number of item size valued elements.
+ *
+ * @default 0
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get length():uint
+ {
+ return pendingLength == -1 ? _length : pendingLength;
+ }
+
+ /**
+ * @private
+ */
+ public function set length(value:uint):void
+ {
+ flushPendingChanges();
+ setLength(value);
+ }
+
+ /**
+ * @private
+ * Grows or truncates the vector to be the specified newLength.
+ * When truncating, releases empty blocks and sets to NaN any values
+ * in the last block beyond the newLength.
+ */
+ private function setLength(newLength:uint):void
+ {
+ if(newLength < _length)
+ {
+ // Clear any remaining non-NaN values in the last block
+ var blockIndex:uint = newLength >> BLOCK_SHIFT;
+ var endIndex:int = Math.min(blockIndex * BLOCK_SIZE + BLOCK_SIZE, _length) - 1;
+ clearInterval(newLength, endIndex);
+ }
+
+ _length = newLength;
+
+ // update the table
+ var partialBlock:uint = ((_length & BLOCK_MASK) == 0) ? 0 : 1;
+ blockTable.length = (_length >> BLOCK_SHIFT) + partialBlock;
+ }
+
+ //----------------------------------
+ // defaultSize
+ //----------------------------------
+
+ private var _defaultSize:Number = 0;
+
+ /**
+ * The size of items whose size was not specified with setItemSize.
+ *
+ * @default 0
+ * @see #cacheDimensions
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get defaultSize():Number
+ {
+ return _defaultSize;
+ }
+
+ /**
+ * @private
+ */
+ public function set defaultSize(value:Number):void
+ {
+ _defaultSize = value;
+ }
+
+ //----------------------------------
+ // axisOffset
+ //----------------------------------
+
+ private var _axisOffset:Number = 0;
+
+ /**
+ * The offset of the first item from the origin in the majorAxis
+ * direction. This is useful when implementing padding,
+ * in addition to gaps, for virtual layouts.
+ *
+ * @see #gap
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get axisOffset():Number
+ {
+ return _axisOffset;
+ }
+
+ /**
+ * @private
+ */
+ public function set axisOffset(value:Number):void
+ {
+ _axisOffset = value;
+ }
+
+ //----------------------------------
+ // gap
+ //----------------------------------
+
+ private var _gap:Number = 0;
+
+ /**
+ * The distance between items.
+ *
+ * @default 0
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get gap():Number
+ {
+ return _gap;
+ }
+
+ /**
+ * @private
+ */
+ public function set gap(value:Number):void
+ {
+ _gap = value;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Methods
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * Return the size of the item at index. If no size was ever
+ * specified then then the defaultSize is returned.
+ *
+ * @param index The item's index.
+ * @see defaultSize
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function getItemSize(index:uint):Number
+ {
+ flushPendingChanges();
+
+ var block:Block = blockTable[index >> BLOCK_SHIFT];
+ if(block)
+ {
+ var value:Number = block.sizes[index & BLOCK_MASK];
+ return (isNaN(value)) ? _defaultSize : value;
+ }
+ else
+ return _defaultSize;
+ }
+
+ /**
+ * Set the size of the item at index. If an index is
+ * set to NaN then subsequent calls to get
+ * will return the defaultSize.
+ *
+ * @param index The item's index.
+ * @param value The item's size.
+ * @see defaultSize
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function setItemSize(index:uint, value:Number):void
+ {
+ flushPendingChanges();
+
+ if(index >= length)
+ throw new Error("Invalid index and all that.");
+
+ var blockIndex:uint = index >> BLOCK_SHIFT;
+ var block:Block = blockTable[blockIndex];
+ if(!block)
+ block = blockTable[blockIndex] = new Block();
+
+ var sizesIndex:uint = index & BLOCK_MASK;
+ var sizes:Vector. = block.sizes;
+ var oldValue:Number = sizes[sizesIndex];
+ if(oldValue == value)
+ return;
+
+ if(isNaN(oldValue))
+ {
+ block.defaultCount -= 1;
+ block.sizesSum += value;
+ }
+ else if(isNaN(value))
+ {
+ block.defaultCount += 1;
+ block.sizesSum -= oldValue;
+ }
+ else
+ block.sizesSum += value - oldValue;
+
+ sizes[sizesIndex] = value;
+ }
+
+ /**
+ * Make room for a new item at index by shifting all of the sizes
+ * one position to the right, beginning with startIndex.
+ *
+ * The value at index will be NaN.
+ *
+ * This is similar to array.splice(index, 0, NaN).
+ *
+ * @param index The position of the new NaN size item.
+ */
+ public function insert(index:uint):void
+ {
+ // We don't support interleaved pending inserts and removes
+ if(pendingRemoves)
+ flushPendingChanges();
+
+ if(pendingInserts)
+ {
+ // Update the last interval or add a new one?
+ var lastIndex:int = pendingInserts.length - 1;
+ var intervalEnd:int = pendingInserts[lastIndex];
+
+ if(index == intervalEnd + 1)
+ {
+ // Extend the end of the interval
+ pendingInserts[lastIndex] = index;
+ }
+ else if(index > intervalEnd)
+ {
+ // New interval
+ pendingInserts.push(index);
+ pendingInserts.push(index);
+ }
+ else
+ {
+ // We can't support pending inserts that are not ascending
+ flushPendingChanges();
+ }
+ }
+
+ pendingLength = Math.max(length + 1, index + 1);
+
+ if(!pendingInserts)
+ {
+ pendingInserts = new Vector.();
+ pendingInserts.push(index);
+ pendingInserts.push(index);
+ }
+ }
+
+ /**
+ * Remove index by shifting all of the sizes one position to the left,
+ * begining with index+1.
+ *
+ * This is similar to array.splice(index, 1).
+ *
+ * @param index The position to be removed.
+ */
+ public function remove(index:uint):void
+ {
+ // We don't support interleaved pending inserts and removes
+ if(pendingInserts)
+ flushPendingChanges();
+
+ // length getter takes into account pending inserts/removes but doesn't flush
+ if(index >= length)
+ throw new Error("Invalid index and all that.");
+
+ if(pendingRemoves)
+ {
+ // Update the last interval or add a new one?
+ var lastIndex:int = pendingRemoves.length - 1;
+ var intervalStart:int = pendingRemoves[lastIndex];
+
+ if(index == intervalStart - 1)
+ {
+ // Extend the start of the interval
+ pendingRemoves[lastIndex] = index;
+ }
+ else if(index < intervalStart)
+ {
+ // New interval
+ pendingRemoves.push(index);
+ pendingRemoves.push(index);
+ }
+ else
+ {
+ // We can't support pending removes that are not descending
+ flushPendingChanges();
+ }
+ }
+
+ pendingLength = (pendingLength == -1) ? length - 1 : pendingLength - 1;
+
+ if(!pendingRemoves)
+ {
+ pendingRemoves = new Vector.();
+ pendingRemoves.push(index);
+ pendingRemoves.push(index);
+ }
+ }
+
+ /**
+ * @private
+ * Returns true when all sizes in the specified interval for the block are NaN
+ */
+ private function isIntervalClear(block:Block, index:int, count:int):Boolean
+ {
+ var sizesSrc:Vector. = block.sizes;
+ for(var i:int = 0; i < count; i++)
+ {
+ if(!isNaN(sizesSrc[index + i]))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @private
+ * Copies elements between blocks. Indices relative to the blocks.
+ * If srcBlock is null, then it fills the destination with NaNs.
+ * The case of srcBlock == dstBlock is also supported.
+ * The caller must ensure that count is within range.
+ */
+ private function inBlockCopy(dstBlock:Block, dstIndexStart:int, srcBlock:Block, srcIndexStart:int, count:int):void
+ {
+ var ascending:Boolean = dstIndexStart < srcIndexStart;
+
+ var srcIndex:int = ascending ? srcIndexStart : srcIndexStart + count - 1;
+ var dstIndex:int = ascending ? dstIndexStart : dstIndexStart + count - 1;
+ var increment:int = ascending ? +1 : -1;
+
+ var dstSizes:Vector. = dstBlock.sizes;
+ var srcSizes:Vector. = srcBlock ? srcBlock.sizes : null;
+ var dstValue:Number = NaN;
+ var srcValue:Number = NaN;
+ var sizesSumDelta:Number = 0; // How much the destination sizesSum will change
+ var defaultCountDelta:int = 0; // How much the destination defaultCount will change
+
+ while(count > 0)
+ {
+ if(srcSizes)
+ srcValue = srcSizes[srcIndex];
+ dstValue = dstSizes[dstIndex];
+
+ // Are the values different?
+ if(!(srcValue === dstValue)) // Triple '=' to handle NaN comparison
+ {
+ // Are we removing a default size or a chached size?
+ if(isNaN(dstValue))
+ defaultCountDelta--;
+ else
+ sizesSumDelta -= dstValue;
+
+ // Are we adding a default size or a cached size?
+ if(isNaN(srcValue))
+ defaultCountDelta++;
+ else
+ sizesSumDelta += srcValue;
+
+ dstSizes[dstIndex] = srcValue;
+ }
+
+ srcIndex += increment;
+ dstIndex += increment;
+ count--;
+ }
+
+ dstBlock.sizesSum += sizesSumDelta;
+ dstBlock.defaultCount += defaultCountDelta;
+ }
+
+ /**
+ * @private
+ * Copies 'count' elements from dstIndex to srcIndex.
+ * Safe for overlapping source and destination intervals.
+ * If any blocks are left full of NaNs, they will be deallcated.
+ */
+ private function copyInterval(dstIndex:int, srcIndex:int, count:int):void
+ {
+ var ascending:Boolean = dstIndex < srcIndex;
+ if(!ascending)
+ {
+ dstIndex += count - 1;
+ srcIndex += count - 1;
+ }
+
+ while(count > 0)
+ {
+ // Figure out destination block
+ var dstBlockIndex:uint = dstIndex >> BLOCK_SHIFT;
+ var dstSizesIndex:uint = dstIndex & BLOCK_MASK;
+ var dstBlock:Block = blockTable[dstBlockIndex];
+
+ // Figure out source block
+ var srcBlockIndex:uint = srcIndex >> BLOCK_SHIFT;
+ var srcSizesIndex:uint = srcIndex & BLOCK_MASK;
+ var srcBlock:Block = blockTable[srcBlockIndex];
+
+ // Figure out number of elements to copy
+ var copyCount:int;
+ if(ascending)
+ copyCount = Math.min(BLOCK_SIZE - dstSizesIndex, BLOCK_SIZE - srcSizesIndex);
+ else
+ copyCount = 1 + Math.min(dstSizesIndex, srcSizesIndex);
+ copyCount = Math.min(copyCount, count);
+
+ // Figure out the start index for each block
+ var dstStartIndex:int = ascending ? dstSizesIndex : dstSizesIndex - copyCount + 1;
+ var srcStartIndex:int = ascending ? srcSizesIndex : srcSizesIndex - copyCount + 1;
+
+ // Check whether a destination block needs to be allocated.
+ // Allocate only if there are non-default values to be copied from the source.
+ if(srcBlock && !dstBlock &&
+ isIntervalClear(srcBlock, srcStartIndex, copyCount))
+ {
+ dstBlock = new Block();
+ blockTable[dstBlockIndex] = dstBlock;
+ }
+
+ // Copy to non-null dstBlock, srcBlock can be null
+ if(dstBlock)
+ {
+ inBlockCopy(dstBlock, dstStartIndex, srcBlock, srcStartIndex, copyCount);
+
+ // If this is the last time we're visiting this block, and it contains
+ // only NaNs, then remove it
+ if(dstBlock.defaultCount == BLOCK_SIZE)
+ {
+ var blockEndReached:Boolean = ascending ? (dstStartIndex + copyCount == BLOCK_SIZE) :
+ (dstStartIndex == 0);
+ if(blockEndReached || count == copyCount)
+ blockTable[dstBlockIndex] = null;
+ }
+ }
+
+ dstIndex += ascending ? copyCount : -copyCount;
+ srcIndex += ascending ? copyCount : -copyCount;
+ count -= copyCount;
+ }
+ }
+
+ /**
+ * @private
+ * Sets all elements within the specified interval to NaN (both ends inclusive).
+ * Releases empty blocks.
+ */
+ private function clearInterval(start:int, end:int):void
+ {
+ while(start <= end)
+ {
+ // Figure our destination block
+ var blockIndex:uint = start >> BLOCK_SHIFT;
+ var sizesIndex:uint = start & BLOCK_MASK;
+ var block:Block = blockTable[blockIndex];
+
+ // Figure out number of elements to clear in this iteration
+ // Make sure we don't clear more items than requested
+ var clearCount:int = BLOCK_SIZE - sizesIndex;
+ clearCount = Math.min(clearCount, end - start + 1);
+
+ if(block)
+ {
+ if(clearCount == BLOCK_SIZE)
+ blockTable[blockIndex] = null;
+ else
+ {
+ // Copying from null source block is equivalent of clearing the destination block
+ inBlockCopy(block, sizesIndex, null /*srcBlock*/, 0, clearCount);
+
+ // If the blockDst contains only default sizes, then remove the block
+ if(block.defaultCount == BLOCK_SIZE)
+ blockTable[blockIndex] = null;
+ }
+ }
+
+ start += clearCount;
+ }
+ }
+
+ /**
+ * @private
+ * Removes the elements designated by the intervals and truncates
+ * the LinearLayoutVector to the new length.
+ * 'intervals' is a Vector of descending intervals [7, 5, 3, 1]
+ */
+ private function removeIntervals(intervals:Vector.):void
+ {
+ var intervalsCount:int = intervals.length;
+ if(intervalsCount == 0)
+ return;
+
+ // Adding final nextIntervalStart value (see below).
+ intervals.reverse(); // turn into ascending, for example [7, 5, 3, 1] --> [1, 3, 5, 7]
+ intervals.push(length);
+
+ // Move the elements
+ var dstStart:int = intervals[0];
+ var srcStart:int;
+ var count:int;
+ var i:int = 0;
+ do
+ {
+ var intervalEnd:int = intervals[i + 1];
+ var nextIntervalStart:int = intervals[i + 2]
+ i += 2;
+
+ // Start copy from after the end of current interval
+ srcStart = intervalEnd + 1;
+
+ // copy all elements up to the start of the next interval.
+ count = nextIntervalStart - srcStart;
+
+ copyInterval(dstStart, srcStart, count);
+ dstStart += count;
+ } while(i < intervalsCount)
+
+ // Truncate the excess elements.
+ setLength(dstStart);
+ }
+
+ /**
+ * @private
+ * Increases the length and inserts NaN values for the elements designated by the intervals.
+ * 'intervals' is a Vector of ascending intervals [1, 3, 5, 7]
+ */
+ private function insertIntervals(intervals:Vector., newLength:int):void
+ {
+ var intervalsCount:int = intervals.length;
+ if(intervalsCount == 0)
+ return;
+
+ // Allocate enough space for the insertions, all the elements
+ // allocated are NaN by default.
+ var oldLength:int = length;
+ setLength(newLength);
+
+ var srcEnd:int = oldLength - 1;
+ var dstEnd:int = newLength - 1;
+ var i:int = intervalsCount - 2;
+ while(i >= 0)
+ {
+ // Find current interval
+ var intervalStart:int = intervals[i];
+ var intervalEnd:int = intervals[i + 1];
+ i -= 2;
+
+ // Start after the current interval
+ var dstStart:int = intervalEnd + 1;
+ var copyCount:int = dstEnd - dstStart + 1;
+ var srcStart:int = srcEnd - copyCount + 1;
+
+ copyInterval(dstStart, srcStart, copyCount);
+ dstStart -= copyCount;
+ dstEnd = intervalStart - 1;
+
+ // Fill in with default NaN values after the copy
+ clearInterval(intervalStart, intervalEnd);
+ }
+ }
+
+ /**
+ * @private
+ * Processes any pending removes or pending inserts.
+ */
+ private function flushPendingChanges():void
+ {
+ var intervals:Vector.;
+ if(pendingRemoves)
+ {
+ intervals = pendingRemoves;
+ pendingRemoves = null;
+ pendingLength = -1;
+ removeIntervals(intervals);
+ }
+ else if(pendingInserts)
+ {
+ intervals = pendingInserts;
+ var newLength:int = pendingLength;
+ pendingInserts = null;
+ pendingLength = -1;
+ insertIntervals(intervals, newLength);
+ }
+ }
+
+ /**
+ * The cumulative distance to the start of the item at index, including
+ * the gaps between items and the axisOffset.
+ *
+ * The value of start(0) is axisOffset.
+ *
+ * Equivalent to:
+ *
+ *
+ * The actual implementation is relatively efficient.
+ *
+ * @param index The item's index.
+ * @see #end
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function start(index:uint):Number
+ {
+ flushPendingChanges();
+
+ if((_length == 0) || (index == 0))
+ return axisOffset;
+
+ if(index >= _length)
+ throw new Error("Invalid index and all that.");
+
+ var distance:Number = axisOffset;
+ var blockIndex:uint = index >> BLOCK_SHIFT;
+ for(var i:int = 0; i < blockIndex; i++)
+ {
+ var block:Block = blockTable[i];
+ if(block)
+ distance += block.sizesSum + (block.defaultCount * _defaultSize);
+ else
+ distance += BLOCK_SIZE * _defaultSize;
+ }
+ var lastBlock:Block = blockTable[blockIndex];
+ var lastBlockOffset:uint = index & ~BLOCK_MASK;
+ var lastBlockLength:uint = index - lastBlockOffset;
+ if(lastBlock)
+ {
+ var sizes:Vector. = lastBlock.sizes;
+ for(i = 0; i < lastBlockLength; i++)
+ {
+ var size:Number = sizes[i];
+ distance += (isNaN(size)) ? _defaultSize : size;
+ }
+ }
+ else
+ distance += _defaultSize * lastBlockLength;
+ distance += index * gap;
+ return distance;
+ }
+
+ /**
+ * The cumulative distance to the end of the item at index, including
+ * the gaps between items.
+ *
+ * If index <(length-1) then the value of this
+ * function is defined as:
+ * start(index) + get(index).
+ *
+ * @param index The item's index.
+ * @see #start
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function end(index:uint):Number
+ {
+ flushPendingChanges();
+ return start(index) + getItemSize(index);
+ }
+
+ /**
+ * Returns the index of the item that overlaps the specified distance.
+ *
+ * The item at index i overlaps a distance value
+ * if start(i) <= distance < end(i).
+ *
+ * If no such item exists, -1 is returned.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function indexOf(distance:Number):int
+ {
+ flushPendingChanges();
+ var index:int = indexOfInternal(distance);
+ return (index >= _length) ? -1 : index;
+ }
+
+ private function indexOfInternal(distance:Number):int
+ {
+ if((_length == 0) || (distance < 0))
+ return -1;
+
+ // The area of the first item includes the axisOffset
+ var curDistance:Number = axisOffset;
+ if(distance < curDistance)
+ return 0;
+
+ var index:int = -1;
+ var block:Block = null;
+ var blockGap:Number = _gap * BLOCK_SIZE;
+
+ // Find the block that contains distance and the index of its
+ // first element
+ for(var blockIndex:uint = 0; blockIndex < blockTable.length; blockIndex++)
+ {
+ block = blockTable[blockIndex];
+ var blockDistance:Number = blockGap;
+ if(block)
+ blockDistance += block.sizesSum + (block.defaultCount * _defaultSize);
+ else
+ blockDistance += BLOCK_SIZE * _defaultSize;
+ if((distance == curDistance) ||
+ ((distance >= curDistance) && (distance < (curDistance + blockDistance))))
+ {
+ index = blockIndex << BLOCK_SHIFT;
+ break;
+ }
+ curDistance += blockDistance;
+ }
+
+ if((index == -1) || (distance == curDistance))
+ return index;
+
+ // At this point index corresponds to the first item in this block
+ if(block)
+ {
+ // Find the item that contains distance and return its index
+ var sizes:Vector. = block.sizes;
+ for(var i:int = 0; i < BLOCK_SIZE - 1; i++)
+ {
+ var size:Number = sizes[i];
+ curDistance += _gap + (isNaN(size) ? _defaultSize : size);
+ if(curDistance > distance)
+ return index + i;
+ }
+ // TBD special-case for the very last index
+ return index + BLOCK_SIZE - 1;
+ }
+ else
+ {
+ return index + Math.floor(Number(distance - curDistance) / Number(_defaultSize + _gap));
+ }
+ }
+
+ /**
+ * Clear all cached state, reset length to zero.
+ */
+ public function clear():void
+ {
+ // Discard any pending changes, before setting the length
+ // otherwise the length setter will commit the changes.
+ pendingRemoves = null;
+ pendingInserts = null;
+ pendingLength = -1;
+
+ length = 0; // clears the BlockTable as well
+ }
+
+ public function toString():String
+ {
+ return "LinearLayoutVector{" +
+ "length=" + _length +
+ " [blocks=" + blockTable.length + "]" +
+ " gap=" + _gap +
+ " defaultSize=" + _defaultSize +
+ " pendingRemoves=" + (pendingRemoves ? pendingRemoves.length : 0) +
+ " pendingInserts=" + (pendingInserts ? pendingInserts.length : 0) +
+ "}";
+ }
+ }
+}
+
+/**
+ * @private
+ * A SparseArray block of layout element heights or widths.
+ *
+ * Total "distance" for a Block is: sizesSum + (defaultCount * distanceVector.default).
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+internal final class Block
+{
+ internal const sizes:Vector. = new Vector.(BLOCK_SIZE, true);
+ internal var sizesSum:Number = 0;
+ internal var defaultCount:uint = BLOCK_SIZE;
+
+ public function Block()
+ {
+ super();
+ for(var i:int = 0; i < BLOCK_SIZE; i += 1)
+ sizes[i] = NaN;
+ }
+
+ internal static const BLOCK_SIZE:uint = 128;
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/analytics/Virtualizer.as b/tinytlf-core/src/org/tinytlf/analytics/Virtualizer.as
new file mode 100644
index 0000000..8fa653a
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/analytics/Virtualizer.as
@@ -0,0 +1,132 @@
+package org.tinytlf.analytics
+{
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.ITextEngine;
+
+ public class Virtualizer implements IVirtualizer
+ {
+ private var vector:SparseArray = new SparseArray();
+ private var indexCache:Array = [];
+ private var itemCache:Dictionary = new Dictionary(false);
+
+ protected var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if (textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ public function get size():int
+ {
+ return vector.end(vector.length - 1);
+ }
+
+ public function get length():int
+ {
+ return indexCache.length;
+ }
+
+ public function get items():Dictionary
+ {
+ return itemCache;
+ }
+
+ public function clear():void
+ {
+ vector = new SparseArray();
+ indexCache = [];
+ itemCache = new Dictionary(false);
+ }
+
+ public function enqueue(item:*, size:int):*
+ {
+ return enqueueAt(item, length, size);
+ }
+
+ public function enqueueAt(item:*, index:int, size:int):*
+ {
+ if(item in itemCache)
+ return item;
+
+ if(size <= 0)
+ size = 1;
+
+ indexCache.splice(index, 0, item);
+ itemCache[item] = true;
+ vector.insert(index);
+ vector.setItemSize(index, size);
+ return item;
+ }
+
+ public function dequeue(item:*):*
+ {
+ dequeueAt(getItemIndex(item));
+ }
+
+ public function dequeueAt(index:int):*
+ {
+ var item:* = getItemFromIndex(index);
+
+ if(!item || !(item in itemCache))
+ return item;
+
+ indexCache.splice(index, 1);
+ vector.remove(index);
+ delete itemCache[item];
+
+ return item;
+ }
+
+ public function dequeueAtPosition(position:int):*
+ {
+ return dequeueAt(getItemIndex(getItemFromPosition(position)));
+ }
+
+ public function getItemIndex(item:*):int
+ {
+ return indexCache.indexOf(item);
+ }
+
+ public function getItemStart(item:*):int
+ {
+ return vector.start(getItemIndex(item));
+ }
+
+ public function getItemEnd(item:*):int
+ {
+ return vector.end(getItemIndex(item));
+ }
+
+ public function getItemSize(item:*):int
+ {
+ return vector.getItemSize(getItemIndex(item));
+ }
+
+ public function getIndexFromPosition(position:int):int
+ {
+ return vector.indexOf(position);
+ }
+
+ public function getItemFromPosition(position:int):*
+ {
+ return getItemFromIndex(getIndexFromPosition(position));
+ }
+
+ public function getItemFromIndex(index:int):*
+ {
+ if(index >= length)
+ return null;
+
+ return indexCache[index];
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/conversion/ContentElementFactory.as b/tinytlf-core/src/org/tinytlf/conversion/ContentElementFactory.as
new file mode 100755
index 0000000..eff777e
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/conversion/ContentElementFactory.as
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.conversion
+{
+ import flash.display.*;
+ import flash.events.EventDispatcher;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class ContentElementFactory implements IContentElementFactory
+ {
+ public function execute(data:Object, ... context:Array):ContentElement
+ {
+ var element:ContentElement;
+
+ //If the data is an empty string, insert a placeholder GraphicElement.
+ if(data && data.toString() === "")
+ {
+ element = new GraphicElement(new Shape(), 0, 0, getElementFormat(context), getEventMirror(context));
+ }
+ else if(data is String)
+ {
+ element = new TextElement(String(data), getElementFormat(context), getEventMirror(context));
+ element.userData = context;
+ }
+ else if(data is Vector.)
+ {
+ element = new GroupElement(Vector.(data), getElementFormat(context));
+ element.userData = context;
+ }
+
+ if(!element)
+ return null;
+
+ if(!(element is GroupElement))
+ {
+ //Do any decorations for this element
+ var dec:Object = engine.styler.describeElement(context.length ? context : null);
+ if(dec != null)
+ {
+ engine.decor.decorate(element, dec, dec.layer, null, dec.foreground);
+ }
+ }
+
+ return element;
+ }
+
+ private var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine === _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ protected function getElementFormat(context:Object):ElementFormat
+ {
+ // You can't render a textLine with a null ElementFormat, so return an empty one here.
+ if(!_engine)
+ return new ElementFormat();
+
+ return engine.styler.getElementFormat(context);
+ }
+
+ protected function getEventMirror(context:Object):EventDispatcher
+ {
+ if(!_engine)
+ return null;
+
+ return engine.interactor.getMirror(context);
+ }
+
+ protected function get ef():ElementFormat
+ {
+ return new ElementFormat();
+ }
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/conversion/IContentElementFactory.as b/tinytlf-core/src/org/tinytlf/conversion/IContentElementFactory.as
new file mode 100755
index 0000000..8108e1f
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/conversion/IContentElementFactory.as
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.conversion
+{
+ import flash.text.engine.ContentElement;
+
+ import org.tinytlf.ITextEngine;
+
+ /**
+ * IContentElementFactory is an interface for parsing arbitrary data into
+ * Flash Text Engine ContentElements. Classes of this type are mapped to the
+ * ILayoutFactoryMap for element types, then the ILayoutFactoryMap
+ * implementation calls execute for each matching data type.
+ */
+ public interface IContentElementFactory
+ {
+ /**
+ *
+ * Reference to the central ITextEngine facade for this
+ * contentElementFactory.
+ *
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ * Returns a Flash Text Engine ContentElement for the data object.
+ * Accepts arbitrary contextual parameters which implementations can
+ * harness to create specialized ContentElements.
+ *
+ * @see org.tinytlf.layout.model.factories.LayoutFactoryMap#createBlocks()
+ */
+ function execute(data:Object, ...context:Array):ContentElement;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/conversion/ITextBlockFactory.as b/tinytlf-core/src/org/tinytlf/conversion/ITextBlockFactory.as
new file mode 100755
index 0000000..af7928d
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/conversion/ITextBlockFactory.as
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.conversion
+{
+ import flash.text.engine.TextBlock;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.analytics.IVirtualizer;
+
+ /**
+ * ITextBlockFactory parses arbitrary data and returns a Vector of
+ * TextBlocks to tinytlf. Since generating TextBlocks also requires
+ * generating FTE ContentElements, ITextBlockFactory exposes a map and a
+ * command pattern which can be harnessed while parsing hierarchical data.
+ *
+ *
+ * ITextBlockFactory exposes a map which lets you define
+ * IContentElementFactories for an "element." The "element"
+ * type is unimportant, it just acts as a key for retrieving the factory.
+ *
+ *
+ * No matter what your data source, it's more than likely a tree of some
+ * sort, since rich text is most easily represented as some sort of tree.
+ * Tinytlf provides the ITextBlockFactory so you can generically traverse
+ * the tree, creating a corresponding tree of ContentElements. At each step
+ * in the recursion, if you call into the ITextBlockFactory,
+ * requesting the IContentElementFactory for the corresponding
+ * node, you have an algorithm that accepts external modification, but can
+ * convert from your specific data structure to FTE
+ * ContentElements.
+ *
+ */
+ public interface ITextBlockFactory
+ {
+ /**
+ * Reference to the central ITextEngine facade for this
+ * factory.
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ function get data():Object;
+ function set data(value:Object):void;
+
+ function get textBlockGenerator():ITextBlockGenerator;
+ function set textBlockGenerator(value:ITextBlockGenerator):void;
+
+ function get contentVirtualizer():IVirtualizer;
+
+ /**
+ * The number of potential discreet TextBlock instances that this
+ * ITextBlockFactory can create. This number is incremented
+ * and decremented when you make calls to addBlockData/removeBlockData.
+ *
+ * This does not represent the number of TextBlocks currently in
+ * existance, only the number of potential blocks.
+ */
+ function get numBlocks():int;
+
+ /**
+ * Called during the ITextEngine render phase, just before the
+ * ITextLayoutBase#render() method is called.
+ *
+ * This provides an opportunity to invalidate the visible TextBlocks,
+ * gather up orphan lines, and anything else that the render algorithm
+ * may depend on being done.
+ */
+ function preRender():void;
+
+ /**
+ * Returns a reference to the next visible TextBlock. This method should
+ * generate the TextBlock on the fly, as keeping references to every
+ * TextBlock in the TextField is potentially expensive.
+ */
+ function getTextBlock(index:int):TextBlock;
+
+ /**
+ * Checks to see if an IContentElementFactory class has
+ * mapped for the given element.
+ *
+ * @Returns true if a factory has been mapped, false otherwise.
+ *
+ * @see org.tinytlf.layout.model.factories.IContentElementFactory
+ */
+ function hasElementFactory(element:*):Boolean;
+
+ /**
+ * Returns an IContentElementFactory instance for the given element.
+ *
+ * @see org.tinytlf.layout.model.factories.IContentElementFactory
+ */
+ function getElementFactory(element:*):IContentElementFactory;
+
+ /**
+ * Maps an IContentElementFactory for the given element.
+ *
+ * @see org.tinytlf.layout.model.factories.IContentElementFactory
+ */
+ function mapElementFactory(element:*, classOrFactory:Object):void;
+
+ /**
+ * Unmaps the given element.
+ *
+ * @see org.tinytlf.layout.model.factories.IContentElementFactory
+ */
+ function unMapElementFactory(element:*):Boolean;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/conversion/ITextBlockGenerator.as b/tinytlf-core/src/org/tinytlf/conversion/ITextBlockGenerator.as
new file mode 100644
index 0000000..3d0ab10
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/conversion/ITextBlockGenerator.as
@@ -0,0 +1,9 @@
+package org.tinytlf.conversion
+{
+ import flash.text.engine.TextBlock;
+
+ public interface ITextBlockGenerator
+ {
+ function generate(data:*, factory:IContentElementFactory):TextBlock;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/conversion/TextBlockFactoryBase.as b/tinytlf-core/src/org/tinytlf/conversion/TextBlockFactoryBase.as
new file mode 100755
index 0000000..c4c8286
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/conversion/TextBlockFactoryBase.as
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.conversion
+{
+ import flash.text.engine.*;
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.*;
+ import org.tinytlf.analytics.*;
+ import org.tinytlf.layout.properties.*;
+
+ public class TextBlockFactoryBase implements ITextBlockFactory
+ {
+ private var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ protected var virtualizer:IVirtualizer = new Virtualizer();
+
+ public function get contentVirtualizer():IVirtualizer
+ {
+ return virtualizer;
+ }
+
+ public function preRender():void
+ {
+ }
+
+ public function getTextBlock(index:int):TextBlock
+ {
+ var block:TextBlock = contentVirtualizer.getItemFromIndex(index) as TextBlock;
+
+ if(block)
+ return block;
+
+ return textBlockGenerator.generate(data, getElementFactory(data));
+ }
+
+ protected var elementAdapterMap:Dictionary = new Dictionary(false);
+
+ public function hasElementFactory(element:*):Boolean
+ {
+ return Boolean(element in elementAdapterMap);
+ }
+
+ public function getElementFactory(element:*):IContentElementFactory
+ {
+ var adapter:*;
+
+ //Return the generic adapter if we haven't mapped any.
+ if(!hasElementFactory(element))
+ {
+ adapter = new ContentElementFactory();
+ IContentElementFactory(adapter).engine = engine;
+ return adapter;
+ }
+
+ adapter = elementAdapterMap[element];
+ if(adapter is Class)
+ adapter = IContentElementFactory(new (adapter as Class)());
+ if(adapter is Function)
+ adapter = IContentElementFactory((adapter as Function)());
+
+ IContentElementFactory(adapter).engine = engine;
+
+ return IContentElementFactory(adapter);
+ }
+
+ public function mapElementFactory(element:*, classOrFactory:Object):void
+ {
+ elementAdapterMap[element] = classOrFactory;
+ }
+
+ public function unMapElementFactory(element:*):Boolean
+ {
+ if(!(element in elementAdapterMap))
+ return false;
+
+ return delete elementAdapterMap[element];
+ }
+
+ protected var _data:Object;
+
+ public function get data():Object
+ {
+ return _data;
+ }
+
+ public function set data(value:Object):void
+ {
+ if(value == _data)
+ return;
+
+ _data = value;
+ if(engine)
+ engine.invalidate();
+ }
+
+ private var generator:ITextBlockGenerator = new TextBlockGenerator();
+
+ public function get textBlockGenerator():ITextBlockGenerator
+ {
+ return generator;
+ }
+
+ public function set textBlockGenerator(blockGenerator:ITextBlockGenerator):void
+ {
+ if(blockGenerator == generator)
+ return;
+
+ generator = blockGenerator;
+ }
+
+ public function get numBlocks():int
+ {
+ return 0;
+ }
+ }
+}
+import flash.text.engine.TextBlock;
+
+import org.tinytlf.ITextEngine;
+import org.tinytlf.conversion.*;
+import org.tinytlf.layout.properties.LayoutProperties;
+import org.tinytlf.util.TinytlfUtil;
+import org.tinytlf.util.fte.TextBlockUtil;
+
+internal class TextBlockGenerator implements ITextBlockGenerator
+{
+ public function generate(data:*, factory:IContentElementFactory):TextBlock
+ {
+ var block:TextBlock = TextBlockUtil.checkOut();
+ block.content = factory.execute(data);
+
+ var props:LayoutProperties = TinytlfUtil.getLP();
+ props.mergeWith(data);
+ props.applyTo(block);
+ props.model = data;
+ block.userData = props;
+
+ return block;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/decor/ITextDecor.as b/tinytlf-core/src/org/tinytlf/decor/ITextDecor.as
new file mode 100755
index 0000000..b12a326
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/decor/ITextDecor.as
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.ITextContainer;
+
+ /**
+ * ITextDecor is the decoration actor for tinytlf. ITextDecor
+ * is a map for defining text decorations, including what they're called and
+ * the classes or instances which draw the decoration.
+ * ITextDecor also manages the application and rendering of
+ * decorations. Use decorate and undecorate to
+ * apply or un-apply a decoration to an element. ITextDecor
+ * handles invalidating and rendering the decorations.
+ */
+ public interface ITextDecor
+ {
+ /**
+ * Reference to the central ITextEngine facade for this
+ * decor.
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ * Renders all the decorations in the text field.
+ *
+ * @see org.tinytlf.ITextEngine#invalidateDecorations()
+ */
+ function render():void;
+
+ /**
+ * Removes all the decorations in the text field.
+ */
+ function removeAll():void;
+
+ /**
+ * Creates a decoration at the specified layer for the
+ * element based on the styleObj. An optional
+ * containers argument can be supplied if it is known which
+ * ITextContainers this decoration will render onto. This
+ * is an optimization, as the ITextContainers can be
+ * determined at render time, though it can be an expensive operation.
+ *
+ *
+ * The element type is deliberately ambiguous, as it's up to the
+ * implementor to support different types. In the core
+ * TextDecor, element can be one of four
+ * types:
+ *
+ *
A flash.text.engine.ContentElement.
+ *
A flash.text.engine.TextLine.
+ *
A flash.geom.Rectangle.
+ *
A Vector of flash.geom.Rectangles.
+ *
+ * If the type is a ContentElement, the optional
+ * containers vector is ignored, as
+ * ITextContainers can't be associated for a
+ * ContentElement until render time.
+ *
+ *
+ *
+ * The layer property defines which layer the decoration
+ * should exist on. Smaller numbers are closer to the z-order top, with
+ * 0 being the "highest" allowed layer. The ITextEngine's
+ * caret decoration is on layer 0, and the selection decoration is on
+ * layer 1.
+ *
If element and decorationProp are
+ * specified, undecorate removes a the specified
+ * decorationProp from the element.
+ *
If element is specified with no
+ * decorationProp, undecorate removes all
+ * decorations for the element.
+ *
If decorationProp is specified with no
+ * element, undecorate removes all instances
+ * of the decoration from all elements in the
+ * ITextDecor.
+ *
+ *
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function undecorate(element:* = null, decorationProp:String = null):void;
+
+ /**
+ * Associates the specified decorationProp with the
+ * decorationClassOrFactory (decoration). The
+ * decoration can be a Class reference for an object which
+ * implements org.tinytlf.decor.ITextDecoration, or a
+ * Function which returns an object that implements
+ * org.tinytlf.decor.ITextDecoration.
+ * If decoration is a Class reference, the
+ * decoration is instantiated and returned. If
+ * decoration is a Function, the Function is called and the
+ * return value used as the ITextDecoration instance.
+ *
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function mapDecoration(decorationProp:String,
+ decorationClassOrFactory:Object):void;
+
+ /**
+ * Unmaps a decoration for the specified decorationProp.
+ * @returns True if decorationProp was successfully
+ * unmapped, False if there was an error or there was no
+ * ITextDecoration mapped for this
+ * decorationProp.
+ */
+ function unMapDecoration(decorationProp:String):Boolean;
+
+ /**
+ * Checks to see if an ITextDecoration has been mapped for
+ * the specified decorationProp.
+ * @returns True if there is an ITextDecoration, False if
+ * there isn't.
+ */
+ function hasDecoration(decorationProp:String):Boolean;
+
+ /**
+ * Gets or instantiates an instance of an ITextDecoration
+ * for the specified decorationProp. Optionally associates
+ * the specified ITextContainer with the decoration.
+ */
+ function getDecoration(decorationProp:String, container:ITextContainer = null):ITextDecoration;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/decor/ITextDecoration.as b/tinytlf-core/src/org/tinytlf/decor/ITextDecoration.as
new file mode 100755
index 0000000..7648cef
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/decor/ITextDecoration.as
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import flash.events.IEventDispatcher;
+ import flash.geom.Rectangle;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.styles.IStyleAware;
+
+ /**
+ *
+ * ITextDecoration is the interface for objects which draw
+ * decorations to the display list.
+ *
+ *
+ * An ITextDecoration is a single text decoration, such as a
+ * background color, underline, etc. A decoration draws onto the
+ * shapes Sprite of an ITextContainer. The
+ * decoration's setup method preps the decoration for rendering
+ * and returns a Vector of flash.geom.Rectangles which
+ * represent the area within which this decoration should render. The
+ * decoration's draw method is passed the output from the
+ * setup method, and should use the
+ * flash.geom.Rectangles for guidance in rendering decorations.
+ *
+ *
+ * Because tinytlf supports multi-container layouts, a decoration can span
+ * DisplayObjectContainers. Therefore it is necessary to
+ * specify a vector of associated ITextContainer instances for
+ * decorations, so they know which Sprites to draw into.
+ *
+ *
+ * Note: A decoration shares the same layer sprite with other decoration
+ * instances. It is not necessary to call graphics.clear() in the
+ * draw, that will clear the previously rendered decorations.
+ * The ITextContainer should take care of clearing the display
+ * list before each rendering cycle.
+ *
+ */
+ public interface ITextDecoration extends IStyleAware
+ {
+ function get container():ITextContainer;
+ function set container(textContainer:ITextContainer):void;
+
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ * Whether this decoration is in the foreground (on top of the TextLines)
+ * or in the background (behind the TextLines).
+ */
+ function set foreground(value:Boolean):void;
+
+ /**
+ *
+ * Prepares this decoration for rendering. Setup takes its
+ * input and should return a Vector of
+ * flash.geom.Rectangles which can be passed to
+ * draw.
+ *
+ *
+ * Setup accepts a variable number of
+ * arguments, but in tinytlf core, the second argument is the
+ * element passed into ITextDecor#decorate().
+ *
+ *
+ * This interface is non-restrictive. If there's more information that
+ * your custom ITextDecoration needs, feel free to pass it
+ * in.
+ *
+ * Renders the decoration to the display list. Draw accepts
+ * a Vector of flash.geom.Rectangles which represent the
+ * area within which to render the decoration. For example, an underline
+ * decoration would draw a thin line along the bottom of each
+ * Rectangle.
+ *
+ */
+ function draw(bounds:Vector.):void;
+
+ /**
+ * Called when a decoration is removed from an element. Cleans up any
+ * artifacts left behind from the decoration.
+ *
+ * @see org.tinytlf.decor.ITextDecor#undecorate
+ */
+ function destroy():void;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/decor/TextDecor.as b/tinytlf-core/src/org/tinytlf/decor/TextDecor.as
new file mode 100755
index 0000000..24aa373
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/decor/TextDecor.as
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.ITextContainer;
+
+ ////
+ // This renders every decoration again every time the decorations are
+ // invalidated. Isn't there a way to know which decorations changed and
+ // only clear/render those? I mean without creating separate
+ // Shapes/Sprites per decoration (ick!).
+ //
+ // 09/05/2010: did some testing tonight, seems this isn't as big a deal (yet)
+ // as I thought it could be. Will keep my eye on the situation.
+ ////
+
+ /**
+ * The decoration actor for tinytlf.
+ *
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ public class TextDecor implements ITextDecor
+ {
+ public static const CARET_LAYER:int = 1;
+ public static const SELECTION_LAYER:int = 2;
+
+ protected var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ public function render():void
+ {
+ for each(var el:Element in elements)
+ {
+ el.decorations.sort(function(d1:Decoration, d2:Decoration):int
+ {
+ return d1.layer - d2.layer;
+ });
+
+ for each(var dec:Decoration in el.decorations)
+ {
+ dec.draw(dec.setup(el.element));
+ }
+ }
+ }
+
+ public function removeAll():void
+ {
+ for each(var el:Element in elements)
+ {
+ for each(var dec:Decoration in el.decorations)
+ {
+ el.removeDecoration(dec);
+ dec.destroy();
+ }
+ }
+
+ elements.length = 0;
+ }
+
+ private var elements:Vector. = new Vector.();
+
+ public function decorate(element:*, styleObject:Object, layer:int = 2,
+ container:ITextContainer = null, foreground:Boolean = false):void
+ {
+ var styleProp:String = String(styleObject);
+
+ // Early return optimizations not to loop or invalidate
+ // if there are no decorations in the styleObject.
+ if(styleObject is String && !hasDecoration(styleProp))
+ return;
+ else
+ {
+ var hasOne:Boolean = false;
+ for(styleProp in styleObject)
+ {
+ if(hasOne = hasDecoration(styleProp))
+ break;
+ }
+
+ if(hasOne == false)
+ return;
+ }
+
+ var el:Element = getElement(element);
+
+ if(styleObject is String && hasDecoration(styleProp))
+ {
+ el.addDecoration(new Decoration(getDecoration(styleProp, container), layer));
+ }
+ else
+ {
+ var decoration:ITextDecoration;
+ var dec:Decoration;
+ var styleValue:*;
+
+ for(styleProp in styleObject)
+ {
+ styleValue = styleObject[styleProp];
+ //You can de-apply a decoration by passing in {decoration:false} or {decoration:null}
+ if(styleValue === false || styleValue === 'false' || styleValue == null)
+ {
+ undecorate(element, styleProp);
+ }
+ else if(hasDecoration(styleProp))
+ {
+ dec = el.getDecoration(decorationsMap[styleProp]);
+ if(dec)
+ {
+ decoration = dec.decoration;
+ decoration.mergeWith(styleObject);
+ continue;
+ }
+
+ decoration = getDecoration(styleProp, container);
+ decoration.foreground = foreground;
+ decoration.style = styleObject;
+ el.addDecoration(new Decoration(decoration, layer));
+ }
+ }
+ }
+
+ engine.invalidateDecorations();
+ }
+
+ public function undecorate(element:* = null, decorationProp:String = null):void
+ {
+ if(element === null && decorationProp === null)
+ return;
+
+ var el:Element;
+ var dec:Decoration;
+
+ if(element)
+ {
+ el = getElement(element);
+
+ if(!el)
+ return;
+
+ if(decorationProp)
+ {
+ if(!hasDecoration(decorationProp))
+ return;
+
+ dec = el.getDecoration(decorationsMap[decorationProp]);
+ while(dec)
+ {
+ el.removeDecoration(dec);
+ dec.destroy();
+ dec = el.getDecoration(decorationsMap[decorationProp]);
+ }
+ }
+ else
+ {
+ var decorations:Vector. = el.decorations.concat();
+ for each(dec in decorations)
+ {
+ el.removeDecoration(dec);
+ dec.destroy();
+ }
+ }
+
+ cleanupElement(el);
+ }
+ else
+ {
+ if(!hasDecoration(decorationProp))
+ return;
+
+ var type:Class = decorationsMap[decorationProp];
+ var ements:Vector. = elements.concat();
+
+ for each(el in ements)
+ {
+ dec = el.getDecoration(type);
+ if(dec)
+ {
+ el.removeDecoration(dec);
+ dec.destroy();
+ cleanupElement(el);
+ }
+ }
+ }
+
+ engine.invalidateDecorations();
+ }
+
+ protected var decorationsMap:Object = {};
+
+ public function mapDecoration(styleProp:String, decorationClassOrFactory:Object):void
+ {
+ if(decorationClassOrFactory)
+ decorationsMap[styleProp] = decorationClassOrFactory;
+ else
+ unMapDecoration(styleProp);
+ }
+
+ public function unMapDecoration(styleProp:String):Boolean
+ {
+ if(!hasDecoration(styleProp))
+ return false;
+
+ return delete decorationsMap[styleProp];
+ }
+
+ public function hasDecoration(decorationProp:String):Boolean
+ {
+ return Boolean(decorationProp in decorationsMap);
+ }
+
+ public function getDecoration(styleProp:String, container:ITextContainer = null):ITextDecoration
+ {
+ if(!hasDecoration(styleProp))
+ return null;
+
+ var decoration:* = decorationsMap[styleProp];
+ if(decoration is Class)
+ decoration = ITextDecoration(new decoration());
+ else if(decoration is Function)
+ decoration = ITextDecoration((decoration as Function)());
+
+ if(!decoration)
+ return null;
+
+ ITextDecoration(decoration).container = container;
+
+ ITextDecoration(decoration).engine = engine;
+
+ return ITextDecoration(decoration);
+ }
+
+ protected function getElement(element:*):Element
+ {
+ var filter:Vector. = elements.filter(function(e:Element, ... args):Boolean
+ {
+ return e.element === element;
+ });
+
+ if(filter.length)
+ return filter[0];
+
+ var el:Element = new Element(element);
+ elements.push(el);
+ return el;
+ }
+
+ protected function cleanupElement(el:Element):void
+ {
+ if(el.decorations.length > 0)
+ return;
+
+ elements.splice(elements.indexOf(el), 1);
+ el.decorations = null;
+ el.element = null;
+ }
+ }
+}
+
+import flash.events.EventDispatcher;
+import flash.geom.Rectangle;
+import flash.text.engine.ContentElement;
+
+import org.tinytlf.decor.ITextDecoration;
+
+internal class Element
+{
+ public function Element(e:*)
+ {
+ this.element = e;
+
+ if(e is ContentElement && ContentElement(e).eventMirror == null)
+ ContentElement(e).eventMirror = new EventDispatcher();
+ }
+
+ public var element:*;
+ public var decorations:Vector. = new Vector.();
+
+ public function getDecoration(decorationType:Class):Decoration
+ {
+ var filter:Vector. = decorations.filter(function(d:Decoration, ... args):Boolean
+ {
+ return (d.decoration is decorationType);
+ });
+
+ return filter.length ? filter[0] : null;
+ }
+
+ public function addDecoration(decoration:Decoration):void
+ {
+ if(decorations.indexOf(decoration) == -1)
+ decorations.push(decoration);
+ }
+
+ public function removeDecoration(decoration:Decoration):void
+ {
+ var i:int = decorations.indexOf(decoration);
+ if(i != -1)
+ decorations.splice(i, 1);
+ }
+}
+
+internal class Decoration
+{
+ public function Decoration(decoration:ITextDecoration, layer:int = 2)
+ {
+ this.decoration = decoration;
+ this.layer = layer;
+ }
+
+ public function destroy():void
+ {
+ decoration.destroy();
+ decoration = null;
+ }
+
+ public function setup(... args):Vector.
+ {
+ return decoration.setup.apply(null, [layer].concat(args));
+ }
+
+ public function draw(bounds:Vector.):void
+ {
+ decoration.draw(bounds);
+ }
+
+ public var layer:int = 2;
+ public var decoration:ITextDecoration;
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/decor/TextDecoration.as b/tinytlf-core/src/org/tinytlf/decor/TextDecoration.as
new file mode 100755
index 0000000..02fc410
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/decor/TextDecoration.as
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import flash.display.*;
+ import flash.events.*;
+ import flash.geom.*;
+ import flash.text.engine.*;
+ import flash.utils.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.styles.StyleAwareActor;
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class TextDecoration extends StyleAwareActor implements ITextDecoration
+ {
+ public function TextDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+ private var _container:ITextContainer;
+
+ public function get container():ITextContainer
+ {
+ return _container;
+ }
+
+ public function set container(textContainer:ITextContainer):void
+ {
+ if(textContainer === _container)
+ return;
+
+ _container = textContainer;
+ }
+
+ protected var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ private var _foreground:Boolean = false;
+
+ public function set foreground(value:Boolean):void
+ {
+ _foreground = value;
+ }
+
+ public function get foreground():Boolean
+ {
+ return _foreground;
+ }
+
+ protected var rectToContainer:Dictionary = new Dictionary(true);
+
+ public function setup(layer:int = 2, ... args):Vector.
+ {
+ var bounds:Vector. = new [];
+
+ if(args.length <= 0)
+ return bounds;
+
+ var arg:* = args[0];
+ var rect:Rectangle;
+
+ var tl:TextLine;
+
+ if(arg is ContentElement)
+ {
+ ////
+ // When you decorate a ContentElement, there's no way to
+ // associate it with the ITextContainer(s) that it renders in.
+ // Here, grab every container that holds TextLines for the
+ // element.
+ //
+ // @TODO
+ // It might be better if this exists somewhere else.
+ // Theoretically the ITextLayout's layout routine could update
+ // ITextDecor's decorations that are keyed off ContentElements
+ // as the lines are rendering, but I'm almost certain this would
+ // require special API to stay efficient.
+ ////
+
+ var tlmrs:Vector. = ContentElementUtil.getMirrorRegions(ContentElement(arg));
+ var tlmr:TextLineMirrorRegion;
+
+ var n:int = tlmrs.length;
+ for(var i:int = 0; i < n; ++i)
+ {
+ tlmr = tlmrs[i];
+ rect = tlmr.bounds.clone();
+ tl = tlmr.textLine;
+ rect.offset(tl.x, tl.y);
+ rectToContainer[rect] = ensureLayerExists(engine.layout.getContainerForLine(tl), layer)
+ bounds.push(rect);
+ }
+ }
+ else if(arg is TextBlock)
+ {
+ var block:TextBlock = TextBlock(arg);
+ tl = block.firstLine;
+ rect = tl.getBounds(tl.parent);
+ var tc:ITextContainer = engine.layout.getContainerForLine(tl);
+
+ while(tl)
+ {
+ if(tc != engine.layout.getContainerForLine(tl))
+ {
+ rectToContainer[rect] = ensureLayerExists(tc, layer);
+ bounds.push(rect);
+ rect = tl.getBounds(tl.parent)
+ }
+
+ rect = rect.union(tl.getBounds(tl.parent));
+ tc = engine.layout.getContainerForLine(tl);
+ tl = tl.nextLine;
+
+ if(!tl)
+ {
+ rectToContainer[rect] = ensureLayerExists(tc, layer);
+ bounds.push(rect);
+ }
+ }
+ }
+ else
+ {
+ if(arg is TextLine)
+ {
+ tl = TextLine(arg);
+ bounds.push(tl.getBounds(tl.parent));
+ }
+ else if(arg is Rectangle)
+ {
+ bounds.push(arg);
+ }
+ else if(arg is Vector.)
+ {
+ bounds = bounds.concat(arg);
+ }
+ else if(arg is Point)
+ {
+ bounds.push(new Rectangle(arg.x, arg.y));
+ }
+
+ var sprite:Sprite = ensureLayerExists(container, layer);
+ for each(rect in bounds)
+ {
+ rectToContainer[rect] = sprite;
+ }
+ }
+
+ return bounds;
+ }
+
+ public function draw(bounds:Vector.):void
+ {
+ if(!container)
+ return;
+ }
+
+ public function destroy():void
+ {
+ _container = null;
+ _engine = null;
+ rectToContainer = null;
+ }
+
+ protected function ensureLayerExists(container:ITextContainer, layer:int):Sprite
+ {
+ if(container == null)
+ return null;
+
+ var shapes:Sprite = foreground ? container.foreground : container.background;
+ while(shapes.numChildren < (layer + 1))
+ {
+ shapes.addChild(new MouseDisabledSprite());
+ }
+
+ return Sprite(shapes.getChildAt(shapes.numChildren - layer - 1));
+ }
+
+ protected function rectToLayer(rect:Rectangle):Sprite
+ {
+ return rectToContainer[rect] || (container ? container.foreground : null);
+ }
+ }
+}
+import flash.display.Sprite;
+
+internal class MouseDisabledSprite extends Sprite
+{
+ public function MouseDisabledSprite()
+ {
+ mouseEnabled = false;
+ mouseChildren = true;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/interaction/EventLineInfo.as b/tinytlf-core/src/org/tinytlf/interaction/EventLineInfo.as
new file mode 100755
index 0000000..110b3d5
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/interaction/EventLineInfo.as
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.interaction
+{
+ import flash.display.Sprite;
+ import flash.events.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.ITextContainer;
+
+ // This code is entirely too clever to be proud of it.
+ // Seriously, I'm a fucking douche.
+ public class EventLineInfo
+ {
+ private static function eventFilter(event:Event):Function
+ {
+ return (event is MouseEvent) ? mouseTargetFilter : genericTargetFilter;
+ }
+
+ private static function mouseTargetFilter(target:*):Info
+ {
+ if(target is ITextContainer)
+ return new MouseContainerInfo();
+ if(target is TextLine)
+ return new MouseLineInfo();
+ if(target is Sprite)
+ return new MouseSpriteInfo();
+
+ return new NullInfo();
+ }
+
+ private static function genericTargetFilter(target:*):Info
+ {
+ if(target is ITextContainer)
+ return new GenericContainerInfo();
+ if(target is TextLine)
+ return new GenericLineInfo();
+ if(target is Sprite)
+ return new GenericSpriteInfo();
+
+ return new NullInfo();
+ }
+
+ public static function getInfo(event:Event):EventLineInfo
+ {
+ return Info(eventFilter(event).call(null, event.target))
+ .getInfo(event, event.target)
+ ||
+ Info(eventFilter(event).call(null, event.currentTarget))
+ .getInfo(event, event.currentTarget);
+ }
+
+ public function EventLineInfo(line:TextLine, engine:ITextEngine,
+ mirrorRegion:TextLineMirrorRegion, element:ContentElement)
+ {
+ _engine = engine;
+ _element = element;
+ _line = line;
+ _mirrorRegion = mirrorRegion;
+ _container = engine.layout.getContainerForLine(line);
+ }
+
+ private var _container:ITextContainer;
+ public function get container():ITextContainer
+ {
+ return _container;
+ }
+
+ private var _engine:ITextEngine;
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ private var _element:ContentElement;
+ public function get element():ContentElement
+ {
+ return _element;
+ }
+
+ private var _line:TextLine;
+ public function get line():TextLine
+ {
+ return _line;
+ }
+
+ private var _mirrorRegion:TextLineMirrorRegion;
+ public function get mirrorRegion():TextLineMirrorRegion
+ {
+ return _mirrorRegion;
+ }
+ }
+}
+import flash.display.*;
+import flash.events.*;
+import flash.geom.*;
+import flash.text.engine.*;
+
+import org.tinytlf.*;
+import org.tinytlf.interaction.EventLineInfo;
+import org.tinytlf.layout.ITextContainer;
+import org.tinytlf.util.TinytlfUtil;
+import org.tinytlf.util.fte.TextLineUtil;
+
+internal interface Info
+{
+ function getInfo(event:Event, target:*):EventLineInfo;
+}
+
+internal class NullInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ return new EventLineInfo(null, null, null, null);
+ }
+}
+
+internal class MouseContainerInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ var m:MouseEvent = MouseEvent(event);
+ var c:ITextContainer = ITextContainer(target);
+ var engine:ITextEngine = c.engine;
+ var p:Point = new Point(m.stageX, m.stageY);
+
+ var globalIndex:int = TinytlfUtil.pointToGlobalIndex(engine, p);
+ var line:TextLine = TinytlfUtil.globalIndexToTextLine(engine, globalIndex);
+ var atomIndex:int = TextLineUtil.getAtomIndexAtPoint(line, p)
+ var element:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+ var mirrorRegion:TextLineMirrorRegion = TextLineUtil.getMirrorRegionForElement(line, element);
+
+ return new EventLineInfo(line, engine, mirrorRegion, element);
+ }
+}
+
+internal class GenericContainerInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ var c:ITextContainer = ITextContainer(target);
+ var engine:ITextEngine = c.engine;
+
+ var globalIndex:int = engine.caretIndex;
+
+ var line:TextLine = TinytlfUtil.globalIndexToTextLine(engine, globalIndex);
+ var atomIndex:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, engine.caretIndex);
+ var element:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+ var mirrorRegion:TextLineMirrorRegion = TextLineUtil.getMirrorRegionForElement(line, element);
+
+ return new EventLineInfo(line, engine, mirrorRegion, element);
+ }
+}
+
+internal class MouseLineInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ var line:TextLine = TextLine(target);
+
+ if(line.validity != TextLineValidity.VALID)
+ {
+ trace('error: TextLine validity was ' + line.validity);
+ return null;
+ }
+
+ var engine:ITextEngine = line.userData as ITextEngine;
+ var block:TextBlock = line.textBlock;
+ var atomIndex:int = 0;
+ var m:MouseEvent = MouseEvent(event);
+
+ atomIndex = TextLineUtil.getAtomIndexAtPoint(line, new Point(m.stageX, m.stageY));
+ var element:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+ var mirrorRegion:TextLineMirrorRegion = TextLineUtil.getMirrorRegionForElement(line, element);
+
+ return new EventLineInfo(line, engine, mirrorRegion, element);
+ }
+}
+
+internal class GenericLineInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ var line:TextLine = TextLine(target);
+
+ if(line.validity != TextLineValidity.VALID)
+ {
+ trace('error: TextLine validity was ' + line.validity);
+ return null;
+ }
+
+ var engine:ITextEngine = line.userData as ITextEngine;
+ var block:TextBlock = line.textBlock;
+ var atomIndex:int = 0;
+
+ atomIndex = TinytlfUtil.globalIndexToAtomIndex(engine, line, engine.caretIndex);
+ var element:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+ var mirrorRegion:TextLineMirrorRegion = TextLineUtil.getMirrorRegionForElement(line, element);
+
+ return new EventLineInfo(line, engine, mirrorRegion, element);
+ }
+}
+
+internal class MouseSpriteInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ var m:MouseEvent = event as MouseEvent;
+ var sprite:Sprite = Sprite(target);
+
+ var objs:Array = sprite.getObjectsUnderPoint(new Point(m.localX, m.localY));
+
+ var line:TextLine;
+
+ while(line == null && objs.length)
+ line = objs.shift() as TextLine;
+
+ if(!line)
+ return null;
+
+ return event is MouseEvent ?
+ new MouseLineInfo().getInfo(event, line) :
+ new GenericLineInfo().getInfo(event, line);
+ }
+}
+
+internal class GenericSpriteInfo implements Info
+{
+ public function getInfo(event:Event, target:*):EventLineInfo
+ {
+ throw new Error("How am I supposed to know which line to select when all " +
+ "you give me is a Sprite with no coordinates?");
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/interaction/ITextInteractor.as b/tinytlf-core/src/org/tinytlf/interaction/ITextInteractor.as
new file mode 100755
index 0000000..150f8d4
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/interaction/ITextInteractor.as
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.interaction
+{
+ import flash.events.EventDispatcher;
+ import flash.events.IEventDispatcher;
+
+ import org.tinytlf.ITextEngine;
+
+ /**
+ *
+ * ITextInteractor is the central interaction actor for tinytlf. You can
+ * externally map element names or types to Classes or Functions which
+ * return EventDispatcher instances.
+ *
+ *
+ * This map is most commonly called during ContentElement
+ * creation, from IContentElementFactorys to provide an
+ * eventMirror EventDispatcher to the
+ * ContentElement.
+ *
+ * Reference to the central ITextEngine facade for this
+ * interactor.
+ *
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ *
+ * Maps a mirror Class or Function to an elementName.
+ *
+ *
+ * @return True if the mirror was successfully mapped, False
+ * if it wasn't.
+ */
+ function mapMirror(element:*, mirrorClassOrFactory:Object):void;
+
+ /**
+ *
+ * Unmaps the mirror Class or Function for the given element.
+ *
+ *
+ * @return True if the mirror was successfully unmapped, or False
+ * if it wasn't.
+ */
+ function unMapMirror(element:*):Boolean;
+
+ /**
+ *
+ * Checks to see if this interactor has a Class or Function for the
+ * given element.
+ *
+ *
+ * @return True if there is a mirror, False if there isn't.
+ */
+ function hasMirror(element:*):Boolean;
+
+ /**
+ *
+ * Returns an EventDispatcher for an element name. Typically an
+ * IContentElementFactory calls this when it creates a ContentElement
+ * and needs to specify an eventMirror.
+ *
+ *
+ * If a Class was mapped, this instantiates the class and returns an
+ * instance. If a Function was mapped, this returns the value returned
+ * by calling the Function.
+ *
+ *
+ * If element is null, this method should return a generic
+ * EventDispatcher instance.
+ *
+ */
+ function getMirror(element:* = null):EventDispatcher;
+ }
+}
+
diff --git a/src/org/tinytlf/interaction/TextInteractorBase.as b/tinytlf-core/src/org/tinytlf/interaction/TextInteractorBase.as
old mode 100644
new mode 100755
similarity index 58%
rename from src/org/tinytlf/interaction/TextInteractorBase.as
rename to tinytlf-core/src/org/tinytlf/interaction/TextInteractorBase.as
index 071417c..6140963
--- a/src/org/tinytlf/interaction/TextInteractorBase.as
+++ b/tinytlf-core/src/org/tinytlf/interaction/TextInteractorBase.as
@@ -6,11 +6,12 @@
*/
package org.tinytlf.interaction
{
- import org.tinytlf.ITextEngine;
-
import flash.events.EventDispatcher;
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.ITextEngine;
- public class TextInteractorBase extends TextDispatcherBase implements ITextInteractor
+ public class TextInteractorBase extends EventDispatcher implements ITextInteractor
{
protected var _engine:ITextEngine;
public function get engine():ITextEngine
@@ -26,34 +27,37 @@ package org.tinytlf.interaction
_engine = textEngine;
}
- public function getMirror(elementName:String = ""):EventDispatcher
+ private var mirrorMap:Dictionary = new Dictionary(true);
+
+ public function mapMirror(element:*, mirrorClassOrFactory:Object):void
{
- var mirror:Object = mirrorMap[elementName];
-
- if(!mirror)
- return this;
-
- if(mirror is Class)
- return new(mirror as Class)() as EventDispatcher;
-
- if(mirror is Function)
- return (mirror as Function)() as EventDispatcher;
+ mirrorMap[element] = mirrorClassOrFactory;
+ }
+
+ public function unMapMirror(element:*):Boolean
+ {
+ if(element in mirrorMap)
+ return delete mirrorMap[element];
- return mirror as EventDispatcher;
+ return false;
}
- private var mirrorMap:Object = {};
- public function mapMirror(elementName:String, mirrorClassOrInstance:Object):void
+ public function hasMirror(element:*):Boolean
{
- mirrorMap[elementName] = mirrorClassOrInstance;
+ return Boolean(element in mirrorMap);
}
- public function unMapMirror(elementName:String):Boolean
+ public function getMirror(element:* = null):EventDispatcher
{
- if(elementName in mirrorMap)
- return delete mirrorMap[elementName];
+ var mirror:Object = mirrorMap[element];
- return false;
+ if(mirror is Class)
+ return new(mirror as Class)() as EventDispatcher;
+
+ if(mirror is Function)
+ return (mirror as Function)() as EventDispatcher;
+
+ return null;
}
}
}
diff --git a/tinytlf-core/src/org/tinytlf/layout/ITextContainer.as b/tinytlf-core/src/org/tinytlf/layout/ITextContainer.as
new file mode 100755
index 0000000..3601e10
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/ITextContainer.as
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout
+{
+ import flash.display.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+
+ /**
+ * ITextContainer is the layout controller for a
+ * DisplayObjectContainer used in text layout. Tinytlf renders TextLines
+ * across multiple DisplayObjectContainers, and it delegates control
+ * over layout and line creation to ITextContainers.
+ *
+ *
For a DisplayObjectContainer to be used for layout, it must belong to
+ * an ITextContainer registered with the ITextLayout layout actor.
+ * ITextLayout handles rendering across containers. ITextContainer
+ * participates in layout by rendering as many lines as possible into its
+ * target DisplayObjectContainer, then returning either:
+ *
+ *
the last line successfully rendered, meaning there's no more room for
+ * lines in the target DisplayObjectContainer, or
+ *
null, meaning all the lines from the TextBlock were
+ * rendered and there's still more space in the target
+ * DisplayObjectContainer.
+ *
+ *
+ *
+ *
ITextContainer exposes a shapes Sprite, used
+ * for drawing decorations into the target DisplayObjectContainer.
+ *
+ * @see org.tinytlf.layout.ITextLayout
+ */
+ public interface ITextContainer
+ {
+ /**
+ * Reference to the central ITextEngine facade for this
+ * container.
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ * The target DisplayObjectContainer for this
+ * ITextContainer. TextLines are added to and
+ * removed from the target during layout.
+ *
+ * @see org.tinytlf.layout.ITextLayout
+ */
+ function get target():Sprite;
+ function set target(textContainer:Sprite):void;
+
+ /**
+ * The Sprite which background decorations are rendered into. Background
+ * decorations exist behind the TextLines.
+ *
+ * @see org.tinytlf.decor.ITextDecor
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function get background():Sprite;
+ function set background(shapesContainer:Sprite):void;
+
+ /**
+ * The Sprite which foreground decorations are rendered into. Foreground
+ * decorations exist in front of the TextLines.
+ *
+ * @see org.tinytlf.decor.ITextDecor
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function get foreground():Sprite;
+ function set foreground(shapesContainer:Sprite):void;
+
+ /**
+ * The Sprite in which TextLines are added. Exists between the
+ * background and foreground Sprites.
+ *
+ * @see org.tinytlf.decor.ITextDecor
+ * @see org.tinytlf.decor.ITextDecoration
+ */
+ function get lines():Sprite;
+ function set lines(linesContainer:Sprite):void;
+
+ /**
+ * The height within which to render and lay out TextLines. If this
+ * value is not set, the ITextContainer will render lines
+ * into this container indefinitely, never signaling that the container
+ * is full.
+ *
+ * @see scrollable
+ */
+ function get explicitHeight():Number;
+ function set explicitHeight(value:Number):void;
+
+ /**
+ * The defined width to render TextLines during layout.
+ * If this value is not set, lines are created with
+ * TextLine.MAX_LINE_WIDTH, 1000000.
+ *
+ */
+ function get explicitWidth():Number;
+ function set explicitWidth(value:Number):void;
+
+ /**
+ * The width of the widest TextLine in this
+ * ITextContainer. If the lines aren't justified, the Flash
+ * Text Engine will attempt to render lines into a certain width. If the
+ * FTE determines an atom or word won't fit, it will defer the atom or
+ * word to the next TextLine. The result is that the actual width of the
+ * rendered TextLine is less than the specifiedWidth. In turn, the
+ * measuredWidth of this TextContainer won't necessarily be
+ * the same as the explicitWidth.
+ *
+ */
+ function get measuredWidth():Number;
+ function set measuredWidth(value:Number):void;
+
+ /**
+ * The measured height of all the TextLines, including lineHeight and
+ * paddingTop/paddingBottom.
+ *
+ */
+ function get measuredHeight():Number;
+ function set measuredHeight(value:Number):void;
+
+ /**
+ * Whether the TextContainer is scrollable or not. The TextContainer must
+ * have an explicitHeight (or explicitWidth if
+ * a vertical layout is applied) defined which binds TextLines within
+ * the content area.
+ *
+ * @see explicitHeight
+ */
+ function get scrollable():Boolean;
+ function set scrollable(value:Boolean):void;
+
+ /**
+ * The total height of the measured lines in the content area. This is
+ * irrespective of scrolling, and is the height that should be used as
+ * the total height to calculate a scrollbar.
+ *
+ * @see measuredHeight
+ */
+ function get totalHeight():Number;
+ function set totalHeight(value:Number):void;
+
+ /**
+ * The total width of the measured lines in the content area. This is
+ * irrespective of scrolling, and is the width that should be used as
+ * the total width to calculate a scrollbar.
+ *
+ * @see measuredWidth
+ */
+ function get totalWidth():Number;
+ function set totalWidth(value:Number):void;
+
+ /**
+ * Checks whether this ITextContainer has a particular TextLine.
+ *
+ * @returns true if the line is in this TextContainer, false otherwise.
+ */
+ function hasLine(line:TextLine):Boolean;
+
+ /**
+ * Renders as many TextLines from the specified
+ * TextBlock into the target as possible.
+ *
+ *
There is a special contract between layout and
+ * ITextLayout#render.
+ * This method is passed a TextBlock to render into the
+ * target DisplayObjectContainer. If all the lines from the
+ * TextBlock were able to be rendered into the target, this method
+ * returns null, indicating that there is still space left in the
+ * target. If the TextLines went out of the targets boundaries, this
+ * method returns the last TextLine that fit, indicating that there is
+ * no more room in the target and the ITextLayout should move to the
+ * next container.
+ *
+ * @see org.tinytlf.layout.ITextLayout#render
+ */
+ function layout(block:TextBlock, line:TextLine):TextLine;
+
+ /**
+ * Called on every ITextContainer before layout begins.
+ */
+ function preLayout():void;
+
+ /**
+ * Called on every ITextContainer after the entire layout pass ends.
+ */
+ function postLayout():void;
+
+ /**
+ * Clears the graphics and removes all children from the
+ * shapes Sprite.
+ *
+ */
+ function resetShapes():void;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/ITextLayout.as b/tinytlf-core/src/org/tinytlf/layout/ITextLayout.as
new file mode 100755
index 0000000..6f7dc8c
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/ITextLayout.as
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout
+{
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.analytics.IVirtualizer;
+
+ /**
+ * ITextLayout is the tinytlf text layout actor. ITextLayout is responsible
+ * for:
+ *
+ *
Parsing the TextField data into Flash Text Engine ContentElements.
+ *
Laying out a Vector of TextBlocks across multiple
+ * DisplayObjectContainers.
+ *
+ *
+ *
ITextLayout's textBlockFactory transforms the data into
+ * Flash Text Engine ContentElements and TextBlocks. This is the closest
+ * thing tinytlf has to a "Model" actor. The data can be in any format, as
+ * long as you can write a conversion or parsing routine to generate
+ * ContentElements and TextBlocks.
+ *
+ *
ITextLayout maintains a list of ITextContainers, which are layout
+ * controllers that manage text layout inside DisplayObjectContainers.
+ *
+ * @see org.tinytlf.layout.model.factories.ILayoutFactoryMap
+ * @see org.tinytlf.layout.ITextContainer
+ */
+ public interface ITextLayout
+ {
+ /**
+ * A read-only list of ITextContainers on this layout. To add and remove
+ * containers, use addContainer and
+ * removeContainer instead.
+ *
+ * @see org.tinytlf.layout.ITextContainer
+ */
+ function get containers():Vector.;
+
+ /**
+ *
+ * Reference to the central ITextEngine facade for this
+ * layout.
+ *
+ *
+ * @see org.tinytlf.ITextEngine
+ */
+ function get engine():ITextEngine;
+ function set engine(textEngine:ITextEngine):void;
+
+ /**
+ *
+ * Reference to the IVirtualizer this ITextLayout keeps for
+ * virtualizing TextBlocks.
+ *
+ */
+ function get textBlockVirtualizer():IVirtualizer;
+
+ /**
+ * Adds an ITextContainer to be managed and used in layout.
+ *
+ * @see org.tinytlf.layout.ITextContainer
+ */
+ function addContainer(container:ITextContainer):void;
+
+ /**
+ * Removes an ITextContainer from use in layout.
+ *
+ * @see org.tinytlf.layout.ITextContainer
+ */
+ function removeContainer(container:ITextContainer):void;
+
+ /**
+ * Returns the ITextContainer that a particular TextLine exists in.
+ * Returns null if no container was found to the line.
+ */
+ function getContainerForLine(line:TextLine):ITextContainer;
+
+ /**
+ * Clears the graphics context and removes all children of each shapes
+ * sprite on each ITextContainer.
+ *
+ * @see org.tinytlf.layout.ITextContainer#resetShapes()
+ */
+ function resetShapes():void;
+
+ /**
+ * Renders as many lines from the Vector of TextBlocks as possible.
+ *
+ *
This method attempts to render as many TextLines from each
+ * TextBlock into the available ITextContainers as possible. A special
+ * contract exists between render and
+ * org.tinytlf.layout.ITextContainer#layout: #render()
+ * passes a TextBlock and the previously rendered line to
+ * layout, and layout returns either:
+ *
+ *
The last line rendered out of the TextBlock, which indicates that
+ * there is no more space in the ITextContainer, and this ITextLayout
+ * should move on to the next ITextContainer, keeping the same
+ * TextBlock, or
+ *
null indicating that there are no more lines in the
+ * TextBlock but still more space in the ITextContainer, and this
+ * ITextLayout should move on to the next TextBlock, keeping the same
+ * ITextContainer.
+ *
+ *
+ *
+ *
This method quits either when all the lines have been rendered
+ * into the ITextContainers, or all the ITextContainers are full of
+ * TextLines.
+ *
+ * @see org.tinytlf.layout.ITextContainer#layout()
+ */
+ function render():void;
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/TextContainerBase.as b/tinytlf-core/src/org/tinytlf/layout/TextContainerBase.as
new file mode 100755
index 0000000..d1c645b
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/TextContainerBase.as
@@ -0,0 +1,393 @@
+/*
+* Copyright (c) 2010 the original author or authors
+*
+* Permission is hereby granted to use, modify, and distribute this file
+* in accordance with the terms of the license agreement accompanying it.
+*/
+package org.tinytlf.layout
+{
+ import flash.display.*;
+ import flash.text.engine.*;
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.ITextEngine;
+
+ public class TextContainerBase implements ITextContainer
+ {
+ public function TextContainerBase(container:Sprite,
+ explicitWidth:Number = NaN,
+ explicitHeight:Number = NaN)
+ {
+ this.target = container;
+
+ _explicitWidth = explicitWidth;
+ _explicitHeight = explicitHeight;
+ }
+
+ public function layout(block:TextBlock, line:TextLine):TextLine
+ {
+ return null;
+ }
+
+ protected function createTextLine(block:TextBlock, previousLine:TextLine):TextLine
+ {
+ return null;
+ }
+
+ protected var _target:Sprite;
+
+ public function get target():Sprite
+ {
+ return _target;
+ }
+
+ public function set target(doc:Sprite):void
+ {
+ if(doc == _target)
+ return;
+
+ _target = doc;
+
+ background = Sprite(target.addChildAt(bgShapes || new Sprite(), 0));
+ lines = Sprite(target.addChild(lines || new Sprite()));
+ foreground = Sprite(target.addChild(fgShapes || new Sprite()));
+ }
+
+ protected var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ private var bgShapes:Sprite;
+
+ public function get background():Sprite
+ {
+ return bgShapes;
+ }
+
+ public function set background(shapesContainer:Sprite):void
+ {
+ if(shapesContainer === bgShapes)
+ return;
+
+ bgShapes = shapesContainer;
+ }
+
+ private var fgShapes:Sprite;
+
+ public function get foreground():Sprite
+ {
+ return fgShapes;
+ }
+
+ public function set foreground(shapesContainer:Sprite):void
+ {
+ if(shapesContainer === fgShapes)
+ return;
+
+ fgShapes = shapesContainer;
+
+ if(fgShapes)
+ {
+ // Don't let foreground shapes get in the way of interacting
+ // with the TextLines.
+ fgShapes.mouseEnabled = false;
+ //But maybe we do want children to receive mouse events?
+ //fgShapes.mouseChildren = false;
+ }
+ }
+
+ private var lineContainer:Sprite;
+
+ public function get lines():Sprite
+ {
+ return lineContainer;
+ }
+
+ public function set lines(container:Sprite):void
+ {
+ if(container === lineContainer)
+ return;
+
+ lineContainer = container;
+
+ if(lineContainer)
+ {
+ lineContainer.mouseChildren = true;
+ lineContainer.mouseEnabled = false;
+ }
+ }
+
+ protected var _explicitHeight:Number = NaN;
+
+ public function get explicitHeight():Number
+ {
+ return _explicitHeight;
+ }
+
+ public function set explicitHeight(value:Number):void
+ {
+ if(value === _explicitHeight)
+ return;
+
+ _explicitHeight = value;
+ invalidateVisibleLines();
+ engine.invalidate();
+ }
+
+ protected var _explicitWidth:Number = NaN;
+
+ public function get explicitWidth():Number
+ {
+ return _explicitWidth;
+ }
+
+ public function set explicitWidth(value:Number):void
+ {
+ if(value === _explicitWidth)
+ return;
+
+ _explicitWidth = value;
+ invalidateVisibleLines();
+ engine.invalidate();
+ }
+
+ protected var height:Number = 0;
+
+ public function get measuredHeight():Number
+ {
+ return height;
+ }
+
+ public function set measuredHeight(value:Number):void
+ {
+ if(value === height)
+ return;
+
+ height = value;
+ }
+
+ protected var width:Number = 0;
+
+ public function get measuredWidth():Number
+ {
+ return width;
+ }
+
+ public function set measuredWidth(value:Number):void
+ {
+ if(value === width)
+ return;
+
+ width = value;
+ }
+
+ protected var tHeight:Number = 0;
+
+ public function get totalHeight():Number
+ {
+ return tHeight;
+ }
+
+ public function set totalHeight(value:Number):void
+ {
+ if(value === tHeight)
+ return;
+
+ tHeight = value;
+ }
+
+ protected var tWidth:Number = 0;
+
+ public function get totalWidth():Number
+ {
+ return tWidth;
+ }
+
+ public function set totalWidth(value:Number):void
+ {
+ if(value === tWidth)
+ return;
+
+ tWidth = value;
+ }
+
+ private var _scrollable:Boolean = true;
+ public function get scrollable():Boolean
+ {
+ return _scrollable;
+ }
+
+ public function set scrollable(value:Boolean):void
+ {
+ if(value == _scrollable)
+ return;
+
+ _scrollable = value;
+ if(engine)
+ engine.invalidate();
+ }
+
+ protected var orphanLines:Vector. = new [];
+ protected var visibleLines:Vector. = new [];
+
+ public function hasLine(line:TextLine):Boolean
+ {
+ return visibleLines.indexOf(line) != -1;
+ }
+
+ public function preLayout():void
+ {
+ var lines:Vector. = visibleLines.concat();
+ var n:int = lines.length;
+ var l:TextLine;
+
+ // Parse through and look for invalid lines.
+ for(var i:int = 0; i < n; i += 1)
+ {
+ l = lines[i];
+
+ if(l.validity === TextLineValidity.VALID)
+ continue;
+
+ orphanLines.unshift(l);
+ removeLineFromTarget(l);
+ }
+ }
+
+ public function postLayout():void
+ {
+ var visibleBlocks:Dictionary = engine.layout.textBlockVirtualizer.items;
+ var n:int = visibleLines.length;
+ var line:TextLine;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ line = visibleLines[i];
+ if(line.textBlock in visibleBlocks)
+ continue;
+
+ orphanLines.push(line);
+ }
+
+ n = orphanLines.length;
+ for(i = 0; i < n; i += 1)
+ {
+ line = orphanLines[i];
+ unregisterLine(line);
+ removeLineFromTarget(line);
+ }
+ }
+
+ public function resetShapes():void
+ {
+ clearShapeContainer(foreground);
+ clearShapeContainer(background);
+ }
+
+ private function clearShapeContainer(container:Sprite):void
+ {
+ if(!container)
+ return;
+
+ container.graphics.clear();
+ var n:int = container.numChildren;
+ var child:DisplayObject;
+
+ for(var i:int = 0; i < n; ++i)
+ {
+ child = container.getChildAt(i);
+ if(child is Shape)
+ {
+ Shape(child).graphics.clear();
+ }
+ else if(child is Sprite)
+ {
+ Sprite(child).graphics.clear();
+ while(Sprite(child).numChildren)
+ Sprite(child).removeChildAt(0);
+ }
+
+ }
+ }
+
+ protected function registerLine(line:TextLine):void
+ {
+ if(!hasLine(line))
+ visibleLines.push(line);
+
+ line.userData = engine;
+ engine.interactor.getMirror(line);
+ }
+
+ protected function unregisterLine(line:TextLine):void
+ {
+ line.userData = null;
+
+ var i:int = visibleLines.indexOf(line);
+ if(i != -1)
+ visibleLines.splice(i, 1);
+ }
+
+ protected function addLineToTarget(line:TextLine, index:int = 0):TextLine
+ {
+ if(lines.contains(line))
+ return line;
+
+// index ||= target.numChildren > 1 ? target.numChildren - 1 : 1;
+
+ return TextLine(lines.addChildAt(line, index));
+ }
+
+ protected function removeLineFromTarget(line:TextLine):TextLine
+ {
+ if(!lines.contains(line))
+ return line;
+
+ return TextLine(lines.removeChild(line));
+ }
+
+ protected function getLineIndexFromTarget(line:TextLine):int
+ {
+ if(!target.contains(line))
+ return -1;
+
+ return target.getChildIndex(line);
+ }
+
+ protected function invalidateVisibleLines():void
+ {
+ var n:int = visibleLines.length;
+ for(var i:int = 0; i < n; ++i)
+ {
+ visibleLines[i].validity = TextLineValidity.INVALID;
+ }
+
+ totalHeight = 0;
+ totalWidth = 0;
+ }
+
+ protected function getRecycledLine(previousLine:TextLine):TextLine
+ {
+ if(orphanLines.length == 0)
+ return null;
+
+ var line:TextLine = previousLine;
+
+ while(line === previousLine)
+ line = orphanLines.pop();
+
+ return line;
+ }
+ }
+}
diff --git a/tinytlf-core/src/org/tinytlf/layout/TextLayoutBase.as b/tinytlf-core/src/org/tinytlf/layout/TextLayoutBase.as
new file mode 100755
index 0000000..b85dab8
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/TextLayoutBase.as
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.*;
+ import org.tinytlf.analytics.*;
+ import org.tinytlf.conversion.*;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.*;
+ import org.tinytlf.util.fte.*;
+
+ public class TextLayoutBase implements ITextLayout
+ {
+ protected var _engine:ITextEngine;
+
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ private const virtualizer:IVirtualizer = new Virtualizer();
+
+ public function get textBlockVirtualizer():IVirtualizer
+ {
+ return virtualizer;
+ }
+
+ /**
+ * Clears the shapes out of this Layout's ITextContainers.
+ */
+ public function resetShapes():void
+ {
+ for(var i:int = 0; i < containers.length; i++)
+ {
+ containers[i].resetShapes();
+ }
+ }
+
+ protected var _containers:Vector. = new Vector.;
+
+ public function get containers():Vector.
+ {
+ return _containers ? _containers.concat() : new Vector.;
+ }
+
+ public function addContainer(container:ITextContainer):void
+ {
+ if(containers.indexOf(container) != -1)
+ return;
+
+ _containers.forEach(function(c:ITextContainer, ...args):void{
+ c.scrollable = false;
+ });
+
+ _containers.push(container);
+ container.engine = engine;
+ container.scrollable = true;
+ engine.interactor.getMirror(container);
+ }
+
+ public function removeContainer(container:ITextContainer):void
+ {
+ var i:int = containers.indexOf(container);
+ if(i == -1)
+ return;
+
+ _containers.splice(i, 1);
+ container.engine = null;
+ }
+
+ public function getContainerForLine(line:TextLine):ITextContainer
+ {
+ var n:int = containers.length;
+
+ for(var i:int = 0; i < n; i++)
+ {
+ if(containers[i].hasLine(line))
+ {
+ return containers[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Renders all the TextLines from the list of TextBlocks into this
+ * layout's ITextContainers. Stops when it runs out of TextBlocks or
+ * runs out of space.
+ */
+ public function render():void
+ {
+ if(!containers || !containers.length)
+ return;
+
+ containers.forEach(function(c:ITextContainer, ... args):void{
+ c.preLayout();
+ });
+
+ var factory:ITextBlockFactory = engine.blockFactory;
+
+ var blockIndex:int = textBlockVirtualizer.getIndexFromPosition(engine.scrollPosition);
+ blockIndex = blockIndex <= 0 ? 0 : blockIndex;
+
+ var block:TextBlock = factory.getTextBlock(blockIndex);
+ var container:ITextContainer = containers[0];
+
+ beginRender(blockIndex);
+
+ while(block && container)
+ {
+ container = renderBlockAcrossContainers(block, container);
+
+ cacheBlock(block, blockIndex);
+
+ block.releaseLineCreationData();
+
+ // Only get the next block if there's a container.
+ if(container)
+ {
+ block = factory.getTextBlock(++blockIndex);
+ }
+ }
+
+ endRender(blockIndex);
+
+ containers.forEach(function(c:ITextContainer, ... args):void{
+ c.postLayout();
+ });
+ }
+
+ /**
+ * Renders all the lines from the input TextBlock into the containers,
+ * starting from the container specified by startContainer.
+ *
+ * This method should render every line from the TextBlock into the
+ * ITextContainers.
+ *
+ * @returns The last ITextContainer rendered into, or null if there are
+ * no containers left.
+ */
+ protected function renderBlockAcrossContainers(block:TextBlock,
+ startContainer:ITextContainer):ITextContainer
+ {
+ // If it's an invalid block that's already been rendered, re-render
+ // the damaged lines across multiple containers
+ if(TextBlockUtil.isInvalid(block) && block.firstLine)
+ return renderInvalidLines(block, startContainer);
+
+ // If it's never been rendered, render all the lines across the
+ // containers.
+ //
+ // Or if the block is 100% valid, still call the ITC's layout
+ // method, but it should be smart enough to skip lines from this
+ // textblock.
+
+ return renderAllLines(block, startContainer);
+ }
+
+ protected function renderInvalidLines(block:TextBlock, container:ITextContainer):ITextContainer
+ {
+ // Check if there's any lines we need to render at the end
+ // of the TextBlock.
+ // Note: not sure if this case actually happens...
+ if(block.firstLine && !block.firstInvalidLine)
+ {
+ return renderLines(block, block.lastLine, container);
+ }
+ // Otherwise re-render the invalid lines.
+ else if(block.firstInvalidLine)
+ {
+ return renderLines(block, block.firstInvalidLine.previousLine, container);
+ }
+
+ return container;
+ }
+
+ protected function renderAllLines(block:TextBlock, container:ITextContainer):ITextContainer
+ {
+ return renderLines(block, null, container);
+ }
+
+ protected function renderLines(block:TextBlock,
+ previousLine:TextLine,
+ container:ITextContainer):ITextContainer
+ {
+ var containerIndex:int = containers.indexOf(container);
+ var line:TextLine = container.layout(block, previousLine);
+
+ while(line)
+ {
+ if(++containerIndex < containers.length)
+ container = containers[containerIndex];
+ else
+ return null;
+
+ line = container.layout(block, line);
+ }
+
+ return container;
+ }
+
+ protected function beginRender(startIndex:int):void
+ {
+ //Uncache the TextBlocks that exist before the startIndex
+ for(var i:int = 0; i < startIndex; i += 1)
+ {
+ TextBlockUtil.checkIn(textBlockVirtualizer.getItemFromIndex(i));
+ textBlockVirtualizer.dequeueAt(i);
+ }
+ }
+
+ protected function cacheBlock(block:TextBlock, index:int):void
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+ var size:Number = lp.paddingTop + lp.height + lp.paddingBottom;
+
+ textBlockVirtualizer.enqueueAt(block, index, size);
+ }
+
+ protected function endRender(lastIndex:int):void
+ {
+ //Uncache any blocks after the end index.
+ for(var n:int = engine.blockFactory.numBlocks; lastIndex < n; lastIndex += 1)
+ {
+ TextBlockUtil.checkIn(textBlockVirtualizer.getItemFromIndex(lastIndex));
+ textBlockVirtualizer.dequeueAt(lastIndex);
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/LayoutProperties.as b/tinytlf-core/src/org/tinytlf/layout/properties/LayoutProperties.as
new file mode 100755
index 0000000..1331a2e
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/LayoutProperties.as
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout.properties
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.styles.StyleAwareActor;
+
+ /**
+ * This class is essentially a struct which stores values that describe the
+ * layout values that should be applied to lines rendered from a TextBlock.
+ *
+ * This is associated via the TextBlock's userData value, and
+ * is the only valid value for the userData of a TextBlock in
+ * tinytlf.
+ *
+ * LayoutProperties is dynamic and extends from the tinytlf styling
+ * framework, so he's not without extension points. Most of the inline and
+ * block level layout values are defined as public members, but feel free to
+ * tack on properties as you need.
+ */
+ public dynamic class LayoutProperties extends StyleAwareActor
+ {
+ public function LayoutProperties(props:Object = null)
+ {
+ super(props);
+ }
+
+ public var x:Number = 0;
+ public var y:Number = 0;
+
+ public var width:Number = 0;
+ public var height:Number = 0;
+ public var leading:Number = 0;
+ public var textIndent:Number = 0;
+ public var paddingLeft:Number = 0;
+ public var paddingRight:Number = 0;
+ public var paddingBottom:Number = 0;
+ public var paddingTop:Number = 0;
+
+ public var textAlign:String = TextAlign.LEFT;
+ public var textDirection:String = TextDirection.LTR;
+ public var textTransform:String = TextTransform.NONE;
+ public var float:String = '';
+ public var display:String = TextDisplay.INLINE;
+ public var letterSpacing:Boolean = false;
+ public var locale:String = 'en';
+
+ override protected function applyProperty(property:String, destination:Object):void
+ {
+ if(property === "textAlign" && destination is TextBlock)
+ {
+ setupBlockJustifier(TextBlock(destination));
+ return;
+ }
+
+ super.applyProperty(property, destination);
+ }
+
+ /**
+ * Utility method which applies justification properties to the
+ * TextBlock before it's rendered.
+ */
+ protected function setupBlockJustifier(block:TextBlock):void
+ {
+ var justification:String = LineJustification.UNJUSTIFIED;
+ var justifier:TextJustifier = TextJustifier.getJustifierForLocale(locale);
+
+ if(textAlign == TextAlign.JUSTIFY)
+ justification = LineJustification.ALL_BUT_LAST;
+
+ justifier.lineJustification = justification;
+
+ if( !block.textJustifier ||
+ block.textJustifier.lineJustification != justification ||
+ block.textJustifier.locale != locale)
+ {
+ applyTo(justifier);
+ block.textJustifier = justifier;
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/TextAlign.as b/tinytlf-core/src/org/tinytlf/layout/properties/TextAlign.as
new file mode 100755
index 0000000..c2d75ed
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/TextAlign.as
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout.properties
+{
+ public final class TextAlign
+ {
+ public static const LEFT:String = 'left';
+ public static const CENTER:String = 'center';
+ public static const RIGHT:String = 'right';
+ public static const JUSTIFY:String = 'justify';
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/TextDirection.as b/tinytlf-core/src/org/tinytlf/layout/properties/TextDirection.as
new file mode 100755
index 0000000..2c3a5c7
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/TextDirection.as
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout.properties
+{
+ public class TextDirection
+ {
+ public static const LTR:String = "ltr";
+ public static const RTL:String = "rtl";
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/TextDisplay.as b/tinytlf-core/src/org/tinytlf/layout/properties/TextDisplay.as
new file mode 100644
index 0000000..79ffcda
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/TextDisplay.as
@@ -0,0 +1,8 @@
+package org.tinytlf.layout.properties
+{
+ public final class TextDisplay
+ {
+ public static const INLINE:String = 'inline';
+ public static const BLOCK:String = 'block';
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/TextFloat.as b/tinytlf-core/src/org/tinytlf/layout/properties/TextFloat.as
new file mode 100755
index 0000000..7a0fcf9
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/TextFloat.as
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout.properties
+{
+ public final class TextFloat
+ {
+ public static const LEFT:String = 'left';
+ public static const RIGHT:String = 'right';
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/layout/properties/TextTransform.as b/tinytlf-core/src/org/tinytlf/layout/properties/TextTransform.as
new file mode 100755
index 0000000..9f38593
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/layout/properties/TextTransform.as
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.layout.properties
+{
+ public final class TextTransform
+ {
+ public static const CAPITALIZE:String = 'capitalize';
+ public static const UPPERCASE:String = 'uppercase';
+ public static const LOWERCASE:String = 'lowercase';
+ public static const NONE:String = 'none';
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/styles/IStyleAware.as b/tinytlf-core/src/org/tinytlf/styles/IStyleAware.as
new file mode 100755
index 0000000..e94cdf8
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/styles/IStyleAware.as
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.styles
+{
+ /**
+ * An interface for an object that has styles.
+ */
+ public interface IStyleAware
+ {
+ /**
+ * The style property of the IStyleAware implementation. Often times
+ * this object stores the key/value style pairs, but can also support
+ * wholesale style application and overloading.
+ */
+ function get style():Object;
+ function set style(value:Object):void;
+
+ /**
+ * Clears a style's definition and value from this implementation.
+ */
+ function clearStyle(styleProp:String):Boolean;
+
+ /**
+ * Gets the value for a style property. Can return null if this object's
+ * styles aren't set up, or undefined if the styleProp isn't a style on
+ * this instance.
+ */
+ function getStyle(styleProp:String):*;
+
+ /**
+ * Sets a styleProp to the value defined by newValue.
+ */
+ function setStyle(styleProp:String, newValue:*):void;
+
+ /**
+ * Applies this IStyleAware's styles to the specified Object.
+ */
+ function applyTo(target:Object):void;
+
+ /**
+ * Unapplies this IStyleAware's styles from the specified Object.
+ */
+ function unapplyTo(target:Object):void;
+
+ /**
+ * Merges the specified object's properties into this IStyleAware object.
+ */
+ function mergeWith(withObj:Object):void;
+
+ /**
+ * Removes the specified object's properties from this IStyleAware object.
+ */
+ function unmergeWith(withObj:Object):void;
+
+ /**
+ * toString of course. duh.
+ */
+ function toString():String;
+ }
+}
+
diff --git a/src/org/tinytlf/styles/ITextStyler.as b/tinytlf-core/src/org/tinytlf/styles/ITextStyler.as
old mode 100644
new mode 100755
similarity index 79%
rename from src/org/tinytlf/styles/ITextStyler.as
rename to tinytlf-core/src/org/tinytlf/styles/ITextStyler.as
index e276ab0..f81682d
--- a/src/org/tinytlf/styles/ITextStyler.as
+++ b/tinytlf-core/src/org/tinytlf/styles/ITextStyler.as
@@ -9,7 +9,6 @@ package org.tinytlf.styles
import flash.text.engine.ElementFormat;
import org.tinytlf.ITextEngine;
- import org.tinytlf.core.IStyleAware;
public interface ITextStyler extends IStyleAware
{
@@ -21,7 +20,11 @@ package org.tinytlf.styles
*/
function getElementFormat(element:*):ElementFormat;
- function getMappedStyle(element:*):*;
+ /**
+ * Creates a decorations object for an element which
+ * you can pass to the ITextDecor#decorate method.
+ */
+ function describeElement(element:*):Object;
function mapStyle(element:*, value:*):void;
function unMapStyle(element:*):Boolean;
diff --git a/tinytlf-core/src/org/tinytlf/styles/StyleAwareActor.as b/tinytlf-core/src/org/tinytlf/styles/StyleAwareActor.as
new file mode 100755
index 0000000..ca027d3
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/styles/StyleAwareActor.as
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.styles
+{
+ import flash.utils.Proxy;
+ import flash.utils.flash_proxy;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ use namespace flash_proxy;
+
+ /**
+ * StyleAwareActor is a useful base class for objects with sealed properties
+ * but who also wish to dynamically accept and store named values.
+ *
+ * Since it extends Proxy, it overrides the flash_proxy functions for setting
+ * and retrieving data. If you are calling a sealed property on
+ * StyleAwareActor or one of his subclasses, the property or function is called
+ * like normal. However, if you dynamically set or call a property on it,
+ * getStyle and setStyle are called instead.
+ *
+ * StyleAwareActor has a style member, on which the style
+ * properties and values are stored. You can pass in your own dynamic
+ * instance to store styles on by setting the style setter.
+ * This will set the new value as the internal styles storage object, as
+ * well as copy over all the key/value pairs currently on the new instance.
+ *
+ * This is useful if you wish to proxy styles, or to support external styling
+ * implementations (currently Flex and F*CSS).
+ */
+ public dynamic class StyleAwareActor extends Proxy implements IStyleAware
+ {
+ public function StyleAwareActor(styleObject:Object = null)
+ {
+ if(!styleObject)
+ return;
+
+ style = styleObject;
+ }
+
+ protected var properties:Object = {};
+ protected var propNames:Array = [];
+
+ public function get style():Object
+ {
+ return properties;
+ }
+
+ public function set style(value:Object):void
+ {
+ if(value === properties)
+ return;
+
+ mergeWith(value);
+
+ if(value is IStyleAware)
+ {
+ IStyleAware(value).mergeWith(this);
+ properties = value;
+ }
+ }
+
+ public function clearStyle(styleProp:String):Boolean
+ {
+ return delete this[styleProp];
+ }
+
+ public function getStyle(styleProp:String):*
+ {
+ return this[styleProp];
+ }
+
+ public function setStyle(styleProp:String, newValue:*):void
+ {
+ this[styleProp] = newValue;
+ }
+
+ public function mergeWith(object:Object):void
+ {
+ for(var prop:String in object)
+ mergeProperty(prop, object);
+ }
+
+ protected function mergeProperty(property:String, source:Object):void
+ {
+ this[property] = source[property];
+ }
+
+ public function unmergeWith(object:Object):void
+ {
+ for(var prop:String in object)
+ unmergeProperty(prop);
+ }
+
+ protected function unmergeProperty(property:String):void
+ {
+ delete this[property];
+ }
+
+ public function applyTo(object:Object):void
+ {
+ for(var prop:String in properties)
+ applyProperty(prop, object);
+ }
+
+ protected function applyProperty(property:String, destination:Object):void
+ {
+ if(property in destination && !(destination[property] is Function))
+ destination[property] = this[property];
+ }
+
+ public function unapplyTo(object:Object):void
+ {
+ for(var prop:String in properties)
+ unapplyProperty(prop, object);
+ }
+
+ protected function unapplyProperty(property:String, destination:Object):void
+ {
+ if(property in destination && !(destination[property] is Function))
+ delete destination[property];
+ }
+
+ public function toString():String
+ {
+ var styleString:String = "{";
+ var i:int;
+ var total:int = propNames.length;
+ var prop:String;
+ for(i = 0; i < total; i++)
+ {
+ prop = propNames[i];
+ styleString = styleString.concat(prop, ":", properties[prop].toString(), ";");
+ }
+
+ styleString = styleString.concat("}");
+
+ return styleString;
+ }
+
+ override flash_proxy function getProperty(name:*):*
+ {
+ return properties[name];
+ }
+
+ override flash_proxy function hasProperty(name:*):Boolean
+ {
+ return properties.hasOwnProperty(name);
+ }
+
+ override flash_proxy function callProperty(name:*, ... parameters):*
+ {
+ if(properties.hasOwnProperty(name))
+ return function(... args):*{return properties[name]};
+
+ return null;
+ }
+
+ override flash_proxy function deleteProperty(name:*):Boolean
+ {
+ if(delete properties[name])
+ {
+ propNames.splice(propNames.indexOf(name.toString()), 1);
+ return true;
+ }
+
+ return false;
+ }
+
+ override flash_proxy function setProperty(name:*, value:*):void
+ {
+ name = TinytlfUtil.stripSeparators(name);
+
+ if(!properties.hasOwnProperty(name))
+ propNames.push(name.toString());
+
+ properties[name] = value;
+ }
+
+ override flash_proxy function nextName(index:int):String
+ {
+ return propNames[index - 1];
+ }
+
+ override flash_proxy function nextNameIndex(index:int):int
+ {
+ if(index < propNames.length)
+ return index + 1;
+ else
+ return 0;
+ }
+
+ override flash_proxy function nextValue(index:int):*
+ {
+ return properties[propNames[index - 1]];
+ }
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/styles/TextStyler.as b/tinytlf-core/src/org/tinytlf/styles/TextStyler.as
new file mode 100755
index 0000000..bf86efd
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/styles/TextStyler.as
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.styles
+{
+ import flash.text.engine.ElementFormat;
+ import flash.text.engine.FontDescription;
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.ITextEngine;
+
+ public class TextStyler extends StyleAwareActor implements ITextStyler
+ {
+ public function TextStyler(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ protected var _engine:ITextEngine;
+ public function get engine():ITextEngine
+ {
+ return _engine;
+ }
+
+ public function set engine(textEngine:ITextEngine):void
+ {
+ if(textEngine == _engine)
+ return;
+
+ _engine = textEngine;
+ }
+
+ public function getElementFormat(element:*):ElementFormat
+ {
+ var styleProp:String;
+ var format:ElementFormat = new ElementFormat();
+ var description:FontDescription = new FontDescription();
+
+ for(styleProp in this)
+ {
+ if(styleProp in format)
+ format[styleProp] = this[styleProp];
+ if(styleProp in description)
+ description[styleProp] = this[styleProp];
+ }
+
+ format.fontDescription = description;
+
+ return format;
+ }
+
+ protected var styleMap:Dictionary = new Dictionary(true);
+
+ public function describeElement(element:*):Object
+ {
+ var obj:IStyleAware = new StyleAwareActor();
+
+ if(element in styleMap)
+ obj.mergeWith(styleMap[element]);
+
+ return obj;
+ }
+
+ public function mapStyle(element:*, value:*):void
+ {
+ styleMap[element] = value;
+ }
+
+ public function unMapStyle(element:*):Boolean
+ {
+ if(element in styleMap)
+ return delete styleMap[element];
+
+ return false;
+ }
+ }
+}
+
diff --git a/tinytlf-core/src/org/tinytlf/util/TinytlfUtil.as b/tinytlf-core/src/org/tinytlf/util/TinytlfUtil.as
new file mode 100755
index 0000000..3869729
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/util/TinytlfUtil.as
@@ -0,0 +1,334 @@
+package org.tinytlf.util
+{
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.system.Capabilities;
+ import flash.text.engine.*;
+ import flash.utils.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.analytics.IVirtualizer;
+ import org.tinytlf.layout.properties.LayoutProperties;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public final class TinytlfUtil
+ {
+ public static function atomIndexToGlobalIndex(engine:ITextEngine, line:TextLine, atomIndex:int):int
+ {
+ var v:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var blockStart:int = v.getItemStart(line.textBlock);
+ return blockStart + line.textBlockBeginIndex + atomIndex;
+ }
+
+ public static function globalIndexToAtomIndex(engine:ITextEngine, line:TextLine, globalIndex:int):int
+ {
+ var v:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var blockStart:int = v.getItemStart(line.textBlock);
+ return globalIndex - blockStart - line.textBlockBeginIndex;
+ }
+
+ public static function globalIndexToTextLine(engine:ITextEngine, globalIndex:int):TextLine
+ {
+ var textBlockVirtualizer:IVirtualizer = engine.layout.textBlockVirtualizer;
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var block:TextBlock = textBlockVirtualizer.getItemFromPosition(globalIndex);
+
+ if(globalIndex >= contentVirtualizer.size)
+ {
+ block = textBlockVirtualizer.getItemFromIndex(textBlockVirtualizer.length - 1);
+ --globalIndex;
+ }
+
+ return block.getTextLineAtCharIndex(globalIndex - textBlockVirtualizer.getItemStart(block));
+ }
+
+ public static function globalIndexToAtomBounds(engine:ITextEngine, globalIndex:int):Rectangle
+ {
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var b:TextBlock = contentVirtualizer.getItemFromPosition(globalIndex);
+
+ if(globalIndex >= contentVirtualizer.size)
+ {
+ b = contentVirtualizer.getItemFromIndex(contentVirtualizer.length - 1);
+ globalIndex = contentVirtualizer.size - 1;
+ }
+
+ if(!b)
+ return null;
+
+ var blockIndex:int = globalIndex - contentVirtualizer.getItemStart(b);
+ var line:TextLine = b.getTextLineAtCharIndex(blockIndex);
+ var atomBounds:Rectangle = line.getAtomBounds(blockIndex - line.textBlockBeginIndex);
+ atomBounds.offset(line.x, line.y);
+ return atomBounds;
+ }
+
+ public static function pointToGlobalIndex(engine:ITextEngine, point:Point):int
+ {
+ var textBlockVirtualizer:IVirtualizer = engine.layout.textBlockVirtualizer;
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var blocks:Dictionary = textBlockVirtualizer.items;
+ var b:TextBlock;
+ var l:TextLine;
+
+ for(var tmp:* in blocks)
+ {
+ b = TextBlock(tmp);
+ l = b.firstLine;
+ while(l)
+ {
+ if(l.hitTestPoint(point.x, point.y))
+ break;
+
+ l = l.nextLine;
+ }
+
+ if(l)
+ break;
+ }
+
+ if(!l)
+ return -1;
+
+ var atomIndex:int = TextLineUtil.getAtomIndexAtPoint(l, point);
+
+ return contentVirtualizer.getItemStart(b) + l.textBlockBeginIndex + atomIndex;
+ }
+
+ public static function yToTextLine(engine:ITextEngine, y:Number):TextLine
+ {
+ var textBlockVirtualizer:IVirtualizer = engine.layout.textBlockVirtualizer;
+ var b:TextBlock = textBlockVirtualizer.getItemFromPosition(engine.scrollPosition + y);
+
+ if(!b)
+ {
+ if(y < 0)
+ return TextBlock(textBlockVirtualizer.getItemFromIndex(0)).firstLine;
+
+ return null;
+ }
+
+ var l:TextLine = b.firstLine;
+ var lp:LayoutProperties = getLP(b);
+ var h:Number = textBlockVirtualizer.getItemStart(b) + lp.paddingTop;
+
+ while(l)
+ {
+ h += l.ascent;
+
+ if(h >= y)
+ break;
+
+ h += l.descent + lp.leading;
+ l = l.nextLine;
+ }
+
+ if(!l && h <= lp.y + lp.height + lp.paddingBottom)
+ l = b.lastLine;
+
+ return l;
+ }
+
+ private static var mac:Boolean = (/mac/i).test(Capabilities.os);
+
+ /**
+ * Useful checker to determine what system you're on. Native Mac
+ * applications respond to different keys than their Windows or Linux
+ * counterparts, and every interaction in tinytlf should be the same as
+ * the native system functions.
+ */
+ public static function isMac():Boolean
+ {
+ return mac;
+ }
+
+ public static function validPoint(p:Point):Boolean
+ {
+ return p.x == p.x && p.y == p.y;
+ }
+
+ /**
+ * Returns true if all of the flags specified by flagMask are set.
+ */
+ public static function isBitSet(flags:uint, flagMask:uint):Boolean
+ {
+ return flagMask == (flags & flagMask);
+ }
+
+ /**
+ * Sets the flags specified by flagMask according to value.
+ * Returns the new bitflag.
+ * flagMask can be a combination of multiple flags.
+ */
+ public static function updateBits(flags:uint, flagMask:uint, update:Boolean = true):uint
+ {
+ if(update)
+ {
+ if((flags & flagMask) == flagMask)
+ return flags; // Nothing to change
+ // Don't use ^ since flagMask could be a combination of multiple flags
+ flags |= flagMask;
+ }
+ else
+ {
+ if((flags & flagMask) == 0)
+ return flags; // Nothing to change
+ // Don't use ^ since flagMask could be a combination of multiple flags
+ flags &= ~flagMask;
+ }
+ return flags;
+ }
+
+ /**
+ * Retrieves the LayoutProperties object from the argument passed in.
+ * If no LayoutProperties could be determined, a new instance is
+ * returned.
+ *
+ */
+ public static function getLP(from:Object = null):LayoutProperties
+ {
+ if(from is LayoutProperties)
+ return LayoutProperties(from);
+
+ var block:TextBlock;
+ if(from is TextLine)
+ block = TextLine(from).textBlock;
+ else if(from is TextBlock)
+ block = TextBlock(from);
+
+ if(block)
+ {
+ if(block.userData is LayoutProperties)
+ return LayoutProperties(block.userData);
+ else
+ return block.userData = new LayoutProperties();
+ }
+
+ return new LayoutProperties(from);
+ }
+
+ /**
+ * Like compare, except only primitive types matter. Â
+ * If objectA has a child object with no values and objectB doesn't have
+ * that object, they still compare as true because no primitive types
+ * had to be evaluated.
+ *
+ * @return True if the two Object's values are the same, False if
+ * they're different.
+ */
+ public static function compareObjectValues(objectA:Object,
+ objectB:Object,
+ exceptions:Object = null):Boolean
+ {
+ if(!!objectA != !!objectB)
+ return false;
+
+ return (
+ recursiveCompare(objectA, objectB, exceptions) &&
+ recursiveCompare(objectB, objectA, exceptions)
+ );
+ }
+
+ private static const compareTypes:Dictionary = new Dictionary();
+
+ private static function recursiveCompare(source:Object,
+ dest:Object,
+ exceptions:Object = null):Boolean
+ {
+ exceptions ||= {};
+
+ if(source is Array)
+ {
+ if(source.length != dest.length)
+ return false;
+
+ for(var i:int = 0; i < source.length; i += 1)
+ {
+ if(source[i] !== dest[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ if(source is XML || source is XMLList)
+ {
+ if(source.length() != dest.length())
+ return false;
+
+ return source === dest;
+ }
+
+ var accessors:XMLList;
+ if(!(source.constructor in compareTypes))
+ {
+ var xml:XML = describeType(source);
+ compareTypes[source.constructor] = xml..accessor.(@access == 'readwrite');
+ }
+
+ accessors = compareTypes[source.constructor];
+
+ var n:String;
+ for each(var x:XML in accessors)
+ {
+ n = x.@name;
+
+ if(n in exceptions)
+ continue;
+
+ switch(typeof source[n])
+ {
+ case 'object':
+ case 'xml':
+ if(!recursiveCompare(source[n], dest[n], exceptions))
+ return false;
+ break;
+ case 'boolean':
+ case 'number':
+ case 'string':
+ case 'function':
+ if(source[n] !== dest[n])
+ return false;
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Converts a string from underscore or dash separators to no separators.
+ */
+ public static function stripSeparators(str:String):String
+ {
+ var s:String = str.replace(/(-|_)(\w)/g, function(...args):String{
+ return String(args[2]).toUpperCase();
+ });
+
+ return s.replace(/(-|_)/g, '');
+ }
+
+ private static var typeCache:Dictionary = new Dictionary(false);
+
+ public static function describeType(value:Object, refreshCache:Boolean = false):XML
+ {
+ if(!(value is Class))
+ {
+ if(value is Proxy)
+ value = getDefinitionByName(getQualifiedClassName(value)) as Class;
+ else if(value is Number)
+ value = Number;
+ else
+ value = value.constructor as Class;
+ }
+
+ if(refreshCache || typeCache[value] == null)
+ {
+ typeCache[value] = flash.utils.describeType(value);
+ }
+
+ return typeCache[value];
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-core/src/org/tinytlf/util/XMLUtil.as b/tinytlf-core/src/org/tinytlf/util/XMLUtil.as
new file mode 100755
index 0000000..c43f0b5
--- /dev/null
+++ b/tinytlf-core/src/org/tinytlf/util/XMLUtil.as
@@ -0,0 +1,35 @@
+package org.tinytlf.util
+{
+ public class XMLUtil
+ {
+ public static function buildKeyValueAttributes(attributes:XMLList):Object
+ {
+ var obj:Object = {};
+ var n:int = attributes.length();
+ var attr:XML;
+
+ for (var i:int = 0; i < n; ++i)
+ {
+ attr = attributes[i];
+ obj[TinytlfUtil.stripSeparators(attr.localName())] = attr.toString();
+ }
+
+ return obj;
+ }
+
+ public static function arrayToString(array:Array):String
+ {
+ var s:String = '';
+ var n:int = array.length;
+ for(var i:int = 0; i < n; ++i)
+ {
+ if(array[i] is XML || array[i] is XMLList)
+ s += TinytlfUtil.stripSeparators(array[i].toXMLString());
+ else
+ s += TinytlfUtil.stripSeparators(array[i].toString());
+ }
+
+ return s;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/BackgroundColorDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/BackgroundColorDecoration.as
new file mode 100755
index 0000000..3b3ef13
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/BackgroundColorDecoration.as
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import flash.display.Graphics;
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+ import flash.text.engine.TextLineMirrorRegion;
+
+ public class BackgroundColorDecoration extends ContentElementDecoration
+ {
+ public function BackgroundColorDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override protected function processTLMR(tlmr:TextLineMirrorRegion):Rectangle
+ {
+ var rect:Rectangle = tlmr.bounds.clone();
+ rect.y = emBox.y;
+ rect.height = emBox.height;
+ rect.offset(tlmr.textLine.x, tlmr.textLine.y);
+ return rect;
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ super.draw(bounds);
+
+ var rect:Rectangle;
+ var g:Graphics;
+ var copy:Vector. = bounds.concat();
+ var bgColor:uint;
+ var bgAlpha:Number;
+ var layer:Sprite;
+
+ while(copy.length > 0)
+ {
+ rect = copy.pop();
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
+ g.lineStyle();
+
+ bgColor = uint(getStyle('backgroundColor'));
+ bgAlpha = Number(getStyle('backgroundAlpha'));
+
+ g.beginFill(isNaN(bgColor) ? 0x000000 : bgColor, isNaN(bgAlpha) ? 1 : bgAlpha);
+ g.drawRect(rect.x, rect.y, rect.width, rect.height);
+ g.endFill();
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/BorderDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/BorderDecoration.as
new file mode 100644
index 0000000..9b32265
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/BorderDecoration.as
@@ -0,0 +1,49 @@
+package org.tinytlf.decor
+{
+ import flash.display.Graphics;
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+
+ import org.tinytlf.decor.BackgroundColorDecoration;
+
+ public class BorderDecoration extends BackgroundColorDecoration
+ {
+ public function BorderDecoration(styleObject:Object=null)
+ {
+ super(styleObject);
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ var rect:Rectangle;
+ var g:Graphics;
+ var copy:Vector. = bounds.concat();
+ var layer:Sprite;
+
+ while(copy.length > 0)
+ {
+ rect = copy.pop();
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
+ g.lineStyle(
+ getStyle("thickness") || 1,
+ getStyle("color") || 0x00,
+ getStyle("alpha") || 1,
+ getStyle("pixelHinting") || false,
+ getStyle("scaleMode") || "normal",
+ getStyle("caps") || null,
+ getStyle("joints") || null,
+ getStyle("miterLimit") || 3);
+
+ g.beginFill(0x00, 0);
+ g.drawRect(rect.x, rect.y, rect.width, rect.height);
+ g.lineStyle();
+ g.endFill();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/BulletDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/BulletDecoration.as
new file mode 100755
index 0000000..4384658
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/BulletDecoration.as
@@ -0,0 +1,41 @@
+package org.tinytlf.decor
+{
+ import flash.display.Graphics;
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+
+
+ public class BulletDecoration extends TextDecoration
+ {
+ public function BulletDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ var copy:Vector. = bounds.concat();
+ var rect:Rectangle;
+ var g:Graphics;
+ var diameter:Number = getStyle('diameter') || 4;
+ var layer:Sprite;
+
+ while(copy.length)
+ {
+ rect = copy.pop();
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
+
+ g.beginFill(getStyle('bulletColor') || getStyle('fontColor') || 0x00,
+ getStyle('bulletAlpha') || getStyle('fontAlpha') || 1);
+
+ g.drawCircle(rect.x + (rect.width - diameter), rect.y + (diameter * 2), diameter * .5);
+ g.endFill();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/ContentElementDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/ContentElementDecoration.as
new file mode 100755
index 0000000..7359ba3
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/ContentElementDecoration.as
@@ -0,0 +1,59 @@
+package org.tinytlf.decor
+{
+ import flash.geom.Rectangle;
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.FontMetrics;
+ import flash.text.engine.TextLineMirrorRegion;
+
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class ContentElementDecoration extends TextDecoration
+ {
+ public function ContentElementDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function setup(layer:int = 2, ... parameters):Vector.
+ {
+ if(parameters.length < 1)
+ return super.setup.apply(null, [layer, foreground].concat(parameters));
+
+ var arg:* = parameters[0];
+ if(!(arg is ContentElement))
+ return super.setup.apply(null, [layer].concat(parameters));
+
+ processContentElement(ContentElement(arg));
+
+ var bounds:Vector. = new Vector.();
+ var tlmrs:Vector. = ContentElementUtil.getMirrorRegions(ContentElement(arg));
+ var n:int = tlmrs.length;
+
+ var tlmr:TextLineMirrorRegion;
+ var rect:Rectangle;
+
+ for(var i:int = 0; i < n; ++i)
+ {
+ tlmr = tlmrs[i];
+ rect = processTLMR(tlmr);
+ rectToContainer[rect] = ensureLayerExists(engine.layout.getContainerForLine(tlmr.textLine), layer);
+ bounds.push(rect);
+ }
+
+ return bounds;
+ }
+
+ protected var emBox:Rectangle;
+
+ protected function processContentElement(element:ContentElement):void
+ {
+ var metrics:FontMetrics = element.elementFormat.getFontMetrics();
+ emBox = metrics.emBox;
+ }
+
+ protected function processTLMR(tlmr:TextLineMirrorRegion):Rectangle
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/HorizontalRuleDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/HorizontalRuleDecoration.as
new file mode 100755
index 0000000..23c3f1d
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/HorizontalRuleDecoration.as
@@ -0,0 +1,40 @@
+package org.tinytlf.decor
+{
+ import flash.geom.Rectangle;
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class HorizontalRuleDecoration extends StrikeThroughDecoration
+ {
+ public function HorizontalRuleDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function setup(layer:int = 0, ... parameters):Vector.
+ {
+ var arg:* = parameters[0];
+ if(arg is ContentElement)
+ {
+ var lines:Vector. = ContentElementUtil.getTextLines(ContentElement(arg));
+ if(!lines.length)
+ return new [];
+
+ var line:TextLine = lines[0];
+ var rect:Rectangle = line.getBounds(line.parent);
+ var container:ITextContainer;
+
+ rect.width = line.specifiedWidth;
+
+ rectToContainer[rect] = ensureLayerExists(engine.layout.getContainerForLine(line), layer);
+
+ return new [rect];
+ }
+ else
+ return super.setup.apply(null, [layer].concat(parameters));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/PopupDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/PopupDecoration.as
new file mode 100644
index 0000000..2a48477
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/PopupDecoration.as
@@ -0,0 +1,35 @@
+package org.tinytlf.decor
+{
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+
+ [Style(name="component", type="Object")]
+
+ public class PopupDecoration extends TextDecoration
+ {
+ public function PopupDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ var component:DisplayObject = (getStyle('component') as DisplayObject);
+ if(!component)
+ return;
+
+ var r:Rectangle = bounds[0];
+ var layer:Sprite = rectToLayer(r);
+
+ if(!layer.contains(component))
+ layer.addChild(component);
+
+ component.x = r.x;
+ component.y = r.y;
+
+ //Pass along any styles that may also be public attributes of the component.
+ applyTo(component);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/tinytlf/decor/decorations/StrikeThroughDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/StrikeThroughDecoration.as
old mode 100644
new mode 100755
similarity index 60%
rename from src/org/tinytlf/decor/decorations/StrikeThroughDecoration.as
rename to tinytlf-decorations/src/org/tinytlf/decor/StrikeThroughDecoration.as
index d05e753..d3e6985
--- a/src/org/tinytlf/decor/decorations/StrikeThroughDecoration.as
+++ b/tinytlf-decorations/src/org/tinytlf/decor/StrikeThroughDecoration.as
@@ -4,50 +4,58 @@
* Permission is hereby granted to use, modify, and distribute this file
* in accordance with the terms of the license agreement accompanying it.
*/
-package org.tinytlf.decor.decorations
+package org.tinytlf.decor
{
+ import flash.display.Graphics;
import flash.display.Sprite;
import flash.geom.Point;
import flash.geom.Rectangle;
- import org.tinytlf.decor.TextDecoration;
public class StrikeThroughDecoration extends TextDecoration
{
- public function StrikeThroughDecoration(styleName:String="")
+ public function StrikeThroughDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function draw(bounds:Vector.):void
{
- super(styleName);
- }
-
- override public function draw(bounds:Vector., layer:int = 0):void
- {
- super.draw(bounds, layer);
+ super.draw(bounds);
var start:Point;
var end:Point;
var rect:Rectangle;
- var parent:Sprite;
+ var g:Graphics;
+ var layer:Sprite;
while(bounds.length > 0)
{
rect = bounds.pop();
- parent = spriteMap[rect];
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
start = new Point(rect.x, rect.y + (rect.height * 0.5));
end = new Point(rect.x + rect.width, rect.y + (rect.height * 0.5));
- parent.graphics.lineStyle(
+ g.lineStyle(
getStyle("weight") || 2,
- getStyle("color") || getStyle("color") || 0x00,
- getStyle("alpha") || 1,
+ getStyle("strikethroughColor") || getStyle("color") || 0x00,
+ getStyle("strikethroughAlpha") || getStyle("alpha") || 1,
getStyle("pixelHinting") || false,
getStyle("scaleMode") || "normal",
getStyle("caps") || null,
getStyle("joints") || null,
getStyle("miterLimit") || 3);
- parent.graphics.moveTo(start.x, start.y);
- parent.graphics.lineTo(end.x, end.y);
+ g.moveTo(start.x, start.y);
+ g.lineTo(end.x, end.y);
+
+ g.lineStyle();
}
}
}
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/UnderlineDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/UnderlineDecoration.as
new file mode 100755
index 0000000..3bcd737
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/UnderlineDecoration.as
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor
+{
+ import flash.display.Graphics;
+ import flash.display.Sprite;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.FontMetrics;
+ import flash.text.engine.TextLineMirrorRegion;
+
+ public class UnderlineDecoration extends ContentElementDecoration
+ {
+ public function UnderlineDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override protected function processContentElement(element:ContentElement):void
+ {
+ super.processContentElement(element);
+ var metrics:FontMetrics = element.elementFormat.getFontMetrics();
+ setStyle("underlineThickness", metrics.underlineThickness);
+ }
+
+ override protected function processTLMR(tlmr:TextLineMirrorRegion):Rectangle
+ {
+ var rect:Rectangle = tlmr.bounds.clone();
+ rect.y = emBox.y;
+ rect.height = emBox.height - getStyle('underlineThickness');
+ rect.offset(tlmr.textLine.x, tlmr.textLine.y);
+ return rect;
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ super.draw(bounds);
+
+ var start:Point;
+ var end:Point;
+ var rect:Rectangle;
+ var g:Graphics;
+ var copy:Vector. = bounds.concat();
+ var thickness:Number = getStyle("underlineThickness") || 2;
+ var layer:Sprite;
+
+ while(copy.length > 0)
+ {
+ rect = copy.pop();
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
+ start = new Point(rect.left, rect.bottom - thickness);
+ end = new Point(rect.right, rect.bottom - thickness);
+
+ g.lineStyle(
+ thickness,
+ getStyle("underlineColor") || getStyle("color") || 0x00,
+ getStyle("underlineAlpha") || getStyle("alpha") || 1,
+ getStyle("pixelHinting") || false,
+ getStyle("scaleMode") || "normal",
+ getStyle("caps") || null,
+ getStyle("joints") || null,
+ getStyle("miterLimit") || 3);
+
+ g.moveTo(start.x, start.y);
+ g.lineTo(end.x, end.y);
+ g.endFill();
+ g.lineStyle();
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/selection/CaretDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/selection/CaretDecoration.as
new file mode 100755
index 0000000..b124ad6
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/selection/CaretDecoration.as
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor.selection
+{
+ import flash.display.Graphics;
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.events.TimerEvent;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.text.engine.TextLine;
+ import flash.utils.Timer;
+
+ import org.tinytlf.decor.TextDecoration;
+
+
+ public class CaretDecoration extends SelectionDecorationBase
+ {
+ public function CaretDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ private var timer:Timer;
+ private var g:Graphics;
+ private var rect:Rectangle;
+
+ override public function setup(layer:int = 0, ... args):Vector.
+ {
+ if(!timer)
+ timer = new Timer(365);
+ else
+ timer.stop();
+
+ var wrapper:Object = args[0];
+ var index:int = wrapper.caretIndex;
+
+ return super.setup.apply(null, [layer].concat(new Point(index, index + 1)));
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ super.draw(bounds);
+
+ if(!bounds.length)
+ return;
+
+ rect = bounds[0];
+
+ var layer:Sprite = rectToLayer(rect);
+ if(!layer)
+ return;
+
+ g = Shape(layer.addChild(new Shape())).graphics;
+
+ if(!timer.hasEventListener(TimerEvent.TIMER))
+ timer.addEventListener(TimerEvent.TIMER, toggle);
+
+ if(!timer.running)
+ {
+ toggle(null);
+ timer.start();
+ }
+ }
+
+ override public function destroy():void
+ {
+ super.destroy();
+
+ if(timer)
+ {
+ timer.stop();
+ timer.removeEventListener(TimerEvent.TIMER, toggle);
+ }
+
+ timer = null;
+ showing = false;
+
+ if(g)
+ g.clear();
+ g = null;
+ rect = null;
+ }
+
+ private var showing:Boolean = false;
+
+ private function toggle(event:TimerEvent):void
+ {
+ if(!g)
+ return;
+
+ g.clear();
+
+ if(showing = !showing)
+ return;
+
+ var right:int = int(Boolean(getStyle('position') == 'right'));
+
+ g.lineStyle(getStyle('caretWeight') || 1, getStyle('caretColor'));
+ g.moveTo(rect.x + right * rect.width, rect.y);
+ g.lineTo(rect.x + right * rect.width, rect.y + rect.height);
+ }
+
+ override protected function getLineRect(line:TextLine, selectionIndicies:Point):Rectangle
+ {
+ line.stage.focus = line;
+ return super.getLineRect(line, selectionIndicies);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/selection/SelectionDecorationBase.as b/tinytlf-decorations/src/org/tinytlf/decor/selection/SelectionDecorationBase.as
new file mode 100644
index 0000000..78045b0
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/selection/SelectionDecorationBase.as
@@ -0,0 +1,188 @@
+package org.tinytlf.decor.selection
+{
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.analytics.IVirtualizer;
+ import org.tinytlf.decor.TextDecor;
+ import org.tinytlf.decor.TextDecoration;
+ import org.tinytlf.layout.ITextContainer;
+
+ public class SelectionDecorationBase extends TextDecoration
+ {
+ public function SelectionDecorationBase(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function setup(layer:int = 2, ... parameters):Vector.
+ {
+ // We need to resolve some rects from our selection indicies.
+ var rects:Vector. = new [];
+ // this must be a Point object
+ var pt:Point = Point(parameters[0]).clone();
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var block:TextBlock;
+ var index:int = 0;
+
+ var indicies:Point;
+ var start:Number = pt.x;
+
+ while(start < pt.y)
+ {
+ index = contentVirtualizer.getIndexFromPosition(start);
+
+ if(start == contentVirtualizer.size)
+ index = contentVirtualizer.length - 1;
+
+ if(index == -1)
+ break;
+
+ block = contentVirtualizer.getItemFromIndex(index);
+
+ if(block)
+ {
+ indicies = getBlockSelectionIndicies(block, pt.clone());
+
+ if(indicies.x == indicies.x && indicies.y == indicies.y)
+ rects = rects.concat(getBlockRects(block, indicies));
+ else
+ break;
+ }
+
+ if(start == contentVirtualizer.size)
+ break;
+
+ start = contentVirtualizer.getItemStart(block) + contentVirtualizer.getItemSize(block);
+ }
+
+ return rects;
+ }
+
+ protected function getBlockSelectionIndicies(block:TextBlock, selection:Point):Point
+ {
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var blockIndex:int = contentVirtualizer.getItemIndex(block);
+ var contentItem:* = contentVirtualizer.getItemFromIndex(blockIndex);
+ var start:Number = contentVirtualizer.getItemStart(contentItem);
+ var size:Number = contentVirtualizer.getItemSize(contentItem);
+ var end:Number = start + size;
+
+ var p:Point = new Point();
+
+ if(selection.y < start || selection.x > end)
+ p.x = p.y = NaN;
+ else
+ {
+ if(selection.x <= start)
+ p.x = 0;
+ else if(selection.x <= end)
+ p.x = selection.x - start;
+
+ if(selection.y > end)
+ p.y = end;
+ else if(selection.y <= end)
+ p.y = selection.y - start;
+
+ if(selection.x > end)
+ p.x = p.y = NaN;
+ }
+
+ return p;
+ }
+
+ protected function getBlockRects(block:TextBlock, selectionIndicies:Point):Vector.
+ {
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var blockSize:Number = contentVirtualizer.getItemSize(block);
+
+ if(selectionIndicies.x == 0 && selectionIndicies.y >= blockSize - 1)
+ {
+ return super.setup(TextDecor.SELECTION_LAYER, block);
+ }
+
+ var rects:Vector. = new [];
+ var blockLength:int = contentVirtualizer.getItemSize(block);
+ var line:TextLine = block.getTextLineAtCharIndex(Math.min(selectionIndicies.x, blockLength - 1));
+ var indicies:Point;
+
+ while(line)
+ {
+ indicies = getLineSelectionIndicies(line, selectionIndicies);
+ if( indicies.x == indicies.x &&
+ indicies.y == indicies.y &&
+ indicies.y > indicies.x)
+ {
+ rects.push(getLineRect(line, indicies));
+ line = line.nextLine;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return rects;
+ }
+
+ protected function getLineSelectionIndicies(line:TextLine, selection:Point):Point
+ {
+ var p:Point = new Point();
+ var begin:int = line.textBlockBeginIndex;
+ var end:int = begin + line.atomCount;
+
+ if(selection.y < begin || selection.x > end)
+ {
+ p.x = p.y = NaN;
+ }
+ else
+ {
+ if(selection.x <= begin)
+ p.x = 0;
+ else if(selection.x > begin)
+ p.x = selection.x - begin;
+
+ if(selection.y > end)
+ p.y = end;
+ else if(selection.y <= end)
+ p.y = selection.y - begin;
+ }
+
+ return p;
+ }
+
+ protected function getLineRect(line:TextLine, selectionIndicies:Point):Rectangle
+ {
+ var startIndex:int = selectionIndicies.x;
+ var endIndex:int = selectionIndicies.y;
+
+ if(startIndex >= line.atomCount)
+ startIndex = line.atomCount - 1;
+ if(endIndex >= line.atomCount)
+ endIndex = line.atomCount - 1;
+
+ var rect:Rectangle = line.getAtomBounds(startIndex);
+ rect.width ||= 1;
+ rect.height ||= 1;
+
+ var rect2:Rectangle = line.getAtomBounds(Math.max(endIndex, 0));
+
+ if(endIndex > line.atomCount)
+ rect2 = new Rectangle(line.specifiedWidth);
+
+ rect2.width ||= 1;
+ rect2.height ||= 1;
+
+ rect = rect.union(rect2);
+
+ rect.offset(line.x, line.y);
+
+ var container:ITextContainer = engine.layout.getContainerForLine(line);
+ rectToContainer[rect] = ensureLayerExists(container, TextDecor.SELECTION_LAYER);
+
+ return rect;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-decorations/src/org/tinytlf/decor/selection/StandardSelectionDecoration.as b/tinytlf-decorations/src/org/tinytlf/decor/selection/StandardSelectionDecoration.as
new file mode 100755
index 0000000..bc521e6
--- /dev/null
+++ b/tinytlf-decorations/src/org/tinytlf/decor/selection/StandardSelectionDecoration.as
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.decor.selection
+{
+ import flash.display.*;
+ import flash.geom.Rectangle;
+
+ public class StandardSelectionDecoration extends SelectionDecorationBase
+ {
+ public function StandardSelectionDecoration(styleObject:Object = null)
+ {
+ super(styleObject);
+ }
+
+ override public function draw(bounds:Vector.):void
+ {
+ super.draw(bounds);
+
+ var rect:Rectangle;
+ var copy:Vector. = bounds.concat();
+ var g:Graphics;
+ var color:uint;
+ var alpha:Number;
+ var layer:Sprite;
+
+ while (copy.length > 0)
+ {
+ rect = copy.pop();
+
+ layer = rectToLayer(rect);
+ if(!layer)
+ continue;
+
+ g = layer.graphics;
+
+ color = uint(getStyle("selectionColor")) || 0x0000CC;
+ alpha = Number(getStyle("selectionAlpha")) || 1;
+
+ g.beginFill(color, alpha);
+ g.drawRect(rect.x, rect.y, rect.width, rect.height);
+ g.endFill();
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceBehavior.as
new file mode 100644
index 0000000..4e6cc77
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceBehavior.as
@@ -0,0 +1,30 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+
+ import org.tinytlf.operations.*;
+
+ public class BackspaceBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function backspace():void
+ {
+ var op:CompositeOperation = new CompositeOperation();
+
+ if(!validSelection)
+ selection = new Point(--caret, caret + 1);
+ else
+ caret = selection.x;
+
+ op.add(
+ new TextRemoveOperation({start:selection.x, end:selection.y}),
+ new CaretMoveOperation({caret: caret}),
+ new TextSelectionOperation({selection: null})
+ );
+
+ op.runAtEnd(new InvalidateEngineOperation());
+
+ initAndExecute(push(op));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceCtrlBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceCtrlBehavior.as
new file mode 100644
index 0000000..0cf08b5
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/BackspaceCtrlBehavior.as
@@ -0,0 +1,31 @@
+package org.tinytlf.behaviors
+{
+ import org.tinytlf.operations.*;
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class BackspaceCtrlBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function backspace():void
+ {
+ var op:CompositeOperation = new CompositeOperation();
+
+ //Translate to the local atom index.
+ var atomIndex:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, caret);
+
+ //Get the previous word boundary
+ var start:int = TextLineUtil.getAtomWordBoundary(line, atomIndex);
+
+ op.add(
+ new TextRemoveOperation({start:start, end: caret}),
+ new CaretMoveOperation({caret: TinytlfUtil.atomIndexToGlobalIndex(engine, line, start)}),
+ new TextSelectionOperation({selection: null})
+ );
+
+ op.runAtEnd(new InvalidateEngineOperation());
+
+ initAndExecute(push(op));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/BoldBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/BoldBehavior.as
new file mode 100644
index 0000000..2dfb9dd
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/BoldBehavior.as
@@ -0,0 +1,15 @@
+package org.tinytlf.behaviors
+{
+ public class BoldBehavior extends OperationFactoryBehavior
+ {
+ public function BoldBehavior()
+ {
+ super();
+ }
+
+ [Event("keyDown")]
+ public function down():void
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/CopyBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/CopyBehavior.as
new file mode 100644
index 0000000..2e0ac94
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/CopyBehavior.as
@@ -0,0 +1,10 @@
+package org.tinytlf.behaviors
+{
+ public class CopyBehavior extends OperationFactoryBehavior
+ {
+ [Event("copy")]
+ public function copy():void
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/DeleteBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/DeleteBehavior.as
new file mode 100644
index 0000000..78b453a
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/DeleteBehavior.as
@@ -0,0 +1,28 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+
+ import org.tinytlf.operations.*;
+
+ public class DeleteBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function deleteChars():void
+ {
+ var op:CompositeOperation = new CompositeOperation();
+
+ if(!validSelection)
+ selection = new Point(caret, caret + 1);
+
+ op.add(
+ new TextRemoveOperation({start:selection.x, end:selection.y}),
+ new CaretMoveOperation({caret: selection.x + 1}),
+ new TextSelectionOperation({selection: null})
+ );
+
+ op.runAtEnd(new InvalidateEngineOperation());
+
+ initAndExecute(push(op));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/DownArrowBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/DownArrowBehavior.as
new file mode 100644
index 0000000..b3745c8
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/DownArrowBehavior.as
@@ -0,0 +1,43 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class DownArrowBehavior extends KeySelectionBehaviorBase
+ {
+ public function DownArrowBehavior()
+ {
+ super();
+ }
+
+
+ override protected function getAnchor():Point
+ {
+ var newCaret:int = caret;
+ var atomIndex:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, caret);
+ var bounds:Rectangle = TinytlfUtil.globalIndexToAtomBounds(engine, caret);
+
+ var l:TextLine = line;
+
+ if(l.nextLine)
+ {
+ l = l.nextLine;
+ var pt:Point = l.localToGlobal(new Point(bounds.x, 1));
+ newCaret = TinytlfUtil.atomIndexToGlobalIndex(
+ engine, l,
+ TextLineUtil.getAtomIndexAtPoint(l, pt)
+ );
+ }
+ else
+ {
+ newCaret = TinytlfUtil.atomIndexToGlobalIndex(engine, l, l.atomCount);
+ }
+
+ return new Point(newCaret, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/KeySelectionBehaviorBase.as b/tinytlf-edit/src/org/tinytlf/behaviors/KeySelectionBehaviorBase.as
new file mode 100644
index 0000000..7d223a3
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/KeySelectionBehaviorBase.as
@@ -0,0 +1,73 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.KeyboardEvent;
+ import flash.geom.Point;
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.analytics.IVirtualizer;
+
+ public class KeySelectionBehaviorBase extends SelectionBehaviorBase
+ {
+ public function KeySelectionBehaviorBase()
+ {
+ super();
+ }
+
+ [Event("keyDown")]
+ override public function downAction():void
+ {
+ var pt:Point = getSelection();
+
+ var k:KeyboardEvent = KeyboardEvent(finalEvent);
+ if(k.shiftKey)
+ engine.select(pt.x, pt.y);
+ else
+ engine.select();
+
+ var anchor:Point = getAnchor();
+ engine.caretIndex = anchor.x;
+
+ //assign focus to the proper line
+ assignFocus();
+ }
+
+ override protected function getSelection():Point
+ {
+ var pt:Point = selection;
+ var nextCaret:Point = getAnchor();
+
+ if(!validSelection)
+ pt = new Point(caret, caret);
+
+ if(caret <= pt.x)
+ pt.x = nextCaret.x;
+ else if(caret > pt.x)
+ pt.y = nextCaret.x;
+
+ return pt;
+ }
+
+ protected function assignFocus():void
+ {
+ var textBlockVirtualizer:IVirtualizer = engine.layout.textBlockVirtualizer;
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+
+ var caret:int = engine.caretIndex;
+
+ if(caret < 0)
+ caret = 0;
+ if(caret >= contentVirtualizer.size)
+ caret = contentVirtualizer.size - 1;
+
+ var block:TextBlock = contentVirtualizer.getItemFromPosition(caret);
+ var blockStart:int = textBlockVirtualizer.getItemStart(block);
+ var newLine:TextLine = block.getTextLineAtCharIndex(caret - blockStart);
+
+ if(newLine != line)
+ {
+ line.stage.focus = container.target;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowBehavior.as
new file mode 100644
index 0000000..fb2a377
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowBehavior.as
@@ -0,0 +1,29 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.KeyboardEvent;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ public class LeftArrowBehavior extends KeySelectionBehaviorBase
+ {
+ public function LeftArrowBehavior()
+ {
+ super();
+ }
+
+ override protected function getAnchor():Point
+ {
+ var k:KeyboardEvent = finalEvent as KeyboardEvent;
+ if(validSelection && !k.shiftKey)
+ {
+ if(caret > selection.x)
+ return new Point(selection.x, 0);
+
+ return new Point(caret, 0);
+ }
+
+ return new Point(caret - 1, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowCtrlBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowCtrlBehavior.as
new file mode 100644
index 0000000..7e50d5f
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/LeftArrowCtrlBehavior.as
@@ -0,0 +1,23 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class LeftArrowCtrlBehavior extends LeftArrowBehavior
+ {
+ public function LeftArrowCtrlBehavior()
+ {
+ super();
+ }
+
+ override protected function getAnchor():Point
+ {
+ var caretAtom:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, caret);
+ var newCaret:int = TextLineUtil.getAtomWordBoundary(line, caretAtom);
+ return new Point(TinytlfUtil.atomIndexToGlobalIndex(engine, line, newCaret), 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/OperationFactoryBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/OperationFactoryBehavior.as
new file mode 100644
index 0000000..f7ff181
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/OperationFactoryBehavior.as
@@ -0,0 +1,40 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.Event;
+
+ import org.tinytlf.conversion.IHTMLNode;
+ import org.tinytlf.interaction.IEditInteractor;
+ import org.tinytlf.operations.ITextOperation;
+
+ public class OperationFactoryBehavior extends MultiGestureBehavior
+ {
+ protected var model:IHTMLNode;
+ protected var interactor:IEditInteractor;
+
+ override protected function act(events:Vector.):void
+ {
+ model = engine.blockFactory.data as IHTMLNode;
+ interactor = engine.interactor as IEditInteractor;
+
+ if(!model || !interactor)
+ return;
+
+ super.act(events);
+ }
+
+ protected function push(op:ITextOperation):ITextOperation
+ {
+ return interactor.push(op);
+ }
+
+ protected function initAndExecute(...ops):void
+ {
+ ops.forEach(ieCallback);
+ }
+
+ private function ieCallback(op:ITextOperation, ...args):void
+ {
+ op.initialize(model).execute();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/RedoBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/RedoBehavior.as
new file mode 100644
index 0000000..71690b1
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/RedoBehavior.as
@@ -0,0 +1,11 @@
+package org.tinytlf.behaviors
+{
+ public class RedoBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function redo():void
+ {
+ interactor.redo().execute();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowBehavior.as
new file mode 100644
index 0000000..0ad1759
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowBehavior.as
@@ -0,0 +1,29 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.KeyboardEvent;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ public class RightArrowBehavior extends KeySelectionBehaviorBase
+ {
+ public function RightArrowBehavior()
+ {
+ super();
+ }
+
+ override protected function getAnchor():Point
+ {
+ var k:KeyboardEvent = finalEvent as KeyboardEvent;
+ if(validSelection && !k.shiftKey)
+ {
+ if(caret < selection.y)
+ return new Point(selection.y, 0);
+
+ return new Point(caret, 0);
+ }
+
+ return new Point(caret + 1, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowCtrlBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowCtrlBehavior.as
new file mode 100644
index 0000000..46d906a
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/RightArrowCtrlBehavior.as
@@ -0,0 +1,26 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class RightArrowCtrlBehavior extends RightArrowBehavior
+ {
+ public function RightArrowCtrlBehavior()
+ {
+ super();
+ }
+
+ override protected function getAnchor():Point
+ {
+ var caretAtom:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, caret - 1);
+ var newCaret:int = TextLineUtil.getAtomWordBoundary(line, caretAtom, false);
+
+ if(newCaret == caretAtom)
+ ++newCaret;
+
+ return new Point(TinytlfUtil.atomIndexToGlobalIndex(engine, line, newCaret), 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/ShiftEnterBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/ShiftEnterBehavior.as
new file mode 100644
index 0000000..9dbfb3b
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/ShiftEnterBehavior.as
@@ -0,0 +1,29 @@
+package org.tinytlf.behaviors
+{
+ import org.tinytlf.operations.*;
+
+ public class ShiftEnterBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function down():void
+ {
+ var op:CompositeOperation = new CompositeOperation();
+
+ if(validSelection)
+ {
+ op.add(
+ new TextRemoveOperation({start: selection.x, end: selection.y}),
+ new CaretMoveOperation({caret: selection.x + 1}),
+ new TextSelectionOperation({selection: null})
+ );
+ }
+
+ var index:int = 0;//model.getChildIndexAtPosition(caret);
+
+ op.add(new SplitParagraphOperation({caret: caret, nodeIndex: index}));
+ op.runAtEnd(new InvalidateEngineOperation());
+
+ initAndExecute(push(op));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/TextInsertBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/TextInsertBehavior.as
new file mode 100644
index 0000000..9b78092
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/TextInsertBehavior.as
@@ -0,0 +1,40 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.Event;
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.operations.*;
+
+ public class TextInsertBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function insert(events:Vector.):void
+ {
+ var maxCaret:Number = NaN;
+ var evt:KeyboardEvent = finalEvent as KeyboardEvent;
+ var chars:String = String.fromCharCode(evt.charCode);
+
+ var op:CompositeOperation = new CompositeOperation();
+
+ if(validSelection)
+ {
+ op.add(new TextRemoveOperation({start:selection.x, end:selection.y}));
+
+ if(caret == selection.y)
+ caret -= (selection.y - selection.x);
+
+// maxCaret = model.length - (selection.y - selection.x);
+ }
+
+ op.add(
+ new TextInsertOperation({start:caret, value:chars, end:caret + 1}),
+ new CaretMoveOperation({caret: ++caret, maxCaret: maxCaret}),
+ new TextSelectionOperation({selection: null})
+ );
+
+ op.runAtEnd(new InvalidateEngineOperation());
+
+ initAndExecute(push(op));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/UndoBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/UndoBehavior.as
new file mode 100644
index 0000000..b390c3e
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/UndoBehavior.as
@@ -0,0 +1,11 @@
+package org.tinytlf.behaviors
+{
+ public class UndoBehavior extends OperationFactoryBehavior
+ {
+ [Event("keyDown")]
+ public function undo():void
+ {
+ interactor.undo().backout();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/behaviors/UpArrowBehavior.as b/tinytlf-edit/src/org/tinytlf/behaviors/UpArrowBehavior.as
new file mode 100644
index 0000000..e52da30
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/behaviors/UpArrowBehavior.as
@@ -0,0 +1,46 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.KeyboardEvent;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class UpArrowBehavior extends KeySelectionBehaviorBase
+ {
+ public function UpArrowBehavior()
+ {
+ super();
+ }
+
+ override protected function getAnchor():Point
+ {
+ var newCaret:int = caret;
+ var atomIndex:int = TinytlfUtil.globalIndexToAtomIndex(engine, line, caret);
+ var bounds:Rectangle = TinytlfUtil.globalIndexToAtomBounds(engine, caret);
+
+ if(!bounds)
+ return null;
+
+ var l:TextLine = line;
+
+ if(l.previousLine)
+ {
+ l = l.previousLine;
+ var pt:Point = l.localToGlobal(new Point(bounds.x, 1));
+ newCaret = TinytlfUtil.atomIndexToGlobalIndex(
+ engine, l,
+ TextLineUtil.getAtomIndexAtPoint(l, pt)
+ );
+ }
+ else
+ {
+ newCaret = TinytlfUtil.atomIndexToGlobalIndex(engine, l, -1);
+ }
+
+ return new Point(newCaret, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/conversion/XMLEditableBlockFactory.as b/tinytlf-edit/src/org/tinytlf/conversion/XMLEditableBlockFactory.as
new file mode 100644
index 0000000..8ddddbc
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/conversion/XMLEditableBlockFactory.as
@@ -0,0 +1,16 @@
+package org.tinytlf.conversion
+{
+ public class XMLEditableBlockFactory extends HTMLBlockFactory
+ {
+ public function XMLEditableBlockFactory()
+ {
+ super();
+ }
+
+ override public function preRender():void
+ {
+ // TODO: This is where I should re-validate any nodes that have been
+ // modified by editing.
+ }
+ }
+}
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/BackspaceCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/BackspaceCtrlGesture.as
new file mode 100755
index 0000000..75705bd
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/BackspaceCtrlGesture.as
@@ -0,0 +1,17 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+
+ public class BackspaceCtrlGesture extends BackspaceGesture
+ {
+ override public function backspace(event:KeyboardEvent):Boolean
+ {
+ var ctrlKey:Boolean = TinytlfUtil.isMac() ? event.altKey : event.ctrlKey;
+ return super.backspace(event) && ctrlKey;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/BackspaceGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/BackspaceGesture.as
new file mode 100755
index 0000000..74621ca
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/BackspaceGesture.as
@@ -0,0 +1,23 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+
+ [Event("keyDown")]
+
+ public class BackspaceGesture extends Gesture
+ {
+ public function BackspaceGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function backspace(event:KeyboardEvent):Boolean
+ {
+ return event.keyCode == Keyboard.BACKSPACE;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/CommandBGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/CommandBGesture.as
new file mode 100644
index 0000000..436c8ee
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/CommandBGesture.as
@@ -0,0 +1,22 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ [Event("keyDown")]
+ public class CommandBGesture extends Gesture
+ {
+ public function CommandBGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function b(event:KeyboardEvent):Boolean
+ {
+ var char:String = String.fromCharCode(event.charCode);
+ var result:Boolean = (char == 'b' || char == 'B') && event.charCode && event.ctrlKey;
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/DeleteCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/DeleteCtrlGesture.as
new file mode 100755
index 0000000..6ddc4ed
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/DeleteCtrlGesture.as
@@ -0,0 +1,13 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ [Event("keyDown")]
+ public class DeleteCtrlGesture extends DeleteGesture
+ {
+ override public function deleteKey(event:KeyboardEvent):Boolean
+ {
+ return super.deleteKey(event) && event.ctrlKey;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/DeleteGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/DeleteGesture.as
new file mode 100755
index 0000000..d39313a
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/DeleteGesture.as
@@ -0,0 +1,23 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+
+ [Event("keyDown")]
+
+ public class DeleteGesture extends Gesture
+ {
+ public function DeleteGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function deleteKey(event:KeyboardEvent):Boolean
+ {
+ return event.keyCode == Keyboard.DELETE;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/DownArrowCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/DownArrowCtrlGesture.as
new file mode 100755
index 0000000..398bde1
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/DownArrowCtrlGesture.as
@@ -0,0 +1,16 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+
+ public class DownArrowCtrlGesture extends DownArrowGesture
+ {
+ override public function down(event:KeyboardEvent):Boolean
+ {
+ return super.down(event) && (TinytlfUtil.isMac() ? event.altKey : event.ctrlKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/DownArrowGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/DownArrowGesture.as
new file mode 100755
index 0000000..c0dbefa
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/DownArrowGesture.as
@@ -0,0 +1,22 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+ [Event("keyDown")]
+
+ public class DownArrowGesture extends Gesture
+ {
+ public function DownArrowGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function down(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode === Keyboard.DOWN);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowCtrlGesture.as
new file mode 100755
index 0000000..6d0c0dd
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowCtrlGesture.as
@@ -0,0 +1,16 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+
+ public class LeftArrowCtrlGesture extends LeftArrowGesture
+ {
+ override public function left(event:KeyboardEvent):Boolean
+ {
+ return super.left(event) && (TinytlfUtil.isMac() ? event.altKey : event.ctrlKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowGesture.as
new file mode 100755
index 0000000..6f90220
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/LeftArrowGesture.as
@@ -0,0 +1,27 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+ [Event("keyDown")]
+ public class LeftArrowGesture extends Gesture
+ {
+ public function LeftArrowGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ hsm.appendChild();
+ }
+
+ public function shift(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode == Keyboard.SHIFT && event.shiftKey);
+ }
+
+ public function left(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode === Keyboard.LEFT);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/RedoGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/RedoGesture.as
new file mode 100644
index 0000000..1d732d2
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/RedoGesture.as
@@ -0,0 +1,27 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+ public class RedoGesture extends Gesture
+ {
+ public function RedoGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function redo(event:KeyboardEvent):Boolean
+ {
+ var char:String = String.fromCharCode(event.charCode);
+
+ if(TinytlfUtil.isMac())
+ return char === 'z' && event.shiftKey && event.ctrlKey;
+
+ return char === 'y' && event.ctrlKey;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/RightArrowCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/RightArrowCtrlGesture.as
new file mode 100755
index 0000000..b8e3954
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/RightArrowCtrlGesture.as
@@ -0,0 +1,16 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+
+ public class RightArrowCtrlGesture extends RightArrowGesture
+ {
+ override public function right(event:KeyboardEvent):Boolean
+ {
+ return super.right(event) && (TinytlfUtil.isMac() ? event.altKey : event.ctrlKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/RightArrowGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/RightArrowGesture.as
new file mode 100755
index 0000000..36b23a2
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/RightArrowGesture.as
@@ -0,0 +1,29 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+
+ [Event("keyDown")]
+
+ public class RightArrowGesture extends Gesture
+ {
+ public function RightArrowGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ hsm.appendChild();
+ }
+
+ public function shift(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode == Keyboard.SHIFT && event.shiftKey);
+ }
+
+ public function right(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode === Keyboard.RIGHT);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/ShiftEnterGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/ShiftEnterGesture.as
new file mode 100644
index 0000000..be8e49e
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/ShiftEnterGesture.as
@@ -0,0 +1,21 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+ [Event("keyDown")]
+ public class ShiftEnterGesture extends Gesture
+ {
+ public function ShiftEnterGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function enter(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode == Keyboard.ENTER && event.shiftKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/TextEntryGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/TextEntryGesture.as
new file mode 100644
index 0000000..64b9026
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/TextEntryGesture.as
@@ -0,0 +1,28 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+ [Event("keyDown")]
+ public class TextEntryGesture extends Gesture
+ {
+ public function TextEntryGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function down(event:KeyboardEvent):Boolean
+ {
+ var char:String = String.fromCharCode(event.charCode);
+ var result:Boolean = char != '' && event.charCode;
+ result &&= !(event.keyCode == Keyboard.ENTER && event.shiftKey);
+ result &&= !event.ctrlKey;
+ result &&= !(event.keyCode == Keyboard.DELETE);
+ result &&= !(event.keyCode == Keyboard.BACKSPACE);
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/UndoGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/UndoGesture.as
new file mode 100644
index 0000000..313fc34
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/UndoGesture.as
@@ -0,0 +1,27 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+ public class UndoGesture extends Gesture
+ {
+ public function UndoGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function undo(event:KeyboardEvent):Boolean
+ {
+ var result:Boolean = event.ctrlKey && (event.charCode == 122); //small 'z'
+
+ if(TinytlfUtil.isMac())
+ result &&= !event.shiftKey;
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/UpArrowCtrlGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/UpArrowCtrlGesture.as
new file mode 100755
index 0000000..99cb94b
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/UpArrowCtrlGesture.as
@@ -0,0 +1,16 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ [Event("keyDown")]
+
+ public class UpArrowCtrlGesture extends UpArrowGesture
+ {
+ override public function up(event:KeyboardEvent):Boolean
+ {
+ return super.up(event) && (TinytlfUtil.isMac() ? event.altKey : event.ctrlKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/gestures/UpArrowGesture.as b/tinytlf-edit/src/org/tinytlf/gestures/UpArrowGesture.as
new file mode 100755
index 0000000..3d0be2d
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/gestures/UpArrowGesture.as
@@ -0,0 +1,23 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+
+ [Event("keyDown")]
+
+ public class UpArrowGesture extends Gesture
+ {
+ public function UpArrowGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function up(event:KeyboardEvent):Boolean
+ {
+ return (event.keyCode === Keyboard.UP);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/interaction/EditInteractor.as b/tinytlf-edit/src/org/tinytlf/interaction/EditInteractor.as
new file mode 100644
index 0000000..4df5f62
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/interaction/EditInteractor.as
@@ -0,0 +1,89 @@
+package org.tinytlf.interaction
+{
+ import flash.events.EventDispatcher;
+ import flash.text.engine.TextLine;
+ import flash.ui.*;
+
+ import org.tinytlf.operations.ITextOperation;
+
+ public class EditInteractor extends GestureInteractor implements IEditInteractor
+ {
+ public function EditInteractor()
+ {
+ super();
+
+ stack = new [];
+ }
+
+ override public function getMirror(element:* = null):EventDispatcher
+ {
+ if(element is TextLine)
+ {
+ var line:TextLine = element as TextLine;
+ line.parent.contextMenu ||= new ContextMenu();
+
+ var menu:ContextMenu = line.parent.contextMenu;
+ menu.clipboardMenu = true;
+
+ var items:ContextMenuClipboardItems = menu.clipboardItems;
+ items.clear = true;
+ items.copy = true;
+ items.cut = true;
+ items.paste = true;
+ items.selectAll = true;
+ }
+
+ return super.getMirror(element);
+ }
+
+ private var stack:Vector.;
+ private var pointer:int = -1;
+
+ public function push(op:ITextOperation):ITextOperation
+ {
+ if(pointer != stack.length - 1)
+ stack.splice(pointer, stack.length - pointer);
+
+ stack.push(op);
+ pointer = stack.length;
+
+ return op;
+ }
+
+ public function undo():ITextOperation
+ {
+ if(pointer > 0)
+ return stack[--pointer];
+
+ return new NullOperation();
+ }
+
+ public function redo():ITextOperation
+ {
+ if(pointer < stack.length)
+ return stack[pointer++];
+
+ return new NullOperation();
+ }
+
+ public function clearOperations(num:int = -1):void
+ {
+ if(num <= -1)
+ stack.length = 0;
+ else
+ stack.splice(stack.length - num, num);
+
+ pointer = stack.length - 1;
+ }
+ }
+}
+import org.tinytlf.conversion.IHTMLNode;
+import org.tinytlf.operations.ITextOperation;
+
+internal class NullOperation implements ITextOperation
+{
+ public function initialize(model:IHTMLNode):ITextOperation{return this;}
+ public function execute():void{}
+ public function backout():void{}
+ public function merge(op:ITextOperation):void{}
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/interaction/IEditInteractor.as b/tinytlf-edit/src/org/tinytlf/interaction/IEditInteractor.as
new file mode 100644
index 0000000..2e45c66
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/interaction/IEditInteractor.as
@@ -0,0 +1,39 @@
+package org.tinytlf.interaction
+{
+ import org.tinytlf.operations.ITextOperation;
+
+ public interface IEditInteractor extends ITextInteractor
+ {
+ /**
+ * Pushes an operation onto the operation stack. Does not execute the
+ * operation.
+ *
+ * @returns The ITextOperation that was passed in.
+ */
+ function push(op:ITextOperation):ITextOperation;
+
+ /**
+ * Decrements the operation stack iterator to the previous operation,
+ * if it exists. This does not execute the operation.
+ *
+ * @returns The previous operation in the operation stack, or a
+ * NullOperation implementation if there is no previous operation.
+ */
+ function undo():ITextOperation;
+
+ /**
+ * Increments the operation stack pointer to the next operation, if it
+ * exists. This does not execute the operation.
+ *
+ * @returns The next operation in the operation stack, or a
+ * NullOperation implementation if there is no next operation.
+ */
+ function redo():ITextOperation;
+
+ /**
+ * Clears operations off the stack, starting with the most recently
+ * added.
+ */
+ function clearOperations(num:int = -1):void;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/CaretMoveOperation.as b/tinytlf-edit/src/org/tinytlf/operations/CaretMoveOperation.as
new file mode 100644
index 0000000..f8dabdf
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/CaretMoveOperation.as
@@ -0,0 +1,33 @@
+package org.tinytlf.operations
+{
+ import org.tinytlf.ITextEngine;
+
+ public class CaretMoveOperation extends TextOperation
+ {
+ public function CaretMoveOperation(props:Object=null)
+ {
+ super(props);
+ }
+
+ public var caret:int;
+ public var maxCaret:Number = NaN;
+
+ private var start:int;
+
+ override public function execute():void
+ {
+ start = engine.caretIndex;
+
+ if(maxCaret == maxCaret)
+ caret = Math.min(maxCaret, caret);
+
+ engine.caretIndex = caret;
+ }
+
+ override public function backout():void
+ {
+ caret = engine.caretIndex;
+ engine.caretIndex = start;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/CompositeOperation.as b/tinytlf-edit/src/org/tinytlf/operations/CompositeOperation.as
new file mode 100644
index 0000000..01c3e62
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/CompositeOperation.as
@@ -0,0 +1,74 @@
+package org.tinytlf.operations
+{
+ import org.tinytlf.conversion.IHTMLNode;
+
+ public class CompositeOperation extends TextOperation
+ {
+ public function CompositeOperation(props:Object = null)
+ {
+ super(props);
+ }
+
+ private var operations:Vector. = new [];
+ public function add(...ops):void
+ {
+ operations = operations.concat(Vector.(ops));
+ }
+
+ private var enders:Vector. = new [];
+ public function runAtEnd(...ops):void
+ {
+ enders = enders.concat(Vector.(ops));
+ }
+
+ override public function initialize(model:IHTMLNode):ITextOperation
+ {
+ super.initialize(model);
+
+ operations.forEach(initCallback);
+ enders.forEach(initCallback);
+
+ return this;
+ }
+
+ override public function execute():void
+ {
+ operations.forEach(eCallback);
+ enders.forEach(eCallback);
+ }
+
+ override public function backout():void
+ {
+ operations.concat().reverse().forEach(bCallback);
+ enders.forEach(bCallback);
+ }
+
+ override public function merge(op:ITextOperation):void
+ {
+ super.merge(op);
+
+ var composite:CompositeOperation = CompositeOperation(op);
+ composite.operations.forEach(mCallback, this);
+ }
+
+ private function initCallback(op:ITextOperation, ...args):void
+ {
+ op.initialize(model)
+ }
+
+ private function eCallback(op:ITextOperation, ...args):void
+ {
+ op.execute();
+ }
+
+ private function bCallback(op:ITextOperation, ...args):void
+ {
+ op.backout();
+ }
+
+ private function mCallback(op:ITextOperation, index:int, ...args):void
+ {
+ operations[index].merge(op);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/ITextOperation.as b/tinytlf-edit/src/org/tinytlf/operations/ITextOperation.as
new file mode 100644
index 0000000..5cac115
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/ITextOperation.as
@@ -0,0 +1,12 @@
+package org.tinytlf.operations
+{
+ import org.tinytlf.conversion.IHTMLNode;
+
+ public interface ITextOperation
+ {
+ function initialize(model:IHTMLNode):ITextOperation;
+ function execute():void;
+ function backout():void;
+ function merge(op:ITextOperation):void;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/InvalidateEngineOperation.as b/tinytlf-edit/src/org/tinytlf/operations/InvalidateEngineOperation.as
new file mode 100644
index 0000000..c0dce19
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/InvalidateEngineOperation.as
@@ -0,0 +1,20 @@
+package org.tinytlf.operations
+{
+ public class InvalidateEngineOperation extends TextOperation
+ {
+ public function InvalidateEngineOperation(props:Object=null)
+ {
+ super(props);
+ }
+
+ override public function execute():void
+ {
+ engine.invalidate();
+ }
+
+ override public function backout():void
+ {
+ engine.invalidate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/SplitParagraphOperation.as b/tinytlf-edit/src/org/tinytlf/operations/SplitParagraphOperation.as
new file mode 100644
index 0000000..d01b6a1
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/SplitParagraphOperation.as
@@ -0,0 +1,31 @@
+package org.tinytlf.operations
+{
+
+ public class SplitParagraphOperation extends TextOperation
+ {
+ public function SplitParagraphOperation(props:Object = null)
+ {
+ super(props);
+ }
+
+ public var caret:int;
+ public var nodeIndex:int;
+
+ override public function execute():void
+ {
+// var root:ITLFNodeParent = ITLFNodeParent(model);
+// nodeIndex = root.getChildIndexAtPosition(caret);
+// var child:ITLFNode = root.getChildAt(nodeIndex);
+// child.split(caret - root.getChildPosition(nodeIndex));
+// engine.analytics.addBlockAt(new TextBlock(), nodeIndex + 1, 0);
+ }
+
+ override public function backout():void
+ {
+// engine.analytics.removeBlockAt(nodeIndex + 1);
+// var root:ITLFNodeParent = ITLFNodeParent(model);
+// var pos:int = root.getChildPosition(nodeIndex);
+// root.merge(pos, pos + 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/TextInsertOperation.as b/tinytlf-edit/src/org/tinytlf/operations/TextInsertOperation.as
new file mode 100644
index 0000000..85687cb
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/TextInsertOperation.as
@@ -0,0 +1,43 @@
+package org.tinytlf.operations
+{
+
+
+ public class TextInsertOperation extends TextOperation implements ITextOperation
+ {
+ public function TextInsertOperation(props:Object)
+ {
+ super(props);
+ }
+
+ public var start:int;
+ public var end:int;
+ public var value:String;
+
+ override public function execute():void
+ {
+// model.insert(value, start);
+ }
+
+ override public function backout():void
+ {
+// model.remove(start, end);
+ }
+
+ override public function merge(op:ITextOperation):void
+ {
+ super.merge(op);
+
+ var obj:TextInsertOperation = TextInsertOperation(op);
+ if(obj.start < start)
+ {
+ value = obj.value + value;
+ start = obj.start;
+ }
+ else if(obj.start >= start)
+ {
+ value += obj.value;
+ end = obj.end;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/TextOperation.as b/tinytlf-edit/src/org/tinytlf/operations/TextOperation.as
new file mode 100644
index 0000000..7414215
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/TextOperation.as
@@ -0,0 +1,38 @@
+package org.tinytlf.operations
+{
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.conversion.IHTMLNode;
+ import org.tinytlf.styles.StyleAwareActor;
+
+ public class TextOperation extends StyleAwareActor implements ITextOperation
+ {
+ public function TextOperation(props:Object = null)
+ {
+ super(props);
+ }
+
+ protected var model:IHTMLNode;
+ protected var engine:ITextEngine;
+
+ public function initialize(model:IHTMLNode):ITextOperation
+ {
+ this.model = model;
+// this.engine = model.engine;
+ return this;
+ }
+
+ public function execute():void
+ {
+ }
+
+ public function backout():void
+ {
+ }
+
+ public function merge(op:ITextOperation):void
+ {
+ if(!(op is (this['constructor'] as Class)))
+ throw new ArgumentError('Cannot merge operations of different types.');
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/TextRemoveOperation.as b/tinytlf-edit/src/org/tinytlf/operations/TextRemoveOperation.as
new file mode 100644
index 0000000..7282ff9
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/TextRemoveOperation.as
@@ -0,0 +1,25 @@
+package org.tinytlf.operations
+{
+ public class TextRemoveOperation extends TextOperation implements ITextOperation
+ {
+ public function TextRemoveOperation(props:Object)
+ {
+ super(props);
+ }
+
+ public var start:int;
+ public var end:int;
+// private var value:ITLFNode;
+
+ override public function execute():void
+ {
+// value = model.clone(start, end);
+// model.remove(start, end)
+ }
+
+ override public function backout():void
+ {
+// model.insert(value.text, start);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-edit/src/org/tinytlf/operations/TextSelectionOperation.as b/tinytlf-edit/src/org/tinytlf/operations/TextSelectionOperation.as
new file mode 100644
index 0000000..87171ad
--- /dev/null
+++ b/tinytlf-edit/src/org/tinytlf/operations/TextSelectionOperation.as
@@ -0,0 +1,29 @@
+package org.tinytlf.operations
+{
+ import flash.geom.Point;
+
+ import org.tinytlf.ITextEngine;
+
+ public class TextSelectionOperation extends TextOperation
+ {
+ public function TextSelectionOperation(props:Object=null)
+ {
+ super(props);
+ }
+
+ public var selection:Point;
+ private var start:Point;
+
+ override public function execute():void
+ {
+ start = engine.selection.clone();
+ engine.select(selection ? selection.x : NaN, selection ? selection.y : NaN);
+ }
+
+ override public function backout():void
+ {
+ selection = engine.selection.clone();
+ engine.select(start ? start.x : NaN, start ? start.y : NaN);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-fte-utils/src/org/tinytlf/util/fte/ContentElementUtil.as b/tinytlf-fte-utils/src/org/tinytlf/util/fte/ContentElementUtil.as
new file mode 100755
index 0000000..7176639
--- /dev/null
+++ b/tinytlf-fte-utils/src/org/tinytlf/util/fte/ContentElementUtil.as
@@ -0,0 +1,269 @@
+package org.tinytlf.util.fte
+{
+ import flash.display.Bitmap;
+ import flash.display.Shape;
+ import flash.geom.Rectangle;
+ import flash.text.engine.*;
+
+ public final class ContentElementUtil
+ {
+ /**
+ * Returns all the lines that render the supplied ContentElement.
+ */
+ public static function getTextLines(element:ContentElement):Vector.
+ {
+ var lines:Vector. = new Vector.();
+ var block:TextBlock = element.textBlock;
+
+ if(!block)
+ return lines;
+
+ var endIndex:int = element.textBlockBeginIndex + element.rawText.length;
+ var line:TextLine = block.getTextLineAtCharIndex(element.textBlockBeginIndex);
+
+ while(line && line.textBlockBeginIndex < endIndex)
+ {
+ lines.push(line);
+ line = line.nextLine;
+ }
+
+ return lines;
+ }
+
+ /**
+ * Returns a Vector of TextLineMirrorRegions for the ContentElement.
+ */
+ public static function getMirrorRegions(element:ContentElement):Vector.
+ {
+ var lines:Vector. = getTextLines(element);
+ var line:TextLine;
+
+ var regions:Vector. = new Vector.();
+ var tlmrs:Vector.;
+ var tlmr:TextLineMirrorRegion;
+
+ while(lines.length)
+ {
+ line = lines.pop();
+ tlmrs = line.mirrorRegions;
+
+ if(line.validity != TextLineValidity.VALID || !tlmrs)
+ continue;
+
+ tlmrs = tlmrs.concat();
+
+ while(tlmrs.length)
+ {
+ tlmr = tlmrs.pop();
+ if(tlmr.mirror === element.eventMirror)
+ regions.push(tlmr);
+ }
+ }
+
+ return regions;
+ }
+
+ /**
+ * Returns a Vector of Rectangles that represent the area in which the
+ * ContentElement exists on the Stage. This will only return properly
+ * if the ContentElement has an eventMirror set. If the ContentElement
+ * has no eventMirror, FTE doesn't create TextLineMirrorRegions, and
+ * it's impossible to determine the boundaries of the ContentElement.
+ */
+ public static function getBounds(element:ContentElement):Vector.
+ {
+ var regions:Vector. = getMirrorRegions(element);
+ var bounds:Vector. = new Vector.();
+ var tlmr:TextLineMirrorRegion;
+ var rect:Rectangle;
+
+ while(regions.length)
+ {
+ tlmr = regions.pop();
+ rect = tlmr.bounds.clone();
+ rect.offset(tlmr.textLine.x, tlmr.textLine.y);
+ bounds.push(rect);
+ }
+
+ return bounds;
+ }
+
+ public static function attachLineBreak(element:ContentElement):GroupElement
+ {
+ var graphic:GraphicElement = getLineBreakGraphic('lineBreak', 0xFF0000);
+ return new GroupElement(new [element, graphic], new ElementFormat());
+ }
+
+ /**
+ * Creates a GroupElement with two children, a place-holder GraphicElement
+ * and the input ContentElement. The GroupElement has an elementFormat
+ * with breakOpportunity set to "all," which tells the FTE to break
+ * the line between the GroupElement's children, i.e. between the
+ * placeholder GraphicElement and the input ContentElement.
+ *
+ *
The optional marker parameter is set as the userData property of
+ * the dummy GraphicElement. This allows you to mark/differentiate this
+ * graphic during layout.
+ */
+ public static function lineBreakBefore(element:ContentElement, marker:Object = null):GroupElement
+ {
+ var graphic:GraphicElement = getLineBreakGraphic(marker, 0xFF0000);
+ return new GroupElement(new [graphic, element], breakAllEF);
+ }
+
+ /**
+ * Creates a GroupElement which has a line break after the input
+ * ContentElement.
+ */
+ public static function lineBreakAfter(element:ContentElement, marker:Object = null):GroupElement
+ {
+ var graphic:GraphicElement = getLineBreakGraphic(marker, 0x0000FF);
+ return new GroupElement(new [element, graphic], breakAllEF);
+ }
+
+ /**
+ * Creates a GroupElement which has line breaks before and after the
+ * input ContentElement.
+ */
+ public static function lineBreakBeforeAndAfter(element:ContentElement,
+ markerLeft:Object = null,
+ markerRight:Object = null):GroupElement
+ {
+ var start:GraphicElement = getLineBreakGraphic(markerLeft, 0xFF0000);
+ var end:GraphicElement = getLineBreakGraphic(markerRight, 0x0000FF);
+ return new GroupElement(new [start, element, end], breakAllEF);
+ }
+
+ private static function get breakAllEF():ElementFormat
+ {
+ var ef:ElementFormat = new ElementFormat();
+ ef.breakOpportunity = BreakOpportunity.ALL;
+ return ef;
+ }
+
+ private static function getLineBreakGraphic(marker:Object = null, color:uint = 0xFFFFFF):GraphicElement
+ {
+ var ef:ElementFormat = new ElementFormat();
+ ef.dominantBaseline = TextBaseline.IDEOGRAPHIC_TOP;
+
+ var s:Shape = new Shape();
+// s.graphics.beginFill(color);
+// s.graphics.drawRect(0, 0, 10, 2);
+// s.graphics.endFill();
+
+ var g:GraphicElement = new GraphicElement(s, 0, 0, ef);
+ g.userData = marker;
+
+ return g;
+ }
+
+ public static function dumpElement(e:ContentElement, depth:int = 0):String
+ {
+ var str:String = '';
+ var tabs:String = '';
+ var j:int = depth;
+
+ while(j-- > 0)
+ {
+ tabs += '\t';
+ }
+ str += tabs;
+
+ if(e is GroupElement)
+ {
+ ++depth;
+
+ str += 'GroupElement(\n';
+ var n:int = GroupElement(e).elementCount;
+ for(var i:int = 0; i < n; ++i)
+ {
+ str += dumpElement(GroupElement(e).getElementAt(i), depth);
+ }
+ str += tabs + ')';
+ }
+ else if(e is TextElement)
+ {
+ str += 'TextElement("' + TextElement(e).text + '")';
+ }
+ else if(e is GraphicElement)
+ {
+ str += 'GraphicElement()';
+ }
+
+ return str + '\n';
+ }
+
+ public static function addChild(parent:ContentElement, child:ContentElement):ContentElement
+ {
+ if(!(parent is GroupElement))
+ return child;
+
+ var group:GroupElement = GroupElement(parent);
+ return addChildAt(group, child, group.elementCount);
+ }
+
+ public static function addChildAt(parent:ContentElement, child:ContentElement, index:int):ContentElement
+ {
+ if(!(parent is GroupElement))
+ return child;
+
+ var group:GroupElement = GroupElement(parent);
+ var elements:Vector. = getChildren(group);
+
+ if(elements.indexOf(child) != -1)
+ removeChild(group, child);
+
+ elements.splice(index, 0, child);
+ group.setElements(elements);
+
+ return child;
+ }
+
+ public static function removeChild(parent:ContentElement, child:ContentElement):ContentElement
+ {
+ if(!(parent is GroupElement))
+ return child;
+
+ var group:GroupElement = GroupElement(parent);
+ return removeChildAt(group, group.getElementIndex(child));
+ }
+
+ public static function removeChildAt(parent:ContentElement, index:int):ContentElement
+ {
+ if(!(parent is GroupElement))
+ return null;
+
+ var group:GroupElement = GroupElement(parent);
+ var child:ContentElement = group.getElementAt(index);
+ group.replaceElements(index, index + 1, null);
+
+ return child;
+ }
+
+ public static function removeChildren(parent:ContentElement):Vector.
+ {
+ if(!(parent is GroupElement))
+ return null;
+
+ var group:GroupElement = GroupElement(parent);
+ var children:Vector. = new [];
+
+ while(group.elementCount)
+ children.push(removeChildAt(group, group.elementCount));
+
+ return children;
+ }
+
+ public static function getChildren(group:GroupElement):Vector.
+ {
+ var n:int = group.elementCount;
+ var elements:Vector. = new [];
+ for(var i:int = 0; i < n; i += 1)
+ {
+ elements.push(group.getElementAt(i));
+ }
+
+ return elements;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextBlockUtil.as b/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextBlockUtil.as
new file mode 100644
index 0000000..749a1c3
--- /dev/null
+++ b/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextBlockUtil.as
@@ -0,0 +1,37 @@
+package org.tinytlf.util.fte
+{
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLineCreationResult;
+
+ public final class TextBlockUtil
+ {
+ public static function isInvalid(block:TextBlock):Boolean
+ {
+ return block.firstLine == null ||
+ block.firstInvalidLine ||
+ block.textLineCreationResult != TextLineCreationResult.COMPLETE;
+ }
+
+ public static function cleanBlock(block:TextBlock):void
+ {
+ if(block.firstLine)
+ block.releaseLines(block.firstLine, block.lastLine);
+
+ block.releaseLineCreationData();
+ block.content = null;
+ block.userData = null;
+ }
+
+ private static const blocks:Vector. = new [];
+ public static function checkIn(block:TextBlock):void
+ {
+ cleanBlock(block);
+ blocks.push(block);
+ }
+
+ public static function checkOut():TextBlock
+ {
+ return blocks.pop() || new TextBlock();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextLineUtil.as b/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextLineUtil.as
new file mode 100755
index 0000000..e5d4971
--- /dev/null
+++ b/tinytlf-fte-utils/src/org/tinytlf/util/fte/TextLineUtil.as
@@ -0,0 +1,215 @@
+package org.tinytlf.util.fte
+{
+ import flash.display.DisplayObject;
+ import flash.geom.*;
+ import flash.text.engine.*;
+ import flash.utils.Dictionary;
+
+ public final class TextLineUtil
+ {
+ /**
+ * Returns the index of the atom at a particular point. If the point
+ * is outside the boundaries of the line, this determines which side the
+ * point is on, and returns 0 or line.atomCount.
+ */
+ public static function getAtomIndexAtPoint(line:TextLine, stageCoords:Point):int
+ {
+ var index:int = line.getAtomIndexAtPoint(stageCoords.x, stageCoords.y);
+
+ if(index < 0)
+ {
+ var bounds:Rectangle = line.getBounds(line.stage);
+ var center:Point = bounds.topLeft.clone();
+ center.offset(bounds.width * .5, bounds.height * .5);
+
+ if(stageCoords.y < bounds.y)
+ return 0;
+ if(stageCoords.y > bounds.y &&
+ stageCoords.y < bounds.y + bounds.height)
+ return line.atomCount;
+
+ index = (stageCoords.x < center.x) ? 0 : line.atomCount;
+ }
+
+ var atomIncrement:int = getAtomSide(line, stageCoords) ? 0 : 1;
+
+ return Math.max(index + atomIncrement, 0);
+ }
+
+ /**
+ * Finds which side of the atom the point is on.
+ * @returns true for left, false for right.
+ */
+ public static function getAtomSide(line:TextLine, stageCoords:Point):Boolean
+ {
+ var atomIndex:int = line.getAtomIndexAtPoint(stageCoords.x, stageCoords.y);
+
+ if(atomIndex < 0)
+ return true;
+
+ var center:Number = line.getAtomCenter(atomIndex);
+ var pt:Point = line.localToGlobal(new Point(center));
+
+ return pt.x > stageCoords.x;
+ }
+
+ private static const defaultWordBoundaryPattern:RegExp = /\W+|\b[^\Wï·¯]*/;
+ private static const nonWordPattern:RegExp = /\W/;
+
+ /**
+ * Finds the next/prev word boundary specified by the direction and the
+ * boundaryPattern. If no boundary pattern is specified, the default
+ * is used, which matches non-word characters or graphic characters.
+ */
+ public static function getAtomWordBoundary(line:TextLine, atomIndex:int,
+ left:Boolean = true, boundaryPattern:RegExp = null):int
+ {
+ if(!boundaryPattern)
+ boundaryPattern = defaultWordBoundaryPattern;
+
+ if(atomIndex >= line.atomCount)
+ atomIndex = line.atomCount - 1;
+ else if(atomIndex < 0)
+ atomIndex = 0;
+
+ var rawText:String = line.textBlock.content.rawText;
+ var adjustedIndex:int = line.getAtomTextBlockBeginIndex(atomIndex);
+
+ // If the index is already at a word boundary,
+ // move to find the next word boundary.
+ while(nonWordPattern.test(rawText.charAt(adjustedIndex)))
+ {
+ adjustedIndex += left ? -1 : 1;
+ atomIndex += left ? -1 : 1;
+ }
+
+ var text:String = left ?
+ rawText.slice(0, adjustedIndex).split("").reverse().join("") :
+ rawText.slice(adjustedIndex, rawText.length);
+
+ var match:Array = boundaryPattern.exec(text);
+ if(match)
+ {
+ var str:String = String(match[0]);
+ atomIndex += nonWordPattern.test(str) ? 0 : str.length * (left ? -1 : 1);
+ }
+
+ return Math.max(atomIndex, 0);
+ }
+
+ /**
+ * Recursively drills down into the ContentElement of the TextLine's
+ * TextBlock to return the leaf element at the specified atomIndex.
+ */
+ public static function getElementAtAtomIndex(line:TextLine, atomIndex:int):ContentElement
+ {
+ if(atomIndex < 0)
+ return null;
+
+ var block:TextBlock = line.textBlock;
+ var blockBeginIndex:int = line.textBlockBeginIndex;
+ var content:ContentElement = block.content;
+ var charIndex:int = blockBeginIndex - content.textBlockBeginIndex + atomIndex;
+
+ // If you recycle TextBlocks, funky things will happen.
+ // For example, the blockBeginIndex here will report a value of
+ // 0xFFFFFFFF, obviously erroneous. We have a second check here to
+ // stop this from throwing RTEs, but honestly, it's likely you'll
+ // just get an RTE somewhere else, because the returned
+ // contentElement will be a GroupElement here instead of a
+ // TextElement or GraphicElement like you were expecting.
+ // .poop.
+
+ while(content is GroupElement && charIndex < block.content.rawText.length)
+ {
+ content = GroupElement(content).getElementAtCharIndex(charIndex);
+ charIndex = blockBeginIndex - content.textBlockBeginIndex + atomIndex;
+ }
+
+ return content;
+ }
+
+ /**
+ * Returns a Vector of ContentElements which are rendered in this
+ * TextLine. This can only return the elements that have specified
+ * eventMirrors, so it's not guaranteed to be every ContentElement,
+ * and the elements won't necessarily be in order.
+ */
+ public static function getContentElements(line:TextLine):Vector.
+ {
+ var dict:Dictionary = new Dictionary();
+ var tlmrs:Vector. = line.mirrorRegions;
+
+ if(!tlmrs)
+ return elements;
+
+ var n:int = tlmrs.length;
+
+ for(var i:int = 0; i < n; ++i)
+ {
+ dict[tlmrs[i].element] = true;
+ }
+
+ var elements:Vector. = new [];
+ for(var element:* in dict)
+ {
+ elements.push(ContentElement(element));
+ }
+
+ return elements;
+ }
+
+ public static function getMirrorRegionForElement(line:TextLine, element:ContentElement):TextLineMirrorRegion
+ {
+ if(!line.mirrorRegions)
+ return null;
+
+ var regions:Vector. = line.mirrorRegions;
+ var region:TextLineMirrorRegion;
+ var n:int = regions.length;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ region = regions[i];
+ if(region.element === element)
+ return region;
+ }
+
+ return null;
+ }
+
+ public static function hasLineBreak(line:TextLine):Boolean
+ {
+ if(line.atomCount <= 1)
+ return false;
+
+ //Check to see if we have a line break graphic at the end of the TextLine
+ var graphicIndex:int = line.atomCount - 1;
+ var dObj:DisplayObject = line.getAtomGraphic(graphicIndex);
+ if(!dObj)
+ return false;
+
+ //We have some kind of graphic at the end, is it a line break?
+ var g:GraphicElement = GraphicElement(getElementAtAtomIndex(line, graphicIndex));
+ return g.userData === 'lineBreak';
+ }
+
+ public static function cleanLine(line:TextLine):void
+ {
+ line.userData = null;
+ }
+
+ private static const lines:Vector. = new [];
+
+ public static function checkIn(line:TextLine):void
+ {
+ cleanLine(line);
+ lines.push(line);
+ }
+
+ public static function checkOut():TextLine
+ {
+ return lines.pop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/CharacterSelectionBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/CharacterSelectionBehavior.as
new file mode 100644
index 0000000..22861a0
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/CharacterSelectionBehavior.as
@@ -0,0 +1,7 @@
+package org.tinytlf.behaviors
+{
+ public class CharacterSelectionBehavior extends MouseSelectionBehavior
+ {
+ //MouseSelectionBehavior is the character selection implementation.
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/FocusBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/FocusBehavior.as
new file mode 100644
index 0000000..e416246
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/FocusBehavior.as
@@ -0,0 +1,26 @@
+/*
+* Copyright (c) 2010 the original author or authors
+*
+* Permission is hereby granted to use, modify, and distribute this file
+* in accordance with the terms of the license agreement accompanying it.
+*/
+package org.tinytlf.behaviors
+{
+ import flash.events.Event;
+ import flash.events.KeyboardEvent;
+ import flash.events.MouseEvent;
+
+
+ public class FocusBehavior extends MultiGestureBehavior
+ {
+ [Event("keyDown")]
+ [Event("mouseDown")]
+ [Event("mouseMove")]
+ [Event("click")]
+ [Event("doubleClick")]
+ public function setFocus():void
+ {
+ line.stage.focus = container.target;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/IBeamBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/IBeamBehavior.as
new file mode 100755
index 0000000..969b701
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/IBeamBehavior.as
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.behaviors
+{
+ import flash.events.MouseEvent;
+ import flash.ui.Mouse;
+ import flash.ui.MouseCursor;
+
+
+ public class IBeamBehavior extends MultiGestureBehavior
+ {
+ [Event("rollOver")]
+ [Event("mouseOver")]
+ [Event("mouseMove")]
+ public function setIBeam():void
+ {
+ if(mirrorRegion)
+ return;
+
+ Mouse.cursor = MouseCursor.IBEAM;
+ }
+
+ [Event("rollOut")]
+ public function setAuto():void
+ {
+ Mouse.cursor = MouseCursor.AUTO;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/LineSelectionBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/LineSelectionBehavior.as
new file mode 100644
index 0000000..367fc0c
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/LineSelectionBehavior.as
@@ -0,0 +1,26 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+ import flash.text.engine.*;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ public class LineSelectionBehavior extends MouseSelectionBehavior
+ {
+ [Event("mouseDown")]
+ override public function downAction():void
+ {
+ super.downAction();
+
+ engine.select(anchor.x, anchor.y);
+ }
+
+ override protected function getAnchor():Point
+ {
+ var start:int = TinytlfUtil.atomIndexToGlobalIndex(engine, line, 0);
+ var end:int = start + line.atomCount;
+
+ return new Point(start, end);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/MouseSelectionBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/MouseSelectionBehavior.as
new file mode 100644
index 0000000..8878a08
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/MouseSelectionBehavior.as
@@ -0,0 +1,58 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class MouseSelectionBehavior extends SelectionBehaviorBase
+ {
+ public function MouseSelectionBehavior()
+ {
+ super();
+ }
+
+ protected var triggeredHere:Boolean = false;
+
+ [Event("mouseDown")]
+ override public function downAction():void
+ {
+ super.downAction();
+
+ engine.select();
+
+ triggeredHere = true;
+ }
+
+ [Event("mouseMove")]
+ override public function moveAction():void
+ {
+ var m:MouseEvent = MouseEvent(finalEvent);
+ if(!m.buttonDown || !triggeredHere)
+ return;
+
+ super.moveAction();
+ }
+
+ [Event("mouseUp")]
+ override public function upAction():void
+ {
+ super.upAction();
+ triggeredHere = false;
+ }
+
+ override protected function getAnchor():Point
+ {
+ var m:MouseEvent = MouseEvent(finalEvent);
+ var atomIndex:int = TextLineUtil.getAtomIndexAtPoint(line, new Point(m.stageX, m.stageY));
+ var globalIndex:int = TinytlfUtil.atomIndexToGlobalIndex(engine, line, atomIndex);
+ return new Point(globalIndex, globalIndex);
+ }
+
+ override protected function getSelection():Point
+ {
+ return getAnchor();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/MultiGestureBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/MultiGestureBehavior.as
new file mode 100644
index 0000000..c88d5ef
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/MultiGestureBehavior.as
@@ -0,0 +1,66 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.Event;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+
+ public class MultiGestureBehavior extends TinytlfBehavior
+ {
+ public function MultiGestureBehavior()
+ {
+ super();
+
+ attachListeners(this);
+ }
+
+ public function attachListeners(target:IBehavior):void
+ {
+ var listeners:XMLList = TinytlfUtil.describeType(this).factory.method.(child('metadata').(@name == 'Event').length());
+
+ var methodName:String;
+ var type:String;
+ var events:XMLList;
+
+ for each(var listener:XML in listeners)
+ {
+ methodName = listener.attribute('name').toString();
+ events = listener.metadata.(@name == 'Event');
+ for each(var meta:XML in events)
+ {
+ type = meta.arg.@value.toString();
+ types[type] = this[methodName];
+ }
+ }
+ }
+
+ protected var selection:Point;
+ protected var caret:int;
+
+ override protected function act(events:Vector.):void
+ {
+ super.act(events);
+
+ selection = engine.selection.clone();
+ caret = engine.caretIndex;
+
+ var type:String = finalEvent.type;
+ if(type in types)
+ types[type].length ? types[type](events) : types[type]();
+ else
+ defaultAction(events);
+ }
+
+ private const types:Object = {};
+
+ protected function defaultAction(events:Vector.):void
+ {
+// trace('default action occurred on ' + this['constructor'].toString());
+ }
+
+ protected function get validSelection():Boolean
+ {
+ return selection.x == selection.x && selection.y == selection.y;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/ParagraphSelectionBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/ParagraphSelectionBehavior.as
new file mode 100644
index 0000000..62a68d0
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/ParagraphSelectionBehavior.as
@@ -0,0 +1,28 @@
+package org.tinytlf.behaviors
+{
+ import flash.geom.Point;
+ import flash.text.engine.TextBlock;
+
+ import org.tinytlf.analytics.IVirtualizer;
+
+ public class ParagraphSelectionBehavior extends MouseSelectionBehavior
+ {
+ [Event("mouseDown")]
+ override public function downAction():void
+ {
+ super.downAction();
+
+ engine.select(anchor.x, anchor.y);
+ }
+
+ override protected function getAnchor():Point
+ {
+ var textBlockVirtualizer:IVirtualizer = engine.layout.textBlockVirtualizer;
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ var block:TextBlock = line.textBlock;
+ var begin:int = contentVirtualizer.getItemStart(block);
+ var end:int = begin + contentVirtualizer.getItemSize(block);
+ return new Point(begin, end);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/ScrollBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/ScrollBehavior.as
new file mode 100644
index 0000000..12f3035
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/ScrollBehavior.as
@@ -0,0 +1,18 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.*;
+
+ public class ScrollBehavior extends MultiGestureBehavior
+ {
+ public function ScrollBehavior()
+ {
+ super();
+ }
+
+ [Event("mouseWheel")]
+ public function scrollWheel(events:Vector.):void
+ {
+ engine.scrollPosition -= (MouseEvent(finalEvent).delta * 3);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/SelectAllBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/SelectAllBehavior.as
new file mode 100644
index 0000000..7ff65ef
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/SelectAllBehavior.as
@@ -0,0 +1,14 @@
+package org.tinytlf.behaviors
+{
+ import org.tinytlf.analytics.IVirtualizer;
+
+ public class SelectAllBehavior extends SelectionBehaviorBase
+ {
+ [Event("keyDown")]
+ override public function downAction():void
+ {
+ var contentVirtualizer:IVirtualizer = engine.blockFactory.contentVirtualizer;
+ engine.select(0, contentVirtualizer.size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/SelectionBehaviorBase.as b/tinytlf-gestures/src/org/tinytlf/behaviors/SelectionBehaviorBase.as
new file mode 100644
index 0000000..957db67
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/SelectionBehaviorBase.as
@@ -0,0 +1,58 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+
+ public class SelectionBehaviorBase extends MultiGestureBehavior
+ {
+ public function SelectionBehaviorBase()
+ {
+ super();
+ }
+
+ public function downAction():void
+ {
+ anchor = getAnchor();
+ engine.caretIndex = anchor.y;
+ }
+
+ public function moveAction():void
+ {
+ var end:Point = getSelection();
+
+ if(anchor.x > end.x)
+ {
+ engine.select(end.x, anchor.y);
+ engine.caretIndex = end.x;
+ }
+ else if(anchor.x < end.x)
+ {
+ engine.select(anchor.x, end.y);
+ engine.caretIndex = end.y;
+ }
+ else if(anchor.x == end.x)
+ {
+ engine.select(anchor.x, anchor.y);
+ engine.caretIndex = anchor.x;
+ }
+ }
+
+ public function upAction():void
+ {
+ anchor.x = 0;
+ anchor.y = 0;
+ }
+
+ protected var anchor:Point = new Point();
+
+ protected function getAnchor():Point
+ {
+ return new Point();
+ }
+
+ protected function getSelection():Point
+ {
+ return new Point();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/TinytlfBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/TinytlfBehavior.as
new file mode 100644
index 0000000..bccb541
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/TinytlfBehavior.as
@@ -0,0 +1,39 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.Event;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.interaction.EventLineInfo;
+ import org.tinytlf.layout.ITextContainer;
+
+ public class TinytlfBehavior extends Behavior
+ {
+ protected var engine:ITextEngine;
+ protected var line:TextLine;
+ protected var container:ITextContainer;
+ protected var contentElement:ContentElement;
+ protected var mirrorRegion:TextLineMirrorRegion;
+
+ override public function activate(events:Vector.):void
+ {
+ if(events.length <= 0)
+ return;
+
+ finalEvent = events[events.length - 1];
+
+ const info:EventLineInfo = EventLineInfo.getInfo(finalEvent);
+
+ if(!info)
+ return;
+
+ engine = info.engine;
+ line = info.line;
+ container = info.container;
+ mirrorRegion = info.mirrorRegion;
+ contentElement = info.element;
+
+ act(events);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/behaviors/WordSelectionBehavior.as b/tinytlf-gestures/src/org/tinytlf/behaviors/WordSelectionBehavior.as
new file mode 100644
index 0000000..750787e
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/behaviors/WordSelectionBehavior.as
@@ -0,0 +1,31 @@
+package org.tinytlf.behaviors
+{
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class WordSelectionBehavior extends MouseSelectionBehavior
+ {
+ [Event("mouseDown")]
+ override public function downAction():void
+ {
+ super.downAction();
+
+ engine.caretIndex = anchor.y;
+ engine.select(anchor.x, anchor.y);
+ }
+
+ override protected function getAnchor():Point
+ {
+ var m:MouseEvent = MouseEvent(finalEvent);
+ var atomIndex:int = TextLineUtil.getAtomIndexAtPoint(line, new Point(m.stageX, m.stageY));
+ var begin:int = TextLineUtil.getAtomWordBoundary(line, atomIndex);
+ var end:int = TextLineUtil.getAtomWordBoundary(line, atomIndex, false) - 1;
+
+ return new Point(TinytlfUtil.atomIndexToGlobalIndex(engine, line, begin),
+ TinytlfUtil.atomIndexToGlobalIndex(engine, line, end));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/CopyGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/CopyGesture.as
new file mode 100755
index 0000000..157ce76
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/CopyGesture.as
@@ -0,0 +1,21 @@
+package org.tinytlf.gestures
+{
+ import flash.events.Event;
+
+ [Event("copy")]
+
+ public class CopyGesture extends Gesture
+ {
+ public function CopyGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function copy(event:Event):Boolean
+ {
+ return (event.type === Event.COPY);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/KonamiCodeGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/KonamiCodeGesture.as
new file mode 100755
index 0000000..4d96f82
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/KonamiCodeGesture.as
@@ -0,0 +1,94 @@
+package org.tinytlf.gestures
+{
+ import flash.events.KeyboardEvent;
+ import flash.ui.Keyboard;
+
+
+ [Event("keyUp")]
+ public class KonamiCodeGesture extends Gesture
+ {
+ public function KonamiCodeGesture()
+ {
+ super();
+
+ hsm.appendChild(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ public function up(e:KeyboardEvent):Boolean
+ {
+ return e.keyCode === Keyboard.UP;
+ }
+
+ public function up2(e:KeyboardEvent):Boolean
+ {
+ return up(e);
+ }
+
+ public function down(e:KeyboardEvent):Boolean
+ {
+ return e.keyCode === Keyboard.DOWN;
+ }
+
+ public function down2(e:KeyboardEvent):Boolean
+ {
+ return down(e);
+ }
+
+ public function left(e:KeyboardEvent):Boolean
+ {
+ return e.keyCode == Keyboard.LEFT;
+ }
+
+ public function left2(e:KeyboardEvent):Boolean
+ {
+ return left(e);
+ }
+
+ public function right(e:KeyboardEvent):Boolean
+ {
+ return e.keyCode == Keyboard.RIGHT;
+ }
+
+ public function right2(e:KeyboardEvent):Boolean
+ {
+ return right(e);
+ }
+
+ public function b(e:KeyboardEvent):Boolean
+ {
+ return String.fromCharCode(e.charCode) === 'b';
+ }
+
+ public function a(e:KeyboardEvent):Boolean
+ {
+ return String.fromCharCode(e.charCode) === 'a';
+ }
+
+ public function enter(e:KeyboardEvent):Boolean
+ {
+ return e.keyCode == Keyboard.ENTER;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseClickGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseClickGesture.as
new file mode 100755
index 0000000..d029773
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseClickGesture.as
@@ -0,0 +1,50 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+
+
+ [Event("mouseDown")]
+ [Event("mouseUp")]
+ [Event("mouseMove")]
+ [Event("click")]
+ [Event("doubleClick")]
+
+ public class MouseClickGesture extends Gesture
+ {
+ public function MouseClickGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ hsm.appendChild();
+ hsm.appendChild();
+ hsm.appendChild();
+ hsm.appendChild();
+ }
+
+ public function click(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.CLICK;
+ }
+
+ public function doubleClick(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.DOUBLE_CLICK;
+ }
+
+ public function down(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_DOWN;
+ }
+
+ public function up(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_UP;
+ }
+
+ public function drag(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_MOVE && event.buttonDown;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseDoubleDownGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseDoubleDownGesture.as
new file mode 100755
index 0000000..028d77d
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseDoubleDownGesture.as
@@ -0,0 +1,62 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+ import flash.utils.getTimer;
+
+
+ [Event("mouseDown")]
+ [Event("mouseUp")]
+ [Event("mouseMove")]
+
+ public class MouseDoubleDownGesture extends Gesture
+ {
+ public function MouseDoubleDownGesture()
+ {
+ super();
+
+ hsm.appendChild(
+
+
+
+
+ );
+
+ hsm.appendChild();
+ }
+
+ public function drag(event:MouseEvent):Boolean
+ {
+ return go && event.buttonDown && event.type == MouseEvent.MOUSE_MOVE;
+ }
+
+ public function down(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_DOWN;
+ }
+
+ private var go:Boolean = false;
+
+ public function down2(event:MouseEvent):Boolean
+ {
+ go = (getTimer() - upTime) < 400;
+
+ var doIt:Boolean = go && event.type == MouseEvent.MOUSE_DOWN;
+
+ return doIt;
+ }
+
+ private var upTime:int = 0;
+
+ public function up(event:MouseEvent):Boolean
+ {
+ go = false;
+ upTime = getTimer();
+ return event.type == MouseEvent.MOUSE_UP;
+ }
+
+ override protected function testNotifiable(state:XML):Boolean
+ {
+ return super.testNotifiable(state) && go;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseOutGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseOutGesture.as
new file mode 100755
index 0000000..7e4817a
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseOutGesture.as
@@ -0,0 +1,22 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+
+
+ [Event("rollOut")]
+
+ public class MouseOutGesture extends Gesture
+ {
+ public function MouseOutGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function out(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.ROLL_OUT;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseOverGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseOverGesture.as
new file mode 100755
index 0000000..86ac784
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseOverGesture.as
@@ -0,0 +1,28 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+
+
+ [Event("mouseMove")]
+ [Event("mouseOver")]
+ [Event("rollOver")]
+
+ public class MouseOverGesture extends Gesture
+ {
+ public function MouseOverGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function move(event:MouseEvent):Boolean
+ {
+ return (
+ event.type == MouseEvent.ROLL_OVER ||
+ event.type == MouseEvent.MOUSE_MOVE ||
+ event.type == MouseEvent.MOUSE_OVER
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseTripleDownGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseTripleDownGesture.as
new file mode 100755
index 0000000..20a2dc7
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseTripleDownGesture.as
@@ -0,0 +1,66 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+ import flash.utils.getTimer;
+
+
+ [Event("mouseDown")]
+ [Event("mouseUp")]
+ [Event("mouseMove")]
+
+ public class MouseTripleDownGesture extends Gesture
+ {
+ public function MouseTripleDownGesture()
+ {
+ super();
+
+ hsm.appendChild(
+
+
+
+
+
+
+
+
+ );
+ hsm.appendChild();
+ }
+
+ public function drag(event:MouseEvent):Boolean
+ {
+ return go && event.buttonDown && event.type == MouseEvent.MOUSE_MOVE;
+ }
+
+ public function down(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_DOWN;
+ }
+
+ private var go:Boolean = false;
+
+ public function down3(event:MouseEvent):Boolean
+ {
+ go = (getTimer() - upTime) < 500;
+ upTime = 0;
+
+ resetStates();
+
+ return go && event.type == MouseEvent.MOUSE_DOWN;
+ }
+
+ private var upTime:int = 0;
+
+ public function up(event:MouseEvent):Boolean
+ {
+ go = false;
+ upTime = getTimer();
+ return event.type == MouseEvent.MOUSE_UP;
+ }
+
+ override protected function testNotifiable(state:XML):Boolean
+ {
+ return super.testNotifiable(state) && go;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/MouseWheelGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/MouseWheelGesture.as
new file mode 100644
index 0000000..adc77d9
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/MouseWheelGesture.as
@@ -0,0 +1,20 @@
+package org.tinytlf.gestures
+{
+ import flash.events.MouseEvent;
+
+
+ [Event("mouseWheel")]
+ public class MouseWheelGesture extends Gesture
+ {
+ public function MouseWheelGesture()
+ {
+ super();
+ hsm.appendChild()
+ }
+
+ public function wheel(event:MouseEvent):Boolean
+ {
+ return event.type == MouseEvent.MOUSE_WHEEL;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/gestures/SelectAllGesture.as b/tinytlf-gestures/src/org/tinytlf/gestures/SelectAllGesture.as
new file mode 100755
index 0000000..4300bd6
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/gestures/SelectAllGesture.as
@@ -0,0 +1,21 @@
+package org.tinytlf.gestures
+{
+ import flash.events.Event;
+
+
+ [Event("selectAll")]
+ public class SelectAllGesture extends Gesture
+ {
+ public function SelectAllGesture()
+ {
+ super();
+
+ hsm.appendChild();
+ }
+
+ public function select(event:Event):Boolean
+ {
+ return (event.type === Event.SELECT_ALL);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/interaction/GestureInteractor.as b/tinytlf-gestures/src/org/tinytlf/interaction/GestureInteractor.as
new file mode 100755
index 0000000..3e33c45
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/interaction/GestureInteractor.as
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.interaction
+{
+ import flash.display.*;
+ import flash.events.*;
+ import flash.geom.Rectangle;
+ import flash.text.engine.*;
+
+ import org.tinytlf.behaviors.*;
+ import org.tinytlf.gestures.IGesture;
+ import org.tinytlf.layout.ITextContainer;
+ import org.tinytlf.layout.properties.*;
+
+ public class GestureInteractor extends TextInteractorBase implements IGestureInteractor
+ {
+ override public function getMirror(element:* = null):EventDispatcher
+ {
+ if(element is TextLine)
+ {
+ var line:TextLine = element as TextLine;
+ if(line.getChildByName('lineCatcher') == null)
+ createBackground(line);
+
+ addListeners(line);
+ }
+
+ return super.getMirror(element);
+ }
+
+ public function removeListeners(target:IEventDispatcher):void
+ {
+ for each(var gesture:IGesture in gestures)
+ {
+ gesture.removeSource(target);
+ }
+ }
+
+ public function addListeners(target:IEventDispatcher):void
+ {
+ for each(var gesture:IGesture in gestures)
+ {
+ gesture.addSource(target);
+ }
+ }
+
+ public function addGesture(gesture:IGesture, ... behaviors):IGesture
+ {
+ if(_gestures.indexOf(gesture) == -1)
+ _gestures.push(gesture);
+
+ for each(var behavior:IBehavior in behaviors)
+ {
+ gesture.addBehavior(behavior);
+ }
+
+ return gesture;
+ }
+
+ public function removeGesture(gesture:IGesture):IGesture
+ {
+ var i:int = _gestures.indexOf(gesture);
+ if(i != -1)
+ _gestures.splice(i, 1);
+
+ return gesture;
+ }
+
+ public function removeAllGestures():void
+ {
+ _gestures.length = 0;
+ }
+
+ private var _gestures:Vector. = new Vector.();
+
+ public function get gestures():Vector.
+ {
+ return _gestures.concat(); // Defensive copy.
+ // This is the only way to configure behaviors after
+ // a gesture has been mapped. Don't allow access to the real list.
+ // Provided so it can be searched, behaviors should be added to the
+ // gesture itself, or by calling addGesture with an existing gesture
+ // instance, passing in the new behaviors to be added.
+ }
+
+ /**
+ * @private
+ * Adds a catcher to the TextLine so that mouse events bubble and can be
+ * caught by the gestures.
+ */
+// /*
+ private function createBackground(line:TextLine):void
+ {
+ //Try to guess the original paragraph width.
+ var lp:LayoutProperties = (line.textBlock.userData as LayoutProperties) || new LayoutProperties();
+ var w:Number = line.specifiedWidth;
+ w += lp.paddingLeft;
+ w += lp.paddingRight;
+
+ var x:Number = 0;
+ var rect:Rectangle = new Rectangle(0, -line.ascent, 0, line.height);
+
+ //Add in the indent if this is the first line in the TextBlock
+ if(!line.previousLine)
+ {
+ w += lp.textIndent;
+ x -= lp.textIndent;
+
+ rect.y -= lp.paddingTop;
+ rect.height += lp.paddingTop;
+ }
+
+ switch(lp.textAlign)
+ {
+ case TextAlign.CENTER:
+ case TextAlign.RIGHT:
+ x = -line.x;
+ break;
+ }
+
+ rect.x = x;
+ rect.width = w;
+
+ var sprite:Sprite = new Sprite();
+ sprite.name = 'lineCatcher';
+// sprite.graphics.beginFill(0x00, 0.1);
+ sprite.graphics.beginFill(0x00, 0);
+ sprite.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
+ line.addChildAt(sprite, 0);
+ }
+// */
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-gestures/src/org/tinytlf/interaction/IGestureInteractor.as b/tinytlf-gestures/src/org/tinytlf/interaction/IGestureInteractor.as
new file mode 100755
index 0000000..e1d064f
--- /dev/null
+++ b/tinytlf-gestures/src/org/tinytlf/interaction/IGestureInteractor.as
@@ -0,0 +1,20 @@
+/*
+* Copyright (c) 2010 the original author or authors
+*
+* Permission is hereby granted to use, modify, and distribute this file
+* in accordance with the terms of the license agreement accompanying it.
+*/
+package org.tinytlf.interaction
+{
+ import org.tinytlf.gestures.IGesture;
+
+ public interface IGestureInteractor extends ITextInteractor
+ {
+ function addGesture(gesture:IGesture, ...behaviors):IGesture;
+ function removeGesture(gesture:IGesture):IGesture;
+
+ function removeAllGestures():void;
+
+ function get gestures():Vector.;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/ConstraintTextContainer.as b/tinytlf-layouts/src/org/tinytlf/layout/ConstraintTextContainer.as
new file mode 100755
index 0000000..9eaf97b
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/ConstraintTextContainer.as
@@ -0,0 +1,286 @@
+package org.tinytlf.layout
+{
+ import flash.display.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.layout.orientation.*;
+ import org.tinytlf.layout.orientation.horizontal.*;
+ import org.tinytlf.layout.properties.LayoutProperties;
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.*;
+
+ public class ConstraintTextContainer extends TextContainerBase implements IConstraintTextContainer
+ {
+ public function ConstraintTextContainer(container:Sprite,
+ explicitWidth:Number = NaN,
+ explicitHeight:Number = NaN)
+ {
+ super(container, explicitWidth, explicitHeight);
+
+ constraintFactory = new ConstraintFactory();
+
+ majorDirection = new LTRMajor(this);
+ minorDirection = new HMinor(this);
+ }
+
+ private var major:IMajorOrientation;
+
+ public function get majorDirection():IMajorOrientation
+ {
+ return major;
+ }
+
+ public function set majorDirection(delegate:IMajorOrientation):void
+ {
+ if(delegate == major)
+ return;
+
+ major = delegate;
+ major.target = this;
+
+ if(engine)
+ engine.invalidate();
+ }
+
+ private var minor:IMinorOrientation;
+
+ public function get minorDirection():IMinorOrientation
+ {
+ return minor;
+ }
+
+ public function set minorDirection(delegate:IMinorOrientation):void
+ {
+ if(delegate == minor)
+ return;
+
+ minor = delegate;
+ minor.target = this;
+
+ if(engine)
+ engine.invalidate();
+ }
+
+ private var _constraintFactory:IConstraintFactory;
+
+ public function set constraintFactory(factory:IConstraintFactory):void
+ {
+ if(factory === _constraintFactory)
+ return;
+
+ _constraintFactory = factory;
+ }
+
+ public function get constraintFactory():IConstraintFactory
+ {
+ return _constraintFactory;
+ }
+
+ protected var _constraints:Vector. = new [];
+
+ public function get constraints():Vector.
+ {
+ return _constraints.concat();
+ }
+
+ public function addConstraint(constraint:ITextConstraint):void
+ {
+ if(!constraint)
+ return;
+
+ if(getConstraint(constraint.content))
+ return;
+
+ _constraints.push(constraint);
+ }
+
+ public function getConstraint(element:*):ITextConstraint
+ {
+ var n:int = _constraints.length;
+ var constraint:ITextConstraint;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ constraint = _constraints[i];
+ if(constraint.content === element)
+ return constraint;
+ }
+
+ return null;
+ }
+
+ public function removeConstraint(constraint:ITextConstraint):void
+ {
+ var i:int = _constraints.indexOf(constraint);
+ if(i != -1)
+ _constraints.splice(i, 1);
+ }
+
+ override public function preLayout():void
+ {
+ super.preLayout();
+
+ major.preLayout();
+ minor.preLayout();
+ }
+
+ override public function postLayout():void
+ {
+ super.postLayout();
+
+ major.postLayout();
+ minor.postLayout();
+ }
+
+ override public function layout(block:TextBlock, previousLine:TextLine):TextLine
+ {
+ if(TextBlockUtil.isInvalid(block))
+ return renderBlockLines(block, previousLine);
+
+ return checkBlockLines(block);
+ }
+
+ private function checkBlockLines(block:TextBlock):TextLine
+ {
+ var line:TextLine = block.firstLine;
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+
+ minor.prepForTextBlock(block, null);
+
+ while(line)
+ {
+ if(hasLine(line))
+ {
+ major.position(line);
+ minor.position(line);
+
+ if(minor.checkTargetBounds(line))
+ {
+ break;
+ }
+
+ line = line.nextLine;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ minor.postTextBlock(block);
+
+ return line;
+ }
+
+ private function renderBlockLines(block:TextBlock, previousLine:TextLine):TextLine
+ {
+ minor.prepForTextBlock(block, previousLine);
+ major.prepForTextBlock(block, previousLine);
+
+ var line:TextLine = checkLineBreakJustification(block, createTextLine(block, previousLine));
+
+ while(line)
+ {
+ addLineToTarget(line);
+
+ registerLine(line);
+
+ major.position(line);
+ minor.position(line);
+
+ findConstraints(line);
+
+ if(minor.checkTargetBounds(line))
+ return line;
+
+ line = checkLineBreakJustification(block, createTextLine(block, line));
+ }
+
+ major.postTextBlock(block);
+ minor.postTextBlock(block);
+
+ return null;
+ }
+
+ override protected function createTextLine(block:TextBlock, previousLine:TextLine):TextLine
+ {
+ var size:Number = major.getLineSize(block, previousLine);
+
+ var orphan:TextLine = getRecycledLine(previousLine);
+ if(orphan)
+ return block.recreateTextLine(orphan, previousLine, size, 0.0, true);
+
+ return block.createTextLine(previousLine, size, 0.0, true);
+ }
+
+ override protected function invalidateVisibleLines():void
+ {
+ super.invalidateVisibleLines();
+
+ _constraints.length = 0;
+ }
+
+ protected function findConstraints(line:TextLine):void
+ {
+ if(!line.hasGraphicElement)
+ return;
+
+ var n:int = line.atomCount;
+ for(var i:int = 0; i < n; i += 1)
+ if(line.getAtomGraphic(i))
+ if(major.registerConstraint(line, i))
+ return;
+ }
+
+ /**
+ * @private
+ * Checks to see if the input TextLine needs to be recreated due to the
+ * combination of justification and a style line break.
+ *
+ * This achieves the "HTML-style" line break for justified text.
+ */
+ private function checkLineBreakJustification(block:TextBlock, line:TextLine):TextLine
+ {
+ if(!block || !line)
+ return line;
+
+ // If we're not justified ALL_BUT_LAST, exit early. If they're
+ // justified ALL_INCLUDING_LAST, they want the last line justified
+ // anyway, so we don't mind that a tag caused the
+ // justification spacing to look funny.
+ var justifier:TextJustifier = block.textJustifier;
+ if(!justifier)
+ return line;
+
+ if(justifier.lineJustification !== LineJustification.ALL_BUT_LAST)
+ return line;
+
+ if(!TextLineUtil.hasLineBreak(line))
+ return line;
+
+ // We've broken a justified line, it does have a line break graphic
+ // at the end, and it's going to look funny. So re-break the line
+ // using the unjustifiedWidth instead of the total width.
+ return block.recreateTextLine(
+ line,
+ line.previousLine,
+ Math.min(line.unjustifiedTextWidth, line.textWidth),
+ 0.0,
+ true)
+ }
+ }
+}
+
+import flash.text.engine.TextLine;
+
+import org.tinytlf.layout.constraints.*;
+import org.tinytlf.util.fte.TextLineUtil;
+
+internal class ConstraintFactory implements IConstraintFactory
+{
+ public function getConstraint(line:TextLine, atomIndex:int):ITextConstraint
+ {
+ return new TextConstraintBase(TextLineUtil.getElementAtAtomIndex(line, atomIndex));
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/IConstraintTextContainer.as b/tinytlf-layouts/src/org/tinytlf/layout/IConstraintTextContainer.as
new file mode 100755
index 0000000..cab2a35
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/IConstraintTextContainer.as
@@ -0,0 +1,48 @@
+package org.tinytlf.layout
+{
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.layout.orientation.*;
+
+ public interface IConstraintTextContainer extends ITextContainer
+ {
+ /**
+ * The primary direction of the TextLines in a TextField. Determines the
+ * width of each TextLine, and positions the TextLine along the major
+ * axis.
+ *
+ * For left-to-right and right-to-left, this determines the width of
+ * each TextLine, as well as the X position, including floating
+ * around inline graphics and list items.
+ *
+ * For top-to-bottom and bottom-to-top, this determines the height of
+ * each TextLine, as well as the Y position.
+ */
+ function get majorDirection():IMajorOrientation;
+ function set majorDirection(delegate:IMajorOrientation):void;
+
+ /**
+ * The secondary direction in text layout. For left-to-right or
+ * right-to-left text, this determines the Y position of each TextLine.
+ * For top-to-bottom or bottom-to-top, this determines the X position
+ * of each TextLine.
+ */
+ function get minorDirection():IMinorOrientation;
+ function set minorDirection(delegate:IMinorOrientation):void;
+
+ /**
+ * The factory which generates custom constraint types from the rendered
+ * Text Lines.
+ */
+ function get constraintFactory():IConstraintFactory;
+ function set constraintFactory(factory:IConstraintFactory):void;
+
+ /**
+ * A read-only list of active constraints.
+ */
+ function get constraints():Vector.;
+
+ function addConstraint(constraint:ITextConstraint):void;
+ function getConstraint(element:*):ITextConstraint;
+ function removeConstraint(constraint:ITextConstraint):void;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/IConstraintFactory.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/IConstraintFactory.as
new file mode 100755
index 0000000..79f4342
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/IConstraintFactory.as
@@ -0,0 +1,10 @@
+package org.tinytlf.layout.constraints
+{
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.TextLine;
+
+ public interface IConstraintFactory
+ {
+ function getConstraint(line:TextLine, atomIndex:int):ITextConstraint;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/ITextConstraint.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/ITextConstraint.as
new file mode 100755
index 0000000..a02578a
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/ITextConstraint.as
@@ -0,0 +1,32 @@
+package org.tinytlf.layout.constraints
+{
+ import flash.text.engine.TextLine;
+
+ /**
+ * A text constraint is an element in a TextField which can't have any
+ * overlap with other constraints.
+ *
+ * When an image in a TextLine is detected, a constraint is created for it.
+ */
+ public interface ITextConstraint
+ {
+ /**
+ * Initializes the constraint. The constraintElement argument can be
+ * any object detected within the TextLine, including the TextLine
+ * itself.
+ */
+ function initialize(element:*):void;
+
+ function get content():*;
+ function get constraintMarker():Object;
+ function get float():String;
+
+ function get majorValue():Number;
+ function set majorValue(value:Number):void;
+
+ function get majorSize():Number;
+ function set majorSize(value:Number):void;
+
+ function getMajorValue(atMinor:Number, fromMajor:Number):Number;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/TextConstraintBase.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/TextConstraintBase.as
new file mode 100755
index 0000000..41bae1d
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/TextConstraintBase.as
@@ -0,0 +1,113 @@
+package org.tinytlf.layout.constraints
+{
+ import flash.display.DisplayObject;
+ import flash.geom.Rectangle;
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.properties.LayoutProperties;
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ /**
+ * The base text constraint.
+ */
+ public class TextConstraintBase implements ITextConstraint
+ {
+ public function TextConstraintBase(constraintElement:* = null)
+ {
+ if(constraintElement)
+ initialize(constraintElement);
+ }
+
+ protected var engine:ITextEngine;
+ protected var lp:LayoutProperties;
+
+ public function initialize(e:*):void
+ {
+ element = e;
+
+ if(e is ContentElement)
+ {
+ marker = ContentElement(e).userData;
+ }
+
+ if(e is GraphicElement)
+ {
+ var g:GraphicElement = GraphicElement(e);
+ var dObj:DisplayObject = g.graphic;
+ var line:TextLine = ContentElementUtil.getTextLines(g)[0];
+ engine = ITextEngine(line.userData);
+
+ lp = new LayoutProperties(g.userData);
+
+ lp.x = line.x;
+ lp.y = line.y;
+
+ if(lp.float)
+ {
+ dObj.x = lp.paddingLeft;
+ dObj.y = lp.paddingTop;
+ }
+ else
+ {
+ var bounds:Rectangle = dObj.getBounds(line);
+
+ lp.x = bounds.x;
+ lp.y = line.y;
+ lp.width = bounds.width || g.elementWidth;
+ lp.height = bounds.height || g.elementHeight;
+ }
+ }
+ }
+
+ private var marker:Object;
+ public function get constraintMarker():Object
+ {
+ return marker;
+ }
+
+ public function get float():String
+ {
+ return lp.float;
+ }
+
+ private var element:*;
+ public function get content():*
+ {
+ return element;
+ }
+
+ public function get majorValue():Number
+ {
+ return 0;
+ }
+
+ public function set majorValue(value:Number):void
+ {
+ }
+
+ public function get majorSize():Number
+ {
+ return 0;
+ }
+
+ public function set majorSize(value:Number):void
+ {
+ }
+
+ public function getMajorValue(atMinor:Number, fromMajor:Number):Number
+ {
+ return -1;
+ }
+
+ protected function get totalWidth():Number
+ {
+ return lp.width + lp.paddingLeft + lp.paddingRight;
+ }
+
+ protected function get totalHeight():Number
+ {
+ return lp.height + lp.paddingTop + lp.paddingBottom;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraint.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraint.as
new file mode 100644
index 0000000..45a2ab4
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraint.as
@@ -0,0 +1,81 @@
+package org.tinytlf.layout.constraints.horizontal
+{
+ import flash.text.engine.ContentElement;
+
+ import org.tinytlf.layout.constraints.TextConstraintBase;
+ import org.tinytlf.layout.properties.TextFloat;
+
+ /**
+ * The base class for a constraint that exists within a horizontal ltr or
+ * rtl text field.
+ */
+ public class HConstraint extends TextConstraintBase
+ {
+ public function HConstraint(constraintElement:* = null)
+ {
+ super(constraintElement);
+ }
+
+ override public function get majorValue():Number
+ {
+ return lp.x;
+ }
+
+ override public function set majorValue(value:Number):void
+ {
+ lp.x = value;
+ }
+
+ override public function get majorSize():Number
+ {
+ return totalWidth;
+ }
+
+ override public function set majorSize(value:Number):void
+ {
+ lp.width = value;
+ }
+
+ override public function getMajorValue(atMinor:Number, fromMajor:Number):Number
+ {
+ if(atMinor < lp.y)
+ return -1;
+
+ if(atMinor >= (lp.y + totalHeight))
+ return -1;
+
+ if(float == TextFloat.LEFT)
+ {
+ return fromLeft(atMinor, fromMajor);
+ }
+ else if(float == TextFloat.RIGHT)
+ {
+ return fromRight(atMinor, fromMajor);
+ }
+
+ return fromLeft(atMinor, fromMajor);
+ }
+
+ private function fromLeft(atMinor:Number, fromMajor:Number):Number
+ {
+ if(fromMajor < lp.x)
+ return fromMajor;
+
+ if(fromMajor >= lp.x && fromMajor < (lp.x + totalWidth))
+ return (lp.x + totalWidth);
+
+ return fromMajor;
+ }
+
+ private function fromRight(atMinor:Number, fromMajor:Number):Number
+ {
+ if(fromMajor < lp.x)
+ return lp.x;
+
+ if(fromMajor >= lp.x && fromMajor < (lp.x + totalWidth))
+ return (lp.x - totalWidth);
+
+ return fromMajor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraintFactory.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraintFactory.as
new file mode 100644
index 0000000..e1b71d1
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/horizontal/HConstraintFactory.as
@@ -0,0 +1,14 @@
+package org.tinytlf.layout.constraints.horizontal
+{
+ import flash.text.engine.TextLine;
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class HConstraintFactory implements IConstraintFactory
+ {
+ public function getConstraint(line:TextLine, atomIndex:int):ITextConstraint
+ {
+ return new HConstraint(TextLineUtil.getElementAtAtomIndex(line, atomIndex));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraint.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraint.as
new file mode 100644
index 0000000..a542774
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraint.as
@@ -0,0 +1,55 @@
+package org.tinytlf.layout.constraints.vertical
+{
+ import flash.text.engine.ContentElement;
+
+ import org.tinytlf.layout.constraints.TextConstraintBase;
+
+ /**
+ * The base class for a constraint that exists within a vertical ttb or btt
+ * text field.
+ */
+ public class VConstraint extends TextConstraintBase
+ {
+ public function VConstraint(constraintElement:ContentElement = null)
+ {
+ super(constraintElement);
+ }
+
+ override public function get majorValue():Number
+ {
+ return lp.y;
+ }
+
+ override public function set majorValue(value:Number):void
+ {
+ lp.y = value;
+ }
+
+ override public function get majorSize():Number
+ {
+ return totalHeight;
+ }
+
+ override public function set majorSize(value:Number):void
+ {
+ lp.height = value;
+ }
+
+ override public function getMajorValue(atMinor:Number, fromMajor:Number):Number
+ {
+ if(atMinor < lp.x)
+ return -1;
+
+ if(atMinor > (lp.x + totalWidth))
+ return -1;
+
+ if(fromMajor < lp.y)
+ return fromMajor;
+
+ if(fromMajor >= lp.y && fromMajor < (lp.y + totalHeight))
+ return (lp.y + totalHeight);
+
+ return fromMajor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraintFactory.as b/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraintFactory.as
new file mode 100644
index 0000000..9b3551e
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/constraints/vertical/VConstraintFactory.as
@@ -0,0 +1,15 @@
+package org.tinytlf.layout.constraints.vertical
+{
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class VConstraintFactory implements IConstraintFactory
+ {
+ public function getConstraint(line:TextLine, atomIndex:int):ITextConstraint
+ {
+ return new VConstraint(TextLineUtil.getElementAtAtomIndex(line, atomIndex));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/IFlowOrientation.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IFlowOrientation.as
new file mode 100755
index 0000000..a86c7c3
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IFlowOrientation.as
@@ -0,0 +1,50 @@
+package org.tinytlf.layout.orientation
+{
+ import flash.geom.Point;
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.layout.IConstraintTextContainer;
+
+ public interface IFlowOrientation
+ {
+ /**
+ * Called on every ITextContainer just before layout begins.
+ */
+ function preLayout():void;
+
+ /**
+ * Called on every ITextContainer after the entire layout pass ends.
+ */
+ function postLayout():void;
+
+ /**
+ * Called by the IFlowLayout just before layout on this TextBlock begins.
+ * Allows the IFlowDirectionDelegate to adjust before rendering the next
+ * paragraph.
+ */
+ function prepForTextBlock(block:TextBlock, line:TextLine):void;
+
+ /**
+ * Called by the IFlowLayout just before layout on this TextBlock begins.
+ * Allows the IFlowDirectionDelegate to adjust before rendering the next
+ * paragraph.
+ */
+ function postTextBlock(block:TextBlock):void;
+
+ /**
+ * Sets the x and y positions of the TextLine.
+ */
+ function position(latestLine:TextLine):void;
+
+ /**
+ * The IFlowLayout this direction delegate belongs to/acts on.
+ */
+ function set target(flowLayout:IConstraintTextContainer):void;
+
+ /**
+ * The current layout position for the orientation.
+ */
+ function get value():Number;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMajorOrientation.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMajorOrientation.as
new file mode 100644
index 0000000..2f617ca
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMajorOrientation.as
@@ -0,0 +1,23 @@
+package org.tinytlf.layout.orientation
+{
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLine;
+
+ public interface IMajorOrientation extends IFlowOrientation
+ {
+ /**
+ * Calculates the width of the newest TextLine.
+ */
+ function getLineSize(block:TextBlock, previousLine:TextLine):Number;
+
+ /**
+ * Called from the IFlowLayout when an IFlowLayoutElement has been
+ * detected in a TextLine.
+ *
+ * @returns Boolean true if this IFlowLayoutElement should cause the
+ * IFlowLayout to block processing any more IFlowLayoutElements, false
+ * if it should continue.
+ */
+ function registerConstraint(line:TextLine, atomIndex:int):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMinorOrientation.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMinorOrientation.as
new file mode 100644
index 0000000..1e8efce
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/IMinorOrientation.as
@@ -0,0 +1,13 @@
+package org.tinytlf.layout.orientation
+{
+ import flash.text.engine.TextLine;
+
+ public interface IMinorOrientation extends IFlowOrientation
+ {
+ /**
+ * Returns true if layout has moved outside the constraints of the
+ * target container, false if we're still within bounds.
+ */
+ function checkTargetBounds(latestLine:TextLine):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/TextFlowOrientationBase.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/TextFlowOrientationBase.as
new file mode 100755
index 0000000..82fa150
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/TextFlowOrientationBase.as
@@ -0,0 +1,163 @@
+package org.tinytlf.layout.orientation
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.layout.IConstraintTextContainer;
+ import org.tinytlf.layout.constraints.ITextConstraint;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class TextFlowOrientationBase implements IMajorOrientation, IMinorOrientation
+ {
+ public function TextFlowOrientationBase(target:IConstraintTextContainer)
+ {
+ this.target = target;
+ }
+
+ private var layout:IConstraintTextContainer;
+ public function set target(flowLayout:IConstraintTextContainer):void
+ {
+ if(flowLayout === layout)
+ return;
+
+ layout = flowLayout;
+ }
+
+ public function get target():IConstraintTextContainer
+ {
+ return layout;
+ }
+
+ public function preLayout():void
+ {
+ }
+
+ public function postLayout():void
+ {
+ }
+
+ public function prepForTextBlock(block:TextBlock, line:TextLine):void
+ {
+ }
+
+ public function postTextBlock(block:TextBlock):void
+ {
+ }
+
+ public function getLineSize(block:TextBlock, previousLine:TextLine):Number
+ {
+ return 0;
+ }
+
+ public function position(latestLine:TextLine):void
+ {
+ }
+
+ /**
+ * Checks to see if we've laid out lines within the boundaries of our
+ * target container. Returns true if we're outside bounds, false if we
+ * aren't.
+ */
+ public function checkTargetBounds(latestLine:TextLine):Boolean
+ {
+ var constraints:Vector. = target.constraints;
+
+ if(!constraints.length)
+ return false;
+
+ var e:ITextConstraint = constraints[constraints.length - 1];
+
+ // Return true if the last IFlowLayoutElement is a
+ // ContainerTerminator, which causes tinytlf to stop laying out in
+ // this container and move on to the next one.
+
+ return (e.constraintMarker === 'containerTerminator');
+ }
+
+ /**
+ * Called when an element can potentially be added to the list of
+ * IFlowLayoutElements. Override this to respect more types of layout
+ * elements.
+ */
+ public function registerConstraint(line:TextLine, atomIndex:int):Boolean
+ {
+ var constraint:ITextConstraint = createConstraint(line, atomIndex);
+
+ if(!constraint)
+ return false;
+
+ handleConstraint(line, constraint);
+
+ return finalizeConstraint(line, constraint);
+ }
+
+ public function get value():Number
+ {
+ return 0;
+ }
+
+ protected function getTotalSize(from:Object = null):Number
+ {
+ return 0;
+ }
+
+ protected function createConstraint(line:TextLine, atomIndex:int):ITextConstraint
+ {
+ var cElement:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+ var constraint:ITextConstraint = target.getConstraint(cElement);
+
+ if(!constraint)
+ {
+ constraint = target.constraintFactory.getConstraint(line, atomIndex);
+ if(constraint)
+ {
+ target.addConstraint(constraint);
+ }
+ }
+
+ return constraint;
+ }
+
+ protected function handleConstraint(line:TextLine, constraint:ITextConstraint):void
+ {
+ if(constraint.constraintMarker === 'listItemTerminator')
+ {
+ handleListItemTermination();
+ }
+ }
+
+ protected function finalizeConstraint(line:TextLine, constraint:ITextConstraint):Boolean
+ {
+ return constraint.constraintMarker === 'containerTerminator';
+ }
+
+ /**
+ * When we get to the end of list item, traverse backwards in the
+ * LayoutElement list to the first LIST_ITEM element and remove it.
+ * This ensures we stop flowing around the bullet graphic.
+ */
+ protected function handleListItemTermination():void
+ {
+ var constraints:Vector. = layout.constraints;
+ var el:ITextConstraint;
+
+ for(var i:int = constraints.length - 1; i >= 0; --i)
+ {
+ el = constraints[i];
+
+ if( el.constraintMarker === 'listItemOutside' ||
+ el.constraintMarker === 'listItemInside')
+ {
+ target.removeConstraint(el);
+ break;
+ }
+ }
+ }
+
+ protected function get engine():ITextEngine
+ {
+ return target ? target.engine : null;
+ }
+ }
+}
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HMinor.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HMinor.as
new file mode 100644
index 0000000..ba5b9a9
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HMinor.as
@@ -0,0 +1,138 @@
+package org.tinytlf.layout.orientation.horizontal
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.IConstraintTextContainer;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.*;
+
+ /**
+ * The IMinorOrientation implementation for left-to-right and right-to-left
+ * languages.
+ */
+ public class HMinor extends HOrientationBase
+ {
+ public function HMinor(target:IConstraintTextContainer)
+ {
+ super(target);
+ }
+
+ private var y:Number = 0;
+
+ override public function preLayout():void
+ {
+ super.preLayout();
+
+ y = 0;
+ }
+
+ override public function prepForTextBlock(block:TextBlock, line:TextLine):void
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+
+ if(line)
+ {
+ if(target.hasLine(line))
+ {
+ y = line.y + line.descent + lp.leading;
+ }
+ }
+ else
+ {
+ y += lp.paddingTop;
+ }
+ }
+
+ override public function postTextBlock(block:TextBlock):void
+ {
+ y += TinytlfUtil.getLP(block).paddingBottom;
+ }
+
+ override public function position(line:TextLine):void
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(line);
+ var totalWidth:Number = getTotalSize(lp);
+
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ positionLeft(line, totalWidth);
+ break;
+ case TextAlign.RIGHT:
+ positionRight(line, totalWidth);
+ break;
+ }
+ }
+
+ private function positionLeft(line:TextLine, totalWidth:Number):void
+ {
+ var x:Number = target.majorDirection.value;
+
+ if((x + line.specifiedWidth) >= totalWidth)
+ {
+ incrementY(line);
+ }
+
+ //Check to see if there's a line break at the end of this line.
+ //If so, increment the Y regardless of the X position.
+ else if(TextLineUtil.hasLineBreak(line))
+ {
+ incrementY(line);
+ }
+ else
+ {
+ line.y = y + line.ascent;
+ }
+ }
+
+ private function positionRight(line:TextLine, totalWidth:Number):void
+ {
+ }
+
+ private function incrementY(line:TextLine):void
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(line);
+ var block:TextBlock = line.textBlock;
+
+ y += line.ascent;
+ line.y = y;
+ y += line.descent + lp.leading;
+ }
+
+ override public function checkTargetBounds(line:TextLine):Boolean
+ {
+ if(super.checkTargetBounds(line))
+ {
+ y = 0;
+ return true;
+ }
+
+ if(target.totalHeight < target.measuredHeight)
+ {
+ target.totalHeight = target.measuredHeight;
+
+ if(target.scrollable)
+ {
+ return false;
+ }
+ }
+
+ var eHeight:Number = target.explicitHeight;
+
+ if(eHeight != eHeight)
+ return false;
+
+ if(line)
+ return ((line.y + line.textHeight) >= (eHeight + engine.scrollPosition));
+
+ return (y >= (eHeight + engine.scrollPosition));
+ }
+
+ override public function get value():Number
+ {
+ return y;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HOrientationBase.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HOrientationBase.as
new file mode 100644
index 0000000..9fb3c60
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/HOrientationBase.as
@@ -0,0 +1,213 @@
+package org.tinytlf.layout.orientation.horizontal
+{
+ import flash.text.engine.TextBlock;
+ import flash.text.engine.TextLine;
+ import flash.text.engine.TextLineValidity;
+ import flash.utils.Dictionary;
+
+ import org.tinytlf.layout.IConstraintTextContainer;
+ import org.tinytlf.layout.orientation.TextFlowOrientationBase;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.util.fte.TextBlockUtil;
+
+ /**
+ * The base class for the IMajorOrientation and IMinorOrientation classes
+ * for horizontal-based languages. Languages which are left-to-right
+ * (typically the Romance languages) or right-to-left (Hebrew, Arabic, etc.)
+ * use either the LTR or RTL major orientations for their horizontal
+ * alignment and spacing, but rely on the same (vertical) minor orientation.
+ */
+ public class HOrientationBase extends TextFlowOrientationBase
+ {
+ public function HOrientationBase(target:IConstraintTextContainer)
+ {
+ super(target);
+ }
+
+ override public function getLineSize(block:TextBlock, previousLine:TextLine):Number
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+ var totalWidth:Number = getTotalSize(block);
+
+ if(previousLine == null)
+ totalWidth -= lp.textIndent;
+
+ return totalWidth - lp.paddingLeft - lp.paddingRight;
+ }
+
+ override public function position(line:TextLine):void
+ {
+ var props:LayoutProperties = TinytlfUtil.getLP(line);
+ var totalWidth:Number = getTotalSize(line);
+
+ var lineWidth:Number = line.width;
+ var x:Number = 0;
+
+ if(!line.previousLine)
+ x += props.textIndent;
+
+ switch(props.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ x += props.paddingLeft;
+ break;
+ case TextAlign.CENTER:
+ x = (totalWidth - lineWidth) * 0.5;
+ break;
+ case TextAlign.RIGHT:
+ x = totalWidth - lineWidth + props.paddingRight;
+ break;
+ }
+
+ line.x = x;
+ }
+
+ override protected function getTotalSize(from:Object = null):Number
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(from);
+
+ if(lp.width)
+ return lp.width;
+
+ if(target.explicitWidth != target.explicitWidth)
+ return TextLine.MAX_LINE_WIDTH;
+
+ return target.explicitWidth;
+ }
+
+ private const importantLines:Vector. = new [];
+
+ override public function checkTargetBounds(latestLine:TextLine):Boolean
+ {
+ if(super.checkTargetBounds(latestLine))
+ return true;
+
+ importantLines.push(latestLine);
+
+ return false;
+ }
+
+ override public function postTextBlock(block:TextBlock):void
+ {
+ super.postTextBlock(block);
+
+ var props:LayoutProperties = TinytlfUtil.getLP(block);
+ var info:TextBlockInfo = new TextBlockInfo(block);
+
+ if(target.hasLine(block.firstLine))
+ props.height = 0;
+
+ var lines:Vector. = info.lines;
+ for(var i:int = 0, n:int = lines.length; i < n; i += 1)
+ {
+ props.height += lines[i].height + props.leading;
+ }
+ }
+
+ override public function postLayout():void
+ {
+ super.postLayout();
+
+ //Attempt to calculate the measured height of the target container.
+
+ var measuredHeight:Number = 0;
+
+ // A dictionary of blocks, but also stores the number of lines
+ // from each block that exist in this container.
+ var blocks:Dictionary = new Dictionary(true);
+ var info:TextBlockInfo;
+ var block:TextBlock;
+ var props:LayoutProperties;
+
+ for(var i:int = 0, n:int = importantLines.length; i < n; i += 1)
+ {
+ if(importantLines[i].validity != TextLineValidity.VALID)
+ continue;
+
+ block = importantLines[i].textBlock;
+
+ if(block in blocks)
+ continue;
+
+ blocks[block] = true;
+
+ info = new TextBlockInfo(block);
+ props = info.props;
+
+ if(target.hasLine(block.firstLine))
+ measuredHeight += props.paddingTop;
+
+ if(info.rendered && target.hasLine(block.lastLine))
+ measuredHeight += props.paddingBottom;
+
+ measuredHeight += props.height;
+ }
+
+ target.measuredHeight = measuredHeight;
+
+ //Don't hold onto these lines, they will be reused later.
+ importantLines.length = 0;
+ }
+ }
+}
+
+import flash.text.engine.*;
+
+import org.tinytlf.layout.properties.LayoutProperties;
+import org.tinytlf.util.TinytlfUtil;
+import org.tinytlf.util.fte.TextBlockUtil;
+
+internal class TextBlockInfo
+{
+ public function TextBlockInfo(block:TextBlock)
+ {
+ _rendered = !TextBlockUtil.isInvalid(block);
+ lp = TinytlfUtil.getLP(block);
+
+ var line:TextLine = block.firstLine;
+ while(line)
+ {
+ ++allLines;
+ textLines.push(line);
+ line = line.nextLine;
+ }
+ }
+
+ private const textLines:Vector. = new [];
+
+ public function get lines():Vector.
+ {
+ return textLines.concat();
+ }
+
+ private var lineCount:int = 0;
+ public function get numLines():int
+ {
+ return lineCount;
+ }
+
+ public function set numLines(value:int):void
+ {
+ lineCount = value;
+ }
+
+ private var allLines:int = 0;
+ public function get totalLines():int
+ {
+ return allLines;
+ }
+
+ private var _rendered:Boolean = false;
+ public function get rendered():Boolean
+ {
+ return _rendered;
+ }
+
+ private var lp:LayoutProperties;
+ public function get props():LayoutProperties
+ {
+ return lp;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/LTRMajor.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/LTRMajor.as
new file mode 100755
index 0000000..a2955eb
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/LTRMajor.as
@@ -0,0 +1,238 @@
+package org.tinytlf.layout.orientation.horizontal
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.*;
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.layout.constraints.horizontal.HConstraintFactory;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.TinytlfUtil;
+
+ /**
+ * The IMajorOrientation implementation for left-to-right languages.
+ */
+ public class LTRMajor extends HOrientationBase
+ {
+ public function LTRMajor(target:IConstraintTextContainer)
+ {
+ super(target);
+
+ target.constraintFactory = new HConstraintFactory();
+ }
+
+ private var x:Number = 0;
+ private var leftConstraint:Number = 0;
+ private var rightConstraint:Number = 0;
+
+ override public function get value():Number
+ {
+ return x;
+ }
+
+ override public function preLayout():void
+ {
+ super.preLayout();
+
+ x = 0;
+ leftConstraint = 0;
+ rightConstraint = getTotalSize();
+ }
+
+ override public function prepForTextBlock(block:TextBlock, line:TextLine):void
+ {
+ super.prepForTextBlock(block, line);
+
+ // Search through the list of constraints, removing any that exist
+ // after the line we're starting from. If line is null, we know
+ // we're re-rendering starting from the first line, so this will
+ // remove all constraints for this textBlock.
+ //
+ // We have to do this because we'll recreate the constraint when
+ // we re-render the line that the constraint exists in.
+
+ var constraints:Vector. = target.constraints;
+ var n:int = constraints.length;
+ var c:ITextConstraint;
+ var elem:ContentElement;
+
+ var lineBlockBeginIndex:int = line ? line.textBlockBeginIndex : -1;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ c = constraints[i];
+ if(c.content is ContentElement)
+ {
+ elem = ContentElement(c.content);
+ if(elem.textBlock == block)
+ if(elem.textBlockBeginIndex > lineBlockBeginIndex)
+ target.removeConstraint(c);
+ }
+ }
+
+ evaluateConstraints(block);
+ }
+
+ override public function getLineSize(block:TextBlock, previousLine:TextLine):Number
+ {
+ evaluateConstraints(block);
+
+ return rightConstraint - leftConstraint;
+ }
+
+ override public function position(line:TextLine):void
+ {
+ evaluateConstraints(line);
+
+ var lp:LayoutProperties = TinytlfUtil.getLP(line);
+
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ positionLeft(line);
+ break;
+ case TextAlign.CENTER:
+ positionCenter(line);
+ break;
+ case TextAlign.RIGHT:
+ positionRight(line);
+ break;
+ }
+ }
+
+ override protected function handleConstraint(line:TextLine, constraint:ITextConstraint):void
+ {
+ super.handleConstraint(line, constraint);
+
+ if(!constraint.constraintMarker)
+ return;
+
+ if(constraint.float)
+ {
+ switch(constraint.float)
+ {
+ case TextFloat.LEFT:
+ constraint.majorValue = leftConstraint;
+ break;
+ case TextFloat.RIGHT:
+ constraint.majorValue = rightConstraint - constraint.majorSize;
+ break;
+ }
+
+ line.x = constraint.majorValue;
+ }
+
+ evaluateConstraints(line);
+ }
+
+ private function evaluateConstraints(around:Object):void
+ {
+ var c:ITextConstraint;
+ var constraints:Vector. = target.constraints;
+ var n:int = constraints.length;
+
+ var minorValue:Number = target.minorDirection.value;
+ var l:Number = 0;
+ var totalWidth:Number = getTotalSize();
+ var r:Number = totalWidth;
+ var majorValue:Number = -1;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ c = constraints[i];
+
+ majorValue = c.getMajorValue(minorValue, l);
+
+ if(majorValue == -1)
+ continue;
+
+ if(c.float)
+ {
+ if(c.float == TextFloat.LEFT)
+ {
+ if(majorValue >= l)
+ {
+ l = majorValue;
+ }
+ }
+ else if(c.float == TextFloat.RIGHT)
+ {
+ if(majorValue < r)
+ {
+ r = majorValue;
+ }
+ }
+ }
+ else
+ {
+ if(c.majorValue <= l)
+ l = majorValue;
+ if(c.majorValue >= r)
+ r = c.majorValue;
+ }
+ }
+
+ leftConstraint = Math.min(l, totalWidth);
+ rightConstraint = Math.max(r, 0);
+
+ var lp:LayoutProperties = TinytlfUtil.getLP(around);
+ var indent:Number = 0;
+
+ if(around is TextBlock)
+ {
+ var block:TextBlock = TextBlock(around);
+ if(block.firstLine == null || block.firstInvalidLine == block.firstLine)
+ {
+ indent = lp.textIndent;
+ }
+ }
+
+ if(around is TextLine && !TextLine(around).previousLine)
+ {
+ indent += lp.textIndent;
+ }
+
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ leftConstraint += indent;
+ x = leftConstraint;
+ break;
+ case TextAlign.CENTER:
+ leftConstraint += indent;
+ x = (totalWidth * .5) + (leftConstraint * .5) - (rightConstraint * .5);
+ break;
+ case TextAlign.RIGHT:
+ rightConstraint -= indent;
+ x = rightConstraint;
+ break;
+ }
+ }
+
+ private function positionLeft(line:TextLine):void
+ {
+ line.x = leftConstraint;
+ x = leftConstraint + line.specifiedWidth;
+ if(x >= rightConstraint)
+ x = getTotalSize(line);
+ }
+
+ private function positionCenter(line:TextLine):void
+ {
+ line.x = x;
+ x += line.specifiedWidth;
+
+ if(x > rightConstraint)
+ x = getTotalSize(line);
+ }
+
+ private function positionRight(line:TextLine):void
+ {
+ line.x = rightConstraint - line.specifiedWidth;
+ x = line.x;
+ if(x <= leftConstraint)
+ x = 0;
+ }
+ }
+}
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/RTLMajor.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/RTLMajor.as
new file mode 100755
index 0000000..f6c591b
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/horizontal/RTLMajor.as
@@ -0,0 +1,176 @@
+package org.tinytlf.layout.orientation.horizontal
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.*;
+ import org.tinytlf.layout.constraints.*;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.TinytlfUtil;
+
+ /**
+ * The IMajorOrientation implementation for right-to-left languages.
+ *
+ * TODO: implement!
+ */
+ public class RTLMajor extends HOrientationBase
+ {
+ public function RTLMajor(target:IConstraintTextContainer)
+ {
+ super(target);
+ }
+
+ override public function preLayout():void
+ {
+ super.preLayout();
+
+ x = 0;
+ }
+
+ private var x:Number = 0;
+
+ override public function prepForTextBlock(block:TextBlock, line:TextLine):void
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+
+ if(line)
+ {
+ if(target.hasLine(line))
+ {
+ var totalWidth:Number = getTotalSize(block);
+
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ x = 0;
+ break;
+ case TextAlign.RIGHT:
+ x = totalWidth;
+ break;
+ }
+ }
+
+ return;
+ }
+
+ x = lp.textIndent;
+ }
+
+ override public function getLineSize(block:TextBlock, previousLine:TextLine):Number
+ {
+ var totalSize:Number = super.getLineSize(block, previousLine);
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ return sizeFromLeft(totalSize, previousLine);
+ break;
+ case TextAlign.RIGHT:
+ return sizeFromRight(totalSize, previousLine);
+ break;
+ }
+
+ return totalSize;
+ }
+
+ override public function position(line:TextLine):void
+ {
+ //position here, don't rely on the sizing method to set the position.
+ var lp:LayoutProperties = TinytlfUtil.getLP(line);
+ switch(lp.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ positionFromLeft(line);
+ break;
+ case TextAlign.RIGHT:
+ break;
+ }
+ }
+
+ override public function get value():Number
+ {
+ return x;
+ }
+
+ private function sizeFromLeft(total:Number, previousLine:TextLine):Number
+ {
+ var elements:Vector. = target.constraints;
+ var el:ITextConstraint;
+
+ var size:Number = total;
+ var xPos:Number = x;
+ var n:int = elements.length;
+
+ var elX:Number = 0;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ el = elements[i];
+
+ elX = el.getMajorValue(target.minorDirection.value, xPos);
+
+ // If there's no major direction value for this constraint, it
+ // musn't exist within this minor direction. Skip it.
+ if(elX == -1)
+ continue;
+
+ // Current xPos doesn't intersect with the constraint.
+ // it can be either on the left or right.
+ if(elX == xPos)
+ {
+ // If xPos is to the left of the majorValue, optionally
+ // update the size.
+ if(xPos < el.majorValue)
+ {
+ size = Math.min(size, el.majorValue - xPos);
+ }
+
+ // if xPos is to the right, we don't care about this constraint,
+ // so we don't have anything to update.
+ }
+ // Otherwise, the xPos intersected with the bounds of the
+ // constraint. update xPos and move on.
+ else
+ {
+ xPos = elX;
+ }
+ }
+
+ return size;
+ }
+
+ private function positionFromLeft(line:TextLine):void
+ {
+ line.x = x;
+ x += line.specifiedWidth;
+
+ var el:ITextConstraint;
+ var constraints:Vector. = target.constraints;
+ var n:int = constraints.length;
+ var elX:Number = 0;
+
+ var y:Number = target.minorDirection.value;
+
+ for(var i:int = 0; i < n; i += 1)
+ {
+ el = constraints[i];
+ elX = el.getMajorValue(y, x);
+
+ if(elX == -1)
+ continue;
+
+ x = elX;
+ }
+ }
+
+ private function sizeFromRight(total:Number, previousLine:TextLine):Number
+ {
+ //TODO: implement
+ var xPos:Number = total;
+
+ return total;
+ }
+ }
+}
diff --git a/tinytlf-layouts/src/org/tinytlf/layout/orientation/vertical/VOrientationBase.as b/tinytlf-layouts/src/org/tinytlf/layout/orientation/vertical/VOrientationBase.as
new file mode 100644
index 0000000..962a2ae
--- /dev/null
+++ b/tinytlf-layouts/src/org/tinytlf/layout/orientation/vertical/VOrientationBase.as
@@ -0,0 +1,69 @@
+package org.tinytlf.layout.orientation.vertical
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.IConstraintTextContainer;
+ import org.tinytlf.layout.properties.*;
+ import org.tinytlf.util.TinytlfUtil;
+ import org.tinytlf.layout.orientation.TextFlowOrientationBase;
+
+ public class VOrientationBase extends TextFlowOrientationBase
+ {
+ public function VOrientationBase(target:IConstraintTextContainer)
+ {
+ super(target);
+ }
+
+ override public function getLineSize(block:TextBlock, previousLine:TextLine):Number
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(block);
+ var totalHeight:Number = getTotalSize(block);
+
+ if(previousLine == null)
+ totalHeight -= lp.textIndent;
+
+ return totalHeight - lp.paddingTop - lp.paddingBottom;
+ }
+
+ override public function position(line:TextLine):void
+ {
+ var props:LayoutProperties = TinytlfUtil.getLP(line);
+ var totalHeight:Number = getTotalSize(line);
+
+ var lineWidth:Number = line.width;
+ var y:Number = 0;
+
+ if(!line.previousLine)
+ y += props.textIndent;
+
+ switch(props.textAlign)
+ {
+ case TextAlign.LEFT:
+ case TextAlign.JUSTIFY:
+ y += props.paddingLeft;
+ break;
+ case TextAlign.CENTER:
+ y = (totalHeight - lineWidth) * 0.5;
+ break;
+ case TextAlign.RIGHT:
+ y = totalHeight - lineWidth + props.paddingRight;
+ break;
+ }
+
+ line.y = y;
+ }
+
+ override protected function getTotalSize(from:Object = null):Number
+ {
+ var lp:LayoutProperties = TinytlfUtil.getLP(from);
+
+ if(lp.height)
+ return lp.height;
+
+ if(target.explicitHeight != target.explicitHeight)
+ return TextLine.MAX_LINE_WIDTH;
+
+ return target.explicitHeight;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLBlockFactory.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLBlockFactory.as
new file mode 100644
index 0000000..5403cc7
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLBlockFactory.as
@@ -0,0 +1,74 @@
+package org.tinytlf.conversion
+{
+ import flash.text.engine.*;
+
+ import org.tinytlf.xml.TagSoup;
+
+ public class HTMLBlockFactory extends TextBlockFactoryBase
+ {
+ public function HTMLBlockFactory()
+ {
+ super();
+ }
+
+ override public function getTextBlock(index:int):TextBlock
+ {
+ if(index >= numBlocks)
+ return null;
+
+ var block:TextBlock = contentVirtualizer.getItemFromIndex(index);
+
+ if(!block)
+ {
+ var paragraph:IHTMLNode = new HTMLNode(paragraphs[index]);
+ paragraph.mergeWith(engine.styler);
+ paragraph.mergeWith(engine.styler.describeElement(paragraph.inheritanceList.split(' ')));
+ block = textBlockGenerator.generate(paragraph, getElementFactory(paragraph.name));
+ }
+
+ if(!block)
+ return null;
+
+ contentVirtualizer.enqueueAt(block, index, block.content.rawText.length);
+
+ return block;
+ }
+
+ private var paragraphs:XMLList = new XMLList();
+
+ override public function set data(value:Object):void
+ {
+ if(value is String)
+ {
+ XML.prettyPrinting = false;
+ XML.ignoreWhitespace = false;
+ value = TagSoup.toXML(String(value), true);
+ }
+
+ if(value is XMLList)
+ value = {value}
+
+ if(value is XML)
+ paragraphs = XML(value).*;
+
+ super.data = value;
+ }
+
+ override public function get numBlocks():int
+ {
+ return paragraphs.length();
+ }
+
+ override public function getElementFactory(element:*):IContentElementFactory
+ {
+ if(!hasElementFactory(element))
+ {
+ var adapter:IContentElementFactory = new HTMLNodeElementFactory();
+ IContentElementFactory(adapter).engine = engine;
+ return adapter;
+ }
+
+ return super.getElementFactory(element);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLColumnBreakAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLColumnBreakAdapter.as
new file mode 100755
index 0000000..a9401c7
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLColumnBreakAdapter.as
@@ -0,0 +1,17 @@
+package org.tinytlf.conversion
+{
+ import flash.display.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class HTMLColumnBreakAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ...parameters):ContentElement
+ {
+ var graphic:GraphicElement = new GraphicElement(new Shape(), 0, 0, ef);
+ graphic.userData = 'containerTerminator';
+ return ContentElementUtil.lineBreakBefore(graphic);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLHorizontalRuleAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLHorizontalRuleAdapter.as
new file mode 100755
index 0000000..00e9273
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLHorizontalRuleAdapter.as
@@ -0,0 +1,18 @@
+package org.tinytlf.conversion
+{
+ import flash.display.*;
+ import flash.events.EventDispatcher;
+ import flash.text.engine.*;
+
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class HTMLHorizontalRuleAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ...context):ContentElement
+ {
+ var graphic:GraphicElement = new GraphicElement(new Shape(), 0, 0, ef, new EventDispatcher());
+ engine.decor.decorate(graphic, {horizontalRule:true});
+ return ContentElementUtil.lineBreakBefore(graphic, 'lineBreak');
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLImageAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLImageAdapter.as
new file mode 100755
index 0000000..fd1a08a
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLImageAdapter.as
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.conversion
+{
+ import flash.display.Shape;
+ import flash.events.EventDispatcher;
+ import flash.net.URLRequest;
+ import flash.text.engine.*;
+
+ import org.tinytlf.layout.properties.LayoutProperties;
+ import org.tinytlf.util.fte.*;
+
+ public class HTMLImageAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ...context:Array):ContentElement
+ {
+ var img:IHTMLNode = data as IHTMLNode;
+ var lp:LayoutProperties = new LayoutProperties(img);
+
+ var format:ElementFormat = getElementFormat(context);
+ format.dominantBaseline = TextBaseline.IDEOGRAPHIC_TOP;
+
+ var element:ContentElement;
+
+ if(img['float'])
+ {
+ element = new GraphicElement(
+ new ImageLoader(img['src'], lp),
+ lp.width,
+ lp.height,
+ format,
+ getEventMirror(img) || new EventDispatcher());
+
+ element.userData = img;
+
+ //Decorate this element?
+ engine.decor.decorate(element, img, img['layer'], null, img['foreground']);
+
+ var lBreakGraphic:GraphicElement = new GraphicElement(new Shape(),0, 0, new ElementFormat());
+ lBreakGraphic.userData = 'lineBreak';
+
+ return ContentElementUtil.lineBreakBeforeAndAfter(
+ new GroupElement(new [element, lBreakGraphic]));
+ }
+
+ element = new GraphicElement(
+ new ImageLoader(img['src'], lp),
+ lp.width + lp.paddingLeft + lp.paddingRight,
+ lp.height + lp.paddingTop + lp.paddingBottom,
+ format, getEventMirror(img) || new EventDispatcher());
+
+ element.userData = img;
+
+ engine.decor.decorate(element, img, img['layer'], null, img['foreground']);
+
+ return element;
+ }
+ }
+}
+
+import flash.display.*;
+import flash.events.*;
+import flash.geom.Matrix;
+import flash.net.URLRequest;
+
+import org.tinytlf.layout.properties.LayoutProperties;
+
+internal class ImageLoader extends Sprite
+{
+ private var lp:LayoutProperties;
+
+ public function ImageLoader(src:String, lp:LayoutProperties)
+ {
+ this.lp = lp;
+
+ graphics.beginFill(0x00, 0);
+ graphics.drawRect(0, 0, lp.width, lp.height);
+
+ var loader:Loader = new Loader();
+ loader.load(new URLRequest(src));
+ loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
+ loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
+ }
+
+ private function onError(event:IOErrorEvent):void
+ {
+ var li:LoaderInfo = LoaderInfo(event.target);
+ li.removeEventListener(event.type, onError);
+ trace(event.toString());
+ }
+
+ private function onComplete(event:Event):void
+ {
+ var li:LoaderInfo = LoaderInfo(event.target);
+ li.removeEventListener(event.type, onComplete);
+
+ var child:Bitmap = Bitmap(li.content);
+
+ var m:Matrix = new Matrix();
+ m.scale(lp.width / child.width, lp.height / child.height);
+ var bmd:BitmapData = new BitmapData(lp.width, lp.height);
+ bmd.draw(child, m);
+
+ var bitmap:Bitmap = new Bitmap(bmd);
+ addChild(bitmap);
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLLineBreakAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLLineBreakAdapter.as
new file mode 100755
index 0000000..7ecbf7b
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLLineBreakAdapter.as
@@ -0,0 +1,15 @@
+package org.tinytlf.conversion
+{
+ import flash.display.*;
+ import flash.text.engine.*;
+
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class HTMLLineBreakAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ...parameters):ContentElement
+ {
+ return ContentElementUtil.lineBreakBefore(new GraphicElement(new Shape(), 0, 0, ef), 'lineBreak');
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListAdapter.as
new file mode 100755
index 0000000..b59e8e2
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListAdapter.as
@@ -0,0 +1,19 @@
+package org.tinytlf.conversion
+{
+ import flash.text.engine.ContentElement;
+
+ import org.tinytlf.util.fte.ContentElementUtil;
+
+ public class HTMLListAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ...context:Array):ContentElement
+ {
+ //Continue parsing the children
+ var listElement:ContentElement = super.execute.apply(null, [data].concat(context));
+
+ return (data is XML) ?
+ ContentElementUtil.lineBreakBefore(listElement) :
+ listElement;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListItemAdapter.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListItemAdapter.as
new file mode 100755
index 0000000..bbaf8a8
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLListItemAdapter.as
@@ -0,0 +1,41 @@
+package org.tinytlf.conversion
+{
+ import flash.display.Shape;
+ import flash.geom.Rectangle;
+ import flash.text.engine.*;
+
+ import org.tinytlf.util.fte.*;
+
+ public class HTMLListItemAdapter extends HTMLNodeElementFactory
+ {
+ override public function execute(data:Object, ... context:Array):ContentElement
+ {
+ var item:ContentElement = super.execute.apply(null, [data].concat(context));
+
+ if(data is XML)
+ {
+ var styles:Object = engine.styler.describeElement(context);
+ var outside:Boolean = styles.listStylePosition == 'outside';
+ var marginLeft:Number = styles.marginLeft || 25;
+
+ var graphic:GraphicElement =
+ new GraphicElement(new Shape(), marginLeft, 0, new ElementFormat());
+
+ graphic.userData = outside ? 'listItemOutside' : 'listItemInside';
+
+ var end:GraphicElement = new GraphicElement(new Shape(), 0, 0, new ElementFormat());
+ end.userData = 'listItemTerminator';
+
+ var box:Rectangle = item.elementFormat.getFontMetrics().emBox;
+ engine.decor.decorate(graphic, {bullet: true, diameter: box.height * .25});
+
+ return ContentElementUtil.lineBreakBeforeAndAfter(
+ new GroupElement(new [graphic, item, end]));
+ }
+ else
+ {
+ return item;
+ }
+ }
+ }
+}
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNode.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNode.as
new file mode 100644
index 0000000..21c1c10
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNode.as
@@ -0,0 +1,87 @@
+package org.tinytlf.conversion
+{
+ import flash.utils.flash_proxy;
+
+ import org.tinytlf.styles.StyleAwareActor;
+ import org.tinytlf.util.XMLUtil;
+
+ use namespace flash_proxy;
+
+ public dynamic class HTMLNode extends StyleAwareActor implements IHTMLNode
+ {
+ public function HTMLNode(node:XML, parent:IHTMLNode = null)
+ {
+ super(XMLUtil.buildKeyValueAttributes(node.attributes()));
+
+ xml = node;
+
+ this.parent = parent;
+
+ inheritance = (parent ? parent.inheritanceList : '* body') + ' ' + describeInheritance(node);
+ }
+
+ override flash_proxy function getDescendants(name:*):*
+ {
+ return xml..*;
+ }
+
+ private var xml:XML = <_/>;
+
+ override flash_proxy function getProperty(name:*):*
+ {
+ if(name.toString() == "*")
+ return children;
+
+ return super.getProperty(name);
+ }
+
+ public function get children():XMLList
+ {
+ return xml.*;
+ }
+
+ private var inheritance:String = '';
+
+ public function get inheritanceList():String
+ {
+ return inheritance;
+ }
+
+ private function describeInheritance(node:XML):String
+ {
+ var str:String = node.localName();
+
+ if(node.attribute('class').length())
+ str += ' .' + node.attribute('class');
+ if(node.attribute('id').length())
+ str += ' #' + node.@id;
+
+ return str;
+ }
+
+ public function get name():String
+ {
+ return xml.localName();
+ }
+
+ public function get text():String
+ {
+ if(xml.nodeKind() == 'text')
+ return xml.toString();
+
+ return xml.text().toString();
+ }
+
+ private var p:IHTMLNode;
+
+ public function get parent():IHTMLNode
+ {
+ return p;
+ }
+
+ public function set parent(value:IHTMLNode):void
+ {
+ p = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNodeElementFactory.as b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNodeElementFactory.as
new file mode 100644
index 0000000..79e496f
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/HTMLNodeElementFactory.as
@@ -0,0 +1,52 @@
+package org.tinytlf.conversion
+{
+ import flash.events.EventDispatcher;
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.ElementFormat;
+ import flash.text.engine.GroupElement;
+ import flash.text.engine.TextElement;
+
+ public class HTMLNodeElementFactory extends ContentElementFactory
+ {
+ override public function execute(data:Object, ...parameters):ContentElement
+ {
+ if(data is IHTMLNode)
+ {
+ var node:IHTMLNode = data as IHTMLNode;
+ var n:int = node.children.length();
+
+ var element:ContentElement;
+ var ef:ElementFormat = getElementFormat(node);
+ var em:EventDispatcher = getEventMirror(node.name);
+
+ if(n <= 1)
+ {
+ element = new TextElement(node.text, ef, em);
+ }
+ else
+ {
+ var factory:ITextBlockFactory = engine.blockFactory;
+ var elements:Vector. = new [];
+ var children:XMLList = node.children;
+ var child:IHTMLNode;
+
+ for(var i:int = 0; i < n; ++i)
+ {
+ child = new HTMLNode(children[i], node);
+ child.mergeWith(node);
+ child.mergeWith(engine.styler.describeElement(child.inheritanceList.split(' ')));
+
+ elements.push(factory.getElementFactory(child.name).execute(child));
+ }
+
+ element = new GroupElement(elements, ef);
+ }
+
+ element.userData = node;
+ return element;
+ }
+
+ return super.execute.apply([data].concat(parameters));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/conversion/IHTMLNode.as b/tinytlf-xhtml/src/org/tinytlf/conversion/IHTMLNode.as
new file mode 100644
index 0000000..69df7ab
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/conversion/IHTMLNode.as
@@ -0,0 +1,15 @@
+package org.tinytlf.conversion
+{
+ import org.tinytlf.styles.IStyleAware;
+
+ public interface IHTMLNode extends IStyleAware
+ {
+ function get name():String;
+ function get children():XMLList;
+ function get text():String;
+ function get inheritanceList():String;
+
+ function get parent():IHTMLNode;
+ function set parent(value:IHTMLNode):void;
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/interaction/AnchorMirror.as b/tinytlf-xhtml/src/org/tinytlf/interaction/AnchorMirror.as
new file mode 100755
index 0000000..26600e2
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/interaction/AnchorMirror.as
@@ -0,0 +1,100 @@
+package org.tinytlf.interaction
+{
+ import flash.events.*;
+ import flash.net.*;
+ import flash.ui.*;
+
+ import org.tinytlf.conversion.IHTMLNode;
+
+ public class AnchorMirror extends CSSMirror
+ {
+ [Event("mouseUp")]
+ override public function up():void
+ {
+ if(cssState != ACTIVE)
+ return super.up();
+
+ var request:URLRequest = getURLRequest();
+ var node:IHTMLNode = content.userData as IHTMLNode;
+
+ //If there's an href, launch the URL.
+ if(request)
+ {
+ navigateToURL(request, node['target'] || '_blank');
+ }
+ else
+ {
+ //Otherwise, try to dispatch an event from the line.
+ var href:String = getLink();
+ if(href.indexOf('event:') == 0)
+ {
+ line.dispatchEvent(new TextEvent(TextEvent.LINK, true, false,
+ href.substr(6) || content.text));
+ }
+ }
+
+ super.up();
+ }
+
+ [Event("rollOver")]
+ [Event("mouseOver")]
+ override public function over(event:MouseEvent):void
+ {
+ super.over(event);
+
+ applyLink();
+ }
+
+ [Event("rollOut")]
+ [Event("mouseOut")]
+ override public function out(event:MouseEvent):void
+ {
+ super.out(event);
+
+ unapplyLink();
+ }
+
+ [Event("mouseMove")]
+ override public function move():void
+ {
+ if(cssState == HOVER)
+ applyLink();
+ else if(cssState == VISITED || cssState == NORMAL)
+ unapplyLink();
+ }
+
+ private function applyLink():void
+ {
+ var menu:ContextMenu = line.contextMenu;
+
+ if(menu)
+ menu.link = getURLRequest();
+ }
+
+ private function unapplyLink():void
+ {
+ var menu:ContextMenu = line.contextMenu;
+
+ if(!menu) return;
+
+ menu.link = null;
+ menu.clipboardMenu = true;
+ }
+
+ private function getURLRequest():URLRequest
+ {
+ var href:String = getLink();
+
+ if(/event:/i.test(href) == false)
+ return new URLRequest(href);
+
+ return null;
+ }
+
+ private function getLink():String
+ {
+ var node:IHTMLNode = content.userData as IHTMLNode;
+ return node['href'];
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/interaction/CSSMirror.as b/tinytlf-xhtml/src/org/tinytlf/interaction/CSSMirror.as
new file mode 100644
index 0000000..9e3f08d
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/interaction/CSSMirror.as
@@ -0,0 +1,135 @@
+package org.tinytlf.interaction
+{
+ import flash.events.MouseEvent;
+ import flash.text.engine.ElementFormat;
+ import flash.ui.Mouse;
+ import flash.ui.MouseCursor;
+
+ import org.tinytlf.conversion.IHTMLNode;
+ import org.tinytlf.decor.ITextDecor;
+ import org.tinytlf.util.TinytlfUtil;
+
+ public class CSSMirror extends EventMirrorBase
+ {
+ protected static const NORMAL:String = 'normal';
+ protected static const ACTIVE:String = 'active';
+ protected static const HOVER:String = 'hover';
+ protected static const VISITED:String = 'visited';
+
+ private var _cssState:String = NORMAL;
+ protected function get cssState():String
+ {
+ return _cssState;
+ }
+
+ protected function set cssState(value:String):void
+ {
+ repealCSSProperties(cssState);
+
+ _cssState = value;
+
+ applyCSSProperties(cssState);
+ }
+
+ protected var visited:Boolean = false;
+
+ [Event("mouseUp")]
+ public function up():void
+ {
+ visited ||= cssState == ACTIVE;
+ cssState = visited ? VISITED : NORMAL;
+ }
+
+ [Event("mouseDown")]
+ public function down():void
+ {
+ cssState = ACTIVE;
+ }
+
+ [Event("mouseMove")]
+ public function move():void
+ {
+ applyCSSProperties(cssState);
+ }
+
+ [Event("rollOver")]
+ [Event("mouseOver")]
+ public function over(event:MouseEvent):void
+ {
+ if(cssState == ACTIVE && event.buttonDown)
+ {
+ line.stage.removeEventListener(MouseEvent.MOUSE_UP, outUp, true);
+ cssState = ACTIVE;
+ }
+ else
+ {
+ cssState = HOVER;
+ }
+ }
+
+ [Event("rollOut")]
+ [Event("mouseOut")]
+ public function out(event:MouseEvent):void
+ {
+ if(cssState == ACTIVE && event.buttonDown)
+ {
+ line.stage.addEventListener(MouseEvent.MOUSE_UP, outUp, true);
+ cssState = ACTIVE;
+ }
+ else
+ {
+ cssState = visited ? VISITED : NORMAL;
+ }
+ }
+
+ protected function outUp(event:MouseEvent):void
+ {
+ event.currentTarget.removeEventListener(event.type, outUp, true);
+ cssState = visited ? VISITED : NORMAL;
+ }
+
+ protected function applyCSSProperties(state:String):void
+ {
+ var props:Object = resolveCSSProperties(state);
+
+ if(!props)
+ return;
+
+ applyFormat(props);
+ engine.decor.decorate(content, props);
+ Mouse.cursor = props['cursor'] || MouseCursor.AUTO;
+ }
+
+ protected function repealCSSProperties(state:String):void
+ {
+ var props:Object = resolveCSSProperties(state);
+ if(!props)
+ return;
+
+ applyFormat(props);
+
+ var decor:ITextDecor = engine.decor;
+
+ for(var prop:String in props)
+ decor.undecorate(content, prop);
+
+ Mouse.cursor = props['cursor'] || MouseCursor.AUTO;
+ }
+
+ protected function applyFormat(properties:Object):void
+ {
+ var format:ElementFormat = engine.styler.getElementFormat(properties);
+ if(TinytlfUtil.compareObjectValues(content.elementFormat, format, {locked:true}) == false)
+ {
+ content.elementFormat = format;
+ engine.invalidateLines();
+ }
+ }
+
+ protected function resolveCSSProperties(state:String):Object
+ {
+ var node:IHTMLNode = content.userData as IHTMLNode;
+ return node ? node[state + ':'] : {};
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/interaction/CascadingTextInteractor.as b/tinytlf-xhtml/src/org/tinytlf/interaction/CascadingTextInteractor.as
new file mode 100755
index 0000000..95ee288
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/interaction/CascadingTextInteractor.as
@@ -0,0 +1,36 @@
+package org.tinytlf.interaction
+{
+ import flash.events.EventDispatcher;
+
+ import org.tinytlf.conversion.IHTMLNode;
+
+ public class CascadingTextInteractor extends GestureInteractor
+ {
+ public function CascadingTextInteractor()
+ {
+ super();
+ }
+
+ override public function getMirror(element:* = null):EventDispatcher
+ {
+ if(element is XML)
+ {
+ var mirror:EventDispatcher;
+ var node:IHTMLNode = element as IHTMLNode;
+ while(node)
+ {
+ mirror = super.getMirror(node.name);
+
+ if(mirror)
+ return mirror;
+
+ node = node.parent;
+ }
+
+ return null;
+ }
+
+ return super.getMirror(element);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/interaction/EventMirrorBase.as b/tinytlf-xhtml/src/org/tinytlf/interaction/EventMirrorBase.as
new file mode 100755
index 0000000..54d6201
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/interaction/EventMirrorBase.as
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.interaction
+{
+ import flash.events.*;
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.TextLine;
+ import flash.utils.*;
+
+ import org.tinytlf.ITextEngine;
+ import org.tinytlf.util.TinytlfUtil;
+
+ /**
+ * EventMirrorBase is a nice base class for classes which
+ * handle TLMR-based interaction in tinytlf.
+ */
+ public class EventMirrorBase extends EventDispatcher
+ {
+ public function EventMirrorBase(target:IEventDispatcher = null)
+ {
+ super(target);
+
+ attachListeners(this);
+ }
+
+ public function attachListeners(target:IEventDispatcher):void
+ {
+ var listeners:XMLList = TinytlfUtil.describeType(this).factory.method.(child('metadata').(@name == 'Event').length());
+
+ var methodName:String;
+ var type:String;
+ var events:XMLList;
+
+ for each(var listener:XML in listeners)
+ {
+ methodName = listener.attribute('name').toString();
+ events = listener.metadata.(@name == 'Event');
+ for each(var meta:XML in events)
+ {
+ type = meta.arg.@value.toString();
+ target.addEventListener(type, this[methodName], false, 0, true);
+ }
+ }
+ }
+
+ protected const listeners:Object = {};
+
+ override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
+ {
+ super.addEventListener(type, execute, useCapture, priority, useWeakReference);
+
+ listeners[type] = listener;
+ }
+
+ override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
+ {
+ super.removeEventListener(type, listener, useCapture);
+
+ if(listeners.hasOwnProperty(type))
+ delete listeners[type];
+ }
+
+ protected var info:EventLineInfo;
+ protected var line:TextLine;
+ protected var content:ContentElement;
+ protected var engine:ITextEngine;
+ protected var event:Event;
+
+ protected final function execute(event:Event):void
+ {
+ var type:String = event.type;
+
+ if(type in blockedEvents && blockedEvents[type] == true)
+ {
+ delete blockedEvents[type];
+ return;
+ }
+
+ info = EventLineInfo.getInfo(event);
+ if(!info)
+ return;
+
+ engine = info.engine;
+ line = info.line;
+ content = info.element;
+ this.event = event;
+
+ if(type in listeners)
+ {
+ var func:Function = listeners[type];
+ func.length ? func(event) : func();
+ }
+
+ event = null;
+ }
+
+ private var blockedEvents:Object = {};
+ protected final function blockEvent(type:String):void
+ {
+ blockedEvents[type] = true;
+ }
+
+ protected final function unblockEvent(type:String):void
+ {
+ delete blockedEvents[type];
+ }
+
+ protected final function blockAllEvents():void
+ {
+ for(var type:String in listeners)
+ blockedEvents[type] = true;
+ }
+
+ protected final function unblockAllEvents():void
+ {
+ blockedEvents = {};
+ }
+
+ public function destroy():void
+ {
+ engine = null;
+ line = null;
+ content = null;
+ event = null;
+ blockedEvents = null;
+ for(var type:String in listeners)
+ {
+ removeEventListener(type, execute);
+ delete listeners[type];
+ }
+ }
+ }
+}
+
diff --git a/tinytlf-xhtml/src/org/tinytlf/layout/constraints/HTMLConstraintFactory.as b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/HTMLConstraintFactory.as
new file mode 100644
index 0000000..0813d7f
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/HTMLConstraintFactory.as
@@ -0,0 +1,30 @@
+package org.tinytlf.layout.constraints
+{
+ import flash.text.engine.ContentElement;
+ import flash.text.engine.TextLine;
+
+ import org.tinytlf.layout.constraints.horizontal.HConstraintFactory;
+ import org.tinytlf.util.fte.TextLineUtil;
+
+ public class HTMLConstraintFactory extends HConstraintFactory
+ {
+ override public function getConstraint(line:TextLine, atomIndex:int):ITextConstraint
+ {
+ var el:ContentElement = TextLineUtil.getElementAtAtomIndex(line, atomIndex);
+
+ switch(el.userData)
+ {
+ case null:
+ case undefined:
+ case 'lineBreak':
+ return null;
+ case 'listItemOutside':
+ return new OutsideLIConstraint(el);
+ case 'listItemInside':
+ return new InsideLIConstraint(el);
+ default:
+ return super.getConstraint(line, atomIndex);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/layout/constraints/InsideLIConstraint.as b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/InsideLIConstraint.as
new file mode 100644
index 0000000..960afc9
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/InsideLIConstraint.as
@@ -0,0 +1,32 @@
+package org.tinytlf.layout.constraints
+{
+ import flash.text.engine.ContentElement;
+
+ import org.tinytlf.layout.constraints.horizontal.HConstraint;
+ import org.tinytlf.layout.properties.TextFloat;
+
+ public class InsideLIConstraint extends HConstraint
+ {
+ public function InsideLIConstraint(constraintElement:* = null)
+ {
+ super(constraintElement);
+ }
+
+ override public function get float():String
+ {
+ return TextFloat.LEFT;
+ }
+
+ override public function initialize(e:*):void
+ {
+ super.initialize(e);
+
+ lp.x = majorSize;
+ }
+
+ override public function getMajorValue(atMinor:Number, fromMajor:Number):Number
+ {
+ return lp.x;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/layout/constraints/OutsideLIConstraint.as b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/OutsideLIConstraint.as
new file mode 100644
index 0000000..3c7e63b
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/layout/constraints/OutsideLIConstraint.as
@@ -0,0 +1,23 @@
+package org.tinytlf.layout.constraints
+{
+ import org.tinytlf.layout.constraints.horizontal.HConstraint;
+ import org.tinytlf.layout.properties.TextFloat;
+
+ public class OutsideLIConstraint extends HConstraint
+ {
+ public function OutsideLIConstraint(constraintElement:* = null)
+ {
+ super(constraintElement);
+ }
+
+ override public function get float():String
+ {
+ return TextFloat.LEFT;
+ }
+
+ override public function getMajorValue(atMinor:Number, fromMajor:Number):Number
+ {
+ return lp.x + totalWidth;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/styles/FCSSStyleProxy.as b/tinytlf-xhtml/src/org/tinytlf/styles/FCSSStyleProxy.as
new file mode 100755
index 0000000..68faa33
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/styles/FCSSStyleProxy.as
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.styles
+{
+ import com.flashartofwar.fcss.styles.IStyle;
+
+ import flash.system.Capabilities;
+ import flash.utils.flash_proxy;
+
+ use namespace flash_proxy;
+
+ public class FCSSStyleProxy extends StyleAwareActor implements IStyle
+ {
+ public function FCSSStyleProxy(styleObject:Object = null)
+ {
+ super(styleObject);
+
+ if(styleObject is IStyle)
+ {
+ styleName = IStyle(styleObject).styleName;
+ }
+ }
+
+ override protected function mergeProperty(property:String, source:Object):void
+ {
+ var value:* = source[property];
+
+ if(value is String)
+ {
+ if(valueIsEm(value))
+ this[property] = deriveValueFromBaseValue(value, this[property]);
+ else if(valueIsPoint(value))
+ this[property] = deriveValueFromPoint(value);
+ else if(valueIsPercent(value))
+ this[property] = deriveValueFromBaseValue(value, this[property]);
+ else if(valueIsPixel(value))
+ this[property] = deriveValueFromPixel(value);
+ else
+ super.mergeProperty(property, source);
+ }
+ else
+ {
+ super.mergeProperty(property, source);
+ }
+ }
+
+ private var _styleName:String = '';
+
+ public function get styleName():String
+ {
+ return _styleName;
+ }
+
+ public function set styleName(value:String):void
+ {
+ if(value === _styleName)
+ return;
+
+ _styleName = value;
+ }
+
+ public function clone():IStyle
+ {
+ return new FCSSStyleProxy(this);
+ }
+
+ protected function valueIsPoint(value:String):Boolean
+ {
+ return /pt/i.test(value);
+ }
+
+ protected function valueIsEm(value:String):Boolean
+ {
+ return /em/i.test(value);
+ }
+
+ protected function valueIsPercent(value:String):Boolean
+ {
+ return /%/i.test(value);
+ }
+
+ protected function valueIsPixel(value:String):Boolean
+ {
+ return /px/i.test(value);
+ }
+
+ protected function deriveValueFromPoint(value:String):Number
+ {
+ //a point is 1/72nd of an inch.
+ return (Number(value.substring(0, value.indexOf('pt'))) / 72) * screenDPI;
+ }
+
+ protected function deriveValueFromBaseValue(value:String, baseValue:Number = NaN):*
+ {
+ //If no base value was passed in, we can't derive an M-height.
+ if(baseValue != baseValue)
+ return value;
+
+ return baseValue * Number(value.substring(0, value.indexOf('em')));
+ }
+
+ protected function deriveValueFromPixel(value:String):Number
+ {
+ return Number(value.substring(0, value.indexOf('px')));
+ }
+
+ //Cache the screen DPI.
+ protected static const screenDPI:Number = Capabilities.screenDPI;
+
+ override flash_proxy function setProperty(name:*, value:*):void
+ {
+ //Convert any #FFFFFF values to 0xFFFFFF
+ if(value is String && String(value).indexOf("#") != -1)
+ {
+ value = uint('0x' + String(value).substring(1));
+ }
+
+ super.setProperty(name, value);
+ }
+
+ public function merge(obj:Object):void
+ {
+ mergeWith(obj);
+
+ if(obj is IStyle)
+ {
+ styleName = IStyle(obj).styleName;
+ }
+ }
+
+ override public function toString():String
+ {
+ return styleName + ' ' + super.toString();
+ }
+ }
+}
+
diff --git a/tinytlf-xhtml/src/org/tinytlf/styles/FCSSTextStyler.as b/tinytlf-xhtml/src/org/tinytlf/styles/FCSSTextStyler.as
new file mode 100755
index 0000000..677bd44
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/styles/FCSSTextStyler.as
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2010 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
+package org.tinytlf.styles
+{
+ import com.flashartofwar.fcss.styles.*;
+ import com.flashartofwar.fcss.stylesheets.*;
+
+ import flash.text.engine.*;
+
+ public class FCSSTextStyler extends TextStyler
+ {
+ [Embed(source="default.css", mimeType="application/octet-stream")]
+ private var defaultCSS:Class;
+
+ public function FCSSTextStyler()
+ {
+ style = new defaultCSS().toString();
+ // A list of non-inheriting styles. If a style isn't in this list,
+ // it's assumed to be an inheriting style.
+ setStyle('nonInheritingStyles',
+ {
+ margin: null, marginLeft: null, marginRight: null, marginTop: null,
+ padding: null, paddingLeft: null, paddingRight: null, paddingTop: null,
+ width: null, height: null, 'class': null, id: null, style: null
+ });
+ }
+
+ private const styleSheet:TinytlfStyleSheet = new TinytlfStyleSheet();
+
+ override public function set style(value:Object):void
+ {
+ if(value is String)
+ {
+ styleSheet.parseCSS(String(value));
+ value = styleSheet.getStyle("*");
+ }
+
+ super.style = value;
+ }
+
+ override public function getElementFormat(element:*):ElementFormat
+ {
+ var format:ElementFormat = new ElementFormat();
+ var description:FontDescription = new FontDescription();
+
+ if(element)
+ {
+ new EFApplicator().applyStyle(format, element);
+ new FDApplicator().applyStyle(description, element);
+ format.fontDescription = description;
+ }
+
+ return format;
+ }
+
+ override public function describeElement(element:*):Object
+ {
+ if(element is String)
+ element = String(element).split(' ');
+ if(element is Array)
+ return styleSheet.getStyle.apply(null, element as Array);
+
+ return super.describeElement(element);
+ }
+
+ override public function toString():String
+ {
+ return styleSheet.toString();
+ }
+ }
+}
+
+import com.flashartofwar.fcss.applicators.*;
+import com.flashartofwar.fcss.styles.*;
+import com.flashartofwar.fcss.stylesheets.*;
+import com.flashartofwar.fcss.utils.*;
+
+import flash.text.engine.*;
+
+import org.tinytlf.styles.*;
+
+internal class EFApplicator extends AbstractApplicator
+{
+ public function EFApplicator()
+ {
+ super(this);
+ }
+
+ override public function applyStyle(target:Object, style:Object):void
+ {
+ if(!(target is ElementFormat))
+ throw new ArgumentError('The target of an EFApplicator must be an ElementFormat!');
+
+ var ef:ElementFormat = ElementFormat(target);
+ ef.alignmentBaseline = style.alignmentBaseline || TextBaseline.USE_DOMINANT_BASELINE;
+ ef.alpha = valueFilter(style.alpha || '1', 'number');
+ ef.baselineShift = valueFilter(style.baselineShift || '0', 'number');
+ ef.breakOpportunity = style.breakOpportunity || BreakOpportunity.AUTO;
+ ef.color = valueFilter(style.color || '0x00', 'uint');
+ ef.digitCase = style.digitCase || DigitCase.DEFAULT;
+ ef.digitWidth = style.digitWidth || DigitWidth.DEFAULT;
+ ef.dominantBaseline = style.dominantBaseline || TextBaseline.ROMAN;
+ ef.fontSize = valueFilter(style.fontSize || '12', 'number');
+ ef.kerning = style.kerning || Kerning.AUTO;
+ ef.ligatureLevel = style.ligatureLevel || LigatureLevel.COMMON;
+ ef.locale = style.locale || 'en_US';
+ ef.textRotation = style.textRotation || TextRotation.AUTO;
+ ef.trackingLeft = valueFilter(style.trackingLeft || '0', 'number');
+ ef.trackingRight = valueFilter(style.trackingRight || '0', 'number');
+ ef.typographicCase = style.typographicCase || TypographicCase.DEFAULT;
+ }
+
+ override protected function valueFilter(value:String, type:String):*
+ {
+ return TypeHelperUtil.getType(value, type);
+ }
+}
+
+internal class FDApplicator extends AbstractApplicator
+{
+ public function FDApplicator()
+ {
+ super(this);
+ }
+
+ override public function applyStyle(target:Object, style:Object):void
+ {
+ if(!(target is FontDescription))
+ throw new ArgumentError('The target of an FDApplicator must be a FontDescription!');
+
+ var fd:FontDescription = FontDescription(target);
+
+ fd.cffHinting = style.cffHinting || CFFHinting.HORIZONTAL_STEM;
+ fd.fontLookup = style.fontLookup || FontLookup.EMBEDDED_CFF;
+ fd.fontName = style.fontName || style.fontFamily || '_sans';
+
+ if('fontStyle' in style)
+ fd.fontPosture = style.fontStyle == FontPosture.ITALIC ? FontPosture.ITALIC : FontPosture.NORMAL;
+
+ fd.fontPosture = style.fontPosture || fd.fontPosture;
+
+ fd.fontWeight = style.fontWeight || FontWeight.NORMAL;
+ fd.renderingMode = style.renderingMode || RenderingMode.CFF;
+ }
+
+ override protected function valueFilter(value:String, type:String):*
+ {
+ return TypeHelperUtil.getType(value, type);
+ }
+}
+
+internal class TinytlfStyleSheet extends FStyleSheet
+{
+ override public function parseCSS(cssText:String, useCSSTidy:Boolean = true):IStyleSheet
+ {
+ cachedstyles.length = 0;
+
+ indexCSS(useCSSTidy ? tidy(cssText) : cssText);
+
+ // Force @variables to cache
+ getStyle("@variables");
+
+ this.cssText = '';
+
+ return this;
+ }
+
+ override public function toString():String
+ {
+ if(cssText != '')
+ return cssText;
+
+ var s:String = '';
+ var o:Object;
+
+ for each(var styleName:String in styleNames)
+ {
+ s += styleLookup(styleName, false).toString();
+ }
+
+ return cssText = s;
+ }
+
+ override protected function createEmptyStyle():IStyle
+ {
+ return new FCSSStyleProxy();
+ }
+}
diff --git a/tinytlf-xhtml/src/org/tinytlf/styles/default.css b/tinytlf-xhtml/src/org/tinytlf/styles/default.css
new file mode 100755
index 0000000..77180c6
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/styles/default.css
@@ -0,0 +1,66 @@
+*{
+ font-lookup: embeddedCFF;
+ font-name: _sans;
+ font-size: 16px;
+ layout-direction: ltr;
+}
+h1{
+ font-size: 2em;
+ font-weight: bold;
+}
+h2{
+ font-size: 1.5em;
+ font-weight: bold;
+}
+h3{
+ font-size: 1.2em;
+ font-weight: bold;
+ font-weight: bold;
+}
+h4{
+ font-size: 1.12em;
+ font-weight: bold;
+}
+h5{
+ font-size: .83em;
+ font-weight: bold;
+}
+h6{
+ font-size: .75em;
+ font-weight: bold;
+}
+b{
+ font-weight: bold;
+}
+strong{
+ font-weight: bold;
+}
+em{
+ font-posture: italic;
+}
+i{
+ font-posture: italic;
+}
+u{
+ underline: true;
+}
+ol{
+ margin-left: 40px;
+ list-style-position: inside;
+}
+ul{
+ margin-left: 40px;
+ list-style-position: inside;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+sup{
+ font-size: .6em;
+ alignment-baseline: ideographicCenter;
+}
+hr{
+ padding-top: 5;
+ padding-bottom: 5;
+ weight: 2;
+ height: 2;
+}
\ No newline at end of file
diff --git a/tinytlf-xhtml/src/org/tinytlf/xml/TagSoup.as b/tinytlf-xhtml/src/org/tinytlf/xml/TagSoup.as
new file mode 100644
index 0000000..0147b12
--- /dev/null
+++ b/tinytlf-xhtml/src/org/tinytlf/xml/TagSoup.as
@@ -0,0 +1,121 @@
+package org.tinytlf.xml
+{
+ import flash.external.ExternalInterface;
+ import flash.utils.getDefinitionByName;
+
+ /**
+ * Tries to convert weird or possibly invalid HTML into valid XML.
+ * Pass the unwashed HTML to the slurp method to clean it up, or toXML
+ * method if you just want some XML to work with.
+ */
+ public final class TagSoup
+ {
+ public static function toXML(unwashed:String, wrap:Boolean = false):XML
+ {
+ var x:XML;
+ try
+ {
+ //Maybe our string can be easily converted to XML?
+ x = new XML(trim(unwashed.toString()));
+ }
+ catch(e:Error)
+ {
+ try{
+ //But maybe he's just missing a root node?
+ x = new XML('' + unwashed.toString() + ' ');
+ }
+ catch(e:Error){
+ //Nope, too optimistic. slurp em up.
+ x = new XML('' + slurp(unwashed.toString()) + ' ');
+ }
+ }
+
+ if(wrap)
+ {
+ // if we were passed a string with no root at all
+ // or if we were passed the root node and we want
+ // it wrapped inside another root.
+ if( (x..*.length() == 0) ||
+ ((x.*.length() > 0) && (x.*[0].nodeKind().toString() == 'text'))
+ )
+ {
+ //wrap it upp
+ x = {x};
+ }
+ }
+
+ return x;
+ }
+
+ /**
+ * Slurp your soup. Do some translation on self-terminating nodes
+ * before and after slurpage, because the browser doesn't understand
+ * self-terminating nodes and it passes back invalid XML but potentially
+ * valid HTML.
+ */
+ public static function slurp(tags:String):String
+ {
+ //Replace self terminating nodes with open/close pairs
+ //e.g.: to
+ tags = tags.replace(/<[^>\S]*([^>\s|br|hr|img]+)([^>]*)\/[^>\S]*>/g, '<$1$2>$1>');
+ tags = soup(tags);
+ tags = tags.replace(/<(br|hr|img).*?>/g, "<$1/>");
+ return trim(tags);
+ }
+
+ /**
+ * @private
+ * Attempts to parse the input malformed XML tags with the browser
+ * through an ExternalInterface call.
+ *
+ */
+ private static function soup(tags:String):String
+ {
+ //Are we running in the browser?
+ if(ExternalInterface.available)
+ {
+ return ExternalInterface.call('function(tags)\
+ {\
+ var div = document.createElement("div");\
+ div.innerHTML = tags;\
+ return div.innerHTML;\
+ }', tags);
+ }
+ //Might we be running in AIR?
+ else if(getDefinitionByName('flash.html.HTMLLoader') != null)
+ {
+ var htmlLoader:* = new (getDefinitionByName('flash.html.HTMLLoader') as Class)();
+ var html:String = '\
+ \
+ ';
+ htmlLoader['loadString'](html);
+ tags = htmlLoader.window.soup(tags);
+ }
+
+ return tags;
+ }
+
+ /**
+ * Trims out the excess white space between XML nodes before parsing,
+ * but we still want to respect at least one white space between nodes.
+ * This is a feature of HTML.
+ *
+ * TODO: This will have to be tweaked to take into account the
+ * difference between sibling block-level nodes (Divs and Ps) and
+ * sibling inline elements (like Spans). Spaces are not respected
+ * between block-level elements, but are trimmed to one space between
+ * inline elements.
+ */
+ private static function trim(input:String):String
+ {
+ return input.replace(/\n|\r|\t/g, ' ').replace(/>(\s\s+)<').replace(/(\s\s+)/g, ' ');
+ }
+ }
+}
\ No newline at end of file