Yeah. I thought about subclassing HashChangeNotifierBead and the version with 
Title, but almost all the code would have needed to be overridden (i.e. I’m 
auto-stipping off the hash, etc.), so I decided to write it from scratch.

The bead was not intended to be a lightweight PAYG bead. If someone wants very 
lightweight, they should use the Notifier beads. It’s meant to be as drop-in as 
possible to make it easier on the developer.

> On Jan 20, 2020, at 6:41 PM, Alex Harui <[email protected]> wrote:
> 
> Nit-picky comments:
> 
> 1) there is already a bead that watches location.hash.  It might be worth 
> using it to share code
> 2) forward/backward/go/title doesn't seem PAYG to me.  I can imagine lots of 
> users won't need it, or might use it independently of routing (especially 
> Title).
> 
> My 2 cents,
> -Alex
> 
> On 1/20/20, 4:11 AM, "[email protected] <mailto:[email protected]>" 
> <[email protected] <mailto:[email protected]>> wrote:
> 
>    This is an automated email from the ASF dual-hosted git repository.
> 
>    harbs pushed a commit to branch develop
>    in repository 
> https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitbox.apache.org%2Frepos%2Fasf%2Froyale-asjs.git&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=8WwlelechwWHKMy8IfC4PpPRu4phg0n%2BZxFKcgUyd%2BE%3D&amp;reserved=0
>  
> <https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitbox.apache.org%2Frepos%2Fasf%2Froyale-asjs.git&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=8WwlelechwWHKMy8IfC4PpPRu4phg0n%2BZxFKcgUyd%2BE%3D&amp;reserved=0>
> 
> 
>    The following commit(s) were added to refs/heads/develop by this push:
>         new 6cbc555  Added Router
>    6cbc555 is described below
> 
>    commit 6cbc5559bcc99bf2ceb3e033747ca3680b3b0d91
>    Author: Harbs <[email protected] <mailto:[email protected]>>
>    AuthorDate: Mon Jan 20 14:10:42 2020 +0200
> 
>        Added Router
>    ---
>     .../Basic/src/main/resources/basic-manifest.xml    |   2 +
>     .../royale/org/apache/royale/routing/RouteState.as |  34 +++
>     .../royale/org/apache/royale/routing/Router.as     | 274 
> +++++++++++++++++++++
>     3 files changed, 310 insertions(+)
> 
>    diff --git 
> a/frameworks/projects/Basic/src/main/resources/basic-manifest.xml 
> b/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
>    index 00e2325..3c30bed 100644
>    --- a/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
>    +++ b/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
>    @@ -273,4 +273,6 @@
>         <component id="ModalDisplay" 
> class="org.apache.royale.html.beads.plugin.ModalDisplay"/>
>         <component id="ModalOverlay" 
> class="org.apache.royale.html.beads.plugin.ModalOverlay"/>
> 
>    +    <component id="Router" class="org.apache.royale.routing.Router"/>
>    +
>     </componentPackage>
>    diff --git 
> a/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/RouteState.as
>  
> b/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/RouteState.as
>    new file mode 100644
>    index 0000000..84fcc4c
>    --- /dev/null
>    +++ 
> b/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/RouteState.as
>    @@ -0,0 +1,34 @@
>    
> +////////////////////////////////////////////////////////////////////////////////
>    +//
>    +//  Licensed to the Apache Software Foundation (ASF) under one or more
>    +//  contributor license agreements.  See the NOTICE file distributed with
>    +//  this work for additional information regarding copyright ownership.
>    +//  The ASF licenses this file to You under the Apache License, Version 
> 2.0
>    +//  (the "License"); you may not use this file except in compliance with
>    +//  the License.  You may obtain a copy of the License at
>    +//
>    +//      
> https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&amp;reserved=0
>  
> <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&amp;reserved=0>
>    +//
>    +//  Unless required by applicable law or agreed to in writing, software
>    +//  distributed under the License is distributed on an "AS IS" BASIS,
>    +//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
> implied.
>    +//  See the License for the specific language governing permissions and
>    +//  limitations under the License.
>    +//
>    
> +////////////////////////////////////////////////////////////////////////////////
>    +package org.apache.royale.routing
>    +{
>    +  public class RouteState
>    +  {
>    +    public function RouteState(state:String="",title:String="")
>    +    {
>    +      this.state = state;
>    +      this.title = title;
>    +
>    +    }
>    +    public var state:String;
>    +    public var title:String;
>    +    public var parameters:Object;
>    +    public var path:Array;
>    +  }
>    +}
>    \ No newline at end of file
>    diff --git 
> a/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/Router.as
>  
> b/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/Router.as
>    new file mode 100644
>    index 0000000..98b9fc6
>    --- /dev/null
>    +++ 
> b/frameworks/projects/Basic/src/main/royale/org/apache/royale/routing/Router.as
>    @@ -0,0 +1,274 @@
>    
> +////////////////////////////////////////////////////////////////////////////////
>    +//
>    +//  Licensed to the Apache Software Foundation (ASF) under one or more
>    +//  contributor license agreements.  See the NOTICE file distributed with
>    +//  this work for additional information regarding copyright ownership.
>    +//  The ASF licenses this file to You under the Apache License, Version 
> 2.0
>    +//  (the "License"); you may not use this file except in compliance with
>    +//  the License.  You may obtain a copy of the License at
>    +//
>    +//      
> https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&amp;reserved=0
>  
> <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&amp;sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&amp;reserved=0>
>    +//
>    +//  Unless required by applicable law or agreed to in writing, software
>    +//  distributed under the License is distributed on an "AS IS" BASIS,
>    +//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
> implied.
>    +//  See the License for the specific language governing permissions and
>    +//  limitations under the License.
>    +//
>    
> +////////////////////////////////////////////////////////////////////////////////
>    +package org.apache.royale.routing
>    +{
>    +  import org.apache.royale.core.DispatcherBead;
>    +  import org.apache.royale.core.IStrand;
>    +  import org.apache.royale.debugging.assert;
>    +  import org.apache.royale.core.IStatesObject;
>    +  import org.apache.royale.events.Event;
>    +  import org.apache.royale.core.IInitialViewApplication;
>    +    /**
>    +     *  Dispatched when the state is changed.
>    +     *
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    [Event(name="stateChange", type="org.apache.royale.events.Event")]
>    +
>    +    /**
>    +     * Router is a bead which automatically handles browsing history.
>    +     * It could be attached to any strand, but typically it would be 
> attached to Application or View
>    +     * Listen to stateChange events to handle changes to browsing history 
> and use setState and renderState for modifying the history.
>    +     * The state of the router can be modified before committing the 
> state changes.
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +  public class Router extends DispatcherBead
>    +  {
>    +    public function Router()
>    +    {
>    +      
>    +    }
>    +    /**
>    +     * Use this to automatically sync the state of the strand.
>    +     * This only works for the state property of the RouterState.
>    +     * It also assumes that the strand is an IStatesObject.
>    +     * For this to work correctly, it's usually assumed that the bead is 
> attached to the application View
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public var syncState:Boolean;
>    +          override public function set strand(value:IStrand):void
>    +          {       
>    +                  _strand = value;
>    +                  COMPILE::JS
>    +                  {
>    +                          window.addEventListener("hashchange", 
> hashChangeHandler);
>    +        initialTitle = document.title;
>    +                  }
>    +      // If it's an Application, listen to applicationComplete
>    +      if(_strand is IInitialViewApplication)
>    +        listenOnStrand("applicationComplete",onInit);
>    +      //Otherwise listen to initComplete
>    +      else
>    +        listenOnStrand("initComplete",onInit);
>    +          }
>    +    private function onInit(event:Event):void
>    +    {
>    +      COMPILE::JS
>    +      {
>    +        if(location.hash)
>    +        {
>    +          hashChangeHandler();
>    +        }
>    +      }
>    +    }
>    +    private var initialTitle:String;
>    +          private function hashChangeHandler():void
>    +          {
>    +      parseHash();
>    +      if(syncState)
>    +      {
>    +        assert(_strand is IStatesObject,"syncState can only be used on 
> IStatesObjects");
>    +        (_strand as IStatesObject).currentState = _routeState.state;
>    +      }
>    +                  dispatchEvent(new Event("stateChange"));
>    +          }
>    +    private function parseHash():void
>    +    {
>    +      //TODO SWF implementation
>    +      COMPILE::JS
>    +      {
>    +        var hash:String = location.hash;
>    +        var index:int = 0;
>    +        if(hash.indexOf("!")==1){
>    +          index = 1;
>    +        }
>    +        hash = hash.slice(index+1);
>    +        var paths:Array = hash.split("/");
>    +        var statePart:String = paths.pop();
>    +        var splitParts:Array = statePart.split("?");
>    +        statePart = splitParts[0];
>    +        _routeState = new RouteState(statePart,document.title);
>    +        _routeState.path = paths;
>    +        _routeState.parameters = parseParameters(splitParts[1]);
>    +      }
>    +    }
>    +    private function parseParameters(query:String):Object
>    +    {
>    +      var urlVars:Object;
>    +      if(query){
>    +        var vars:Array = query.split("&");
>    +        if(vars.length){
>    +          urlVars = {};
>    +        }
>    +        for (var i:int=0;i<vars.length;i++) {
>    +            var pair:Array = vars[i].split("=");
>    +            urlVars[pair[0]] = pair[1] == undefined ? undefined : 
> decodeURIComponent(pair[1]);
>    +        }
>    +      }
>    +      return urlVars;
>    +    }
>    +
>    +    private function buildHash():String
>    +    {
>    +      var hash:String = "#!";
>    +      if(_routeState.path && routeState.path.length){
>    +        hash += (_routeState.path.join("/") + "/");
>    +      }
>    +      if(_routeState.state){
>    +        hash += _routeState.state;
>    +      }
>    +      hash+= buildParameterString();
>    +      return hash;
>    +    }
>    +    private function buildParameterString():String{
>    +      var retVal:String = "";
>    +      if(_routeState.parameters){
>    +        retVal += "?";
>    +        for(var x:String in _routeState.parameters){
>    +          retVal += x;
>    +          if(_routeState.parameters[x] != undefined){
>    +            retVal += "=" + encodeURIComponent(_routeState.parameters[x]);
>    +            retVal += "&";
>    +          }
>    +        }
>    +        //remove trailing &
>    +        retVal = retVal.slice(0, -1);
>    +      }
>    +
>    +      return retVal;
>    +    }
>    +
>    +    private var _routeState:RouteState;
>    +
>    +    public function get routeState():RouteState
>    +    {
>    +      if(!_routeState){
>    +        _routeState = new RouteState();
>    +      }
>    +          return _routeState;
>    +    }
>    +
>    +    public function set routeState(value:RouteState):void
>    +    {
>    +          _routeState = value;
>    +    }
>    +    /**
>    +     * Commits the current state to the browsing history
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public function setState():void
>    +    {
>    +      COMPILE::JS
>    +      {
>    +        
> window.history.pushState({"title":_routeState.title},_routeState.title,buildHash());
>    +        if(_routeState.title)
>    +        {
>    +          document.title = _routeState.title;
>    +        }
>    +      }
>    +    }
>    +    /**
>    +     * Same as setState, but also notifies of the state change 
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public function renderState():void
>    +    {
>    +      setState();
>    +      if(syncState)
>    +      {
>    +        assert(_strand is IStatesObject,"syncState can only be used on 
> IStatesObjects");
>    +        (_strand as IStatesObject).currentState = _routeState.state;
>    +      }
>    +      dispatchEvent(new Event("stateChange"));
>    +    }
>    +    private function setTitle():void
>    +    {
>    +      COMPILE::JS
>    +      {
>    +        if(window.history.state){
>    +          document.title = window.history.state["title"];
>    +        } else {
>    +          document.title = initialTitle;
>    +        }
>    +      }
>    +    }
>    +    /**
>    +     * Goes forward in the history
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public function forward():void{
>    +      COMPILE::JS
>    +      {
>    +         window.history.forward();
>    +         setTitle();
>    +         parseHash();
>    +      }
>    +    }
>    +    /**
>    +     * Goes backwards in the history
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public function back():void{
>    +      COMPILE::JS
>    +      {
>    +         window.history.back();
>    +         setTitle();
>    +         parseHash();
>    +      }
>    +    }
>    +
>    +    /**
>    +     * Moved the specified number of steps (forward or backwards) in the 
> history
>    +     * calling it with 0 or no value will reload the page.
>    +     *  @langversion 3.0
>    +     *  @playerversion Flash 10.2
>    +     *  @playerversion AIR 2.6
>    +     *  @productversion Royale 0.9.7
>    +     */
>    +    public function go(steps:int=0):void{
>    +      COMPILE::JS
>    +      {
>    +         window.history.go(steps);
>    +         parseHash();
>    +      }
>    +    }
>    +
>    +  }
>    +}
>    \ No newline at end of file

Reply via email to