penguin/AstroJS

Javascript framework for my blog

commit 4c93896df3f63cbd0f803a9c86d15fa637d7800e

author斟酌 鵬兄 <tgckpg@gmail.com>
date2015-08-14T10:12:10Z
subjectInitial commit
commit 4c93896df3f63cbd0f803a9c86d15fa637d7800e
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2015-08-14T10:12:10Z

    Initial commit
---
 .gitignore                                         |   6 +
 README.md                                          |  30 ++
 botan-rebuild.py                                   |  12 +
 botan-start.py                                     |  75 ++++
 botanjs/__init__.py                                |   0
 botanjs/classmap.py                                | 193 ++++++++
 botanjs/compressor/__init__.py                     |   0
 botanjs/compressor/closure.py                      |  51 +++
 botanjs/compressor/yui.py                          |  23 +
 botanjs/config.py                                  |   5 +
 botanjs/service/__init__.py                        |   0
 botanjs/service/jclassresv.py                      | 335 ++++++++++++++
 botanjs/service/jwork.py                           |  49 ++
 .../test/Dandelian.CSSAnimations.MovieClip.html    |  40 ++
 .../templates/test/System.Net.ClassLoader.html     |  25 ++
 .../service/templates/test/System.utils.IKey.html  |  13 +
 botanjs/service/templates/test_list.html           |  19 +
 botanjs/service/webapi.py                          |  72 +++
 botanjs/src/Components/Console.css                 |  71 +++
 botanjs/src/Components/Console.js                  | 177 ++++++++
 botanjs/src/Components/DockPanel.css               |  34 ++
 botanjs/src/Components/DockPanel.js                |  54 +++
 botanjs/src/Components/MessageBox.css              |  62 +++
 botanjs/src/Components/MessageBox.js               |  91 ++++
 botanjs/src/Components/Mouse/Clipboard.css         |   5 +
 botanjs/src/Components/Mouse/Clipboard.js          | 131 ++++++
 botanjs/src/Components/Mouse/ContextMenu.css       |  54 +++
 botanjs/src/Components/Mouse/ContextMenu.js        | 495 +++++++++++++++++++++
 botanjs/src/Components/Mouse/_this.js              |   3 +
 botanjs/src/Components/_this.js                    |   3 +
 botanjs/src/Dandelion/CSSAnimations/MovieClip.css  |   3 +
 botanjs/src/Dandelion/CSSAnimations/MovieClip.js   | 158 +++++++
 botanjs/src/Dandelion/CSSAnimations/_this.css      |  41 ++
 botanjs/src/Dandelion/CSSAnimations/_this.js       |   5 +
 botanjs/src/Dandelion/CSSReset.css                 |  43 ++
 botanjs/src/Dandelion/CSSReset.js                  |   5 +
 botanjs/src/Dandelion/IDOMElement.js               | 199 +++++++++
 botanjs/src/Dandelion/IDOMObject.js                | 149 +++++++
 botanjs/src/Dandelion/Swf/ExtAPI.css               |   7 +
 botanjs/src/Dandelion/Swf/ExtAPI.js                |  44 ++
 botanjs/src/Dandelion/Swf/_this.css                |  56 +++
 botanjs/src/Dandelion/Swf/_this.js                 | 173 +++++++
 botanjs/src/Dandelion/Window.js                    |  10 +
 botanjs/src/Dandelion/_this.js                     | 187 ++++++++
 botanjs/src/README.md                              |  64 +++
 botanjs/src/System/Cycle/Trigger.js                |  75 ++++
 botanjs/src/System/Cycle/_this.js                  | 108 +++++
 botanjs/src/System/Debug.js                        |  46 ++
 botanjs/src/System/Global.js                       |  16 +
 botanjs/src/System/Log.js                          |  46 ++
 botanjs/src/System/Net/ClassLoader.js              |  75 ++++
 botanjs/src/System/Net/_this.js                    | 141 ++++++
 botanjs/src/System/Policy/_this.js                 |  13 +
 botanjs/src/System/Tick.js                         |  50 +++
 botanjs/src/System/_this.js                        |   3 +
 botanjs/src/System/utils/DataKey.js                |  17 +
 botanjs/src/System/utils/EventKey.js               |  19 +
 botanjs/src/System/utils/IKey.js                   |  49 ++
 botanjs/src/System/utils/Perf.js                   |  49 ++
 botanjs/src/System/utils/_this.js                  |  41 ++
 botanjs/src/_this.js                               | 383 ++++++++++++++++
 botanjs/src/externs/BotanEvent.js                  |  16 +
 botanjs/src/externs/BotanJS.js                     |  18 +
 botanjs/src/externs/Components.MessageBox.js       |   7 +
 .../Components.Mouse.Clipboard.SwfHelperObj.js     |   9 +
 botanjs/src/externs/Components.Mouse.Clipboard.js  |  15 +
 .../src/externs/Components.Mouse.ContextMenu.js    |   2 +
 botanjs/src/externs/Components.Mouse.js            |   2 +
 botanjs/src/externs/Components.js                  |   2 +
 .../externs/Dandelion.CSSAnimations.MovieClip.js   |  14 +
 botanjs/src/externs/Dandelion.CSSAnimations.js     |   2 +
 botanjs/src/externs/Dandelion.IDOMElement.js       |  23 +
 botanjs/src/externs/Dandelion.IDOMObject.js        |  11 +
 botanjs/src/externs/Dandelion.Swf.ExtAPI.js        |   5 +
 botanjs/src/externs/Dandelion.Swf.js               |   7 +
 botanjs/src/externs/Dandelion.js                   |  38 ++
 botanjs/src/externs/System.Cycle.Trigger.js        |  11 +
 botanjs/src/externs/System.Cycle.js                |  17 +
 botanjs/src/externs/System.Debug.js                |   8 +
 botanjs/src/externs/System.Global.js               |   8 +
 botanjs/src/externs/System.Log.js                  |  16 +
 botanjs/src/externs/System.Net.js                  |  11 +
 botanjs/src/externs/System.Policy.js               |   5 +
 botanjs/src/externs/System.Tick.js                 |  13 +
 botanjs/src/externs/System.js                      |   2 +
 botanjs/src/externs/System.utils.DataKey.js        |   4 +
 botanjs/src/externs/System.utils.EventKey.js       |  10 +
 botanjs/src/externs/System.utils.IKey.js           |  11 +
 botanjs/src/externs/System.utils.Perf.js           |   8 +
 botanjs/src/externs/System.utils.js                |   9 +
 cache/.keep                                        |   0
 logs/.keep                                         |   0
 settings.ini                                       |  17 +
 93 files changed, 4724 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..19f98d7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*~
+*.swp
+*.pyc
+__pycache__
+cache/*
+logs/*
diff --git a/README.md b/README.md
index 27aa44c..b831f24 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,33 @@ A working concept of Js/Css framework for web browsers
 - Python-like, class oriented syntax structure
 - Everything is merged into one file for that page
 - css class inheritance
+
+### Disclaimer
+- This is a working concept. So it works on me. And may have a bunch of useless dependency. Use at your own risks!
+- It requires Python 3!
+
+
+### Documentation
+- Will be added later
+
+### Prerequisties
+#### For Service.WebAPI
+- pip install Flask
+- pip install Celery
+- pip install redis
+- pip install compressinja
+
+#### Environment varialbles
+```
+PYTHONPATH=<path to the parent of this directory>
+```
+
+#### For the compressor & cache, run:
+```
+celery -A botanjs.service.jwork worker
+```
+
+#### For the API Serivces, run:
+```
+./service/webapi.py
+```
diff --git a/botan-rebuild.py b/botan-rebuild.py
new file mode 100755
index 0000000..3c89c0a
--- /dev/null
+++ b/botan-rebuild.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+import os, sys
+sys.path.append( os.path.abspath( "." ) )
+
+from botanjs.service.jwork import app, JWork
+from botanjs.config import Config as config
+
+bmap = os.path.join( config["Paths"]["Cache"], "botanjs", "bmap.xml" )
+
+app.conf.update( BROKER_URL = config["BotanJS"]["CeleryBroker"] )
+
+JWork.buildClassMap.delay( config["BotanJS"]["SrcDir"], bmap )
diff --git a/botan-start.py b/botan-start.py
new file mode 100755
index 0000000..4e9fce3
--- /dev/null
+++ b/botan-start.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+
+import os, pwd, grp
+from botanjs.config import Config as config
+
+def drop_privileges( uid_name='nobody', gid_name='nogroup' ):
+	# Get the uid/gid from the name
+	running_uid = pwd.getpwnam( uid_name ).pw_uid
+	running_gid = grp.getgrnam( gid_name ).gr_gid
+
+	current_uid = os.getuid()
+
+	if current_uid != 0:
+		if running_uid == current_uid:
+			return True
+		print( "Service must be started by root" )
+		return False
+
+	# Remove group privileges
+	os.setgroups([])
+
+	# Try setting the new uid/gid
+	os.setgid( running_gid )
+	os.setuid( running_uid )
+
+	# Ensure a very conservative umask
+	old_umask = os.umask( 0o022 )
+
+	return True
+
+import shutil
+
+# Create the lock folder for celery
+lockDir = "/var/run/celery"
+os.makedirs( lockDir, exist_ok=True )
+shutil.chown( lockDir, config["Service"]["user"] )
+
+# Imediately drop the root privilege
+if drop_privileges( config["Service"]["user"], config["Service"]["group"] ) != True:
+	exit()
+
+import sys
+from subprocess import Popen
+
+sys.path.append( os.path.abspath( "." ) )
+
+from botanjs.service.webapi import WebAPI
+
+if __name__ == "__main__":
+
+	jwork = "botanjs.service.jwork"
+	nodeName = "botanNode1"
+
+	Popen([
+		"celery"
+		, "multi"
+		, "restart"
+		, nodeName
+		, "-A"
+		, jwork
+		, "worker"
+		, "--pidfile=/var/run/celery/" + jwork + ".pid"
+		, "--logfile=" + os.path.join( config["Paths"]["Log"], jwork + ".log" )
+		, "--workdir=" + config["Paths"]["Runtime"]
+		, "beat"
+		, "-l"
+		, "info"
+	]).communicate()
+
+	WebAPI(
+		jsCache = config["Paths"]["Cache"]
+		, jsRoot = config["BotanJS"]["SrcDir"]
+		, brokerURL = config["BotanJS"]["CeleryBroker"]
+	)
+
diff --git a/botanjs/__init__.py b/botanjs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/botanjs/classmap.py b/botanjs/classmap.py
new file mode 100644
index 0000000..bb9b386
--- /dev/null
+++ b/botanjs/classmap.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+
+import os;
+import re;
+import sys;
+
+from xml.dom import minidom
+from collections import defaultdict
+
+RegEx_N = re.compile( r"""
+		.*
+		__namespace
+		\s*\(
+			\s*(['"])([^\1]+)\1
+		\s*\)
+		.*
+		""", re.X )
+
+RegEx_I = re.compile( r"""
+		.*
+		__import
+		\s*\(
+			\s*(['"])([^\1]+)\1
+		\s*\)
+		.*
+		""", re.X )
+
+RegEx_V = re.compile( r"""
+		.*
+		ns
+		\s*\[
+		\s*NS_INVOKE
+		\s*\]
+		\s*\(
+			\s*(['"])([^\1]+)\1
+		\s*\)
+		.*
+		""", re.X )
+
+RegEx_E = re.compile( r"""
+		.*
+		ns
+		\s*\[
+		\s*NS_EXPORT
+		\s*\]
+		\s*\(
+			\s*EX_([A-Z_]+[A-Z])
+			\s*,
+			\s*(['"])([^\1]+)\2
+			\s*,
+			[^\)]+
+		\s*\)
+		.*
+		""", re.X )
+
+def classMeta( cf ):
+	ns = ""
+	imps = list()
+	exps = list()
+	for line in open( cf, "r" ):
+
+		m = RegEx_N.match( line )
+		if m:
+			ns = m.group(2)
+			continue
+
+		m = RegEx_I.match( line )
+		if m:
+			imps.append( m.group(2) )
+			continue
+
+		m = RegEx_V.match( line )
+		if m:
+			imps.append( ns + "." + m.group(2) )
+			continue
+
+		m = RegEx_E.match( line )
+		if m:
+			exps.append( [ m.group(1), m.group(3) ] )
+			continue
+
+	return [ ns, imps, exps ]
+
+def className( classFile ):
+	return ( os.path
+				.splitext( classFile )[0]
+				.replace( os.sep, "." )
+				.replace( "._this", "" )
+				.replace( "..BotanJS.", "" ) )
+
+# __export types definition => nodeName
+EX_CLASS = "class"
+EX_FUNC = "method"
+eDef = defaultdict( lambda: 'prop', { "CLASS": EX_CLASS, "FUNC": EX_FUNC } )
+
+class ClassMap:
+	head = None
+	DOM = None
+	R = None
+
+	def __init__( self, BotanRoot ):
+		self.R = BotanRoot
+		self.DOM = minidom.parseString( "<BotanJS></BotanJS>" )
+		head = os.path.join( self.R, "_this.js" )
+
+	def getNode( self, name, t = EX_CLASS ):
+		paths = name.split( "." )
+
+		currentNode = self.DOM.firstChild
+
+		# Step down the path and create the path if necessary
+		for path in paths:
+
+			l = currentNode.childNodes
+			for i in l:
+				if i.getAttribute( "name" ) == path:
+					currentNode = i
+					break
+
+			if currentNode.getAttribute( "name" ) != path:
+				newNode = self.DOM.createElement( t )
+				newNode.setAttribute( "name", path )
+				currentNode.appendChild( newNode )
+				currentNode = newNode
+
+		return currentNode
+
+
+
+	def skipFile( self, cf ):
+
+		if os.path.splitext( cf )[1] == ".js":
+			if cf == self.head:
+				return True
+			return False
+
+		return True;
+
+	def drawMap( self, ns, ci, ce, cf ):
+		nsNode = self.getNode( ns )
+
+		# Source every:
+		# Since namespace may differ from file name
+		# ns.__export may export to a different level
+		# Source the exported val explicitly means
+		# exports only available when source file is imported
+		srcEvery = ( ns != className( cf ) )
+
+		cf = cf.replace( self.R, "" )
+
+		if not srcEvery:
+			nsNode.setAttribute( "src", cf )
+
+		for ex in ce:
+			_t = eDef[ ex[0] ]
+			cNode = self.getNode( ns + "." + ex[1], t = _t )
+
+			if srcEvery:
+				# The import is for the defined class
+				if _t == EX_CLASS:
+					for imp in ci:
+						impNode = self.DOM.createElement( "import" )
+						impNode.appendChild( self.DOM.createTextNode( imp ) )
+						cNode.appendChild( impNode )
+
+				cNode.setAttribute( "src", cf )
+
+		# the file dose not export classes
+		# Hence it import for itself
+		if not srcEvery:
+			for imp in ci:
+				impNode = self.DOM.createElement( "import" )
+				impNode.appendChild( self.DOM.createTextNode( imp ) )
+				nsNode.appendChild( impNode )
+
+
+	def build( self ):
+		for root, dirs, files in os.walk( self.R ):
+
+			if root == self.R:
+				dirs.remove("externs")
+
+			for name in files:
+				classFile = os.path.join( root, name )
+
+				if self.skipFile( classFile ):
+					continue
+
+				ns, ci, ce = classMeta( classFile )
+				classFile = classFile.replace( self.R + os.path.sep, "" )
+				self.drawMap( ns, ci, ce, classFile )
+		return self.DOM.toxml()
+
diff --git a/botanjs/compressor/__init__.py b/botanjs/compressor/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/botanjs/compressor/closure.py b/botanjs/compressor/closure.py
new file mode 100755
index 0000000..a36d87e
--- /dev/null
+++ b/botanjs/compressor/closure.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+
+import os
+from tempfile import NamedTemporaryFile
+
+COMPILER = "/opt/utils/closure.jar"
+
+if not os.path.isfile( COMPILER ):
+	raise Exception( "Compiler not found" )
+
+
+COMPILER_OPTIONS = [
+	"--compilation_level ADVANCED_OPTIMIZATIONS"
+	, "--output_wrapper=\"(function(){%output%})();\""
+]
+
+
+class Wrapper:
+
+	C = None
+	# externs
+	E = ""
+
+	def __init__( self ):
+		self.C = "java -jar "+ COMPILER + " " + " ".join( COMPILER_OPTIONS )
+
+	def scanExterns( self, sdir ):
+		for root, dirs, files in os.walk( sdir ):
+			# Split file extensions
+			files = list( os.path.splitext( x ) for x in files )
+			files.sort()
+			for f in files:
+				files.remove( f ) if f[1] != ".js" else None
+
+			self.E = " --externs " + " --externs ".join(
+				os.path.join( root, x )
+				# join back extensions
+				for x in list( "".join( x ) for x in files )
+			)
+			break
+
+	def compress( self, loc ):
+		content = ""
+		with open( loc, "rb" ) as f:
+			content = f.read()
+
+		with NamedTemporaryFile() as f:
+			f.write( content[12:-5] )
+			os.system( self.C + self.E + " --js " + f.name + " --js_output_file " + loc[:-3] + ".c.js" )
+
+
diff --git a/botanjs/compressor/yui.py b/botanjs/compressor/yui.py
new file mode 100755
index 0000000..99341d6
--- /dev/null
+++ b/botanjs/compressor/yui.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+import os
+
+COMPILER = "/opt/utils/yuicompressor.jar"
+
+if not os.path.isfile( COMPILER ):
+	raise Exception( "Compiler not found" )
+
+COMPILER_OPTIONS = [
+	"--type css"
+]
+
+class Wrapper:
+
+	C = None
+
+	def __init__( self ):
+		self.C = "java -jar " + COMPILER + " " + " ".join( COMPILER_OPTIONS )
+
+	def compress( self, loc ):
+		os.system( self.C + " " + loc + " -o " + loc[:-4] + ".c.css" )
+
diff --git a/botanjs/config.py b/botanjs/config.py
new file mode 100755
index 0000000..31eaaa7
--- /dev/null
+++ b/botanjs/config.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python3
+import configparser
+
+Config = configparser.ConfigParser( interpolation = configparser.ExtendedInterpolation() )
+Config.read( "settings.ini" )
diff --git a/botanjs/service/__init__.py b/botanjs/service/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/botanjs/service/jclassresv.py b/botanjs/service/jclassresv.py
new file mode 100644
index 0000000..fca9931
--- /dev/null
+++ b/botanjs/service/jclassresv.py
@@ -0,0 +1,335 @@
+
+import os
+import re
+import base64
+import zlib
+import hashlib
+import xml.etree.ElementTree as ET
+
+PY_SEP = os.path.sep
+
+def wrapScope( src ):
+	return "(function(){" + src + "})();"
+
+class Resolver:
+	resolved = None
+
+	bMap = None
+	parentMap = None
+
+	EX_PROP = "prop"
+	EX_FUNC = "method"
+	EX_CLASS = "class"
+
+	def __init__( self, classMap ):
+		self.classMap = classMap
+		self._reload()
+
+	def _reload( self ):
+		self.bMap = ET.parse( self.classMap )
+		# ElementTree does not support Element.find( ".." )
+		# Make a parent map to work around
+		self.parentMap = { c:p for p in self.bMap.iter() for c in p }
+
+		self.resolved = []
+
+	def resource( self, elem ):
+		if "src" in elem.attrib:
+			return elem.attrib[ "src" ]
+
+		parent = self.parentMap[ elem ]
+
+		if parent != None:
+			return self.resource( parent )
+
+	def resolve( self, c, classList ):
+		self.resolved = []
+		self.__resolve( c, classList )
+
+	def __resolve( self, c, classList ):
+
+		lista = list( "[@name=\"" + x + "\"]" for x in c.split(".") )
+
+		fx = "/".join( self.EX_CLASS + x for x in lista[:-1] )
+
+		it = lista[-1]
+		# Test if class
+		elem = self.bMap.find( ".//" + fx + self.EX_CLASS + it )
+
+		if elem == None:
+			if fx != '': fx += "/"
+
+			# Test if prop
+			elem = self.bMap.find( ".//" + fx + self.EX_PROP + it )
+
+			# test if func
+			if elem == None:
+				elem = self.bMap.find( ".//" + fx + self.EX_FUNC + it )
+
+			if elem == None:
+				raise LookupError( "No such class: " + c )
+
+			imports = self.parentMap[ elem ].findall( "import" )
+
+		else:
+			imports = elem.findall( "import" )
+
+		self.resolved.append( c )
+
+		for imp in imports:
+			if imp.text not in self.resolved:
+				self.__resolve( imp.text, classList )
+
+		classList.append([ c, elem ])
+
+class BotanClassResolver:
+	R = ""
+	CR = None
+
+	classMap = ""
+	flagCompress = True
+	returnHash = False
+	resv = None
+
+	def __init__( self, jwork, BotanRoot, classMap, cacheRoot ):
+
+		self.JWork = jwork;
+		self.R = os.path.abspath( BotanRoot )
+		self.CR = os.path.abspath( cacheRoot )
+
+		os.makedirs( self.CR, 0o755, exist_ok = True )
+
+		if not os.path.exists( classMap ):
+			self.JWork.buildClassMap( self.R, classMap )
+
+		self.resv = Resolver( classMap )
+
+	def BotanFile( self, t ):
+		content = ""
+		with open( os.path.join( self.R, t ), "r" ) as f:
+			content = f.read()
+
+		return content
+
+	def BotanCache( self, t ):
+		content = ""
+		with open( t, "r" ) as f:
+			content = f.read()
+
+		return content
+
+	def cleanList( self, lista ):
+		olist = []
+		for i in lista:
+			if i not in olist:
+				olist.append( i )
+		return olist
+
+	def jsLookup( self, classList, classFiles ):
+		for c in classList:
+			src = self.resv.resource( c[1] )
+
+			if src == None:
+				raise LookupError( "Cannot find src file for: "  + c[0] )
+
+			if src not in classFiles:
+				classFiles.append( src )
+
+	def cssLookup( self, jsList, cssList ):
+
+		for f in jsList:
+			possibleList = []
+
+			cssFile = os.path.splitext( f )[0] + ".css"
+
+			if cssFile not in possibleList:
+				possibleList.append( cssFile )
+
+			f = f.split( PY_SEP )
+			l = len( f )
+
+			for i in range( 1, l ):
+				cssFile = PY_SEP.join( x for x in f[:-i] ) + PY_SEP + "@_this.css"
+				if cssFile not in possibleList:
+					possibleList.append( cssFile )
+
+			possibleList.sort()
+
+			for f in possibleList:
+				f = f.replace( "@_this.css", "_this.css" )
+				if os.path.exists( os.path.join( self.R, f ) ):
+					cssList.append( f )
+
+
+	def getCache( self, fileList, cName, mode ):
+		if self.CR == None:
+			return None
+
+		md5 = hashlib.md5( bytearray( "".join( fileList ), "utf-8" ) ).hexdigest()
+
+		cName[0] = oFHash = md5 + "." + mode
+		cFHash = md5 + ".c." + mode
+
+		# Raw file
+		oFile = os.path.join( self.CR, oFHash )
+		# Compressed file
+		cFile = os.path.join( self.CR, cFHash )
+
+		dates = list(
+			os.path.getmtime( os.path.join( self.R, x ) )
+			if os.path.exists( os.path.join( self.R, x ) ) else -1
+			for x in fileList
+		)
+
+		# Root file date
+		dates.append( os.path.getmtime( os.path.join( self.R, "_this.js" ) ) );
+
+		if self.useCache( cFile, dates ) and self.flagCompress == True:
+			return cFHash if self.returnHash else self.BotanCache( cFile )
+
+		elif self.useCache( oFile, dates ):
+			self.JWork.saveCache.delay(
+				oFile
+				# Content is None to initiate a compression
+				, None
+				, mode
+				, os.path.join( self.R, "externs" )
+			)
+
+			return oFHash if self.returnHash else self.BotanCache( oFile )
+
+	def useCache( self, f, dList ):
+		if not os.path.exists( f ):
+			return False
+
+		t = os.path.getmtime( f )
+
+		for i in dList:
+			if t < i:
+				return False
+
+		return True
+
+	def compileJs( self, cList, xList ):
+		md5 = [ None ]
+
+		for x in xList:
+			cList.remove( x ) if x in cList else None
+
+		cacheFile = self.getCache( cList, md5, "js" )
+
+		if cacheFile != None:
+			return cacheFile;
+
+		# The root file
+		outputJs = self.BotanFile( "_this.js" )
+
+
+		for f in cList:
+			path = (
+				os.path.splitext( f )[0]
+					.replace( PY_SEP, "." )
+					.replace( "._this", "" )
+			)
+			struct = ";BotanJS.define( \"" + path + "\" );"
+
+			outputJs += struct + self.BotanFile( f )
+
+		outputJs = wrapScope( outputJs )
+
+		[ self.JWork.saveCache if self.returnHash else self.JWork.saveCache.delay ][0] (
+			os.path.join( self.CR, md5[0] )
+			, outputJs
+			, "js"
+			, os.path.join( self.R, "externs" )
+		)
+
+		if self.returnHash:
+			return md5[0]
+
+		return outputJs
+
+	def compileCss( self, cList, xList ):
+		cssIList = []
+		cssXList = []
+		self.cssLookup( cList, cssIList )
+		self.cssLookup( xList, cssXList )
+
+		cList = []
+		xList = cssXList
+
+		for x in cssIList:
+			cList.append( x ) if x not in xList else None
+
+		md5 = [ None ]
+		cacheFile = self.getCache( cList, md5, "css" )
+
+		if cacheFile != None:
+			return cacheFile;
+
+		outputCss = ""
+
+		for f in self.cleanList( cList ):
+			outputCss += self.BotanFile( f )
+
+		[ self.JWork.saveCache if self.returnHash else self.JWork.saveCache.delay ][0] (
+			os.path.join( self.CR, md5[0] ), outputCss, "css"
+		)
+
+		if self.returnHash:
+			return md5[0]
+
+		return outputCss
+
+	def getAPI( self, code, mode ):
+		self.flagCompress = True
+		# Should reload on debug mode only
+		self.resv._reload()
+		flag = mode[0]
+		requestAPIs = code
+		sp = "/"
+
+		if flag == "o":
+			mode = mode[1:]
+		elif flag == "r":
+			mode = mode[1:]
+			self.flagCompress = False
+		else:
+			self.returnHash = True
+			requestAPIs = (
+				# decode -> decompress -> split
+				zlib.decompress( base64.b64decode( code, None, True ) )
+					.decode( "utf-8" )
+			)
+			sp = ","
+
+		# strip malicious
+		requestAPIs = (
+				requestAPIs
+					.replace( "[^A-Za-z\.\*" + re.escape( sp ) + " ]", "" )
+					.split( sp )
+			)
+
+		imports = []
+		excludes = []
+
+		for apis in requestAPIs:
+
+			if apis == None: continue
+
+			classList = []
+			lookupList = imports
+
+			if apis[0] == "-":
+				apis = apis[1:]
+				lookupList = excludes
+
+			self.resv.resolve( apis, classList )
+			self.jsLookup( classList, lookupList )
+
+		if mode == "js":
+			return self.compileJs( imports, excludes )
+		elif mode == "css":
+			return self.compileCss( imports, excludes )
+
+		raise TypeError( "Invalid mode: " + js )
diff --git a/botanjs/service/jwork.py b/botanjs/service/jwork.py
new file mode 100644
index 0000000..a6e7546
--- /dev/null
+++ b/botanjs/service/jwork.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+import os
+from celery import Celery
+from celery.utils.log import get_task_logger
+from botanjs.compressor.closure import Wrapper as ClosureWrapper
+from botanjs.compressor.yui import Wrapper as YUIWrapper
+from botanjs.classmap import ClassMap
+
+app = Celery( "botanJWork" )
+log = get_task_logger( __name__ )
+
+if os.path.exists( "settings.ini" ):
+	from botanjs.config import Config
+	app.conf.update( BROKER_URL = Config["BotanJS"]["CeleryBroker"] )
+
+class JWork:
+
+	@app.task()
+	def saveCache( location, content = None, mode = None, externs = "" ):
+		if content != None:
+			log.info( "Writing file(" +  str( len( content ) ) + "): " + os.path.abspath( location ) )
+			with open( location, "w" ) as f:
+				f.write( content )
+
+		if mode == "js":
+			JWork.compressJs.delay( location, externs )
+		elif mode == "css":
+			JWork.compressCss.delay( location )
+
+	@app.task()
+	def compressJs( md5, externs ):
+		log.info( "Compress js: " + md5 )
+		w = ClosureWrapper()
+		w.scanExterns( externs )
+		w.compress( md5 )
+
+	@app.task()
+	def compressCss( md5 ):
+		log.info( "Compress css: " + md5 )
+		w = YUIWrapper()
+		w.compress( md5 )
+
+	@app.task()
+	def buildClassMap( src, location ):
+		log.info( "Building Class Map" )
+		c = ClassMap( src )
+
+		with open( location, "w" ) as f:
+			f.write( c.build() )
diff --git a/botanjs/service/templates/test/Dandelian.CSSAnimations.MovieClip.html b/botanjs/service/templates/test/Dandelian.CSSAnimations.MovieClip.html
new file mode 100644
index 0000000..12bc6be
--- /dev/null
+++ b/botanjs/service/templates/test/Dandelian.CSSAnimations.MovieClip.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+</script>
+
+<link href="/ocss/Dandelion.CSSAnimations.MovieClip" rel="stylesheet" type="text/css" />
+<script src="/ojs/Dandelion.CSSAnimations.MovieClip"></script>
+<script>
+function load ()
+{
+    var CSSAnimations = BotanJS.import( "Dandelion.CSSAnimations.*" );
+    var mc = new CSSAnimations.MovieClip(
+        "http://file.astropenguin.net/blog/Gothic/__A.png"
+        // Row, Col
+        , 6   , 7
+        // wh
+        , 125 , 125
+        // Frame, start
+        , 42  , 0
+    );
+
+    // CSSAnimations.MouseOverMovie( mc );
+
+    document.body.appendChild( mc.stage );
+    mc.nextFrame();
+    mc.nextFrame();
+    mc.nextFrame();
+    mc.nextFrame();
+    mc.nextFrame();
+
+    mc.prevFrame();
+
+};
+</script>
+
+<body onload="load()">
+</body>
+</html>
diff --git a/botanjs/service/templates/test/System.Net.ClassLoader.html b/botanjs/service/templates/test/System.Net.ClassLoader.html
new file mode 100644
index 0000000..398e9b1
--- /dev/null
+++ b/botanjs/service/templates/test/System.Net.ClassLoader.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = { };
+</script>
+
+<link href="/ocss/System.Net.ClassLoader" rel="stylesheet" type="text/css" />
+<script src="/ojs/System.Net.ClassLoader"></script>
+<script>
+var Loader = BotanJS.import( "System.Net.ClassLoader" );
+
+var makeMessagebox = function()
+{
+    var MessageBox = BotanJS.import( "Components.MessageBox" );
+    new MessageBox( "Title", "Message" ).show();
+};
+
+var ldr = new Loader( "/" );
+ldr.load( "Components.MessageBox", makeMessagebox );
+</script>
+<body>
+</body>
+</html>
diff --git a/botanjs/service/templates/test/System.utils.IKey.html b/botanjs/service/templates/test/System.utils.IKey.html
new file mode 100644
index 0000000..bedef20
--- /dev/null
+++ b/botanjs/service/templates/test/System.utils.IKey.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = { };
+</script>
+
+<link href="/ocss/System.utils.IKey" rel="stylesheet" type="text/css" />
+<script src="/ojs/System.utils.IKey"></script>
+<body>
+</body>
+</html>
diff --git a/botanjs/service/templates/test_list.html b/botanjs/service/templates/test_list.html
new file mode 100644
index 0000000..4732e8b
--- /dev/null
+++ b/botanjs/service/templates/test_list.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Botan JS - List of Tests</title>
+<style>
+<!--
+body {
+    font-size: 1.2em;
+}
+-->
+</style>
+<body>
+<pre>
+{% for d in data %}
+    <a href="/test/{{ d }}">{{ d }}</a>
+    <br>
+{% endfor %}
+</pre>
+</body>
+</html>
diff --git a/botanjs/service/webapi.py b/botanjs/service/webapi.py
new file mode 100755
index 0000000..db3ef88
--- /dev/null
+++ b/botanjs/service/webapi.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+from flask import Flask
+from flask import Response
+from flask import render_template
+from botanjs.service.jclassresv import BotanClassResolver as JCResv
+from botanjs.service.jwork import app as CeleryApp, JWork
+import os
+
+class WebAPI:
+
+	app = None
+
+	BRoot = None
+	BMap = None
+	BCache = None
+
+	def __init__( self, jsRoot = "../src", jsCache = "/tmp", brokerURL = None ):
+
+		self.test_templates = os.path.join(
+			os.path.dirname( os.path.realpath( __file__ ) )
+			, "templates"
+			, "test"
+		)
+
+		self.BRoot = os.path.abspath( jsRoot )
+		self.BCache = os.path.join( jsCache, "botanjs" )
+		self.BMap = os.path.join( self.BCache, "bmap.xml" )
+
+		if brokerURL != None:
+			CeleryApp.conf.update( BROKER_URL = brokerURL )
+
+		self.app = Flask( __name__, static_url_path = '', static_folder = self.BCache )
+		self.app.jinja_env.add_extension( "compressinja.html.HtmlCompressor" )
+
+		self.app.add_url_rule( "/test" , view_func = self.r_test_list )
+		self.app.add_url_rule( "/test/<string:test_file>" , view_func = self.r_test )
+		self.app.add_url_rule( "/<path:mode>/<path:code>"          , view_func = self.api_request )
+
+		self.app.run( host="0.0.0.0" )
+
+	def r_test_list( self ):
+		for root, dirs, files in os.walk( self.test_templates ):
+			break
+
+		files.sort()
+		files = ( os.path.splitext( x )[0] for x in files )
+
+		return render_template( "test_list.html", data = files )
+
+	def r_test( self, test_file ):
+		return render_template( os.path.join( "test", test_file + ".html" ) )
+
+
+	def api_request( self, mode, code ):
+		try:
+			t = mode[1:]
+			if t == "js":
+				t = "application/javascript"
+			elif t == "css":
+				t = "text/css"
+
+			srvHandler = JCResv( JWork, self.BRoot, self.BMap, self.BCache )
+			return Response( srvHandler.getAPI( code, mode = mode ), mimetype = t )
+		except LookupError as e:
+			return str(e), 404
+		# except Exception as e:
+			# return str(e), 404
+
+		return "Invalid request", 404
+
+if __name__ == "__main__":
+	WebAPI()
diff --git a/botanjs/src/Components/Console.css b/botanjs/src/Components/Console.css
new file mode 100644
index 0000000..625abb9
--- /dev/null
+++ b/botanjs/src/Components/Console.css
@@ -0,0 +1,71 @@
+.dbg_statusbar {
+}
+
+.dbg_title {
+	padding-bottom: 0.2em;
+	float: left;
+}
+
+.dbg_lastMsg {
+	float: left;
+	font-family: monospace;
+	color: #AAA;
+	font-size: 1em;
+
+	white-space: nowrap;
+	max-width: 580px;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.dbg_led {
+	position: absolute;
+	right: 0.5em;
+
+	color: coral;
+
+	font-size: 0.75em;
+	text-shadow: 0 0 2px;
+}
+
+#dbg_led[error] {
+	color: red !important;
+}
+
+
+#time_stmp {
+	position: fixed;
+	bottom: 0;
+	right: 0;
+	font-size: 12px;
+	font-family: Arial,Helvetica,sans-serif;
+	padding: 0.5em;
+	background-color: rgba(0, 0, 0, 0.8);
+	color: white;
+}
+
+#debugCons[expanded] {
+	top: 0 !important;
+}
+
+#debugCons{
+	z-index: 99;
+}
+
+#debugWrap {
+	white-space: nowrap;
+}
+
+#debugWrap textarea.response {
+	line-height: 1.2;
+	height: 350px;
+	padding: 0.2em;
+
+	width: 800px;
+	color: #AAA;
+	background: #333;
+	outline: none;
+	border: none;
+
+    font-family: monospace;
+}
diff --git a/botanjs/src/Components/Console.js b/botanjs/src/Components/Console.js
new file mode 100644
index 0000000..0121d22
--- /dev/null
+++ b/botanjs/src/Components/Console.js
@@ -0,0 +1,177 @@
+(function(){
+	// Logger
+	// log things.
+	var ns = __namespace( "Components" );
+
+	/** @type {System.utils.Perf} */
+	var Perf                                = __import( "System.utils.Perf" );
+	/** @type {System.Cycle} */
+	var Cycle                               = __import( "System.Cycle" );
+	/** @type {System.Cycle.Tick} */
+	var sTick                               = __import( "System.Cycle.TICK" );
+	/** @type {System.Global} */
+	var _global                             = __import( "System.Global" );
+	/** @type {System.Log} */
+	var Log                                 = __import( "System.Log" );
+	/** @type {System.Debug} */
+	var debug                               = __import( "System.Debug" );
+	/** @type {Dandelion} */
+	var Dand                                = __import( "Dandelion" );
+	/** @type {Dandelion.IDOMElement} */
+	var IDOMElement                         = __import( "Dandelion.IDOMElement" );
+	/** @type {Components.DockPanel} */
+	var DockPanel                           = __import( "Components.DockPanel" );
+
+	var objTreeView = function( obj, level, prepend )
+	{
+		var c = "";
+		for (var p in obj)
+		{
+			if(typeof(obj[p]) == "object")
+			{
+				c +=
+				prepend + Array( level + 1 ).join(" ")
+				+ p + ":\n"
+				+ objTreeView(
+					obj[p]
+					, level + 2
+					, 0 < level ? prepend : Array( prepend.length ).join(" ")
+				);
+			}
+			else
+			{
+				c += prepend + Array(level + 1).join(" ") + p + ": " + obj[p] + "\n";
+			}
+		}
+		return c;
+	};
+
+	var Console = function ()
+	{
+		var stage = null
+		, response_txt = null
+		, lastMsg = null
+		, time_txt = null
+		, otop = ""
+		, cycle = 0
+		, sampling = 500
+		, led = null
+
+			, ticking = function () {
+				if( debugEnv )
+				{
+					time_txt.innerHTML = ( sTick.count - cycle ) + " cps, Sampling " + sampling + "ms";
+					cycle = sTick.count;
+					if(led) led.style.color = "yellowgreen";
+				}
+			}
+
+		, writeLine = function ( dat, type )
+		{
+			var res_txt = response_txt;
+
+			res_txt.value += "\n" + ( dat = ( dat != undefined ? dat.toString() : "undefined" ) );
+
+			lastMsg.textContent = lastMsg.innerText = dat;
+
+			// disabling color will make led blink since ticking is frequently setting the color to default
+			led.style.color = "";
+			if( otop ) stage.style.top = otop;
+			res_txt.scrollTop = res_txt.scrollHeight;
+		}
+
+		, writeError = function (dat)
+		{
+			writeLine( dat );
+			led.setAttribute( "error", 1 );
+			stage.setAttribute( "expanded", 1 );
+		}
+		, LogHandle = function( mesg, type )
+		{
+			if( type == Log.ERROR ) writeError( mesg );
+			else writeLine( mesg );
+		}
+		;
+
+
+		response_txt = Dand.wrap('textarea', null, 'response');
+		// Time stamp on the bottom right corner
+		time_txt = Dand.wrap('span', 'time_stmp');
+
+		// append child
+		document.body.appendChild(time_txt);
+
+		response_txt.readOnly = true;
+
+		stage = new DockPanel(
+			"debugCons"
+			, Dand.wrapc(
+				'dbg_statusbar'
+				, [
+					Dand.wrapc('dbg_title', "Debug Console(Press F9)")
+					, lastMsg = Dand.wrapc('dbg_lastMsg')
+					, led = Dand.wrap('div', 'dbg_led', 'dbg_led', '\u25CF')
+				]
+			)
+			, Dand.wrap( null, 'debugWrap', null, response_txt )
+			, "dtop"
+		);
+
+		var istage = IDOMElement( stage );
+
+		Cycle.next(
+			function ()
+			{
+				// get otop
+				otop = this.getDAttribute("top");
+			}.bind( istage )
+		);
+
+		var autoHide = function () { this.style.top = ""; }.bind(stage);
+		Cycle.perma('gTicker' + Perf.uuid, ticking, sampling);
+		Cycle.perma('gTicker' + Perf.uuid, autoHide, 3000);
+		debugEnv = true;
+
+		ticking();
+
+		var f9Binding = function ( e )
+		{
+			e = e || window.event;
+			if ( e.keyCode ) code = e.keyCode;
+			else if ( e.which ) code = e.which;
+
+			if ( code == 120 )
+			{
+				if( this.hasAttribute( "expanded" ) )
+				{
+					this.removeAttribute( "expanded" );
+					led.hasAttribute( "error" ) && led.removeAttribute( "error" );
+					this.style.top = otop;
+				}
+				else
+				{
+					this.setAttribute( "expanded", "1" );
+				}
+			}
+		}.bind( stage );
+
+		//Attach the var with the event = function 
+		if(document.addEventListener) document.addEventListener('keydown', f9Binding, false);
+		else if(document.attachEvent) document.attachEvent('onkeydown', f9Binding);
+		else document.onkeydown = f9Binding;
+
+		this.log = writeLine;
+		this.logError = writeError;
+
+		Log.registerHandler( LogHandle );
+		// This will output the debug info
+		if( window["debug_info"] )
+		{
+			debug.Info( objTreeView( debug_info, 0, "[Server] " ) );
+		}
+	};
+
+	__static_method( Console, "objTreeView", objTreeView );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Console", Console );
+})();
diff --git a/botanjs/src/Components/DockPanel.css b/botanjs/src/Components/DockPanel.css
new file mode 100644
index 0000000..e5a5887
--- /dev/null
+++ b/botanjs/src/Components/DockPanel.css
@@ -0,0 +1,34 @@
+.right {text-align: right;}
+
+.window {
+	z-index: 99;
+	border: 1px darkgrey solid;
+	background: #222;
+}
+
+.window > span {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display:inline-block;
+}
+
+.titleBar {
+	font-family: custom-sans;
+	font-size: 1em;
+	padding: 0.3em 0.5em;
+	cursor: default;
+	color: white;
+}
+
+div[class^="fdock"] {
+	position: fixed;
+	transition: all .5s ease-in-out;
+	-webkit-transition: all .5s ease-in-out;
+	-moz-transition: all .5s ease-in-out;
+}
+
+div.dleft { left: 0; }
+div.dtop { top: -100%; }
+div.dright { right: 0; }
+div.dbottom { bottom: 1.5em; }
+
diff --git a/botanjs/src/Components/DockPanel.js b/botanjs/src/Components/DockPanel.js
new file mode 100644
index 0000000..ffbf92a
--- /dev/null
+++ b/botanjs/src/Components/DockPanel.js
@@ -0,0 +1,54 @@
+(function(){
+	var ns = __namespace( "Components" );
+
+	/** @type {System.Cycle} */
+	var Cycle                            = __import( "System.Cycle" );
+	/** @type {System.utils.DataKey} */
+	var DataKey                          = __import( "System.utils.DataKey" );
+	/** @type {Dandelion} */
+	var Dand                             = __import( "Dandelion" );
+	/** @type {Dandelion.IDOMElement} */
+	var IDOMElement                      = __import( "Dandelion.IDOMElement" );
+
+	var DockPanel = function( w_id, w_title, w_content, align )
+	{
+		var w_div = Dand.wrapc('window')
+			, w_titleBar = Dand.wrapc('titleBar', w_title)
+		;
+
+		if( align.indexOf('top') != -1 )
+		{
+			w_div.appendChild( Dand.wrapc( 'contentPanel', w_content ) );
+			w_div.appendChild( w_titleBar );
+			w_div.style.position = "absolute";
+
+			Cycle.next(function ()
+			{
+				w_div.style.transition = "none";
+				w_div.style.top = -w_content.clientHeight + "px";
+				IDOMElement( w_div ).setAttribute( new DataKey( "top", w_div.style.top ) );
+				Cycle.next( function () { w_div.style.transition = ""; } );
+			});
+		}
+		else if( align.indexOf('bottom') != -1 )
+		{
+			w_div.appendChild(w_titleBar);
+			w_div.appendChild( Dand.wrapc( 'contentPanel', w_content ) );
+
+			Cycle.next(function ()
+			{
+				w_div.style.transition = "none";
+				// w_div.style.bottom = String(w_content.clientHeight) + "px";
+				// IDOMElement(w_div).addEventListeners();
+				Cycle.next(function () { w_div.style.transition = ""; });
+			});
+		}
+
+		w_div = Dand.wrap( null, w_id, 'fdock ' + align, w_div );
+
+		document.body.appendChild( w_div );
+		return w_div;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "DockPanel", DockPanel );
+})();
diff --git a/botanjs/src/Components/MessageBox.css b/botanjs/src/Components/MessageBox.css
new file mode 100644
index 0000000..e8845cc
--- /dev/null
+++ b/botanjs/src/Components/MessageBox.css
@@ -0,0 +1,62 @@
+.mbox_mask {
+	position: fixed;
+	width: 100%;
+	height: 100%;
+	top: 0;
+	left: 0;
+	background: rgba(0, 0, 0, 0.5);
+	z-index: 100;
+}
+
+.mbox_body {
+	min-width: 350px;
+	min-height: 100px;
+	max-width: 800px;
+	max-height: 600px;
+	position: absolute;
+	background: #222;
+
+	-moz-box-shadow: 0 0 10px black;
+	-webkit-box-shadow: 0 0 10px black;
+	box-shadow: 0 0 10px black;
+}
+
+.mbox_titlebar {
+	font-size: 1.2em;
+	padding: 0.35em;
+	color: white;
+	background: #181818;
+}
+
+.mbox_content {
+	min-height: 80px;
+	padding: 1em;
+	font-family: sans-serif;
+	color: white;
+
+	/*
+	-moz-box-shadow: inset 0 10px 10px -10px black;
+	-webkit-box-shadow: inset 0 10px 10px -10px black;
+	box-shadow: inset 0 10px 10px -10px black;
+	*/
+}
+
+.mbox_buttons {
+	text-align: right;
+	padding: 1em 0.5em;
+}
+
+.mbox_button {
+	text-align: center;
+}
+
+.mbox_button > span {
+	padding: 0.5em 1em;
+	margin: 0.2em;
+	color: white;
+	cursor: default;
+}
+
+.mbox_button > span:hover {
+	background: orangered;
+}
\ No newline at end of file
diff --git a/botanjs/src/Components/MessageBox.js b/botanjs/src/Components/MessageBox.js
new file mode 100644
index 0000000..1d29cb5
--- /dev/null
+++ b/botanjs/src/Components/MessageBox.js
@@ -0,0 +1,91 @@
+(function(){
+	var ns = __namespace( "Components" );
+
+	/** @type {System.Cycle} */
+	var Trigger                             = __import( "System.Cycle.Trigger" );
+	/** @type {Dandelion} */
+	var Dand                                = __import( "Dandelion" );
+
+	// __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+
+	var MessageBox = function ( title, content, yes, no, handler )
+	{
+		var _yes = Dand.wrap(
+			"span", null
+			, "mbox_button"
+			, Dand.wrap( "span", null, "comp flsf", yes || "OK" )
+		);
+
+		// left right button
+		_yes.onclick = function()
+		{
+			// if handler is set
+			if( this.clickHandler ) this.clickHandler( true );
+			document.body.removeChild( this.stage );
+			this.stage = null;
+		}.bind( this );
+
+		if ( no )
+		{
+			var _no = Dand.wrap(
+				"span", null
+				, "mbox_button"
+				, Dand.wrap( "span", null, "comp flsf", no )
+			);
+
+			_no.onclick = function()
+			{
+				if( this.clickHandler ) this.clickHandler( false );
+				document.body.removeChild( this.stage );
+				this.stage = null;
+			}.bind( this );
+		}
+
+
+		// set handler
+		if ( handler ) this.clickHandler = handler;
+
+		this.stage = Dand.wrapc(
+			"mbox_mask"
+			, this.mbox = Dand.wrapc(
+				"mbox_body cubic500"
+				, [
+					Dand.wrapc( "mbox_titlebar flsf", title )
+					, Dand.wrapc( "mbox_content", content )
+					, Dand.wrapc( "mbox_buttons", no ? [ _yes, _no ] : _yes )
+				]
+			)
+		);
+	};
+
+	MessageBox.prototype.setHandler = function( handler ) { this.clickHandler = handler };
+
+	MessageBox.prototype.show = function ()
+	{
+		document.body.appendChild( this.stage );
+		// Center the box
+		var m_style = this.mbox.style;
+
+		m_style.transition = "none";
+		m_style.top = m_style.left = "50%";
+		m_style.marginLeft = String( -0.5 * this.mbox.clientWidth ) + "px";
+		m_style.overflow = "hidden";
+		m_style.minHeight = m_style.opacity = m_style.height = 0;
+		// The interval in firefox seems independent to etablishing element style
+		// using heightTriggers-hack instead
+		Trigger.height(
+			this.mbox, 0
+			, function()
+			{
+				m_style.transition = m_style.minHeight = m_style.height = m_style.overflow = "";
+				m_style.marginTop = String( -0.5 * this.mbox.clientHeight ) + "px";
+
+				m_style.opacity = 1;
+			}.bind( this )
+		);
+
+		return this.stage;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "MessageBox", MessageBox );
+})();
diff --git a/botanjs/src/Components/Mouse/Clipboard.css b/botanjs/src/Components/Mouse/Clipboard.css
new file mode 100644
index 0000000..b1f3d45
--- /dev/null
+++ b/botanjs/src/Components/Mouse/Clipboard.css
@@ -0,0 +1,5 @@
+.ch_obj {
+	position: fixed;
+	/* 1-level higher than Context Menu*/
+	z-index: 101;
+}
\ No newline at end of file
diff --git a/botanjs/src/Components/Mouse/Clipboard.js b/botanjs/src/Components/Mouse/Clipboard.js
new file mode 100644
index 0000000..73229d6
--- /dev/null
+++ b/botanjs/src/Components/Mouse/Clipboard.js
@@ -0,0 +1,131 @@
+(function(){
+	var ns = __namespace( "Components.Mouse.Clipboard" );
+
+	/** @type {System.Global} */
+	var Global                           = __import( "System.Global" );
+	/** @type {System.Debug} */
+	var debug                            = __import( "System.Debug" );
+	/** @type {System.utils.Perf} */
+	var Perf                             = __import( "System.utils.Perf" );
+	/** @type {System.Cycle} */
+	var Cycle                            = __import( "System.Cycle" );
+	/** @type {Dandelion} */
+	var Dand                             = __import( "Dandelion" );
+	/** @type {Dandelion.Swf} */
+	var Swf                              = __import( "Dandelion.Swf" );
+	/** @type {Dandelion.Swf.ExtAPI} */
+	var ExtAPI                           = __import( "Dandelion.Swf.ExtAPI" );
+
+	var stage
+		, helperAddress = "/assets/swf/iClipboard.swf"
+		, helperId
+		, cCallback
+	;
+
+	/** @type {Components.Mouse.Clipboard.SwfHelperObj */
+	var clipboardHelper = null;
+
+	var init = function ()
+	{
+		if( stage ) return;
+		stage = Dand.wrapc('ch_obj no_transition no_transition_recursive');
+
+		if( Global.IE )
+		{
+			document.body.appendChild(stage);
+			Cycle.next(
+				function (){
+					stage.innerHTML = Swf.create(
+						helperAddress, 20, 20, helperId = Perf.uuid, 'always', 'transparent'
+					);
+				}
+			);
+		}
+		else
+		{
+			stage.appendChild(
+				Swf.create(
+					helperAddress, 20, 20, helperId = Perf.uuid, 'always', 'transparent'
+				)
+			);
+
+			document.body.appendChild(stage);
+		}
+
+		stage.style.visibility = "hidden";
+
+		ExtAPI.init();
+	};
+
+	// Using onmouse<action> properties since event needs to be unique. e.g. single handler
+
+	// Capture mouse by trigger
+	var capture = function ( trigger, callback )
+	{
+		cCallback = callback;
+
+		document.onmousemove = function (e)
+		{
+			if( trigger() )
+			{
+				Global.IE && (event || (event = e));
+				stage.style.visibility = "";
+				stage.style.left = ( e.pageX - 10 ) + "px";
+				stage.style.top = ( e.pageY - 10 ) + "px";
+			}
+		}
+	};
+
+	var setTextToCopy = function (textToCopy)
+	{
+		if( clipboardHelper && clipboardHelper.copy )
+		{
+			clipboardHelper.copy( textToCopy );
+			if( Global.debug && clipboardHelper.debug ) clipboardHelper.debug();
+		}
+		else
+		{
+			// This will loop though cycle by cycle until the movie is ready
+			Cycle.next(function () {
+				clipboardHelper = Swf( helperId );
+				setTextToCopy( textToCopy );
+			});
+		}
+	};
+
+	// Called by swf
+	var textCopied = function ()
+	{
+		if( cCallback ) cCallback();
+
+		debug.Info( "[Clipboard] Text copied" );
+		stage.style.visibility = "hidden";
+
+		// Release the focus on swf
+		clipboardHelper.blur();
+
+		document.onmousemove = null;
+	};
+
+	var onMouseOver = function ( callback )
+	{
+		if( callback == undefined ) return stage.onmouseover;
+		stage.onmouseover = callback;
+	};
+
+	var onMouseOut = function ( callback )
+	{
+		if( callback == undefined ) return stage.onmouseout;
+		stage.onmouseout = function () {
+			stage.style.visibility = "hidden";
+			this._callback();
+		}.bind({_callback: callback});
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "init", init );
+	ns[ NS_EXPORT ]( EX_FUNC, "setTextToCopy", setTextToCopy );
+	ns[ NS_EXPORT ]( EX_FUNC, "capture", capture );
+	ns[ NS_EXPORT ]( EX_VAR, "onMouseOver", onMouseOver );
+	ns[ NS_EXPORT ]( EX_VAR, "onMouseOut", onMouseOut );
+	ns[ NS_EXPORT ]( EX_FUNC, "_textCopied", textCopied );
+})();
diff --git a/botanjs/src/Components/Mouse/ContextMenu.css b/botanjs/src/Components/Mouse/ContextMenu.css
new file mode 100644
index 0000000..887ecf5
--- /dev/null
+++ b/botanjs/src/Components/Mouse/ContextMenu.css
@@ -0,0 +1,54 @@
+.contextMenu {
+	display: none;
+
+	cursor: default;
+	position: fixed;
+	/* 1 level higher than window */
+	z-index: 100;
+
+	min-width: 135px;
+
+	font-size: 0.8em;
+
+	font-family: sans-serif;
+}
+
+.contextMenu ul > li {
+	padding: 0.8em 2em 0.8em 0.8em;
+	background: white;
+	position: relative;
+
+	white-space: nowrap;
+}
+
+.contextMenu ul > li:hover, .contextMenu ul > li[hover] {
+	background: #EEE;
+}
+
+.contextMenu li > ul {
+	position: absolute;
+	top: 0;
+	min-width: 135px;
+	max-width: 200px;
+	left: 100%;
+}
+
+.contextMenu li.expandable:after {
+	content: '\25b6';
+
+    position: absolute;
+
+    width: 0.2em;
+    height: 100%;
+
+    line-height: 0;
+
+    top: 50%;
+    right: 1em;
+}
+
+.contextMenu ul {
+	list-style-type: none;
+	background-color: white;
+	border: 1px solid #DDD;
+}
diff --git a/botanjs/src/Components/Mouse/ContextMenu.js b/botanjs/src/Components/Mouse/ContextMenu.js
new file mode 100644
index 0000000..8a2a55a
--- /dev/null
+++ b/botanjs/src/Components/Mouse/ContextMenu.js
@@ -0,0 +1,495 @@
+(function(){
+	// Usage reference refers to Astroblog.AstroEdit.SiteLibrary::buildImageCanvas
+	var ns = __namespace( "Components.Mouse" );
+
+	/** @type {System.utils} */
+	var utils                            = __import( "System.utils" );
+	/** @type {System.utils.IKey} */
+	var IKey                             = __import( "System.utils.IKey" );
+	/** @type {System.utils.DataKey} */
+	var DataKey                          = __import( "System.utils.DataKey" );
+	/** @type {System.utils.EventKey} */
+	var EventKey                         = __import( "System.utils.EventKey" );
+	/** @type {System.Cycle} */
+	var Cycle                            = __import( "System.Cycle" );
+	/** @type {Dandelion} */
+	var Dand                             = __import( "Dandelion" );
+	/** @type {Dandelion.IDOMElement} */
+	var IDOMElement                      = __import( "Dandelion.IDOMElement" );
+	/** @type {Components.Mouse.Clipboard} */
+	var Clipboard 						 = __import( "Components.Mouse.Clipboard" );
+
+
+	var ContextMenu = function ( target, items, whichButton, menuInsideTarget, overrides )
+	{
+		if ( !target.id )
+			throw new Error( "[ContextMenu] target's id is missing" );
+
+		if( !( items instanceof Array ) )
+			throw new Error( "[ContextMenu] items is not an array" );
+
+		var stage = null
+		, mouseOnTarget = false
+		, mouseOnContext = false
+		, menuGroup = Dand.id("contextMenus") || (
+			document.body.appendChild( Dand.wrap( null, "contextMenus", "compx" ) )
+		)
+		, targetParent
+		, temp, i, j
+		, _items = []
+
+		, getFuncOverride = function(prop) { return utils.objGetProp( overrides, prop, "Function" ); }
+		, getStrOverride = function(prop) { return utils.objGetProp( overrides, prop, "String" ); }
+
+		, matchActions = function (element)
+		{
+			element = Dand.bubbleUp( element, isMenuItem );
+
+			for (i in _items)
+			{
+				// match attributes
+				if(element == _items[i].stage)
+				{
+					// perform action
+					return _items[i].key.handler(element);
+				}
+			}
+			return false;
+		}
+
+		, isMenuItem = function(e)
+		{
+			if( e === document ) return false;
+			return IDOMElement(e).getDAttribute("menuItem");
+		}
+
+		, addMenuIdentifier = function(dataKey)
+		{
+			var d = new DataKey("menuItem", 1);
+			return dataKey ? [dataKey, d] : d;
+		}
+
+		, contextMouseDown = function(event)
+		{
+			// IE is evil and doesn"t pass the event object
+			if (event == null)
+					event = window.event;
+
+			// we assume we have a standards compliant browser, but check if we have IE
+			var target = event.target != null ? event.target : event.srcElement;
+
+			// only show the context menu if the right mouse button is pressed
+			// and a hyperlink has been clicked (the code can be made more selective)
+
+			if (event.button == 0)
+				matchActions(target) || hideMenu();
+			else hideMenu();
+		}
+
+		, _chainHide = getFuncOverride("chainHide")
+		, _chainShow = getFuncOverride("chainShow")
+		, _hideMenu = getFuncOverride("hideMenu")
+		, hideMenu
+
+		, _showMenu = getFuncOverride("showMenu")
+		, showMenu = function (event)
+		{
+			if (event == null) event = window.event;
+			// hide the menu first to avoid an "up-then-over" visual effect
+			if(_showMenu)
+			{
+				_showMenu(stage, event);
+			}
+			else
+			{
+				stage.style.display = "none";
+				stage.style.left = event.clientX + "px";
+				stage.style.top = event.clientY + "px";
+				stage.style.display = "block";
+			}
+
+			if( hasCopyItem )
+			{
+				Clipboard.capture(copyTrigger, hideMenu);
+			}
+		}
+
+		, contextShow = function (event)
+		{
+			if(!mouseOnTarget) return false;
+
+			showMenu(event);
+			return true;
+		}
+
+		,contextDelete = function ()
+		{
+			targetParent.removeChild(stage);
+		}
+
+
+		, menuItems = []
+
+			, applyHover = function( elem )
+			{
+				if( targetParent == elem ) return false;
+
+				if( elem.nodeName == "LI" )
+				{
+					elem.setAttribute( "hover", 1 );
+				}
+				return true;
+			}
+
+			, removeHover = function( elem )
+			{
+				if( targetParent == elem ) return false;
+
+				if( elem.nodeName == "LI" )
+				{
+					elem.removeAttribute( "hover" );
+				}
+				return true;
+			}
+
+			, itemHover = function ()
+			{
+				Dand.chainUpApply( this, applyHover );
+			}
+			, itemOut = function ()
+			{
+				Dand.chainUpApply( this, removeHover );
+			}
+			, copyTrigger = function () { return mouseOverSwitch; }
+
+		, mouseOverSwitch = false
+		, hasCopyItem = false
+
+		, tryBindClipboard = function (target, evtKey)
+		{
+			// Event handler must return the string to copy
+
+			if( evtKey.keyName.toUpperCase().indexOf("COPY") != -1 )
+			{
+				Clipboard.init();
+				// Handle clipboard events
+				hasCopyItem = true;
+				IDOMElement(target).addEventListeners(
+					[
+						new EventKey("MouseOver", function ()
+							{
+								mouseOverSwitch = true;
+								Clipboard.setTextToCopy( this.strcpy() );
+								Clipboard.onMouseOver = itemHover.bind( this.stage );
+								Clipboard.onMouseOut = itemOut.bind( this.stage );
+							}.bind({ strcpy: evtKey.handler, stage: target })
+						)
+
+						, new EventKey("MouseDown", function ()
+							{
+								Clipboard.setTextToCopy( this.strcpy() );
+							}.bind({ strcpy: evtKey.handler })
+						)
+
+						, new EventKey("MouseOut", function ()
+							{
+								mouseOverSwitch = false;
+								itemOut.bind(this)();
+							}.bind( target )
+						)
+					]
+				);
+				return true;
+			}
+			return false
+		}
+
+		, stepSubListeners = function (target)
+		{
+			var lockedOn = null, j, overedOn
+				, nodes = target.childNodes
+			;
+
+			// Collapse menu chain, each chain is a route
+			// Like dominoes
+			target.chainRoute = target.chainRoute || [];
+
+			for(var i in nodes)
+			{
+				j = nodes[i];
+				if(!(j && j.nodeType == 1)) continue;
+
+				// If this item has a submenu item
+				if(j.lastChild.nodeType == 1)
+				{
+					target.chainRoute[target.chainRoute.length] = {menu: j.lastChild, next: stepSubListeners(j.lastChild)};
+				}
+
+				IDOMElement(j).addEventListener(
+					new EventKey("MouseOver", function ()
+					{
+						// record "this (overed)" item
+						overedOn = this;
+
+						var subItem = this.lastChild;
+
+						Cycle.delay(function ()
+						{
+							// Hide last displayed submenu if submenu is available
+							if(lockedOn && lockedOn != subItem)
+							{
+								_chainHide ? _chainHide(lockedOn) : (lockedOn.style.display = "none");
+								// bind the chains into chain reactor
+								chainReactor.bind(lockedOn)();
+							}
+
+							// if mouse is still on "this (overed)" item
+							if(overedOn == this)
+							{
+
+								// and if this item has sub item
+								if(subItem.nodeType == 1)
+								{
+									lockedOn = this.lastChild;
+									_chainShow ? _chainShow(subItem) : (subItem.style.display = "");
+								}
+							}
+						}.bind(this), 300);
+					}.bind(j))
+				);
+			}
+
+			return target.chainRoute.length ? chainReactor.bind(target) : null;
+		}
+
+			, chainReactor = function ()
+			{
+				if(this && this.chainRoute)
+				for(var i in this.chainRoute)
+				{
+					_chainHide ? _chainHide(this.chainRoute[i].menu) : (this.chainRoute[i].menu.style.display = "none");
+					if(this.chainRoute[i].next) this.chainRoute[i].next();
+				}
+			}
+
+		, createSubMenu = function (obj)
+		{
+			if(!(obj.name || obj.items)) return Dand.wrapna("ul", addMenuIdentifier());
+
+			// Begin create submenu
+			var itemroot = Dand.wrapna("ul", addMenuIdentifier())
+				, j = obj.items;
+
+			for (var i in j)
+			{
+
+				if(!(j[i] instanceof IKey))
+				{
+					// step inside level
+					itemroot.appendChild(createSubMenu(j[i]));
+					continue;
+				}
+
+				// Switch inside if this key has an id-ed event
+				var eKey = (j[i].keyValue instanceof EventKey) ? j[i].keyValue : j[i];
+
+				itemroot.appendChild( temp = Dand.wrap( "li", null, null, j[i].keyName, addMenuIdentifier(new DataKey("id", eKey.keyName))) );
+
+				// store EventKey & reference
+				_items[_items.length] = {key: eKey, stage: temp};
+
+				tryBindClipboard(temp, eKey);
+			}
+
+
+			// Datakey is set for matching action
+			var stage = Dand.wrap("li", null, "expandable", [ Dand.textNode(obj.name), itemroot ], addMenuIdentifier(new DataKey("id", obj.name)));
+
+			// Set handler if available
+			if(obj.handler)
+			{
+				_items[_items.length] = {key: new EventKey(obj.name, obj.handler), stage: stage};
+			}
+			else
+			{
+				// Prevent menu disappear when clicked
+				_items[_items.length] = {key: new EventKey(obj.name, function() { return true; }), stage: stage};
+			}
+
+
+			_hideMenu ? _hideMenu(itemroot) : (itemroot.style.display = "none");
+
+			return stage;
+		};
+
+		for ( i in items )
+		{
+			if ( ( j = items[i] ) )
+			{
+				if(!(j instanceof IKey))
+				{
+					menuItems[menuItems.length] = createSubMenu(j);
+					continue;
+				}
+
+				// Switch inside if this key has an id-ed event
+				var eKey = (j.keyValue instanceof EventKey) ? j.keyValue : j;
+				temp = menuItems[menuItems.length] = Dand.wrapne("li", j.keyName, addMenuIdentifier(new DataKey("id", eKey.keyName)));
+
+				// store EventKey & reference
+				_items[_items.length] = {key: eKey, stage: temp};
+
+				tryBindClipboard(temp, j);
+			}
+		}
+
+		stage = Dand.wrap(
+			"div", null
+			, getStrOverride("class") || "contextMenu"
+			, temp = Dand.wrapne( "ul", menuItems, addMenuIdentifier() )
+		);
+
+		var chain = stepSubListeners(temp);
+		var hideMenu = function ()
+		{
+			_hideMenu ? _hideMenu( stage ) : ( stage.style.display = "none" );
+
+			// Root collapse chain
+			if( chain ) chain();
+		};
+
+		if( menuInsideTarget && menuInsideTarget.nodeType == 1 )
+		{
+			( targetParent = menuInsideTarget ).appendChild(stage);
+		}
+		else
+		{
+			( targetParent = menuGroup ).appendChild(stage);
+		}
+
+		stage.zindex = 99;
+
+		// Handle which button
+		i = temp = false;
+		if(whichButton == "both") i = temp = true;
+		else if (whichButton == "LMB") i = true;
+		else if (whichButton == "RMB") temp = true;
+
+		if (i)
+		{
+			// Handle LMB
+			target.onclick = function (event)
+			{
+				if ( Dand.id( target.id ) )
+				{
+					showMenu(event);
+					document.body.onmousedown = contextMouseDown;
+				}
+				cleanUpActionList();
+			}
+		}
+
+		// Register with RMB switch
+		registerBodyClickAction(contextMouseDown, contextShow, contextDelete, target.id, temp);
+
+		temp = IDOMElement(target);
+		temp.addEventListener("MouseOver", function() { mouseOnTarget = true; });
+		temp.addEventListener("MouseOut", function() { mouseOnTarget = false; });
+
+		temp = IDOMElement(stage);
+		temp.addEventListener("MouseOver", function() { mouseOnContext = true; });
+		temp.addEventListener("MouseOut", function() { mouseOnContext = false; });
+
+		IDOMElement(document.body).addEventListener("ContextMenu", bodyOnContext);
+
+		this.getItemByKey = function (key)
+		{
+			for (i in _items)
+			{
+				// match attributes
+				if(IDOMElement(_items[i].stage).getDAttribute("id") == key)
+				{
+					return _items[i];
+				}
+			}
+			return false;
+		}
+	};
+
+	var bodyClickPairs = [];
+
+	var bodyOnContext = function (event)
+	{
+		var i, j;
+		for (i in bodyClickPairs)
+		{
+			if ( Dand.id( ( j = bodyClickPairs[i] )._id ) )
+			{
+				if(j._rmb && j._context(event))
+				{
+					document.body.onmousedown = j._down;
+
+					if( event && event.preventDefault ) event.preventDefault();
+					return false;
+				}
+			}
+			else
+			{
+				// target lost
+				j._lost();
+				delete bodyClickPairs[i];
+			}
+		}
+		return true;
+	};
+
+	var cleanUpActionList = function ()
+	{
+		var i, j;
+		for ( i in bodyClickPairs )
+		{
+			if ( !Dand.id( ( j = bodyClickPairs[i] )._id ) )
+			{
+				// target lost
+				j._lost();
+				delete bodyClickPairs[i];
+			}
+		}
+	};
+
+	var registerBodyClickAction = function ( _mousedown, _oncontext, _targetlost, id, _useRMB )
+	{
+		var i = tryGetAction(id);
+		if(i)
+		{
+			// Remove previous menu
+			i._lost();
+			// Perform update
+			i._down = _mousedown;
+			i._context = _oncontext;
+			i._lost = _targetlost;
+			i._rmb = _useRMB;
+		}
+		else
+		{
+			bodyClickPairs[bodyClickPairs.length] = {
+				_down: _mousedown, _context: _oncontext, _lost: _targetlost, _id: id, _rmb: _useRMB
+			};
+		}
+	};
+
+	var tryGetAction = function (id)
+	{
+		for (i in bodyClickPairs)
+		{
+			if (bodyClickPairs[i]._id == id)
+			{
+				return bodyClickPairs[i];
+			}
+		}
+		return false;
+	};
+
+
+	ns[ NS_EXPORT ]( EX_CLASS, "ContextMenu", ContextMenu );
+})();
diff --git a/botanjs/src/Components/Mouse/_this.js b/botanjs/src/Components/Mouse/_this.js
new file mode 100644
index 0000000..bcdd73d
--- /dev/null
+++ b/botanjs/src/Components/Mouse/_this.js
@@ -0,0 +1,3 @@
+(function(){
+	var ns = __namespace( "Components.Mouse" );
+})();
diff --git a/botanjs/src/Components/_this.js b/botanjs/src/Components/_this.js
new file mode 100644
index 0000000..2c224e9
--- /dev/null
+++ b/botanjs/src/Components/_this.js
@@ -0,0 +1,3 @@
+(function(){
+	var ns = __namespace( "Components" );
+})();
diff --git a/botanjs/src/Dandelion/CSSAnimations/MovieClip.css b/botanjs/src/Dandelion/CSSAnimations/MovieClip.css
new file mode 100644
index 0000000..e223134
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSAnimations/MovieClip.css
@@ -0,0 +1,3 @@
+.motion_block {
+	display: inline-block;
+}
\ No newline at end of file
diff --git a/botanjs/src/Dandelion/CSSAnimations/MovieClip.js b/botanjs/src/Dandelion/CSSAnimations/MovieClip.js
new file mode 100644
index 0000000..dccc0de
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSAnimations/MovieClip.js
@@ -0,0 +1,158 @@
+(function(){
+	var ns = __namespace( "Dandelion.CSSAnimations" );
+
+	/** @type {System.utils.EventKey} */
+	var EventKey                         = __import( "System.utils.EventKey" );
+	/** @type {System.Cycle.Trigger} */
+	var Trigger                          = __import( "System.Cycle.Trigger" );
+	/** @type {Dandelion} */
+	var Dand                             = __import( "Dandelion" );
+	/** @type {Dandelion.IDOMElement} */
+	var IDOMElement                      = __import( "Dandelion.IDOMElement" );
+
+	var MovieClip = function (
+		__target
+		, __row, __col
+		, __width, __height
+		, frame, start
+	)
+	{
+		if( frame == __row * __col ) frame = 0;
+
+		var canvas = Dand.wrapc( "motion_block no_transition" )
+			, r = __row - 1
+			, c = __col - 1
+			, w = __width
+			, h = __height
+			, i = 0, j = 0
+			, k = 0, l = frame - 1
+		;
+
+		var _next, _prev, _goto;
+
+		if( frame )
+		{
+			_next = function ()
+			{
+				( c < ++ i ) && ( ( i = 0 ) || ( ( r < ++ j ) && ( j = 0 ) ) );
+				( l < ++ k ) && ( i = j = k = 0 );
+			};
+
+			_prev = function ()
+			{
+				( -- k < 0 ) && ( k = l );
+				_goto( k );
+			};
+
+			_goto = function ( f )
+			{
+				for ( i = j = k = 0; 0 < f; f -- ) _next();
+			};
+		}
+		else
+		{
+			_next = function ()
+			{
+				( c < ++ i ) && ( ( i = 0 ) || ( ( r < ++ j ) && ( j = 0 ) ) );
+			};
+
+			_prev = function ()
+			{
+				( -- i < 0 ) && ( ( i = c ) && ( ( -- j < 0 ) && ( j = r ) ) );
+			};
+
+			_goto = function ( f )
+			{
+                for ( i = j = k = 0; k < f; k ++ ) _next();
+			};
+		}
+
+		canvas.style.backgroundImage = "url(" + __target + ")";
+		canvas.style.width = w + "px";
+		canvas.style.height = h + "px";
+		canvas.style.backgroundPosition = "0px 0px";
+
+		var updateCanvas = function()
+		{
+			canvas.style.backgroundPosition = ( -i * w ) + "px " + ( -j * h ) + "px";
+		};
+
+		// At 0 position
+		var at0 = function() { return ( i == 0 && j == 0 ) };
+
+		var obj = {
+			_next: function() { _next(); updateCanvas(); return at0(); }
+			, _prev: function() { _prev(); updateCanvas(); return at0(); }
+			, _goto: function( n ) { _goto( n ); updateCanvas(); }
+		};
+
+		this["stage"] = canvas;
+		this["nextFrame"] = this.nextFrame.bind( obj );
+		this["prevFrame"] = this.prevFrame.bind( obj );
+		this["gotoFrame"] = this.gotoFrame.bind( obj );
+	};
+
+	MovieClip.prototype.nextFrame = function ()
+	{
+		return this._next();
+	};
+
+	MovieClip.prototype.prevFrame = function ()
+	{
+		return this._prev();
+	};
+
+	MovieClip.prototype.gotoFrame = function ( frameNumber )
+	{
+		return this._goto( frameNumber );
+	};
+
+	MovieClip.prototype.stage = null;
+
+	/** @param {Dandelion.CSSAnimations.MovieClip} mc
+	 *  @param {Boolean} whenStatic
+	 */
+	var MouseOverMovie = function ( mc, whenStatic )
+	{
+		if ( mc instanceof MovieClip )
+		{
+			var canRegister = true;
+			var terminate = false;
+
+			var registrar = function () { return mc.nextFrame() || terminate; }; 
+
+			var handler = function ()
+			{
+				mc.gotoFrame( whenStatic );
+				canRegister = true;
+				terminate = false;
+			};
+
+			var mouseOverHandler = function ( e )
+			{
+				if( canRegister )
+				{
+					canRegister = false;
+					mc.gotoFrame(0);
+					Trigger.register( registrar, handler, 33 );
+				}
+			};
+
+			var mouseOutHandler = function ( e )
+			{
+				if( !canRegister )
+					terminate = true;
+			};
+
+			mc.gotoFrame( whenStatic );
+
+			IDOMElement( mc.stage ).addEventListeners([
+				new EventKey( "MouseOver", mouseOverHandler )
+				, new EventKey( "MouseOut", mouseOutHandler )
+			]);
+		}
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "MovieClip", MovieClip );
+	ns[ NS_EXPORT ]( EX_FUNC, "MouseOverMovie", MouseOverMovie );
+})();
diff --git a/botanjs/src/Dandelion/CSSAnimations/_this.css b/botanjs/src/Dandelion/CSSAnimations/_this.css
new file mode 100644
index 0000000..7603650
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSAnimations/_this.css
@@ -0,0 +1,41 @@
+.comp, .compx * {
+    -webkit-transition: all .25s ease-out;
+    -moz-transition: all .25s ease-out;
+    -o-transition: all .25s ease-out;
+    transition: all .25s ease-out;
+}
+
+.rcomp, .rcompx * {
+    -webkit-transition: none;
+    -moz-transition: none;
+    -o-transition: none;
+    transition: none;
+}
+
+.cubic500 {
+    -webkit-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -moz-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -ms-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -o-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+
+    -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.cubic200 {
+    -webkit-transition: all 200ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -moz-transition: all 200ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -ms-transition: all 200ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -o-transition: all 200ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    transition: all 200ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+
+    -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+    transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
diff --git a/botanjs/src/Dandelion/CSSAnimations/_this.js b/botanjs/src/Dandelion/CSSAnimations/_this.js
new file mode 100644
index 0000000..e4c99e6
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSAnimations/_this.js
@@ -0,0 +1,5 @@
+/*
+(function(){
+	var ns = __namespace( "Dandelion.CSSAnimations" );
+})();
+*/
diff --git a/botanjs/src/Dandelion/CSSReset.css b/botanjs/src/Dandelion/CSSReset.css
new file mode 100644
index 0000000..e90dc19
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSReset.css
@@ -0,0 +1,43 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	font: inherit;
+	vertical-align: baseline;
+}
+sup, sub {
+	vertical-align: baseline;
+	font-size: 80%;
+}
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+	display: block;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
diff --git a/botanjs/src/Dandelion/CSSReset.js b/botanjs/src/Dandelion/CSSReset.js
new file mode 100644
index 0000000..3f714e7
--- /dev/null
+++ b/botanjs/src/Dandelion/CSSReset.js
@@ -0,0 +1,5 @@
+/*
+(function(){
+	var ns = __namespace( "Dandelion.CSSReset" );
+})();
+*/
diff --git a/botanjs/src/Dandelion/IDOMElement.js b/botanjs/src/Dandelion/IDOMElement.js
new file mode 100644
index 0000000..ccffbb7
--- /dev/null
+++ b/botanjs/src/Dandelion/IDOMElement.js
@@ -0,0 +1,199 @@
+(function(){
+	var ns = __namespace( "Dandelion" );
+
+	/** @type {System.utils.IKey} */
+	var IKey                         = __import( "System.utils.IKey" );
+	/** @type {System.utils.Perf} */
+	var Perf                         = __import( "System.utils.Perf" );
+
+	var wrap = ns[ NS_INVOKE ]( "wrap" );
+	var IDOMObject = ns[ NS_INVOKE ]( "IDOMObject" );
+
+	// IDOMElement, augmented element wrapper utilizing IKeys
+	var IDOMElement = function (element, sw)
+	{
+		if (element instanceof IDOMElement) return element;
+
+		if (sw)
+		{
+			IDOMObject.call( this, element, true );
+
+			this["getDAttribute"] = this.getDAttribute.bind(element);
+
+			this["lootChildren"] = this.lootChildren.bind(element);
+
+			this["foreach"] = this.foreach.bind(element);
+			this["reverseChild"] = this.reverseChild.bind(element);
+			this["first"] = this.first.bind(element);
+			this["last"] = this.last.bind(element);
+			this["contains"] = this.contains.bind(element);
+
+			// Org values
+			this["style"] = element.style;
+			this["hasAttribute"] = function ( key ) { this.hasAttribute( key ); }.bind( element );
+			this["removeAttribute"] = function ( key ) { this.removeAttribute( key ); }.bind( element );
+			this["element"] = element;
+
+			// Overrides
+			this["setAttribute"] = this.setAttribute.bind( element );
+		}
+		else if ( element && element[ "nodeType" ] != undefined && element.nodeType == 1 )
+		{
+			return new IDOMElement( element, true );
+		}
+		else if( element === undefined )
+		{
+			return new IDOMElement( wrap(), true );
+		}
+		else
+		{
+			throw new Error( "[Dandelion.IDOMElement] Invalid argument" );
+		}
+		return this;
+	};
+
+	__extends( IDOMElement, IDOMObject );
+
+	IDOMElement.prototype.setAttribute = function( k, v )
+	{
+		if( k instanceof IKey )
+		{
+			this.setAttribute( k.keyName, k.keyValue );
+		}
+		else if( k instanceof Array )
+		{
+			for ( var i in k )
+			{
+				if ( k[i] instanceof IKey )
+				{
+					this.setAttribute( k[i].keyName, k[i].keyValue );
+				}
+			}
+		}
+		else
+		{
+			this.setAttribute( k, v );
+		}
+	};
+
+	IDOMElement.prototype.lootChildren = function ( element )
+	{
+		var _nodes = element.childNodes;
+		while(_nodes.length)
+		{
+			this.appendChild( element.removeChild( _nodes[0] ) );
+		}
+	};
+
+	IDOMElement.prototype.getDAttribute = function(name)
+	{
+		var i = this.getAttribute("data-" + name);
+		return i && decodeURIComponent(i);
+	};
+
+	IDOMElement.prototype.foreach = function(type, callback)
+	{
+		var c = Array.apply( [], this.childNodes ), l = c.length;
+		for(var i = 0; i < l; i ++)
+		{
+			if (c[i].nodeType == type)
+			{
+				callback(c[i], this);
+			}
+		}
+	};
+
+	var matchNone = function() { return false; };
+	var matchType = function( c, t ) { return c.nodeType == t; };
+	var matchName = function( c, t ) { return c.nodeName == t; };
+
+	var getMatch = function( type )
+	{
+		type = typeof( type );
+		if( type == "number" ) return matchType;
+		else if( type == "string" ) return matchName;
+
+		return matchNone;
+	};
+
+	IDOMElement.prototype.first = function ( type, callback )
+	{
+		var c = this.childNodes;
+		var l = c.length;
+		var elem = null;
+		var tc = getMatch( type );
+
+		for( var i = 0; i < l; i ++ )
+		{
+			if ( tc( c[i], type ) )
+			{
+				if( callback === undefined || callback( c[i], this ) )
+				{
+					elem = c[i];
+					break;
+				}
+			}
+		}
+
+		return elem;
+	};
+
+	IDOMElement.prototype.last = function ( type, callback )
+	{
+		var c = this.childNodes;
+		var l = c.length - 1;
+		var elem = null;
+		var tc = getMatch( type );
+
+		for( var i = l; -1 < i ; i -- )
+		{
+			if ( tc( c[i], type ) )
+			{
+				if( callback === undefined || callback( c[i], this ) )
+				{
+					elem = c[i];
+					break;
+				}
+			}
+		}
+
+		return elem;
+	};
+
+	IDOMElement.prototype.contains = function ( target )
+	{
+		if( target.parentElement )
+		{
+			if( target == this )
+			{
+				return true;
+			}
+			return this.contains( target.parentElement );
+		}
+		return false;
+	};
+
+	// attribute keys
+	IDOMElement.prototype.aKeys = function()
+	{
+		var ikeys = [];
+		var attrs = this.element.attributes;
+		var l = attrs.length;
+		for( var i = 0; i < l; i ++ )
+		{
+			ikeys.push( new IKey( attrs[i].name, attrs[i].value ) );
+		}
+		return ikeys;
+	};
+
+	IDOMElement.prototype.reverseChild = function()
+	{
+		var l = this.childNodes.length - 1;
+		while( -1 < -- l )
+		{
+			this.appendChild( this.childNodes[l] );
+		}
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "IDOMElement", IDOMElement );
+})();
diff --git a/botanjs/src/Dandelion/IDOMObject.js b/botanjs/src/Dandelion/IDOMObject.js
new file mode 100644
index 0000000..e9c42a3
--- /dev/null
+++ b/botanjs/src/Dandelion/IDOMObject.js
@@ -0,0 +1,149 @@
+(function(){
+	var ns = __namespace( "Dandelion" );
+	/** @type {System.utils.EventKey} */
+	var EventKey = __import( "System.utils.EventKey" );
+
+	var EvtsArr = function () { Array.call( this ); };
+
+	/** @param {System.utils.EventKey} e */
+	EvtsArr.prototype.indexOf = function( e )
+	{
+		var l = this.length;
+		for( var i = 0; i < l; i ++ )
+		{
+			/** @type {System.utils.EventKey} */
+			var evt = this[i];
+			if( evt.type == e.type && evt.handler == e.handler )
+			{
+				return i;
+			}
+		}
+
+		return -1;
+	};
+
+	__extends( EvtsArr, Array );
+
+	var IDOMObject = function ( obj, sw )
+	{
+		if ( obj instanceof IDOMObject ) return obj;
+
+		if ( sw )
+		{
+			this["addEventListener"] = this.addEventListener.bind(obj);
+			this["addEventListeners"] = this.addEventListeners.bind(this);
+			this["hasListener"] = this.hasListener.bind(obj);
+			this["removeEventListener"] = this.removeEventListener.bind(obj);
+		}
+		else if ( obj )
+		{
+			return new IDOMObject( obj, true );
+		}
+		else
+		{
+			throw new Error( "[Dandelion.IDOMObject] Invalid argument" );
+		}
+	}
+
+	IDOMObject.prototype.hasListener = function(e)
+	{
+		if( e instanceof EventKey
+			&& this._events
+			&& this._events.indexOf(e) != -1
+		)
+		{
+			return this._events[ this._events.indexOf(e) ];
+		}
+		return null;
+	};
+
+	IDOMObject.prototype.addEventListener = function (event, handler)
+	{
+		var e;
+		if (typeof event == "string" && handler)
+		{
+			e = new EventKey(event, handler);
+		}
+		else if (event instanceof EventKey)
+		{
+			e = event;
+		}
+		else
+		{
+			return false;
+		}
+
+		if ( this._events )
+		{
+			if ( this._events.indexOf( e ) < 0 )
+			{
+				this._events.push( e );
+			}
+			else
+			{
+				return false;
+			}
+		}
+		else
+		{
+			this._events = new EvtsArr();
+			this._events[0] = e;
+		}
+
+		if( this.addEventListener )
+		{
+			this.addEventListener( e.type, e.handler, false );
+		}
+		// IE
+		else if( this.attachEvent )
+		{
+			this.attachEvent('on' + e.type, e.handler);
+		}
+		else
+		{
+			this['on' + e.type] = e.handler;
+		}
+		return true;
+	};
+
+	IDOMObject.prototype.addEventListeners = function(evtKeys)
+	{
+		if(evtKeys instanceof Array)
+		{
+			for (var i in evtKeys)
+			{
+				this.addEventListener(evtKeys[i]);
+			}
+		}
+	};
+
+	IDOMObject.prototype.removeEventListener = function( e, handler )
+	{
+		if( handler )
+		{
+			e = new EventKey( e, handler );
+		}
+
+		if( this._events )
+		{
+			delete this._events[ this._events.indexOf(e) ];
+		}
+
+		if( this.removeEventListener )
+		{
+			this.removeEventListener( e.type, e.handler );
+		}
+		// IE
+		else if( this.detachEvent )
+		{
+			this.detachEvent( 'on' + e.type, e.handler );
+		}
+		else
+		{
+			this['on' + e.type] = null;
+		}
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "IDOMObject", IDOMObject );
+})();
+
diff --git a/botanjs/src/Dandelion/Swf/ExtAPI.css b/botanjs/src/Dandelion/Swf/ExtAPI.css
new file mode 100644
index 0000000..d7913d9
--- /dev/null
+++ b/botanjs/src/Dandelion/Swf/ExtAPI.css
@@ -0,0 +1,7 @@
+#extAPIConsole {
+	-webkit-transition: all 0s;
+	-moz-transition: all 0s;
+	-ms-transition: all 0s;
+	-o-transition: all 0s;
+	transition: all 0s;
+}
\ No newline at end of file
diff --git a/botanjs/src/Dandelion/Swf/ExtAPI.js b/botanjs/src/Dandelion/Swf/ExtAPI.js
new file mode 100644
index 0000000..e29c3a5
--- /dev/null
+++ b/botanjs/src/Dandelion/Swf/ExtAPI.js
@@ -0,0 +1,44 @@
+(function(){
+	var ns = __namespace( "Dandelion.Swf.ExtAPI" );
+	/** @type {System.Debug} */
+	var debug = __import( "System.Debug" );
+
+	var jsReady = false
+		, as = 0
+		, cutConnection = false
+
+		, watchDogTime = new Date().getTime()
+		, watchDogCounter = 0
+		, lastCount = 0
+	;
+
+	var ExtAPI = function ()
+	{
+		// TODO
+	};
+
+	var isReady = function () { return jsReady; };
+
+	var init = function ()
+	{
+		jsReady = true;
+		debug.Info( "[ExtAPI] " + new Date().toString() );
+	};
+
+	var watch = function ()
+	{
+		if( watchDogCounter - lastCount < 300 )
+		{
+			lastCount = watchDogCounter;
+		}
+		else
+		{
+			cutConnection = true;
+			debug.Warning( "[ExtAPI] Console Disabled: possible hanging dectected." )
+		}
+		watchDogTime = new Date().getTime();
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "init", init );
+	ns[ NS_EXPORT ]( EX_FUNC, "ready", isReady );
+})();
diff --git a/botanjs/src/Dandelion/Swf/_this.css b/botanjs/src/Dandelion/Swf/_this.css
new file mode 100644
index 0000000..75a7f7c
--- /dev/null
+++ b/botanjs/src/Dandelion/Swf/_this.css
@@ -0,0 +1,56 @@
+.swf_wrapper {
+	position: relative;
+	overflow: hidden;
+}
+
+.swf_wrapper > div {
+	position: relative;
+	width: 100%;
+	height: 100%;
+}
+
+.swf_inactive {
+	background: rgba(0, 0, 0, 0.2);
+	opacity: 1;
+}
+
+.swf_inactive:hover {
+	opacity: 0.6;
+}
+
+.swf_inactive > div {
+	width: 0;
+	height: 0;
+	border-top: 3.5em solid transparent;
+	border-bottom: 3.5em solid transparent;
+	border-left: 5em solid #FFF;
+	position: absolute;
+
+	left: 50%;
+	top: 50%;
+	margin-top: -3.5em;
+	margin-left: -1.8em;
+}
+
+ /* title */
+.swf_caption {
+	bottom: 0;
+
+	position: absolute;
+
+	display: block;
+	width: 100%;
+
+	color: white;
+	background: rgba(0, 0, 0, 0.8);
+
+	padding: 0.2em;
+
+	font-size: 2em;
+
+	cursor: default;
+}
+
+.swf_desc {
+	font-size: 60%;
+}
\ No newline at end of file
diff --git a/botanjs/src/Dandelion/Swf/_this.js b/botanjs/src/Dandelion/Swf/_this.js
new file mode 100644
index 0000000..0ea91f0
--- /dev/null
+++ b/botanjs/src/Dandelion/Swf/_this.js
@@ -0,0 +1,173 @@
+(function(){
+	var ns = __namespace( "Dandelion" );
+
+	/** @type {System.utils} */
+	var utils                        = __import( "System.utils" );
+	/** @type {System.utils.IKey} */
+	var IKey                         = __import( "System.utils.IKey" );
+	/** @type {System.Policy} */
+	var Policy                       = __import( "System.Policy" );
+	/** @type {System.Debug} */
+	var Dand                         = __import( "Dandelion" );
+
+	var IE                           = __import( "System.Global.IE" );
+
+	var s_current = {player: false, mask: false, stage: false, listener: false, sid: null};
+
+	var Swf = function (movieName)
+	{
+		if ( navigator.appName.indexOf("Microsoft") != -1 )
+		{
+			return window[movieName];
+		}
+		else
+		{
+			return document[movieName];
+		}
+	};
+
+	var realize = function (id)
+	{
+		var movieElement = Dand.id('swfWrapper_' + id)
+		, swf_origin = Dand.wrap('a')
+		, _src = movieElement.getAttribute("value")
+
+		swf_origin.href = _src;
+
+		if( Policy.isAllowedOrigin( stf_origin.host ) ) {
+			swf_origin = "always";
+		} else {
+			swf_origin = "never";
+		}
+
+		movieElement.onclick = ( IE ? createMovieIE : createMovie ).bind({
+			sid: id
+			, src: _src
+			, width: movieElement.style.width
+			, height: movieElement.style.height
+			, s_stage:movieElement
+			, origin: swf_origin
+		});
+	};
+
+	/*
+	var getCurrentMovie = function (args)
+	{
+		if ( window.ExtAPI )
+		{
+			return thisMovie( s_current.sid );
+		}
+		return null;
+	};
+	*/
+
+	var createMovieIE = function ()
+	{
+		// Remove previous movie if exists
+		if(s_current.stage)
+		{
+			s_current.stage.firstChild.className = "swf_inactive";
+			s_current.stage.firstChild.innerHTML = s_current.player;
+			s_current.stage.childNodes[1].style.bottom = "";
+			s_current.stage.onclick = s_current.listener;
+		}
+
+		this.s_stage.childNodes[1].style.bottom = "-2em";
+		s_current.sid = "swf_" + this.sid;
+
+		s_current.stage = this.s_stage;
+		s_current.player = this.s_stage.firstChild.innerHTML;
+
+		this.s_stage.firstChild.className = "swf_active";
+		this.s_stage.firstChild.innerHTML = makeStageIE(this.src, this.width, this.height, s_current.sid, this.origin);
+
+		// Save event listener
+		s_current.listener = this.s_stage.onclick;
+
+		// disable click event
+		this.s_stage.onclick = null;
+	};
+
+	var createMovie = function ()
+	{
+
+		// Remove previous movie if exists
+		if(s_current.stage)
+		{
+			s_current.stage.removeChild(s_current.player);
+			s_current.stage.firstChild.style.bottom = "";
+			s_current.stage.insertBefore(mask, stage.firstChild);
+			s_current.stage.onclick = s_current.listener;
+		}
+
+
+		this.s_stage.childNodes[1].style.bottom = "-2em";
+		s_current.sid = "swf_" + this.sid;
+
+		// Remove mask
+		(s_current.stage = this.s_stage).removeChild(s_current.mask = this.s_stage.childNodes[0]);
+
+		s_current.player = makeStage(this.src, this.width, this.height, s_current.sid, this.origin);
+		this.s_stage.appendChild(s_current.player);
+
+		// Save event listener
+		s_current.listener = this.s_stage.onclick;
+
+		// disable click event
+		this.s_stage.onclick = null;
+
+	};
+
+	var makeStage = function (src, width, height, sid, origin, wmode)
+	{
+		return Dand.wrapna("embed",
+			[
+				new IKey("src", src)
+				, new IKey("quality", "high")
+				, new IKey("bgcolor", "#869ca7")
+				, new IKey("width", width)
+				, new IKey("height", height)
+				, new IKey("name", sid)
+				, new IKey("align", "middle")
+				, new IKey("wmode", wmode || "direct")
+				, new IKey("play", "true")
+				, new IKey("loop", "false")
+				, new IKey("quality", "high")
+				, new IKey("allowScriptAccess", origin)
+				, new IKey("type", "application/x-shockwave-flash")
+				, new IKey("pluginspage", "http://www.macromedia.com/go/getflashplayer")
+			]
+		);
+	};
+
+	var makeStageIE = function (src, width, height, sid, origin, wmode)
+	{
+		return Dand.wrape(
+			Dand.wrapne('object',
+				[
+					Dand.wrapna("param", [new IKey("name", "wmode"), new IKey("value", wmode || "direct")])
+					, Dand.wrapna("param", [new IKey("name", "quality"), new IKey("value", "high")])
+					, Dand.wrapna("param", [new IKey("name", "movie"), new IKey("value", src)])
+					, Dand.wrapna("param", [new IKey("name", "bgcolor"), new IKey("value", "#869ca7")])
+					, Dand.wrapna("param", [new IKey("name", "allowScriptAccess"), new IKey("value", origin)])
+				]
+				,
+				[
+					new IKey("classid", "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000")
+					, new IKey("id", sid)
+					, new IKey("name", sid)
+					, new IKey("width", width)
+					, new IKey("height", height)
+					, new IKey("data", src)
+					, new IKey("type", "application/x-shockwave-flash")
+					, new IKey("codebase", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab")
+				]
+			)
+		).innerHTML;
+	};
+
+	__static_method( Swf, "create", IE ? makeStageIE : makeStage );
+	__static_method( Swf, "realize", realize );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Swf", Swf );
+})();
diff --git a/botanjs/src/Dandelion/Window.js b/botanjs/src/Dandelion/Window.js
new file mode 100644
index 0000000..4873b37
--- /dev/null
+++ b/botanjs/src/Dandelion/Window.js
@@ -0,0 +1,10 @@
+(function(){
+	var ns = __namespace( "Dandelion.Window" );
+
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "scrollTop"    , function() { return window.pageYOffset || document.documentElement.scrollTop; } );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "scrollLeft"   , function() { return window.pageXOffset || document.documentElement.scrollLeft; } );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "scrollHeight" , function() { return window.scrollHeight || document.documentElement.scrollHeight } );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "clientHeight" , function() { return window.clientHeight || document.documentElement.clientHeight; } );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "scrollWidth"  , function() { return window.scrollWidth || document.documentElement.scrollWidth } );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER , "clientWidth"  , function() { return window.clientWidth || document.documentElement.clientWidth; } );
+})();
diff --git a/botanjs/src/Dandelion/_this.js b/botanjs/src/Dandelion/_this.js
new file mode 100644
index 0000000..5643a4c
--- /dev/null
+++ b/botanjs/src/Dandelion/_this.js
@@ -0,0 +1,187 @@
+(function(){
+	var ns = __namespace( "Dandelion" );
+	var IE = __import( "System.Global.IE" );
+
+	/* @type {System.utils.IKey}*/
+	var IKey = __import( "System.utils.IKey" );
+
+	/* @type {Dandelion.IDOMElement}*/
+	var IDOMElement;
+
+	var wrap = function ( wwith, id, wclass, elements, iKeys )
+	{
+		var tmp = document.createElement( wwith || "div" );
+		if( id ) tmp.id = id;
+		if( wclass )
+		{
+   			if( IE )
+			{
+				tmp.className = wclass;
+			}
+			else
+			{	
+				tmp.setAttribute( "class", wclass );
+			}
+		}
+
+		if ( iKeys )
+		{
+			if ( iKeys instanceof Array )
+			{
+				for (var i in iKeys)
+				{
+					tmp.setAttribute( iKeys[i].keyName, iKeys[i].keyValue );
+				}
+			}
+			else if ( iKeys instanceof IKey )
+			{
+				tmp.setAttribute( iKeys.keyName, iKeys.keyValue );
+			}
+		}
+
+		if( elements )
+		{
+			// is array?
+			if(elements instanceof Array)
+			{
+				var l = elements.length;
+				for( var i = 0; i < l; i ++ )
+				{
+					elements[i] && tmp.appendChild( elements[i] );
+				}
+			}
+			// else if string
+			else if( typeof elements == "string" )
+			{
+				tmp.appendChild(_createTextNode(elements));
+			}
+			// else append child, do not do any error handling!
+			else if( elements )
+			{
+				tmp.appendChild( elements );
+			}
+		}
+		return tmp;
+	};
+
+	var wrapc = function ( aClass, elements, iKeys ) {
+		return wrap( false, false, aClass, elements, iKeys );
+	};
+
+	// wrap element afters
+	var wrape = function ( elements, iKeys ) {
+		return wrap( false, false, false, elements, iKeys );
+	};
+
+	// wrap name element after
+	var wrapne = function ( name, elements, iKeys ) {
+		return wrap(name, false, false, elements, iKeys);
+	};
+
+	// wrap name attirbutes after
+	var wrapna = function ( name, iKeys ) {
+		return wrap(name, false, false, false, iKeys);
+	};
+
+	var _createTextNode = function (s)
+	{
+		return document.createTextNode(s);
+	};
+
+	// Bubble up element if <condition>
+	var bubbleUp = function ( elem, condition )
+	{
+		if( condition( elem ) ) return elem;
+
+		return elem.parentNode && bubbleUp( elem.parentNode, condition );
+	};
+
+	var chainUpApply = function( elem, func )
+	{
+		if( !elem ) return;
+
+		var chain = func( elem );
+
+		if( chain && elem.parentNode )
+		{
+			chainUpApply( elem.parentNode, func );
+		}
+	};
+
+	var id = function( name, idom )
+	{
+		var elem = document.getElementById( name );
+		if( !elem ) return elem;
+
+		if( idom && runtimeImport() )
+		{
+			return IDOMElement( elem );
+		}
+
+		return elem;
+	};
+
+	var elements = function( elem, idom )
+	{
+		if( idom && runtimeImport() )
+		{
+			var l = elem.length;
+			var ielem = [];
+			for( var i = 0; i < l; i ++ )
+			{
+				ielem[i] = IDOMElement( elem[i] );
+			}
+			return ielem;
+		}
+
+		return elem;
+	};
+
+	var tag = function( name, idom, target )
+	{
+		target = target === undefined ? document : target;
+		var elem = target.getElementsByTagName( name );
+		return elements( elem, idom );
+	};
+
+	var name = function( name, idom, target )
+	{
+		target = target === undefined ? document : target;
+		var elem = target.getElementsByName( name );
+		return elements( elem, idom );
+	};
+
+	var getClass = function( name, idom, target )
+	{
+		target = target === undefined ? document : target;
+		var elem = target.getElementsByClassName( name );
+		return elements( elem, idom );
+	};
+
+	var runtimeImport = function()
+	{
+		if( IDOMElement ) return true;
+
+		try
+		{
+			var a = "Dandelion.IDOMElement";
+			IDOMElement = __import( a );
+			return true;
+		}
+		catch( e ) { }
+		return false;
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "wrap", wrap );
+	ns[ NS_EXPORT ]( EX_FUNC, "wrapc", wrapc );
+	ns[ NS_EXPORT ]( EX_FUNC, "wrape", wrape );
+	ns[ NS_EXPORT ]( EX_FUNC, "wrapne", wrapne );
+	ns[ NS_EXPORT ]( EX_FUNC, "wrapna", wrapna );
+	ns[ NS_EXPORT ]( EX_FUNC, "textNode", _createTextNode );
+	ns[ NS_EXPORT ]( EX_FUNC, "bubbleUp", bubbleUp );
+	ns[ NS_EXPORT ]( EX_FUNC, "chainUpApply", chainUpApply );
+	ns[ NS_EXPORT ]( EX_FUNC, "id", id );
+	ns[ NS_EXPORT ]( EX_FUNC, "tag", tag );
+	ns[ NS_EXPORT ]( EX_FUNC, "name", name );
+	ns[ NS_EXPORT ]( EX_FUNC, "glass", getClass );
+})();
diff --git a/botanjs/src/README.md b/botanjs/src/README.md
new file mode 100644
index 0000000..a6ed715
--- /dev/null
+++ b/botanjs/src/README.md
@@ -0,0 +1,64 @@
+Goals:
+    1. Scalable
+    2. Compressable ( Closure compiler with ADVANCED option )
+    3. Readable
+    4. Only use 1 var in global scope, I.E. window.BotanJS
+    5. Package like
+
+Predefined programming rules: 
+```
+    __namespace
+    __import
+    __readOnly
+    __static_method
+    __extends
+
+    ns = __namespace( <package> )
+    ns[ NS_INVOKE ]( <Same Level Packages> )
+    ns[ NS_EXPORT ]( <TYPE>, <Name>, <Object> )
+    ns[ NS_TRIGGER ]( <TYPE>, <Callback> )
+```
+
+Usage:
+```
+    BotanJS.import( <package> )
+```
+
+BotanJS Class Map:
+```
+<BotanJS>
+    <class name="System" location="/System/_this.js">
+        <class name="Global" location="/System/Global.js">
+            <export>ALLOWED_ORIGIN</export>
+        </class>
+        <class name="Debug" location="/System/Debug.js">
+            <import>System.Global</import>
+        </class>
+    </class>
+
+    <class name="Dandelion" location="/Dandelion/_this.js">
+        <class name="Swf" location="/Dandelion/Swf/_this.js">
+            <!-- problem exists on Extern -->
+            <!-- extern must follow path order -->
+            <import>Dandelion.Swf.ExtAPI</import>
+            <!--
+                once Dandelion.Swf is imported
+                Dandelion.MY_CLASS_DEPENDENT_PROP will become available
+            -->
+            <prop src="/Dandelion/Swf/_this.js">MY_CLASS_DEPENDENT_PROP</prop>
+            <class name="ExtAPI" location="/Dandelion/Swf/ExtAPI.js">
+                <import> ... </import>
+            </class>
+        </class>
+    </class>
+</BotanJS>
+```
+
+Css Inheritance:
+    Unlike js, which use classes only when __import is explicitly called.
+    Structure of css class are inherited by it's parent namespace
+```
+Dandelion.Swf.ExtAPI.css
+inherits from Dandelion.Swf.css
+inherits from Dandelion.css
+```
diff --git a/botanjs/src/System/Cycle/Trigger.js b/botanjs/src/System/Cycle/Trigger.js
new file mode 100644
index 0000000..6d6529c
--- /dev/null
+++ b/botanjs/src/System/Cycle/Trigger.js
@@ -0,0 +1,75 @@
+(function(){
+	var ns = __namespace( "System.Cycle.Trigger" );
+
+	/** @type {System.Cycle} */
+	var Cycle = __import( "System.Cycle" );
+
+	// trigger list
+	var tList = [];
+	var stepperId = -1;
+
+	var stepper = function( args )
+	{
+		var thisTime = new Date().getTime();
+		for ( var i in tList )
+		{
+			var f = tList[i];
+			if( f && thisTime > f[2] )
+			{
+				try
+				{
+					if( f[0]() )
+					{
+						f[1]();
+						delete tList[i];
+					}
+					else f[2] = thisTime + f[3];
+				}
+				catch(e)
+				{
+					debug.Error(e);
+					delete tList[i];
+				}
+			}
+		}
+	};
+
+	var registerTrigger = function ( trigger, handler, peekInt )
+	{
+		tList[ tList.length ] = [ trigger, handler, new Date().getTime() + peekInt, peekInt ]
+	};
+
+	var heightTrigger = function ( __element, value, handler )
+	{
+		var k = function () {
+			return ( this.a.clientHeight == this.b );
+		}.bind({ a: __element, b: value });
+
+		registerTrigger( k, handler, 50 );
+	};
+
+	var transitionTrigger = function (__style, value, handler)
+	{
+		var k = function ()
+		{
+			return ( this.a.transition == this.b );
+		}.bind({a: __style, b: value });
+		registerTrigger( k, handler, 50 );
+	};
+
+	var regTick = function( e )
+	{
+		if( stepperId < 0 )
+		{
+			stepperId = Cycle.TICK.putStepper( stepper );
+		}
+	};
+
+	ns.addEventListener( "NS_IMPORT", regTick );
+
+	ns[ NS_EXPORT ]( EX_FUNC, "register", registerTrigger );
+	ns[ NS_EXPORT ]( EX_FUNC, "transition", transitionTrigger );
+	ns[ NS_EXPORT ]( EX_FUNC, "height", heightTrigger );
+
+	ns[ NS_TRIGGER ]( TGR_IMPORT, regTick );
+})();
diff --git a/botanjs/src/System/Cycle/_this.js b/botanjs/src/System/Cycle/_this.js
new file mode 100644
index 0000000..f9431a1
--- /dev/null
+++ b/botanjs/src/System/Cycle/_this.js
@@ -0,0 +1,108 @@
+(function(){
+	var ns = __namespace( "System.Cycle" );
+
+	/** @type {System.Tick} */
+	var utils                   = __import( "System.utils" );
+	/** @type {System.Tick} */
+	var Tick                    = __import( "System.Tick" );
+	/** @type {System.Debug} */
+	var debug                   = __import( "System.Debug" );
+
+	var tList = [];
+
+	var stepper = function()
+	{
+		var thisTime = new Date().getTime();
+		// 0: Callback
+		// 1: scheduled run time
+		// 2: Permanent
+		// ( 3: id )
+		// 4: interval
+		for ( var i in tList )
+		{
+			var f = tList[i];
+			if( f && thisTime > f[1] )
+			{
+				try
+				{
+					f[0]();
+				}
+				catch(e)
+				{
+					debug.Error(e);
+					delete tList[i];
+					continue;
+				}
+
+				if( f[2] )
+				{
+					delete tList[i];
+				}
+				else
+				{
+					f[1] = thisTime + f[4];
+				}
+			}
+		}
+	};
+
+	// Should bind "func" before register
+	var registerDelay = function (func, milliSec)
+	{
+		tList[ tList.length ] = [ func, new Date().getTime() + milliSec, true ];
+	};
+
+	var registerPermanentTicker = function ( id, func, interval )
+	{
+		for ( var i in tList )
+		{
+			if( tList[i][3] == id )
+				return false;
+		}
+
+		tList[ tList.length ] = [ func, new Date().getTime() + interval, false, id, interval ];
+	};
+
+	var deletePermanentTicker = function ( id )
+	{
+		// 3: id
+		for ( var i in tList )
+		{
+			if( tList[i][3] == id )
+				delete tList[i];
+		}
+	};
+
+	var next = function( func )
+	{
+		tList[ tList.length ] = [ func, 0, true ];
+	};
+
+	var ourTick = new Tick();
+	ourTick.putStepper( stepper );
+
+	var gTickStart = function( e )
+	{
+		e.target.removeEventListener( "NS_IMPORT", gTickStart );
+
+		var TICK = __import( "System.Cycle.TICK", true );
+
+		if( TICK != ourTick && TICK.started )
+		{
+			debug.Info( "[System.Cycle] Global Tick exists" );
+			ourTick = null;
+			return;
+		}
+
+		debug.Info( "[System.Cycle] Creating global Tick" );
+		ourTick.start();
+	};
+
+	ns.addEventListener( "NS_IMPORT", gTickStart );
+
+	ns[ NS_EXPORT ]( EX_FUNC, "next", next );
+	ns[ NS_EXPORT ]( EX_FUNC, "delay", registerDelay );
+	ns[ NS_EXPORT ]( EX_FUNC, "perma", registerPermanentTicker );
+	ns[ NS_EXPORT ]( EX_FUNC, "permaRemove", deletePermanentTicker );
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER, "TICK", function(){ return ourTick; } );
+})();
diff --git a/botanjs/src/System/Debug.js b/botanjs/src/System/Debug.js
new file mode 100644
index 0000000..018da5c
--- /dev/null
+++ b/botanjs/src/System/Debug.js
@@ -0,0 +1,46 @@
+(function(){
+	var ns = __namespace( "System.Debug" );
+	/** @type {System.Log} */
+	var Log = __import( "System.Log" );
+	/** @type {System.Global} */
+	var _global = __import( "System.Global" );
+
+	var st_info = _global.debug;
+	var st_error = true;
+
+	var Error = function( e )
+	{
+		if( st_error )
+		Log.writeLine( e.name + "\n\t" + e.message + "\n\t" + e.stack, Log.ERROR );
+	};
+
+	var Info = function(e)
+	{
+		if( st_info )
+		Log.writeLine( e, Log.INFO );
+	};
+
+	var turnOff = function( what )
+	{
+		if( what == "info" ) st_info = false;
+		else if( what == "error" ) st_error = false;
+	};
+
+	var turnOn = function( what )
+	{
+		if( what == "info" ) st_info = true;
+		else if( what == "error" ) st_error = true;
+	};
+
+	/* {{{ Root log override */
+	BotanJS.log.write = Info;
+
+	while( !BotanJS.log.end() )
+		Info( BotanJS.log.read() );
+	/* End Root log override }}}*/
+
+	ns[ NS_EXPORT ]( EX_FUNC, "Error", Error );
+	ns[ NS_EXPORT ]( EX_FUNC, "Info", Info );
+	ns[ NS_EXPORT ]( EX_FUNC, "turnOff", turnOff );
+	ns[ NS_EXPORT ]( EX_FUNC, "turnOn", turnOn );
+})();
diff --git a/botanjs/src/System/Global.js b/botanjs/src/System/Global.js
new file mode 100644
index 0000000..3f262d6
--- /dev/null
+++ b/botanjs/src/System/Global.js
@@ -0,0 +1,16 @@
+(function(){
+	var ns = __namespace( "System.Global" );
+
+	var debug = function()
+	{
+		return window[ "debugEnv" ] && window[ "debugEnv" ];
+	};
+
+	// for IE < 10
+	var IE = Boolean( document[ "all" ] );
+	var ALLOWED_ORIGINS = window[ "allowed_origins" ] || [];
+
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER, "debug", debug );
+	ns[ NS_EXPORT ]( EX_CONST, "IE", IE );
+	ns[ NS_EXPORT ]( EX_CONST, "ALLOWED_ORIGINS", ALLOWED_ORIGINS );
+})();
diff --git a/botanjs/src/System/Log.js b/botanjs/src/System/Log.js
new file mode 100644
index 0000000..f1f7433
--- /dev/null
+++ b/botanjs/src/System/Log.js
@@ -0,0 +1,46 @@
+(function(){
+	var ns = __namespace( "System.Log" );
+	var handler = [];
+
+	var SYSTEM = 1;
+	var INFO = 16;
+	var ERROR = 32;
+
+	var writeLine = function ( mesg, type )
+	{
+		type = ( type === undefined ) ? INFO : type;
+
+		var handled = false;
+		for( var i in handler )
+		{
+			handler[i]( mesg, type );
+			handled = true;
+		}
+
+		if( !handled
+			&& window[ "console" ]
+			&& console.log
+		) console.log( mesg );
+	};
+
+	var registerHandler = function( func )
+	{
+		var index = -1;
+		handler[ index = handler.length ] = func;
+
+		return index;
+	};
+
+	var removeHandler = function( index )
+	{
+		delete handler[ index ];
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "writeLine", writeLine );
+	ns[ NS_EXPORT ]( EX_FUNC, "registerHandler", registerHandler );
+	ns[ NS_EXPORT ]( EX_FUNC, "removeHandler", removeHandler );
+
+	ns[ NS_EXPORT ]( EX_CONST, "INFO", INFO );
+	ns[ NS_EXPORT ]( EX_CONST, "ERROR", ERROR );
+	ns[ NS_EXPORT ]( EX_CONST, "SYSTEM", SYSTEM );
+})();
diff --git a/botanjs/src/System/Net/ClassLoader.js b/botanjs/src/System/Net/ClassLoader.js
new file mode 100644
index 0000000..06223ae
--- /dev/null
+++ b/botanjs/src/System/Net/ClassLoader.js
@@ -0,0 +1,75 @@
+(function(){
+	var ns = __namespace( "System.Net" );
+	var className = "ClassLoader";
+
+	/** @type {System.utils} */
+	var utils                        = __import( "System.utils" );
+	/** @type {System.utils.IKey} */
+	var IKey                         = __import( "System.utils.IKey" );
+	/** @type {Dandelion} */
+	var Dand                         = __import( "Dandelion" );
+
+	var loadFile = function ( sapi, request, mode )
+	{
+		var head = Dand.tag( "head" )[0];
+
+		// Add css
+		head.appendChild(
+			Dand.wrapna(
+				"link"
+				, IKey.quickDef(
+					"rel", "stylesheet"
+					, "type", "text/css"
+					, "href", sapi + mode + "css/" + request
+				)
+			)
+		);
+
+		// Add js
+		head.appendChild(
+			Dand.wrapna(
+				"script"
+				, IKey.quickDef(
+					"type", "text/javascript"
+					, "src",  sapi + mode + "js/" + request
+				)
+			)
+		);
+
+	};
+
+	var Loader = function( sapi, mode )
+	{
+		mode = ( mode === undefined ) ? "o" : mode;
+
+		this.load = function( classes, handler )
+		{
+			if( !classes.join ) classes = [ classes ];
+
+			var excludes = BotanJS.getDef();
+			// Excludes
+			utils.objMap( excludes , function( v ) { return "-" + v; } );
+
+			var loadc = null;
+			var onLoad = function( e )
+			{
+				if( classes.indexOf( e.data.name ) < 0 ) return;
+				handler( e.data.name );
+			};
+
+			var sp = mode ? { 'o': '/', 'r': '/' }[ mode ] : ',';
+
+			loadFile(
+				sapi
+				, classes.join( sp ) + sp + excludes.join( sp )
+				, mode
+			);
+
+			BotanJS.addEventListener( "NS_INIT", onLoad );
+			BotanJS.addEventListener( "NS_EXPORT", onLoad );
+		};
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "ClassLoader", Loader );
+})();
+
diff --git a/botanjs/src/System/Net/_this.js b/botanjs/src/System/Net/_this.js
new file mode 100644
index 0000000..89b8386
--- /dev/null
+++ b/botanjs/src/System/Net/_this.js
@@ -0,0 +1,141 @@
+(function(){
+	var ns                              = __namespace( "System.Net" );
+	/** @type {System.Debug} */
+	var debug                           = __import( "System.Debug" );
+	/** @type {Dandelion.IDOMObject} */
+	var IDOMObject                      = __import( "Dandelion.IDOMObject" );
+
+	// Handles all http transfers
+	var getData = function (uri, handler, failedHandler)
+	{
+		var request = new XMLHttpRequest();
+		debug.Info("[Net] GET: " + uri);
+		request.onreadystatechange = function()
+		{
+			/*
+				0: request not initialized 
+				1: server connection established
+				2: request received 
+				3: processing request 
+				4: request finished and response is ready
+			*/
+			if(request.readyState == 2)
+			{
+				debug.Info("[Net] GET R2:" + uri);
+			}
+			else if (request.readyState == 4)
+			{
+				debug.Info("[Net] GET R4:" + uri);
+				if (request.status == 200)
+				{
+					try
+					{
+						handler(request.responseText);
+					}
+					catch(e)
+					{
+						debug.Error(e);
+						failedHandler(null);
+					}
+				}
+				else
+				{
+					debug.Info("[Net] Status: " + request.status.toString());
+					debug.Info(request.responseText);
+					failedHandler && failedHandler(null);
+				}
+			}
+		}
+		request.open("GET", uri, true);
+		request.send();
+	};
+
+	var postFile = function (uri, data, handlers)
+	{
+		var request = new XMLHttpRequest()
+			, generalExchange = new IDOMObject( request )
+			, uploadExchange = new IDOMObject( request.upload )
+		;
+
+		if ( uploadExchange ) {
+			handlers.progress && uploadExchange.addEventListener("Progress", handlers.progress);
+			handlers.failed && uploadExchange.addEventListener("Error", handlers.failed);
+		}
+
+		generalExchange.addEventListener("ReadyStateChange", function(e) {
+			if ( 4 == this.readyState ) {
+				handlers.complete && handlers.complete(request.responseText);
+			}
+		});
+
+		request.open("POST", uri, true);
+		// request.setRequestHeader("Content-Type", "multipart/form-data");
+		request.send(data);
+	};
+
+	var postData = function (uri, data, handler, failedHandler)
+	{
+		var request = new XMLHttpRequest();
+		// compile post string
+		data = compilePostData(data);
+		debug.Info("[Net] PostString: " + data);
+		request.onreadystatechange = function()
+		{
+			/*
+				0: request not initialized 
+				1: server connection established
+				2: request received 
+				3: processing request 
+				4: request finished and response is ready
+			*/
+			if(request.readyState == 2)
+			{
+				debug.Info("[Net] POST R2:" + uri);
+			}
+			else if (request.readyState == 4)
+			{
+				debug.Info("[Net] POST R4:" + uri);
+				if (request.status == 200)
+				{
+					try
+					{
+						var obj = JSON.parse(request.responseText);
+						obj.status ? handler(obj): failedHandler(obj);
+					}
+					catch(e)
+					{
+						debug.Error(e);
+						failedHandler(null);
+					}
+				}
+				else
+				{
+					debug.Info("[Net] Status: " + request.status.toString());
+					debug.Info(request.responseText);
+					failedHandler(null);
+				}
+			}
+		};
+
+		request.open( "POST", uri );
+		request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
+		request.send( data );
+	};
+
+	var compilePostData = function (obj)
+	{
+		var postdata = "timestamp=" + new Date().getTime();
+		for(var name in obj)
+		{
+			postdata += "&" + name + "=" + encodeURIComponent(obj[name]);
+		}
+
+		return postdata;
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "getData", getData );
+	ns[ NS_EXPORT ]( EX_FUNC, "postData", postData );
+	ns[ NS_EXPORT ]( EX_FUNC, "postFile", postFile );
+	// ns[ NS_EXPORT ]( EX_FUNC, "compilePostData", compilePostData );
+})();
+
diff --git a/botanjs/src/System/Policy/_this.js b/botanjs/src/System/Policy/_this.js
new file mode 100644
index 0000000..3ccef37
--- /dev/null
+++ b/botanjs/src/System/Policy/_this.js
@@ -0,0 +1,13 @@
+(function(){
+	var ns = __namespace( "System.Policy" );
+
+	/** @type {System.Global} */
+	var Global = __import( "System.Global" );
+
+	var isOriginAllowed = function( origin )
+	{
+		return -1 < Global.ALLOWED_ORIGINS.indexOf( origin );
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "isOriginAllowed", isOriginAllowed );
+})();
diff --git a/botanjs/src/System/Tick.js b/botanjs/src/System/Tick.js
new file mode 100644
index 0000000..3951161
--- /dev/null
+++ b/botanjs/src/System/Tick.js
@@ -0,0 +1,50 @@
+(function(){
+	var ns = __namespace( "System" );
+
+	var Tick = function()
+	{
+		// cycle counter
+		var nc = 0;
+		this.__started = false;
+
+		this.loop = function()
+		{
+			for( var i in this.steppers )
+				this.steppers[i]();
+			nc ++;
+		};
+
+		__readOnly( this, "count", function() { return nc; } );
+	};
+
+	Tick.prototype.putStepper = function( stepperCallback )
+	{
+		var l = this.steppers.length;
+		this.steppers[l] = stepperCallback;
+		return l;
+	};
+
+	Tick.prototype.start = function()
+	{
+		if( !this.__started )
+		{
+			this.id = setInterval( this.loop.bind( this ), 0 );
+			this.__started = true;
+		}
+	};
+
+	Tick.prototype.stop = function()
+	{
+		if( this.__started )
+		{
+			this.__started = false;
+			clearInterval( this.id );
+		}
+	};
+
+	Tick.prototype.steppers = [];
+
+	__readOnly( Tick.prototype, "started", function() { return this.__started; } );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Tick", Tick );
+})();
diff --git a/botanjs/src/System/_this.js b/botanjs/src/System/_this.js
new file mode 100644
index 0000000..4801f9e
--- /dev/null
+++ b/botanjs/src/System/_this.js
@@ -0,0 +1,3 @@
+(function(){
+	var ns = __namespace( "System" )
+})();
diff --git a/botanjs/src/System/utils/DataKey.js b/botanjs/src/System/utils/DataKey.js
new file mode 100644
index 0000000..065a414
--- /dev/null
+++ b/botanjs/src/System/utils/DataKey.js
@@ -0,0 +1,17 @@
+(function(){
+	var ns = __namespace( "System.utils" );
+	var IKey = ns[ NS_INVOKE ]( "IKey" );
+	// Data key
+	var DataKey = function ( name, value )
+	{
+		IKey.call(
+			this
+			, "data-" + name
+			, value ? encodeURIComponent( String( value ) ) : ""
+		);
+	};
+
+	__extends( DataKey, IKey );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "DataKey", DataKey );
+})();
diff --git a/botanjs/src/System/utils/EventKey.js b/botanjs/src/System/utils/EventKey.js
new file mode 100644
index 0000000..c43e4d5
--- /dev/null
+++ b/botanjs/src/System/utils/EventKey.js
@@ -0,0 +1,19 @@
+(function(){
+	var ns = __namespace( "System.utils" );
+	var IKey = ns[ NS_INVOKE ]( "IKey" );
+	// Event key
+	var EventKey = function ( eventType, eventHandler )
+	{
+		IKey.call( this, eventType, eventHandler );
+
+		this.type = eventType.toLowerCase();
+		this.handler = eventHandler;
+	}
+
+	__extends( EventKey, IKey );
+
+	EventKey.prototype.type = "";
+	EventKey.prototype.handler = null;
+
+	ns[ NS_EXPORT ]( EX_CLASS, "EventKey", EventKey );
+})();
diff --git a/botanjs/src/System/utils/IKey.js b/botanjs/src/System/utils/IKey.js
new file mode 100644
index 0000000..7a3b641
--- /dev/null
+++ b/botanjs/src/System/utils/IKey.js
@@ -0,0 +1,49 @@
+(function(){
+	var ns = __namespace( "System.utils" );
+	var ClassName = "IKey";
+
+	////// Class IKey
+	var IKey = function (name, value)
+	{
+		if ( name && ( typeof name != "string" ) ) return;
+		this.keyName = name;
+
+		if( value instanceof IKey )
+		{
+			this.keyValue = value;
+		}
+		else
+		{
+			this.keyValue = (value != undefined) ? String(value) : "";
+		}
+
+		this["keyName"] = this.keyName;
+		this["keyValue"] = this.keyValue;
+	};
+
+	IKey.prototype.keyName = "";
+	IKey.prototype.keyValue = "";
+
+	var quickDef = function()
+	{
+		var l = arguments.length;
+
+		if( l % 2 != 0 )
+		{
+			ns[ NS_THROW ]( "Invalid Definition Count", ClassName );
+		}
+
+		var keys = [];
+		for( var i = 0; i < l; i += 2 )
+		{
+			keys[ keys.length ] = new IKey( arguments[i], arguments[ i + 1 ] );
+		}
+
+		return keys;
+	};
+
+	__static_method( IKey, "quickDef", quickDef );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "IKey", IKey );
+
+})();
diff --git a/botanjs/src/System/utils/Perf.js b/botanjs/src/System/utils/Perf.js
new file mode 100644
index 0000000..237ee47
--- /dev/null
+++ b/botanjs/src/System/utils/Perf.js
@@ -0,0 +1,49 @@
+(function(){
+	// Performance Functions
+	var ns = __namespace( "System.utils.Perf" );
+
+	/** {{{ Fast UUID generator, RFC4122 version 4 compliant.
+	 * author: Jeff Ward (jcward.com).
+	 * license: MIT license
+	 * link: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+	 **/
+	var lut = [];
+	for ( var i=0; i < 256; i++ )
+	{
+		lut[i] = ( i < 16 ? '0' : '' ) + ( i ).toString(16);
+	}
+
+	var UUID = function()
+	{
+		var d0 = Math.random()*0xffffffff|0;
+		var d1 = Math.random()*0xffffffff|0;
+		var d2 = Math.random()*0xffffffff|0;
+		var d3 = Math.random()*0xffffffff|0;
+		return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
+			lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
+			lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
+			lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
+	};
+	/* }}}*/
+
+	// Reverse an array using XOR swap
+	var Array_Reverse = function( array )
+	{
+		var i = null;
+		var l = array.length;
+		var r = null;
+		for (i = 0, r = l - 1; i < r; i += 1, r -= 1)
+		{
+			var left = array[i];
+			var right = array[r];
+			left ^= right;
+			right ^= left;
+			left ^= right;
+			array[i] = left;
+			array[r] = right;
+		}
+	};
+
+	ns[ NS_EXPORT ]( EX_READONLY_GETTER, "uuid", UUID );
+	ns[ NS_EXPORT ]( EX_FUNC, "ArrayReverse", Array_Reverse );
+})();
diff --git a/botanjs/src/System/utils/_this.js b/botanjs/src/System/utils/_this.js
new file mode 100644
index 0000000..32ca158
--- /dev/null
+++ b/botanjs/src/System/utils/_this.js
@@ -0,0 +1,41 @@
+(function(){
+	var ns = __namespace( "System.utils" );
+
+	// Get prop from obj if obj.<prop> is <type>
+	var objGetProp = function ( obj, prop, type )
+	{
+		if(obj && obj[prop])
+		{
+			var t = obj[prop].constructor.toString().match(/function ([^\(]+)/);
+			if(t && t.length == 2 && t[1].toUpperCase() == type.toUpperCase())
+			{
+				return obj[prop];
+			}
+		}
+		return null;
+	};
+
+	var objSearch = function ( obj, cond, prop )
+	{
+		for( var i in obj )
+		{
+			if( cond( obj[i] ) )
+			{
+				return obj[i][prop] || obj[i];
+			}
+		}
+		return null;
+	};
+
+	var objMap = function( obj, callback )
+	{
+		for( var i in obj )
+		{
+			obj[i] = callback( obj[i] );
+		}
+	};
+
+	ns[ NS_EXPORT ]( EX_FUNC, "objGetProp", objGetProp );
+	ns[ NS_EXPORT ]( EX_FUNC, "objSearch", objSearch );
+	ns[ NS_EXPORT ]( EX_FUNC, "objMap", objMap );
+})();
diff --git a/botanjs/src/_this.js b/botanjs/src/_this.js
new file mode 100644
index 0000000..18a7c10
--- /dev/null
+++ b/botanjs/src/_this.js
@@ -0,0 +1,383 @@
+/*{{{ Shorthand Functions */
+var __extends = function( obj, target ) {
+	obj.prototype = Object.create( target.prototype );
+	obj.prototype.constructor = obj;
+};
+var __readOnly = function( prototype, name, callback )
+{
+	Object.defineProperty( prototype, name, {
+		get: callback
+		, set: function( v ) {
+			throw new Error( "Setting a read-only property: " + this.p );
+		}.bind( { p: name } )
+	} );
+};
+var __getter = function( obj, name, callback )
+{
+	Object.defineProperty( obj, name, {
+		get: callback
+		, set: function( v ) {
+			throw new Error( "Setting a read-only property: " + this.p );
+		}.bind( { p: name } )
+	} );
+};
+var __static_method = function( obj, name, callback )
+{
+	Object.defineProperty( obj, name, {
+		get: function(){ return callback; }
+		, set: function( v ) {
+			throw new Error( "Setting a read-only property: " + this.p );
+		}.bind( { p: name } )
+	} );
+};
+var __const = __static_method;
+/* End Shorthand Functions }}}*/
+
+/*{{{ BotanEvent & EventDispatcher */
+var BotanEvent = function( name, data )
+{
+	var __propagating = false;
+	var __propagated = false;
+
+	__static_method(
+	   this, "propagate"
+	   , function()
+	   {
+		   if( !__propagated )
+		   {
+			   __propagating = true;
+		   }
+		   __propagated = true;
+	   }
+	);
+
+	__static_method(
+	   this, "stopPropagating"
+	   , function()
+	   {
+		   __propagating = false;
+	   }
+	);
+
+	__const( this, "type", name );
+
+	__getter(
+		this, "propagating"
+		, function()
+		{
+			return __propagating;
+		}
+	);
+
+	__const( this, "data", data );
+
+	this.setTarget = function( target )
+	{
+		__const( this, "target", target );
+		this.setTarget = undefined;
+	}.bind( this );
+};
+
+/** @constructor
+ *  @extends EventTarget
+ **/
+var EventDispatcher = function() {
+	var events = {};
+	var _self = this;
+
+	var getStack = function( name )
+	{
+		if( !events[ name ] ) events[ name ] = [];
+		return events[ name ];
+	};
+
+	var _dispatch = function()
+	{
+		this.evt.propagate();
+		for( var i in this.stack )
+		{
+			if( this.evt.propagating )
+			{
+				this.stack[ i ]( this.evt );
+			}
+		}
+		this.evt.stopPropagating();
+	};
+
+	this.addEventListener = function( type, handler )
+	{
+		var stack = getStack( type );
+		stack[ stack.length ] = handler;
+	};
+
+	this.removeEventListener = function( type, handler )
+	{
+		var stack = getStack( type );
+		var i = stack.indexOf( handler );
+		if( i < 0 ) return;
+		delete stack[ i ];
+	};
+
+	/** @type {Function}
+	 *  @param {BotanEvent} evt
+	 */
+	this.dispatchEvent = function( _evt )
+	{
+		var _stack = getStack( _evt.type );
+		if( _evt.setTarget ) _evt.setTarget( _self );
+		// Dispatch the event asynchronously
+		setTimeout( _dispatch.bind({ evt: _evt, stack: _stack }), 0 );
+	};
+};
+
+/* End BotanEvent & EventDispatcher }}}*/
+
+/** @type {Array}
+ *  @extends {EventDispatcher}
+ */
+var NamespaceObj = function() {
+	EventDispatcher.call( this );
+};
+
+NamespaceObj.prototype = new Array();
+
+var packages = {};
+var _global = {};
+var _NSs = {};
+var _cacheIMP = {};
+
+/*{{{ Constants */
+var EX_CONST = 0;
+var EX_VAR = 1;
+var EX_CLASS = 2;
+var EX_FUNC = 3;
+
+var EX_READONLY_GETTER = 10;
+
+var NS_INVOKE = 0;
+var NS_EXPORT = 1;
+var NS_TRIGGER = 10;
+var NS_THROW = 11;
+
+var TGR_IMPORT = 0;
+/* End Constants }}}*/
+
+/*{{{ Multi-instance handle */
+// Check if we are being imported 2nd time
+// If yes, we get the instance and overload
+// some core methods
+var sGarden = window["BotanJS"];
+
+/** @type {BotanJS} */
+var BotanJS = null;
+var __namespace = null;
+var __import = null;
+
+if( sGarden )
+{
+	BotanJS = sGarden["sg"][0];
+	__namespace =  sGarden["sg"][1];
+	__import = sGarden["sg"][2];
+}
+/* End Multi-instance handle }}}*/
+
+/*{{{ Root level bug-free handlers */
+BotanJS = BotanJS || (function()
+{
+	var i = 0;
+	var mesg = [];
+	var structures = [];
+	var edp = new EventDispatcher();
+
+	var log = {};
+	log.write = function( m ) { mesg[ mesg.length ] = m; };
+	__static_method( log, "read", function() { return mesg[ i ++ ]; } );
+	__static_method( log, "end", function() { return ( mesg.length <= i ); } );
+
+	__const( edp, "log", log );
+
+	__static_method( edp, "define", function( f ) { structures[ structures.length ] = f; } );
+	__static_method( edp, "getDef", function() { return structures.slice(); } );
+
+	return edp;
+})();
+/* End Root level bug-free handlers }}}*/
+
+/*{{{ Namespace declarator */
+__namespace = __namespace || function( ns )
+{
+	if( _NSs[ ns ] ) return _NSs[ ns ];
+
+	var p = ns.split(".");
+	var l = p.length;
+
+	var target = packages;
+	for( var i = 0; i < l; i ++ ) {
+		target[ p[i] ] = target[ p[i] ] || {};
+		target = target[ p[i] ];
+	}
+
+	target.__TRIGGERS = [];
+
+	nsObj = new NamespaceObj;
+	nsObj[ NS_EXPORT ] = function( type, name, obj )
+	{
+		if( this.t[ name ] ) return;
+		this.t[ name ] = [ type, obj ];
+
+		/** @type {BotanEvent} */
+		var evt = new BotanEvent( "NS_EXPORT", {
+			"name": this.n + "." + name
+			, "type": type
+		});
+
+		BotanJS.dispatchEvent( evt );
+
+	}.bind({ t: target, n: ns });
+
+	nsObj[ NS_INVOKE ] = function( target )
+	{
+		if( !this.t[ target ] )
+		{
+			throw new Error(
+				"[" + this.n + "] "
+				+ "Invoke failed: " + target + " does not exists"
+			);
+		}
+
+		return this.t[ target ][1];
+	}.bind({ t: target, n: ns });
+
+	nsObj[ NS_TRIGGER ] = function( code, func )
+	{
+		this.__TRIGGERS[ code ] = func;
+	}.bind( target );
+
+	nsObj[ NS_THROW ] = function( message, subclass )
+	{
+		subclass = subclass ? ( "." + subclass ) : "";
+		throw new Error(
+			"[" + this.n + subclass + "] " + message
+		);
+	}.bind({ n: ns });
+
+	/** @type {BotanEvent} */
+	BotanJS.dispatchEvent( new BotanEvent( "NS_INIT", ns ) );
+	return ( _NSs[ ns ] = nsObj );
+};
+/* End Namespace declarator }}}*/
+
+/*{{{ Import operator */
+__import = __import || function( ns, noCache )
+{
+	var nss = ns.replace( ".*", "" );
+	if( _NSs[ nss ] )
+	{
+		_NSs[ nss ].dispatchEvent( new BotanEvent( "NS_IMPORT", target ) );
+	}
+
+	// Read The Cache First
+	if( !noCache && _cacheIMP[ ns ] ) return _cacheIMP[ ns ];
+
+	var p = ns.split(".");
+	var l = p.length;
+
+	var target = packages;
+	var wildcard = false;
+	for( var i = 0; i < l; i ++ )
+	{
+		if( p[i] == "*" )
+		{
+			wildcard = true;
+			break;
+		}
+
+		target = target[ p[i] ];
+
+		if( !target )
+			throw new Error( "No such class: " + ns );
+	}
+
+	if( target instanceof Array && p[i] != "*" )
+	{
+		var rtarget = null;
+		if( target[0] == EX_READONLY_GETTER )
+		{
+			rtarget = target[1]();
+		}
+		else
+		{
+			rtarget = target[1];
+		}
+
+		_cacheIMP[ ns ] = rtarget;
+		return rtarget;
+	}
+
+	var nsObj = {};
+
+	for( var i in target )
+	{
+		var j = target[i];
+		if( j instanceof Array )
+		{
+			if( wildcard && j[0] == EX_CLASS )
+			{
+				nsObj[ i ] = j[1];
+			}
+			else if( j[0] == EX_FUNC )
+			{
+				nsObj[ i ] = j[1];
+			}
+		   	else if( j[0] == EX_CONST )
+			{
+				Object.defineProperty( nsObj, i, {
+					get: function() {
+						return this.t[ this.p ][1];
+					}.bind( { p: i, t: target } )
+					, set: function( v ) {
+						throw new Error( "Setting a read-only property: " + this.p );
+					}.bind( { p: i } )
+				});
+			}
+		   	else if( j[0] == EX_VAR )
+			{
+				Object.defineProperty( nsObj, i, {
+					get: function() {
+						return this.t[ this.p ][1]();
+					}.bind( { p: i, t: target } )
+					, set: function( v ) {
+						this.t[ this.p ][1]( v );
+					}.bind( { p: i, t: target } )
+				});
+			}
+			else if( j[0] == EX_READONLY_GETTER )
+			{
+				Object.defineProperty( nsObj, i, {
+					get: j[1]
+					, set: function( v ) {
+						throw new Error( "Setting a read-only property: " + this.p );
+					}.bind( { p: i } )
+				});
+			}
+		}
+	}
+
+	_cacheIMP[ ns ] = nsObj;
+	return nsObj;
+};
+/* End Import operator }}}*/
+
+window["BotanJS"] = {};
+window["BotanJS"]["version"] = "0.2";
+window["BotanJS"]["codename"] = "Botanical framework.js";
+window["BotanJS"]["import"] = function( p )
+{
+	try { return __import( p ); }
+	catch( e )
+	{
+		if( sGarden ) return sGarden["import"]( p );
+		throw e;
+	}
+};
+window["BotanJS"]["sg"] = [ BotanJS, __namespace, __import ];
diff --git a/botanjs/src/externs/BotanEvent.js b/botanjs/src/externs/BotanEvent.js
new file mode 100644
index 0000000..8da3ab0
--- /dev/null
+++ b/botanjs/src/externs/BotanEvent.js
@@ -0,0 +1,16 @@
+/** @constructor */
+var BotanEvent = function (){};
+
+/** @type {object} */
+BotanEvent.data;
+
+/** @type {Function} */
+BotanEvent.propagate;
+/** @type {Function} */
+BotanEvent.stopPropagating;
+
+/** @type {Boolean} */
+BotanEvent.propagating;
+
+/** @type {Function} */
+BotanEvent.setTarget;
diff --git a/botanjs/src/externs/BotanJS.js b/botanjs/src/externs/BotanJS.js
new file mode 100644
index 0000000..d695530
--- /dev/null
+++ b/botanjs/src/externs/BotanJS.js
@@ -0,0 +1,18 @@
+/** @constructor
+ *  @implements {EventTarget}
+ **/
+var BotanJS = function() {};
+
+/** @type {Function} */
+BotanJS.define;
+/** @type {Function} */
+BotanJS.getDef;
+
+/** @type {Object} */
+BotanJS.log = {};
+	/** @type {Function} */
+	BotanJS.log.write;
+	/** @type {Function} */
+	BotanJS.log.read;
+	/** @type {Function} */
+	BotanJS.log.end;
diff --git a/botanjs/src/externs/Components.MessageBox.js b/botanjs/src/externs/Components.MessageBox.js
new file mode 100644
index 0000000..5dfea04
--- /dev/null
+++ b/botanjs/src/externs/Components.MessageBox.js
@@ -0,0 +1,7 @@
+/** @constructor */
+Components.MessageBox = function() {};
+
+/** @type {Function} */
+Components.MessageBox.setHandler;
+/** @type {Function} */
+Components.MessageBox.show;
diff --git a/botanjs/src/externs/Components.Mouse.Clipboard.SwfHelperObj.js b/botanjs/src/externs/Components.Mouse.Clipboard.SwfHelperObj.js
new file mode 100644
index 0000000..7431017
--- /dev/null
+++ b/botanjs/src/externs/Components.Mouse.Clipboard.SwfHelperObj.js
@@ -0,0 +1,9 @@
+/** @constructor */
+Components.Mouse.Clipboard.SwfHelperObj = function() {};
+
+/** @type {Function} */
+Components.Mouse.Clipboard.SwfHelperObj.dummy;
+/** @type {Function} */
+Components.Mouse.Clipboard.SwfHelperObj.debug;
+/** @type {Function} */
+Components.Mouse.Clipboard.SwfHelperObj.copy;
diff --git a/botanjs/src/externs/Components.Mouse.Clipboard.js b/botanjs/src/externs/Components.Mouse.Clipboard.js
new file mode 100644
index 0000000..75ffc13
--- /dev/null
+++ b/botanjs/src/externs/Components.Mouse.Clipboard.js
@@ -0,0 +1,15 @@
+/** @constructor */
+Components.Mouse.Clipboard = function() {};
+
+/** @type {Function} */
+Components.Mouse.Clipboard.init;
+/** @type {Function} */
+Components.Mouse.Clipboard.setTextToCopy;
+/** @type {Function} */
+Components.Mouse.Clipboard.onMouseOver;
+/** @type {Function} */
+Components.Mouse.Clipboard.onMouseOut;
+/** @type {Function} */
+Components.Mouse.Clipboard._textCopied;
+/** @type {Function} */
+Components.Mouse.Clipboard.capture;
diff --git a/botanjs/src/externs/Components.Mouse.ContextMenu.js b/botanjs/src/externs/Components.Mouse.ContextMenu.js
new file mode 100644
index 0000000..5f04077
--- /dev/null
+++ b/botanjs/src/externs/Components.Mouse.ContextMenu.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Components.Mouse.ContextMenu = function() {};
diff --git a/botanjs/src/externs/Components.Mouse.js b/botanjs/src/externs/Components.Mouse.js
new file mode 100644
index 0000000..047fb9d
--- /dev/null
+++ b/botanjs/src/externs/Components.Mouse.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Components.Mouse = function() {};
diff --git a/botanjs/src/externs/Components.js b/botanjs/src/externs/Components.js
new file mode 100644
index 0000000..e22d0fe
--- /dev/null
+++ b/botanjs/src/externs/Components.js
@@ -0,0 +1,2 @@
+/** @constructor */
+var Components = function() {};
diff --git a/botanjs/src/externs/Dandelion.CSSAnimations.MovieClip.js b/botanjs/src/externs/Dandelion.CSSAnimations.MovieClip.js
new file mode 100644
index 0000000..d7af28f
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.CSSAnimations.MovieClip.js
@@ -0,0 +1,14 @@
+/** @constructor */
+Dandelion.CSSAnimations.MovieClip = function (){};
+
+/** @type {Function} */
+Dandelion.CSSAnimations.MovieClip.gotoFrame;
+/** @type {Function} */
+Dandelion.CSSAnimations.MovieClip.prevFrame;
+/** @type {Function} */
+Dandelion.CSSAnimations.MovieClip.nextFrame;
+/** @type {Element} */
+Dandelion.CSSAnimations.MovieClip.stage;
+
+/** @type {Function} */
+Dandelion.CSSAnimations.MouseOverMovie;
diff --git a/botanjs/src/externs/Dandelion.CSSAnimations.js b/botanjs/src/externs/Dandelion.CSSAnimations.js
new file mode 100644
index 0000000..1324aa4
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.CSSAnimations.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Dandelion.CSSAnimations = function (){};
diff --git a/botanjs/src/externs/Dandelion.IDOMElement.js b/botanjs/src/externs/Dandelion.IDOMElement.js
new file mode 100644
index 0000000..b00b4ec
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.IDOMElement.js
@@ -0,0 +1,23 @@
+/** @constructor */
+Dandelion.IDOMElement = function (){}
+
+/** @type {Function} */
+Dandelion.IDOMElement.getDAttribute;
+/** @type {Function} */
+Dandelion.IDOMElement.setAttribute;
+/** @type {Function} */
+Dandelion.IDOMElement.lootChildren;
+/** @type {Function} */
+Dandelion.IDOMElement.foreach;
+/** @type {Function} */
+Dandelion.IDOMElement.first;
+/** @type {Function} */
+Dandelion.IDOMElement.last;
+/** @type {Function} */
+Dandelion.IDOMElement.contains;
+/** @type {Function} */
+Dandelion.IDOMElement.aKeys;
+/** @type {Element} */
+Dandelion.IDOMElement.element;
+/** @type {Element} */
+Dandelion.IDOMElement.reverseChild;
diff --git a/botanjs/src/externs/Dandelion.IDOMObject.js b/botanjs/src/externs/Dandelion.IDOMObject.js
new file mode 100644
index 0000000..d48a31c
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.IDOMObject.js
@@ -0,0 +1,11 @@
+/** @constructor */
+Dandelion.IDOMObject = function (){}
+
+/** @type {Function} */
+Dandelion.IDOMObject.addEventListener;
+/** @type {Function} */
+Dandelion.IDOMObject.addEventListeners;
+/** @type {Function} */
+Dandelion.IDOMObject.hasListener;
+/** @type {Function} */
+Dandelion.IDOMObject.removeEventListener;
diff --git a/botanjs/src/externs/Dandelion.Swf.ExtAPI.js b/botanjs/src/externs/Dandelion.Swf.ExtAPI.js
new file mode 100644
index 0000000..2cceb3e
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.Swf.ExtAPI.js
@@ -0,0 +1,5 @@
+/** @constructor */
+Dandelion.Swf.ExtAPI = function (){}
+
+/** @type {Function} */
+Dandelion.Swf.ExtAPI.ready;
diff --git a/botanjs/src/externs/Dandelion.Swf.js b/botanjs/src/externs/Dandelion.Swf.js
new file mode 100644
index 0000000..27381f8
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.Swf.js
@@ -0,0 +1,7 @@
+/** @constructor */
+Dandelion.Swf = function (){}
+
+/** @type {Function} */
+Dandelion.Swf.create;
+/** @type {Function} */
+Dandelion.Swf.realize;
diff --git a/botanjs/src/externs/Dandelion.js b/botanjs/src/externs/Dandelion.js
new file mode 100644
index 0000000..9a6897b
--- /dev/null
+++ b/botanjs/src/externs/Dandelion.js
@@ -0,0 +1,38 @@
+/** @constructor */
+var Dandelion = function (){}
+
+/** @type {Function} */
+Dandelion.wrap;
+
+/** @type {Function} */
+Dandelion.wrapc;
+
+/** @type {Function} */
+Dandelion.wrape;
+
+/** @type {Function} */
+Dandelion.wrapne;
+
+/** @type {Function} */
+Dandelion.wrapna;
+
+/** @type {Function} */
+Dandelion.textNode;
+
+/** @type {Function} */
+Dandelion.bubbleUp;
+
+/** @type {Function} */
+Dandelion.chainUpApply;
+
+/** @type {Function} */
+Dandelion.id;
+
+/** @type {Function} */
+Dandelion.tag;
+
+/** @type {Function} */
+Dandelion.name;
+
+/** @type {Function} */
+Dandelion.glass;
diff --git a/botanjs/src/externs/System.Cycle.Trigger.js b/botanjs/src/externs/System.Cycle.Trigger.js
new file mode 100644
index 0000000..6df930d
--- /dev/null
+++ b/botanjs/src/externs/System.Cycle.Trigger.js
@@ -0,0 +1,11 @@
+/** @constructor */
+System.Cycle.Trigger = function (){}
+
+/** @type {Function} */
+System.Cycle.Trigger.register;
+
+/** @type {Function} */
+System.Cycle.Trigger.transition;
+
+/** @type {Function} */
+System.Cycle.Trigger.height;
diff --git a/botanjs/src/externs/System.Cycle.js b/botanjs/src/externs/System.Cycle.js
new file mode 100644
index 0000000..917267e
--- /dev/null
+++ b/botanjs/src/externs/System.Cycle.js
@@ -0,0 +1,17 @@
+/** @constructor */
+System.Cycle = function (){}
+
+/** @type {Function} */
+System.Cycle.next;
+
+/** @type {Function} */
+System.Cycle.delay;
+
+/** @type {Function} */
+System.Cycle.perma;
+
+/** @type {Function} */
+System.Cycle.permaRemove;
+
+/** @type {System.Tick} */
+System.Cycle.TICK;
diff --git a/botanjs/src/externs/System.Debug.js b/botanjs/src/externs/System.Debug.js
new file mode 100644
index 0000000..da76ffb
--- /dev/null
+++ b/botanjs/src/externs/System.Debug.js
@@ -0,0 +1,8 @@
+/** @constructor */
+System.Debug = function (){}
+
+/** @type {Function} */
+System.Debug.Info;
+
+/** @type {Function} */
+System.Debug.Error;
diff --git a/botanjs/src/externs/System.Global.js b/botanjs/src/externs/System.Global.js
new file mode 100644
index 0000000..4a725d0
--- /dev/null
+++ b/botanjs/src/externs/System.Global.js
@@ -0,0 +1,8 @@
+/** @constructor */
+System.Global = function (){}
+
+/** @type {Boolean} */
+System.Global.debug;
+
+/** @type {Boolean} */
+System.Global.IE;
diff --git a/botanjs/src/externs/System.Log.js b/botanjs/src/externs/System.Log.js
new file mode 100644
index 0000000..504d258
--- /dev/null
+++ b/botanjs/src/externs/System.Log.js
@@ -0,0 +1,16 @@
+/** @constructor */
+System.Log = function (){}
+
+/** @type {Function} */
+System.Log.writeLine;
+/** @type {Function} */
+System.Log.registerHandler;
+/** @type {Function} */
+System.Log.removeHandler;
+
+/** @type {const} */
+System.Log.ERROR;
+/** @type {const} */
+System.Log.INFO;
+/** @type {const} */
+System.Log.SYSTEM;
diff --git a/botanjs/src/externs/System.Net.js b/botanjs/src/externs/System.Net.js
new file mode 100644
index 0000000..b4277ac
--- /dev/null
+++ b/botanjs/src/externs/System.Net.js
@@ -0,0 +1,11 @@
+/** @constructor */
+System.Net = function (){}
+
+/** @type {Function} */
+System.Net.getData;
+
+/** @type {Function} */
+System.Net.postFile;
+
+/** @type {Function} */
+System.Net.postData;
diff --git a/botanjs/src/externs/System.Policy.js b/botanjs/src/externs/System.Policy.js
new file mode 100644
index 0000000..647dd3d
--- /dev/null
+++ b/botanjs/src/externs/System.Policy.js
@@ -0,0 +1,5 @@
+/** @constructor */
+System.Policy = function (){};
+
+/** @type {Function} */
+System.Policy.isOriginAllowed;
diff --git a/botanjs/src/externs/System.Tick.js b/botanjs/src/externs/System.Tick.js
new file mode 100644
index 0000000..5deafd2
--- /dev/null
+++ b/botanjs/src/externs/System.Tick.js
@@ -0,0 +1,13 @@
+/** @constructor */
+System.Tick = function (){}
+
+/** @type {Function} */
+System.Tick.prototype.putStepper;
+
+/** @type {Function} */
+System.Tick.prototype.start;
+/** @type {Function} */
+System.Tick.prototype.stop;
+
+/** @type {Array} */
+System.Tick.prototype.steppers;
diff --git a/botanjs/src/externs/System.js b/botanjs/src/externs/System.js
new file mode 100644
index 0000000..8d48ff7
--- /dev/null
+++ b/botanjs/src/externs/System.js
@@ -0,0 +1,2 @@
+/** @constructor */
+var System = function (){}
diff --git a/botanjs/src/externs/System.utils.DataKey.js b/botanjs/src/externs/System.utils.DataKey.js
new file mode 100644
index 0000000..2528085
--- /dev/null
+++ b/botanjs/src/externs/System.utils.DataKey.js
@@ -0,0 +1,4 @@
+/** @constructor
+ *  @extends {System.utils.IKey}
+ */
+System.utils.DataKey = function (){}
diff --git a/botanjs/src/externs/System.utils.EventKey.js b/botanjs/src/externs/System.utils.EventKey.js
new file mode 100644
index 0000000..daac6d8
--- /dev/null
+++ b/botanjs/src/externs/System.utils.EventKey.js
@@ -0,0 +1,10 @@
+/** @constructor
+ *  @extends {System.utils.IKey}
+ */
+System.utils.EventKey = function (){};
+
+/** @type {String} */
+System.utils.EventKey.type;
+
+/** @type {Function} */
+System.utils.EventKey.handler;
diff --git a/botanjs/src/externs/System.utils.IKey.js b/botanjs/src/externs/System.utils.IKey.js
new file mode 100644
index 0000000..5957861
--- /dev/null
+++ b/botanjs/src/externs/System.utils.IKey.js
@@ -0,0 +1,11 @@
+/** @constructor */
+System.utils.IKey = function (){}
+
+/** @type {string} */
+System.utils.IKey.keyName;
+
+/** @type {object} */
+System.utils.IKey.keyValue;
+
+/** @type {Function} */
+System.utils.IKey.quickDef;
diff --git a/botanjs/src/externs/System.utils.Perf.js b/botanjs/src/externs/System.utils.Perf.js
new file mode 100644
index 0000000..756699f
--- /dev/null
+++ b/botanjs/src/externs/System.utils.Perf.js
@@ -0,0 +1,8 @@
+/** @constructor */
+System.utils.Perf = function (){}
+
+/** @type {String} */
+System.utils.Perf.uuid;
+
+/** @type {Function} */
+System.utils.Perf.ArrayReverse;
diff --git a/botanjs/src/externs/System.utils.js b/botanjs/src/externs/System.utils.js
new file mode 100644
index 0000000..2c1eb90
--- /dev/null
+++ b/botanjs/src/externs/System.utils.js
@@ -0,0 +1,9 @@
+/** @constructor */
+System.utils = function (){}
+
+/** @type {Function} */
+System.utils.objGetProp;
+/** @type {Function} */
+System.utils.objSearch;
+/** @type {Function} */
+System.utils.objMap;
diff --git a/cache/.keep b/cache/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/logs/.keep b/logs/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/settings.ini b/settings.ini
new file mode 100644
index 0000000..65c8da3
--- /dev/null
+++ b/settings.ini
@@ -0,0 +1,17 @@
+[Paths]
+SiteRoot = /var/www/botanjs
+Runtime = ${SiteRoot}
+Log = ${SiteRoot}/logs
+Cache = ${SiteRoot}/cache
+
+[BotanJS]
+SrcDir = ${Paths:Runtime}/botanjs/src
+
+REDIS_PASS = PASSWORD_FOR_REDIS_DB
+
+CeleryBroker = redis://:${REDIS_PASS}@123.123.123.123:1234/9
+
+
+[Service]
+user = www-data
+group = www-data