New topics: Your Pet, IOU, Baby IQ, The Poisons, Birther II, Games, Future Power

Welcome to the Tech Space!

Interested in Games?

Skip to end of metadata
Go to start of metadata

The Adobe Flex AIR runtime provides an mx:html control that lets you embedded an html control in your RIA (rich internet application). It can be directed to a particular webpage by setting the location property. It is a live browser, the user can then click around on the links they see and navigate to other pages.

I thought it would be nice to wrap the control in a flex component and give it a very simple address bar. The Flex programming model makes it very easy to drop this new component into a flex application. Changes to the component are easily reflected in any application that uses the component.

I wanted the text area of the address bar to meet some special requirements

  • When a new web page is navigated to, it should change to reflect the new url
  • If the user is editing the address, do not change what is shown in the text input box. i think it's nasty to replace what a user is typing while they are typing it, and I spent a lot of time to figure out how to prevent that.

I also wanted the label for "URL:" in front of the text input area to show some scrolling dots while a page load is in progress.

It took me an evening to figure out how to create a webbrowser component that would meet these requirements. It's one of my earliest examples of a component, so I apologize in advance if I've missed any common Flex stylistic etiquette. (It sure would be nice if Flex included a "pretty printer" to cleanup the indentation of the source code, the mixed spaces and tabs didn't look so great on this wiki page.)

Updates:

  • 1.02 - (Not yet released)
    • Put in a flex library project "WebmillComponents" com.webmill.air.browserpane
    • Goal: Drop in replacement for \<mx:HTML\>
      • To use as a component in another project:
  • 1.01
    • Wrapped the buttons in a canvas so they can easily be moved to the top or bottom of the html control
    • Actually hookup the go button
    • Make all event handlers and most other functions private.
    • Add a refresh button, and make sure it starts the scrolling dots.
    • Add some comments

Todo:

  • Create a location property on the component that external users can set
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="init();">
	<mx:Canvas id="addressBar" bottom="0" right="0" left="0">
		<mx:Label id="lblURL" text="URL:" bottom="3" left="0"/>
		<mx:TextInput id="txtURL" text="http://www.cnn.com"
			right="218" left="31" bottom="1"
			change="changeInput(event);"
			textInput="urlTextInput(event)"
			enter="urlEnter(event)"/>
		<mx:Button label="Go" id="btnGo" right="177" bottom="1" width="40" click="clickGo()"/>
		<mx:Button id="btnRefresh" label="Refresh" click="refresh();" fontSize="7" bottom="1" right="121" width="55"/>
		<mx:Button id="btnBack" label="&lt; Back" click="navigateBackward();" fontSize="7" bottom="1" right="68" width="52"/>
		<mx:Button id="btnFwd" label="Forward &gt;" click="navigateForward()" fontSize="7" width="66" right="1" bottom="1"/>
	</mx:Canvas>

	<mx:HTML id="browser" right="0" left="0" top="0" bottom="26"
		location="http://www.cnn.com" complete="browserComplete(event)"
		locationChange="browserLocationChange(event)"/>
	<mx:Script>
		<![CDATA[

import flash.events.Event;
import flash.events.TextEvent;

private var sVersion:String = "1.01";
private var bLocationChanging:Boolean = false;
private var DefaultButtonColor:Object;
private var DefaultButtonText:String = "URL:";
private var bUserEditingLocation:Boolean  = false;

private function init():void {
	// Remember the beginning color and text so we can restore it.
	DefaultButtonColor=lblURL.getStyle("color");
	DefaultButtonText = lblURL.text;

	SetupTimer();
}

private var myTimer:Timer=null;
private var iTimerCount:int = 0;

private function SetupTimer():void {
	if (myTimer == null)
	{   // Four times a second seems fast enough.
		myTimer = new Timer(250);
		myTimer.addEventListener("timer", timerHandler);
		myTimer.start();
	}
}

private function timerHandler(event:TimerEvent):void {
	iTimerCount++;
	if (this.bLocationChanging)
	{   // Have some fun making the asterisks walk, and the color change too.
		var S:String = "     ";
		S=S.substr(0,(iTimerCount % 5)) + "***";
		lblURL.setStyle("color","#"+(3+iTimerCount%10).toString()+"C"+
		                            (1+iTimerCount%10).toString()+"7"+
		                            (0+iTimerCount%10).toString()+"B");
		lblURL.text = S;
	}

	// Dynamically enable the forward and back buttons.
	btnBack.enabled = (browser.htmlLoader.historyPosition>0);
	btnFwd.enabled = (browser.htmlLoader.historyPosition<browser.htmlLoader.historyLength-1);
}

private function IsUserEditingLocation():Boolean {
	// It turns out that checking if the user has the focus on the txtURL is a
	// great way to figure out if the user is intending to change the addres.
	// Cursor keys within the txtURL box don't trigger the regular key event, so
	// checking for focus gets around that. In fact, the bUserEditingLocation
	// could probably be eliminated in favor of just using the focus.
	// Make sure you check that focus is a defined object, or you'll null out.
	if (systemManager.stage.focus)
	{
	  if (systemManager.stage.focus.parent == txtURL)
	  {
		trace("User has txtURL focused.");
		return true;
	  }
	}

	return (bUserEditingLocation);
}

private function setChanging():void {
	lblURL.setStyle("color","#3C170B");
	lblURL.text = "...";
	bLocationChanging = true;
}

private function browserLocationChange(event:Event):void {
	setChanging();
	if (!IsUserEditingLocation())
	{
	  SetURLTextInput(browser.location);
	}
}

private function urlTextInput(event:TextEvent):void {
	// trace("urlTextInput event:"+event.text);
	if (!this.bUserEditingLocation)
	{
	  trace("Setting bUserEditingLocation = true");
	  this.bUserEditingLocation =true;
	}
}

private function urlEnter(event:Event):void {
	trace("urlEnter");
	if (browser.location != txtURL.text)
	{
		trace("bUserEditingLocation = false and setting location to "+txtURL.text);
		browser.htmlLoader.cancelLoad();
		browser.location = 	txtURL.text;
		bUserEditingLocation = false;
		focusManager.setFocus(browser);
	}
}

private function changeInput(event:Event):void {
}

private function SetURLTextInput(S:String):void
{
	trace("SetURLTextInput to "+S);
	txtURL.text = S;
}

private function browserComplete(event:Event):void {
	if (txtURL.text != browser.location)
	{
		if (!IsUserEditingLocation())
		{
			trace("BrowserComplete setting new location to text input "+ browser.location);
			SetURLTextInput(browser.location);

		}
		else
		{
		    trace("BrowserComplete while user is editing");
		}
	}
	else
	{
		trace("BrowserComplete to same location");
	}

    // Stop the marching dots, and return to the default text and color.
    bLocationChanging = false;
	lblURL.setStyle("color",DefaultButtonColor);
	lblURL.text = DefaultButtonText;
}

public function navigateForward():void {
    if (browser.htmlLoader.historyPosition<browser.htmlLoader.historyLength-1)
    {
    	var S:String = browser.htmlLoader.getHistoryAt(browser.htmlLoader.historyPosition+1).url;
		trace("Next url:"+S);
    	browser.htmlLoader.cancelLoad();
    	this.bUserEditingLocation = false;
    	this.txtURL.text = S;
		browser.historyForward();
    }
    else
    {
    	trace("No next url");
    }
}

public function navigateBackward():void {
    if (browser.htmlLoader.historyPosition>0)
    {   // Flex docs say the function name is "historyAt", but that is incorrect
    	var S:String = browser.htmlLoader.getHistoryAt(browser.htmlLoader.historyPosition-1).url;
    	trace("Prev url:"+S);
        browser.htmlLoader.cancelLoad();
    	this.bUserEditingLocation = false;
    	this.txtURL.text = S;
	    browser.historyBack();
    }
    else
    {
    	trace("No previous url");
    }
}

private function clickGo():void {
	browser.cancelLoad();
	bUserEditingLocation = false;
	browser.location = txtURL.text;
}

public function refresh():void {
	setChanging();
	browser.reload();
}

		]]>
	</mx:Script>
</mx:Canvas>

Labels:
air air Delete
source-code source-code Delete
flex flex Delete
open-source open-source Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.