• Home
  • About

fixing deep linking for browsers like chrome

Posted by flexnrosescom on May 18, 2010; This entry is filed under Flex.
Please if you like what you read, i would like to hear from you and how this post is helping you.
i went through a difficult time trying to use the anchor navigation in order to update a background and video in an application. As there are html calls to modify the anchoring and i didn’t want to make call to my flex application. I discovered from other blogs that i need to wrap my html object with the same id as the anchor value.  But my application doesn’t detect the changes unless i call that javascript function. Then i also discovered that it can happen that my application can handle browsermanager events related to the application but not the browser. I came up with the idea that i can set a timer,  the only way to ping the changes, and stop it if i detect that the application can listen to browser changes not just application.  So that saves a lot of drawbacks and waste of CPU.
So far i have tested my html page in IE 8, firefox and google chrome and it works.  What do you need besides my files? you need the history.js and history.css which are generated by default from flex. i also used swfobject, so that could be another part of the this article how to use swfobject with browsermanager succesfully.
Thanks

mxml file

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”
layout=”vertical”
addedToStage=”this.onStageReady(event);”
creationComplete=”this.onAppComplete(event);” width=”200″ height=”200″
>
<mx:Script>
<![CDATA[
import mx.managers.BrowserManager;
import mx.events.BrowserChangeEvent;
import mx.events.FlexEvent;
import mx.managers.IBrowserManager;

private var bm:IBrowserManager;
private var hashTimer:Timer;
private var hashValue:String = "";

private function onAppComplete( event:FlexEvent ):void
{
this.hashTimer = new Timer( 500 );
this.hashTimer.addEventListener( TimerEvent.TIMER, this.onHashTimer );

this.bm = BrowserManager.getInstance();
this.bm.init( "", "Site" );
this.bm.addEventListener( BrowserChangeEvent.BROWSER_URL_CHANGE, this.onBrowserChange );
this.bm.addEventListener( BrowserChangeEvent.APPLICATION_URL_CHANGE, this.onAppChange );
}

private function onStageReady( event:Event ):void
{
this.setHash( this.bm.fragment );
this.setHashTimer();
}

private function setHashTimer():void
{
//inmediately call to detect if the anchor has a value.
this.onHashTimer();

if( !this.hashTimer.running )
this.hashTimer.start();
}

private function setHash( value:String ):void
{
this.hashValue = value;
txt.text = "hash value: " + this.hashValue;
}

private function getHash():void
{
var hash:String = ExternalInterface.call( "getHash" );
ExternalInterface.call( "alert", "Javascript.getHash() returns " + hash );
}

private function onBrowserChange( event:BrowserChangeEvent=null ):void
{
/**
* i noticed that chrome on first chrome change was getting a null value
* for browsermanager's fragment. so that was a good key for me to set the
* timer and read the anchor value coming from javascript.
*
* if i am able to get the fragment, then it means the browser works fine
* and i don't need to use my timer. but if the fragment value is null then
* i can set it back to use the timer.
*/

if( this.bm.fragment )
{
if( this.hashTimer.running )
this.hashTimer.stop();

this.setHash( this.bm.fragment );
}
else
{
this.setHashTimer();
}
}

/**
* if the application can handle browse changes by the browser, i wanted to keep this
* functionality independent from the browser making the changes instead */

private function onAppChange( event:BrowserChangeEvent ):void
{
this.setHash( this.bm.fragment );
}

/**
* checkign if the hash value has changed, and making sure we have the JS function available
* to retrieve the hash value **/
private function onHashTimer( event:TimerEvent=null ):void
{
var hash:String = ExternalInterface.call( "getHash" );

if( hash != this.hashValue )
{
this.setHash( hash );
}
}

/**
* this function was done in purpose to test what would happen in chrome if we
* change the hash value from the application instead of the browser */
private function linkHash():void
{
this.bm.setFragment( "1" );
}
]]>
</mx:Script>
<mx:Text id=”txt” width=”100″ height=”40″ text=”hello” />
<mx:Button label=”getHash” click=”this.getHash();” />
<mx:Button label=”setHash(1)” click=”this.linkHash();” />
</mx:Application>

page

<head>
<link rel=”stylesheet” type=”text/css” href=”/styles/history.css” />
<title></title>

<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />
<script type=”text/javascript” src=”/scripts/swfobject.js”></script>
<script type=”text/javascript” src=”/scripts/history.js”></script>
</head>

<body>

<div id=”AppLayer”>
<a href=”http://www.adobe.com/go/EN_US-H-GET-FLASH”>Get Flash Player</a> to view animation.

<script type=”text/javascript”>

//function required for getting the current hash
function getHash()
{
return unescape(self.document.location.hash.substring(1));
}

var requiredMajorVersion = 9;
var requiredMinorVersion = 0;
var requiredRevision = 124;

//swfobject
var flashvars = {};

var params = {};
params.menu = “false”;
params.quality = “best”;
params.scale = “noscale”;
params.salign = “tl”;
params.bgcolor = “#FFFFFF”;
params.allowscriptaccess = “always”;

var attributes = {};

swfobject.embedSWF(“/Main.swf”,
“AppLayer”,
“200″,
“200″,
“9″,
“/common/swf/expressInstall.swf”,
flashvars, params, attributes );
</script>
</div>

<a href=”#2″><p id=”2″>hash 2</p></a>
<a href=”#3″><p id=”3″>hash 3</p></a>
<a href=”#4″><p id=”4″>hash 4</p></a>
<a href=”#5″><p id=”5″>hash 5</p></a>

</body>

6 Comments

  1. Peter Geczy December 1st, 2010 at 8:07 pm

    Thank you very much for your effort. I’ve tested this solution after I got some problem with Google Chrome.
    Modified the default generated html wrapper (by flex 4) just extended with your getHash() function. Everything is working fine with tested in Chrome/IE7/IE8/Firefox/Safari/Opera
    Thanks a lot, keep up the good work.

  2. flexnroses December 6th, 2010 at 3:36 am

    Thank you for your comment. I am going to moving to my own wordpress so i can have this word running to be easier. i hope my code wasn’t confusing.. :)

  3. Veiko S June 8th, 2012 at 1:32 pm

    The above approach works nicely, with the caveat that in fact, the whole timer is unnecessary. As you write in your code comment, the key is to detect that bm.fragment == null in onBrowserChange handler. All you need to do is call the getHash method from javascript in this case and forward it to the bm.setFragment method. The rest can be skipped, actually.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    bm.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE, onBrowserChange);

    private function onBrowserChange( event:BrowserChangeEvent=null ):void
    {
    if( this.bm.fragment == null )
       this.bm.setFragment(ExternalInterface.call( "getHash" ));

    // continue using value of bm.fragment
    }
  4. flexnrosescom June 8th, 2012 at 2:47 pm

    thanks Veiko, i will give it a try. :) i haven’t touch this solution in two years. i thought maybe by now google chrome had solved it

  5. Haim March 1st, 2013 at 12:25 pm

    Not working for me.
    Is BROWSER_URL_CHANGE message handler is called when user clicks the back button?
    It seems not… would appreciate any help. Thanks.

  6. flexnrosescom March 9th, 2013 at 7:29 am

    hi, i am surprised that Flex still is missing this problem solved. My solution was written maybe three years ago. Hmm… I remember there was a timer in place so that it could detect the change you mentioned. There was a previous comment left regarding no need for the timer, did you do that as well, and maybe that’s why it’s not working for you.

Leave a Reply

Categories

  • Actionscript (19)
  • Android (3)
  • CSS (2)
  • Design Patterns (4)
  • Flex (18)
  • Geek (2)
  • Javascript (12)
  • PHP (3)
  • Wordpress (1)

RSS

  • RSS Blog
  • RSS Comments