blob: a910334259677c56c3821956c44fc0abacc2be06 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 spark.managers
{
import flash.display.DisplayObjectContainer;
import mx.core.IVisualElement;
import mx.events.BrowserChangeEvent;
import mx.events.FlexEvent;
import mx.managers.BrowserManager;
import mx.managers.IBrowserManager;
import spark.components.DataGroup;
import spark.components.supportClasses.GroupBase;
import spark.events.IndexChangeEvent;
import spark.layouts.supportClasses.LayoutBase;
import spark.utils.LabelUtil;
import spark.layouts.supportClasses.INavigatorLayout;
// for asdoc
[Experimental]
[ExcludeClass]
/**
* @private
* The NavigatorBrowserManager is a Singleton manager that acts as
* a proxy between the BrowserManager and INavigatorLayout instances
* added to it.
*
* <p>It updates the <code>fragment</code> property of the IBrowserManager
* when a registered INavigatorLayout changes its <code>selectedindex</code>,
* and also sets the <code>selectedIndex</code> of registered INavigatorLayout instances
* when the <code>fragment</code> property of the IBrowserManager changes.
*
* @see mx.managers.IBrowserManager
* @see spark.layouts.supportClasses.INavigatorLayout
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class NavigatorBrowserManagerImpl implements INavigatorBrowserManager
{
//--------------------------------------------------------------------------
//
// Class Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static var _instance:NavigatorBrowserManagerImpl;
//--------------------------------------------------------------------------
//
// Class Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
public static function getInstance():NavigatorBrowserManagerImpl
{
if( !_instance ) _instance = new NavigatorBrowserManagerImpl();
return _instance;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function NavigatorBrowserManagerImpl()
{
super();
_browserManager = BrowserManager.getInstance();
_browserManager.addEventListener( BrowserChangeEvent.BROWSER_URL_CHANGE, onBrowserManagerBrowserURLChange, false, 0, true );
_browserManager.init("", "");
parseFrament();
updateFragment();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Instance of the browser manager to be used.
*/
private var _browserManager:IBrowserManager;
/**
* @private
* The list of layouts being managed.
*/
private var _layouts:Vector.<INavigatorLayout> = new Vector.<INavigatorLayout>();
/**
* @private
* The list of layouts that can still be parsed after a URL change.
*/
private var _layoutsToParse:Vector.<INavigatorLayout> = new Vector.<INavigatorLayout>();
/**
* @private
* The list of layouts to ignore their <code>FlexEvent.VALUE_COMMIT</code> events.
* When the index of a layout is changed, we want to ignore the value commit, if
* the layouts <code>selectedIndex</code> is the same as the index set.
*/
private var _layoutsToIgnore:Vector.<LayoutToIgnore> = new Vector.<LayoutToIgnore>();
/**
* @private
* A list of fragment parts that still need to be parsed.
*/
private var _fragmentsToParse:Array;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// fragmentField
//----------------------------------
/**
* @private
* Storage property for fragmentField.
*/
private var _fragmentField:String;
/**
* 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 fragmentField():String
{
return _fragmentField;
}
/**
* @private
*/
public function set fragmentField( value:String ):void
{
if( _fragmentField == value ) return;
_fragmentField = value;
updateFragment();
}
//----------------------------------
// fragmentFunction
//----------------------------------
/**
* @private
* Storage property for fragmentFunction.
*/
private var _fragmentFunction:Function;
/**
* 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 fragmentFunction():Function
{
return _fragmentFunction;
}
/**
* @private
*/
public function set fragmentFunction( value:Function ):void
{
if( _fragmentFunction == value ) return;
_fragmentFunction = value;
updateFragment();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function registerLayout( value:INavigatorLayout ):void
{
if( _layouts.indexOf( value ) == -1 )
{
value.addEventListener( FlexEvent.VALUE_COMMIT, onLayoutIndexChange, false, 0, true );
_layouts.push( value );
_layouts.sort( nestLevelCompareFunction );
_layoutsToParse.push( value );
_layoutsToParse.sort( nestLevelCompareFunction );
}
if( _fragmentsToParse && _fragmentsToParse.length )
{
parseFrament();
}
else
{
updateFragment();
}
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function unRegisterLayout( value:INavigatorLayout ):void
{
const index:int = _layouts.indexOf( value )
if( index != -1 )
{
const l:INavigatorLayout = _layouts.splice( index, 1 )[ 0 ];
l.removeEventListener( IndexChangeEvent.CHANGE, onLayoutIndexChange, false );
}
updateFragment();
}
/**
* @private
* Sorts the layouts into order depending on th nestLevel (how deep they are in the displayList).
*/
private function nestLevelCompareFunction( a:INavigatorLayout, b:INavigatorLayout ):int
{
const at:GroupBase = LayoutBase( a ).target;
const bt:GroupBase = LayoutBase( b ).target;
if( !at && !bt )
{
return 0;
}
else if( !at )
{
return 1;
}
else if( !bt )
{
return -1;
}
else
{
return Math.min( 1, Math.max( -1, at.nestLevel - bt.nestLevel ) );
}
}
/**
* @private
* Parses the fragments by looking for the correct layouts to select the index on.
*/
private function parseFrament( override:Boolean = true ):void
{
if( !_fragmentsToParse || !_fragmentsToParse.length ) return;
var target:GroupBase;
var part:String;
var layout:INavigatorLayout;
var elements:Array;
const numLayouts:int = _layoutsToParse.length;
for( var i:int = 0; i < numLayouts; i++ )
{
part = _fragmentsToParse[ 0 ];
layout = _layoutsToParse[ 0 ];
target = LayoutBase( layout ).target;
if( target )
{
elements = getElements( target );
var numElements:int = elements.length;
for( var e:int = 0; e < numElements; e++ )
{
if( LabelUtil.itemToLabel( elements[ e ], fragmentField, fragmentFunction ) == part )
{
// Remove both from their lists
_fragmentsToParse.splice( 0, 1 );
_layoutsToParse.splice( 0, 1 );
// Make sure we ignore this layouts valueCommit.
_layoutsToIgnore.push( new LayoutToIgnore( layout, e ) );
layout.selectedIndex = e;
if( _fragmentsToParse.length )
{
break;
}
else
{
return;
}
}
}
}
}
}
/**
* @private
* Util method to get a list of the items being rendrered.
* These could be visual items or data items.
*/
private function updateFragment():void
{
_layouts.sort( nestLevelCompareFunction );
var target:GroupBase;
var fragment:String = "";
var selectedElement:IVisualElement;
for each( var layout:INavigatorLayout in _layouts )
{
target = LayoutBase( layout ).target;
if( target )
{
if( !selectedElement )
{
selectedElement = layout.selectedElement;
fragment += getFragment( layout, target );
}
// Make sure that this layouts target is a child of the
// previously selectedElement.
else if( selectedElement is DisplayObjectContainer &&
DisplayObjectContainer( selectedElement ).contains( target ) )
{
fragment += getFragment( layout, target );
selectedElement = layout.selectedElement;
}
}
}
if( _browserManager.fragment != fragment ) _browserManager.setFragment( fragment );
}
/**
* @private
* Util method to get a list of the items being rendrered.
* These could be visual items or data items.
*/
protected function getElements( target:GroupBase ):Array
{
if( target is DataGroup )
{
return DataGroup( target ).dataProvider.toArray();
}
else
{
try
{
return target[ "getMXMLContent" ]();
}
catch( e:Error )
{
return target[ "toArray" ]();
}
catch( e:Error )
{
throw new Error( "NavigatorLayoutBase cannot be used as a layout for this kind of container" );
return new Array();
}
}
return new Array();
}
/**
* @private
* Util method get a framenet string for the selectedIndex.
*/
private function getFragment( l:INavigatorLayout, target:GroupBase ):String
{
const elements:Array = getElements( target );
return LabelUtil.itemToLabel( elements[ l.selectedIndex ], fragmentField, fragmentFunction ) + "/";
}
//--------------------------------------------------------------------------
//
// Event Listeners
//
//--------------------------------------------------------------------------
/**
* @private
* Invoked by the the URL is changed in the browser.
*/
private function onBrowserManagerBrowserURLChange( event:BrowserChangeEvent ):void
{
_layoutsToParse = _layouts.concat();
_fragmentsToParse = _browserManager.fragment.split( "/" );
_fragmentsToParse.splice( _fragmentsToParse.length - 1, 1 );
parseFrament();
}
/**
* @private
* Invoked when the selectedIndex changes in a layout.
*/
private function onLayoutIndexChange( event:FlexEvent ):void
{
const l:INavigatorLayout = INavigatorLayout( event.currentTarget );
const numLayouts:int = _layoutsToIgnore.length;
for( var i:int = 0; i < numLayouts; i++ )
{
if( _layoutsToIgnore[ i ].layout == l && _layoutsToIgnore[ i ].selectedIndex == l.selectedIndex )
{
_layoutsToIgnore.splice( i, 1 );
return;
}
}
updateFragment();
}
}
}
import spark.layouts.supportClasses.INavigatorLayout;
/**
* @private
* Util class used to store layouts whos valueCommit property
* we want to ignore due to use setting their selectedIndex.
*/
internal class LayoutToIgnore
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function LayoutToIgnore( layout:INavigatorLayout, selectedIndex:int )
{
_layout = layout;
_selectedIndex = selectedIndex;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// index
//----------------------------------
/**
* @private
* Storage property for selectedIndex.
*/
private var _selectedIndex:int;
/**
* The selectedIndex that the NavigatorBrowserManager set on the layout.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get selectedIndex():int
{
return _selectedIndex;
}
//----------------------------------
// layout
//----------------------------------
/**
* @private
* Storage property for layout.
*/
private var _layout:INavigatorLayout;
/**
* The layout that the NavigatorBroswerManager updated.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get layout():INavigatorLayout
{
return _layout;
}
}