Dead Code Preservation :: Archived AS3 works from wonderfl.net

MediaRSS City

/**
 * Copyright tencho ( http://wonderfl.net/user/tencho )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/suNP2
 */

// forked from Event's Simple MediaRSS Viewer
/**
 * flickrで検索した画像が広告になります。
 * 広告をクリックするとちょっと大きい画像が見れます。
 */
package  {
	import com.bit101.components.*;
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.system.*;
	
	public class AdCity extends Sprite {
		private var _flickr:Flickr = new Flickr();
		private var _map:MapView = new MapView();
		private var _bg:Sprite;
		private var _fade:Sprite;
		private var _sky:Sprite;
		private var _mouseOverSprite:Sprite = new Sprite();
		private var _canvas:BitmapData = new BitmapData(Display.WIDTH, Display.HEIGHT, true, 0);
		private var _loader:ImageLoader = new ImageLoader();
		private var _viewer:Viewer = new Viewer();
		private var _loading:LoadingEffect;
		private var _drag:MouseDrag;
		private var _search:SearchDialog = new SearchDialog();
		private var _menu:TopMenu;
		private var _mousePos:Point = new Point();
		private var _cameraMng:CameraManager = new CameraManager();
		
		private const SCENE_SEARCH:int = 0;
		private const SCENE_NORMAL:int = 1;
		private const SCENE_ZOOM:int = 2;
		private var _scene:int = SCENE_SEARCH;
		
		public function AdCity() {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			Display.stage = stage;
			stage.frameRate = 30;
			stage.quality = StageQuality.LOW;
			Style.fontSize *= 2;
			Style.LABEL_TEXT = 0xFFFFFF;
			//画面構成
			_bg = Painter.createColorRect(2000, 2000, 0);
			_bg.x = (Display.WIDTH - _bg.width) / 2;
			_bg.y = (Display.HEIGHT - _bg.height) / 2;
			_sky = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0x78B2F8);
			_sky.mouseEnabled = false;
			_loading = new LoadingEffect();
			_loading.x = (Display.WIDTH - _loading.width) / 2;
			_loading.y = (Display.HEIGHT - _loading.height) / 2;
			_menu = new TopMenu();
			_menu.hide();
			_fade = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0);
			_fade.mouseEnabled = false;
			
			addChild(_bg);
			addChild(_sky);
			addChild(new Bitmap(_canvas));
			addChild(_viewer);
			addChild(_mouseOverSprite);
			addChild(_search);
			addChild(_menu);
			addChild(_fade);
			addChild(_loading);
			
			_menu.search.addEventListener(LabelButton.EVENT_CLICK, onClickSeach);
			_menu.camera.addEventListener(Event.CHANGE, onChangeCamera);
			_search.hide();
			//hitTestを有効にするためstageに配置しておく
			addChild(_map.container);
			_map.container.visible = false;
			//指マークに変えるためのボタン
			_mouseOverSprite.addChild(Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0, 0));
			_mouseOverSprite.buttonMode = true;
			_mouseOverSprite.mouseChildren = false;
			_mouseOverSprite.visible = false;
			
			_drag = new MouseDrag(_bg, 400, 400);
			_drag.speed.x = -0.5;
			_drag.speed.y = -0.5;
			
			//マップチップとかを読み込む
			_loader.addImage("http://assets.wonderfl.net/images/related_images/2/2f/2fca/2fca9c6a48ea9c729059dc8f7d4717283933b92d", "mapchip");
			_loader.addImage("http://assets.wonderfl.net/images/related_images/7/78/781f/781f056d44583c10d7b214cf2891e7258ad20520", "bldg");
			_loader.addImage("http://assets.wonderfl.net/images/related_images/9/91/9180/918077e79672d25963f77a8ff3f37a700574f2d4", "car");
			_loader.addImage("http://assets.wonderfl.net/images/related_images/6/67/67c9/67c9fe2e421ee0f5776ca969a38a0d3a9e3fdc4f", "ui");
			_loader.load(onLoadMaterial, onErrorMaterial, onProgressMaterial);
		}
		
		private function onProgressMaterial(per:Number):void {
			_loading.setProgress(per);
		}
		//メニューのSEARCHボタンを押した
		private function onClickSeach(e:Event):void {
			_scene = SCENE_SEARCH;
			_search.setSearch();
			setCamera(CameraManager.MODE_CAR);
			_menu.hide();
		}
		//カメラボタンで切り替えた
		private function onChangeCamera(e:Event):void {
			nextCamera();
		}
		//マップチップロード失敗
		private function onErrorMaterial():void {
			_loading.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0xFF, 0, 0, 0);
		}
		//全ての素材を読み込めた
		private function onLoadMaterial():void {
			EaseMotion.ease(_fade, "alpha", 0, 0.15, function():void { _fade.parent.removeChild(_fade); } );
			_loading.hide();
			_loading.parent.removeChild(_loading);
			_viewer.init(_loader.image.ui);
			_map.init(_loader);
			initCamera();
			onSimulateTick(null);
			_search.init("poster logo", onSearch);
			_flickr.loadLimit = 16;
			_flickr.isSkipFlash = true;
			_flickr.addEventListener(Flickr.EVENT_LOADIMAGE, onLoadImage);
			_flickr.addEventListener(Flickr.EVENT_NOIMAGE, onNoImage);
			_flickr.addEventListener(ErrorEvent.ERROR, onErrorRss);
			_flickr.addEventListener(Event.COMPLETE, onCompleteImage);
		}
		//検索実行時
		private function onSearch(tag:String):void{
			//flickrのRSSを取得
			_search.setMessage("LOADING... ");
			_flickr.load(tag);
		}
		private function onNoImage(e:Event):void {
			_search.errorMessage("NO IMAGE!");
		}
		private function onErrorRss(e:ErrorEvent):void {
			_search.errorMessage("ERROR!");
		}
		private function onLoadImage(e:Event):void {
			var per:Number = _flickr.loaded.length / _flickr.items.length * 100;
			_search.setMessage("LOADING... " + int(per) + "%");
		}
		//全てのサムネイルが読み込まれた
		private function onCompleteImage(e:Event):void {
			_scene = SCENE_NORMAL;
			_search.hide();
			_bg.addEventListener(MouseEvent.MOUSE_DOWN, onClickStage);
			_mouseOverSprite.addEventListener(MouseEvent.MOUSE_DOWN, onClickStage);
			_menu.show();
			var count:int = -1;
			if (_flickr.items.length > 0) {
				for each(var obj:Piece in _map.piece.objects) {
					for each(var ad:Ad in obj.ads) {
						count = (count + 1) % _flickr.items.length;
						ad.setItem(_flickr.items[count]);
					}
				}
			}
			startSim();
		}
		//背景クリック時
		private function onClickStage(e:MouseEvent):void {
			switch(_scene) {
				case SCENE_ZOOM:
					startSim();
					_viewer.close();
					_menu.show();
					_scene = SCENE_NORMAL;
					break;
				case SCENE_NORMAL:
					var ad:Ad = _map.piece.hitTest(_mousePos.x, _mousePos.y);
					if (ad) {
						stopSim();
						_scene = SCENE_ZOOM;
						_menu.hide();
						_mouseOverSprite.visible = false;
						_viewer.setItem(ad.item);
					}
					break;
			}
		}
		//カメラ初期化
		private function initCamera():void {
			var cam_car:CameraData = new CameraData();
			var cam_drag:CameraData = new CameraData();
			var cam_sky:CameraData = new CameraData();
			cam_car.scale = 3;
			cam_car.speed = 1;
			cam_car.update = function(me:CameraData):void {
				me.position.x = _map.myCar.x;
				me.position.y = _map.myCar.y  - 30;
			}
			cam_drag.scale = 2;
			cam_drag.speed = 0.7;
			cam_drag.update = function(me:CameraData):void {
				me.position.x = _drag.position.x;
				me.position.y = _drag.position.y;
			}
			cam_sky.scale = 0.5;
			cam_sky.speed = 1;
			var pp:Point = Quarter.toScreen(_map.data.W / 2, _map.data.H / 2, 10);
			cam_sky.position.x = pp.x;
			cam_sky.position.y = pp.y;
			
			_cameraMng.cameras[CameraManager.MODE_CAR] = cam_car;
			_cameraMng.cameras[CameraManager.MODE_DRAG] = cam_drag;
			_cameraMng.cameras[CameraManager.MODE_SKY] = cam_sky;
			setCamera(CameraManager.MODE_CAR);
			_cameraMng.goto(_cameraMng.activeCamera);
		}
		//カメラ切り替え
		private function nextCamera():void {
			setCamera(_cameraMng.getNextMode());
		}
		private function setCamera(mode:int):void {
			var prev:CameraData = _cameraMng.activeCamera;
			_cameraMng.setMode(mode);
			_menu.camera.setLabel(mode);
			if (_cameraMng.mode == CameraManager.MODE_DRAG) {
				_drag.position = prev.position.clone();
			}
		}
		public function startSim():void {
			addEventListener(Event.ENTER_FRAME, onSimulateTick);
		}
		public function stopSim():void {
			removeEventListener(Event.ENTER_FRAME, onSimulateTick);
		}
		private function onSimulateTick(e:Event):void {
			_map.piece.objects.sort(sortFunc);
			var l:int = _map.piece.objects.length;
			for (var i:int = 0; i < l; i++) {
				_map.objLayer.setChildIndex(_map.piece.objects[i], i);
			}
			_map.myCar.simulateTick();
			var ad:Ad = _map.piece.hitTest(_mousePos.x, _mousePos.y)
			_mouseOverSprite.visible = !!ad;
			for each(var c:Car in _map.cars) c.simulateTick();
			_cameraMng.ease(_cameraMng.activeCamera);
			var cp:Point = _cameraMng.getPosition();
			var scl:Number = _cameraMng.scale;
			_mousePos = new Point((cp.x + stage.mouseX) / scl, (cp.y + stage.mouseY) / scl);
			_canvas.fillRect(_canvas.rect, 0);
			_canvas.draw(_map.container, new Matrix(scl, 0, 0, scl, int( -cp.x), int( -cp.y)));
		}
		private function sortFunc(a:Piece, b:Piece):int{
			return (a.y + a.basePos.y) - (b.y + b.basePos.y);
		}
	}
}

import com.bit101.components.*;
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.ui.*;
import flash.utils.*;

/**
 * ローディング用ノイズバー
 */
class LoadingEffect extends Sprite {
	private var _img:BitmapData;
	private var _slide:int = 0;
	private var _zoomX:int = 8;
	private var _zoomY:int = 3;
	private var _loop:int = 100;
	private var _count:int = 0;
	private var _label:Label;
	public function LoadingEffect() {
		_img = new BitmapData(20, _loop, false, 0xFF00000000);
		_img.noise(1234, 0, 255, 7);
		_img.applyFilter(_img, _img.rect, new Point(), new BlurFilter(0, 2, 2));
		_label = new Label(this, 0, -15, "");
		_label.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0xA0, 0xA0, 0xA0, 0);
		_label.scaleX = _label.scaleY = 0.5;
		setProgress(0);
		show();
	}
	private function onEnterFrame(e:Event):void {
		if (++_count % 2) update();
	}
	public function setProgress(per:Number):void {
		_label.text = int(per * 100) + "%";
	}
	private function update():void {
		_slide = ++_slide % _loop;
		graphics.clear();
		graphics.beginBitmapFill(_img, new Matrix(_zoomX, 0, 0, _zoomY, 0, _slide * _zoomY), true, false);
		graphics.drawRect(0, 0, _img.width * _zoomX, _zoomY);
	}
	public function show():void {
		update();
		visible = true;
		addEventListener(Event.ENTER_FRAME, onEnterFrame);
	}
	public function hide():void {
		visible = false;
		removeEventListener(Event.ENTER_FRAME, onEnterFrame);
	}
}

/**
 * カメラの動き管理
 */
class CameraManager {
	public var activeCamera:CameraData;
	public var cameras:Vector.<CameraData> = new Vector.<CameraData>();
	public var position:Point = new Point();
	public var scale:Number = 1;
	public var mode:int = 0;
	private var _speed:Number = 0;
	private var _targetPos:Point = new Point();
	private var _targetScale:Number = 1;
	private var _targetSpeed:Number = 0.2;
	static public const MODE_CAR:int = 0;
	static public const MODE_DRAG:int = 1;
	static public const MODE_SKY:int = 2;
	public function CameraManager() {
	}
	public function getNextMode():int {
		return (mode + 1) % cameras.length;
	}
	public function setMode(mode:int):void {
		this.mode = mode;
		resetTrackingSpeed();
		activeCamera = cameras[mode];
	}
	public function resetTrackingSpeed():void {
		_speed = 0;
	}
	public function goto(cam:CameraData):void {
		ease(cam);
		position = _targetPos.clone();
		scale = _targetScale;
		_speed = _targetSpeed;
	}
	public function ease(cam:CameraData):void {
		cam.update(cam);
		_targetScale = cam.scale;
		_targetPos = cam.position.clone();
		_targetSpeed = cam.speed;
		update();
	}
	public function getPosition():Point {
		var p:Point = new Point();
		p.x = position.x * scale - Display.CENTER.x;
		p.y = position.y * scale - Display.CENTER.y;
		return p;
	}
	public function update():void {
		_speed += (_targetSpeed - _speed) * 0.03;
		position.x += (_targetPos.x - position.x) * _speed;
		position.y += (_targetPos.y - position.y) * _speed;
		scale += (_targetScale - scale) * _speed;
		if (Math.abs(_targetScale - scale) < 0.01) scale = _targetScale;
	}
}

/**
 * カメラデータ
 */
class CameraData {
	//画面位置
	public var position:Point = new Point();
	//画面スケール
	public var scale:Number = 1;
	//最終的な追尾速度
	public var speed:Number = 0.2;
	//毎フレーム実行する処理
	public var update:Function = function(me:CameraData):void { };
	public function CameraData() {
	}
}

/**
 * 上部メニュー
 */
class TopMenu extends Sprite {
	private var _base:Sprite;
	public var search:LabelButton;
	public var camera:CameraSwitch;
	private var _height:Number = 35;
	public function TopMenu() {
		_base = addChild(Painter.createColorRect(Display.WIDTH, _height, 0, 0.7)) as Sprite;
		search = new LabelButton(this, 8, 0, "SEARCH", 100);
		camera = new CameraSwitch(this, Display.WIDTH - 145, 0);
		vanish();
	}
	public function show():void {
		mouseEnabled = true;
		mouseChildren = true;
		EaseMotion.ease(this, "y", 0, 0.4);
		EaseMotion.ease(search, "alpha", 1, 0.2);
		EaseMotion.ease(camera, "alpha", 1, 0.2);
	}
	public function vanish():void {
		mouseEnabled = false;
		y = -_height;
		search.alpha = 0;
		camera.alpha = 0;
	}
	public function hide():void {
		mouseEnabled = false;
		mouseChildren = false;
		EaseMotion.ease(this, "y", -_height, 0.4);
		EaseMotion.ease(search, "alpha", 0, 0.2);
		EaseMotion.ease(camera, "alpha", 0, 0.2);
	}
}

/**
 * 検索窓
 */
class SearchDialog extends Sprite {
	private var _base:Sprite = new Sprite();
	private var _message:Label;
	private var _input:InputText;
	private var _searchLayer:Sprite = new Sprite();
	private var _searchFunc:Function;
	private var _timer:Timer = new Timer(2000, 1);
	public function SearchDialog() {
		_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimesUp);
		addChild(_base);
		var imgBase:BitmapData = new BitmapData(170, 34, true, 0);
		var imgTri:BitmapData = new BitmapData(11, 11, true, 0);
		var i:int, px:int, py:int;
		var round:int = 3;
		for (i = 0; i < imgTri.width; i += 2) {
			imgTri.fillRect(new Rectangle(i, 0, 2, imgTri.height-i), 0xFFffffff);
		}
		for (i = 0; i < round; i++) {
			px = i * 2;
			py = (round - i -1) * 2;
			imgBase.fillRect(new Rectangle(px, py, imgBase.width - px * 2, imgBase.height - py * 2), 0xFFffffff);
		}
		var tri:Bitmap = _base.addChild(new Bitmap(imgTri)) as Bitmap;
		var base:Bitmap = _base.addChild(new Bitmap(imgBase)) as Bitmap;
		tri.y = -tri.height;
		base.y = -imgTri.height + 1 - imgBase.height;
		base.x = int( -imgBase.width / 2);
		addChild(_searchLayer);
		_input = new InputText(_searchLayer, -78, -35, "");
		new PushButton(_searchLayer, 28, -35, "SEARCH", onClickSearch).setSize(50, 16);
		_message = new Label(this, -34, -37, "");
		_message.visible = false;
		filters = [new DropShadowFilter(2, 45, 0, 0.5, 6, 6, 100, 1)];
		x = 230;
		y = 270;
	}
	public function init(text:String, onSearch:Function):void {
		_searchFunc = onSearch;
		setSearch();
		_input.text = text;
	}
	public function getKeyword():String {
		return _input.text.replace(/\s+|\/|\./g, ",");
	}
	public function setSearch():void {
		visible = true;
		EaseMotion.ease(this, "scaleX", 2, 0.6);
		EaseMotion.ease(this, "scaleY", 2, 0.6);
		_searchLayer.visible = true;
		_message.visible = false;
		stage.focus = _input.textField;
		stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
	}
	private function onKeyDown(e:KeyboardEvent):void {
		if (e.keyCode == Keyboard.ENTER) onClickSearch(null);
	}
	public function hide():void {
		visible = false;
		EaseMotion.goto(this, "scaleX", 0);
		EaseMotion.goto(this, "scaleY", 0);
	}
	public function errorMessage(text:String):void {
		setMessage(text);
		_timer.reset();
		_timer.start();
	}
	private function onTimesUp(e:TimerEvent):void {
		setSearch();
	}
	public function setMessage(text:String):void {
		_searchLayer.visible = false;
		_message.text = text;
		_message.visible = true;
	}
	private function onClickSearch(e:MouseEvent):void {
		stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
		if (_input.text.replace(/\s*/g, "") == "") return;
		if (_searchFunc != null)_searchFunc.apply(null, [getKeyword()]);
	}
}

/**
 * メニューのラベルボタン
 */
class LabelButton extends Label {
	static public const EVENT_CLICK:String = "onClickMe";
	private var _base:Sprite = new Sprite();
	private var _mainColor:ColorTransform = new ColorTransform();
	private var _overColor:ColorTransform = new ColorTransform();
	public function LabelButton(parent:DisplayObjectContainer, xpos:Number, ypos:Number, text:String = "", width:Number = 150) {
		super(parent, xpos, ypos, text);
		_mainColor.color = 0xFFFFFF;
		_overColor.color = 0xFFFF63;
		_base.addChild(Painter.createColorRect(width, 30, 0, 0));
		_base.addEventListener(MouseEvent.ROLL_OVER, onOver);
		_base.addEventListener(MouseEvent.ROLL_OUT, onOut);
		_base.addEventListener(MouseEvent.CLICK, onClick);
		_base.buttonMode = true;
		_base.x = -4;
		_base.y = 3;
		mouseEnabled = true;
		mouseChildren = true;
		addChildAt(_base, 0);
	}
	private function onOut(e:MouseEvent):void {
		textField.transform.colorTransform = _mainColor;
	}
	private function onOver(e:MouseEvent):void {
		textField.transform.colorTransform = _overColor;
	}
	protected function onClick(e:MouseEvent):void {
		dispatchEvent(new Event(EVENT_CLICK));
	}
}

/**
 * カメラ切り替えボタン
 */
class CameraSwitch extends LabelButton {
	public function CameraSwitch(parent:DisplayObjectContainer, xpos:Number, ypos:Number) {
		super(parent, xpos, ypos);
	}
	override protected function onClick(e:MouseEvent):void {
		dispatchEvent(new Event(Event.CHANGE));
		super.onClick(e);
	}
	public function setLabel(mode:int):void {
		text = "CAMERA: " + ["  CAR", "DRAG", "  SKY"][mode];
	}
}

/**
 * マウスドラッグ管理
 */
class MouseDrag extends EventDispatcher {
	public var position:Point = new Point();
	public var speed:Point = new Point(1, 1);
	public var clickRange:Number = 2;
	private var _sprite:DisplayObjectContainer;
	private var _isDragged:Boolean = false;
	private var _isMouseDown:Boolean = false;
	private var _savePosition:Point = new Point();
	private var _saveMousePos:Point;
	static public const EVENT_STARTDRAG:String = "onStartDrag";
	public function get isMouseDown():Boolean { return _isMouseDown; }
	public function get isDragged():Boolean { return _isDragged; }
	public function MouseDrag(target:DisplayObjectContainer, x:Number = 0, y:Number = 0) {
		position.x = x;
		position.y = y;
		_sprite = target;
		setEnabled(true);
	}
	public function setEnabled(enabled:Boolean):void {
		if (enabled) {
			_sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
		} else {
			_sprite.removeEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
		}
	}
	private function onMsDown(e:MouseEvent):void {
		_isMouseDown = true;
		_isDragged = false;
		_sprite.stage.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
		_sprite.stage.addEventListener(Event.MOUSE_LEAVE, onMsUp);
		_sprite.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
		_savePosition = position.clone();
		_saveMousePos = new Point(_sprite.mouseX, _sprite.mouseY);
		dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN));
	}
	private function onMsUp(...rest):void {
		_isMouseDown = false;
		_sprite.stage.removeEventListener(MouseEvent.MOUSE_UP, onMsUp);
		_sprite.stage.removeEventListener(Event.MOUSE_LEAVE, onMsUp);
		_sprite.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
		checkDrag();
		dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
	}
	private function onMsMove(e:MouseEvent):void {
		checkDrag();
	}
	private function checkDrag():void {
		var drag:Point = new Point(_sprite.mouseX, _sprite.mouseY).subtract(_saveMousePos);
		if (!_isDragged && drag.length > clickRange) {
			dispatchEvent(new Event(EVENT_STARTDRAG));
			_isDragged = true;
		}
		if (_isDragged) {
			position.x = _savePosition.x + drag.x * speed.x;
			position.y = _savePosition.y + drag.y * speed.y;
			dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE));
		}
	}
}

/**
 * 拡大画像ビューワ
 */
class Viewer extends Sprite {
	private var _viewRect:Rectangle = new Rectangle(27, 45, 411, 330);
	private var _bg:Sprite;
	private var _bmp:Bitmap;
	private var _slideLayer:Sprite = new Sprite();
	private var _imageContainer:Sprite = new Sprite();
	private var _loader:ImageLoader = new ImageLoader();
	private var _title:Label;
	private var _loading:LoadingEffect;
	private var _errorLabel:Label;
	public function Viewer() {
		mouseEnabled = false;
		mouseChildren = false;
	}
	/**
	 * フレーム画像を渡して初期化
	 */
	public function init(bmp:BitmapData):void {
		_bg = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0xFFFFFF);
		_bg.alpha = 0;
		_bg.visible = false;
		_bg.blendMode = BlendMode.ADD;
		addChild(_bg);
		addChild(_slideLayer);
		_loading = new LoadingEffect();
		_loading.x = (Display.WIDTH - _loading.width) / 2;
		_loading.y = 215;
		_slideLayer.addChild(new Bitmap(bmp));
		_slideLayer.addChild(_loading);
		_slideLayer.addChild(_imageContainer);
		_title = new Label(_slideLayer, 30, 420);
		_title.x = 30;
		_title.width = 200;
		_title.height = 50;
		_title.scrollRect = new Rectangle(0, 0, 400, 100);
		_errorLabel = new Label(_slideLayer, 130, 180, "");
		_errorLabel.visible = false;
		_imageContainer.mask = addChild(Painter.createColorRect(_viewRect.width, _viewRect.height, 0, 1, _viewRect.x, _viewRect.y));
		mask = addChild(Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0));
		_slideLayer.y = Display.HEIGHT;
	}
	public function dispose():void {
		if(_bmp) _bmp.bitmapData.dispose();
		while (_imageContainer.numChildren) _imageContainer.removeChildAt(0);
		_loader.stop();
		_loader.disposeAll();
	}
	public function setItem(item:MediaItem):void {
		dispose();
		EaseMotion.goto(_loading, "alpha", 0);
		_imageContainer.alpha = 0;
		EaseMotion.ease(_slideLayer, "y", 0, 0.4, loadImage, [item]);
		EaseMotion.ease(_bg, "alpha", 0.5, 0.25);
		_bg.visible = true;
		_title.text = item.authorName +" - "+ item.title;
	}
	private function loadImage(item:MediaItem):void {
		_loading.show();
		_loading.setProgress(0);
		EaseMotion.ease(_loading, "alpha", 1);
		_loader.resetItem();
		_loader.addImage(item.content, "image");
		_loader.load(onLoadImage, null, onLoading);
	}
	private function onLoading(per:Number):void {
		_loading.setProgress(per);
	}
	private function onLoadImage():void {
		if (!(_loader.image.image is BitmapData)) return;
		var img:BitmapData = _loader.image.image;
		EaseMotion.ease(_loading, "alpha", 0, 0.2, function():void { _loading.hide(); } );
		//画像が大きすぎたらエラー
		if (img.width > 8192 || img.height > 8192 || img.width * img.height > 16777216) {
			_errorLabel.text = "ERROR: Image too large\n(" + img.width + " X " + img.height + " px)";
			_errorLabel.visible = true;
			return;
		}
		var bmpTemp:Bitmap = new Bitmap(img, "auto", true);
		var rect:Rectangle = RectUtil.adjustRect(img.rect, _viewRect, RectUtil.FULL, false);
		var scaleW:Number = rect.width / img.width;
		var scaleH:Number = rect.height / img.height;
		var img2:BitmapData = new BitmapData(rect.width, rect.height, false, 0xFF000000);
		
		//一時的に最高画質でキャプチャ
		stage.quality = StageQuality.BEST;
		img2.draw(bmpTemp, new Matrix(scaleW, 0, 0, scaleH), null, null, null, true);
		stage.quality = StageQuality.LOW;
		
		_loader.disposeAll();
		_bmp = _imageContainer.addChild(new Bitmap(img2)) as Bitmap;
		_bmp.x = _viewRect.x + (_viewRect.width - _bmp.width) * 0.5;
		_bmp.y = _viewRect.y + (_viewRect.height - _bmp.height) * 0.5;
		stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
		onMsMove(null);
		EaseMotion.ease(_imageContainer, "alpha", 1);
	}
	private function onMsMove(e:MouseEvent):void {
		if (!_bmp) return;
		var centerX:Number = _viewRect.left + _viewRect.right / 2;
		var centerY:Number = _viewRect.top + _viewRect.bottom / 2;
		var perX:Number = (_bmp.width < _viewRect.width)? 0.5 : (stage.mouseX - centerX) / Display.WIDTH * 2 + 0.5;
		var perY:Number = (_bmp.height < _viewRect.height)? 0.5 : (stage.mouseY - centerY) / Display.HEIGHT * 2 + 0.5;
		perX = Math.max(0, Math.min(1, perX));
		perY = Math.max(0, Math.min(1, perY));
		EaseMotion.ease(_bmp, "x", _viewRect.x + (_viewRect.width - _bmp.width) * perX, 0.3);
		EaseMotion.ease(_bmp, "y", _viewRect.y + (_viewRect.height - _bmp.height) * perY, 0.3);
	}
	public function close():void {
		stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
		dispose();
		_errorLabel.visible = false;
		EaseMotion.ease(_slideLayer, "y", 465, 0.4, function():void { } );
		EaseMotion.ease(_bg, "alpha", 0, 0.25, function():void { _bg.visible = false; } );
	}
}

class RectUtil {
	static public const FULL:String = "full";
	static public const AUTO:String = "auto";
	static public const FIT:String = "fit";
	static public const NONE:String = "none";
	static public function adjustRect(target:Rectangle, frame:Rectangle, mode:String = AUTO, isExpand:Boolean = true):Rectangle {
		var asp1:Number = frame.width / frame.height;
		var asp2:Number = target.width / target.height;
		var resize:Rectangle = new Rectangle();
		if (mode == FIT) {
			resize = frame.clone();
		} else if (mode == NONE) {
			resize = target.clone();
		} else if ((mode == AUTO && asp1 < asp2) || (mode == FULL && asp1 > asp2)) {
			resize.width = (!isExpand && target.width < frame.width)? target.width : frame.width;
			resize.height = resize.width / asp2;
		} else {
			resize.height = (!isExpand && target.height < frame.height)? target.height : frame.height;
			resize.width = resize.height * asp2;
		}
		return resize;
	}
}

/**
 * マップ上に配置するオブジェクト
 */
class Piece extends Sprite {
	public var bmp:Bitmap;
	public var image:BitmapData;
	public var w:int;
	public var h:int;
	public var materialID:int = -1;
	public var offsetX:int = 0;
	public var offsetY:int = 0;
	public var ads:Vector.<Ad> = new Vector.<Ad>();
	public var basePos:Point = new Point();
	protected var _px:Number;
	protected var _py:Number;
	protected var _pz:Number;
	public function Piece(img:BitmapData, sizeW:int = 2, sizeH:int = 2) {
		image = img;
		bmp = addChild(new Bitmap(image)) as Bitmap;
		w = sizeW;
		h = sizeH;
		var min:Number = Math.min(w, h) / 2
		basePos = (w >= h)? Quarter.toLocalScreen(w - min, h / 2, 0) : Quarter.toLocalScreen(w / 2, h - min, 0);
		bmp.x = h * -22;
		bmp.y = -bmp.height + (w + h) * 11;
	}
	public function setPosition(px:Number, py:Number, pz:Number):void {
		_px = px;
		_py = py;
		_pz = pz;
		update();
	}
	public function update():void {
		var p:Point = Quarter.toScreen(_px, _py, _pz);
		x = p.x + offsetX;
		y = p.y + offsetY;
	}
	public function addAd(px:Number, py:Number, width:Number, height:Number, skew:Number = -0.5):void {
		var ad:Ad = new Ad(width, height, skew);
		ad.x = bmp.x + px;
		ad.y = bmp.y + py;
		addChild(ad);
		ads.push(ad);
	}
}

/**
 * 車
 */
class Car extends Piece {
	private var _size:Rectangle = new Rectangle(0, 0, 21, 18);
	private var _carImage:BitmapData;
	private var _snapPos:Point = new Point();
	private var _map:MapView;
	//速度
	public var speed:Number = 0.05;
	//現在の方向
	private var _direction:int = 0;
	//次に曲がる方向
	private var _nextDirection:int = -1;
	public var route:Array = [];
	private var _routCount:int = -1;
	public function Car(img:BitmapData, xx:int, yy:int, map:MapView) {
		_map = map;
		_carImage = img;
		super(new BitmapData(_size.width, _size.height, true, 0), 1, 1);
		_snapPos.x = xx;
		_snapPos.y = yy;
		setPosition(xx, yy, 2);
		bmp.x += 10;
		bmp.y += -15;
		basePos.x = 0;
		basePos.y = 0;
		setDirection(_direction);
	}
	//道路に沿うように調整
	private function adjustPos():void {
		if (_direction == 0) _px = _snapPos.x + 0.25;
		else if (_direction == 2) _px = _snapPos.x + 0.75;
		else if (_direction == 1) _py = _snapPos.y + 0.25;
		else if (_direction == 3) _py = _snapPos.y + 0.75;
	}
	//前方のベクトルを取得
	private function getVector(dir:int):Point {
		var px:int = [0, 1, 0, -1][dir];
		var py:int = [ -1, 0, 1, 0][dir];
		return new Point(px, py);
	}
	//前方のタイルの座標を調べる
	private function getNextPos():Point {
		return _snapPos.add(getVector(_direction));
	}
	//次にどっちに曲がるか調べる
	private function checkNextPath():void {
		var p:Point = getNextPos();
		var path:Array = [];
		//前方のタイルからマップ外に続く進路を削る
		for each(var dir:int in _map.data.roadPath[_map.toIndex(p.x, p.y)]) {
			var p2:Point = getVector(dir).add(p);
			var num:int = _map.toIndex(p2.x, p2.y);
			if (num >= 0 && num < _map.data.W * _map.data.H) path.push(dir); 
		}
		//自分と逆方向の進路を削る
		if (path.length >= 2) {
			var ngPath:int = (_direction + 2) % 4;
			var index:int = path.indexOf(ngPath);
			if (index != -1) path.splice(index, 1);
		}
		var routeDir:int = -1;
		if (route.length && path.length >= 2) {
			_routCount = (_routCount + 1) % route.length;
			routeDir = route[_routCount];
		}
		_nextDirection = (!path.length)? -1 : (routeDir != -1)? routeDir : path[Math.floor(Math.random() * path.length)];
		if (_direction == _nextDirection) _nextDirection = -1;
	}
	/**
	 * 方向設定
	 */
	public function setDirection(dir:int):void {
		_direction = dir;
		image.copyPixels(_carImage, new Rectangle(_size.width * dir, 0, _size.width, _size.height), new Point());
	}
	/**
	 * 動かす
	 */
	public function simulateTick():void {
		var vec:Point = getVector(_direction);
		var nextTile:Point = getNextPos();
		if (_nextDirection != -1) {
			if (_nextDirection == 0) {
				if (_direction == 1 && _px > nextTile.x + 0.25) nextPath();
				else if (_direction == 3 && _px < nextTile.x + 0.25) nextPath();
				else if (_direction == 2 && _py > nextTile.y + 0.25) nextPath();
			} else if (_nextDirection == 2) {
				if (_direction == 1 && _px > nextTile.x + 0.75) nextPath();
				else if (_direction == 3 && _px < nextTile.x + 0.75) nextPath();
				else if (_direction == 0 && _py < nextTile.y + 0.75) nextPath();
			} else if (_nextDirection == 1) {
				if (_direction == 0 && _py < nextTile.y + 0.25) nextPath();
				else if (_direction == 2 && _py > nextTile.y + 0.25) nextPath();
				else if (_direction == 3 && _px < nextTile.x + 0.25) nextPath();
			} else if (_nextDirection == 3) {
				if (_direction == 0 && _py < nextTile.y + 0.75) nextPath();
				else if (_direction == 2 && _py > nextTile.y + 0.75) nextPath();
				else if (_direction == 1 && _px > nextTile.x + 0.75) nextPath();
			}
		} else {
			nextPath();
		}
		_px += vec.x * speed;
		_py += vec.y * speed;
		_pz = _map.getAltitude(_px, _py);
		rotation = _slopeRotation[_map.getSlope(_px, _py)];
		adjustPos();
		update();
	}
	private var _slopeRotation:Array = [0, -20, -20, 20, 20];
	private function nextPath():void {
		_snapPos = getNextPos();
		if(_nextDirection != -1) setDirection(_nextDirection);
		adjustPos();
		checkNextPath();
	}
}

/**
 * 色変換
 */
class ColorMatrix {
	static private const _LUM_R:Number = 0.212671;
	static private const _LUM_G:Number = 0.715160;
	static private const _LUM_B:Number = 0.072169;
	/**
	 * 色相変化フィルター用配列を取得
	 */
	static public function getHueArray(num:Number):Array {
		var rad:Number = Math.PI * num;
		var cos:Number = Math.cos(rad);
		var sin:Number = Math.sin(rad);
		var rNum:Number = 1 - cos - sin;
		var gNum:Number = 1 - cos;
		var bNum:Number = 1 - cos + sin;
		var r1:Number = _LUM_R * rNum + cos;
		var r2:Number = _LUM_G * rNum;
		var r3:Number = _LUM_B * rNum + sin;
		var g1:Number = _LUM_R * gNum + sin * 0.143;
		var g2:Number = _LUM_G * gNum + sin * 0.140 + cos;
		var g3:Number = _LUM_B * gNum - sin * 0.283;
		var b1:Number = _LUM_R * bNum - sin;
		var b2:Number = _LUM_G * bNum;
		var b3:Number = _LUM_B * bNum + cos;
		return [r1, r2, r3, 0, 0, g1, g2, g3, 0, 0, b1, b2, b3, 0, 0, 0, 0, 0, 1, 0];
	}
}

/**
 * 広告
 */
class Ad extends Sprite{
	public var item:MediaItem;
	private var _mask:Sprite;
	private var _container:Sprite = new Sprite();
	private var _size:Rectangle = new Rectangle();
	public function Ad(width:Number, height:Number, skew:Number = -0.5) {
		_mask = Painter.createColorRect(width, height);
		addChild(_container);
		addChild(_mask);
		_size.width = width;
		_size.height = height;
		_container.mask = _mask;
		_container.blendMode = BlendMode.ADD;
		transform.matrix = new Matrix(1, skew, 0, 1);
	}
	public function setItem(item:MediaItem):void {
		this.item = item;
		while (_container.numChildren) _container.removeChildAt(0);
		var bmp:Bitmap = _container.addChild(new Bitmap(item.image, "auto")) as Bitmap;
		var rect:Rectangle = RectUtil.adjustRect(bmp.bitmapData.rect, _size, RectUtil.FULL);
		bmp.width = rect.width;
		bmp.height = rect.height;
		bmp.x = (_size.width - rect.width) / 2;
		bmp.y = (_size.height - rect.height) / 2;
	}
}

/**
 * マップデータ
 */
class MapData {
	public const W:int = 25;
	public const H:int = 25;
	//水面の高さ
	public const WATER_LEVEL:int = 2;
	//道路の最低高度
	public const ROAD_LEVEL:int = 3;
	public var roadPath:Vector.<Array> = new Vector.<Array>();
	//地形の高さ
	public var level:Array = [
		[8,7,6,6,5,5,5,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[5,6,5,5,4,5,4,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[4,5,5,4,4,4,4,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[4,4,4,4,4,4,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
		[4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1],
		[3,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
		[1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
		[1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]
	];
	//地形の傾き
	public var slope:Array = [
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
		[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,4,0],
		[0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[0,0,2,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		[0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
	];
	/**
	 * 地面の種類
	 * 0:地面
	 * 1:道路
	 * 2:草
	 * 3:アスファルト
	 * 4:砂
	 * 5:木
	 * 6:電話ボックス
	 * 7:丸い植物
	 * 8:排水管(斜面専用)
	 */
	public var material:Array = [
		[2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,6,7,5,5,5,0,0,0],
		[2,2,2,2,2,2,2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,0,0,0],
		[2,2,2,2,2,2,4,0,0,1,0,0,0,0,1,0,0,0,0,5,1,0,0,0,0],
		[2,2,2,2,2,4,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,5,8,0,0],
		[2,2,2,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0],
		[0,0,0,0,0,0,0,0,5,1,0,0,0,0,1,0,0,0,0,0,1,5,0,0,0],
		[0,0,7,1,1,1,1,1,1,1,0,0,0,0,1,5,5,5,5,7,1,5,0,0,0],
		[0,0,5,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1],
		[0,0,7,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[0,0,0,1,0,0,0,0,0,1,5,0,0,5,1,0,0,0,0,1,0,0,8,0,0],
		[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,0,0,0,0],
		[0,0,7,0,0,6,5,5,5,1,5,0,6,5,1,5,0,0,5,1,5,0,0,0,0],
		[1,1,5,0,8,0,0,0,0,1,0,8,0,0,1,0,8,0,0,1,0,0,0,0,0],
		[0,1,5,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
		[0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,5,6,7,7,1,0,0,0,0,5],
		[0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,5],
		[0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,5],
		[0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,3,3,3,3,5],
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,5,0,0,6,5],
		[0,0,0,0,5,5,5,5,0,0,0,0,0,2,1,1,1,1,1,1,1,1,1,1,1],
		[0,0,0,0,0,0,5,5,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2]
	];
	public function MapData() {
		//二重配列の前後にダミーのデータを入れる
		var a:Array, i:int;
		level.unshift(level[0].concat());
		level.push(level[level.length - 1].concat());
		for each(a in level) {
			a.unshift(a[0]);
			a.push(a[a.length - 1]);
		}
		a.length = 0;
		for (i = 0; i < slope[0].length; i++) a.push(0);
		slope.unshift(a);
		slope.push(a.concat());
		for each(a in slope) {
			a.unshift(0);
			a.push(0);
		}
		material.unshift(material[0].concat());
		material.push(material[material.length - 1].concat());
		for each(a in material) {
			a.unshift(a[0]);
			a.push(a[a.length - 1]);
		}
		//
		var l:int = W * H;
		for (i = 0; i < l; i++) roadPath[i] = [];
	}
}

/**
 * オブジェクト管理
 */
class PieceMaker {
	private var _rawImage:BitmapData;
	public var image:Object = { };
	public var objects:Vector.<Piece> = new Vector.<Piece>();
	public function PieceMaker(img:BitmapData) {
		_rawImage = img;
	}
	public function hitTest(x:Number, y:Number):Ad {
		for each(var obj:Piece in objects) {
			for each(var ad:Ad in obj.ads) {
				if (ad.hitTestPoint(x, y, true)) return ad;
			}
		}
		return null;
	}
	public function newImage(x:int, y:int, width:int, height:int):BitmapData {
		var img:BitmapData = new BitmapData(width, height, true, 0);
		img.copyPixels(_rawImage, new Rectangle(x, y, width, height), new Point(), null, null, true);
		return img;
	}
	public function createPiece(name:String, x:int, y:int, width:int, height:int, sizeW:int = 2, sizeH:int = 2, matID:int = -1):Piece {
		return newPiece(name, newImage(x, y, width, height), sizeW, sizeH, matID);
	}
	public function newPiece(name:String, image:BitmapData, sizeW:int = 2, sizeH:int = 2, matID:int = -1):Piece {
		var obj:Piece = new Piece(image, sizeW, sizeH);
		obj.name = name;
		obj.materialID = matID;
		objects.push(obj);
		return obj;
	}
	public function addPiece(p:Piece):Piece {
		objects.push(p);
		return p;
	}
	public function getPiece(name:String):Piece {
		for each(var p:Piece in objects) {
			if (name == p.name) return p;
		}
		return null;
	}
	public function addChildAll(target:DisplayObjectContainer):void {
		for each(var p:Piece in objects) target.addChild(p);
	}
}

/**
 * マップ管理
 */
class MapView {
	public var container:Sprite = new Sprite();
	public var objLayer:Sprite = new Sprite();
	public var canvas:BitmapData;
	public var waterSection:BitmapData;
	public var chip:MapChip = new MapChip();
	public var data:MapData = new MapData();
	public var piece:PieceMaker;
	public var myCar:Car;
	public var cars:Vector.<Car> = new Vector.<Car>();
	public function MapView() {
		canvas = new BitmapData(1100, 865, true, 0);
		waterSection = new BitmapData(canvas.width, canvas.height, false, 0xFF000000);
		container.addChild(new Bitmap(canvas));
		container.addChild(objLayer);
	}
	private function transparent(img:BitmapData):BitmapData {
		var temp:BitmapData = new BitmapData(img.width, img.height, true, 0);
		temp.copyPixels(img, img.rect, new Point());
		temp.threshold(temp, temp.rect, new Point(), "==", 0xFF << 24 | chip.TRANSPARENT1, 0x00FFFFFF);
		temp.threshold(temp, temp.rect, new Point(), "==", 0xFF << 24 | chip.TRANSPARENT2, 0x00FFFFFF);
		return temp;
	}
	public function init(loader:ImageLoader):void {
		//マップチップ切り出し開始
		var ix:int, iy:int, i:int, k:String, img:BitmapData;
		var bmp:BitmapData = transparent(loader.image.mapchip);
		var bmp2:BitmapData = transparent(loader.image.bldg);
		var bmp3:BitmapData = transparent(loader.image.car);
		loader.image.bldg = bmp2;
		var images:Vector.<BitmapData> = new Vector.<BitmapData>();
		for (i = 0; i < 60 ; i++) {
			ix = i % 10;
			iy = int(i / 10);
			img = chip.newBitmap();
			img.copyPixels(bmp, new Rectangle(ix * chip.W, iy * chip.H, chip.W, chip.H), new Point());
			images.push(img);
		}
		//地形
		chip.terrain = images.splice(0, 15);
		//断面
		for each(k in ["L", "R"]) chip["section" + k] = images.shift();
		chip.sections = images.splice(0, 4);
		//道路
		chip.road = images.splice(0, 5);
		//歩道
		for each(k in ["TLI", "TRI", "BRI", "BLI", "TLO", "TRO", "BRO", "BLO", "T", "R", "B", "L"]) chip.sidewalk[k] = images.shift();
		//橋のフェンスと柱
		for each(k in ["PN", "PW1", "PW2", "base", "T", "R", "B", "L"]) chip.bridge[k] = images.shift();
		//白線
		for each(k in ["T", "R", "B", "L"]) chip.centerline[k] = images.shift();
		//横断歩道
		for each(k in ["X", "Y"]) chip["crosswalk" + k] = images.shift();
		for each(k in ["Grass", "Asphalt", "Sand"]) chip["ground" + k] = images.shift();
		
		chip.phoneBooth = images.shift();
		chip.grassBall = images.shift();
		images.shift();
		chip.drainpipe.B = images.shift();
		chip.drainpipe.R = images.shift();
		
		
		//オブジェクト
		piece = new PieceMaker(loader.image.bldg);
		piece.createPiece("bldg1", 0, 183, 108, 150, 2, 3, -1);
		piece.createPiece("bldg2", 230, 0, 86, 163, 2, 2, -1);
		piece.createPiece("bldg3", 0, 0, 121, 178, 3, 3, -1);
		piece.createPiece("bldg4", 121, 0, 109, 168, 2, 3, -1);
		piece.createPiece("bldg5", 316, 0, 132, 220, 4, 2, -1);
		piece.createPiece("house1", 174, 356, 174, 94, 4, 4, 2);
		piece.createPiece("house2", 0, 358, 174, 92, 4, 4, 2);
		piece.createPiece("conveni", 112, 189, 135, 84, 4, 3, 3);
		piece.createPiece("park", 110, 274, 112, 76, 4, 2, 2);
		piece.createPiece("busstopV", 255, 227, 56, 49, 1, 2, -1);
		piece.createPiece("busstopH", 319, 227, 60, 49, 2, 1, -1);
		piece.createPiece("adsignV", 383, 229, 61, 69, 1, 2, -1);
		piece.createPiece("adsignH", 444, 229, 53, 69, 2, 1, -1);
		piece.createPiece("parking", 348, 313, 152, 112, 4, 3, 3);
		piece.image.tree = piece.newImage(232, 313, 42, 37);
		
		piece.getPiece("bldg1").addAd(19, 40, 40, 34, 0.5);
		piece.getPiece("bldg1").addAd(88, 280 - 183, 20, 30, -0.5);
		piece.getPiece("bldg2").addAd(15, 16, 27, 28, 0.5);
		piece.getPiece("bldg2").addAd(44, 29, 27, 28, -0.5);
		piece.getPiece("bldg3").addAd(56, 51, 29, 61, -0.5);
		piece.getPiece("bldg3").addAd(4, 9, 31, 21, 0.5);
		piece.getPiece("bldg4").addAd(59, 66, 44, 46, -0.5);
		piece.getPiece("bldg5").addAd(11, 81, 22, 45, 0.5);
		piece.getPiece("bldg5").addAd(11, 129, 22, 45, 0.5);
		piece.getPiece("bldg5").addAd(69, 110, 40, 94, 0);
		piece.getPiece("conveni").addAd(44, 11, 22, 13, 0.5);
		piece.getPiece("adsignV").addAd(15, 19, 36, 36, -0.5);
		piece.getPiece("adsignH").addAd(10, 1, 36, 36, 0.5);
		piece.getPiece("parking").addAd(56, 2, 36, 35, 0.5);
		piece.getPiece("parking").addAd(100, 24, 36, 35, 0.5);
		
		putObject(piece.getPiece("bldg1"), 16, 7);
		putObject(piece.getPiece("bldg2"), 11, 7);
		putObject(piece.getPiece("bldg3"), 17, 3);
		putObject(piece.getPiece("bldg4"), 0, 8);
		putObject(piece.getPiece("bldg5"), 4, 7);
		putObject(piece.getPiece("house2"), 10, 18);
		putObject(piece.getPiece("house1"), 5, 18);
		putObject(piece.getPiece("park"), 9, 23);
		putObject(piece.getPiece("conveni"), 15, 20);
		putObject(piece.getPiece("busstopV"), 2, 9);
		putObject(piece.getPiece("busstopH"), 21, 22);
		putObject(piece.getPiece("adsignV"), 13, 23);
		putObject(piece.getPiece("adsignH"), 11, 9);
		putObject(piece.getPiece("parking"), 20, 18);
		
		drawTerrain();
		
		myCar = piece.addPiece(new Car(bmp3, 7, 22, this)) as Car;
		//myCar.route = [1,2,0,3,0,1,0,0,2,3,0,3,2];
		myCar.route = [1,2,0,0,0,0,2,3,0,3,2];
		myCar.setDirection(1);
		for (i = 0; i < 10; i++) {
			var c:Car = piece.addPiece(new Car(bmp3, i+3, 12, this)) as Car;
			c.filters = [new ColorMatrixFilter(ColorMatrix.getHueArray(Math.random() * 2 - 1))];
			cars.push(c);
		}
		piece.addChildAll(objLayer);
		
		bmp.dispose();
		bmp2.dispose();
	}
	/**
	 * 建造物を指定の位置に置く
	 */
	public function putObject(obj:Piece, x:int, y:int, z:int = 0):void {
		var h:int, maxH:int = int.MIN_VALUE, ix:int, iy:int;
		for (ix = x; ix < x + obj.w; ix++) {
			for (iy = y; iy < y + obj.h; iy++) {
				h = getLevel(ix, iy);
				if (h > maxH) maxH = h;
			}
		}
		obj.setPosition(x, y, Math.max(z, maxH, data.WATER_LEVEL));
		if (obj.materialID == -1) return;
		for (ix = x; ix < x + obj.w; ix++) {
			for (iy = y; iy < y + obj.h; iy++) {
				setMaterial(ix, iy, obj.materialID);
			}
		}
	}
	/**
	 * 地形マップを描画する
	 */
	public function drawTerrain():void {
		canvas.fillRect(canvas.rect, 0);
		var ct_white1:ColorTransform = new ColorTransform();
		var ct_white2:ColorTransform = new ColorTransform();
		var ct_black:ColorTransform = new ColorTransform();
		var ct_water1:ColorTransform = new ColorTransform();
		var ct_water2:ColorTransform = new ColorTransform();
		ct_white1.color = 0xFFFFFF;
		ct_white2.color = 0x888888;
		ct_black.color = 0x000000;
		ct_water1.color = 0x1C3C82;
		ct_water2.color = 0x1F459B;//0x617DBE;
		
		var dirSign1:Array = ["t", "l", "b", "r"];
		var dirSign2:Array = ["tl", "tr", "bl", "br"];
		var dirSign3:Array = ["t", "l", "b", "r", "tl", "tr", "bl", "br"];
		var ofx:Object = { t:0, r:1, b:0, l: -1, tl:-1, tr:1, bl:-1, br:1 };
		var ofy:Object = { t: -1, r:0, b:1, l:0, tl:-1, tr:-1, bl:1, br:1 };
		var index:Object = { t: 0, r:1, b:2, l:3 };
		
		var k:String, i:int, slope:int, h:int, h2:Number, ix:int, iy:int, iz:int, pz:int, img:BitmapData;
		var isSubmerged:Boolean, id:int, isHorizon:Boolean;
		var level:int = Math.max(data.ROAD_LEVEL, data.WATER_LEVEL);
		for (ix = 0; ix < data.W; ix++) {
			for (iy = 0; iy < data.H; iy++) {
				h = getLevel(ix, iy);
				//背面の水の断面を描画
				if (!ix || !iy || ix == (data.W - 1) || iy == (data.H - 1)) {
					for (iz = 0; iz <= data.WATER_LEVEL; iz++) {
						var txy:Point = Quarter.toScreen(ix, iy, iz).add(new Point(0, -chip.H));
						if (!ix) paintBlock(canvas, chip.sectionR, ix, iy, iz, -20, -11, ct_water1);
						if (!iy) paintBlock(canvas, chip.sectionL, ix, iy, iz, 20, -11, ct_water2);
						if (ix == (data.W - 1)) paintBlock(waterSection, chip.sectionR, ix, iy, iz, 0, 0, ct_white1);
						if (iy == (data.H - 1)) paintBlock(waterSection, chip.sectionL, ix, iy, iz, 0, 0, ct_white2);
					}
				}
				slope = 0
				//地形を描画
				for (iz = 0; iz <= h; iz++) {
					var deep:int = 5 * Math.min(2, Math.max(0, (data.WATER_LEVEL - iz + 1)));
					slope = (iz == h)? getSlope(ix, iy) : 0;
					img = chip.terrain[slope + deep];
					drawBlock(img, ix, iy, iz);
					//地形の断面を描画
					if (ix == (data.W - 1) && slope != 4) {
						//右端
						img = (slope == 0 || slope == 2)? chip.sectionR : chip.sections[slope-1];
						drawBlock(img, ix, iy, iz);
						paintBlock(waterSection, img, ix, iy, iz, 0, 0, ct_black);
					}
					if (iy == (data.H - 1) && slope != 1) {
						//左端
						img = (slope == 0 || slope == 3)? chip.sectionL : chip.sections[slope-1];
						drawBlock(img, ix, iy, iz);
						paintBlock(waterSection, img, ix, iy, iz, 0, 0, ct_black);
					}
				}
				iz = h;
				h2 = getHeight(ix, iy);
				isSubmerged = h2 < data.WATER_LEVEL;
				id = getMaterial(ix, iy);
				isHorizon = (slope == 0);
				//道路
				if (id == 1) {
					if(h2 >= level){
						//普通に描画
						img = chip.road[slope];
						iz = h;
					} else {
						//最低レベルより下にいかないようにする
						img = chip.road[0];
						iz = level;
						setLevel(ix, iy, iz);
						setSlope(ix, iy, 0);
						//橋の支柱
						if (isHorizon) {
							for (pz = h; pz < level; pz++) {
								var pict:BitmapData = [chip.bridge.PN, chip.bridge.PW1, chip.bridge.PW2][Math.min(2, Math.max(0, data.WATER_LEVEL - pz))];
								drawPlane(pict, ix, iy, pz);
							}
						}
						drawPlane(chip.bridge.base, ix, iy, iz, 0, 3);
					}
					drawBlock(img, ix, iy, iz);
					//橋のフェンス
					if (h2 < level) {
						for each(k in dirSign1) {
							if (getMaterial(ix + ofx[k], iy + ofy[k]) != 1) drawPlane(chip.bridge[k.toUpperCase()], ix, iy, iz);
						}
					}
				}
				if (!isSubmerged && isHorizon) {
					if (id == 2) drawBlock(chip.groundGrass, ix, iy, iz);
					if (id == 3) drawBlock(chip.groundAsphalt, ix, iy, iz);
					if (id == 4) drawBlock(chip.groundSand, ix, iy, iz);
					if (id == 5) putObject(piece.newPiece("tree", piece.image.tree, 1, 1), ix, iy, iz);
				}
			}
		}
		var noLine:Array = [];
		var slopeH:Object = {
			t:[0, -0.5, 0, 0.5, 0],
			b:[0, 0.5, 0, -0.5, 0],
			l:[0, 0, 0.5, 0, -0.5],
			r:[0, 0, -0.5, 0, 0.5]
		}
		//道路の歩道と白線
		for (ix = 0; ix < data.W; ix++) {
			for (iy = 0; iy < data.H; iy++) {
				h2 = getHeight(ix, iy);
				id = getMaterial(ix, iy);
				slope = getSlope(ix, iy);
				iz = getLevel(ix, iy);
				isHorizon = slope == 0;
				isSubmerged = (h2 < data.WATER_LEVEL);
				var cn:Object = { };
				for each(k in dirSign3) {
					var plusH:Number = (k.length == 2)? 0 : slopeH[k][getSlope(ix + ofx[k], iy +ofy[k])];
					cn[k] = getMaterial(ix + ofx[k], iy + ofy[k]) == 1 && h2 == getHeight(ix + ofx[k], iy + ofy[k]) + plusH;
				}
				//道路
				if (id == 1) {
					//白線描画
					if (isHorizon) {
						if (cn.t + cn.b + cn.r + cn.l >= 3 && !cn.tl && !cn.tr && !cn.br && !cn.bl) {
							for each(k in dirSign1) {
								if (cn[k]) {
									img = (k == "t" || k == "b")? chip.crosswalkX : chip.crosswalkY;
									drawBlock(chip.road[0], ix + ofx[k], iy + ofy[k], iz);
									drawBlock(img, ix + ofx[k], iy + ofy[k], iz);
									if (k == "r" || k == "b") noLine[toIndex(ix + ofx[k], iy + ofy[k])] = true;
								}
							}
						} else if (!noLine[toIndex(ix, iy)]) {
							for each(k in dirSign1) if (cn[k]) drawBlock(chip.centerline[k.toUpperCase()], ix, iy, iz);
						}
					}
					//進行可能な方向を記録
					for each(k in dirSign1) {
						if (cn[k]) data.roadPath[toIndex(ix, iy)].push(index[k]);
					}
				} else if (isHorizon && iz >= level) {
					//歩道描画
					if(cn.t) drawBlock(chip.sidewalk.T, ix, iy, iz);
					if(cn.b) drawBlock(chip.sidewalk.B, ix, iy, iz);
					if(cn.l) drawBlock(chip.sidewalk.L, ix, iy, iz);
					if(cn.r) drawBlock(chip.sidewalk.R, ix, iy, iz);
					if(cn.t && cn.r) drawBlock(chip.sidewalk.TRI, ix, iy, iz);
					if(cn.t && cn.l) drawBlock(chip.sidewalk.TLI, ix, iy, iz);
					if(cn.b && cn.r) drawBlock(chip.sidewalk.BRI, ix, iy, iz);
					if(cn.b && cn.l) drawBlock(chip.sidewalk.BLI, ix, iy, iz);
					if(!cn.t && !cn.r && cn.tr) drawBlock(chip.sidewalk.TRO, ix, iy, iz);
					if(!cn.t && !cn.l && cn.tl) drawBlock(chip.sidewalk.TLO, ix, iy, iz);
					if(!cn.b && !cn.r && cn.br) drawBlock(chip.sidewalk.BRO, ix, iy, iz);
					if(!cn.b && !cn.l && cn.bl) drawBlock(chip.sidewalk.BLO, ix, iy, iz);
				}
				if (!isSubmerged) {
					if (id == 6) drawPlane(chip.phoneBooth, ix, iy, iz);
					if (id == 7) drawPlane(chip.grassBall, ix, iy, iz);
					if (id == 8) {
						if(slope == 4) drawBlock(chip.drainpipe.R, ix, iy, iz);
						if(slope == 1) drawBlock(chip.drainpipe.B, ix, iy, iz);
					}
				}
			}
		}
		//水の断面を描画
		var bmp:BitmapData = new BitmapData(canvas.width, canvas.height, true, 0xFF324856);
		bmp.copyChannel(waterSection, waterSection.rect, new Point(), BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
		canvas.draw(bmp, new Matrix(), new ColorTransform(1, 1, 1, 1), BlendMode.MULTIPLY);
		bmp.dispose();
	}
	public function toIndex(x:int, y:int):int {
		return x * data.W + y;
	}
	public function getSlope(x:int, y:int):int {
		return data.slope[y + 1][x + 1];
	}
	public function getMaterial(x:int, y:int):int {
		return data.material[y + 1][x + 1];
	}
	/**
	 * 正確な高度を取得
	 */
	public function getAltitude(x:Number, y:Number):Number {
		var lv:Number = getLevel(int(x), int(y));
		var intX:Number = x - int(x);
		var intY:Number = y - int(y);
		var slope:int = getSlope(int(x), int(y));
		if (slope == 1) lv += -intY;
		if (slope == 2) lv += - 1 + intX;
		if (slope == 3) lv += - 1 + intY;
		if (slope == 4) lv += -intX;
		return lv;
	}
	/**
	 * 斜面も考慮した高さを取得
	 */
	public function getHeight(x:int, y:int):Number {
		return data.level[y + 1][x + 1] - int(!!getSlope(x, y)) * 0.5;
	}
	/**
	 * 斜面を無視した高さを取得
	 */
	public function getLevel(x:int, y:int):int {
		return data.level[y + 1][x + 1];
	}
	public function setMaterial(x:int, y:int, id:int):void {
		data.material[y + 1][x + 1] = id;
	}
	public function setSlope(x:int, y:int, slope:int):void {
		data.slope[y + 1][x + 1] = slope;
	}
	public function setLevel(x:int, y:int, lv:int):void {
		data.level[y + 1][x + 1] = lv;
	}
	public function paintBlock(target:BitmapData, img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0, ct:ColorTransform = null):void {
		paintPlane(target, img, x, y, z - 1, offsetX, offsetY - 1, ct);
	}
	public function paintPlane(target:BitmapData, img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0, ct:ColorTransform = null):void {
		var p:Point = Quarter.toScreen(x, y, z);
		p.x -= 22;
		p.y += 22;
		target.draw(img, new Matrix(1, 0, 0, 1, p.x + offsetX, p.y - img.height + offsetY), ct);
	}
	public function drawBlock(img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0):void {
		drawPlane(img, x, y, z - 1, offsetX, offsetY - 1);
	}
	public function drawPlane(img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0):void {
		var p:Point = Quarter.toScreen(x, y, z);
		p.x -= 22;
		p.y += 22;
		canvas.copyPixels(img, img.rect, new Point(p.x + offsetX, p.y - img.height + offsetY), null, null, true); 
	}
}
class Quarter {
	/**
	 * クオータービュー座標をスクリーン座標に変換する
	 */
	static public function toScreen(x:Number, y:Number, z:Number):Point {
		return toLocalScreen(x, y, z).add(new Point(550, 150));
	}
	static public function toLocalScreen(x:Number, y:Number, z:Number):Point {
		return new Point(x * 22 - y * 22, x * 11 + y * 11 - z * 16);
	}
}
/**
 * マップチップ
 */
class MapChip {
	public const W:int = 42;
	public const H:int = 37;
	public const SIZE:Rectangle = new Rectangle(0, 0, W, H);
	//透過色
	public const TRANSPARENT1:uint = 0x324856;
	public const TRANSPARENT2:uint = 0x182329;
	//道路マップリスト
	public var road:Vector.<BitmapData>;
	//T,B,L,R,TL,TR,BL,BR,TLI,TRI,BLI,BRI,TLO,TRO,BLO,BRO
	public var sidewalk:Object = { };
	//RB(TL)
	public var drainpipe:Object = { };
	//TRBL
	public var centerline:Object = { };
	//TBLR
	public var bridge:Object = { };
	//地形マップリスト
	public var terrain:Vector.<BitmapData>;
	public var crosswalkX:BitmapData;
	public var crosswalkY:BitmapData;
	public var groundGrass:BitmapData;
	public var groundAsphalt:BitmapData;
	public var groundSand:BitmapData;
	public var phoneBooth:BitmapData;
	public var grassBall:BitmapData;
	//断面
	public var sectionL:BitmapData;
	public var sectionR:BitmapData;
	public var sections:Vector.<BitmapData>;
	public function MapChip() {
	}
	public function newBitmap(transparent:Boolean = true, color:uint = 0x000000):BitmapData {
		var bg:uint = (transparent)? 0x00 << 24 | color : 0xFF << 24 | color;
		return new BitmapData(W, H, transparent, bg);
	}
}
/**
 * 画面サイズ
 */
class Display {
	static public var stage:Stage;
	static public const WIDTH:int = 465;
	static public const HEIGHT:int = 465;
	static public const SIZE:Rectangle = new Rectangle(0, 0, WIDTH, HEIGHT);
	static public const CENTER:Point = new Point(SIZE.width / 2, SIZE.height / 2);
}
/**
 * Flickrから画像読み込み
 */
class Flickr extends EventDispatcher {
	static public const API:String = "http://api.flickr.com/services/feeds/photos_public.gne";
	static public const NS_MEDIA:String = "http://search.yahoo.com/mrss/";
	static public const NS_DC:String = "http://purl.org/dc/elements/1.1/";
	static public const NS_CC:String = "http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html";
	static public const NS_FLICKR:String = "urn:flickr:";
	/**サムネイルが読み込み完了する度に呼び出されるイベント*/
	static public const EVENT_LOADIMAGE:String = "onLoadImage";
	/**条件にマッチする画像が1つも見つからなかった時のイベント*/
	static public const EVENT_NOIMAGE:String = "onNoImage";
	/**画像を読み込む枚数*/
	public var loadLimit:int = 20;
	/**Flash以外を読み込む*/
	public var isSkipFlash:Boolean = true;
	/**全MediaItemデータ*/
	public var items:Vector.<MediaItem> = new Vector.<MediaItem>();
	/**サムネイルの読み込みが完了したMediaItemデータ*/
	public var loaded:Vector.<MediaItem> = new Vector.<MediaItem>();
	private var _stock:Vector.<MediaItem>;
	private var _rssLoader:URLLoader;
	private var _activeItem:MediaItem;
	private var _imageLoader:Loader;
	public function Flickr() {
		_rssLoader = new URLLoader();
		_imageLoader = new Loader();
	}
	/**
	 * Flickrの画像を検索する
	 */
	public function load(tag:String):void {
		items.length = 0;
		loaded.length = 0;
		removeEventImage();
		_rssLoader.addEventListener(Event.COMPLETE, onLoadRss);
		_rssLoader.addEventListener(IOErrorEvent.IO_ERROR, onErrorRss);
		_rssLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorRss);
		_rssLoader.load(new URLRequest(getSearchPath(tag)));
	}
	private function onErrorRss(e:ErrorEvent):void {
		removeEventRss();
		dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.text));
	}
	private function onLoadRss(e:Event):void {
		for each(var itm:MediaItem in items) itm.dispose();
		_stock = new Vector.<MediaItem>();
		removeEventRss();
		var xml:XML = new XML(_rssLoader.data);
		var media:Namespace = new Namespace(NS_MEDIA);
		var dc:Namespace = new Namespace(NS_DC);
		var cc:Namespace = new Namespace(NS_CC);
		var flickr:Namespace = new Namespace(NS_FLICKR);
		var tf:TextField = new TextField();
		for each(var node:XML in xml..item) {
			var item:MediaItem = new MediaItem();
			item.title = node.media::title;
			tf.htmlText = node.media::description;
			item.authorName = String(node.author).match(/.*?\((.*)\)/)[1];
			item.authorURL = node.author.@flickr::profile;
			item.descriptionHTML = node.media::description;
			item.descriptionText = tf.text;
			item.date.setDate(node.dc::date.Taken);
			item.thumbnail = node.media::thumbnail.@url;
			item.type = node.media::content.@type;
			item.content = node.media::content.@url;
			item.content_t = item.content.replace(/_[mstbo]\./, "_t.");
			item.isFlash = item.type == "application/x-shockwave-flash";
			if (!isSkipFlash || !item.isFlash) _stock.push(item);
		}
		_stock.sort(function():int { return 1 - Math.floor(Math.random() * 3) } );
		if (_stock.length > loadLimit) _stock.length = loadLimit;
		items = _stock.concat();
		if (!items.length) {
			dispatchEvent(new Event(EVENT_NOIMAGE));
		} else {
			loadNext();
		}
	}
	private function loadNext():void {
		if (!_stock.length || loaded.length >= loadLimit) {
			completeLoad();
			return;
		}
		_activeItem = _stock.shift();
		if (!_activeItem.thumbnail) {
			loadNext();
			return;
		}
		_imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadImage);
		_imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
		_imageLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
		_imageLoader.load(new URLRequest(_activeItem.thumbnail), new LoaderContext(true));
	}
	private function onErrorImage(e:ErrorEvent):void {
		removeEventImage();
		_activeItem.isLoading = false;
		_activeItem.isError = true;
		loadNext();
	}
	private function onLoadImage(e:Event):void {
		removeEventImage();
		_activeItem.isLoading = false;
		_activeItem.image = Bitmap(_imageLoader.content).bitmapData;
		loaded.push(_activeItem);
		dispatchEvent(new Event(EVENT_LOADIMAGE));
		loadNext();
	}
	private function completeLoad():void {
		_activeItem = null;
		dispatchEvent(new Event(Event.COMPLETE));
	}
	private function removeEventImage():void {
		_imageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadImage);
		_imageLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
		_imageLoader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
	}
	private function removeEventRss():void {
		_rssLoader.removeEventListener(Event.COMPLETE, onLoadRss);
		_rssLoader.removeEventListener(IOErrorEvent.IO_ERROR, onErrorRss);
		_rssLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorRss);
	}
	public function getSearchPath(tag:String):String {
		return API + "?format=rss_200&tags=" + tag;
	}
}
/**
 * MediaRSSから取りだした画像データ
 */
class MediaItem {
	/**サムネイル読み込み中か*/
	public var isLoading:Boolean = true;
	/**サムネイルの読み込みに失敗したか*/
	public var isError:Boolean = false;
	/**タイトル*/
	public var title:String;
	/**作者名*/
	public var authorName:String;
	/**作者URL*/
	public var authorURL:String;
	/**投稿日時*/
	public var date:Date = new Date();
	/**説明文(HTMLテキスト)*/
	public var descriptionHTML:String;
	/**説明文(テキスト)*/
	public var descriptionText:String;
	/**サムネイルURL*/
	public var thumbnail:String;
	/**原寸画像URL*/
	public var content:String;
	public var type:String;
	public var isFlash:Boolean;
	public var content_t:String;
	/**サムネイルBitmapData*/
	public var image:BitmapData;
	public function MediaItem() {
	}
	/**
	 * データを破棄
	 */
	public function dispose():void {
		image.dispose();
	}
}
/**
 * 複数画像読み込み
 */
class ImageLoader {
	public var image:Object = new Object();
	private var _images:Array = new Array();
	private var _count:int;
	private var _completeFunc:Function;
	private var _errorFunc:Function;
	private var _progressFunc:Function;
	private var _activeLoader:Loader;
	public function ImageLoader() {
	}
	public function addImage(src:String, name:String):void {
		_images.push( { src:src, name:name } );
	}
	public function stop():void {
		if (_activeLoader) {
			_activeLoader.unloadAndStop();
			removeEvent(_activeLoader.contentLoaderInfo);
			_activeLoader = null;
		}
	}
	public function load(complete:Function, error:Function = null, progress:Function = null):void {
		_count = -1;
		_completeFunc = complete;
		_errorFunc = error;
		_progressFunc = progress;
		next();
	}
	public function resetItem():void {
		_images.length = 0;
	}
	public function disposeAll():void {
		for (var k:String in image) {
			dispose(k);
		}
	}
	public function dispose(id:String):void {
		if (image[id] is BitmapData) {
			var bmp:BitmapData = image[id];
			if (bmp) bmp.dispose();
		}
	}
	private function next():void {
		if (++_count >= _images.length) {
			if (_completeFunc != null) _completeFunc();
			return;
		}
		_activeLoader = new Loader();
		_activeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
		_activeLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
		_activeLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
		_activeLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		_activeLoader.load(new URLRequest(_images[_count].src), new LoaderContext(true));
	}
	private function onProgress(e:ProgressEvent):void {
		var info:LoaderInfo = e.currentTarget as LoaderInfo;
		var per:Number = (_count + info.bytesLoaded / info.bytesTotal) / _images.length;
		if (_progressFunc != null) {
			var args:Array = [per, LoaderInfo(e.currentTarget)];
			if (_progressFunc.length < args.length) args.length = _progressFunc.length;
			_progressFunc.apply(null, args);
		}
	}
	private function onError(e:ErrorEvent):void {
		_activeLoader = null;
		removeEvent(e.currentTarget as LoaderInfo);
		if (_errorFunc != null) _errorFunc.apply(null, []);
	}
	private function onComplete(e:Event):void {
		_activeLoader = null;
		var info:LoaderInfo = e.currentTarget as LoaderInfo;
		removeEvent(info);
		if (info.content is Bitmap) {
			image[_images[_count].name] = Bitmap(info.content).bitmapData;
		} else {
			image[_images[_count].name] = info.content;
		}
		next();
	}
	private function removeEvent(target:LoaderInfo):void {
		target.removeEventListener(ProgressEvent.PROGRESS, onProgress);
		target.removeEventListener(Event.COMPLETE, onComplete);
		target.removeEventListener(IOErrorEvent.IO_ERROR, onError);
		target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
	}
}
/**
 * 簡易イージング
 */
class EaseMotion {
	private static var _sprite:Sprite = new Sprite();
	private static var _targets:Dictionary = new Dictionary();
	private static var _isInit:Boolean = false;
	public function EaseMotion() {
	}
	private static function init():void {
		if (_isInit) return;
		_isInit = true;
		_sprite.addEventListener(Event.ENTER_FRAME, onEnterFrame);
	}
	static private function onEnterFrame(e:Event):void {
		var p:ParamData, o:Object, k:String;
		for each(o in _targets) {
			for (k in o) {
				p = ParamData(o[k]);
				p.position += (p.to - p.position) * p.speed;
				var n:Number = p.position < p.to ? p.to - p.position : p.position - p.to;
				p.target[k] = p.position;
				if (n <= p.snap) {
					p.target[k] = p.to;
					if (p.completeFunc != null) {
						p.completeFunc.apply(null, p.args);
						p.completeFunc = null;
					}
					delete o[k];
				}
			}
		}
	}
	static public function goto(target:Object, param:String, to:Number):void {
		var tgt:Object = _targets[target];
		if (tgt) delete tgt[param];
		target[param] = to;
	}
	/**
	 * オブジェクトのプロパティを指定の数値に近付ける
	 * @param	target	対象オブジェクト
	 * @param	param	プロパティ名
	 * @param	to	近づける値
	 * @param	speed	速度(0超過~1以下)
	 * @param	complete	移動しきった時に呼び出す関数
	 * @param	args	関数に渡す引数
	 * @param	snap	
	 */
	static public function ease(target:Object, param:String, to:Number, speed:Number = NaN, complete:Function = null, args:Array = null, snap:Number = NaN):void {
		init();
		var p:ParamData;
		var tgt:Object = _targets[target];
		if (tgt) {
			p = tgt[param];
			if (p) {
				p.to = to;
				p.snap = (isNaN(snap))? (p.to - p.position) * 0.01 : snap;
				if (p.snap < 0) p.snap = -p.snap;
				if (!isNaN(speed)) p.speed = speed;
				p.completeFunc = complete;
				p.args = (!args)? [] : args;
				return;
			}
		} else {
			_targets[target] = { };
		}
		p = new ParamData();
		p.target = target;
		p.position = target[param];
		p.to = to;
		p.snap = (isNaN(snap))? (p.to - p.position) * 0.01 : snap;
		p.completeFunc = complete;
		p.args = (!args)? [] : args;
		if (p.snap < 0) p.snap = -p.snap;
		if (!isNaN(speed)) p.speed = speed;
		_targets[target][param] = p;
	}
	static public function setSpeed(target:Object, param:String, speed:Number):void {
		var pd:ParamData = getEase(target, param);
		if (pd) pd.speed = speed;
	}
	static public function getEase(target:Object, param:String):ParamData {
		if (!_targets[target]) return null;
		return _targets[target][param];
	}
}
/**
 * イージング用データ
 */
class ParamData {
	public var target:Object;
	public var position:Number = 0;
	public var to:Number = 0;
	public var speed:Number = 0.2;
	public var snap:Number = 0;
	public var args:Array = [];
	public var completeFunc:Function;
	public function ParamData() {
	}
}
class Painter {
	/**
	 * べた塗りスプライト生成
	 */
	static public function createColorRect(width:Number, height:Number, color:uint = 0x000000, alpha:Number = 1, x:Number = 0, y:Number = 0):Sprite {
		var sp:Sprite = new Sprite();
		sp.graphics.beginFill(color, alpha);
		sp.graphics.drawRect(0, 0, width, height);
		sp.graphics.endFill();
		sp.x = x;
		sp.y = y;
		return sp;
	}
}