Full Code of fljot/Gestouch for AI

master e2831d9dbdf6 cached
40 files
109.8 KB
30.5k tokens
1 requests
Download .txt
Repository: fljot/Gestouch
Branch: master
Commit: e2831d9dbdf6
Files: 40
Total size: 109.8 KB

Directory structure:
gitextract_orfcx0vt/

├── .gitignore
├── LICENSE
├── README.textile
├── build.template.properties
├── build.xml
├── libs/
│   └── starling.swc
├── pom.xml
├── src/
│   └── org/
│       └── gestouch/
│           ├── core/
│           │   ├── Gestouch.as
│           │   ├── GestureState.as
│           │   ├── GesturesManager.as
│           │   ├── IDisplayListAdapter.as
│           │   ├── IGestureTargetAdapter.as
│           │   ├── IInputAdapter.as
│           │   ├── ITouchHitTester.as
│           │   ├── Touch.as
│           │   ├── TouchesManager.as
│           │   └── gestouch_internal.as
│           ├── events/
│           │   └── GestureEvent.as
│           ├── extensions/
│           │   ├── native/
│           │   │   ├── DisplayObjectUtils.as
│           │   │   ├── NativeDisplayListAdapter.as
│           │   │   └── NativeTouchHitTester.as
│           │   └── starling/
│           │       ├── StarlingDisplayListAdapter.as
│           │       ├── StarlingTouchHitTester.as
│           │       └── StarlingUtils.as
│           ├── gestures/
│           │   ├── AbstractContinuousGesture.as
│           │   ├── AbstractDiscreteGesture.as
│           │   ├── Gesture.as
│           │   ├── LongPressGesture.as
│           │   ├── PanGesture.as
│           │   ├── PanGestureDirection.as
│           │   ├── RotateGesture.as
│           │   ├── SwipeGesture.as
│           │   ├── SwipeGestureDirection.as
│           │   ├── TapGesture.as
│           │   ├── TransformGesture.as
│           │   └── ZoomGesture.as
│           ├── input/
│           │   ├── NativeInputAdapter.as
│           │   └── TUIOInputAdapter.as
│           └── utils/
│               └── GestureUtils.as
└── version.properties

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
_resources/
bin/
bin-debug/
bin-release/
wiki/

.svn/

Thumbs.db
.DS_Store
build.properties

# Intellij project files
*.iml
*.ipr
*.iws
.idea/
target/

================================================
FILE: LICENSE
================================================
The MIT License

Copyright (c) 2011 the original author or authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.textile
================================================
h1. Gestouch: multitouch gesture recognition library for Flash (ActionScript) development.

Gestouch is a ActionScript (AS3) library that helps you to deal with single- and multitouch gestures for building better NUI (Natural User Interface).


h3. Why? There's already gesture support in Flash/AIR!

Yes, last versions of Flash Player and AIR runtimes have built-in touch and multitouch support, but the gestures support is very poor: only small set of gestures are supported, they depend on OS, they are not customizable in any way, only one can be processed at the same time and, finally, you are forced to use either raw TouchEvents, or gestures (@see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/ui/Multitouch.html#inputMode).
_Upd:_ With "native way" you also won't get anything out of Stage3D and of custom input like TUIO protocol. 



h3. What Gestouch does in short?

Well basically there's 3 distinctive tasks to solve.
# To provide various input. It can be native MouseEvents, TouchEvents or more complex things like custom input via TUIO protocol for your hand-made installation. So what we get here is Touches (touch points).
# To recognize gesture analyzing touches. Each type of Gesture has it's own inner algorithms that ... 
# To manage gestures conflicts. As multiple gestures may be recognized simultaneously, we need to be able to control whether it's allowed or some of them should not be recognized (fail).

Gestouch solves these 3 tasks.
I was hardly inspired by Apple team, how they solved this (quite recently to my big surprise! I thought they had it right from the beginning) in they Cocoa-touch UIKit framework. Gestouch is very similar in many ways. But I wouldn't call it "direct port" because 1) the whole architecture was implemented based just on conference videos and user documentation 2) flash platform is a different platform with own specialization, needs, etc.
So I want Gestouch to go far beyond that.

Features:
* Pretty neat architecture! Very similar to Apple's UIGestureRecognizers (Cocoa-Touch UIKit)
* Works with any display list hierarchy structures: native DisplayList (pure AS3/Flex/your UI framework), Starling or ND2D (Stage3D) and 3D libs...
* Doesn't require any additional software (may use runtime's build-in touch support)
* Works across all platforms (where Flash Player or AIR run of course) in exactly same way
* Extendable. You can write your own application-specific gestures
* Open-source and free



h3. Getting Started

All gestures dispatch (if you listen to!) GestureEvent with the next types:
GestureEvent.GESTURE_STATE_CHANGE
GestureEvent.GESTURE_IDLE
GestureEvent.GESTURE_POSSIBLE
GestureEvent.GESTURE_FAILED

Discrete gestures also dispatch:
GestureEvent.GESTURE_RECOGNIZED

Continuous gestures also dispatch:
GestureEvent.GESTURE_BEGAN
GestureEvent.GESTURE_CHANGED
GestureEvent.GESTURE_ENDED

If you use a good IDE (such as IntelliJ IDEA, FDT, FlashDevelop, Flash Builder) you should see these events in autocompletion.

Quick start:
<pre><code>var doubleTap:TapGesture = new TapGesture(myButton);
doubleTap.numTapsRequired = 2;
doubleTap.addEventListener(GestureEvent.GESTURE_RECOGNIZED, onDoubleTap);
...
private function onDoubleTap(event:GestureEvent):void
{
	// handle double tap!
}
</code></pre>
or
<pre><code>var freeTransform:TransformGesture = new TransformGesture(myImage);
freeTransform.addEventListener(GestureEvent.GESTURE_BEGAN, onFreeTransform);
freeTransform.addEventListener(GestureEvent.GESTURE_CHANGED, onFreeTransform);
...
private function onFreeTransform(event:GestureEvent):void
{
	// move, rotate, scale — all at once for better performance!
	trace(freeTransform.offsetX, freeTransform.offsetY, freeTransform.rotation, freeTransform.scale);
}
</code></pre>

* Check the "Gestouch Examples":http://github.com/fljot/GestouchExamples project for a quick jump-in
* *+Highly recommended+* to watch videos from Apple WWDC conferences as they explain all the concepts and show more or less real-life examples. @see links below
* "Introduction video":http://www.youtube.com/watch?v=NjkmB8rfQjY - my first video, currently outdated
* TODO: wiki



h3. Advanced usage: Starling, ...

Recent changes made it possible to work with "Starling":http://www.starling-framework.org display list objects as well as any other display list hierarchical structures, e.g. other Stage3D frameworks that have display objects hierarchy like "ND2D":https://github.com/nulldesign/nd2d or even 3D libraries.
In order to use Gestouch with Starling all you need to do is a bit of bootstrapping:
<pre><code>starling = new Starling(MyStarlingRootClass, stage);
/* setup & start your Starling instance here */

// Gestouch initialization step 1 of 3:
// Initialize native (default) input adapter. Needed for non-DisplayList usage.
Gestouch.inputAdapter ||= new NativeInputAdapter(stage);

// Gestouch initialization step 2 of 3:
// Register instance of StarlingDisplayListAdapter to be used for objects of type starling.display.DisplayObject.
// What it does: helps to build hierarchy (chain of parents) for any Starling display object and
// acts as a adapter for gesture target to provide strong-typed access to methods like globalToLocal() and contains().
Gestouch.addDisplayListAdapter(starling.display.DisplayObject, new StarlingDisplayListAdapter());

// Gestouch initialization step 3 of 3:
// Initialize and register StarlingTouchHitTester.
// What it does: finds appropriate target for the new touches (uses Starling Stage#hitTest() method)
// What does "-1" mean: priority for this hit-tester. Since Stage3D layer sits behind native DisplayList
// we give it lower priority in the sense of interactivity.
Gestouch.addTouchHitTester(new StarlingTouchHitTester(starling), -1);
// NB! Use Gestouch#removeTouchHitTester() method if you manage multiple Starling instances during
// your application lifetime.
</code></pre>

Now you can register gestures as usual:
<pre><code>var tap:TapGesture = new TapGesture(starlingSprite);</code></pre>



h3. Roadmap, TODOs

* "Massive gestures" & Clusters. For bigger form-factor multitouch usage, when gestures must be a bit less about separate fingers but rather touch clusters (massive multitouch) 
* -Simulator (for testing multitouch gestures without special devices)- With new architecture it must be relatively easy to create SimulatorInputAdapter
* Chained gestures concept? To transfer touches from one gesture to another. Example: press/hold for circular menu, then drag it around.
* 3-fingers (3D) gestures (two fingers still, one moving)



h3. News

* "Follow me on Twitter":http://twitter.com/fljot for latest updates
* Don't forget about "issues":https://github.com/fljot/Gestouch/issues section as a good platform for discussions.



h3. Contribution, Donations

Contribute, share. Found it useful, nothing to add? Hire me for some project.



h3. Links

* "Gestouch Examples":http://github.com/fljot/GestouchExamples

* "Apple WWDC 2011: Making the Most of Multi-Touch on iOS":https://developer.apple.com/videos/wwdc/2011/?id=118
* "Apple WWDC 2010: Simplifying Touch Event Handling with Gesture Recognizers":https://developer.apple.com/videos/wwdc/2010/?id=120
* "Apple WWDC 2010: Advanced Gesture Recognition":https://developer.apple.com/videos/wwdc/2010/?id=121
* "Event Handling Guide for iOS":https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/
* "UIGestureRecognizer Class Reference":https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/

* "TUIO":http://www.tuio.org


h2. License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================
FILE: build.template.properties
================================================
# IMPORTANT Change to your local system paths before using ANT

# Flex SDK related properties
FLEX_HOME = path-to-flex-4.5-SDK
flexSDK.dir = ${FLEX_HOME}

project.name = ${ant.project.name}

# Project-path relative properties
src.dir = ${basedir}/src
libs.dir = ${basedir}/libs
binrelease.dir = ${basedir}/bin
asdoc.dir = ${binrelease.dir}/docs

================================================
FILE: build.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<project name="Gestouch" default="release" basedir=".">

	<!-- Thanks to Robotlegs project for the tasks -->

	<!-- Load properties -->
	<property file="build.properties" />
	<property file="version.properties" />


	<target name="release" description="" depends="compile.swc, asdoc, fat.swc">
		<!-- TODO: tests? -->
	</target>

	<target name="compile.swc">
		<echo>[compile] Compiling release SWC</echo>
		<java jar="${flexSDK.dir}/lib/compc.jar" dir="${flexSDK.dir}/frameworks" fork="true" failonerror="true">
			<!-- Build our SWC with a versioned name. -->
			<arg value="-output=${binrelease.dir}/gestouch-${project.version}.swc"/>
			<!-- We want all the org package classes in the SWC being built. -->
			<arg value="-include-sources=${src.dir}"/>
			<!-- Exclude Flex Framework classes (some mx events comes from binding). -->
			<arg value="-external-library-path+=${flexSDK.dir}/frameworks/libs"/>
			<!-- Exclude any external classes (such as Starling framework classes) -->
			<arg value="-external-library-path+=${libs.dir}"/>
			<!-- Keep the metatags (Apparat?). -->
			<arg value="-keep-as3-metadata+=Abstract"/>
			<!-- Boolean mosh pit! -->
			<arg value="-incremental=false"/>
			<arg value="-static-link-runtime-shared-libraries=true"/>
			<arg value="-verbose-stacktraces=true"/>
			<arg value="-headless-server=true"/>
		</java>
		<echo>[compile] Release SWC gestouch-${project.version}.swc created successfully</echo>
	</target>

	<target name="asdoc">
		<echo>[asdoc] Generating ASDOC documentation</echo>
		<tstamp>
			<format property="docgen.time" pattern="MM/dd/yyyy hh:mm aa" unit="hour"/>
		</tstamp>
		<delete includeemptydirs="true" failonerror="false">
			<fileset dir="${asdoc.dir}" defaultexcludes="false">
				<include name="**/*" />
			</fileset>
		</delete>
		<java jar="${FLEX_HOME}/lib/asdoc.jar" dir="${FLEX_HOME}/frameworks" fork="true" failonerror="true">
			<arg line="-source-path ${src.dir}"/>
			<arg line="-external-library-path+=${libs.dir}"/>
			<arg line="-doc-sources ${src.dir}"/>
			<arg line="-output ${asdoc.dir}"/>
			<arg value="-keep-xml=true"/>
			<arg value="-skip-xsl=true"/>
			<arg line="-window-title 'Gestouch ${project.version}'"/>
			<arg line="-main-title 'Gestouch ${project.version}'"/>
			<arg line="-footer 'Gestouch - http://github.com/fljot/Gestouch/ - Documentation generated at: ${docgen.time}'"/>
			<arg line="-package org.gestouch.gestures 'Gestures'"/>
			<arg line="-package org.gestouch.events 'Gesture events, generated by gesture classes and dispatched by display objects.'"/>
		</java>
		<echo>[asdoc] ASDOC documentation generated successfully</echo>
	</target>
	
	<target name="fat.swc">
		<echo>[fat.swc] Adding documentation to swc</echo>
		<!-- updates swc with asdoc xml -->
		<zip destfile="${binrelease.dir}/gestouch-${project.version}.swc" update="true">
			<zipfileset dir="${asdoc.dir}/tempdita" prefix="docs">
				<include name="*.*" />
				<exclude name="ASDoc_Config.xml" />
				<exclude name="overviews.xml" />
			</zipfileset>
		</zip>
		<echo>[fat.swc] Documentation added to swc successfully</echo>
	</target>

</project>

================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.gestouch</groupId>
	<artifactId>gestouch</artifactId>
	<packaging>swc</packaging>
	<version>0.4.7</version>

	<properties>
		<flexmojos.version>5.0-beta</flexmojos.version>
		<flex.sdk.version>4.6.b.23201</flex.sdk.version>
	</properties>

	<!-- Continuous Integration build hosted by CloudBees -->
	<ciManagement>
		<system>jenkins</system>
		<url>https://fljot.ci.cloudbees.com/job/Gestouch/</url>
	</ciManagement>

	<issueManagement>
		<system>github</system>
		<url>https://github.com/fljot/Gestouch/issues</url>
	</issueManagement>

	<!-- GitHub Software Configuration Management -->
	<scm>
		<url>https://github.com/fljot/Gestouch</url>
		<connection>scm:git:ssh://git@github.com:fljot/Gestouch.git</connection>
		<developerConnection>scm:git:ssh://git@github.com:fljot/Gestouch.git</developerConnection>
	</scm>

	<build>
		<sourceDirectory>src/</sourceDirectory>

		<plugins>
			<plugin>
				<groupId>net.flexmojos.oss</groupId>
				<artifactId>flexmojos-maven-plugin</artifactId>
				<version>${flexmojos.version}</version>
				<extensions>true</extensions>
				<configuration>
					<debug>true</debug>
					<storepass/>
					<skipTests>true</skipTests>
					<skipTest>true</skipTest>
					<debug>true</debug>
					<verboseStacktraces>true</verboseStacktraces>
					<!--
					NB! SWC is compiled in debug mode with verbose stacktraces
					for better development process.
					It's okay and will not affect application performance
					UNLESS you are using this library as RSL.
					-->
					<keepAs3Metadatas>
						<!-- potentially for Apparat. probably useless -->
						<keepAs3Metadata>Abstract</keepAs3Metadata>
					</keepAs3Metadatas>
				</configuration>
				<dependencies>
					<dependency>
						<groupId>com.adobe.flex</groupId>
						<artifactId>compiler</artifactId>
						<version>${flex.sdk.version}</version>
						<type>pom</type>
					</dependency>
				</dependencies>
			</plugin>

			<plugin>
				<groupId>net.flexmojos.oss</groupId>
				<artifactId>flexmojos-maven-plugin</artifactId>
				<executions>
					<execution>
						<phase>prepare-package</phase>
						<goals>
							<goal>asdoc</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<storepass/>
					<keepXml>true</keepXml>
					<skipXsl>true</skipXsl>
					<windowTitle>Gestouch ${project.version}</windowTitle>
					<mainTitle>Gestouch ${project.version}</mainTitle>
					<footer>Gestouch - http://github.com/fljot/Gestouch/ - Documentation generated at: ${maven.build.timestamp}</footer>
				</configuration>
			</plugin>

			<plugin>
				<artifactId>maven-antrun-plugin</artifactId>
				<version>1.7</version>
				<executions>
					<execution>
						<id>fail-to-deploy-in-develop</id>
						<phase>package</phase>
						<configuration>
							<target>
								<zip destfile="${project.build.directory}/${project.artifactId}-${project.version}.swc" update="true">
									<zipfileset dir="${project.build.directory}/asdoc/tempdita" prefix="docs">
										<include name="*.*"/>
										<exclude name="ASDoc_Config.xml"/>
										<exclude name="overviews.xml"/>
									</zipfileset>
								</zip>
							</target>
						</configuration>
						<goals>
							<goal>run</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

			<!-- I don't think I need this as I put documentation into original SWC -->
			<!--
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>build-helper-maven-plugin</artifactId>
				<version>1.3</version>
				<executions>
					<execution>
						<id>attach-artifacts</id>
						<phase>package</phase>
						<goals>
							<goal>attach-artifact</goal>
						</goals>
						<configuration>
							<artifacts>
								<artifact>
									<file>${project.build.directory}/${project.artifactId}-fat-${project.version}.swc</file>
									<type>swc</type>
									<classifier>fat</classifier>
								</artifact>
							</artifacts>
						</configuration>
					</execution>
				</executions>
			</plugin>
			-->
		</plugins>

	</build>

	<reporting>
		<plugins>
			<plugin>
				<groupId>org.sonatype.flexmojos</groupId>
				<artifactId>flexmojos-maven-plugin</artifactId>
				<version>${flexmojos.version}</version>
				<reportSets>
					<reportSet>
						<id>flex-reports</id>
						<reports>
							<report>asdoc-report</report>
						</reports>
					</reportSet>
				</reportSets>
			</plugin>
		</plugins>
	</reporting>

	<dependencies>
		<dependency>
			<groupId>com.adobe.flex</groupId>
			<artifactId>compiler</artifactId>
			<version>${flex.sdk.version}</version>
			<type>pom</type>
		</dependency>

		<dependency>
			<groupId>com.adobe.flex.framework</groupId>
			<artifactId>flex-framework</artifactId>
			<version>${flex.sdk.version}</version>
			<type>pom</type>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>com.gamua</groupId>
			<artifactId>starling-framework</artifactId>
			<version>[1.1,]</version>
			<type>swc</type>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>flex-mojos-repository</id>
			<url>http://repository.sonatype.org/content/groups/flexgroup</url>
		</repository>

		<repository>
			<id>starling-repository</id>
			<url>http://repository-trylogic.forge.cloudbees.com/snapshot</url>
			<releases>
				<enabled>false</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>flex-mojos-plugin-repository</id>
			<url>http://repository.sonatype.org/content/groups/flexgroup</url>
			<releases>
				<enabled>true</enabled>
				<updatePolicy>never</updatePolicy>
			</releases>
			<snapshots>
				<enabled>false</enabled>
				<updatePolicy>never</updatePolicy>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

	<developers>
		<developer>
			<name>Pavel fljōt Pevnitskiy</name>
			<email>pavel.fljot at gmail.com</email>
		</developer>
	</developers>
</project>


================================================
FILE: src/org/gestouch/core/Gestouch.as
================================================
package org.gestouch.core
{
	import flash.display.DisplayObject;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;

	import org.gestouch.extensions.native.NativeDisplayListAdapter;


	/**
	 * @author Pavel fljot
	 */
	final public class Gestouch
	{
		private static const _displayListAdaptersMap:Dictionary = new Dictionary();
		
		
		/** @private */
		private static var _inputAdapter:IInputAdapter;
		
		/**
		 * 
		 */
		public static function get inputAdapter():IInputAdapter
		{
			return _inputAdapter;
		}
		public static function set inputAdapter(value:IInputAdapter):void
		{
			if (_inputAdapter == value)
				return;
			
			_inputAdapter = value;
			if (inputAdapter)
			{
				inputAdapter.touchesManager = touchesManager;
				inputAdapter.init();
			}
		}
		
		
		private static var _touchesManager:TouchesManager;
		/**
		 * 
		 */
		public static function get touchesManager():TouchesManager
		{
			return _touchesManager ||= new TouchesManager(gesturesManager);
		}
		
		
		private static var _gesturesManager:GesturesManager;
		public static function get gesturesManager():GesturesManager
		{
			return _gesturesManager ||= new GesturesManager();
		}
		
		
		public static function addDisplayListAdapter(targetClass:Class, adapter:IDisplayListAdapter):void
		{
			if (!targetClass || !adapter)
			{
				throw new Error("Argument error: both arguments required.");
			}
			
			_displayListAdaptersMap[targetClass] = adapter;
		}
		
		
		public static function addTouchHitTester(hitTester:ITouchHitTester, priority:int = 0):void
		{
			touchesManager.gestouch_internal::addTouchHitTester(hitTester, priority);
		}
		
		
		public static function removeTouchHitTester(hitTester:ITouchHitTester):void
		{
			touchesManager.gestouch_internal::removeInputAdapter(hitTester);
		}
		
		
//		public static function getTouches(target:Object = null):Array
//		{
//			return touchesManager.getTouches(target);
//		}
		
		gestouch_internal static function createGestureTargetAdapter(target:Object):IDisplayListAdapter
		{
			var adapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(target);

			// Lazy add display list adapter for flash.display::DisplayObject
			if (!adapter && target is flash.display.DisplayObject)
			{
				adapter = new NativeDisplayListAdapter();
				Gestouch.addDisplayListAdapter(DisplayObject, adapter);
			}

			if (adapter)
			{
				return new (adapter.reflect())(target);
			}
			
			throw new Error("Cannot create adapter for target " + target + " of type " + getQualifiedClassName(target) + ".");
		}
		
		
		gestouch_internal static function getDisplayListAdapter(object:Object):IDisplayListAdapter
		{
			for (var key:Object in _displayListAdaptersMap)
			{
				var targetClass:Class = key as Class;
				if (object is targetClass)
				{
					return _displayListAdaptersMap[key] as IDisplayListAdapter;
				}
			}
			
			return null;
		}
	}
}


================================================
FILE: src/org/gestouch/core/GestureState.as
================================================
package org.gestouch.core
{
	import flash.utils.Dictionary;
	import flash.errors.IllegalOperationError;


	/**
	 * @author Pavel fljot
	 */
	final public class GestureState
	{
		public static const POSSIBLE:GestureState = new GestureState("POSSIBLE");
		public static const RECOGNIZED:GestureState = new GestureState("RECOGNIZED", true);
		public static const BEGAN:GestureState = new GestureState("BEGAN");
		public static const CHANGED:GestureState = new GestureState("CHANGED");
		public static const ENDED:GestureState = new GestureState("ENDED", true);
		public static const CANCELLED:GestureState = new GestureState("CANCELLED", true);
		public static const FAILED:GestureState = new GestureState("FAILED", true);
		
		private static var allStatesInitialized:Boolean;
		
		
		private var name:String;
		private var eventType:String;
		private var validTransitionStateMap:Dictionary = new Dictionary();
		
		{
			_initClass();
		}
		
		
		public function GestureState(name:String, isEndState:Boolean = false)
		{
			if (allStatesInitialized)
			{
				throw new IllegalOperationError("You cannot create gesture states." +
				"Use predefined constats like GestureState.RECOGNIZED");
			}
			
			this.name = "GestureState." + name;
			this.eventType = "gesture" + name.charAt(0).toUpperCase() + name.substr(1).toLowerCase();
			this._isEndState = isEndState;
		}
		
		
		private static function _initClass():void
		{
			POSSIBLE.setValidNextStates(RECOGNIZED, BEGAN, FAILED);
			RECOGNIZED.setValidNextStates(POSSIBLE);
			BEGAN.setValidNextStates(CHANGED, ENDED, CANCELLED);
			CHANGED.setValidNextStates(CHANGED, ENDED, CANCELLED);
			ENDED.setValidNextStates(POSSIBLE);
			FAILED.setValidNextStates(POSSIBLE);
			CANCELLED.setValidNextStates(POSSIBLE);
			
			allStatesInitialized = true;
		}
		
		
		public function toString():String
		{
			return name;
		}
		
		
		private function setValidNextStates(...states):void
		{
			for each (var state:GestureState in states)
			{
				validTransitionStateMap[state] = true;
			}
		}
		
		
		gestouch_internal function toEventType():String
		{
			return eventType;
		}
		
		
		gestouch_internal function canTransitionTo(state:GestureState):Boolean
		{
			return (state in validTransitionStateMap);
		}
		
		
		private var _isEndState:Boolean = false;
		gestouch_internal function get isEndState():Boolean
		{
			return _isEndState;
		}
	}
}


================================================
FILE: src/org/gestouch/core/GesturesManager.as
================================================
package org.gestouch.core
{
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;

	import org.gestouch.extensions.native.NativeTouchHitTester;
	import org.gestouch.gestures.Gesture;
	import org.gestouch.input.NativeInputAdapter;


	/**
	 * @author Pavel fljot
	 */
	public class GesturesManager
	{
		protected const _frameTickerShape:Shape = new Shape();
		protected var _gesturesMap:Dictionary = new Dictionary(true);
		protected var _gesturesForTouchMap:Dictionary = new Dictionary();
		protected var _gesturesForTargetMap:Dictionary = new Dictionary(true);
		protected var _dirtyGesturesCount:uint = 0;
		protected var _dirtyGesturesMap:Dictionary = new Dictionary(true);
		protected var _stage:Stage;
		
		use namespace gestouch_internal;
		
		
		public function GesturesManager()
		{
			
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		
		protected function onStageAvailable(stage:Stage):void
		{
			_stage = stage;
			
			Gestouch.inputAdapter ||= new NativeInputAdapter(stage);
			Gestouch.addTouchHitTester(new NativeTouchHitTester(stage));
		}
		
		
		protected function resetDirtyGestures():void
		{
			for (var gesture:Object in _dirtyGesturesMap)
			{
				(gesture as Gesture).reset();
			}
			_dirtyGesturesCount = 0;
			_dirtyGesturesMap = new Dictionary(true);
			_frameTickerShape.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		
		gestouch_internal function addGesture(gesture:Gesture):void
		{
			if (!gesture)
			{
				throw new ArgumentError("Argument 'gesture' must be not null.");
			}
			
			const target:Object = gesture.target;
			if (!target)
			{
				throw new IllegalOperationError("Gesture must have target.");
			}
			
			var targetGestures:Vector.<Gesture> = _gesturesForTargetMap[target] as Vector.<Gesture>;
			if (targetGestures)
			{
				if (targetGestures.indexOf(gesture) == -1)
				{
					targetGestures.push(gesture);
				}
			}
			else
			{
				targetGestures = _gesturesForTargetMap[target] = new Vector.<Gesture>();
				targetGestures[0] = gesture;
			}
			
			
			_gesturesMap[gesture] = true;
			
			if (!_stage)
			{
				var targetAsDO:DisplayObject = target as DisplayObject;
				if (targetAsDO)
				{
					if (targetAsDO.stage)
					{
						onStageAvailable(targetAsDO.stage);
					}
					else
					{
						targetAsDO.addEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler, false,0, true);
					}
				}
			}
		}
		
		
		gestouch_internal function removeGesture(gesture:Gesture):void
		{
			if (!gesture)
			{
				throw new ArgumentError("Argument 'gesture' must be not null.");
			}
			
			
			var target:Object = gesture.target;
			// check for target because it could be already GC-ed (since target reference is weak)
			if (target)
			{
				var targetGestures:Vector.<Gesture> = _gesturesForTargetMap[target] as Vector.<Gesture>;
				if (targetGestures.length > 1)
				{
					targetGestures.splice(targetGestures.indexOf(gesture), 1);
				}
				else
				{
					delete _gesturesForTargetMap[target];
					if (target is IEventDispatcher)
					{
						(target as IEventDispatcher).removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
					}
				}
			}
			
			delete _gesturesMap[gesture];
			
			gesture.reset();
		}
		
		
		gestouch_internal function scheduleGestureStateReset(gesture:Gesture):void
		{
			if (!_dirtyGesturesMap[gesture])
			{
				_dirtyGesturesMap[gesture] = true;
				_dirtyGesturesCount++;
				_frameTickerShape.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			}
		}
		
		
		gestouch_internal function onGestureRecognized(gesture:Gesture):void
		{
			const target:Object = gesture.target;
			
			for (var key:Object in _gesturesMap)
			{
				var otherGesture:Gesture = key as Gesture;
				var otherTarget:Object = otherGesture.target;
				
				// conditions for otherGesture "own properties"
				if (otherGesture != gesture &&
					target && otherTarget &&//in case GC worked half way through
					otherGesture.enabled &&
					otherGesture.state == GestureState.POSSIBLE)
				{
					if (otherTarget == target ||
						gesture.targetAdapter.contains(otherTarget) ||
						otherGesture.targetAdapter.contains(target)
						)
					{
						// conditions for gestures relations
						if (gesture.canPreventGesture(otherGesture) &&
							otherGesture.canBePreventedByGesture(gesture) &&
							(gesture.gesturesShouldRecognizeSimultaneouslyCallback == null ||
							 !gesture.gesturesShouldRecognizeSimultaneouslyCallback(gesture, otherGesture)) &&
							(otherGesture.gesturesShouldRecognizeSimultaneouslyCallback == null ||
							 !otherGesture.gesturesShouldRecognizeSimultaneouslyCallback(otherGesture, gesture)))
						{
							otherGesture.setState_internal(GestureState.FAILED);
						}
					}
				}
			}
		}
		
		
		gestouch_internal function onTouchBegin(touch:Touch):void
		{
			var gesture:Gesture;
			var i:uint;
			
			// This vector will contain active gestures for specific touch during all touch session.
			var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
			if (!gesturesForTouch)
			{
				gesturesForTouch = new Vector.<Gesture>();
				_gesturesForTouchMap[touch] = gesturesForTouch;
			}
			else
			{
				// touch object may be pooled in the future
				gesturesForTouch.length = 0;
			}
			
			var target:Object = touch.target;
			const displayListAdapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(target);
			if (!displayListAdapter)
			{
				throw new Error("Display list adapter not found for target of type '" + getQualifiedClassName(target) + "'.");
			}
			const hierarchy:Vector.<Object> = displayListAdapter.getHierarchy(target);
			const hierarchyLength:uint = hierarchy.length;
			if (hierarchyLength == 0)
			{
				throw new Error("No hierarchy build for target '" + target +"'. Something is wrong with that IDisplayListAdapter.");
			}
			if (_stage && !(hierarchy[hierarchyLength - 1] is Stage))
			{
				// Looks like some non-native (non DisplayList) hierarchy
				// but we must always handle gestures with Stage target
				// since Stage is anyway the top-most parent
				hierarchy[hierarchyLength] = _stage;
			}
			
			// Create a sorted(!) list of gestures which are interested in this touch.
			// Sorting priority: deeper target has higher priority, recently added gesture has higher priority.
			var gesturesForTarget:Vector.<Gesture>;
			for each (target in hierarchy)
			{
				gesturesForTarget = _gesturesForTargetMap[target] as Vector.<Gesture>;
				if (gesturesForTarget)
				{
					i = gesturesForTarget.length;
					while (i-- > 0)
					{
						gesture = gesturesForTarget[i];
						if (gesture.enabled &&
							(gesture.gestureShouldReceiveTouchCallback == null ||
							 gesture.gestureShouldReceiveTouchCallback(gesture, touch)))
						{
							//TODO: optimize performance! decide between unshift() vs [i++] = gesture + reverse()
							gesturesForTouch.unshift(gesture);
						}
					}
				}
			}
			
			// Then we populate them with this touch and event.
			// They might start tracking this touch or ignore it (via Gesture#ignoreTouch())
			i = gesturesForTouch.length;
			while (i-- > 0)
			{
				gesture = gesturesForTouch[i];
				// Check for state because previous (i+1) gesture may already abort current (i) one
				if (!_dirtyGesturesMap[gesture])
				{
					gesture.touchBeginHandler(touch);
				}
				else
				{
					gesturesForTouch.splice(i, 1);
				}
			}
		}
		
		
		gestouch_internal function onTouchMove(touch:Touch):void
		{
			var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
			var gesture:Gesture;
			var i:uint = gesturesForTouch.length;
			while (i-- > 0)
			{
				gesture = gesturesForTouch[i];
				
				if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
				{
					gesture.touchMoveHandler(touch);
				}
				else
				{
					// gesture is no more interested in this touch (e.g. ignoreTouch was called)
					gesturesForTouch.splice(i, 1);
				}
			}
		}
		
		
		gestouch_internal function onTouchEnd(touch:Touch):void
		{
			var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
			var gesture:Gesture;
			var i:uint = gesturesForTouch.length;
			while (i-- > 0)
			{
				gesture = gesturesForTouch[i];
				
				if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
				{
					gesture.touchEndHandler(touch);
				}
			}
			
			gesturesForTouch.length = 0;// release for GC
			
			delete _gesturesForTouchMap[touch];//TODO: remove this once Touch objects are pooled
		}
		
		
		gestouch_internal function onTouchCancel(touch:Touch):void
		{
			var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
			var gesture:Gesture;
			var i:uint = gesturesForTouch.length;
			while (i-- > 0)
			{
				gesture = gesturesForTouch[i];
				
				if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
				{
					gesture.touchCancelHandler(touch);
				}
			}
			
			gesturesForTouch.length = 0;// release for GC
			
			delete _gesturesForTouchMap[touch];//TODO: remove this once Touch objects are pooled
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		protected function gestureTarget_addedToStageHandler(event:Event):void
		{
			var target:DisplayObject = event.target as DisplayObject;
			target.removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
			if (!_stage)
			{
				onStageAvailable(target.stage);
			}
		}
		
		
		private function enterFrameHandler(event:Event):void
		{
			resetDirtyGestures();
		}
	}
}

================================================
FILE: src/org/gestouch/core/IDisplayListAdapter.as
================================================
package org.gestouch.core
{
	/**
	 * @author Pavel fljot
	 */
	public interface IDisplayListAdapter extends IGestureTargetAdapter
	{
		function getHierarchy(target:Object):Vector.<Object>;
		
		function reflect():Class;
	}
}

================================================
FILE: src/org/gestouch/core/IGestureTargetAdapter.as
================================================
package org.gestouch.core
{
	/**
	 * @author Pavel fljot
	 */
	public interface IGestureTargetAdapter
	{
		function get target():Object;
		
		function contains(object:Object):Boolean;
	}
}

================================================
FILE: src/org/gestouch/core/IInputAdapter.as
================================================
package org.gestouch.core
{
	/**
	 * @author Pavel fljot
	 */
	public interface IInputAdapter
	{
		/**
		 * @private
		 */
		function set touchesManager(value:TouchesManager):void;
		
		/**
		 * Called when input adapter is set.
		 */
		function init():void;
	}
}


================================================
FILE: src/org/gestouch/core/ITouchHitTester.as
================================================
package org.gestouch.core
{
	import flash.geom.Point;


	/**
	 * @author Pavel fljot
	 */
	public interface ITouchHitTester
	{
		function hitTest(point:Point, possibleTarget:Object = null):Object;
	}
}


================================================
FILE: src/org/gestouch/core/Touch.as
================================================
package org.gestouch.core
{
	import flash.geom.Point;


	/**
	 * TODO:
	 * - maybe add "phase" (began, moved, stationary, ended)?
	 * 
	 * @author Pavel fljot
	 */
	public class Touch
	{
		/**
		 * Touch point ID.
		 */
		public var id:uint;
		/**
		 * The original event target for this touch (touch began with).
		 */
		public var target:Object;
		
		public var sizeX:Number;
		public var sizeY:Number;
		public var pressure:Number;
		
//		public var lastMove:Point;

		use namespace gestouch_internal;
		
		
		public function Touch(id:uint = 0)
		{
			this.id = id;
		}
		
		
		protected var _location:Point;
		public function get location():Point
		{
			return _location.clone();
		}
		gestouch_internal function setLocation(x:Number, y:Number, time:uint):void
		{
			_location = new Point(x, y);
			_beginLocation = _location.clone();
			_previousLocation = _location.clone();
			
			_time = time;
			_beginTime = time;
		}
		gestouch_internal function updateLocation(x:Number, y:Number, time:uint):Boolean
		{
			if (_location)
			{
				if (_location.x == x && _location.y == y)
					return false;
				
				_previousLocation.x = _location.x;
				_previousLocation.y = _location.y;
				_location.x = x;
				_location.y = y;
				_time = time;
			}
			else
			{
				setLocation(x, y, time);
			}
			
			return true;
		}
		
		
		protected var _previousLocation:Point;
		public function get previousLocation():Point
		{
			return _previousLocation.clone();
		}
		
		
		protected var _beginLocation:Point;
		public function get beginLocation():Point
		{
			return _beginLocation.clone();
		}
		
		
		public function get locationOffset():Point
		{
			return _location.subtract(_beginLocation);
		}
		
		
		protected var _time:uint;
		public function get time():uint
		{
			return _time;
		}
		gestouch_internal function setTime(value:uint):void
		{
			_time = value;
		}
		
		
		protected var _beginTime:uint;
		public function get beginTime():uint
		{
			return _beginTime;
		}
		gestouch_internal function setBeginTime(value:uint):void
		{
			_beginTime = value;
		}
		
		
		public function clone():Touch
		{
			var touch:Touch = new Touch(id);
			touch._location = _location.clone();
			touch._beginLocation = _beginLocation.clone();
			touch.target = target;
			touch.sizeX = sizeX;
			touch.sizeY = sizeY;
			touch.pressure = pressure;
			touch._time = _time;
			touch._beginTime = _beginTime;
			
			return touch;
		}
		
		
		public function toString():String
		{
			return "Touch [id: " + id + ", location: " + location + ", ...]";
		}
	}
}

================================================
FILE: src/org/gestouch/core/TouchesManager.as
================================================
package org.gestouch.core
{
	import flash.display.Stage;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;


	/**
	 * @author Pavel fljot
	 */
	public class TouchesManager
	{
		protected var _gesturesManager:GesturesManager;
		protected var _touchesMap:Object = {};
		protected var _hitTesters:Vector.<ITouchHitTester> = new Vector.<ITouchHitTester>();
		protected var _hitTesterPrioritiesMap:Dictionary = new Dictionary(true);
		
		use namespace gestouch_internal;
		
		
		public function TouchesManager(gesturesManager:GesturesManager)
		{
			_gesturesManager = gesturesManager;
		}
		
		
		protected var _activeTouchesCount:uint;
		public function get activeTouchesCount():uint
		{
			return _activeTouchesCount;
		}
		
		
		public function getTouches(target:Object = null):Array
		{
			const touches:Array = [];
			if (!target || target is Stage)
			{
				// return all touches
				var i:uint = 0;
				for each (var touch:Touch in _touchesMap)
				{
					touches[i++] = touch;
				}
			}
			else
			{
				//TODO
			}
			
			return touches;
		}
		
		
		gestouch_internal function addTouchHitTester(touchHitTester:ITouchHitTester, priority:int = 0):void
		{
			if (!touchHitTester)
			{
				throw new ArgumentError("Argument must be non null.");
			}
			
			if (_hitTesters.indexOf(touchHitTester) == -1)
			{
				_hitTesters.push(touchHitTester);
			}
			
			_hitTesterPrioritiesMap[touchHitTester] = priority;
			// Sort hit testers using their priorities
			_hitTesters.sort(hitTestersSorter);
		}
		
		
		gestouch_internal function removeInputAdapter(touchHitTester:ITouchHitTester):void
		{
			if (!touchHitTester)
			{
				throw new ArgumentError("Argument must be non null.");
			}
			
			var index:int = _hitTesters.indexOf(touchHitTester);
			if (index == -1)
			{
				throw new Error("This touchHitTester is not registered.");
			}
			
			_hitTesters.splice(index, 1);
			delete _hitTesterPrioritiesMap[touchHitTester];
		}
		
		
		gestouch_internal function onTouchBegin(touchID:uint, x:Number, y:Number, possibleTarget:Object = null):Boolean
		{
			if (touchID in _touchesMap)
				return false;// touch with specified ID is already registered and being tracked
			
			const location:Point = new Point(x, y);
			
			for each (var registeredTouch:Touch in _touchesMap)
			{
				// Check if touch at the same location exists.
				// In case we listen to both TouchEvents and MouseEvents, one of them will come first
				// (right now looks like MouseEvent dispatched first, but who know what Adobe will
				// do tomorrow). This check helps to filter out the one comes after.
				
				// NB! According to the tests with some IR multitouch frame and Windows computer
				// TouchEvent comes first, but the following MouseEvent has slightly offset location
				// (1px both axis). That is why Point#distance() used instead of Point#equals()
				
				if (Point.distance(registeredTouch.location, location) < 2)
					return false;
			}
			
			const touch:Touch = createTouch();
			touch.id = touchID;
			
			var target:Object;
			var altTarget:Object;
			for each (var hitTester:ITouchHitTester in _hitTesters)
			{
				target = hitTester.hitTest(location, possibleTarget);
				if (target)
				{
					if ((target is Stage))
					{
						// NB! Target is flash.display::Stage is a special case. If it is true, we want
						// to give a try to a lower-priority (Stage3D) hit-testers. 
						altTarget = target;
						continue;
					}
					else
					{
						// We found a target.
						break;
					}
				}
			}
			if (!target && !altTarget)
			{
				throw new Error("Not touch target found (hit test)." +
				"Something is wrong, at least flash.display::Stage should be found." +
				"See Gestouch#addTouchHitTester() and Gestouch#inputAdapter.");
			}
			
			touch.target = target || altTarget;
			touch.setLocation(x, y, getTimer());
			
			_touchesMap[touchID] = touch;
			_activeTouchesCount++;
			
			_gesturesManager.onTouchBegin(touch);
			
			return true;
		}
		
		
		gestouch_internal function onTouchMove(touchID:uint, x:Number, y:Number):void
		{
			const touch:Touch = _touchesMap[touchID] as Touch;
			if (!touch)
				return;// touch with specified ID isn't registered
			
			if (touch.updateLocation(x, y, getTimer()))
			{
				// NB! It appeared that native TOUCH_MOVE event is dispatched also when
				// the location is the same, but size has changed. We are only interested
				// in location at the moment, so we shall ignore irrelevant calls.
				
				_gesturesManager.onTouchMove(touch);
			}
		}
		
		
		gestouch_internal function onTouchEnd(touchID:uint, x:Number, y:Number):void
		{
			const touch:Touch = _touchesMap[touchID] as Touch;
			if (!touch)
				return;// touch with specified ID isn't registered
			
			touch.updateLocation(x, y, getTimer());
			
			delete _touchesMap[touchID];
			_activeTouchesCount--;
			
			_gesturesManager.onTouchEnd(touch);
			
			touch.target = null;
		}
		
		
		gestouch_internal function onTouchCancel(touchID:uint, x:Number, y:Number):void
		{
			const touch:Touch = _touchesMap[touchID] as Touch;
			if (!touch)
				return;// touch with specified ID isn't registered
			
			touch.updateLocation(x, y, getTimer());
			
			delete _touchesMap[touchID];
			_activeTouchesCount--;
			
			_gesturesManager.onTouchCancel(touch);
			
			touch.target = null;
		}
		
		
		protected function createTouch():Touch
		{
			//TODO: pool
			return new Touch();
		}
		
		
		/**
		 * Sorts from higher priority to lower. Items with the same priority keep the order
		 * of addition, e.g.:
		 * add(a), add(b), add(c, -1), add(d, 1) will be ordered to
		 * d, a, b, c
		 */
		protected function hitTestersSorter(x:ITouchHitTester, y:ITouchHitTester):Number
		{
			const d:int = int(_hitTesterPrioritiesMap[x]) - int(_hitTesterPrioritiesMap[y]);
			if (d > 0)
				return -1;
			else if (d < 0)
				return 1;
			
			return _hitTesters.indexOf(x) > _hitTesters.indexOf(y) ? 1 : -1;
		}
	}
}


================================================
FILE: src/org/gestouch/core/gestouch_internal.as
================================================
package org.gestouch.core
{
	/**
	 * @author Pavel fljot
	 */
	public namespace gestouch_internal = "org.gestouch.core::gestouch_internal";
}

================================================
FILE: src/org/gestouch/events/GestureEvent.as
================================================
package org.gestouch.events
{
	import org.gestouch.core.GestureState;

	import flash.events.Event;


	/**
	 * @author Pavel fljot
	 */
	public class GestureEvent extends Event
	{
		public static const GESTURE_POSSIBLE:String = "gesturePossible";
		public static const GESTURE_RECOGNIZED:String = "gestureRecognized";
		public static const GESTURE_BEGAN:String = "gestureBegan";
		public static const GESTURE_CHANGED:String = "gestureChanged";
		public static const GESTURE_ENDED:String = "gestureEnded";
		public static const GESTURE_CANCELLED:String = "gestureCancelled";
		public static const GESTURE_FAILED:String = "gestureFailed";
		
		public static const GESTURE_STATE_CHANGE:String = "gestureStateChange";
		
		
		public var newState:GestureState;
		public var oldState:GestureState;
		
		
		public function GestureEvent(type:String, newState:GestureState, oldState:GestureState)
		{
			super(type, false, false);
			
			this.newState = newState;
			this.oldState = oldState;
		}
		
		
		override public function clone():Event
		{
			return new GestureEvent(type, newState, oldState);
		}
		
		
		override public function toString():String
		{
			return formatToString("GestureEvent", "type", "oldState", "newState");
		}
	}
}


================================================
FILE: src/org/gestouch/extensions/native/DisplayObjectUtils.as
================================================
package org.gestouch.extensions.native
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.InteractiveObject;
import flash.display.Stage;
import flash.geom.Point;


/**
 * @author    Valentin Simonov
 */
public class DisplayObjectUtils
{
	/**
	 * Searches display list for top most instance of InteractiveObject.
	 * Checks if mouseEnabled is true and (optionally) parent's mouseChildren.
	 * @param stage                Stage object.
	 * @param point                Global point to test.
	 * @param mouseChildren        If true also checks parents chain for mouseChildren == true.
	 * @param startFrom            An index to start looking from in objects under point array.
	 * @return                    Top most InteractiveObject or Stage.
	 */
	public static function getTopTarget(stage:Stage, point:Point, mouseChildren:Boolean = true, startFrom:uint = 0):InteractiveObject
	{
		var targets:Array = stage.getObjectsUnderPoint(point);
		if (!targets.length) return stage;

		var startIndex:int = targets.length - 1 - startFrom;
		if (startIndex < 0) return stage;

		outer:
				for (var i:int = startIndex; i >= 0; i--)
				{
					var target:DisplayObject = targets[i] as DisplayObject;
					while (target != stage)
					{
						if (target is InteractiveObject)
						{
							if ((target as InteractiveObject).mouseEnabled)
							{
								if (mouseChildren)
								{
									var lastMouseActive:InteractiveObject = target as InteractiveObject;
									var parent:DisplayObjectContainer = target.parent;
									while (parent)
									{
										if (!lastMouseActive && parent.mouseEnabled)
										{
											lastMouseActive = parent;
										}
										else if (!parent.mouseChildren)
										{
											if (parent.mouseEnabled)
											{
												lastMouseActive = parent;
											}
											else
											{
												lastMouseActive = null;
											}
										}
										parent = parent.parent;
									}
									if (lastMouseActive)
									{
										return lastMouseActive;
									}
									else
									{
										return stage;
									}
								}
								else
								{
									return target as InteractiveObject;
								}
							}
							else
							{
								continue outer;
							}
						}
						else
						{
							target = target.parent;
						}
					}
				}

		return stage;
	}
}
}


================================================
FILE: src/org/gestouch/extensions/native/NativeDisplayListAdapter.as
================================================
package org.gestouch.extensions.native
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Stage;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	import org.gestouch.core.IDisplayListAdapter;


	/**
	 * @author Pavel fljot
	 */
	final public class NativeDisplayListAdapter implements IDisplayListAdapter
	{
		private var _targetWeekStorage:Dictionary;
		
		
		public function NativeDisplayListAdapter(target:DisplayObject = null)
		{
			if (target)
			{
				_targetWeekStorage = new Dictionary(true);
				_targetWeekStorage[target] = true;
			}
		}
		
		
		public function get target():Object
		{
			for (var key:Object in _targetWeekStorage)
			{
				return key;
			}
			return null;
		}
		
		
		public function contains(object:Object):Boolean
		{
			const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer;
			if (targetAsDOC is Stage)
			{
				return true;
			}
			const objectAsDO:DisplayObject = object as DisplayObject;
			if (objectAsDO)
			{
				return (targetAsDOC && targetAsDOC.contains(objectAsDO));
			}
			/**
			 * There might be case when we use some old "software" 3D library for instace,
			 * which viewport is added to classic Display List. So native stage, root and some other
			 * sprites will actually be parents of 3D objects. To ensure all gestures (both for
			 * native and 3D objects) work correctly with each other contains() method should be
			 * a bit more sophisticated.
			 * But as all 3D engines (at least it looks like that) are moving towards Stage3D layer
			 * this task doesn't seem significant anymore. So I leave this implementation as
			 * comments in case someone will actually need it.
			 * Just uncomment this and it should work. 
			
			// else: more complex case.
			// object is not of the same type as this.target (flash.display::DisplayObject)
			// it might we some 3D library object in it's viewport (which itself is in DisplayList).
			// So we perform more general check:
			const adapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(object);
			if (adapter)
			{
				return adapter.getHierarchy(object).indexOf(this.target) > -1;
			}
			*/
			
			return false;
		}
		
		
		public function getHierarchy(genericTarget:Object):Vector.<Object>
		{
			var list:Vector.<Object> = new Vector.<Object>();
			var i:uint = 0;
			var target:DisplayObject = genericTarget as DisplayObject;
			while (target)
			{
				list[i] = target;
				target = target.parent;
				i++;
			}
			
			return list;
		}
		
		
		public function reflect():Class
		{
			return NativeDisplayListAdapter;
		}
	}
}

================================================
FILE: src/org/gestouch/extensions/native/NativeTouchHitTester.as
================================================
package org.gestouch.extensions.native
{
	import flash.display.DisplayObject;
	import flash.display.Stage;
	import flash.geom.Point;

	import org.gestouch.core.ITouchHitTester;


	/**
	 * @author Pavel fljot
	 */
	public class NativeTouchHitTester implements ITouchHitTester
	{
		private var stage:Stage;


		public function NativeTouchHitTester(stage:Stage)
		{
			if (!stage)
			{
				throw ArgumentError("Missing stage argument.");
			}

			this.stage = stage;
		}


		public function hitTest(point:Point, possibleTarget:Object = null):Object
		{
			if (possibleTarget && possibleTarget is DisplayObject)
			{
				return possibleTarget;
			}

			// Fallback target detection through getObjectsUnderPoint
			return DisplayObjectUtils.getTopTarget(stage, point);
		}
	}
}


================================================
FILE: src/org/gestouch/extensions/starling/StarlingDisplayListAdapter.as
================================================
package org.gestouch.extensions.starling
{
	import starling.display.DisplayObject;
	import starling.display.DisplayObjectContainer;

	import org.gestouch.core.IDisplayListAdapter;

	import flash.utils.Dictionary;


	/**
	 * @author Pavel fljot
	 */
	final public class StarlingDisplayListAdapter implements IDisplayListAdapter
	{
		private var targetWeekStorage:Dictionary;
		
		
		public function StarlingDisplayListAdapter(target:DisplayObject = null)
		{
			if (target)
			{
				targetWeekStorage = new Dictionary(true);
				targetWeekStorage[target] = true;
			}
		}
		
		
		public function get target():Object
		{
			for (var key:Object in targetWeekStorage)
			{
				return key;
			}
			return null;
		}
		
		
		public function contains(object:Object):Boolean
		{
			const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer;
			const objectAsDO:DisplayObject = object as DisplayObject;
			return (targetAsDOC && objectAsDO && targetAsDOC.contains(objectAsDO));
		}
		
		
		public function getHierarchy(genericTarget:Object):Vector.<Object>
		{
			var list:Vector.<Object> = new Vector.<Object>();
			var i:uint = 0;
			var target:DisplayObject = genericTarget as DisplayObject;
			while (target)
			{
				list[i] = target;
				target = target.parent;
				i++;
			}
			
			return list;
		}
		
		
		public function reflect():Class
		{
			return StarlingDisplayListAdapter;
		}
	}
}

================================================
FILE: src/org/gestouch/extensions/starling/StarlingTouchHitTester.as
================================================
package org.gestouch.extensions.starling
{
	import flash.geom.Point;

	import org.gestouch.core.ITouchHitTester;

	import starling.core.Starling;
	import starling.display.DisplayObject;


	/**
	 * @author Pavel fljot
	 */
	public class StarlingTouchHitTester implements ITouchHitTester
	{
		private var starling:Starling;


		public function StarlingTouchHitTester(starling:Starling)
		{
			if (!starling)
			{
				throw ArgumentError("Missing starling argument.");
			}

			this.starling = starling;
		}


		public function hitTest(point:Point, possibleTarget:Object = null):Object
		{
			if (possibleTarget && possibleTarget is DisplayObject)
			{
				return possibleTarget;
			}

			point = StarlingUtils.adjustGlobalPoint(starling, point);
			return starling.stage.hitTest(point, true);
		}
	}
}


================================================
FILE: src/org/gestouch/extensions/starling/StarlingUtils.as
================================================
package org.gestouch.extensions.starling
{
	import starling.display.Stage;
	import starling.core.Starling;

	import flash.geom.Point;
	import flash.geom.Rectangle;


	/**
	 * @author Pavel fljot
	 */
	final public class StarlingUtils
	{
		/**
		 * Transforms real global point (in the scope of flash.display::Stage) into
		 * starling stage "global" coordinates.
		 */
		public static function adjustGlobalPoint(starling:Starling, point:Point):Point
		{
			const vp:Rectangle = starling.viewPort;
			const stStage:Stage = starling.stage;
			
			if (vp.x != 0 || vp.y != 0 ||
				stStage.stageWidth != vp.width || stStage.stageHeight != vp.height)
			{
				point = point.clone();
				
				// Same transformation they do in Starling
				// WTF!? https://github.com/PrimaryFeather/Starling-Framework/issues/72
				point.x = stStage.stageWidth * (point.x - vp.x) / vp.width;
				point.y = stStage.stageHeight * (point.y - vp.y) / vp.height;
			}
			
			return point;
		}
	}
}


================================================
FILE: src/org/gestouch/gestures/AbstractContinuousGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.gestures.Gesture;


	/**
	 * Dispatched when the state of the gesture changes to GestureState.BEGAN.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureBegan", type="org.gestouch.events.GestureEvent")]
	/**
	 * Dispatched when the state of the gesture changes to GestureState.CHANGED.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureChanged", type="org.gestouch.events.GestureEvent")]
	/**
	 * Dispatched when the state of the gesture changes to GestureState.ENDED.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureEnded", type="org.gestouch.events.GestureEvent")]
	/**
	 * Dispatched when the state of the gesture changes to GestureState.CANCELLED.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureCancelled", type="org.gestouch.events.GestureEvent")]
	/**
	 * @author Pavel fljot
	 */
	public class AbstractContinuousGesture extends Gesture
	{
		public function AbstractContinuousGesture(target:Object = null)
		{
			super(target);
		}
	}
}


================================================
FILE: src/org/gestouch/gestures/AbstractDiscreteGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.gestures.Gesture;


	/**
	 * Dispatched when the state of the gesture changes to GestureState.RECOGNIZED.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureRecognized", type="org.gestouch.events.GestureEvent")]
	/**
	 * @author Pavel fljot
	 */
	public class AbstractDiscreteGesture extends Gesture
	{
		public function AbstractDiscreteGesture(target:Object = null)
		{
			super(target);
		}
	}
}


================================================
FILE: src/org/gestouch/gestures/Gesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.Gestouch;
	import org.gestouch.core.GestureState;
	import org.gestouch.core.GesturesManager;
	import org.gestouch.core.IGestureTargetAdapter;
	import org.gestouch.core.Touch;
	import org.gestouch.core.gestouch_internal;
	import org.gestouch.events.GestureEvent;

	import flash.errors.IllegalOperationError;
	import flash.events.EventDispatcher;
	import flash.geom.Point;
	import flash.system.Capabilities;
	import flash.utils.Dictionary;
	
	use namespace gestouch_internal;
	
	
	/**
	 * Dispatched when the state of the gesture changes.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureStateChange", type="org.gestouch.events.GestureEvent")]
	/**
	 * Dispatched when the state of the gesture changes to GestureState.POSSIBLE.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gesturePossible", type="org.gestouch.events.GestureEvent")]
	/**
	 * Dispatched when the state of the gesture changes to GestureState.FAILED.
	 * 
	 * @eventType org.gestouch.events.GestureEvent
	 * @see #state
	 */
	[Event(name="gestureFailed", type="org.gestouch.events.GestureEvent")]
	/**
	 * Base class for all gestures. Gesture is essentially a detector that tracks touch points
	 * in order detect specific gesture motion and form gesture event on target.
	 * 
	 * @author Pavel fljot
	 */
	public class Gesture extends EventDispatcher
	{
		/**
		 * Threshold for screen distance they must move to count as valid input 
		 * (not an accidental offset on touch), 
		 * based on 20 pixels on a 252ppi device.
		 */
		public static var DEFAULT_SLOP:uint = Math.round(20 / 252 * flash.system.Capabilities.screenDPI);
		
		/**
		 * If a gesture should receive a touch.
		 * Callback signature: function(gesture:Gesture, touch:Touch):Boolean
		 * 
		 * @see Touch
		 */
		public var gestureShouldReceiveTouchCallback:Function;
		/**
		 * If a gesture should be recognized (transition from state POSSIBLE to state RECOGNIZED or BEGAN).
		 * Returning <code>false</code> causes the gesture to transition to the FAILED state.
		 * 
		 * Callback signature: function(gesture:Gesture):Boolean
		 * 
		 * @see state
		 * @see GestureState
		 */
		public var gestureShouldBeginCallback:Function;
		/**
		 * If two gestures should be allowed to recognize simultaneously.
		 * 
		 * Callback signature: function(gesture:Gesture, otherGesture:Gesture):Boolean
		 */
		public var gesturesShouldRecognizeSimultaneouslyCallback:Function;
		
		
		protected const _gesturesManager:GesturesManager = Gestouch.gesturesManager;
		/**
		 * Map (generic object) of tracking touch points, where keys are touch points IDs.
		 */
		protected var _touchesMap:Object = {};
		protected var _centralPoint:Point = new Point();
		/**
		 * List of gesture we require to fail.
		 * @see requireGestureToFail()
		 */
		protected var _gesturesToFail:Dictionary = new Dictionary(true);
		protected var _pendingRecognizedState:GestureState;
		
		
		public function Gesture(target:Object = null)
		{
			super();
			
			preinit();
			
			this.target = target;
		}
		
		
		/** @private */
		protected var _targetAdapter:IGestureTargetAdapter;
		/**
		 * 
		 */
		gestouch_internal function get targetAdapter():IGestureTargetAdapter
		{
			return _targetAdapter;
		}
		protected function get targetAdapter():IGestureTargetAdapter
		{
			return _targetAdapter;
		}
		
		
		/**
		 * FIXME
		 * InteractiveObject (DisplayObject) which this gesture is tracking the actual gesture motion on.
		 * 
		 * <p>Could be some image, component (like map) or the larger view like Stage.</p>
		 * 
		 * <p>You can change the target in the runtime, e.g. you have a gallery
		 * where only one item is visible at the moment, so use one gesture instance
		 * and change the target to the currently visible item.</p>
		 * 
		 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html
		 */
		public function get target():Object
		{
			return _targetAdapter ? _targetAdapter.target : null;
		}
		public function set target(value:Object):void
		{
			var target:Object = this.target;
			if (target == value)
				return;
			
			uninstallTarget(target);
			_targetAdapter = value ? Gestouch.createGestureTargetAdapter(value) : null;
			installTarget(value);
		}
		
		
		/** @private */
		protected var _enabled:Boolean = true;
		
		/** 
		 * @default true
		 */
		public function get enabled():Boolean
		{
			return _enabled;
		}
		public function set enabled(value:Boolean):void
		{
			if (_enabled == value)
				return;
			
			_enabled = value;
			
			if (!_enabled)
			{
				if (state == GestureState.POSSIBLE)
				{
					setState(GestureState.FAILED);
				}
				else
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					setState(GestureState.CANCELLED);
				}
			}
		}
		
		
		protected var _state:GestureState = GestureState.POSSIBLE;
		public function get state():GestureState
		{
			return _state;
		}
		
		
		protected var _idle:Boolean = true;
		gestouch_internal function get idle():Boolean
		{
			return _idle;
		}
		
		
		protected var _touchesCount:uint = 0;
		/**
		 * Amount of currently tracked touch points.
		 * 
		 * @see #_touches
		 */
		public function get touchesCount():uint
		{
			return _touchesCount;
		}
		
		
		protected var _location:Point = new Point();
		/**
		 * Virtual central touch point among all tracking touch points (geometrical center).
		 */
		public function get location():Point
		{
			return _location.clone();
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		
		[Abstract]
		/**
		 * Reflects gesture class (for better perfomance).
		 * 
		 * <p><b>NB!</b> This is abstract method and must be overridden.</p>
		 * 
		 * @see performance optimization tips
		 */
		public function reflect():Class
		{
			throw Error("reflect() is abstract method and must be overridden.");
		}
		
		
		public function isTrackingTouch(touchID:uint):Boolean
		{
			return (_touchesMap[touchID] != undefined);
		}
		
		
		/**
		 * Cancels current tracking (interaction) cycle.
		 * 
		 * <p>Could be useful to "stop" gesture for the current interaction cycle.</p>
		 */
		public function reset():void
		{
			if (idle)
				return;// Do nothing as we are idle and there is nothing to reset
			
			const state:GestureState = this.state;//caching getter
			
			_location.x = 0;
			_location.y = 0;
			_touchesMap = {};
			_touchesCount = 0;
			_idle = true;
			
			for (var key:* in _gesturesToFail)
			{
				var gestureToFail:Gesture = key as Gesture;
				gestureToFail.removeEventListener(GestureEvent.GESTURE_STATE_CHANGE, gestureToFail_stateChangeHandler);
			}
			_pendingRecognizedState = null;
			
			if (state == GestureState.POSSIBLE)
			{
				// manual reset() call. Set to FAILED to keep our State Machine clean and stable
				setState(GestureState.FAILED);
			}
			else if (state == GestureState.BEGAN || state == GestureState.CHANGED)
			{
				// manual reset() call. Set to CANCELLED to keep our State Machine clean and stable
				setState(GestureState.CANCELLED);
			}
			else
			{
				// reset from GesturesManager after reaching one of the 4 final states:
				// (state == GestureState.RECOGNIZED ||
				// state == GestureState.ENDED ||
				// state == GestureState.FAILED ||
				// state == GestureState.CANCELLED)
				setState(GestureState.POSSIBLE);
			}
		}
		
		
		/**
		 * Remove gesture and prepare it for GC.
		 * 
		 * <p>The gesture is not able to use after calling this method.</p>
		 */
		public function dispose():void
		{
			//TODO
			reset();
			target = null;
			gestureShouldReceiveTouchCallback = null;
			gestureShouldBeginCallback = null;
			gesturesShouldRecognizeSimultaneouslyCallback = null;
			_gesturesToFail = null;
		}
		
		
		gestouch_internal function canBePreventedByGesture(preventingGesture:Gesture):Boolean
		{
			return true;
		}
		
		
		gestouch_internal function canPreventGesture(preventedGesture:Gesture):Boolean
		{
			return true;
		}
		
		
		/**
		 * <b>NB! Current implementation is highly experimental!</b> See examples for more info. 
		 */
		public function requireGestureToFail(gesture:Gesture):void
		{
			//TODO
			if (!gesture)
			{
				throw new ArgumentError();
			}
			
			_gesturesToFail[gesture] = true;
		}
		
		


		// --------------------------------------------------------------------------
		// 
		// Protected methods
		// 
		// --------------------------------------------------------------------------
		
		/**
		 * First method, called in constructor.
		 */
		protected function preinit():void
		{
		}
		
		
		/**
		 * Called internally when changing the target.
		 * 
		 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html
		 */
		protected function installTarget(target:Object):void
		{
			if (target)
			{
				_gesturesManager.addGesture(this);
			}
		}
		
		
		/**
		 * Called internally when changing the target.
		 * 
		 * <p>You should remove all listeners from target here.</p>
		 * 
		 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html
		 */
		protected function uninstallTarget(target:Object):void
		{
			if (target)
			{
				_gesturesManager.removeGesture(this);
			}
		}
		
		
		/**
		 * TODO: clarify usage. For now it's supported to call this method in onTouchBegin with return.
		 */
		protected function ignoreTouch(touch:Touch):void
		{
			if (touch.id in _touchesMap)
			{
				delete _touchesMap[touch.id];
				_touchesCount--;
			}
		}
		
		
		protected function failOrIgnoreTouch(touch:Touch):void
		{
			if (state == GestureState.POSSIBLE)
			{
				setState(GestureState.FAILED);
			}
			else
			{
				ignoreTouch(touch);
			}
		}
		
		
		[Abstract]
		/**
		 * <p><b>NB!</b> This is abstract method and must be overridden.</p>
		 */
		protected function onTouchBegin(touch:Touch):void
		{
		}
		
		
		[Abstract]
		/**
		 * <p><b>NB!</b> This is abstract method and must be overridden.</p>
		 */
		protected function onTouchMove(touch:Touch):void
		{
		}
		
		
		[Abstract]
		/**
		 * <p><b>NB!</b> This is abstract method and must be overridden.</p>
		 */
		protected function onTouchEnd(touch:Touch):void
		{
		}
		
		
		/**
		 * 
		 */
		protected function onTouchCancel(touch:Touch):void
		{
		}
		
		
		protected function setState(newState:GestureState):Boolean
		{
			if (_state == newState && _state == GestureState.CHANGED)
			{
				// shortcut for better performance
				
				if (hasEventListener(GestureEvent.GESTURE_STATE_CHANGE))
				{
					dispatchEvent(new GestureEvent(GestureEvent.GESTURE_STATE_CHANGE, _state, _state));
				}
				
				if (hasEventListener(GestureEvent.GESTURE_CHANGED))
				{
					dispatchEvent(new GestureEvent(GestureEvent.GESTURE_CHANGED, _state, _state));
				}
				
				resetNotificationProperties();
				
				return true;
			}
			
			if (!_state.canTransitionTo(newState))
			{
				throw new IllegalOperationError("You cannot change from state " +
					_state + " to state " + newState  + ".");
			}
			
			if (newState != GestureState.POSSIBLE)
			{
				// in case instantly switch state in touchBeganHandler()
				_idle = false;
			}
			
			
			if (newState == GestureState.BEGAN || newState == GestureState.RECOGNIZED)
			{
				var gestureToFail:Gesture;
				var key:*;
				// first we check if other required-to-fail gestures recognized
				// TODO: is this really necessary? using "requireGestureToFail" API assume that
				// required-to-fail gesture always recognizes AFTER this one.
				for (key in _gesturesToFail)
				{
					gestureToFail = key as Gesture;
					if (!gestureToFail.idle &&
						gestureToFail.state != GestureState.POSSIBLE &&
						gestureToFail.state != GestureState.FAILED)
					{
						// Looks like other gesture won't fail,
						// which means the required condition will not happen, so we must fail
						setState(GestureState.FAILED);
						return false;
					}
				}
				// then we check if other required-to-fail gestures are actually tracked (not IDLE)
				// and not still not recognized (e.g. POSSIBLE state)
				for (key in _gesturesToFail)
				{
					gestureToFail = key as Gesture;
					if (gestureToFail.state == GestureState.POSSIBLE)
					{
						// Other gesture might fail soon, so we postpone state change
						_pendingRecognizedState = newState;
						
						for (key in _gesturesToFail)
						{
							gestureToFail = key as Gesture;
							gestureToFail.addEventListener(GestureEvent.GESTURE_STATE_CHANGE, gestureToFail_stateChangeHandler, false, 0, true);
						}
						
						return false;
					}
					// else if gesture is in IDLE state it means it doesn't track anything,
					// so we simply ignore it as it doesn't seem like conflict from this perspective
					// (perspective of using "requireGestureToFail" API)
				}
				
				
				if (gestureShouldBeginCallback != null && !gestureShouldBeginCallback(this))
				{
					setState(GestureState.FAILED);
					return false;
				}
			}
				
			var oldState:GestureState = _state;	
			_state = newState;
			
			if (_state.isEndState)
			{
				_gesturesManager.scheduleGestureStateReset(this);
			}
			
			//TODO: what if RTE happens in event handlers?
			
			if (hasEventListener(GestureEvent.GESTURE_STATE_CHANGE))
			{
				dispatchEvent(new GestureEvent(GestureEvent.GESTURE_STATE_CHANGE, _state, oldState));
			}
			
			if (hasEventListener(_state.toEventType()))
			{
				dispatchEvent(new GestureEvent(_state.toEventType(), _state, oldState));
			}
			
			resetNotificationProperties();
			
			if (_state == GestureState.BEGAN || _state == GestureState.RECOGNIZED)
			{
				_gesturesManager.onGestureRecognized(this);
			}
			
			return true;
		}
		
		
		gestouch_internal function setState_internal(state:GestureState):void
		{
			setState(state);
		}
		
		
		protected function updateCentralPoint():void
		{
			var touchLocation:Point;
			var x:Number = 0;
			var y:Number = 0;
			for (var touchID:String in _touchesMap)
			{
				touchLocation = (_touchesMap[int(touchID)] as Touch).location; 
				x += touchLocation.x;
				y += touchLocation.y;
			}
			_centralPoint.x = x / _touchesCount;
			_centralPoint.y = y / _touchesCount;
		}
		
		
		protected function updateLocation():void
		{
			updateCentralPoint();
			_location.x = _centralPoint.x;
			_location.y = _centralPoint.y;
		}
		
		
		protected function resetNotificationProperties():void
		{
			
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		gestouch_internal function touchBeginHandler(touch:Touch):void
		{
			_touchesMap[touch.id] = touch;
			_touchesCount++;
			
			onTouchBegin(touch);
			
			if (_touchesCount == 1 && state == GestureState.POSSIBLE)
			{
				_idle = false;
			}
		}
		
		
		gestouch_internal function touchMoveHandler(touch:Touch):void
		{
			_touchesMap[touch.id] = touch;
			onTouchMove(touch);
		}
		
		
		gestouch_internal function touchEndHandler(touch:Touch):void
		{
			delete _touchesMap[touch.id];
			_touchesCount--;
			
			onTouchEnd(touch);
		}
		
		
		gestouch_internal function touchCancelHandler(touch:Touch):void
		{
			delete _touchesMap[touch.id];
			_touchesCount--;
			
			onTouchCancel(touch);
			
			if (!state.isEndState)
			{
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					setState(GestureState.CANCELLED);
				}
				else
				{
					setState(GestureState.FAILED);
				}
			}
		}
		
		
		protected function gestureToFail_stateChangeHandler(event:GestureEvent):void
		{
			if (!_pendingRecognizedState || state != GestureState.POSSIBLE)
				return;
			
			if (event.newState == GestureState.FAILED)
			{
				for (var key:* in _gesturesToFail)
				{
					var gestureToFail:Gesture = key as Gesture;
					if (gestureToFail.state == GestureState.POSSIBLE)
					{
						// we're still waiting for some gesture to fail
						return;
					}
				}
				
				// at this point all gestures-to-fail are either in IDLE or in FAILED states
				setState(_pendingRecognizedState);
			}
			else if (event.newState != GestureState.POSSIBLE)
			{
				//TODO: need to re-think this over
				
				setState(GestureState.FAILED);
			}
		}
	}
}


================================================
FILE: src/org/gestouch/gestures/LongPressGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.events.TimerEvent;
	import flash.utils.Timer;


	/**
	 * TODO:
	 * - add numTapsRequired
	 * 
	 * @author Pavel fljot
	 */
	public class LongPressGesture extends AbstractContinuousGesture
	{
		public var numTouchesRequired:uint = 1;
		/**
		 * The minimum time interval in millisecond fingers must press on the target for the gesture to be recognized.
		 * 
		 * @default 500
		 */
		public var minPressDuration:uint = 500;
		public var slop:Number = Gesture.DEFAULT_SLOP;
		
		protected var _timer:Timer;
		protected var _numTouchesRequiredReached:Boolean;
		
		
		public function LongPressGesture(target:Object = null)
		{
			super(target);
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return LongPressGesture;
		}
		
		
		override public function reset():void
		{
			super.reset();
			
			_numTouchesRequiredReached = false;
			_timer.reset();
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function preinit():void
		{
			super.preinit();
			
			_timer = new Timer(minPressDuration, 1);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, timer_timerCompleteHandler);
		}
		
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > numTouchesRequired)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == numTouchesRequired)
			{
				_numTouchesRequiredReached = true;
				_timer.reset();
				_timer.delay = minPressDuration || 1;
				_timer.start();
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (state == GestureState.POSSIBLE && slop > 0 && touch.locationOffset.length > slop)
			{
				setState(GestureState.FAILED);
			}
			else if (state == GestureState.BEGAN || state == GestureState.CHANGED)
			{
				updateLocation();
				setState(GestureState.CHANGED);
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (_numTouchesRequiredReached)
			{
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					updateLocation();
					setState(GestureState.ENDED);
				}
				else
				{
					setState(GestureState.FAILED);
				}
			}
			else
			{
				setState(GestureState.FAILED);
			}
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		protected function timer_timerCompleteHandler(event:TimerEvent = null):void
		{
			if (state == GestureState.POSSIBLE)
			{
				updateLocation();
				setState(GestureState.BEGAN);
			}
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/PanGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.geom.Point;


	/**
	 * TODO:
	 * -location
	 * -check native behavior on iDevice
	 * 
	 * @author Pavel fljot
	 */
	public class PanGesture extends AbstractContinuousGesture
	{
		public var slop:Number = Gesture.DEFAULT_SLOP;
		/**
		 * Used for initial slop overcome calculations only.
		 */
		public var direction:uint = PanGestureDirection.NO_DIRECTION;
		
		
		public function PanGesture(target:Object = null)
		{
			super(target);
		}
		
		
		/** @private */
		private var _maxNumTouchesRequired:uint = uint.MAX_VALUE;
		
		/**
		 * 
		 */
		public function get maxNumTouchesRequired():uint
		{
			return _maxNumTouchesRequired;
		}
		public function set maxNumTouchesRequired(value:uint):void
		{
			if (_maxNumTouchesRequired == value)
				return;
			
			if (value < minNumTouchesRequired)
				throw ArgumentError("maxNumTouchesRequired must be not less then minNumTouchesRequired");
			
			_maxNumTouchesRequired = value;
		}
		
		
		/** @private */
		private var _minNumTouchesRequired:uint = 1;
		
		/**
		 * 
		 */
		public function get minNumTouchesRequired():uint
		{
			return _minNumTouchesRequired;
		}
		public function set minNumTouchesRequired(value:uint):void
		{
			if (_minNumTouchesRequired == value)
				return;
			
			if (value > maxNumTouchesRequired)
				throw ArgumentError("minNumTouchesRequired must be not greater then maxNumTouchesRequired");
			
			_minNumTouchesRequired = value;
		}
		
		
		protected var _offsetX:Number = 0;
		public function get offsetX():Number
		{
			return _offsetX;
		}
		
		
		protected var _offsetY:Number = 0;
		public function get offsetY():Number
		{
			return _offsetY;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return PanGesture;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > maxNumTouchesRequired)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount >= minNumTouchesRequired)
			{
				updateLocation();
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (touchesCount < minNumTouchesRequired)
				return;
			
			var prevLocationX:Number;
			var prevLocationY:Number;
			
			if (state == GestureState.POSSIBLE)
			{
				prevLocationX = _location.x;
				prevLocationY = _location.y;
				updateLocation();
				
				// Check if finger moved enough for gesture to be recognized
				var locationOffset:Point = touch.locationOffset;
				if (direction == PanGestureDirection.VERTICAL)
				{
					locationOffset.x = 0;
				}
				else if (direction == PanGestureDirection.HORIZONTAL)
				{
					locationOffset.y = 0;
				}
				
				if (locationOffset.length > slop || slop != slop)//faster isNaN(slop)
				{
					// NB! += instead of = for the case when this gesture recognition is delayed via requireGestureToFail
					_offsetX += _location.x - prevLocationX;
					_offsetY += _location.y - prevLocationY;
					
					setState(GestureState.BEGAN);
				}
			}
			else if (state == GestureState.BEGAN || state == GestureState.CHANGED)
			{
				prevLocationX = _location.x;
				prevLocationY = _location.y;
				updateLocation();
				_offsetX = _location.x - prevLocationX;
				_offsetY = _location.y - prevLocationY;
				
				setState(GestureState.CHANGED);
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (touchesCount < minNumTouchesRequired)
			{
				if (state == GestureState.POSSIBLE)
				{
					setState(GestureState.FAILED);
				}
				else
				{
					setState(GestureState.ENDED);
				}
			}
			else
			{
				updateLocation();
			}
		}
		
		
		override protected function resetNotificationProperties():void
		{
			super.resetNotificationProperties();
			
			_offsetX = _offsetY = 0;
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/PanGestureDirection.as
================================================
package org.gestouch.gestures
{
	/**
	 * @author Pavel fljot
	 */
	public class PanGestureDirection
	{
		public static const NO_DIRECTION:uint = 0;
		public static const VERTICAL:uint = 1 << 0;
		public static const HORIZONTAL:uint = 1 << 1;
	}
}

================================================
FILE: src/org/gestouch/gestures/RotateGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.geom.Point;


	/**
	 * TODO:
	 * -check native behavior on iDevice
	 * 
	 * @author Pavel fljot
	 */
	public class RotateGesture extends AbstractContinuousGesture
	{
		public var slop:Number = Gesture.DEFAULT_SLOP;
		
		protected var _touch1:Touch;
		protected var _touch2:Touch;
		protected var _transformVector:Point;
		protected var _thresholdAngle:Number;
		
		
		public function RotateGesture(target:Object = null)
		{
			super(target);
		}
		
		
		protected var _rotation:Number = 0;
		public function get rotation():Number
		{
			return _rotation;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return RotateGesture;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > 2)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == 1)
			{
				_touch1 = touch;
			}
			else
			{
				_touch2 = touch;
				
				_transformVector = _touch2.location.subtract(_touch1.location);
				
				// @see chord length formula
				_thresholdAngle = Math.asin(slop / (2 * _transformVector.length)) * 2;
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (touchesCount < 2)
				return;
			
			var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
			var cross:Number = (_transformVector.x * currTransformVector.y) - (currTransformVector.x * _transformVector.y);
			var dot:Number = (_transformVector.x * currTransformVector.x) + (_transformVector.y * currTransformVector.y);
			var rotation:Number = Math.atan2(cross, dot);
			
			if (state == GestureState.POSSIBLE)
			{
				const absRotation:Number = rotation >= 0 ? rotation : -rotation;
				if (absRotation < _thresholdAngle)
				{
					// not recognized yet
					return;
				}
				
				// adjust angle to avoid initial "jump"
				rotation = rotation > 0 ? rotation - _thresholdAngle : rotation + _thresholdAngle;
			}
			
			_transformVector.x = currTransformVector.x;
			_transformVector.y = currTransformVector.y;
			_rotation = rotation;
			
			updateLocation();
			
			if (state == GestureState.POSSIBLE)
			{
				setState(GestureState.BEGAN);
			}
			else
			{
				setState(GestureState.CHANGED);
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (touchesCount == 0)
			{
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					setState(GestureState.ENDED);
				}
				else if (state == GestureState.POSSIBLE)
				{
					setState(GestureState.FAILED);
				}
			}
			else// == 1
			{
				if (touch == _touch1)
				{
					_touch1 = _touch2;
				}
				_touch2 = null;
				
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					updateLocation();
					setState(GestureState.CHANGED);
				}
			}
		}
		
		
		override protected function resetNotificationProperties():void
		{
			super.resetNotificationProperties();
			
			_rotation = 0;
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/SwipeGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;
	import org.gestouch.utils.GestureUtils;

	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.system.Capabilities;
	import flash.utils.Timer;


	/**
	 * Recognition logic:<br/>
	 * 1. should be recognized during <code>maxDuration</code> period<br/>
	 * 2. velocity >= minVelocity <b>OR</b> offset >= minOffset
	 * 
	 * 
	 * @author Pavel fljot
	 */
	public class SwipeGesture extends AbstractDiscreteGesture
	{
		private static const ANGLE:Number = 40 * GestureUtils.DEGREES_TO_RADIANS;
		private static const MAX_DURATION:uint = 500;
		private static const MIN_OFFSET:Number = Capabilities.screenDPI / 6;
		private static const MIN_VELOCITY:Number = 2 * MIN_OFFSET / MAX_DURATION;
		
		/**
		 * "Dirty" region around touch begin location which is not taken into account for
		 * recognition/failing algorithms.
		 * 
		 * @default Gesture.DEFAULT_SLOP
		 */
		public var slop:Number = Gesture.DEFAULT_SLOP;
		public var numTouchesRequired:uint = 1;
		public var direction:uint = SwipeGestureDirection.ORTHOGONAL;
		
		/**
		 * The duration of period (in milliseconds) in which SwipeGesture must be recognized.
		 * If gesture is not recognized during this period it fails. Default value is 500 (half a
		 * second) and generally should not be changed. You can change it though for some special
		 * cases, most likely together with <code>minVelocity</code> and <code>minOffset</code>
		 * to achieve really custom behavior. 
		 * 
		 * @default 500
		 * 
		 * @see #minVelocity
		 * @see #minOffset
		 */
		public var maxDuration:uint = MAX_DURATION;
		
		/**
		 * Minimum offset (in pixels) for gesture to be recognized.
		 * Default value is <code>Capabilities.screenDPI / 6</code> and generally should not
		 * be changed.
		 */
		public var minOffset:Number = MIN_OFFSET;
		
		/**
		 * Minimum velocity (in pixels per millisecond) for gesture to be recognized.
		 * Default value is <code>2 * minOffset / maxDuration</code> and generally should not
		 * be changed.
		 * 
		 * @see #minOffset
		 * @see #minDuration
		 */
		public var minVelocity:Number = MIN_VELOCITY;
		
		protected var _offset:Point = new Point();
		protected var _startTime:int;
		protected var _noDirection:Boolean;
		protected var _avrgVel:Point = new Point();
		protected var _timer:Timer;
		
		
		public function SwipeGesture(target:Object = null)
		{
			super(target);
		}
		
		
		public function get offsetX():Number
		{
			return _offset.x;
		}
		
		
		public function get offsetY():Number
		{
			return _offset.y;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return SwipeGesture;
		}
		
		
		override public function reset():void
		{
			_startTime = 0;
			_offset.x = 0;
			_offset.y = 0;
			_timer.reset();
			
			super.reset();
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function preinit():void
		{
			super.preinit();
			
			_timer = new Timer(maxDuration, 1);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, timer_timerCompleteHandler);
		}
		
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > numTouchesRequired)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == 1)
			{
				// Because we want to fail as quick as possible
				_startTime = touch.time;
				
				_timer.reset();
				_timer.delay = maxDuration;
				_timer.start();
			}
			if (touchesCount == numTouchesRequired)
			{
				updateLocation();
				_avrgVel.x = _avrgVel.y = 0;
				
				// cache direction condition for performance
				_noDirection = (SwipeGestureDirection.ORTHOGONAL & direction) == 0;
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (touchesCount < numTouchesRequired)
				return;
			
			var totalTime:int = touch.time - _startTime;
			if (totalTime == 0)
				return;//It was somehow THAT MUCH performant on one Android tablet
			
			var prevCentralPointX:Number = _centralPoint.x;
			var prevCentralPointY:Number = _centralPoint.y;
			updateCentralPoint();
			
			_offset.x = _centralPoint.x - _location.x;
			_offset.y = _centralPoint.y - _location.y;
			var offsetLength:Number = _offset.length;
			
			// average velocity (total offset to total duration)
			_avrgVel.x = _offset.x / totalTime;
			_avrgVel.y = _offset.y / totalTime;
			var avrgVel:Number = _avrgVel.length;
			
			if (_noDirection)
			{
				if ((offsetLength > slop || slop != slop) &&
					(avrgVel >= minVelocity || offsetLength >= minOffset))
				{
					setState(GestureState.RECOGNIZED);
				}
			}
			else
			{
				var recentOffsetX:Number = _centralPoint.x - prevCentralPointX;
				var recentOffsetY:Number = _centralPoint.y - prevCentralPointY;
				//faster Math.abs()
				var absVelX:Number = _avrgVel.x > 0 ? _avrgVel.x : -_avrgVel.x;
				var absVelY:Number = _avrgVel.y > 0 ? _avrgVel.y : -_avrgVel.y;
				
				if (absVelX > absVelY)
				{
					var absOffsetX:Number = _offset.x > 0 ? _offset.x : -_offset.x;
					
					if (absOffsetX > slop || slop != slop)//faster isNaN()
					{
						if ((recentOffsetX < 0 && (direction & SwipeGestureDirection.LEFT) == 0) ||
							(recentOffsetX > 0 && (direction & SwipeGestureDirection.RIGHT) == 0) ||
							Math.abs(Math.atan(_offset.y/_offset.x)) > ANGLE)
						{
							// movement in opposite direction
							// or too much diagonally
							
							setState(GestureState.FAILED);
						}
						else if (absVelX >= minVelocity || absOffsetX >= minOffset)
						{
							_offset.y = 0;
							setState(GestureState.RECOGNIZED);
						}
					}
				}
				else if (absVelY > absVelX)
				{
					var absOffsetY:Number = _offset.y > 0 ? _offset.y : -_offset.y;
					if (absOffsetY > slop || slop != slop)//faster isNaN()
					{
						if ((recentOffsetY < 0 && (direction & SwipeGestureDirection.UP) == 0) ||
							(recentOffsetY > 0 && (direction & SwipeGestureDirection.DOWN) == 0) ||
							Math.abs(Math.atan(_offset.x/_offset.y)) > ANGLE)
						{
							// movement in opposite direction
							// or too much diagonally
							
							setState(GestureState.FAILED);
						}
						else if (absVelY >= minVelocity || absOffsetY >= minOffset)
						{
							_offset.x = 0;
							setState(GestureState.RECOGNIZED);
						}
					}
				}
				// Give some tolerance for accidental offset on finger press (slop)
				else if (offsetLength > slop || slop != slop)//faster isNaN()
				{
					setState(GestureState.FAILED);
				}
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (touchesCount < numTouchesRequired)
			{
				setState(GestureState.FAILED);
			}
		}
		
		
		override protected function resetNotificationProperties():void
		{
			super.resetNotificationProperties();
			
			_offset.x = _offset.y = 0;
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		protected function timer_timerCompleteHandler(event:TimerEvent):void
		{
			if (state == GestureState.POSSIBLE)
			{
				setState(GestureState.FAILED);
			}
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/SwipeGestureDirection.as
================================================
package org.gestouch.gestures
{
	/**
	 * @author Pavel fljot
	 */
	public class SwipeGestureDirection
	{
		public static const RIGHT:uint = 1 << 0;
		public static const LEFT:uint = 1 << 1;
		public static const UP:uint = 1 << 2;
		public static const DOWN:uint = 1 << 3;
		
		public static const NO_DIRECTION:uint = 0;
		public static const HORIZONTAL:uint = RIGHT | LEFT;
		public static const VERTICAL:uint = UP | DOWN;
		public static const ORTHOGONAL:uint = RIGHT | LEFT | UP | DOWN;
	}
}

================================================
FILE: src/org/gestouch/gestures/TapGesture.as
================================================
package org.gestouch.gestures
{
	import flash.geom.Point;
	import org.gestouch.core.gestouch_internal;
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	use namespace gestouch_internal;


	/**
	 * 
	 * @author Pavel fljot
	 */
	public class TapGesture extends AbstractDiscreteGesture
	{
		public var numTouchesRequired:uint = 1;
		public var numTapsRequired:uint = 1;
		public var slop:Number = Gesture.DEFAULT_SLOP << 2;//iOS has 45px for 132 dpi screen
		public var maxTapDelay:uint = 400;
		public var maxTapDuration:uint = 1500;
		public var maxTapDistance:Number = Gesture.DEFAULT_SLOP << 2;
		
		protected var _timer:Timer;
		protected var _numTouchesRequiredReached:Boolean;
		protected var _tapCounter:uint = 0;
		protected var _touchBeginLocations:Vector.<Point> = new Vector.<Point>();
		
		
		public function TapGesture(target:Object = null)
		{
			super(target);
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return TapGesture;
		}
		
		
		override public function reset():void
		{
			_numTouchesRequiredReached = false;
			_tapCounter = 0;
			_timer.reset();
			_touchBeginLocations.length = 0;
			
			super.reset();
		}
		
		
		override gestouch_internal function canPreventGesture(preventedGesture:Gesture):Boolean
		{
			if (preventedGesture is TapGesture &&
				(preventedGesture as TapGesture).numTapsRequired > this.numTapsRequired)
			{
				return false;
			}
			return true;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function preinit():void
		{
			super.preinit();
			
			_timer = new Timer(maxTapDelay, 1);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, timer_timerCompleteHandler);
		}
		
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > numTouchesRequired)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == 1)
			{
				_timer.reset();
				_timer.delay = maxTapDuration;
				_timer.start();
			}
			
			if (numTapsRequired > 1)
			{
				if (_tapCounter == 0)
				{
					// Save touch begin locations to check
					_touchBeginLocations.push(touch.location);
				}
				else
				{
					// Quite a dirty check, but should work in most cases
					var found:Boolean = false;
					for each (var loc:Point in _touchBeginLocations)
					{
						// current touch should be near any previous one
						if (Point.distance(touch.location, loc) <= maxTapDistance)
						{
							found = true;
							break;
						}
					}

					if (!found)
					{
						setState(GestureState.FAILED);
						return;
					}
				}
			}
			
			if (touchesCount == numTouchesRequired)
			{
				_numTouchesRequiredReached = true;
				updateLocation();
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (slop >= 0 && touch.locationOffset.length > slop)
			{
				setState(GestureState.FAILED);
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (!_numTouchesRequiredReached)
			{
				setState(GestureState.FAILED);
			}
			else if (touchesCount == 0)
			{
				// reset flag for the next "full press" cycle
				_numTouchesRequiredReached = false;
				
				_tapCounter++;
				_timer.reset();
				
				if (_tapCounter == numTapsRequired)
				{
					setState(GestureState.RECOGNIZED);
				}
				else
				{
					_timer.delay = maxTapDelay;
					_timer.start();
				}
			}
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		protected function timer_timerCompleteHandler(event:TimerEvent):void
		{
			if (state == GestureState.POSSIBLE)
			{
				setState(GestureState.FAILED);
			}
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/TransformGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.geom.Point;


	/**
	 * @author Pavel fljot
	 */
	public class TransformGesture extends AbstractContinuousGesture
	{
		public var slop:Number = Gesture.DEFAULT_SLOP;
		
		protected var _touch1:Touch;
		protected var _touch2:Touch;
		protected var _transformVector:Point;
		
		
		public function TransformGesture(target:Object = null)
		{
			super(target);
		}
		
		
		protected var _offsetX:Number = 0;
		public function get offsetX():Number
		{
			return _offsetX;
		}
		
		
		protected var _offsetY:Number = 0;
		public function get offsetY():Number
		{
			return _offsetY;
		}
		
		
		protected var _rotation:Number = 0;
		public function get rotation():Number
		{
			return _rotation;
		}
		
		
		protected var _scale:Number = 1;
		public function get scale():Number
		{
			return _scale;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return TransformGesture;
		}
		
		
		override public function reset():void
		{
			_touch1 = null;
			_touch2 = null;
			
			super.reset();
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > 2)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == 1)
			{
				_touch1 = touch;
			}
			else
			{
				_touch2 = touch;
				
				_transformVector = _touch2.location.subtract(_touch1.location);
			}
			
			updateLocation();
			
			if (state == GestureState.BEGAN || state == GestureState.CHANGED)
			{
				// notify that location (and amount of touches) has changed
				setState(GestureState.CHANGED);
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			var prevLocation:Point = _location.clone();
			updateLocation();
			
			var currTransformVector:Point;
			
			if (state == GestureState.POSSIBLE)
			{
				if (slop > 0 && touch.locationOffset.length < slop)
				{
					// Not recognized yet
					if (_touch2)
					{
						// Recalculate _transformVector to avoid initial "jump" on recognize
						_transformVector = _touch2.location.subtract(_touch1.location);
					}
					return;
				}
			}
			
			if (_touch2 && !currTransformVector)
			{
				currTransformVector = _touch2.location.subtract(_touch1.location);
			}
			
			_offsetX = _location.x - prevLocation.x;
			_offsetY = _location.y - prevLocation.y;
			if (_touch2)
			{
				_rotation = Math.atan2(currTransformVector.y, currTransformVector.x) - Math.atan2(_transformVector.y, _transformVector.x);
				_scale = currTransformVector.length / _transformVector.length;
				_transformVector = _touch2.location.subtract(_touch1.location);
			}
			
			setState(state == GestureState.POSSIBLE ? GestureState.BEGAN : GestureState.CHANGED);
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (touchesCount == 0)
			{
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					setState(GestureState.ENDED);
				}
				else if (state == GestureState.POSSIBLE)
				{
					setState(GestureState.FAILED);
				}
			}
			else// == 1
			{
				if (touch == _touch1)
				{
					_touch1 = _touch2;
				}
				_touch2 = null;
				
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					updateLocation();
					setState(GestureState.CHANGED);
				}
			}
		}
		
		
		override protected function resetNotificationProperties():void
		{
			super.resetNotificationProperties();
			
			_offsetX = _offsetY = 0;
			_rotation = 0;
			_scale = 1;
		}
	}
}

================================================
FILE: src/org/gestouch/gestures/ZoomGesture.as
================================================
package org.gestouch.gestures
{
	import org.gestouch.core.GestureState;
	import org.gestouch.core.Touch;

	import flash.geom.Point;


	/**
	 * 
	 * @author Pavel fljot
	 */
	public class ZoomGesture extends AbstractContinuousGesture
	{
		public var slop:Number = Gesture.DEFAULT_SLOP;
		public var lockAspectRatio:Boolean = true;
		
		protected var _touch1:Touch;
		protected var _touch2:Touch;
		protected var _transformVector:Point;
		protected var _initialDistance:Number;
		
		
		public function ZoomGesture(target:Object = null)
		{
			super(target);
		}
		
		
		protected var _scaleX:Number = 1;
		public function get scaleX():Number
		{
			return _scaleX;
		}
		
		
		protected var _scaleY:Number = 1;
		public function get scaleY():Number
		{
			return _scaleY;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Public methods
		//
		// --------------------------------------------------------------------------
		
		override public function reflect():Class
		{
			return ZoomGesture;
		}
		
		
		
		
		// --------------------------------------------------------------------------
		//
		// Protected methods
		//
		// --------------------------------------------------------------------------
		
		override protected function onTouchBegin(touch:Touch):void
		{
			if (touchesCount > 2)
			{
				failOrIgnoreTouch(touch);
				return;
			}
			
			if (touchesCount == 1)
			{
				_touch1 = touch;
			}
			else// == 2
			{
				_touch2 = touch;
				
				_transformVector = _touch2.location.subtract(_touch1.location);
				_initialDistance = _transformVector.length;
			}
		}
		
		
		override protected function onTouchMove(touch:Touch):void
		{
			if (touchesCount < 2)
				return;
			
			var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
			
			if (state == GestureState.POSSIBLE)
			{
				const d:Number = currTransformVector.length - _initialDistance;
				const absD:Number = d >= 0 ? d : -d;
				if (absD < slop)
				{
					// Not recognized yet
					return;
				}
				
				if (slop > 0)
				{
					// adjust _transformVector to avoid initial "jump"
					const slopVector:Point = currTransformVector.clone();
					slopVector.normalize(_initialDistance + (d >= 0 ? slop : -slop));
					_transformVector = slopVector;
				}
			}
			
			
			if (lockAspectRatio)
			{
				_scaleX *= currTransformVector.length / _transformVector.length;
				_scaleY = _scaleX;
			}
			else
			{
				_scaleX *= currTransformVector.x / _transformVector.x;
				_scaleY *= currTransformVector.y / _transformVector.y;
			}
			
			_transformVector.x = currTransformVector.x;
			_transformVector.y = currTransformVector.y;
			
			updateLocation();
			
			if (state == GestureState.POSSIBLE)
			{
				setState(GestureState.BEGAN);
			}
			else
			{
				setState(GestureState.CHANGED);
			}
		}
		
		
		override protected function onTouchEnd(touch:Touch):void
		{
			if (touchesCount == 0)
			{
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					setState(GestureState.ENDED);
				}
				else if (state == GestureState.POSSIBLE)
				{
					setState(GestureState.FAILED);
				}
			}
			else//== 1
			{
				if (touch == _touch1)
				{
					_touch1 = _touch2;
				}
				_touch2 = null;
				
				if (state == GestureState.BEGAN || state == GestureState.CHANGED)
				{
					updateLocation();
					setState(GestureState.CHANGED);
				}
			}
		}
		
		
		override protected function resetNotificationProperties():void
		{
			super.resetNotificationProperties();
			
			_scaleX = _scaleY = 1;
		}
	}
}

================================================
FILE: src/org/gestouch/input/NativeInputAdapter.as
================================================
package org.gestouch.input
{
	import org.gestouch.core.IInputAdapter;
	import org.gestouch.core.TouchesManager;
	import org.gestouch.core.gestouch_internal;

	import flash.display.InteractiveObject;
	import flash.display.Stage;
	import flash.events.EventPhase;
	import flash.events.MouseEvent;
	import flash.events.TouchEvent;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;


	/**
	 * @author Pavel fljot
	 */
	public class NativeInputAdapter implements IInputAdapter
	{
		protected static const MOUSE_TOUCH_POINT_ID:uint = 0;
		
		protected var _stage:Stage;
		protected var _explicitlyHandleTouchEvents:Boolean;
		protected var _explicitlyHandleMouseEvents:Boolean;
		
		use namespace gestouch_internal;
		
		
		{
			Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
		}
		
		
		public function NativeInputAdapter(stage:Stage,
										   explicitlyHandleTouchEvents:Boolean = false,
										   explicitlyHandleMouseEvents:Boolean = false)
		{
			super();
			
			if (!stage)
			{
				throw new ArgumentError("Stage must be not null.");
			}
			
			_stage = stage;
			
			_explicitlyHandleTouchEvents = explicitlyHandleTouchEvents;
			_explicitlyHandleMouseEvents = explicitlyHandleMouseEvents;
		}
		
		
		protected var _touchesManager:TouchesManager;
		public function set touchesManager(value:TouchesManager):void
		{
			_touchesManager = value;
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		
		public function init():void
		{
			if (Multitouch.supportsTouchEvents || _explicitlyHandleTouchEvents)
			{
				_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
				_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, false);
				_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true);
				_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false);
				// Maximum priority to prevent event hijacking and loosing the touch
				_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, true, int.MAX_VALUE);
				_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, false, int.MAX_VALUE);
			}
			
			if (!Multitouch.supportsTouchEvents || _explicitlyHandleMouseEvents)
			{
				_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
				_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false);
			}
		}


		public function onDispose():void
		{
			_touchesManager = null;
			
			_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
			_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, false);
			_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true);
			_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false);
			_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, true);
			_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, false);
			
			_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
			_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false);
			unstallMouseListeners();
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		
		protected function installMouseListeners():void
		{
			_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true);
			_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false);
			// Maximum priority to prevent event hijacking
			_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true, int.MAX_VALUE);
			_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, int.MAX_VALUE);
		}
		
		
		protected function unstallMouseListeners():void
		{
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true);
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false);
			// Maximum priority to prevent event hijacking
			_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
			_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false);
		}
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		protected function touchBeginHandler(event:TouchEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;
			
			_touchesManager.onTouchBegin(event.touchPointID, event.stageX, event.stageY, event.target as InteractiveObject);
		}
		
		
		protected function touchMoveHandler(event:TouchEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;
			
			_touchesManager.onTouchMove(event.touchPointID, event.stageX, event.stageY);
		}
		
		
		protected function touchEndHandler(event:TouchEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;
			
			if (event.hasOwnProperty("isTouchPointCanceled") && event["isTouchPointCanceled"])
			{
				_touchesManager.onTouchCancel(event.touchPointID, event.stageX, event.stageY);
			}
			else
			{
				_touchesManager.onTouchEnd(event.touchPointID, event.stageX, event.stageY);
			}
		}
		
		
		protected function mouseDownHandler(event:MouseEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;
			
			const touchAccepted:Boolean = _touchesManager.onTouchBegin(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY, event.target as InteractiveObject);
			
			if (touchAccepted)
			{
				installMouseListeners();			
			}
		}
		
		
		protected function mouseMoveHandler(event:MouseEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;
			
			_touchesManager.onTouchMove(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY);
		}
		
		
		protected function mouseUpHandler(event:MouseEvent):void
		{
			// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
			// (to catch on empty stage) phases only
			if (event.eventPhase == EventPhase.BUBBLING_PHASE)
				return;			
			
			_touchesManager.onTouchEnd(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY);
			
			if (_touchesManager.activeTouchesCount == 0)
			{
				unstallMouseListeners();
			}
		}
	}
}


================================================
FILE: src/org/gestouch/input/TUIOInputAdapter.as
================================================
package org.gestouch.input
{
	import org.gestouch.core.IInputAdapter;
	import org.gestouch.core.TouchesManager;


	/**
	 * TODO: You can implement your own TUIO Input Adapter (and supply touchesManager with
	 * touch info), but IMHO it is way easier to use NativeInputAdapter and any TUIO library
	 * and manually dispatch native TouchEvents using DisplayObjectContainer#getObjectsUnderPoint()
	 * 
	 * @see NativeInputAdapter
	 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObjectContainer.html#getObjectsUnderPoint() DisplayObjectContainer#getObjectsUnderPoint() 
	 * 
	 * @author Pavel fljot
	 */
	public class TUIOInputAdapter implements IInputAdapter
	{
		public function init():void
		{
		}


		public function onDispose():void
		{
		}


		public function set touchesManager(value:TouchesManager):void
		{
		}
	}
}


================================================
FILE: src/org/gestouch/utils/GestureUtils.as
================================================
package org.gestouch.utils
{
	import flash.geom.Point;
	import flash.system.Capabilities;
	/**
	 * Set of constants.
	 * 
	 * @author Pavel fljot
	 */
	public class GestureUtils
	{
		/**
		 * Precalculated coefficient used to convert 'inches per second' value to 'pixels per millisecond' value.
		 */
		public static const IPS_TO_PPMS:Number = Capabilities.screenDPI * 0.001;
		/**
		 * Precalculated coefficient used to convert radians to degress.
		 */
		public static const RADIANS_TO_DEGREES:Number = 180 / Math.PI;
		/**
		 * Precalculated coefficient used to convert degress to radians.
		 */
		public static const DEGREES_TO_RADIANS:Number = Math.PI / 180;
		/**
		 * Precalculated coefficient Math.PI * 2
		 */
		public static const PI_DOUBLE:Number = Math.PI * 2;
		public static const GLOBAL_ZERO:Point = new Point();
	}
}

================================================
FILE: version.properties
================================================
project.version = 0.4.6
Download .txt
gitextract_orfcx0vt/

├── .gitignore
├── LICENSE
├── README.textile
├── build.template.properties
├── build.xml
├── libs/
│   └── starling.swc
├── pom.xml
├── src/
│   └── org/
│       └── gestouch/
│           ├── core/
│           │   ├── Gestouch.as
│           │   ├── GestureState.as
│           │   ├── GesturesManager.as
│           │   ├── IDisplayListAdapter.as
│           │   ├── IGestureTargetAdapter.as
│           │   ├── IInputAdapter.as
│           │   ├── ITouchHitTester.as
│           │   ├── Touch.as
│           │   ├── TouchesManager.as
│           │   └── gestouch_internal.as
│           ├── events/
│           │   └── GestureEvent.as
│           ├── extensions/
│           │   ├── native/
│           │   │   ├── DisplayObjectUtils.as
│           │   │   ├── NativeDisplayListAdapter.as
│           │   │   └── NativeTouchHitTester.as
│           │   └── starling/
│           │       ├── StarlingDisplayListAdapter.as
│           │       ├── StarlingTouchHitTester.as
│           │       └── StarlingUtils.as
│           ├── gestures/
│           │   ├── AbstractContinuousGesture.as
│           │   ├── AbstractDiscreteGesture.as
│           │   ├── Gesture.as
│           │   ├── LongPressGesture.as
│           │   ├── PanGesture.as
│           │   ├── PanGestureDirection.as
│           │   ├── RotateGesture.as
│           │   ├── SwipeGesture.as
│           │   ├── SwipeGestureDirection.as
│           │   ├── TapGesture.as
│           │   ├── TransformGesture.as
│           │   └── ZoomGesture.as
│           ├── input/
│           │   ├── NativeInputAdapter.as
│           │   └── TUIOInputAdapter.as
│           └── utils/
│               └── GestureUtils.as
└── version.properties
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (131K chars).
[
  {
    "path": ".gitignore",
    "chars": 150,
    "preview": "_resources/\nbin/\nbin-debug/\nbin-release/\nwiki/\n\n.svn/\n\nThumbs.db\n.DS_Store\nbuild.properties\n\n# Intellij project files\n*."
  },
  {
    "path": "LICENSE",
    "chars": 1090,
    "preview": "The MIT License\n\nCopyright (c) 2011 the original author or authors\n\nPermission is hereby granted, free of charge, to any"
  },
  {
    "path": "README.textile",
    "chars": 8656,
    "preview": "h1. Gestouch: multitouch gesture recognition library for Flash (ActionScript) development.\n\nGestouch is a ActionScript ("
  },
  {
    "path": "build.template.properties",
    "chars": 344,
    "preview": "# IMPORTANT Change to your local system paths before using ANT\n\n# Flex SDK related properties\nFLEX_HOME = path-to-flex-4"
  },
  {
    "path": "build.xml",
    "chars": 3165,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<project name=\"Gestouch\" default=\"release\" basedir=\".\">\n\n\t<!-- Thanks to Robotleg"
  },
  {
    "path": "pom.xml",
    "chars": 6247,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\t\t xmlns:xsi=\"http://www.w3.or"
  },
  {
    "path": "src/org/gestouch/core/Gestouch.as",
    "chars": 2929,
    "preview": "package org.gestouch.core\n{\n\timport flash.display.DisplayObject;\n\timport flash.utils.Dictionary;\n\timport flash.utils.get"
  },
  {
    "path": "src/org/gestouch/core/GestureState.as",
    "chars": 2390,
    "preview": "package org.gestouch.core\n{\n\timport flash.utils.Dictionary;\n\timport flash.errors.IllegalOperationError;\n\n\n\t/**\n\t * @auth"
  },
  {
    "path": "src/org/gestouch/core/GesturesManager.as",
    "chars": 10121,
    "preview": "package org.gestouch.core\n{\n\timport flash.display.DisplayObject;\n\timport flash.display.Shape;\n\timport flash.display.Stag"
  },
  {
    "path": "src/org/gestouch/core/IDisplayListAdapter.as",
    "chars": 224,
    "preview": "package org.gestouch.core\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic interface IDisplayListAdapter extends IGestureTarg"
  },
  {
    "path": "src/org/gestouch/core/IGestureTargetAdapter.as",
    "chars": 188,
    "preview": "package org.gestouch.core\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic interface IGestureTargetAdapter\n\t{\n\t\tfunction get "
  },
  {
    "path": "src/org/gestouch/core/IInputAdapter.as",
    "chars": 264,
    "preview": "package org.gestouch.core\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic interface IInputAdapter\n\t{\n\t\t/**\n\t\t * @private\n\t\t "
  },
  {
    "path": "src/org/gestouch/core/ITouchHitTester.as",
    "chars": 202,
    "preview": "package org.gestouch.core\n{\n\timport flash.geom.Point;\n\n\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic interface ITouchHitTes"
  },
  {
    "path": "src/org/gestouch/core/Touch.as",
    "chars": 2546,
    "preview": "package org.gestouch.core\n{\n\timport flash.geom.Point;\n\n\n\t/**\n\t * TODO:\n\t * - maybe add \"phase\" (began, moved, stationary"
  },
  {
    "path": "src/org/gestouch/core/TouchesManager.as",
    "chars": 5975,
    "preview": "package org.gestouch.core\n{\n\timport flash.display.Stage;\n\timport flash.geom.Point;\n\timport flash.utils.Dictionary;\n\timpo"
  },
  {
    "path": "src/org/gestouch/core/gestouch_internal.as",
    "chars": 141,
    "preview": "package org.gestouch.core\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic namespace gestouch_internal = \"org.gestouch.core::"
  },
  {
    "path": "src/org/gestouch/events/GestureEvent.as",
    "chars": 1234,
    "preview": "package org.gestouch.events\n{\n\timport org.gestouch.core.GestureState;\n\n\timport flash.events.Event;\n\n\n\t/**\n\t * @author Pa"
  },
  {
    "path": "src/org/gestouch/extensions/native/DisplayObjectUtils.as",
    "chars": 2405,
    "preview": "package org.gestouch.extensions.native\n{\nimport flash.display.DisplayObject;\nimport flash.display.DisplayObjectContainer"
  },
  {
    "path": "src/org/gestouch/extensions/native/NativeDisplayListAdapter.as",
    "chars": 2656,
    "preview": "package org.gestouch.extensions.native\n{\n\timport flash.display.DisplayObject;\n\timport flash.display.DisplayObjectContain"
  },
  {
    "path": "src/org/gestouch/extensions/native/NativeTouchHitTester.as",
    "chars": 773,
    "preview": "package org.gestouch.extensions.native\n{\n\timport flash.display.DisplayObject;\n\timport flash.display.Stage;\n\timport flash"
  },
  {
    "path": "src/org/gestouch/extensions/starling/StarlingDisplayListAdapter.as",
    "chars": 1409,
    "preview": "package org.gestouch.extensions.starling\n{\n\timport starling.display.DisplayObject;\n\timport starling.display.DisplayObjec"
  },
  {
    "path": "src/org/gestouch/extensions/starling/StarlingTouchHitTester.as",
    "chars": 800,
    "preview": "package org.gestouch.extensions.starling\n{\n\timport flash.geom.Point;\n\n\timport org.gestouch.core.ITouchHitTester;\n\n\timpor"
  },
  {
    "path": "src/org/gestouch/extensions/starling/StarlingUtils.as",
    "chars": 972,
    "preview": "package org.gestouch.extensions.starling\n{\n\timport starling.display.Stage;\n\timport starling.core.Starling;\n\n\timport flas"
  },
  {
    "path": "src/org/gestouch/gestures/AbstractContinuousGesture.as",
    "chars": 1179,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.gestures.Gesture;\n\n\n\t/**\n\t * Dispatched when the state of the gestu"
  },
  {
    "path": "src/org/gestouch/gestures/AbstractDiscreteGesture.as",
    "chars": 495,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.gestures.Gesture;\n\n\n\t/**\n\t * Dispatched when the state of the gestu"
  },
  {
    "path": "src/org/gestouch/gestures/Gesture.as",
    "chars": 16515,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.Gestouch;\n\timport org.gestouch.core.GestureState;\n\timport org."
  },
  {
    "path": "src/org/gestouch/gestures/LongPressGesture.as",
    "chars": 3061,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\n\timport flash."
  },
  {
    "path": "src/org/gestouch/gestures/PanGesture.as",
    "chars": 4235,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\n\timport flash."
  },
  {
    "path": "src/org/gestouch/gestures/PanGestureDirection.as",
    "chars": 246,
    "preview": "package org.gestouch.gestures\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic class PanGestureDirection\n\t{\n\t\tpublic static c"
  },
  {
    "path": "src/org/gestouch/gestures/RotateGesture.as",
    "chars": 3428,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\n\timport flash."
  },
  {
    "path": "src/org/gestouch/gestures/SwipeGesture.as",
    "chars": 7550,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\timport org.ges"
  },
  {
    "path": "src/org/gestouch/gestures/SwipeGestureDirection.as",
    "chars": 493,
    "preview": "package org.gestouch.gestures\n{\n\t/**\n\t * @author Pavel fljot\n\t */\n\tpublic class SwipeGestureDirection\n\t{\n\t\tpublic static"
  },
  {
    "path": "src/org/gestouch/gestures/TapGesture.as",
    "chars": 4162,
    "preview": "package org.gestouch.gestures\n{\n\timport flash.geom.Point;\n\timport org.gestouch.core.gestouch_internal;\n\timport org.gesto"
  },
  {
    "path": "src/org/gestouch/gestures/TransformGesture.as",
    "chars": 3926,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\n\timport flash."
  },
  {
    "path": "src/org/gestouch/gestures/ZoomGesture.as",
    "chars": 3581,
    "preview": "package org.gestouch.gestures\n{\n\timport org.gestouch.core.GestureState;\n\timport org.gestouch.core.Touch;\n\n\timport flash."
  },
  {
    "path": "src/org/gestouch/input/NativeInputAdapter.as",
    "chars": 6989,
    "preview": "package org.gestouch.input\n{\n\timport org.gestouch.core.IInputAdapter;\n\timport org.gestouch.core.TouchesManager;\n\timport "
  },
  {
    "path": "src/org/gestouch/input/TUIOInputAdapter.as",
    "chars": 870,
    "preview": "package org.gestouch.input\n{\n\timport org.gestouch.core.IInputAdapter;\n\timport org.gestouch.core.TouchesManager;\n\n\n\t/**\n\t"
  },
  {
    "path": "src/org/gestouch/utils/GestureUtils.as",
    "chars": 832,
    "preview": "package org.gestouch.utils\n{\n\timport flash.geom.Point;\n\timport flash.system.Capabilities;\n\t/**\n\t * Set of constants.\n\t *"
  },
  {
    "path": "version.properties",
    "chars": 23,
    "preview": "project.version = 0.4.6"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the fljot/Gestouch GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (109.8 KB), approximately 30.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!