1  /**
     2   * com.sekati.log.Console
     3   * @version 1.3.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.display.StageDisplay;
    10  import com.sekati.events.Dispatcher;
    11  import com.sekati.events.Event;
    12  import com.sekati.log.ConsoleFPSMonitor;
    13  import com.sekati.log.ConsoleItem;
    14  import com.sekati.log.ConsoleStyle;
    15  import com.sekati.log.LCBinding;
    16  import com.sekati.log.LogEvent;
    17  import com.sekati.math.MathBase;
    18  import com.sekati.ui.ContextualMenu;
    19  import com.sekati.ui.Scroll;
    20  import com.sekati.utils.ClassUtils; 
    21  import com.sekati.utils.Delegate;
    22  
    23  /**
    24   * UI Console for attaching or connecting too
    25   */
    26  class com.sekati.log.Console {
    27  
    28  	// core console props
    29  	private static var _instance:Console;
    30  	private var _this:Console;
    31  	private var _cs:ConsoleStyle;
    32  	private var _style:Object;
    33  	// ui props
    34  	private var _console:MovieClip;
    35  	private var _bg:MovieClip;
    36  	private var _head:MovieClip;
    37  	private var _headBg:MovieClip;
    38  	private var _headTf:TextField;
    39  	private var _fps:MovieClip;
    40  	private var _holder:MovieClip;
    41  	private var _list:MovieClip;
    42  	private var _mask:MovieClip;
    43  	private var _scroll:Scroll;
    44  	private var _cmenu:ContextualMenu;
    45  	private var _gutter:MovieClip;
    46  	private var _bar:MovieClip;
    47  	private var _resizer:MovieClip;
    48  	private var _metaItem:MovieClip;
    49  	// manager props
    50  	private var _isEnabled:Boolean;
    51  	private var _logItems:Array;
    52  	private var _logIndex:Number;
    53  	private var _items:Array;
    54  
    55  	/**
    56  	 * Singleton Private Constructor
    57  	 */
    58  	private function Console() {
    59  		_this = this;
    60  		_isEnabled = true;
    61  		_items = new Array( );
    62  		_cs = ConsoleStyle.getInstance( );
    63  		_style = _cs.CSS;
    64  		Dispatcher.$.addEventListener( LogEvent.onLogEVENT, Delegate.create( _this, onLogEvent ) );
    65  		Dispatcher.$.addEventListener( StageDisplay.onStageResizeEVENT, Delegate.create( _this, onStageResize ) );
    66  		LCBinding.connect( Delegate.create( _this, addItem ) );
    67  		createUI( );
    68  	}
    69  
    70  	/**
    71  	 * Singleton Accessor
    72  	 * @return Console
    73  	 */	
    74  	public static function getInstance():Console {
    75  		if (!_instance) _instance = new Console( );
    76  		return _instance;
    77  	}
    78  
    79  	/**
    80  	 * shorthand singleton accessor getter
    81  	 */
    82  	public static function get $():Console {
    83  		return Console.getInstance( );	
    84  	}
    85  
    86  	/**
    87  	 * Create the core Console UI
    88  	 */
    89  	private function createUI():Void {
    90  		_console = _cs.createClip( _level0, _style.console );
    91  		_console._x = _style.console.x;
    92  		_console._y = _style.console.y;
    93  		//_console._quality = "LOW";
    94  		_bg = _cs.createStyledRectangle( _console, _style.console.bg );
    95  		
    96  		_head = _cs.createClip( _console, _style.console.head );
    97  		_headBg = _cs.createStyledRectangle( _head, _style.console.head.bg );
    98  		_headTf = _cs.createStyledTextField( _head, _style.console.head.textfields.head );
    99  		
   100  		// create ConsoleFPSMonitor
   101  		_fps = ClassUtils.createEmptyMovieClip( com.sekati.log.ConsoleFPSMonitor, _head, _style.console.head.fps.n, {_x:_style.console.head.fps.x, _y:_style.console.head.fps.y} );		
   102  		
   103  		// create core ui clip holders
   104  		_holder = _cs.createPositionClip( _console, _style.console.holder );
   105  		_list = _cs.createPositionClip( _holder, _style.console.holder.list );
   106  		
   107  		// create core ui clip shapes
   108  		_mask = _cs.createStyledRectangle( _holder, _style.console.holder.mask );
   109  		_gutter = _cs.createStyledRectangle( _holder, _style.console.holder.gutter );
   110  		_gutter.cacheAsBitmap = true;
   111  		_bar = _cs.createStyledRectangle( _holder, _style.console.holder.bar );
   112  		_bar.cacheAsBitmap = true;
   113  		_bar._visible = false;
   114  		
   115  		// create resizer
   116  		_resizer = _cs.createStyledTriangle( _console, _style.console.resizer );
   117  	
   118  		// create the meta item
   119  		_metaItem = ClassUtils.createEmptyMovieClip( com.sekati.log.ConsoleItem, _list, "metaItem", {_x:0, _y:0, _data:{_isMeta:true}} );
   120  		_items.push( _metaItem );	
   121  	
   122  		// set masking & initialize _scroll
   123  		_list.setMask( _mask );
   124  		_scroll = new Scroll( "_y", _list, _mask, _gutter, _bar, true, true, true, true, _list );
   125  		
   126  		// initialize contextMenu
   127  		_cmenu = new ContextualMenu( _console );
   128  		var cb:Function = function():Void {
   129  			getURL( _style.console.head.textfields.head.url, "_blank" );	
   130  		};
   131  		_cmenu.addItem( _style.console.head.textfields.head.t, Delegate.create( this, cb ), true );
   132  		_cmenu.addItem( "Pause Console", Delegate.create( this, toggleLogEnable ), true );
   133  		_cmenu.addItem( "Copy Console Log", Delegate.create( this, toClipboard ), true );
   134  		_cmenu.addItem( "Clear Console Log", Delegate.create( this, reset ), false );
   135  		_cmenu.addItem( "Minimize Console", Delegate.create( this, resize, _style.console.minW, _style.console.minH ), true );
   136  		_cmenu.addItem( "Maximize Console", Delegate.create( this, resize, _style.console.maxW, _style.console.maxH ), false );
   137  		// events
   138  		if(_style.console.head.isDraggable == true) {
   139  			_head.onPress = Delegate.create( _console, startDrag, false, _style.console.x, _style.console.y, _style.console.maxW, _style.console.maxH );
   140  			_head.onRelease = _head.onReleaseOutside = Delegate.create( _console, stopDrag );
   141  			_resizer.onPress = Delegate.create( this, resizer_onPress );
   142  			_resizer.onRelease = _resizer.onReleaseOutside = Delegate.create( this, resizer_onRelease );
   143  		} else {
   144  			_resizer._visible = false;	
   145  		}
   146  		// resize console to fit swf
   147  		resize( Stage.width - _style.console.x * 4, Stage.height - _style.console.y * 4 );
   148  	}
   149  
   150  	/**
   151  	 * Resizing methods
   152  	 */
   153  	private function resizer_onPress():Void {
   154  		//_resizer.startDrag (false, (_console._x+_style.console.minW), (_console._y+_style.console.minH), Stage.width-50, Stage.height-50);
   155  		_resizer.startDrag( false, (_console._x + _style.console.minW), (_console._y + _style.console.minH), _style.console.maxW, _style.console.maxH );
   156  	}
   157  
   158  	private function resizer_onRelease():Void {
   159  		_resizer.stopDrag( );
   160  		resize( );
   161  	}
   162  
   163  	/**
   164  	 * Resize the Console UI.
   165  	 * @param nw (Number) optional new width
   166  	 * @param nh (Number) optional new height
   167  	 * @return Void
   168  	 */
   169  	public function resize(nw:Number, nh:Number):Void {
   170  		var _w:Number = (!nw) ? int( _console._xmouse ) : nw;
   171  		var _h:Number = (!nh) ? int( _console._ymouse ) : nh;
   172  		
   173  		// add constraints to sanitize resizing
   174  		var w:Number = MathBase.constrain( _w, _style.console.minW, _style.console.maxW );
   175  		var h:Number = MathBase.constrain( _h, _style.console.minH, _style.console.maxH );
   176  		
   177  		// new mask width/height
   178  		var mw:Number = w - 11;
   179  		var mh:Number = h - _cs.IH;
   180  		
   181  		// _bg		
   182  		_bg._width = w;
   183  		_bg._height = h;
   184  		
   185  		// resizer
   186  		_resizer._x = _bg._width - _resizer._width;
   187  		_resizer._y = _bg._height - _resizer._height;		
   188  		
   189  		// _head
   190  		_headBg._width = w;
   191  		_fps._x = w - 120;
   192  		
   193  		// _mask
   194  		_mask._width = mw;
   195  		_mask._height = mh;		
   196  		
   197  		// _gutter, _bar, update _scroll
   198  		_gutter._x = (_bg._width - _gutter._width);
   199  		_gutter._height = (_bg._height - _headBg._height);
   200  		_bar._x = _gutter._x;
   201  		
   202  		// dispatch new itemWidth to all ConsoleItems
   203  		for(var i:Number = 0; i < _items.length ; i++) {
   204  			resizeConsoleItem( i, mw );
   205  		}
   206  		_scroll.slideScroller( _gutter._height, 0 );
   207  	}
   208  
   209  	private function resizeConsoleItem(itemIndex:Number, w:Number):Void {
   210  		var item:MovieClip = _items[itemIndex];
   211  		
   212  		// resize
   213  		item._bg._width = w;
   214  		item._line._width = w;
   215  		item._messageTf._width = w - 239;
   216  		item._benchmarkTf._x = item._messageTf._x + item._messageTf._width + 6;
   217  		
   218  		// realign
   219  		var tallestTf:TextField = (item._messageTf._height > item._originTf._height) ? item._messageTf : item._originTf;
   220  		item._bg._height = tallestTf._height;
   221  		item._line._y = tallestTf._height;	
   222  		
   223  		// reposition
   224  		var prevItem:MovieClip = _items[itemIndex - 1];
   225  		if(prevItem) {
   226  			item._y = prevItem._y + prevItem._height;
   227  		}
   228  		
   229  		// reset _style for future items
   230  		var _istyle:Object = _cs.CSS.item;
   231  		_istyle.bg.w = w;
   232  		_istyle.line.w = w;
   233  		_istyle.textfields.message.w = item._messageTf._width;
   234  		_istyle.textfields.benchmark.x = item._benchmarkTf._x;
   235  	}	
   236  
   237  	/**
   238  	 * Add and return a new ConsoleItem
   239  	 * @param data (Object) ConsoleItem._data = {id:Number, type:String, origin:String, message:String, benchmark:Number, _isMeta:Boolean}
   240  	 * @return 	MovieClip
   241  	 */
   242  	public function addItem(data:Object):MovieClip {
   243  		if(_isEnabled) {
   244  			// reset when new swf is presented
   245  			if(data.id == 0) {
   246  				reset( );
   247  				return;	
   248  			}
   249  			var item:MovieClip = ClassUtils.createEmptyMovieClip( com.sekati.log.ConsoleItem, _list, "consoleItem_" + data.id, {_x:0, _y:_list._height, _data:data} );
   250  			updateScroll( item );
   251  			_items.push( item );
   252  			return item;
   253  		}
   254  	}
   255  
   256  	/**
   257  	 * Update the {@link Scroll} to the last added item if we are not mousing the area.
   258  	 * @param item (MovieClip) last added item
   259  	 * @return Void
   260  	 */
   261  	public function updateScroll(item:MovieClip):Void {
   262  		_global["setTimeout"]( _scroll, "slideContent", 50, item._y + item._height, 0.2 );
   263  		if(!_scroll.isDragging( ) && !_scroll.isMouseInArea( ) && !_console.hitTest( _level0._xmouse, _level0._ymouse, false )) {
   264  			_global["setTimeout"]( _scroll, "slideContent", 50, item._y + item._height, 0.2 );
   265  		}
   266  	}
   267  
   268  	/**
   269  	 * Handle events from Logger and parse them to addItem
   270  	 * @param eventObj (Event)
   271  	 * @return Void
   272  	 */
   273  	 
   274  	private function onLogEvent(eventObj:Event):Void {
   275  		if (_isEnabled) {
   276  			//trace ("eventObj{target:" + eventObj.target + ",type:" + eventObj.type + ",message:" + eventObj.data.message + "};");
   277  			addItem( eventObj.data );
   278  		}
   279  	}
   280  
   281  	/**
   282  	 * Handle Stage resize events
   283  	 * @param eventObj (Event)
   284  	 * @return Void
   285  	 */
   286  	private function onStageResize(eventObj:Event):Void {
   287  		_style.console.maxW = Stage.width, 
   288  		_style.console.maxH = Stage.height;
   289  		resize( Stage.width, Stage.height );
   290  	}
   291  
   292  	/**
   293  	 * Toggle Logger.enabled and contextual menu.
   294  	 * @return Void
   295  	 */
   296  	private function toggleLogEnable():Void {
   297  		if(_isEnabled) {
   298  			_isEnabled = false;
   299  			_cmenu.editItem( "Pause Console", "Resume Console", Delegate.create( this, toggleLogEnable ) );
   300  		} else {
   301  			_isEnabled = true;
   302  			_cmenu.editItem( "Resume Console", "Pause Console", Delegate.create( this, toggleLogEnable ) );
   303  		}
   304  	}
   305  
   306  	/**
   307  	 * Select all item _data and put it in clipboard
   308  	 * @return Void
   309  	 */
   310  	public function toClipboard():Void {
   311  		System.setClipboard( toString( ) );
   312  	}
   313  
   314  	/**
   315  	 * Return Console string data 
   316  	 * @return String
   317  	 */
   318  	public function toString():String {
   319  		var o:String = _style.console.head.textfields.head.t + "\t" + _fps.toString( ) + "\n";
   320  		for (var i:Number = 0; i < _items.length ; i++) {
   321  			o += "\n" + _items[i].toString( );
   322  		}
   323  		return o;	
   324  	}
   325  
   326  	public function reset():Void {
   327  		for(var i:Number = 0; i < _items.length ; i++) {
   328  			_items[i].destroy( );	
   329  		}
   330  		_items = [];
   331  	}
   332  
   333  	/**
   334  	 * Clean and destroy
   335  	 * @return Void
   336  	 */
   337  	public function destroy():Void {
   338  		Dispatcher.$.removeEventListener( LogEvent.onLogEVENT, Delegate.create( _this, onLogEvent ) );
   339  		Dispatcher.$.removeEventListener( StageDisplay.onStageResizeEVENT, Delegate.create( _this, onStageResize ) );
   340  		LCBinding.disconnect( );
   341  		_fps.destroy( );
   342  	}
   343  }