Last active
July 20, 2017 15:09
-
-
Save joshtynjala/7997065 to your computer and use it in GitHub Desktop.
Feathers TextFieldTextRenderer with Hyperlinks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package feathersx.controls.text | |
{ | |
import feathers.controls.text.TextFieldTextRenderer; | |
import flash.geom.Point; | |
import flash.net.URLRequest; | |
import flash.net.navigateToURL; | |
import starling.events.Touch; | |
import starling.events.TouchEvent; | |
import starling.events.TouchPhase; | |
import starling.utils.Pool; | |
public class HyperlinkTextFieldTextRenderer extends TextFieldTextRenderer | |
{ | |
//these tags will translate to the \r character | |
private static const BREAK_CONTENT:Vector.<String> = new <String> | |
[ | |
"br", | |
"br/", | |
"/p", | |
"li", | |
"/li", | |
]; | |
public function HyperlinkTextFieldTextRenderer() | |
{ | |
this.isHTML = true; | |
} | |
override public function set isHTML(value:Boolean):void | |
{ | |
super.isHTML = value; | |
if(this._isHTML) | |
{ | |
this.addEventListener(TouchEvent.TOUCH, touchHandler); | |
} | |
else | |
{ | |
this.removeEventListener(TouchEvent.TOUCH, touchHandler); | |
} | |
} | |
/** | |
* @private | |
*/ | |
protected function touchHandler(event:TouchEvent):void | |
{ | |
if(!this._isHTML) | |
{ | |
return; | |
} | |
var touch:Touch = event.getTouch(this, TouchPhase.ENDED); | |
if(!touch) | |
{ | |
return; | |
} | |
var location:Point = touch.getLocation(this, Pool.getPoint()); | |
var charIndex:int = this.textField.getCharIndexAtPoint(location.x, location.y); | |
Pool.putPoint(location); | |
var htmlCharIndex:int = -1; | |
var htmlText:String = this._text; | |
var regularText:String = this.textField.text; | |
var htmlTextLength:int = htmlText.length; | |
var lastHTMLContent:String; | |
for(var i:int = 0; i <= charIndex; i++) | |
{ | |
htmlCharIndex++; | |
if(htmlCharIndex >= htmlTextLength) | |
{ | |
//this shouldn't happen, but there's a chance that the html | |
//index and the regular index get out of sync, and this is | |
//better than being in an infinite loop! | |
break; | |
} | |
var regularChar:String = regularText.charAt(i); | |
var htmlChar:String = htmlText.charAt(htmlCharIndex); | |
if(regularChar === "\r") | |
{ | |
if(htmlChar === "\n") | |
{ | |
//if the html text uses \n, it will be replaced with \r | |
//in the regular text | |
continue; | |
} | |
else if(htmlChar === "\r") | |
{ | |
//if the html text uses \r\n, it will be replaced with | |
//\r in the regular text | |
//we should also skip the extra \n | |
htmlCharIndex++; | |
continue; | |
} | |
} | |
if(htmlChar === "\r") | |
{ | |
//if the html text uses \r (but not \r\n), it will be | |
//completely skipped in the regular text | |
htmlCharIndex++; | |
htmlChar = htmlText.charAt(htmlCharIndex); | |
} | |
var lastHTMLIndex:int = -1; | |
do | |
{ | |
if(lastHTMLIndex === htmlCharIndex) | |
{ | |
//we haven't moved forward at all, | |
//so we're stuck in an infinite loop! | |
break; | |
} | |
lastHTMLIndex = htmlCharIndex; | |
if(htmlCharIndex >= htmlTextLength) | |
{ | |
//we've gone past the end, we must be stuck | |
//in an infinite loop! | |
break; | |
} | |
if(htmlChar == "<") | |
{ | |
var skipTo:int = htmlText.indexOf(">", htmlCharIndex); | |
lastHTMLContent = htmlText.substr(htmlCharIndex + 1, skipTo - htmlCharIndex - 1); | |
if(regularChar === "\r" && BREAK_CONTENT.indexOf(lastHTMLContent) !== -1) | |
{ | |
htmlCharIndex = skipTo; | |
} | |
else | |
{ | |
htmlCharIndex = skipTo + 1; | |
} | |
htmlChar = htmlText.charAt(htmlCharIndex); | |
} | |
else if(htmlChar == "&") | |
{ | |
skipTo = htmlText.indexOf(";", htmlCharIndex); | |
var spaceIndex:int = htmlText.indexOf(" ", htmlCharIndex); | |
if(skipTo !== -1 && (spaceIndex === -1 || spaceIndex > skipTo)) | |
{ | |
//it's possible that there will be no ; after the & | |
//also, if a space appears before ;, then the & is | |
//not the start of the entity. | |
htmlCharIndex = skipTo; | |
} | |
htmlChar = regularChar; | |
} | |
} | |
while(htmlChar != regularChar); | |
} | |
if(!lastHTMLContent || lastHTMLContent.search(/^a\s+/) != 0) | |
{ | |
return; | |
} | |
var linkStartIndex:int = lastHTMLContent.search(/href=[\"\']/) + 6; | |
if(linkStartIndex < 2) | |
{ | |
return; | |
} | |
var linkEndIndex:int = lastHTMLContent.indexOf("\"", linkStartIndex + 1); | |
if(linkEndIndex < 0) | |
{ | |
linkEndIndex = lastHTMLContent.indexOf("'", linkStartIndex + 1); | |
if(linkEndIndex < 0) | |
{ | |
return; | |
} | |
} | |
var url:String = lastHTMLContent.substr(linkStartIndex, linkEndIndex - linkStartIndex); | |
navigateToURL(new URLRequest(url)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//HyperlinkTextFieldTextRenderer may be used on its own without a parent component | |
var textRenderer:HyperlinkTextFieldTextRenderer = new HyperlinkTextFieldTextRenderer(); | |
textRenderer.text = "This is a link to <u><a href=\"http://google.com\">Google</a></u>. This is a link to <u><a href=\"http://adobe.com\">Adobe</a></u>."; | |
this.addChild(textRenderer); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//HyperlinkTextFieldTextRenderer may also be used in a component that supports text renderers | |
var label:Label = new Label(); | |
//you need to allow hit tests to reach the label's text renderer | |
//as an optimization, isQuickHitAreaEnabled is true by default, so turn it off | |
label.isQuickHitAreaEnabled = false; | |
label.text = "This is a link to <u><a href=\"http://google.com\">Google</a></u>. This is a link to <u><a href=\"http://adobe.com\">Adobe</a></u>."; | |
label.textRendererFactory = function():ITextRenderer | |
{ | |
var textRenderer:HyperlinkTextFieldTextRenderer = new HyperlinkTextFieldTextRenderer(); | |
return textRenderer; | |
}; | |
this.addChild(label); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@lucien144 @skolesnyk I fixed the infinite loop caused by using
<br>
,<br/>
,<p>
,</p>
, or<li>
.I also figured out a good way to detect any more unexpected infinite loops, so it should break out of them. Things may end up getting out of sync with the text comparison between regular text and HTML text, but at least it won't freeze!