DAN ZEN EXPO - CODE EXHIBIT - KITTY TARTAN

package source
{
	
	import com.danzen.effects.Tartan;
	import com.danzen.effects.TartanShow;
	import com.danzen.frameworks.Easy;
	import com.danzen.frameworks.zenconfirm.ConfirmID;
	import com.danzen.frameworks.zenconfirm.ConfirmPurchases;
	import com.danzen.frameworks.zenconfirm.ConfirmReceive;
	import com.danzen.interfaces.AIRPicDrop;
	import com.danzen.interfaces.Picture;
	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.filters.DropShadowFilter;
	import flash.utils.Timer;
	
	public class KittyTartan extends Sprite
	{
		
		internal var picture:Picture;
		internal var show:TartanShow;
		public var sW:Number;
		public var sH:Number;
		private var kittyInterface:KittyInterface;
		private var logo:Logo;
		private var buttons:Buttons;
		internal var startTart:Tartan;
		
		internal var readyStatus:Boolean = false;		
		private const UPLOADS_PER_APP:Number = 4;
		
		private var myConfirm:ConfirmReceive;
		internal var myPurchases:ConfirmPurchases;
		private var myConfirmID:ConfirmID;
		
		private var picDrop:AIRPicDrop;
		
		public function KittyTartan()
		{
			trace ("hi from KittyTartan");
			
			sW = stage.stageWidth;
			sH = stage.stageHeight;		
			
			startTart = new Tartan([15790320,2365452,5520432,8678484,14208192,11047044], [12,12,141,3,160,197], sW, sH);
			addChild(startTart);
					
			kittyInterface = new KittyInterface();
			addChild(kittyInterface);		
						
			// always set up to receive another download confirmation (would give 4 more uploads)
			myConfirm = new ConfirmReceive("kittytartan", "kittytartan.com");
			myConfirm.addEventListener(ConfirmReceive.COMPLETE, confirm);
			
			// ConfirmPurchases is a wrapper for shared objects and has a data property for specifics
			// first parameter is global purchase scope to help prevent mischief
			// second parameter is local purchase scope
			myPurchases = new ConfirmPurchases("danzen", "kittytartan");
			
			// ConfirmID is a wrapper for a shared object that holds an id
			myConfirmID = new ConfirmID("kittytartan");
			
			myPurchases.clear();
			myConfirmID.clear();
			
			// if we have an id then go to ready else we wait for myConfirm to confirm us and add an id
			if (myConfirmID.id) {
				ready();				
			} else {
				kittyInterface.wait();
			}
		}
		private function confirm(e:Event):void {			
			if (e.target.status == ConfirmReceive.BAD) {		
				kittyInterface.error();
				return;
			} else {
				// check to see if we should add uploads
				if (myPurchases.addID(e.target.data.pid)) {
					if (!myPurchases.data.uploads) {myPurchases.data.uploads = 0;}
					myPurchases.data.uploads += UPLOADS_PER_APP;
					if (readyStatus) {
						kittyInterface.uploadsAdded();
					}
				}
				if (!myConfirmID.id) {
					myConfirmID.id = myConfirmID.randomID();
					myConfirmID.record("http://kittytartan.com/tartan/record.php?rand="+Math.random(), {tid:myConfirmID.id});
					myConfirmID.addEventListener(Event.COMPLETE, idRecorded);
				}
				if (!readyStatus) {ready();}
			}
		}
		private function idRecorded(e:Event):void {
			trace ("id recorded " + myConfirmID.id);
		}
		private function ready():void {
			kittyInterface.start();
			
			picDrop = new AIRPicDrop(this, ["jpg", "jpeg", "gif", "png"]);
			picDrop.addEventListener(AIRPicDrop.PIC_READY, getPicture);
			readyStatus = true;							
		}
		
		// called from first close button in KittyInterface
		internal function startShow():void {
			// to do
			// need to make dropping picture do this
			// also need to make sure it works with consecutive pictures - close show open it again
			//getPicture("http://www.cs.brown.edu/orgs/artemis/2012/catsoftheworld/lime-cat.jpg");
		}
		
		private function getPicture(e:Event):void {	
			if (picture) {
				removeChild(picture);
				picture.removeEventListener(Picture.PIC_READY, getTartans);				
			}
			// Picture extends a Sprite, loads an image into itself and gets a palatte (colors property)
			// receives url:String, paletteSize:Number
			picture = new Picture(picDrop.pic, 8);
			picture.addEventListener(Picture.PIC_READY, getTartans);
			
		}
		private function getTartans(e:Event):void {	
			if (contains(startTart)) {
				removeChild(startTart);
			}
			if (show) {
				show.dispose();
				removeChild(show);				
			}
			show = new TartanShow(picture.colors, sW, sH)
			addChild(show);
			setPic(picture);
			setChildIndex(kittyInterface, numChildren-1);
		}
		
		private function setPic(pic:Sprite):void {
			// reduce pic size and center it to start
			var scale:Number = 2;
			if (pic.width > sW/2 || pic.height > sH/2) {
				if (pic.width/sW > pic.height/sH) {		
					pic.height = sW/scale/pic.width*pic.height;
					pic.width=sW/scale;								
				} else {			
					pic.width = sH/scale/pic.height*pic.width;
					pic.height=sH/scale;
				}	
			}
			
			pic.x = (sW-pic.width)/2;
			pic.y = (sH-pic.height)/2;	
			pic.filters = [new DropShadowFilter(4,45,0,.7,16,16,1)];			
			addChild(pic);			
			Easy.drag(pic,0,0,sW,sH,true,false);
			kittyInterface.picReady();
		}
		
	}
}


package source
{
	import com.adobe.images.JPGEncoder;
	import com.danzen.effects.TartanShow;
	import com.danzen.effects.TartanSpec;
	import com.danzen.interfaces.RollFilter;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.filters.DropShadowFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix;
	import flash.utils.ByteArray;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.navigateToURL;
	import flash.printing.PrintJob;
	import flash.printing.PrintJobOrientation;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;	
	import flash.utils.Timer;
	
	
	public class KittyInterface extends MovieClip
	{
		private var kt:KittyTartan;
		private var show:TartanShow;
		private var logo:Logo;		
		private var buttons:Buttons;
		private var help:Help;
		private var form:Form;
		private var alert:Alert;
		private var sW:Number;
		private var sH:Number;
		private var firstClose:Boolean = true;
		private var myFormat:TextFormat = new TextFormat();
		
		
		public function KittyInterface() {				
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event):void {
			kt = KittyTartan(parent);
			sW = kt.sW;
			sH = kt.sH;			
						
			logo = new Logo();			
			logo.x = 30;
			logo.y = sH - logo.height - 24;
			logo.alpha = .7;
			logo.buttonMode = true;	
			logo.addEventListener(MouseEvent.CLICK, clickLogo);
			
			buttons = new Buttons();			
			buttons.x = logo.x + logo.width + 30;
			buttons.y = sH - buttons.height - 24;
			buttons.alpha = .7;
			buttons.visible = false;			
			buttons.butPics.mouseEnabled = false;		
			buttons.butPics.mouseChildren = false;		
			rolls(buttons);
			buttons.addEventListener(MouseEvent.CLICK, doButtons);
						
			help = new Help();
			help.x = (sW-help.width)/2;
			help.y = (sH-help.height)/2;
			help.filters = [new DropShadowFilter(4,45,0,.5)];
			help.closeBut.visible = false;
			rolls(help.closeBut);			
			help.closeBut.addEventListener(MouseEvent.CLICK, doHelpClose);			
			new RollFilter(help.danzen, new GlowFilter(0xffccff, .3, 14, 14));
			help.danzen.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {navigateToURL(new URLRequest("http://danzen.com"), "danzen");});
			
			myFormat.size = 20; 
			myFormat.font = "Verdana"; 
			myFormat.color = 0x666666;
			myFormat.blockIndent = 10;
			
			form = new Form();
			form.x = (sW-form.width)/2;
			form.y = (sH-form.height)/2;
			form.filters = [new DropShadowFilter(4,45,0,.5)];			
			rolls(form.closeBut);
			rolls(form.submitBut);
			var fields:Array = [form.nameText, form.cityText, form.countryText, form.descriptionText, form.thoughtsText];
			for (var i:uint=0; i<fields.length; i++) {
				fields[i].setStyle("textFormat", myFormat); 
			}			
			form.closeBut.addEventListener(MouseEvent.CLICK, doFormClose);
			form.submitBut.addEventListener(MouseEvent.CLICK, doFormSubmit);			
			
			alert = new Alert();
			alert.x = (sW-alert.width)/2;
			alert.y = (sH-alert.height)/2;
			alert.filters = [new DropShadowFilter(4,45,0,.5)];			
			rolls(alert.closeBut);
			rolls(alert.questionBut);
			alert.questionBut.visible = false;
			alert.closeBut.addEventListener(MouseEvent.CLICK, doAlertClose);
			alert.questionBut.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {navigateToURL(new URLRequest("http://kittytartan.com/tartan/kitty/"), "danzen");});
						
			kt.stage.addEventListener(KeyboardEvent.KEY_DOWN, doKeyDown);	
			kt.stage.addEventListener(KeyboardEvent.KEY_UP, doKeyUp);
		}
		
		
		////// internal state functions //////
		
		
		internal function start():void {
			if (contains(alert)) {
				alert.questionBut.visible = false;
				alert.closeBut.visible = true;
				alert.gotoAndStop(1);
				removeChild(alert);
			}
			addChild(logo);
			addChild(buttons);
			addChild(help);
		}
		
		internal function wait():void {
			alert.alertText.gotoAndStop(3);
			alert.questionBut.visible = true;
			alert.closeBut.visible = false;
			addChild(alert);
		}
		
		internal function picReady():void {
			if (contains(help)) {
				removeChild(help);
				help.closeBut.visible = true;
			}
			if (contains(form)) {
				removeChild(form);
			}
			if (contains(alert)) {
				removeChild(alert);
			}
			if (logo.buttonMode == false) {
				logo.buttonMode = true;
				logo.addEventListener(MouseEvent.CLICK, clickLogo);
			}
			if (buttons.butPics.pauseButPic.currentFrame == 2) {
				buttons.butPics.pauseButPic.gotoAndStop(1);
			}
		}
		
		internal function error():void {
			if (kt.picture) {
				kt.picture.visible = false;
				buttons.visible = false;
				logo.buttonMode = false;			
				logo.removeEventListener(MouseEvent.CLICK, clickLogo);
				alert.closeBut.visible = true;
				alert.closeBut.play();
				alert.questionBut.play();
			} else {
				alert.closeBut.visible = false;
			}
			alert.alertText.gotoAndStop(4);
			alert.questionBut.visible = true;			
			if (!contains(alert)) {addChild(alert);}			
			if (contains(help)) {removeChild(help);}
			if (contains(form)) {removeChild(form);}
		}
		
		internal function noUploads():void {
			alert.alertText.gotoAndStop(5);			
			alert.closeBut.gotoAndPlay(1);
			alert.questionBut.gotoAndPlay(1);
			alert.questionBut.visible = true;
			alert.closeBut.visible = true;
			if (!contains(alert)) {addChild(alert);}			
		}		
		
		internal function uploadsAdded():void {
						
			if (kt.picture) {
				kt.picture.visible = false;
				buttons.visible = false;
				logo.buttonMode = false;			
				logo.removeEventListener(MouseEvent.CLICK, clickLogo);
				alert.closeBut.visible = true;
				alert.closeBut.play();
				alert.questionBut.play();
			} else {
				alert.closeBut.visible = false;
			}
			alert.alertText.gotoAndStop(6);			
			alert.closeBut.gotoAndPlay(1);
			alert.questionBut.visible = false;
			alert.closeBut.visible = true;
			if (!contains(alert)) {addChild(alert);}		
			if (contains(help)) {removeChild(help);}
			if (contains(form)) {removeChild(form);}			
			
		}		
		
		////// window close functions //////
		
		
		private function doAlertClose(e:MouseEvent):void {			
			removeChild(alert);
			if (kt.readyStatus) {
				logo.buttonMode = true;
				logo.addEventListener(MouseEvent.CLICK, clickLogo);
				if (kt.picture) {kt.picture.visible = true;}
			}
		}
		
		private function doHelpClose(e:MouseEvent):void {
			if (!kt.picture) {return;}
			/*
			if (firstClose) {
				firstClose = false;
				kt.startShow();				
			}
			*/
			removeChild(help);	
			logo.buttonMode = true;
			logo.addEventListener(MouseEvent.CLICK, clickLogo);
			kt.picture.visible = true;
		}
		
		private function doFormClose(e:MouseEvent):void {			
			removeChild(form);	
			logo.buttonMode = true;
			logo.addEventListener(MouseEvent.CLICK, clickLogo);
			kt.picture.visible = true;
		}		
		
		////// buttons and key events //////
		
		
		private function doButtons(e:MouseEvent):void {
			show = kt.show;
			if (!show) return;
			
			switch (e.target.name) {
				case "leftBut":
					toggleShow(); toggleShow();
					show.prev();
					break;
				case "rightBut":
					toggleShow(); toggleShow();
					show.next();
					break;
				case "pauseBut":
					toggleShow();
					break;
				case "printBut":
					printPicture();					
					break;
				case "postBut":										
					show.pause();
					if (kt.myPurchases.data.uploads <= 0) {						
						noUploads();						
					} else {
						form.closeBut.play();
						form.submitBut.play();
						form.uploadsText.text = String(kt.myPurchases.data.uploads);
						addChild(form);
					}
					kt.picture.visible = false;
					buttons.visible = false;
					logo.buttonMode = false;
					logo.removeEventListener(MouseEvent.CLICK, clickLogo);
					break;
				case "helpBut":					
					show.pause();
					help.closeBut.visible = true;
					help.closeBut.play();
					addChild(help);
					kt.picture.visible = false;
					buttons.visible = false;
					logo.buttonMode = false;
					logo.removeEventListener(MouseEvent.CLICK, clickLogo);
			}
		}
		
		private function doKeyDown(e:KeyboardEvent):void {
			show = kt.show;
			if (!show) return;
			if (contains(form)) return;
			switch (e.keyCode) {
				case Keyboard.LEFT:
					toggleShow();
					toggleShow();
					show.prev();
					buttons.leftBut.gotoAndStop(3);
					break;
				case Keyboard.RIGHT:
					toggleShow();
					toggleShow();
					show.next();
					buttons.rightBut.gotoAndStop(3);
					break;
				case Keyboard.SPACE:
					toggleShow();
					buttons.pauseBut.gotoAndStop(3);
					break;	
				case 76: // l
					logo.visible = !logo.visible;
					break;
			}
			
			
		}
		private function doKeyUp(e:KeyboardEvent):void {
			if (!show) return;			
			buttons.leftBut.gotoAndStop(1);
			buttons.rightBut.gotoAndStop(1);
			buttons.pauseBut.gotoAndStop(1);			
		}
		
		
		////// Printing and Submitting //////	
		
		
		private function printPicture():void {
			var bmd:BitmapData;
			var bm:Bitmap;
			var matrix:Matrix;
			var sprite:Sprite;
			var newW:Number;
			var newH:Number;
			var scale:Number;
			var myPrintJob:PrintJob = new PrintJob();
			
			show.pause();					 
			myPrintJob.orientation = PrintJobOrientation.LANDSCAPE;
			
			myPrintJob.start(); 
			buttons.visible = false;
			
			if (myPrintJob.printableArea.width/sW > myPrintJob.printableArea.height/sH) {		
				newH = sW/myPrintJob.printableArea.width*sH;
				newW=myPrintJob.printableArea.width;								
			} else {			
				newW = sH/myPrintJob.printableArea.height*sW;
				newH=myPrintJob.printableArea.height;
			}	
			scale = myPrintJob.printableArea.width/sW;
			
			bmd = new BitmapData(newW, newH);
			matrix = new Matrix();
			matrix.scale(scale, scale);
			bmd.draw(kt,matrix);
			sprite = new Sprite();						
			bm = new Bitmap(bmd);
			sprite.addChild(bm);
			addChild(sprite);
			try {						
				myPrintJob.addPage(sprite);						
			}
			catch (e:Error) {
			}
			sprite.removeChild(bm);
			bm = null;
			removeChild(sprite);
			sprite = null;
			buttons.visible = true;
			myPrintJob.send();	
			myPrintJob = null;
		}
		
		private function doFormSubmit(e:MouseEvent):void {
			if (form.nameText.text == "" || form.nameText.text == "Enter Name!") {
				form.nameText.text = "Enter Name!";
				return;
			}
			addChild(alert);
			alert.closeBut.play();
			alert.alertText.gotoAndStop(1);
			removeChild(form);
			var delayTimer:Timer = new Timer(500, 1);
			delayTimer.start();
			delayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function(e:TimerEvent):void {sendPicture();});
		}
		
		private function sendPicture():void {
			show.pause();
			var tid:String = String(new Date().time);
			
			// create specs jpg and send to cloud
						
			var tbd:BitmapData = new BitmapData(600,500,false,0xFFFFFF);
			var tm:Matrix = new Matrix();
			var colorNames:Array;
			tm.scale(.7, .7);
			tbd.draw(show.tartans, tm);	
			var spec:TartanSpec = new TartanSpec(show.currentTartan, new Bitmap(tbd));
			addChild(spec);
			var smd:BitmapData = new BitmapData(spec.width, spec.height);
			smd.draw(spec);
			removeChild(spec);
			colorNames = spec.colorNames; // gets list of colors from spec for blog tags
			spec = null;	
			var myEncoder:JPGEncoder = new JPGEncoder(80);
			var stream:ByteArray = myEncoder.encode(smd);			
			var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
			var jpgURLRequest:URLRequest;
			var urlLoader:URLLoader;
			var varsString:String = "id="+tid+"&type=kitty";
			
			jpgURLRequest = new URLRequest("http://www.danzen.com/tartan/spec.php?"+varsString);
			jpgURLRequest.requestHeaders.push(header);
			jpgURLRequest.method = URLRequestMethod.POST;
			jpgURLRequest.data = stream;			
			urlLoader = new URLLoader();
			urlLoader.addEventListener(Event.COMPLETE, imageComplete);
			urlLoader.load(jpgURLRequest);	
			
			// create tartan jpg, send to cloud and send e-mail
			var bmd:BitmapData;
			var matrix:Matrix;
			var newW:Number;
			var newH:Number;
			var scale:Number;
			newW = 770;
			newH = sH*770/sW;					
			scale = 770/sW;					
			bmd = new BitmapData(newW, newH);
			matrix = new Matrix();
			matrix.scale(scale, scale);
			buttons.visible = false;
			alert.visible = false;
			kt.picture.visible = true;
			bmd.draw(kt,matrix);
			kt.picture.visible = false;
			alert.visible = true;
											
			myEncoder = new JPGEncoder(80);
			stream = myEncoder.encode(bmd);			
			header = new URLRequestHeader("Content-type", "application/octet-stream");
			
			var vars:Array = ["name","city","country","description","thoughts"];
			var obj:Object = {};
			for (var i:uint = 0; i < vars.length; i++) {
				obj[vars[i]] = form[vars[i]+"Text"].text;
			}
			varsString = "";
			for (i = 0; i < vars.length; i++) {
				varsString += vars[i] + "="+escape(obj[vars[i]])+"&"
			}			
			varsString += "id="+tid+"&uid=12345&type=kitty&colors="+escape(colorNames.join(","));
						
			jpgURLRequest = new URLRequest("http://www.danzen.com/tartan/image.php?"+varsString);
			jpgURLRequest.requestHeaders.push(header);
			jpgURLRequest.method = URLRequestMethod.POST;
			jpgURLRequest.data = stream;			
			urlLoader = new URLLoader();
			urlLoader.addEventListener(Event.COMPLETE, imageComplete);
			urlLoader.load(jpgURLRequest);				
			function imageComplete(e:Event):void {
				trace (e.target.data);
			}
			alert.alertText.gotoAndStop(2);
			for (i = 0; i < vars.length; i++) {
				form[vars[i]+"Text"].text = "";
			}
			kt.myPurchases.data.uploads --;
			form.uploadsText.text = String(kt.myPurchases.data.uploads);
		}		
		
		////// helpers //////
		
		private function toggleShow():void {
			if (show.running) {
				show.pause();
				buttons.butPics.pauseButPic.gotoAndStop(2);
			} else {
				show.resume();
				buttons.butPics.pauseButPic.gotoAndStop(1);
			}	
		}
		
		private function rolls(c:Sprite):void {
			c.addEventListener(MouseEvent.MOUSE_OVER, function(e:MouseEvent):void {MovieClip(e.target).gotoAndStop(2);});
			c.addEventListener(MouseEvent.MOUSE_OUT, function(e:MouseEvent):void {MovieClip(e.target).gotoAndStop(1);});
			c.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {MovieClip(e.target).gotoAndStop(3);});
			c.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {MovieClip(e.target).gotoAndStop(2);});
		}		
		
		private function clickLogo(e:MouseEvent):void {
			buttons.visible = !buttons.visible;
		}
		
	}
}

package com.danzen.effects
{	
	
	import com.danzen.frameworks.Easy;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.GlowFilter;
		
	public class Tartan extends Sprite
	{		
		private var colors:Array;
		private var widths:Array;
		private var tW:Number;
		private var tH:Number;
		
		public function Tartan(theColors:Array, theWidths:Array, theWidth:Number, theHeight:Number)
		{				
			colors = theColors;
			widths = theWidths;
			tW = theWidth;
			tH = theHeight;
			
			var strip:Easy;
			var lastX:Number = 0;
			var lastY:Number = 0;
			var numColors:Number = colors.length;
			
			for (var i:uint = 0; i < numColors*30; i++) {
				strip = Easy.rectangle(2800, widths[i%numColors], colors[i%numColors]);
				addChild(strip);				
				strip.y = lastY;				
				strip.alpha = .5;
				lastY += widths[i%numColors];
				
				strip = Easy.rectangle(2800, widths[numColors-i%numColors-1], colors[numColors-i%numColors-1]);
				addChild(strip);				
				strip.y = lastY;				
				strip.alpha = .5;
				lastY += widths[numColors-i%numColors-1];
				
			}
			for (i = 0; i < numColors*30; i++) {
				strip = Easy.rectangle(widths[i%numColors], 1800, colors[i%numColors]);
				addChild(strip);
				strip.x = lastX;				
				strip.alpha = .5;
				lastX += widths[i%numColors];
				
				strip = Easy.rectangle(widths[numColors-i%numColors-1], 1800, colors[numColors-i%numColors-1]);
				addChild(strip);
				strip.x = lastX;				
				strip.alpha = .5;
				lastX += widths[numColors-i%numColors-1];			
				
			}	
			var blindR:Easy = Easy.rectangle(lastX-tW, tH, 0x444444);
			blindR.x = tW;
			addChild(blindR);
			var blindB:Easy = Easy.rectangle(lastX-tH, tW, 0x444444);
			blindB.y = tH;
			addChild(blindB);
			var blindL:Easy = Easy.rectangle(lastX-tW, tH, 0x444444);
			blindL.x = -blindL.width;
			addChild(blindL);
			var blindT:Easy = Easy.rectangle(lastX-tH, tW, 0x444444);
			blindT.y = -blindT.height;
			addChild(blindT);
			
			trace (colors)
			trace (widths);
		}	
	}
}

package com.danzen.effects
{	
	import com.danzen.frameworks.Easy;
	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	
	public class TartanShow extends Sprite
	{
		private var timer:Timer;
		private var keepPercent:Number = 70; // how often we overlay tartans in percent		
		private var colors:Array;
		private var speed:Number;
		private var numTartans:Number = 100;
		private var tartanIndex:Number = 0; 		
		
		// tartans hold all the tartan patterns that make up one tartan
		public var tartans:Sprite;
		// tartans always have same color order but varying widths and overlay of widths
		public var tartanList:Array = []; // holds tartan widths including overlays
		// [ [[widths]], [[widths],[widths]], [[widths],[widths],[widths]], [[widths]] ]		
		public var currentTartan:Array = [];
		// [ [colors],[[widths],[widths]] ]
		public var running:Boolean = true;
		
		public const MIN_MIN_WIDTH:Number = 3; // smallest the small width gets
		public const MAX_MIN_WIDTH:Number = 13; // largest the small width gets
		public const MIN_MAX_WIDTH:Number = 50; // smallest the large width gets
		public const MAX_MAX_WIDTH:Number = 200; // largest the large width gets
		
		private var sW:Number;
		private var sH:Number;
		
		public function TartanShow(theColors:Array, theWidth:Number, theHeight:Number, theSpeed:uint=2)
		{
			
			colors = theColors;			
			sW = theWidth;
			sH = theHeight;
			speed = theSpeed;
			
			makeTartanData();			
			
			timer = new Timer(speed*1000);			
			timer.addEventListener(TimerEvent.TIMER, timerGo);		
			timer.start();			
			
			// holder for tartans
			tartans = new Sprite();
			addChild(tartans);
			
			goTartan(0); 
			
		}
		
		private function makeTartanData():void {
			// loop through numTartans make random data for tartan
			// tartans can blend over top of one another and then clear
			var numColors:Number = colors.length;
			var curTart:Array = [];
			var curWidths:Array;
			var count:uint = 0;				
			tartanList = [];
			
			for (var i:uint = 0; i < numTartans; i++) {								
				curWidths = [];
				for (var j:uint = 0; j < numColors; j++) {
					if (Easy.random(2) > 1) {
						curWidths.push(Easy.randomRound(MIN_MIN_WIDTH,MAX_MIN_WIDTH));
					} else {
						curWidths.push(Easy.randomRound(MIN_MAX_WIDTH,MAX_MAX_WIDTH));
					}
				}				
				curTart.push(curWidths.slice());
				count++;	
				tartanList.push(curTart.slice());
				if (Easy.random(100) > keepPercent || count >= 4) {					
					curTart = [];
					count = 0;
				}				
			}			
		}		
		public function next():void {
			tartanIndex++;
			if (tartanIndex > numTartans - 1) {
				tartanIndex--; // end rather than loop
				return;
				//tartanIndex = 0;
			}
			goTartan(tartanIndex);			
		}
		public function prev():void {
			tartanIndex--;
			if (tartanIndex < 0) {
				tartanIndex++; // end rather than loop
				return;
				//tartanIndex = numTartans - 1;
			}
			goTartan(tartanIndex);
		}
		private function goTartan(n:Number):void {		
			if (tartans) {
				removeChild(tartans);
				tartans = null; // no listeners in Tartan class
				tartans = new Sprite();
				addChild(tartans);
			}
			var widths:Array = tartanList[n];
			currentTartan = [colors, widths];
			for (var i:uint=0; i<widths.length; i++) {
				tartans.addChild(new Tartan(colors, widths[i], sW, sH));
			}
		}
		private function timerGo(e:TimerEvent):void {
			next();
		}		
		
		public function pause():void {
			running = false;
			timer.stop();
		}
		public function resume():void {
			running = true;
			timer.reset();
			timer.start();
		}
		public function dispose():void {
			timer.addEventListener(TimerEvent.TIMER, timerGo);		
		}
		
		
	}
}

package com.danzen.effects
{
	import com.danzen.frameworks.Easy;
	import com.danzen.utilities.Colors;
	
	import flash.display.Bitmap;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.geom.ColorTransform;
	
	public class TartanSpec extends Sprite
	{
		public var colorNames:Array = [];
		
		public function TartanSpec(tartanData:Array, tartan:Bitmap)
		{
			var colors:Array = tartanData[0];
			var widths:Array = tartanData[1];
			
			addChild(Easy.rectangle(600, 500, 0xffffff));
			addChild(tartan);
			tartan.alpha = .2;
			
			var spec:Spec = new Spec();
			addChild(spec);
			
			var ct:ColorTransform = new ColorTransform();	
			var counter:Number = 0;
			var row:MovieClip;
			var j:uint;
			var cn:String;
			for (var i:uint = 0; i < colors.length / 2; i++) {				
				ct.color = colors[i];
				row = spec["row"+counter];
				row.s.inner.transform.colorTransform = ct;
				row.s.inner.alpha = .8;	
				cn = Colors.getName(colors[i]);
				colorNames.push(cn);
				row.n.text = cn;
				row.c.text = Colors.getHexFromNumber(colors[i]);
				for (j=0; j<widths.length; j++) {
					row["t"+j].text = String(widths[widths.length-1-j][i]);
				}
				
				counter++;
				ct.color = colors[colors.length-1-i];	
				row = spec["row"+counter];
				row.s.inner.transform.colorTransform = ct;
				row.s.inner.alpha = .8;	
				cn = Colors.getName(colors[colors.length-1-i]);
				colorNames.push(cn);
				row.n.text = cn;
				row.c.text = Colors.getHexFromNumber(colors[colors.length-1-i]);
				for (j=0; j<widths.length; j++) {
					row["t"+j].text = String(widths[widths.length-1-j][colors.length-1-i]);
				}
				counter++;
			}
			
		}
	}
}

package com.danzen.frameworks.zenconfirm
{
	import flash.display.Sprite;
	import flash.events.*;
	import flash.net.LocalConnection;
	
	// copyright 2012 Dan Zen - free to use
	
	// Sets up a confirmation system when purchasing a desktop app through paypal for instance
	// Works in leu of a unique paypal id - because paypal sandbox was not working at the time to test
	// also avoids a username and password situation
	
	// SYSTEM 
	// put a download link and a confirmation button swf file on the returning page from PAYPAL
	// once the user downloads, installs and runs the AIR app the user is to press the confirm button
	// the confirm button uses ConfirmSend with a LocalConnection to confirm and send an optional object of variables
	// on the AIR side, the ConfirmReceive object receives the message and gives a COMPLETE event
	// at which point the status, statusText and data properties are available
	// from that point on store a shared object with the AIR app to keep track of registration
	
	public class ConfirmSend extends Sprite
	{
		public var status:String;
		public var statusText:String;
		private var conn:LocalConnection;
		public static const COMPLETE:String = "complete";
		public static const GOOD:String = "good";
		public static const BAD:String = "bad";
		
		private var site:String;
		private var connectName:String;
		
		// theConnectName must be no spaces, unique for app, and the same for ConfirmSend and ConfirmReceive
		// theSite is the AIR app ID for ConfirmSend i.e. app#com.danzen.kittytartan
		// theSite is the site name where the confirm button is for ConfirmReceive i.e. tartankitty.com 
			
		public function ConfirmSend(theConnectName:String, theSite:String="*")
		{
			trace ("hi from ConfirmSend");
			connectName = theConnectName;
			site = theSite;			
			conn = new LocalConnection();
			conn.addEventListener(StatusEvent.STATUS, onStatus);			
			conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionError);			
		}
		public function send(obj:Object):void {
			conn.send(site+":"+connectName, "confirmMe", obj);
		}
		private function connectionError(event:SecurityErrorEvent):void {
			status = ConfirmSend.BAD;
			statusText = "error with confirming domain";
			dispatchEvent(new Event(ConfirmSend.COMPLETE));
		}
		private function onStatus(e:StatusEvent):void {
			switch (e.level) {
				case "status":
					status = ConfirmSend.GOOD;
					statusText = "success making connection - not necessarily accepted";
					dispatchEvent(new Event(ConfirmSend.COMPLETE));					
					break;
				case "error":
					status = ConfirmSend.BAD;
					statusText = "error with making connection";
					dispatchEvent(new Event(ConfirmSend.COMPLETE));
					break;
			}
		}
	}
}

package com.danzen.frameworks.zenconfirm
{
	import com.danzen.utilities.Falcon;
	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.net.LocalConnection;
	
	// copyright 2012 Dan Zen - free to use
	
	// Sets up a confirmation system when purchasing a desktop app through paypal for instance
	// Works in leu of a unique paypal id - because paypal sandbox was not working at the time to test
	// also avoids a username and password situation
	
	// SYSTEM 
	// put a download link and a confirmation button swf file on the returning page from PAYPAL
	// once the user downloads, installs and runs the AIR app the user is to press the confirm button
	// the confirm button uses ConfirmSend with a LocalConnection to confirm and send an optional object of variables
	// on the AIR side, the ConfirmReceive object receives the message and gives a COMPLETE event
	// at which point the status, statusText and data properties are available
	// from that point on store a shared object with the AIR app to keep track of registration
		
	public class ConfirmReceive extends Sprite
	{
		public var data:Object;
		public var status:String;
		public var statusText:String;
		private var conn:LocalConnection;
		private var site:String;
		private var connectName:String;
		public static const COMPLETE:String = "complete";
		public static const GOOD:String = "good";
		public static const BAD:String = "bad";
		
		// theConnectName must be no spaces, unique for app, and the same for ConfirmSend and ConfirmReceive
		// theSite is the AIR app ID for ConfirmSend i.e. app#com.danzen.kittytartan
		// theSite is the site name where the confirm button is for ConfirmReceive i.e. tartankitty.com 
		
		public function ConfirmReceive(theConnectName:String, theSite:String="*")
		{
			trace ("hi from ConfirmReceive");
			site = theSite;
			connectName = theConnectName;
			conn = new LocalConnection();
			conn.allowDomain(site);
			conn.client = this;			
			try {
				conn.connect(connectName);
			} catch (error:ArgumentError) {
				status = ConfirmReceive.BAD;
				statusText = "error with connection name";
				dispatchEvent(new Event(ConfirmReceive.COMPLETE));
			}			
			conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionError);			
		}
		private function connectionError(event:SecurityErrorEvent):void {
			
			status = ConfirmReceive.BAD;
			statusText = "error with confirming domain";
			dispatchEvent(new Event(ConfirmReceive.COMPLETE));
			conn.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionError);
		}
		
		public function confirmMe(obj:Object=null):void {
			if (status != ConfirmReceive.BAD) {
				status = ConfirmReceive.GOOD;
				statusText = "Confirmation success";
				data = obj;
				dispatchEvent(new Event(ConfirmReceive.COMPLETE));
			}
		}		
		public function usePasscode(url:String, pass:String, type:String):void {
			// usePasscode will check passcode with serverscript and if pass matches,
			// it will create a pid and dispatch as if confirmed from purchase
			var myFalcon:Falcon = new Falcon(url, Falcon.VARIABLES, {pass:pass, type:type});
			myFalcon.addEventListener(Event.COMPLETE, doConfirm);
			function doConfirm(e:Event):void {
				if (e.target.data.error == 0) {
					data = { pid:pass }; // records pid as pass to avoid using pass multiple times
					dispatchEvent(new Event(ConfirmReceive.COMPLETE));
				}
			}
		}
	
	}
}

package com.danzen.frameworks.zenconfirm
{
	// Purchases is a wrapper for shared objects to store purchase ids and goods associated with the purchase
	// Global scope is so that one purchase id is unique for all products to stop parallel manipulation
	// Local scope is where the goods can be tracked
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.SharedObject;
	
	public class ConfirmPurchases extends Sprite
	{
		public var data:Object = {};
		private var globalSO:SharedObject;
		private var localSO:SharedObject;
		
		public function ConfirmPurchases(theGlobalScope:String, theLocalScope:String)
		{
			trace ("hi from ConfirmPurchases");
			globalSO = SharedObject.getLocal(theGlobalScope, "/");	
			if (!globalSO.data.ids) {
				globalSO.data.ids = [];
			}
			localSO = SharedObject.getLocal(theLocalScope, "/");	
			data = localSO.data;
		}
		public function addID(id:Object):Boolean {
			//check global shared object to see if id is already in pidList
			// return false if it is and true if it is not - and add pid
			trace ("id="+id);
			if (globalSO.data.ids.indexOf(id) == -1) {
				globalSO.data.ids.push(id);
				return true;
			} else {
				return false;
			}
		}
		public function clear():void {
			for (var i:Object in globalSO.data) {
				globalSO.data[i] = null;
			}
			globalSO.data.ids = [];
			for (var j:Object in localSO.data) {
				localSO.data[j] = null;
			}			
		}
	}
}

package com.danzen.frameworks.zenconfirm
{
	// ConfirmID is a wrapper for a shared object to store and retrieve the App user id
	
	import com.danzen.utilities.Falcon;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.SharedObject;
	
	public class ConfirmID extends Sprite
	{
		private var localSO:SharedObject;
		public var data:Object;
		public var error:String;
		
		public function ConfirmID(theLocalScope:String)
		{
			trace ("hi from ConfirmID");
			localSO = SharedObject.getLocal(theLocalScope, "/");					
		}
		public function get id():Object {
			return localSO.data.id;
		}
		public function set id(theID:Object):void {
			localSO.data.id = theID;			
		}
		public function randomID(n:Number=10000000000):Number {
			return Math.round(Math.random()*n);
		}
		public function record(url:String, obj:Object):void {
			// if you want you can send some data to a server script - like recording the id
			var myFalcon:Falcon = new Falcon(url, Falcon.VARIABLES, obj);
			myFalcon.addEventListener(Event.COMPLETE, recorded);
		}
		private function recorded(e:Event):void {
			// any variables coming back from the server script is stored in data as properties
			error = e.target.error;
			data = e.target.data;
			dispatchEvent(new Event(Event.COMPLETE));
		}
		public function clear():void {			
				localSO.data.id = null;			
		}
	}
}


package com.danzen.interfaces
{
	import flash.desktop.Clipboard;
	import flash.desktop.ClipboardFormats;
	import flash.desktop.ClipboardTransferMode;
	import flash.desktop.NativeApplication;
	import flash.desktop.NativeDragManager;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.filesystem.File;
	import flash.net.URLRequest;		
	
	public class AIRPicDrop extends Sprite
	{
		private var myTargetClip:Sprite;		
		private var myLoader:Loader;
		private var myExtensions:Array;		
		private var myFile:String;		
		private var myFileList:Array;
		private var myFileNum:Number;		
		private var myTempFile:String;		
		private var myTempFileList:Array;
		private var myTempFileNum:Number;
		
		public var fileList:Array;
		public var numFiles:Number;
		public var pic:Sprite;
		
		public static const PIC_READY:String = "picReady";
		
		public function AIRPicDrop(theTargetClip:Sprite, thePicExtensions:Array=null)
		{
			trace ("hi from AIRPicDrop");			
		
			myTargetClip = theTargetClip;
			myExtensions = (thePicExtensions) ? thePicExtensions : ["jpg", "jpeg", "gif", "png"];
		
			pic = new Sprite(); // holds the current picture
			
			myLoader = new Loader();
			myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, setPic);	
					
			// see if they have dropped a file on the application icon 
			NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, doInvoke);		   
			
			// check to see stuff dragged onto the ball
			myTargetClip.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, dragEnter);
			myTargetClip.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, dragDrop);
			myTargetClip.addEventListener(NativeDragEvent.NATIVE_DRAG_EXIT, dragExit);
		}
		
		private function doInvoke(e:InvokeEvent):void {
			if (e.arguments.length > 0) {
				var myArray:Array = [];
				// turn the array of strings into File objects 
				for(var i:uint=0; i<e.arguments.length; i++) {
					myArray.push(new File(e.arguments[i]));
				}
				if (getFiles(myArray)) {
					loadMyFile();
				}
			}							
		}
		
		private function dragEnter(e:NativeDragEvent):void {			
			// casting as Array(myData...) does not work because Array() is a global function
			var myArray:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
			if (getFiles(myArray)) {
				// need to call this to accept the drop					
				NativeDragManager.acceptDragDrop(myTargetClip);
			}
		}		
		
		private function dragDrop(e:NativeDragEvent):void {						
			loadMyFile();			
		}		
		
		private function dragExit(e:NativeDragEvent):void {
			trace ("drag exit");
		}
		
		// -------  methods to get the file list (AIR)
		
		// prepares a temporary list of files from an array of filenames
		// if the array has one file, the files in the directory are collected
		// this temporary list will get applied when the file (or files) is dropped
		
		private function getFiles(myArray:Array):Boolean {
			myTempFileList = [];		
			var i:uint;
			for(i=0; i<myArray.length; i++) {
				var file:File = myArray[i];
				if (file.isDirectory) {continue;}
				if (myExtensions.indexOf(file.extension.toLowerCase()) != -1) {
					myTempFileList.push(file);					
				}								
			}			
			if (myTempFileList.length > 0) {
				myTempFile = myTempFileList[0].url
				myTempFileNum = 0;				
				if (myTempFileList.length == 1) {
					// get other files in directory
					myTempFileList = getDirectoryFiles(myTempFileList[0]);
				}						
				return true;
			} else {
				return false;
			}
		}
		
		private function getDirectoryFiles(f:File):Array {
			var directoryList:Array = f.parent.getDirectoryListing();		
			var addFiles:Array = [];
			var count:Number = 0;
			for (var i:uint=0; i<directoryList.length; i++) {
				var file:File = directoryList[i];				
				if (file.isDirectory) {continue;}
				if (myExtensions.indexOf(file.extension.toLowerCase()) != -1) {
					if (f.url == file.url) {
						myTempFileNum = count;
					}
					addFiles.push(file);	
					count++;
				}				
			}
			return addFiles;
		}		
		
		// -------  loader method and prev next public methods 
		
		private function loadMyFile():void {
			myFileList = myTempFileList;
			myFile = myTempFile;					
			myFileNum = myTempFileNum;
			myLoader.load(new URLRequest(myFile));
		}
		
		public function prev():void {			
			var newFile:Number = myFileNum - 1;
			if (newFile < 0) {
				newFile = myFileList.length-1;
			}
			myFile = myFileList[newFile].url;
			myFileNum = newFile;
			myLoader.load(new URLRequest(myFile));
			
		}
		public function next():void {
			var newFile:Number = myFileNum + 1;
			if (newFile > myFileList.length-1) {
				newFile = 0;
			}
			myFile = myFileList[newFile].url;
			myFileNum = newFile;
			myLoader.load(new URLRequest(myFile));			
		}						
		
		// -------  loader event
		
		private function setPic(e:Event):void {			
			pic.addChild(myLoader);
			dispatchEvent(new Event(AIRPicDrop.PIC_READY));
		}
		
	}
}

package com.danzen.interfaces
{		
	import com.danzen.frameworks.Easy;
	
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.geom.Matrix;
	import flash.utils.Timer;
	
	import uk.co.soulwire.display.colour.ColourUtils;

	// Picture extends a Sprite and loads an image into itself and gets a palatte (colors property)
	// receives URL:String, paletteSize:Number
		
	public class Picture extends Sprite
	{
		public var url:String;
		public var colors:Array;
		public var paletteSize:Number;
		private var pic:Sprite;	
		
		public static const PIC_READY:String = "picReady";
		
		public function Picture(thePicture:Object, thePaletteSize:Number=16)
		{
			trace("hi from Picture");
			
			if (thePicture is String) { 
				url = String(thePicture);
				pic = Easy.picture(url);
				pic.addEventListener(Event.COMPLETE, done);
			} else {
				pic = Sprite(thePicture);
				picReady();
			}
			paletteSize = thePaletteSize;			
		}
		private function done(e:Event):void {
			picReady();
		}
		private function picReady():void {			
			addChild(pic);
			// url from web is okay without timer but it is synchronous for desktop
			// so need to delay to let the listener be registered
			// also, Bitmap draw needed a touch of a delay too.
			var myTimer:Timer = new Timer(300,1);
			myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, doDelay);
			myTimer.start();
		}
		private function doDelay(e:TimerEvent):void {
			var bitData:BitmapData = new BitmapData(100, 100);
			var matrix:Matrix = new Matrix();
			// make picture smaller to sample more quickly
			matrix.scale(100/pic.width, 100/pic.height);
			bitData.draw(pic,matrix);				
			colors = ColourUtils.colourPalette(bitData, paletteSize, .02);
			pic.removeEventListener(Event.COMPLETE, done);
			dispatchEvent(new Event(Picture.PIC_READY));			
		}
		
	}
}