1  /**
     2   * com.sekati.ui.ContextualMenu
     3   * @version 1.9.1
     4   * @author jason m horwitz | sekati.com
     5   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     6   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     7   */
     8   
     9  import com.sekati.core.CoreObject;
    10  import com.sekati.except.IllegalArgumentException;
    11  
    12  /**
    13   * Extended Context Menu Management
    14   * 
    15   * {@code Usage:
    16   * 	var cm:ContextualMenu = new ContextualMenu(_level0);
    17   * 	cm.addItem("Item One", Delegate.create(this, myFunction), true, true);
    18   * 	cm.addItem("Item Two");
    19   * 	cm.editItem("Item Two", "Item 2");
    20   * 	cm.removeItem("Item One"); // remove the first item
    21   * 	cm.enabled = false; // disable this menu
    22   * }
    23   */
    24  class com.sekati.ui.ContextualMenu extends CoreObject {
    25  
    26  	private var _target:MovieClip;
    27  	private var _isEnabled:Boolean;
    28  	private var _hasBuiltInItems:Boolean;
    29  	private var _items:Array;
    30  	private var _backup:Array;
    31  
    32  	/**
    33  	 * Custom Context Menu Constructor
    34  	 * @param target (MovieClip) object context menu should exist on
    35  	 * @return Void
    36  	 * @throws Error if no target is provided and returns without proper instantiation.
    37  	 */	
    38  	public function ContextualMenu(target:MovieClip) {
    39  		if(!target) {
    40  			throw new IllegalArgumentException( this, "Constructor expects a 'target:MovieClip' parameter.", arguments );
    41  			return;	
    42  		}
    43  		_target = target;
    44  		_isEnabled = true;
    45  		_hasBuiltInItems = false;
    46  		_items = new Array( );
    47  		_backup = new Array( );
    48  	}
    49  
    50  	/**
    51  	 * Add an item to the Context Menu
    52  	 * @param caption (String) menu item text
    53  	 * @param cb (Function) delegated callback function [optional]
    54  	 * @param isDiv (Boolean) add a divider before the item [optional, default: true]
    55  	 * @param isEnabled (Boolean) item is enabled for selection [optional, default: true]
    56  	 * @return Number - item id.
    57  	 */	 
    58  	public function addItem(caption:String, cb:Function, isDiv:Boolean, isEnabled:Boolean):Number {
    59  		cb = (!cb) ? menuClick : cb;
    60  		isDiv = (isDiv == undefined) ? true : isDiv;
    61  		isEnabled = (isEnabled == undefined) ? true : isEnabled;
    62  		var id:Number = _items.push( {_caption:caption, _cb:cb, _isDiv:isDiv, _isEnabled:isEnabled} );
    63  		buildMenu( );
    64  		return id;
    65  	}
    66  
    67  	/**
    68  	 * Edit an added item in the Context Menu
    69  	 * @param oldCaption (String) previous menu item text
    70  	 * @param newCaption (String) new menu item text
    71  	 * @param cb (Function) delegated callback function [optional, default: previous setting]
    72  	 * @param isDiv (Boolean) add a divider before the item [optional, default: previous setting]
    73  	 * @param isEnabled (Boolean) item is enabled for selection [optional, default: previous setting]
    74  	 * @return Boolean - true if item was successfully found and changed, false if edit failed.
    75  	 */
    76  	public function editItem(oldCaption:String, newCaption:String, cb:Function, isDiv:Boolean, isEnabled:Boolean):Boolean {
    77  		var prevItem:Object, newItem:Object;
    78  		for(var i:Number = 0; i < _items.length ; i++) {
    79  			if (_items[i]._caption === oldCaption) {
    80  				prevItem = _items[i];
    81  				var newCb:Function = (!cb) ? prevItem._cb : cb;
    82  				var newDiv:Boolean = (!isDiv) ? prevItem._isDiv : isDiv;
    83  				var newEnabled:Boolean = (!isEnabled) ? true : false;
    84  				newItem = {_caption:newCaption, _cb:newCb, _isDiv:newDiv, _isEnabled:newEnabled};
    85  				_items[i] = newItem;
    86  				buildMenu( );
    87  				return true;
    88  			}			
    89  		}
    90  		return false;	
    91  	}
    92  
    93  	/**
    94  	 * Enable an existing item in the Context Menu
    95  	 * @param name (String) the caption name of the item to be enabled.
    96  	 * @return Void
    97  	 */
    98  	public function enableItem(name:String):Void {
    99  		var i:Number = getItemIndex( "_caption", name );
   100  		if(i == null) return;
   101  		_items[i]._isEnabled = true;
   102  		buildMenu( );		
   103  	}
   104  
   105  	/**
   106  	 * Disable an existing item in the Context Menu
   107  	 * @param name (String) the caption name of the item to be disabled.
   108  	 * @return Void
   109  	 */
   110  	public function disableItem(name:String):Void {
   111  		var i:Number = getItemIndex( "_caption", name );
   112  		if(i == null) return;
   113  		_items[i]._isEnabled = false;
   114  		buildMenu( );
   115  	}
   116  
   117  	/**
   118  	 * Remove an item from the Context Menu
   119  	 * @param name (String) the caption name of the item to be removed
   120  	 * @return Boolean - true if item was sucessfully removed, false if removal failed.
   121  	 */
   122  	public function removeItem(name:String):Boolean {
   123  		var itemIndex:Number = getItemIndex( "_caption", name );
   124  		if (itemIndex != null) {
   125  			// instead of splicing we empty the index to persist ID's
   126  			_items[itemIndex] = {};
   127  			//_items.splice(itemIndex, 1);
   128  			buildMenu( );
   129  			return true;
   130  		}	
   131  		return false;
   132  	}
   133  
   134  	/**
   135  	 * Locate an item's index in the _items menu array by property value.
   136  	 * @param prop (String) item property to search by [e.g. "_caption"]
   137  	 * @param val (Object) unique item value to match with
   138  	 * @return Number - item array index if found, else null
   139  	 */
   140  	private function getItemIndex(prop:String, val:Object):Number {
   141  		for(var i:Number = 0; i < _items.length ; i++) {
   142  			if(_items[i][prop] === val) {
   143  				return i;
   144  			}
   145  		}
   146  		return null;
   147  	}		
   148  
   149  	/**
   150  	 * Build the custom Context Menu.
   151  	 * @return Void
   152  	 */	
   153  	private function buildMenu():Void {
   154  		var m:ContextMenu = new ContextMenu( );
   155  		if (!_hasBuiltInItems) m.hideBuiltInItems( );
   156  		for (var i:Number = 0; i < _items.length ; i++) {
   157  			if (_items[i]._caption) {
   158  				var cmi:ContextMenuItem = new ContextMenuItem( _items[i]._caption, _items[i]._cb, _items[i]._isDiv, _items[i]._isEnabled );
   159  				m.customItems.push( cmi );
   160  			}
   161  		}
   162  		_target.menu = m;
   163  	}
   164  
   165  	/**
   166  	 * Menu enabled setter
   167  	 * @param b (Boolean) - enable (true) or disable (false) the custom Context Menu [default: true].
   168  	 * @return Void
   169  	 */
   170  	public function set enabled(b:Boolean):Void {
   171  		if (b == false) {
   172  			if (_isEnabled != false) {
   173  				_isEnabled = false;
   174  				_backup = _items;
   175  				_items = [];
   176  				buildMenu( );
   177  			}
   178  		} else {
   179  			if (_isEnabled != true) {
   180  				_isEnabled = true;
   181  				_items = _backup;
   182  				buildMenu( );
   183  			}
   184  		}
   185  	}
   186  
   187  	/**
   188  	 * Menu enabled getter
   189  	 * @return Boolean
   190  	 */
   191  	public function get enabled():Boolean {
   192  		return _isEnabled;	
   193  	}	
   194  
   195  	/**
   196  	 * Menu builtInItems setter
   197  	 * @param b (Boolean) - enable (true) or disable (false) the Context Menu's built in items [default: false].
   198  	 * @return Void
   199  	 */
   200  	public function set builtInItems(b:Boolean):Void {
   201  		_hasBuiltInItems = b;	
   202  	}
   203  
   204  	/**
   205  	 * Menu builtInItems getter
   206  	 * @return Boolean
   207  	 */
   208  	public function get builtInItems():Boolean {
   209  		return _hasBuiltInItems;	
   210  	}
   211  
   212  	/**
   213  	 * Void function to assign to items added without callback
   214  	 * @return Void
   215  	 */
   216  	public function menuClick():Void {
   217  	}
   218  }