| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 |
| // |
| // http://www.apache.org/licenses/LICENSE-2.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 mx.managers |
| { |
| |
| import flash.display.Stage; |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.external.ExternalInterface; |
| import flash.net.navigateToURL; |
| import flash.net.URLRequest; |
| import mx.core.FlexGlobals; |
| import mx.events.BrowserChangeEvent; |
| |
| /** |
| * Dispatched when the fragment property is changed either |
| * by the user interacting with the browser, invoking an |
| * application in Apollo |
| * or by code setting the property. |
| * |
| * @eventType mx.events.BrowserChangeEvent.URL_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="urlChange", type="flash.events.Event")] |
| |
| /** |
| * Dispatched when the fragment property is changed |
| * by the browser. |
| * |
| * @eventType mx.events.BrowserChangeEvent.BROWSER_URL_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="browserURLChange", type="mx.events.BrowserChangeEvent")] |
| |
| /** |
| * Dispatched when the fragment property is changed |
| * by the application via setFragment |
| * |
| * @eventType mx.events.BrowserChangeEvent.APPLICATION_URL_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="applicationURLChange", type="mx.events.BrowserChangeEvent")] |
| |
| [ExcludeClass] |
| |
| /** |
| * @private |
| * The BrowserManager is a Singleton manager that acts as |
| * a proxy between the browser and the application. |
| * It provides access to the URL in the browser address |
| * bar similar to accessing document.location in Javascript. |
| * Events are dispatched as the url property is changed. |
| * Listeners can then respond, alter the url, and/or block others |
| * from getting the event. |
| * |
| * For desktop applications, the BrowserManager |
| * provides access to the command-line parameters used to |
| * invoke the application. The url property will be the concatenated |
| * string representing all of the command-line parameters separated |
| * by semi-colons. |
| * |
| */ |
| public class BrowserManagerImpl extends EventDispatcher implements IBrowserManager |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private static var instance:IBrowserManager; |
| |
| private var _defaultFragment:String = ""; |
| |
| private var _browserUserAgent:String; |
| |
| private var _browserPlatform:String; |
| |
| private var _isFirefoxMac:Boolean; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| public static function getInstance():IBrowserManager |
| { |
| if (!instance) |
| instance = new BrowserManagerImpl(); |
| |
| return instance; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function BrowserManagerImpl() |
| { |
| super(); |
| |
| // we want to reduce dependencies for non-flex apps that use resources (e.g. rpc) |
| var systemManager:Object = SystemManagerGlobals.topLevelSystemManagers; |
| if (systemManager) |
| systemManager = systemManager[0]; |
| |
| if (systemManager) |
| { |
| // figure out if we're top level, even if bootstrapped |
| var sandboxRoot:Object = systemManager.getSandboxRoot(); |
| if (!sandboxRoot.dispatchEvent(new Event("mx.managers::BrowserManager", false, true))) |
| { |
| // if someone answered, then we're not the first BM |
| browserMode = false; |
| return; |
| } |
| |
| try |
| { |
| // see if we can walk to the stage |
| var parent:Object = sandboxRoot.parent; |
| while (parent) |
| { |
| if (sandboxRoot.parent is Stage) |
| { |
| break; |
| } |
| else |
| { |
| parent = parent.parent; |
| } |
| } |
| } |
| catch (e:Error) |
| { |
| browserMode = false; |
| return; |
| } |
| sandboxRoot.addEventListener("mx.managers::BrowserManager", sandboxBrowserManagerHandler, false, 0, true); |
| } |
| |
| try |
| { |
| ExternalInterface.addCallback("browserURLChange", browserURLChangeBrowser); |
| |
| ExternalInterface.addCallback("debugTrace", debugTrace); |
| } |
| catch(e:Error) |
| { |
| // not supported in all environments |
| browserMode = false; |
| } |
| } |
| |
| private var browserMode:Boolean = true; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // base |
| //---------------------------------- |
| |
| private var _base:String; |
| |
| [Bindable("urlChange")] |
| /** |
| * The portion of current URL before the '#' as it appears |
| * in the browser address bar. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get base():String |
| { |
| return _base; |
| } |
| |
| //---------------------------------- |
| // fragment |
| //---------------------------------- |
| |
| private var _fragment:String; |
| |
| [Bindable("urlChange")] |
| /** |
| * The portion of current URL after the '#' as it appears |
| * in the browser address bar, or the default fragment |
| * used in setup() if there is nothing after the '#'. |
| * Use setFragment to change this value. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get fragment():String |
| { |
| if (_fragment && _fragment.length) |
| return _fragment; |
| |
| return _defaultFragment; |
| } |
| |
| //---------------------------------- |
| // title |
| //---------------------------------- |
| |
| private var _title:String; |
| |
| [Bindable("urlChange")] |
| /** |
| * The title of the app as it should appear in the |
| * browser history |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get title():String |
| { |
| return _title; |
| } |
| |
| //---------------------------------- |
| // url |
| //---------------------------------- |
| |
| private var _url:String; |
| |
| [Bindable("urlChange")] |
| /** |
| * The current URL as it appears in the browser address bar. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get url():String |
| { |
| return _url; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Initialize the BrowserManager. The BrowserManager will get the initial URL. If it |
| * has a fragment, it will dispatch BROWSER_URL_CHANGE, so add your event listener |
| * before calling this method. |
| * |
| * @param defaultFragment the fragment to use if no fragment in the initial URL. |
| * @param defaultTitle the title to use if no fragment in the initial URL. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function init(defaultFragment:String = "", defaultTitle:String = ""):void |
| { |
| if ("historyManagementEnabled" in FlexGlobals.topLevelApplication) |
| FlexGlobals.topLevelApplication.historyManagementEnabled = false; |
| |
| setup(defaultFragment, defaultTitle); |
| } |
| |
| /** |
| * Initialize the BrowserManager. The HistoryManager calls this method to |
| * prepare the BrowserManager for further calls from the HistoryManager. Use |
| * of HistoryManager and setFragment calls from the application is |
| * not supported, so the init() method sets |
| * FlexGlobals.topLevelApplication.historyManagementEnabled to false to disable |
| * the HistoryManager |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function initForHistoryManager():void |
| { |
| setup("", ""); |
| } |
| |
| private function setup(defaultFragment:String, defaultTitle:String):void |
| { |
| if (!browserMode) |
| return; |
| |
| _defaultFragment = defaultFragment; |
| _url = ExternalInterface.call("BrowserHistory.getURL"); |
| |
| // probably no support in html wrapper |
| if (!_url) |
| { |
| browserMode = false; |
| return; |
| } |
| |
| _browserUserAgent = ExternalInterface.call("BrowserHistory.getUserAgent"); |
| _browserPlatform = ExternalInterface.call("BrowserHistory.getPlatform"); |
| |
| // Unlike browser.js we specifically test for the Firefox browser (vs. other |
| // Gecko or Mozilla derivatives), as the bug fix included in this file that |
| // leverages this flag is very specific to Firefox/Mac. |
| _isFirefoxMac = (_browserUserAgent && _browserPlatform && |
| _browserUserAgent.indexOf("Firefox") > -1 && _browserPlatform.indexOf("Mac") > -1); |
| |
| var pos:int = _url.indexOf('#'); |
| if (pos == -1 || pos == _url.length - 1) |
| { |
| _base = _url; |
| _fragment = ''; |
| _title = defaultTitle; |
| ExternalInterface.call("BrowserHistory.setDefaultURL", defaultFragment); |
| setTitle(defaultTitle); |
| } |
| else |
| { |
| _base = _url.substring(0, pos); |
| _fragment = _url.substring(pos + 1); |
| _title = ExternalInterface.call("BrowserHistory.getTitle"); |
| ExternalInterface.call("BrowserHistory.setDefaultURL", _fragment); |
| //have to force a refresh of the application. |
| if (_fragment != _defaultFragment) |
| browserURLChange(_fragment, true); |
| } |
| } |
| |
| /** |
| * Change the fragment of the url after the '#' in the browser. |
| * An attempt will be made to track this URL in the browser's |
| * history. |
| * |
| * If the title is set, the old title in the browser is replaced |
| * by the new title. |
| * |
| * To actually store the URL, a JavaScript |
| * method named setBrowserURL() will be called. |
| * The application's HTML wrapper must have that method which |
| * must implement a mechanism for taking this |
| * value and registering it with the browser's history scheme |
| * and address bar. |
| * |
| * When set, the APPLICATION_URL_CHANGE event is sent. If the event |
| * is cancelled the setBrowserURL() will not be called. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setFragment(value:String):void |
| { |
| if (!browserMode) |
| return; |
| |
| //value = (value == "") ? _defaultFragment : value; |
| |
| var lastURL:String = _url; |
| var lastFragment:String = _fragment; |
| |
| _url = base + '#' + value; |
| _fragment = value; |
| |
| if (dispatchEvent(new BrowserChangeEvent(BrowserChangeEvent.APPLICATION_URL_CHANGE, false, true, _url, lastURL))) |
| { |
| if (!_isFirefoxMac) |
| { |
| ExternalInterface.call("BrowserHistory.setBrowserURL", value, ExternalInterface.objectID); |
| } |
| else |
| { |
| // We need to avoid updating our browser URL with ExternalInterface, when we are |
| // running within Firefox/Mac. Player rendering bug logged 2276859. |
| var urlReq:URLRequest = new URLRequest("javascript:BrowserHistory.setBrowserURL('" + |
| value + "','" + ExternalInterface.objectID + "');"); |
| navigateToURL( urlReq , "_self" ); |
| } |
| |
| dispatchEvent(new BrowserChangeEvent(BrowserChangeEvent.URL_CHANGE, false, false, _url, lastURL)); |
| } |
| else |
| { |
| _fragment = lastFragment; |
| _url = lastURL; |
| } |
| } |
| |
| /** |
| * Change the title in the browser. |
| * Does not affect the browser's |
| * history. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setTitle(value:String):void |
| { |
| if (!browserMode) |
| return; |
| |
| ExternalInterface.call("BrowserHistory.setTitle", value); |
| _title = ExternalInterface.call("BrowserHistory.getTitle"); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event Listeners |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Callback from browser when the URL has been changed |
| * in the browser. |
| */ |
| private function browserURLChangeBrowser(fragment:String):void |
| { |
| browserURLChange(fragment, false); |
| } |
| |
| private function browserURLChange(fragment:String, force:Boolean = false):void |
| { |
| //trace("browserURLChange: |" + decodeURI(fragment) + "|, |" + decodeURI(_fragment) + "|" + ", " + force.toString()); |
| if (((fragment != null) && (decodeURI(_fragment) != decodeURI(fragment))) || force) |
| { |
| _fragment = fragment; |
| |
| var lastURL:String = url; |
| |
| _url = _base + '#' + fragment; |
| |
| dispatchEvent(new BrowserChangeEvent(BrowserChangeEvent.BROWSER_URL_CHANGE, false, false, url, lastURL)); |
| dispatchEvent(new BrowserChangeEvent(BrowserChangeEvent.URL_CHANGE, false, false, url, lastURL)); |
| } |
| } |
| |
| private function sandboxBrowserManagerHandler(event:Event):void |
| { |
| // cancel event to indicate that the message was heard |
| event.preventDefault(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Diagnostics |
| // |
| //-------------------------------------------------------------------------- |
| |
| private function debugTrace(s:String):void |
| { |
| trace(s); |
| } |
| } |
| |
| } |