package { import flash.geom.Rectangle; import flash.display.Sprite; import flash.display.Shape; import flash.display.StageScaleMode; import flash.text.*; import flash.events.*; import flash.external.*; public class FseFunnel extends Sprite { var rightOffset = 50; var topOffset = 30; var labelPaddingTop = 5; var funnelName: String = "FseFunnel"; var funnelShape :Shape = null; var funnelLabels :Array = []; var likelihoodLabelOffset = 10; var nameLabelOffset = 40 + rightOffset; var nameLabelWidth = 150; var countLabelOffset = 200 + rightOffset; var countLabelWidth = 60; var volumeLabelOffset = 275 + rightOffset; var volumeLabelWidth = 60; var valueLabelOffset = 350 + rightOffset; var valueLabelWidth = 60; public function FseFunnel( ) { if( loaderInfo.parameters.funnelName ) { funnelName = loaderInfo.parameters.funnelName; } var fn = function( config ) : String { try { drawFunnel( config ); } catch( e ) { var xText = new TextField(); xText.text = e.toString(); xText.autoSize = TextFieldAutoSize.LEFT; addChild( xText ); } finally { return funnelName; } }; // stage.scaleMode = StageScaleMode.NO_SCALE; //var readyText = new TextField(); //readyText.text = "Player On"; //addChild( readyText ); try { if( ExternalInterface.available ) { ExternalInterface.addCallback( "drawFunnel", fn ); var fnReady = function( e:Event ) : void { ExternalInterface.call( "fseFunnelReady", funnelName ); }; addEventListener( Event.ADDED_TO_STAGE, fnReady ); } else { var errorText = new TextField(); errorText.text = "ExternalInterface.available = false"; addChild( errorText ); } } catch( e ) { var exceptionText = new TextField(); exceptionText.autoSize = TextFieldAutoSize.LEFT; exceptionText.text = e.message; addChild( exceptionText ); } } private function initializeConfig( funnelConfig ) { if( ! funnelConfig.format ) { funnelConfig.format = {}; } if( ! funnelConfig.format.padding ) { funnelConfig.format.padding = 0; } if( ! funnelConfig.format.backgroundColor ) { funnelConfig.format.backgroundColor = 0xffffff; } if( ! funnelConfig.format.labelFont ) { funnelConfig.format.labelFont = "Arial"; } if( ! funnelConfig.format.labelSize ) { funnelConfig.format.labelSize = 12; } if( ! funnelConfig.format.labelColor ) { funnelConfig.format.labelColor = 0x000000; } if( ! funnelConfig.format.labelBold ) { funnelConfig.format.labelBold = false; } if( ! funnelConfig.labels ) { funnelConfig.labels = {}; } if( ! funnelConfig.labels.annualSizeLabel ) { funnelConfig.labels.annualSizeLabel = "Size"; } if( ! funnelConfig.labels.planImpactLabel ) { funnelConfig.labels.planImpactLabel = "Impact"; } /* if( ! funnelConfig.format.ideaColor ) { funnelConfig.format.ideaColor = 0xC0C0C0; } if( ! funnelConfig.format.closedColor ) { funnelConfig.format.closedColor = 0x000000; } if( ! funnelConfig.format.activeColors ) { funnelConfig.format.activeColors = [ 0xff0000, 0x00ff00, 0x0000ff ]; } */ if( ! funnelConfig.format.borderColor ) { funnelConfig.format.borderColor = 0x171717; } if( ! funnelConfig.format.width ) { funnelConfig.format.width = 200; } if( ! funnelConfig.format.height ) { funnelConfig.format.height = 150; } if( ! funnelConfig.format.funnelColor ) { funnelConfig.format.funnelColor = 0xFF1717; } } private function drawFunnel( funnelConfig ) { initializeConfig( funnelConfig ); var volumeTotal = 0; var valueTotal = 0; var countTotal = 0; var funnelOriginX:uint = funnelConfig.format.padding; var funnelOriginY:uint = funnelConfig.format.padding + topOffset; var funnelHeight:uint = funnelConfig.format.height; var funnelTopWidth:uint = funnelConfig.format.width; var funnelBottomWidth:uint = funnelConfig.format.width * .3; var funnelLevels:uint = funnelConfig.levels.length; var colors = [ 0xE0E0E0, 0xFF0000, 0x00FF00, 0x0000FF, 0x0FF000, 0x343434, 0x000000 ]; var funnel:Shape = new Shape(); var labels:Array = []; var fg = funnel.graphics; var headerFormat = new TextFormat(); headerFormat.font = funnelConfig.format.labelFont; headerFormat.size = funnelConfig.format.labelSize; headerFormat.bold = true; var headerLabel = new TextField(); headerLabel.text = "Stage"; headerLabel.x = funnelTopWidth + nameLabelOffset; headerLabel.y = funnelConfig.format.padding + labelPaddingTop; headerLabel.setTextFormat( headerFormat ); labels[labels.length] = headerLabel; headerLabel = new TextField(); headerLabel.text = "Count"; headerLabel.x = funnelTopWidth + countLabelOffset; headerLabel.y = funnelConfig.format.padding + labelPaddingTop; headerLabel.setTextFormat( headerFormat ); labels[labels.length] = headerLabel; headerLabel = new TextField(); headerLabel.text = funnelConfig.labels.annualSizeLabel; headerLabel.x = funnelTopWidth + volumeLabelOffset; headerLabel.y = funnelConfig.format.padding + labelPaddingTop; headerLabel.setTextFormat( headerFormat ); labels[labels.length] = headerLabel; headerLabel = new TextField(); headerLabel.text = "Impact"; // funnelConfig.labels.planImpactLabel; headerLabel.x = funnelTopWidth + valueLabelOffset; headerLabel.y = funnelConfig.format.padding + labelPaddingTop; headerLabel.setTextFormat( headerFormat ); labels[labels.length] = headerLabel; // root.opaqueBackground = funnelConfig.format.backgroundColor; // calculate boundary var ftlx = funnelOriginX; var ftly = funnelOriginY; var ftrx = funnelOriginX + funnelTopWidth; var ftry = funnelOriginY; var fbrx = funnelOriginX + funnelTopWidth - (funnelTopWidth - funnelBottomWidth) / 2; var fbry = funnelOriginY + funnelHeight; var fblx = funnelOriginX + (funnelTopWidth - funnelBottomWidth) / 2; var fbly = funnelOriginY + funnelHeight; // Draw border var drawBorder = false; if( drawBorder ) { fg.lineStyle( 1, 0x171717 ); fg.moveTo( ftlx, ftly ); fg.lineTo( ftrx, ftry ); fg.lineTo( fbrx, fbry ); fg.lineTo( fblx, fbly ); fg.lineTo( ftlx, ftly ); } var leftSlope = (ftly - fbly) / (ftlx - fblx); var rightSlope = (ftry - fbry) / (ftrx - fbrx); var leftOriginX:uint = ftlx; var leftOriginY:uint = ftly; var rightOriginX:uint = ftrx; var rightOriginY:uint = ftry; // draw levels var levelHeight:uint = funnelHeight / funnelLevels; var baseX:uint = funnelOriginX; var baseY:uint = funnelOriginY; var baseW:uint = funnelTopWidth; var colorIdx:uint = 0; fg.lineStyle( 1, funnelConfig.format.borderColor ); for( var level = 0; level < funnelLevels; level++ ) { var levelConfig = funnelConfig.levels[level]; // calculate level bounds var ltlx:uint = baseX; var ltly:uint = baseY; // y - funnelOriginY = leftSlope * ( x - funnelOriginX ) // y - funnelOriginY = ( leftSlope * x ) - ( leftSlope * funnelOriginX ) // y - funnelOriginY + ( leftSlope * funnelOriginX ) = ( leftSlope * x ) // ( y - funnelOriginY + ( leftSlope * funnelOriginX )) / leftSlope = x var lbly:uint = ltly + levelHeight; var lblx:uint = ( lbly - leftOriginY + ( leftSlope * leftOriginX )) / leftSlope; var ltrx:uint = ltlx + baseW; var ltry:uint = baseY; var lbry:uint = lbly; var lbrx:uint = ( lbry - rightOriginY + ( rightSlope * rightOriginX )) / rightSlope; var percentageToUse; if( level == 0 ) { percentageToUse = 0; } else if ( level == funnelLevels - 1 ) { percentageToUse = 100; } else { percentageToUse = levelConfig.likelihood * 100; } var colorToUse = tintColor( funnelConfig.format.funnelColor, percentageToUse ); fg.beginFill( colorToUse ); fg.moveTo( ltlx, ltly ); fg.lineTo( lblx, lbly ); fg.lineTo( lbrx, lbry ); fg.lineTo( ltrx, ltry ); fg.lineTo( ltlx, ltly ); // name label var nameLabel= new TextField(); nameLabel.text = levelConfig.name; // nameLabel.autoSize = TextFieldAutoSize.LEFT; nameLabel.y = ltly + labelPaddingTop; // + ( lbly - ltly ); nameLabel.x = ftrx + nameLabelOffset; nameLabel.width = nameLabelWidth; var labelFormat = new TextFormat(); labelFormat.font = funnelConfig.format.labelFont; labelFormat.size = funnelConfig.format.labelSize; labelFormat.bold = funnelConfig.format.labelBold; labelFormat.color = funnelConfig.format.labelColor; labelFormat.url = "event:viewPipeline," + levelConfig.linkParameter; nameLabel.setTextFormat( labelFormat ); nameLabel.addEventListener( TextEvent.LINK, linkListener ); // addChild( nameLabel ); labels[labels.length] = nameLabel; // likelihood label if( levelConfig.likelihood > 0 && levelConfig.likelihood < 1 ) { var likelihoodLabel = new TextField(); likelihoodLabel.text = levelConfig.likelihood * 100 + "%"; likelihoodLabel.autoSize = TextFieldAutoSize.RIGHT; likelihoodLabel.y = ltly + labelPaddingTop; likelihoodLabel.x = ftrx + likelihoodLabelOffset; likelihoodLabel.setTextFormat( labelFormat ); likelihoodLabel.addEventListener( TextEvent.LINK, linkListener ); labels[labels.length] = likelihoodLabel; } var volumeLabel = new TextField(); volumeLabel.text = levelConfig.volume; volumeLabel.width = volumeLabelWidth; volumeLabel.y = ltly + labelPaddingTop; volumeLabel.x = ftrx + volumeLabelOffset; volumeLabel.setTextFormat( labelFormat ); volumeLabel.addEventListener( TextEvent.LINK, linkListener ); labels[labels.length] = volumeLabel; volumeTotal += levelConfig.volume; if( levelConfig.likelihood > 0 ) { var valueLabel = new TextField(); valueLabel.text = levelConfig.value; valueLabel.width = valueLabelWidth; valueLabel.y = ltly + labelPaddingTop; valueLabel.x = ftrx + valueLabelOffset; valueLabel.setTextFormat( labelFormat ); valueLabel.addEventListener( TextEvent.LINK, linkListener ); labels[labels.length] = valueLabel; valueTotal += levelConfig.value; } var countLabel = new TextField(); countLabel.text = levelConfig.count; countLabel.width = countLabelWidth; countLabel.y = ltly + labelPaddingTop; countLabel.x = ftrx + countLabelOffset; countLabel.setTextFormat( labelFormat ); countLabel.addEventListener( TextEvent.LINK, linkListener ); labels[labels.length] = countLabel; countTotal += levelConfig.count; baseX = lblx; baseY = lbly; baseW = lbrx - lblx; } // totals var totalFormat = new TextFormat(); totalFormat.font = funnelConfig.format.labelFont; totalFormat.size = funnelConfig.format.labelSize; totalFormat.bold = true; var totalLabel = new TextField(); totalLabel.text = countTotal; totalLabel.x = funnelTopWidth + countLabelOffset; totalLabel.y = baseY + labelPaddingTop; totalLabel.setTextFormat( totalFormat ); labels[labels.length] = totalLabel; totalLabel = new TextField(); totalLabel.text = volumeTotal; totalLabel.x = funnelTopWidth + volumeLabelOffset; totalLabel.y = baseY + labelPaddingTop; totalLabel.setTextFormat( totalFormat ); labels[labels.length] = totalLabel; totalLabel = new TextField(); totalLabel.text = valueTotal; totalLabel.x = funnelTopWidth + valueLabelOffset; totalLabel.y = baseY + labelPaddingTop; totalLabel.setTextFormat( totalFormat ); labels[labels.length] = totalLabel; // remove existing funnel if present if( funnelShape ) { removeChild( funnelShape ); funnelShape = null; } // remove existing labels if present if( funnelLabels.length ) { for( var x = 0; x < funnelLabels.length; x++ ) { removeChild( funnelLabels[x] ); } funnelLabels = []; } // add new funnel and labels funnelShape = funnel; addChild( funnelShape ); funnelLabels = labels; for( var lx = 0; lx < funnelLabels.length; lx++ ) { addChild( funnelLabels[lx] ); } ExternalInterface.call( "funnelDrawn", funnelName, width, height ); } private function linkListener( e:TextEvent ) : void { ExternalInterface.call( "myClickHandler", funnelName, e.text ); } private function tintColor( color:Number, tintPercent:Number ) : Number { var r:Number = ( color & 0xff0000 ) >> 16; var g:Number = ( color & 0x00ff00 ) >> 8; var b:Number = ( color & 0x0000ff ); var tintRate:Number = (100-tintPercent)/100; r = Math.round( r + ((255 - r) * tintRate )); g = Math.round( g + ((255 - g) * tintRate )); b = Math.round( b + ((255 - b) * tintRate )); return (r << 16) | (g << 8) | b; } } }