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&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=8WwlelechwWHKMy8IfC4PpPRu4phg0n%2BZxFKcgUyd%2BE%3D&reserved=0 > > <https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitbox.apache.org%2Frepos%2Fasf%2Froyale-asjs.git&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=8WwlelechwWHKMy8IfC4PpPRu4phg0n%2BZxFKcgUyd%2BE%3D&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&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&reserved=0 > > <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&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&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&reserved=0 > > <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=02%7C01%7Caharui%40adobe.com%7C4b6313defb3e4535c98808d79da1d29d%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C637151190667743872&sdata=lOqUZl4l%2FMj28XKuqVcIpSAWIBdIXy0c9JmkAzjYx28%3D&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
