commit 4c93896df3f63cbd0f803a9c86d15fa637d7800e
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2015-08-14T10:12:10Z |
| subject | Initial 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