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]" <[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 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]> 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 +// +// 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 +// +// 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
