1  /**
     2   * com.sekati.log.OutPanel
     3   * @version 1.9.0
     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.events.Event;
    10  import com.sekati.events.Dispatcher;
    11  import com.sekati.utils.Delegate;
    12  import flash.filters.DropShadowFilter;
    13  
    14  /**
    15   * Creates a panel within current swf for live debugging via {@link com.sekati.log.Out}. 
    16   * Can be toggled on/off with hotkey [Shift+Right Arrow] or [Shift + Left Arrow]
    17   * Can clear panel output with hotkey [Shift + Backspace] or [Shift + Delete]
    18   * 
    19   * XXX - !!! NOTE: v1.9.0 is under heavy construction - please revert to earlier version in SVN for working version!!!
    20   * 
    21   * {@code Usage:
    22   * OutPanel.getInstance(640,480);
    23   * -or-
    24   * var out:Out = Out.getInstance();
    25   * out.createPanel(640,480);
    26   * }
    27   */
    28  class com.sekati.log.OutPanel {
    29  
    30  	// Constant & Static Properties
    31  	public static var CLASSNAME:String = "OutPanel";
    32  	public static var VERSION:String = "1.7.3";
    33  	public static var AUTHOR:String = "Sekati";
    34  	public static var URI:String = "http://sekati.com";
    35  	public static var ID:String = AUTHOR + "." + CLASSNAME + " v" + VERSION;
    36  	private static var _instance:OutPanel;
    37  	// UI Properties
    38  	private var _this:OutPanel;
    39  	private var _outPanel:MovieClip;
    40  	private var _box:MovieClip;
    41  	private var _handle:MovieClip;
    42  	private var _szhandle:MovieClip;
    43  	private var _headTf:TextField;
    44  	private var _fpsTf:TextField;
    45  	private var _outTf:TextField;
    46  	private static var ui:Object = {_width:680, _height:450, _x:10, _y:10, _alpha:85, _szsize:11, _handleHeight:15, _scrollWidth:10, _scrollHeightOffset:31, _scrollxOffset:11, _scrollyOffset:17, _szOffset:1, _barHeight:150, _borderColor:0x333333, _boxColor:0xFFFFFF, _handleColor:0x3A3A3A, _titleColor:0xFFFF00, _outColor:0x000000, _szColor: 0xFF6600, _gutterColor:0xCCCCCC, _barColor:0x666666};
    47  	// Scroll Properties
    48  	private var _scroller:MovieClip;
    49  	private var _gutter:MovieClip;
    50  	private var _bar:MovieClip;
    51  	private var _g:MovieClip;
    52  	private var _b:MovieClip;
    53  	private var _s:MovieClip;
    54  	private var _isDrag:Boolean = false;
    55  	private var _isResize:Boolean = false;
    56  	// Event Handler Properties
    57  	private var _fps:Number;
    58  	private var _startTime:Number;
    59  	private var _totalTime:Number;
    60  	private var _keyListener:Object;
    61  
    62  	/**
    63  	 * Singleton Private Constructor
    64  	 */
    65  	private function OutPanel() {
    66  		_this = this;
    67  		initUI( );
    68  		Dispatcher.$.addEventListener( "OUT_EVENT", Delegate.create( this, eventHandler ) );
    69  	}
    70  
    71  	/**
    72  	 * Singleton Accessor
    73  	 * @param w (Number) panel width
    74  	 * @param h (Number) panel height
    75  	 * @return OutPanel
    76  	 */	
    77  	public static function getInstance(w:Number, h:Number):OutPanel {
    78  		if (!_instance) {
    79  			if (w && h) {
    80  				ui._width = w;
    81  				ui._height = h;
    82  			}
    83  			_instance = new OutPanel( );
    84  		}
    85  		return _instance;
    86  	}
    87  
    88  	/**
    89  	 * shorthand singleton accessor getter
    90  	 */
    91  	public static function get $():OutPanel {
    92  		return OutPanel.getInstance( );	
    93  	}	
    94  
    95  	private function eventHandler(eventObj:Event):Void {
    96  		//trace ("eventObj{target:" + eventObj.target + ",type:" + eventObj.type + ",message:" + eventObj.data.message + "};");
    97  		_outTf.text += eventObj.data.message + "\n";
    98  		_outTf.scroll = _outTf.maxscroll;
    99  		updateBar( );
   100  	}
   101  
   102  	private function clearOutput():Void {
   103  		//trace ("clear");
   104  		_outTf.text = "";
   105  		updateBar( );
   106  	}
   107  
   108  	/**
   109  	 * ui methods
   110  	 */
   111  	private function resizePanel():Void {
   112  		trace( "re" );
   113  		var newW:Number = _szhandle._x + ui._szsize + ui._szOffset;
   114  		var newH:Number = _szhandle._y + ui._szsize + ui._szOffset;
   115  		ui._width = newW;
   116  		ui._height = newH;
   117  	 	//initUI();
   118  	 	
   119  	 	//_szhandle._x + ui._szsize, _szhandle._y + ui._szsize	
   120  		/*
   121  		drawRect (_box, w, h, c, c2, a, true);
   122  		_debug._width = (w/2) -10;
   123  		_debug._height = h-30;
   124  		
   125  		_tdebug._width = (w/2) -10;
   126  		_tdebug._x = (w/2) +5;
   127  		_tdebug._height = h-30;
   128  		_handle._width = w;
   129  		_header._width = _fpsf._x = w-50;
   130  		 */
   131  		 
   132  		 /*
   133  		  	//CC debug	
   134  		drawRect (_box, w, h, c, c2, a, true);
   135  
   136  		
   137  		_debug._width = (w/2) -10;
   138  		_debug._height = h-30;
   139  		
   140  		_tdebug._width = (w/2) -10;
   141  		_tdebug._x = (w/2) +5;
   142  		_tdebug._height = h-30;
   143  		_handle._width = w;
   144  		_header._width = _fpsf._x = w-50;
   145  		  */
   146  	}
   147  
   148  	private function initUI():Void {
   149  		// main panel container with dropshadow
   150  		var _outPanel:MovieClip = _level0.createEmptyMovieClip( "_outPanel", _level0.getNextHighestDepth( ) );
   151  		_outPanel._visible = false; 
   152  		_outPanel.cacheAsBitmap = true;
   153  		_outPanel._x = ui._x;
   154  		_outPanel._y = ui._y;
   155  		var ds:DropShadowFilter = new DropShadowFilter( 3, 45, 0x000000, .5, 5, 5, 1, 3, false, false, false );
   156  		_outPanel.filters = [ ds ];	
   157  		
   158  		var _box:MovieClip = _outPanel.createEmptyMovieClip( "_box", _outPanel.getNextHighestDepth( ) );
   159  		drawRect( _box, ui._width, ui._height, ui._boxColor, ui._borderColor, ui._alpha, true );
   160  
   161  		var _handle:MovieClip = _outPanel.createEmptyMovieClip( "_handle", _outPanel.getNextHighestDepth( ) );
   162  		drawRect( _handle, ui._width, ui._handleHeight, ui._handleColor, ui._borderColor, ui._alpha, true );
   163  
   164  		// create scroller
   165  		var _scroller:MovieClip = _outPanel.createEmptyMovieClip( "_scroller", _outPanel.getNextHighestDepth( ) );
   166  		_scroller._visible = false; 
   167  		_scroller._x = (ui._width - ui._scrollxOffset); 
   168  		_scroller._y = ui._scrollyOffset;
   169  
   170  		var _gutter:MovieClip = _scroller.createEmptyMovieClip( "_gutter", _scroller.getNextHighestDepth( ) );
   171  		drawRect( _gutter, ui._scrollWidth, (ui._height - ui._scrollHeightOffset), ui._gutterColor, ui._gutterColor, 100, false );
   172  
   173  		var _bar:MovieClip = _scroller.createEmptyMovieClip( "_bar", _scroller.getNextHighestDepth( ) );
   174  		drawRect( _bar, ui._scrollWidth, ui._barHeight, ui._barColor, ui._barColor, 100, false );
   175  
   176  		// create refs
   177  		_s = _level0._outPanel._scroller; 
   178  		_g = _s._gutter;
   179  		_b = _s._bar;
   180  
   181  		// create textfields
   182  		_headTf = createTf( _outPanel, "_headTf", 5, 0, (ui._width - 50), 20, {html:true, selectable:false, htmlText:ID}, {color:ui._titleColor, url:URI} );
   183  		_fpsTf = createTf( _outPanel, "_fpsTf", (ui._width - 50), 0, 50, 20, {selectable:false}, {color:ui._titleColor} );
   184  		_outTf = createTf( _outPanel, "_outTf", 5, 20, (ui._width - 20), (ui._height - 25), {type:"dynamic", mouseWheelEnabled:true, wordWrap:true, border:false, multiline:true, selectable:true}, {color:ui._outcolor} );
   185  
   186  		// resize handle
   187  		var _szhandle:MovieClip = _outPanel.createEmptyMovieClip( "_szhandle", _outPanel.getNextHighestDepth( ) );
   188  		_szhandle._x = ui._width - ui._szsize - ui._szOffset;
   189  		_szhandle._y = ui._height - ui._szsize - ui._szOffset;
   190  		drawTriangle( _szhandle, ui._szsize, ui._szColor );
   191  
   192  		// event handlers
   193  		_outPanel.onEnterFrame = Delegate.create( _this, _onEnterFrame );
   194  		
   195  		_handle.onPress = Delegate.create( _outPanel, startDrag );
   196  		_handle.onRelease = _handle.onReleaseOutside = Delegate.create( _outPanel, stopDrag );		
   197  		
   198  		_szhandle.onPress = Delegate.create( _this, szhandle_onPress );
   199  		_szhandle.onRelease = _szhandle.onReleaseOutside = Delegate.create( _this, szhandle_onRelease );
   200  		
   201  		_bar.onPress = Delegate.create( _this, bar_onPress );
   202  		_bar.onRelease = _bar.onReleaseOutside = Delegate.create( _this, bar_onRelease );
   203  		
   204  		_gutter.onPress = Delegate.create( _this, gutter_onPress );
   205  		
   206  		_outTf.onChanged = Delegate.create( _this, updateBar );
   207  		
   208  		_keyListener = new Object( );
   209  		_keyListener.onKeyDown = function ():Void {
   210  			if ((Key.isDown( Key.SHIFT ) && Key.isDown( Key.RIGHT )) || (Key.isDown( Key.SHIFT ) && Key.isDown( Key.LEFT ))) {
   211  				_outPanel._visible = !_outPanel._visible;
   212  			}
   213  			if ((Key.isDown( Key.SHIFT ) && (Key.isDown( Key.BACKSPACE ) || Key.isDown( Key.DELETEKEY )))) {
   214  				Delegate.create( _this, clearOutput );
   215  			}
   216  		};
   217  		Key.addListener( _keyListener );
   218  	}
   219  
   220  	private function createTf(scope:MovieClip, instanceName:String, x:Number, y:Number, w:Number, h:Number, props:Object, fprops:Object):TextField {
   221  		var t:TextField = scope.createTextField( instanceName, scope.getNextHighestDepth( ), x, y, w, h );
   222  		for (var i in props) {
   223  			//t.border = true;
   224  			t.embedFonts = false;
   225  			t.antiAliasType = "normal";
   226  			t.gridFitType = "pixel";
   227  			t.sharpness = 400;
   228  			t.thickness = 0;
   229  
   230  			t[i] = props[i];
   231  		}
   232  		if (fprops) {
   233  			var f:TextFormat = new TextFormat( );
   234  			f.font = "Verdana";
   235  			f.kerning = true;
   236  			f.size = 9;
   237  			for (var j in fprops) {
   238  				f[j] = fprops[j];
   239  			}
   240  			t.setNewTextFormat( f );
   241  		}
   242  		if (props.text) {
   243  			t.text = props.text;
   244  		} else if (props.htmlText) {
   245  			t.htmlText = props.htmlText;
   246  		}
   247  		return t;
   248  	}
   249  
   250  	private function drawRect(mc:MovieClip, w:Number, h:Number, color:Number, borderColor:Number, alpha:Number, isBorder:Boolean):Void {
   251  		var l:Number = (!isBorder) ? undefined : 1;
   252  		mc.clear( );
   253  		mc.lineStyle( l, borderColor, alpha, true, "none", "square", "miter", 1.414 );
   254  		mc.beginFill( color, alpha );
   255  		mc.moveTo( 0, 0 );
   256  		mc.lineTo( w, 0 );
   257  		mc.lineTo( w, h );
   258  		mc.lineTo( 0, h );
   259  		mc.endFill( );
   260  	}
   261  
   262  	private function drawTriangle(mc:MovieClip, s:Number, c:Number):Void {
   263  		mc.lineStyle( undefined, 0x000000, 100, true, "none", "square", "miter", 1.414 );
   264  		mc.beginFill( c, 100 );
   265  		mc.moveTo( 0, s );
   266  		mc.lineTo( s, s );
   267  		mc.lineTo( s, 0 );
   268  		mc.lineTo( 0, s );
   269  	}
   270  
   271  	/**
   272  	 * Event methods
   273  	 */
   274  	private function szhandle_onPress():Void {
   275  		//_isResize = true;
   276  		_szhandle.startDrag( false, 0, 0, 1000, 1000 );
   277  	 	//_szhandle.startDrag();
   278  	}
   279  
   280  	private function szhandle_onRelease():Void {
   281  		//_isResize = false;
   282  		_szhandle.stopDrag( );
   283  		//resizePanel();
   284  	}
   285  
   286  	private function bar_onPress():Void {
   287  		_isDrag = true;
   288  		_b.startDrag( false, _b._x, _g._y, _bar._x, (_g._y + _g._height - _b._height) );
   289  	}
   290  
   291  	private function bar_onRelease():Void {
   292  		_isDrag = false;
   293  		_b.stopDrag( );
   294  	}
   295  
   296  	private function gutter_onPress():Void {
   297  		var newY:Number = constrain( _g._ymouse, _g._y, _g._height - _b._height );
   298  		_b._y = newY;
   299  		updateScroll( );
   300  	}
   301  
   302  	private function updateScroll():Void {
   303  		var _gutterTop:Number = _g._y;
   304  		var _gutterBot:Number = _g._height - _b._height;
   305  		var percent:Number = int( (_b._y - _gutterTop) / (_gutterBot - _gutterTop) * 100 );
   306  		var newScroll:Number = _outTf.maxscroll * percent / 100;
   307  		_outTf.scroll = newScroll;
   308  	}
   309  
   310  	private function updateBar():Void {
   311  		_s._visible = isScrollable( );
   312  		if (!_s._visible) {
   313  			return;
   314  		}
   315  		var _gutterTop:Number = _g._y;
   316  		var _gutterBot:Number = _g._height - _b._height;
   317  		var percent:Number = int( _outTf.scroll * 100 / _outTf.maxscroll );
   318  		var newPos:Number = (_gutterBot - _gutterTop) * percent / 100;
   319  		_b._y = constrain( newPos );
   320  	}
   321  
   322  	private function isScrollable():Boolean {
   323  		return (_outTf.textHeight > _outTf._height);
   324  	}
   325  
   326  	private function constrain(val:Number, min:Number, max:Number):Number {
   327  		if (val < min) {
   328  			val = min;
   329  		} else if (val > max) {
   330  			val = max;
   331  		}
   332  		return val;
   333  	}
   334  
   335  	private function _onEnterFrame():Void {
   336  		fpsMonitor( );
   337  		if(_isResize) {
   338  			//trace("doin a resizer!");
   339  			resizePanel( );	
   340  		}
   341  		if (_isDrag) {
   342  			updateScroll( );
   343  		}
   344  	}
   345  
   346  	private function fpsMonitor():Void {
   347  		_totalTime = getTimer( ) - _startTime;
   348  		_fps = Math.round( 1000 / this._totalTime );
   349  		_startTime = getTimer( );
   350  		_fpsTf.text = " FPS: " + _fps;
   351  	}
   352  }