closes #35
diff --git a/weinre.build/.gitignore b/weinre.build/.gitignore
index b1713a9..cdd5de8 100644
--- a/weinre.build/.gitignore
+++ b/weinre.build/.gitignore
@@ -3,5 +3,6 @@
 out
 tmp
 vendor
+node_modules
 
 personal.properties
\ No newline at end of file
diff --git a/weinre.build/build.xml b/weinre.build/build.xml
index 1826ebd..290139a 100644
--- a/weinre.build/build.xml
+++ b/weinre.build/build.xml
@@ -1,27 +1,27 @@
 <!--
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
 -->
 
-<project name="build" default="build" basedir="."> 
+<project name="build" default="build" basedir=".">
 
     <!-- ============================================================
          set properties
          ============================================================ -->
-    
+
     <property file="personal.properties" />
     <property file="build.properties" />
     <property file="vendor.properties" />
 
     <!-- hint: override in your personal.properties -->
     <property name="BUILDER" value="The Unknown Builder"/>
-    
+
     <basename property="ant.file.basename" file="${ant.file}"/>
 
     <available property="VENDOR_FOUND"   file="${VENDOR}"/>
-    
+
     <tstamp>
         <format property="TSTAMP_STARTED" pattern="HH:mm:ss"/>
     </tstamp>
@@ -29,14 +29,14 @@
     <tstamp>
         <format property="BUILD_NUMBER_DATE" pattern="yyyy-MM-dd--HH-mm-ss" timezone="GMT"/>
     </tstamp>
-    
+
     <property name="BUILD_NUMBER" value="${WEINRE_VERSION}build-${BUILD_NUMBER_DATE}"/>
-    
-    
+
+
     <!-- ============================================================
          main entry point
          ============================================================ -->
-    
+
     <target name="build" depends="build-dev">
         <antcall target="build-jar"/>
         <antcall target="build-ui-jar"/>
@@ -53,12 +53,12 @@
     <target name="clean">
         <delete dir="${OUT}"/>
         <mkdir  dir="${OUT}"/> <!-- this is an Eclipse source folder! -->
-        
+
         <delete dir="${TMP}"/>
         <delete dir="${CACHED}"/>
         <delete dir="${VENDOR}"/>
     </target>
-    
+
     <!-- ============================================================
          development-time build
          ============================================================ -->
@@ -66,14 +66,14 @@
     <target name="build-dev">
         <delete dir="${OUT}"/>
         <mkdir  dir="${OUT}"/>
-        
-        <property 
+
+        <property
             name  = "build-info-text"
             value = "weinre build ${BUILD_NUMBER} by ${BUILDER}"
         />
-        
+
         <echo file="${OUT}/build-info.txt" message="${build-info-text}"/>
-        
+
         <antcall target="build-vendor"/>
         <antcall target="compile-java"/>
         <antcall target="build-web"/>
@@ -85,7 +85,7 @@
         <tstamp>
             <format property="TSTAMP_FINISHED" pattern="HH:mm:ss"/>
         </tstamp>
-        
+
         <property name="MESSAGE_FINISHED" value="at ${TSTAMP_FINISHED} for ${ant.file}:build-dev"/>
 
         <antcall target="finished"/>
@@ -100,7 +100,7 @@
         <delete dir="../${PROJECT_SERVER}/bin"/>
         <mkdir  dir="../${PROJECT_SERVER}/bin"/>
 
-        <javac 
+        <javac
             srcdir            = "../${PROJECT_SERVER}/src"
             destdir           = "../${PROJECT_SERVER}/bin"
             debug             = "true"
@@ -113,11 +113,11 @@
             <classpath path="../${PROJECT_BUILD}/vendor/jetty/servlet-api.jar"/>
             <compilerarg value="-Xlint"/>
         </javac>
-        
+
         <delete dir="../${PROJECT_APPLICATION}/bin"/>
         <mkdir  dir="../${PROJECT_APPLICATION}/bin"/>
-        
-        <javac 
+
+        <javac
             srcdir            = "../${PROJECT_APPLICATION}/src"
             destdir           = "../${PROJECT_APPLICATION}/bin"
             debug             = "true"
@@ -129,9 +129,9 @@
             <classpath path="../${PROJECT_BUILD}/vendor/swt/cocoa-macosx-x86_64/swt.jar"/>
             <compilerarg value="-Xlint"/>
         </javac>
-        
+
     </target>
-        
+
     <!-- ============================================================
          build vendor libs
          ============================================================ -->
@@ -139,7 +139,7 @@
     <target name="build-vendor" unless="VENDOR_FOUND">
         <ant antfile="get-vendor.xml"/>
     </target>
-        
+
     <!-- ============================================================
          basic web resources
          ============================================================ -->
@@ -147,22 +147,50 @@
     <target name="build-web">
         <delete dir="${WEB}" />
         <mkdir  dir="${WEB}" />
-        
+
         <delete dir="${TMP}" />
         <mkdir  dir="${TMP}" />
 
+<!--
         <echo message="compiling scoop modules"/>
         <exec executable="python" failonerror="true" failifexecutionfails="true">
             <arg  file="vendor/scooj/scoopc.py"/>
-            <arg value="--out"/>
+            <arg value="-o"/>
             <arg  file="${TMP}/modules"/>
             <arg  file="../${PROJECT_WEB}/modules"/>
         </exec>
-            
+-->
+
+        <echo message="compiling CoffeeScript modules"/>
+        <mkdir  dir="${TMP}/modules/weinre/client" />
+        <mkdir  dir="${TMP}/modules/weinre/common" />
+        <mkdir  dir="${TMP}/modules/weinre/target" />
+
+        <echo message="compiling CoffeeScript files in: ${PROJECT_WEB}/modules/weinre/client"/>
+        <exec executable="sh" failonerror="true" failifexecutionfails="true">
+            <arg value="-c"/>
+            <arg value="node_modules/.bin/coffee --compile --bare --output ${TMP}/modules/weinre/client ../${PROJECT_WEB}/modules/weinre/client/*.coffee"/>
+        </exec>
+
+        <echo message="compiling CoffeeScript files in: ${PROJECT_WEB}/modules/weinre/common"/>
+        <exec executable="sh" failonerror="true" failifexecutionfails="true">
+            <arg value="-c"/>
+            <arg value="node_modules/.bin/coffee --compile --bare --output ${TMP}/modules/weinre/common ../${PROJECT_WEB}/modules/weinre/common/*.coffee"/>
+        </exec>
+
+        <echo message="compiling CoffeeScript files in: ${PROJECT_WEB}/modules/weinre/target"/>
+        <exec executable="sh" failonerror="true" failifexecutionfails="true">
+            <arg value="-c"/>
+            <arg value="node_modules/.bin/coffee --compile --bare --output ${TMP}/modules/weinre/target ../${PROJECT_WEB}/modules/weinre/target/*.coffee"/>
+        </exec>
+
+
+<!--
         <copy todir="${TMP}/modules">
             <fileset file="${VENDOR}/scooj/scooj.js"/>
         </copy>
-        
+-->
+
         <echo message="transportd-izing modules"/>
         <exec executable="python" failonerror="true" failifexecutionfails="true">
             <arg  file="vendor/modjewel/module2transportd.py"/>
@@ -175,11 +203,11 @@
         <copy todir="${WEB}">
             <fileset file="${VENDOR}/modjewel/modjewel-require.js"/>
         </copy>
-        
+
         <copy todir="${WEB}">
             <fileset file="${VENDOR}/json2/json2.js"/>
         </copy>
-        
+
         <copy todir="${WEB}">
             <fileset dir="../${PROJECT_WEB}">
                 <exclude name="versions.js"/>
@@ -193,23 +221,23 @@
         </copy>
 
         <echo message="building InjectedScript.js"/>
-        <echo 
+        <echo
             file    = "${WEB}/weinre/target/InjectedScript.js"
             message = "var injectedScriptConstructor = ${line.separator}"
         />
-        <concat 
+        <concat
             destfile = "${WEB}/weinre/target/InjectedScript.js"
             append   = "true"
         >
             <fileset file="${VENDOR}/webkit/WebCore/inspector/InjectedScriptSource.js"/>
         </concat>
-            
+
         <echo message="building versions.js"/>
         <copy todir="${WEB}">
             <fileset dir="../${PROJECT_WEB}">
                 <include name="versions.js"/>
             </fileset>
-        
+
             <filterset>
                 <filter token="WEINRE_VERSION"             value="${WEINRE_VERSION}"/>
                 <filter token="BUILD_NUMBER"               value="${BUILD_NUMBER}"/>
@@ -233,7 +261,7 @@
             <arg value="vendor/webkit/WebCore/CSSPropertyNames.in"/>
             <arg value="${WEB}/add-css-properties.js"/>
         </exec>
-        
+
     </target>
 
     <!-- ============================================================
@@ -243,9 +271,9 @@
     <target name="build-doc">
 
         <mkdir  dir="${WEB}/doc" />
-        
+
         <copy todir="${WEB}/doc" file="${OUT}/build-info.txt"/>
-        
+
         <build-html name="Building"     oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
         <build-html name="Home"         oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
         <build-html name="Installing"   oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
@@ -255,17 +283,17 @@
         <build-html name="Security"     oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
         <build-html name="TestDrive"    oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
         <build-html name="ChangeLog"    oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
-        
+
         <copy file="${WEB}/doc/Home.html" tofile="${WEB}/doc/index.html"/>
-        
+
         <copy todir="${WEB}/doc/images" file="../${PROJECT_WEB}/images/weinre-icon-64x64.png"/>
 
         <copy todir="${WEB}/doc/css">     <fileset dir="../${PROJECT_DOC}/css"/>     </copy>
         <copy todir="${WEB}/doc/images">  <fileset dir="../${PROJECT_DOC}/images"/>  </copy>
         <copy todir="${WEB}/doc/scripts"> <fileset dir="../${PROJECT_DOC}/scripts"/> </copy>
-        
+
     </target>
-    
+
     <!-- ============================================================
          convert IDL to JSON (cached)
          ============================================================ -->
@@ -275,21 +303,21 @@
             <srcfiles dir="../${PROJECT_SERVER}/interfaces"    includes="*.idl"/>
             <srcfiles dir="${VENDOR}/webkit/WebCore/inspector" includes="*.idl"/>
         </uptodate>
-        
+
         <antcall target="build-json-idl-fresh"/>
-        
+
         <copy todir="${WEB}/interfaces"> <fileset dir="${CACHED}/json-idl"/></copy>
     </target>
-    
+
     <!-- ============================================================
          convert IDL to JSON (slow part)
          ============================================================ -->
-    
+
     <target name="build-json-idl-fresh" unless="JSON_IDL_UPTODATE">
 
         <delete dir="${CACHED}/json-idl"/>
         <mkdir  dir="${CACHED}/json-idl"/>
-        
+
         <simple-idl2json name="WeinreClientCommands"      oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
         <simple-idl2json name="WeinreClientEvents"        oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
         <simple-idl2json name="WeinreTargetCommands"      oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
@@ -298,12 +326,12 @@
         <simple-idl2json name="WeinreExtraTargetEvents"   oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
 
         <simple-idl2json name="InjectedScriptHost"    oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector"/>
-        
+
         <simple-idl2json name="Inspector"             oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector">
             <arg value="--anyType"/>
             <arg value="Value"/>
         </simple-idl2json>
-        
+
         <simple-idl2json name="InspectorFrontendHost" oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector">
             <arg value="--anyType"/>
             <arg value="MouseEvent"/>
@@ -329,24 +357,24 @@
          ============================================================ -->
 
     <target name="build-target">
-        
+
         <mkdir dir="${WEB}/target"/>
-        
+
         <exec executable="python" failonerror="true" failifexecutionfails="true">
             <arg file="scripts/build-target-scripts.py"/>
             <arg file="../${PROJECT_WEB}/demo/weinre-demo-pieces.html"/>
             <arg file="${WEB}"/>
             <arg file="${WEB}/target"/>
         </exec>
-        
+
     </target>
 
     <!-- ============================================================
          build the web/client files
          ============================================================ -->
-        
+
     <target name="build-client">
-        
+
         <copy todir="${WEB}/client/nls/English.lproj">
             <fileset file="${VENDOR}/webkit/WebCore/English.lproj/localizedStrings.js"/>
         </copy>
@@ -359,51 +387,51 @@
             <arg file="scripts/build-client-html.py"/>
             <arg file="${WEB}"/>
         </exec>
-        
+
     </target>
-    
+
     <!-- ============================================================
          build the main jar
          ============================================================ -->
-        
+
     <target name="build-jar">
-        
+
         <jar destfile="${OUT}/weinre.jar">
             <manifest>
                 <attribute name="Built-By" value="${ant.file.basename} - ${ant.project.name}"/>
                 <attribute name="Main-Class" value="weinre.server.Main"/>
             </manifest>
-            
+
             <fileset file="${OUT}/build-info.txt"/>
 
             <zipfileset src="${VENDOR}/jetty/jetty.jar">
                 <exclude name="about*.html"/>
                 <exclude name="META-INF/**/*"/>
             </zipfileset>
-            
+
             <zipfileset src="${VENDOR}/jetty/servlet-api.jar">
                 <exclude name="META-INF/**/*"/>
             </zipfileset>
-            
+
             <zipfileset src="${VENDOR}/cli/commons-cli.jar">
                 <exclude name="META-INF/**/*"/>
             </zipfileset>
-            
+
             <zipfileset src="${VENDOR}/json4j/json4j.jar">
                 <exclude name="META-INF/**/*"/>
             </zipfileset>
-            
+
             <fileset dir="../${PROJECT_SERVER}/bin"/>
-                
+
             <zipfileset dir="${WEB}" prefix="web"/>
         </jar>
-        
+
     </target>
 
     <!-- ============================================================
          build the ui jar
          ============================================================ -->
-        
+
     <target name="build-ui-jar">
 
         <fileset dir="../${PROJECT_APPLICATION}/bin"/>
@@ -413,22 +441,22 @@
                 <attribute name="Built-By" value="${ant.file.basename} - ${ant.project.name}"/>
                 <attribute name="Main-Class" value="weinre.application.GUIMain"/>
             </manifest>
-            
+
             <fileset file="${OUT}/build-info.txt"/>
-            
+
             <fileset dir="../${PROJECT_APPLICATION}/bin"/>
-            
+
             <zipfileset src="${VENDOR}/swt/cocoa-macosx-x86_64/swt.jar">
                 <exclude name="META-INF/**/*"/>
             </zipfileset>
         </jar>
-        
+
     </target>
 
     <!-- ============================================================
          build the mac application
          ============================================================ -->
-        
+
     <target name="build-application-mac">
         <delete dir="${OUT}/applications/mac"/>
         <mkdir  dir="${OUT}/applications/mac/weinre.app"/>
@@ -438,7 +466,7 @@
                 <exclude name="**/Info.plist"/>
             </fileset>
         </copy>
-        
+
         <copy todir="${OUT}/applications/mac/weinre.app">
             <fileset dir="../${PROJECT_APPLICATION}/resources/macosx/weinre.app">
                 <include name="**/Info.plist"/>
@@ -447,25 +475,25 @@
                 <filter token="VERSION" value="${WEINRE_VERSION}"/>
             </filterset>
         </copy>
-        
+
         <chmod file="${OUT}/applications/mac/weinre.app/Contents/MacOS/launcher" perm="+x"/>
         <copy todir="${OUT}/applications/mac/weinre.app/Contents/MacOS" file="${OUT}/weinre.jar" />
         <copy todir="${OUT}/applications/mac/weinre.app/Contents/MacOS" file="${OUT}/weinre-ui.jar"/>
         <copy todir="${OUT}/applications/mac/weinre.app/Contents/Resources" file="${OUT}/build-info.txt"/>
-        
+
     </target>
 
     <!-- ============================================================
          build the archives
          ============================================================ -->
     <target name="build-archives">
-        
+
         <!-- =================================== -->
         <delete dir="${OUT}/archives"/>
         <mkdir  dir="${OUT}/archives"/>
-        
+
         <delete dir="${OUT}/files"/>
-        
+
         <delete dir="${OUT}/files/weinre-jar"/>
         <mkdir  dir="${OUT}/files/weinre-jar"/>
         <copy todir="${OUT}/files/weinre-jar" file="${OUT}/build-info.txt"/>
@@ -487,7 +515,7 @@
             <fileset dir="${OUT}/applications/mac"/>
         </copy>
         <chmod file="${OUT}/files/weinre-mac/weinre.app/Contents/MacOS/launcher" perm="+x"/>
-        
+
         <!-- ant's zip task doesn't preserve permission bits -->
         <exec executable="zip" dir="${OUT}/files">
             <arg value="-q"/>
@@ -503,7 +531,7 @@
         <copy todir="${OUT}/files/weinre-doc">
             <fileset dir="${OUT}/web/doc"/>
         </copy>
-        
+
         <!-- ant's zip task doesn't preserve permission bits -->
         <exec executable="zip" dir="${OUT}/files">
             <arg value="-q"/>
@@ -521,7 +549,7 @@
         <copy todir="${OUT}/files/weinre-src/weinre.doc">         <fileset dir="../weinre.doc"         excludes="**/bin/"/></copy>
         <copy todir="${OUT}/files/weinre-src/weinre.server">      <fileset dir="../weinre.server"      excludes="**/bin/"/></copy>
         <copy todir="${OUT}/files/weinre-src/weinre.web">         <fileset dir="../weinre.web"         excludes="**/bin/"/></copy>
-        
+
         <!-- ant's zip task doesn't preserve permission bits -->
         <exec executable="zip" dir="${OUT}/files">
             <arg value="-q"/>
@@ -529,25 +557,25 @@
             <arg value="../archives/weinre-src-${BUILD_NUMBER}.zip"/>
             <arg value="weinre-src"/>
         </exec>
-        
+
         <!-- =================================== -->
         <delete dir="${OUT}/files"/>
-        
+
     </target>
-            
-    
+
+
     <!-- ============================================================
          done!
          ============================================================ -->
-             
+
     <target name="finished">
         <echo>ant completed successfully ${MESSAGE_FINISHED}</echo>
     </target>
-    
+
     <!-- ============================================================
          done! via growl
          ============================================================ -->
-             
+
     <target name="finished-growl" if="USE_GROWL">
         <exec os="Mac OS X" executable="growlnotify">
             <arg value="-m"/>
@@ -559,7 +587,7 @@
     <!-- ============================================================
          macro to compile IDL to JSON
          ============================================================ -->
-        
+
     <macrodef name="simple-idl2json">
        <attribute name="name"/>
        <attribute name="iDir"/>
@@ -575,11 +603,11 @@
            </exec>
        </sequential>
     </macrodef>
-                    
+
     <!-- ============================================================
          macro to compile HTML
          ============================================================ -->
-        
+
     <macrodef name="build-html">
        <attribute name="name"/>
        <attribute name="iDir"/>
@@ -600,6 +628,6 @@
            <echo>generated @{oDir}/@{name}.html</echo>
        </sequential>
     </macrodef>
-                    
+
     <!-- ============================================================ -->
 </project>
diff --git a/weinre.build/get-vendor.xml b/weinre.build/get-vendor.xml
index 4e2b477..1984cf9 100644
--- a/weinre.build/get-vendor.xml
+++ b/weinre.build/get-vendor.xml
@@ -1,23 +1,23 @@
 <!--
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
 -->
 
 <project name="get-vendor" default="get-vendor" basedir=".">
-    
+
     <property file="personal.properties"/>
     <property file="build.properties"/>
     <property file="vendor.properties"/>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-vendor">
         <echo>Go get some coffee, this is going to take a while</echo>
-            
+
         <delete dir="${TMP}"/>
-        
+
         <delete dir="${VENDOR}"/>
         <mkdir  dir="${VENDOR}"/>
 
@@ -26,20 +26,21 @@
         <antcall target="get-json4j"/>
         <antcall target="get-jetty"/>
         <antcall target="get-scooj"/>
+        <antcall target="get-coffeescript"/>
         <antcall target="get-modjewel"/>
         <antcall target="get-web-inspector"/>
         <antcall target="get-swt-mac"/>
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-cli">
         <delete dir="${VENDOR}/cli"/>
         <mkdir  dir="${VENDOR}/cli"/>
-        
+
         <delete dir="${TMP}"/>
         <mkdir  dir="${TMP}"/>
-        
+
         <get src="${CLI_URL}" dest="${TMP}/commons-cli.zip" verbose="false"/>
         <unzip src="${TMP}/commons-cli.zip" dest="${TMP}">
             <patternset>
@@ -47,20 +48,20 @@
                 <include name="${CLI_BASENAME}.jar"/>
             </patternset>
         </unzip>
-                    
-            
+
+
         <copy file="${TMP}/${CLI_BASENAME}.jar"         tofile="${VENDOR}/cli/commons-cli.jar"/>
         <copy file="${TMP}/${CLI_BASENAME}-sources.jar" tofile="${VENDOR}/cli/commons-cli-src.jar"/>
-            
+
         <delete dir="${TMP}"/>
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-json4j">
         <delete dir="${VENDOR}/json4j"/>
         <mkdir  dir="${VENDOR}/json4j"/>
-        
+
         <delete dir="${TMP}"/>
         <mkdir  dir="${TMP}"/>
 
@@ -72,7 +73,7 @@
         </unzip>
 
         <copy file="${TMP}/apache-wink-${JSON4J_VERSION}/ext/wink-json4j-provider/lib/wink-json4j-${JSON4J_VERSION}.jar" tofile="${VENDOR}/json4j/json4j.jar"/>
-        
+
         <delete dir="${TMP}"/>
         <mkdir  dir="${TMP}"/>
         <get src="${JSON4J_SRC_URL}" dest="${TMP}/json4j-src.zip" verbose="false"/>
@@ -81,99 +82,109 @@
                 <include name="**/wink-json4j/src/main/java/org/apache/wink/json4j/**/*"/>
             </patternset>
         </unzip>
-        
+
         <zip destfile="${VENDOR}/json4j/json4j-src.jar" basedir="${TMP}/src"/>
 
         <delete dir="${TMP}"/>
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-jetty">
         <delete dir="${VENDOR}/jetty"/>
         <mkdir  dir="${VENDOR}/jetty"/>
-        
+
         <get src="${JETTY_SRC_URL}"     dest="${VENDOR}/jetty/jetty-src.jar"   verbose="false"/>
         <get src="${JETTY_BIN_URL}"     dest="${VENDOR}/jetty/jetty.jar"       verbose="false"/>
         <get src="${JAVAX_SERVLET_URL}" dest="${VENDOR}/jetty/servlet-api.jar" verbose="false"/>
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-scooj">
         <delete dir="${VENDOR}/scooj"/>
         <mkdir  dir="${VENDOR}/scooj"/>
-        
+
         <get src="${SCOOJ_URL_PREFIX}/scooj.js"  dest="${VENDOR}/scooj/scooj.js"  verbose="false"/>
         <get src="${SCOOJ_URL_PREFIX}/scoopc.py" dest="${VENDOR}/scooj/scoopc.py" verbose="false"/>
     </target>
-    
+
+    <!-- ============================================================ -->
+
+    <target name="get-coffeescript">
+        <echo message="coffee-script@${COFFEESCRIPT_VERSION}"/>
+        <exec executable="npm">
+            <arg value="install"/>
+            <arg value="coffee-script@${COFFEESCRIPT_VERSION}"/>
+        </exec>
+    </target>
+
     <!-- ============================================================ -->
 
     <target name="get-modjewel">
         <delete dir="${VENDOR}/modjewel"/>
         <mkdir  dir="${VENDOR}/modjewel"/>
-        
+
         <get src="${MODJEWEL_URL_PREFIX}/modjewel-require.js"  dest="${VENDOR}/modjewel/modjewel-require.js"  verbose="false"/>
         <get src="${MODJEWEL_URL_PREFIX}/module2transportd.py" dest="${VENDOR}/modjewel/module2transportd.py" verbose="false"/>
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-web-inspector">
         <delete dir="${VENDOR}/webkit"/>
         <mkdir  dir="${VENDOR}/webkit/WebCore"/>
-        
+
         <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/inspector"/>
         </exec>
-        
+
         <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/English.lproj"/>
         </exec>
-        
+
         <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/css/CSSPropertyNames.in"/>
         </exec>
-        
+
         <exec executable="grep" outputproperty="gpl-grep-results" dir="${VENDOR}/webkit/WebCore">
-            <arg value="-r"/>        
-            <arg value="GPL"/>        
-            <arg value="."/>        
+            <arg value="-r"/>
+            <arg value="GPL"/>
+            <arg value="."/>
         </exec>
-        
+
         <condition property="found-no-gpl-files">
             <equals arg1="" arg2="${gpl-grep-results}"/>
         </condition>
-        
+
         <fail unless="found-no-gpl-files" message="The string GPL was found in the WebKit files"/>
-            
+
     </target>
-    
+
     <!-- ============================================================ -->
 
     <target name="get-swt-mac">
         <delete dir="${VENDOR}/swt"/>
         <mkdir  dir="${VENDOR}/swt"/>
-            
+
         <delete dir="${TMP}"/>
         <mkdir dir="${TMP}"/>
-            
+
         <get src="${SWT_URL_PREFIX}/swt-${SWT_VERSION_MAJOR}-cocoa-macosx-x86_64.zip" dest="${TMP}/swt.zip" verbose="false"/>
         <unzip src="${TMP}/swt.zip" dest="${TMP}"/>
-            
+
         <copy file="${TMP}/src.zip"       tofile="${VENDOR}/swt/cocoa-macosx-x86_64/swt-src.jar"/>
         <copy file="${TMP}/swt-debug.jar" tofile="${VENDOR}/swt/cocoa-macosx-x86_64/swt.jar"/>
-        
+
         <delete dir="${TMP}"/>
     </target>
 
@@ -182,7 +193,7 @@
     <target name="get-json2">
         <delete dir="${VENDOR}/json2"/>
         <mkdir  dir="${VENDOR}/json2"/>
-        
+
         <get src="${JSON2_URL_PREFIX}/json2.js"  dest="${VENDOR}/json2/json2.js"  verbose="false"/>
     </target>
 
diff --git a/weinre.build/scripts/build-client-html.py b/weinre.build/scripts/build-client-html.py
index 690572a..5186010 100644
--- a/weinre.build/scripts/build-client-html.py
+++ b/weinre.build/scripts/build-client-html.py
@@ -3,7 +3,7 @@
 # ---
 # weinre is available under *either* the terms of the modified BSD license *or* the
 # MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
-# 
+#
 # Copyright (c) 2010, 2011 IBM Corporation
 # ---
 
@@ -19,24 +19,24 @@
         error("expecting parameters [web directory]")
 
     webDir = sys.argv[1]
-    
+
     iFileName = os.path.join(webDir, "client/inspector.html")
     oFileName = os.path.join(webDir, "client/index.html")
     moduleDir = os.path.join(webDir, "weinre")
-    
+
     if not os.path.exists(iFileName): error("file does not exist: %s" % iFileName)
     if not os.path.exists(moduleDir): error("module directory does not exist: %s" % moduleDir)
     if not os.path.isdir(moduleDir):  error("module directory is not a directory: %s" % moduleDir)
-    
+
     createIndexFile(iFileName, oFileName, moduleDir)
 
 #--------------------------------------------------------------------
 def createIndexFile(iFileName, oFileName, moduleDir):
     with open(iFileName) as iFile: lines = iFile.readlines()
-    
+
     pattern_head_start = re.compile(r"^\s*<meta http-equiv=\"content-type\".*$")
     pattern_head_end   = re.compile(r"^\s*</head>\s$")
-    
+
     newLines   = []
     foundStart = False
     foundEnd   = False
@@ -44,7 +44,7 @@
         if pattern_head_start.match(line):
             foundStart = True
             newLines.append(line)
-            
+
             newLines.append("<!-- ========== weinre additions: starting ========== -->\n")
             newLines.extend([
                 '<meta http-equiv="X-UA-Compatible" content="chrome=1">\n'
@@ -56,27 +56,27 @@
                 '<script type="text/javascript">require("modjewel").warnOnRecursiveRequire(true)</script>\n',
                 '<script type="text/javascript" src="../scooj.transportd.js"></script>\n',
             ])
-            
+
             for module in getModules(moduleDir):
                 newLines.append('<script type="text/javascript" src="../%s"></script>\n' % module)
-                
+
             newLines.append("<!-- ========== weinre additions: done ========== -->\n")
-            
+
         elif pattern_head_end.match(line):
             foundEnd = True
             newLines.append("<!-- ========== weinre additions: starting ========== -->\n")
             newLines.append('<link rel="stylesheet" type="text/css" href="weinre/client.css">\n')
             newLines.append('<script type="text/javascript" src="../interfaces/all-json-idls-min.js"></script>\n')
-            newLines.append('<script type="text/javascript">require("weinre/client/Client").getClass().main()</script>\n')
+            newLines.append('<script type="text/javascript">require("weinre/client/Client").main()</script>\n')
             newLines.append("<!-- ========== weinre additions: done ========== -->\n")
             newLines.append(line)
-        
+
         else:
             newLines.append(line)
-            
+
     if not foundStart: error("didn't find the location to start writing")
     if not foundEnd:   error("didn't find the location to finish writing")
-    
+
     with open(oFileName, "w") as oFile: oFile.writelines(newLines)
 
     log("created %s" % oFileName)
@@ -84,15 +84,15 @@
 #--------------------------------------------------------------------
 def getModules(moduleDir):
     modules = []
-    
+
     for module in os.listdir(os.path.join(moduleDir, "common")):
         modules.append("weinre/common/%s" % module)
-    
+
     for module in os.listdir(os.path.join(moduleDir, "client")):
         modules.append("weinre/client/%s" % module)
 
     return modules
-        
+
 #--------------------------------------------------------------------
 def log(message):
     message = "%s: %s" % (PROGRAM_NAME, message)
diff --git a/weinre.build/scripts/build-css-properties.py b/weinre.build/scripts/build-css-properties.py
index 596e461..c6fde75 100644
--- a/weinre.build/scripts/build-css-properties.py
+++ b/weinre.build/scripts/build-css-properties.py
@@ -3,7 +3,7 @@
 # ---
 # weinre is available under *either* the terms of the modified BSD license *or* the
 # MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
-# 
+#
 # Copyright (c) 2010, 2011 IBM Corporation
 # ---
 
@@ -14,34 +14,34 @@
 import optparse
 
 #--------------------------------------------------------------------
-# reads: 
+# reads:
 #   http://svn.webkit.org/repository/webkit/trunk/WebCore/css/CSSPropertyNames.in
 # writes:
-#   json array 
+#   json array
 #--------------------------------------------------------------------
 def main():
     if len(sys.argv) < 3:
         error("expecting parameters inputFile outputFile")
-        
+
     iFileName = sys.argv[1]
     oFileName = sys.argv[2]
-    
+
     if not os.path.exists(iFileName): error("input file not found: '" + iFileName + "'")
-    
+
     with open(iFileName, "r") as iFile:
         lines = iFile.readlines()
 
     properties = [line.strip() for line in lines if not line.strip().startswith("#")]
     properties = [property for property in properties if property != ""]
     properties.sort()
-        
+
     jsonString = json.dumps(properties, indent=4)
-    jsString = 'require("weinre/common/Weinre").getClass().addCSSProperties(%s)' % jsonString
+    jsString = 'require("weinre/common/Weinre").addCSSProperties(%s)' % jsonString
 
     oFile = open(oFileName, "w")
     oFile.write(jsString)
     oFile.close()
-    
+
     log("generated css properties in: " + oFileName)
 
 #--------------------------------------------------------------------
diff --git a/weinre.build/scripts/collect-all-json-idl.py b/weinre.build/scripts/collect-all-json-idl.py
index c9c6281..2d1d912 100644
--- a/weinre.build/scripts/collect-all-json-idl.py
+++ b/weinre.build/scripts/collect-all-json-idl.py
@@ -3,7 +3,7 @@
 # ---
 # weinre is available under *either* the terms of the modified BSD license *or* the
 # MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
-# 
+#
 # Copyright (c) 2010, 2011 IBM Corporation
 # ---
 
@@ -17,17 +17,17 @@
 def main():
     if len(sys.argv) < 3:
         error("expecting parameters outputFile inputDir")
-        
+
     min = False
     if sys.argv[1] == "-min":
         min = True
         oFileName = sys.argv[2]
         iDirName  = sys.argv[3]
-        
+
     else:
         oFileName = sys.argv[1]
         iDirName  = sys.argv[2]
-    
+
     entries  = os.listdir(iDirName)
     if 0 == len(entries): error("no files found in '" + iDirName + "'")
 
@@ -38,26 +38,26 @@
     for entry in entries:
         iFileName = os.path.join(iDirName, entry)
         if not os.path.exists(iFileName): error("File not found: '" + iFileName + "'")
-        
+
         iFile = open(iFileName, "r")
         contents = iFile.read()
         iFile.close()
-        
+
         result.append(json.loads(contents))
-        
+
     if min:
         result = minimize(result)
         jsonString = json.dumps(result)
-    
+
     else:
         jsonString = json.dumps(result, indent=4)
-        
-    jsString = "require('weinre/common/Weinre').getClass().addIDLs(%s)" % jsonString
+
+    jsString = "require('weinre/common/Weinre').addIDLs(%s)" % jsonString
 
     oFile = open(oFileName, "w")
     oFile.write(jsString)
     oFile.close()
-    
+
     log("generated collected json idls in: " + oFileName)
 
 #--------------------------------------------------------------------
@@ -66,7 +66,7 @@
         for interface in module["interfaces"]:
             if "extendedAttributes" in interface:
                 del interface["extendedAttributes"]
-            
+
             if "methods" in interface:
                 for method in interface["methods"]:
                     if "returns" in method:
@@ -75,12 +75,12 @@
                         del method["callbackParameters"]
                     if "extendedAttributes" in method:
                         del method["extendedAttributes"]
-                        
+
                     if "parameters" in method:
                         for parameter in method["parameters"]:
                             if "type" in parameter:
                                 del parameter["type"]
-        
+
     return idl
 
 #--------------------------------------------------------------------
diff --git a/weinre.build/vendor.properties b/weinre.build/vendor.properties
index 8cad8e0..bdb8f72 100644
--- a/weinre.build/vendor.properties
+++ b/weinre.build/vendor.properties
@@ -1,7 +1,7 @@
 # ---
 # weinre is available under *either* the terms of the modified BSD license *or* the
 # MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
-# 
+#
 # Copyright (c) 2011 IBM Corporation
 # ---
 
@@ -28,7 +28,7 @@
 CLI_BASENAME:   commons-cli-${CLI_VERSION}/commons-cli-${CLI_VERSION}
 
 #-----------------------------------------------------------
-# location of JSON4J 
+# location of JSON4J
 #-----------------------------------------------------------
 
 JSON4J_VERSION:    1.1.2-incubating
@@ -51,7 +51,7 @@
 JETTY_BIN_URL:              ${JETTY_URL_PREFIX}/aggregate/jetty-all-server/${JETTY_VERSION}/${JETTY_BIN_JAR_NAME}
 
 #-----------------------------------------------------------
-# location of servlet API 
+# location of servlet API
 #-----------------------------------------------------------
 
 JAVAX_SERVLET_VERSION:      2.5-20110124
@@ -77,6 +77,12 @@
 SCOOJ_URL_PREFIX: https://github.com/pmuellr/scooj/raw/${SCOOJ_VERSION}
 
 #-----------------------------------------------------------
+# location of CoffeeScript
+#-----------------------------------------------------------
+
+COFFEESCRIPT_VERSION:    1.1.2
+
+#-----------------------------------------------------------
 # location of json2
 #-----------------------------------------------------------
 
diff --git a/weinre.web/client/ExtensionRegistryStub.js b/weinre.web/client/ExtensionRegistryStub.js
index 8e30a7e..396f3f1 100644
--- a/weinre.web/client/ExtensionRegistryStub.js
+++ b/weinre.web/client/ExtensionRegistryStub.js
@@ -1,11 +1,11 @@
 /*
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2011 IBM Corporation
  */
 
 (function() {
-    var ExtensionRegistryImpl = require("weinre/client/ExtensionRegistryImpl").getClass()
+    var ExtensionRegistryImpl = require("weinre/client/ExtensionRegistryImpl")
     window.InspectorExtensionRegistry = new ExtensionRegistryImpl()
 })()
diff --git a/weinre.web/client/InspectorBackendStub.js b/weinre.web/client/InspectorBackendStub.js
index 091c2a4..f8d41f2 100644
--- a/weinre.web/client/InspectorBackendStub.js
+++ b/weinre.web/client/InspectorBackendStub.js
@@ -1,11 +1,11 @@
 /*
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
  */
 
 (function() {
-    var InspectorBackendImpl = require("weinre/client/InspectorBackendImpl").getClass()
+    var InspectorBackendImpl = require("weinre/client/InspectorBackendImpl")
     window.InspectorBackend = new InspectorBackendImpl()
 })()
diff --git a/weinre.web/client/InspectorFrontendHostStub.js b/weinre.web/client/InspectorFrontendHostStub.js
index d964918..65f648b 100644
--- a/weinre.web/client/InspectorFrontendHostStub.js
+++ b/weinre.web/client/InspectorFrontendHostStub.js
@@ -1,11 +1,11 @@
 /*
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
  */
 
 (function() {
-    var InspectorFrontEndHostImpl = require("weinre/client/InspectorFrontendHostImpl").getClass()
-    window.InspectorFrontendHost = new InspectorFrontEndHostImpl() 
+    var InspectorFrontEndHostImpl = require("weinre/client/InspectorFrontendHostImpl")
+    window.InspectorFrontendHost = new InspectorFrontEndHostImpl()
 })()
diff --git a/weinre.web/demo/weinre-demo-pieces.html b/weinre.web/demo/weinre-demo-pieces.html
index 51b261f..bb38a17 100644
--- a/weinre.web/demo/weinre-demo-pieces.html
+++ b/weinre.web/demo/weinre-demo-pieces.html
@@ -1,7 +1,7 @@
 <!--
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
 -->
 
@@ -12,7 +12,6 @@
 
 <script src="/modjewel-require.js"></script>
 <script type="text/javascript">require("modjewel").warnOnRecursiveRequire(true)</script>
-<script src="/scooj.transportd.js"></script>
 <script src="/weinre/common/Ex.transportd.js"></script>
 <script src="/weinre/common/StackTrace.transportd.js"></script>
 <script src="/weinre/common/Weinre.transportd.js"></script>
@@ -24,6 +23,7 @@
 <script src="/weinre/common/EventListeners.transportd.js"></script>
 <script src="/weinre/common/Native.transportd.js"></script>
 <script src="/weinre/common/IDGenerator.transportd.js"></script>
+<script src="/weinre/common/MethodNamer.transportd.js"></script>
 <script src="/weinre/target/Console.transportd.js"></script>
 <script src="/add-css-properties.js"></script>
 <script src="/weinre/target/CheckForProblems.transportd.js"></script>
@@ -47,8 +47,8 @@
 <script src="/interfaces/all-json-idls-min.js"></script>
 
 <script type="text/javascript">
-    require("weinre/common/Weinre").getClass().showNotImplemented()
-    require('weinre/target/Target').getClass().main()
+    require("weinre/common/Weinre").showNotImplemented()
+    require('weinre/target/Target').main()
 </script>
 
 <link rel="stylesheet" href="weinre-demo.css">
diff --git a/weinre.web/index.html b/weinre.web/index.html
index ed4f2a8..05f3e53 100644
--- a/weinre.web/index.html
+++ b/weinre.web/index.html
@@ -3,7 +3,7 @@
 <!--
  * weinre is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- * 
+ *
  * Copyright (c) 2010, 2011 IBM Corporation
 -->
 
@@ -20,9 +20,9 @@
 h1, h2 {
     padding:                0.25em 0.50em;
     background-color:       #DDD;
-    border-radius:          0.5em; 
-    -moz-border-radius:     0.5em; 
-    -webkit-border-radius:  0.5em;   
+    border-radius:          0.5em;
+    -moz-border-radius:     0.5em;
+    -webkit-border-radius:  0.5em;
 }
 td {
     vertical-align:         top;
@@ -64,21 +64,21 @@
 
 <h2>Target Bookmarklet</h2>
 
-<p>You can use this bookmarklet to inject the weinre target code into any 
+<p>You can use this bookmarklet to inject the weinre target code into any
 web page you are viewing.
 
-<p>link you can drag to your bookmarks: 
+<p>link you can drag to your bookmarks:
 
 <div class="indent">
-<span id="url-target-bookmarklet">???</span> 
+<span id="url-target-bookmarklet">???</span>
 </div>
 
-<p>bookmarklet url in a pre: 
+<p>bookmarklet url in a pre:
 <pre id="target-bookmarklet-src-pre"></pre>
 
-<p>bookmarklet url in a textarea: 
+<p>bookmarklet url in a textarea:
 <div class="indent">
-<textarea id="target-bookmarklet-src-text-area"></textarea> 
+<textarea id="target-bookmarklet-src-text-area"></textarea>
 </div>
 
 <h2>Development</h2>
@@ -100,7 +100,7 @@
 <tr><td>modjewel:       <td> <span id="version-modjewel">???</span>
 </table>
 
-<img src="images/weinre-icon-128x128.png"> 
+<img src="images/weinre-icon-128x128.png">
 
 <script src="index.js"></script>
 
diff --git a/weinre.web/modules/weinre/client/Client.scoop b/weinre.web/modules-scoop/weinre/client/Client.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/Client.scoop
rename to weinre.web/modules-scoop/weinre/client/Client.scoop
diff --git a/weinre.web/modules/weinre/client/ConnectorList.scoop b/weinre.web/modules-scoop/weinre/client/ConnectorList.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/ConnectorList.scoop
rename to weinre.web/modules-scoop/weinre/client/ConnectorList.scoop
diff --git a/weinre.web/modules/weinre/client/DOMTemplates.scoop b/weinre.web/modules-scoop/weinre/client/DOMTemplates.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/DOMTemplates.scoop
rename to weinre.web/modules-scoop/weinre/client/DOMTemplates.scoop
diff --git a/weinre.web/modules/weinre/client/ExtensionRegistryImpl.scoop b/weinre.web/modules-scoop/weinre/client/ExtensionRegistryImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/ExtensionRegistryImpl.scoop
rename to weinre.web/modules-scoop/weinre/client/ExtensionRegistryImpl.scoop
diff --git a/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop b/weinre.web/modules-scoop/weinre/client/InspectorBackendImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
rename to weinre.web/modules-scoop/weinre/client/InspectorBackendImpl.scoop
diff --git a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop b/weinre.web/modules-scoop/weinre/client/InspectorFrontendHostImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
rename to weinre.web/modules-scoop/weinre/client/InspectorFrontendHostImpl.scoop
diff --git a/weinre.web/modules/weinre/client/RemotePanel.scoop b/weinre.web/modules-scoop/weinre/client/RemotePanel.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/RemotePanel.scoop
rename to weinre.web/modules-scoop/weinre/client/RemotePanel.scoop
diff --git a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop b/weinre.web/modules-scoop/weinre/client/WeinreClientEventsImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
rename to weinre.web/modules-scoop/weinre/client/WeinreClientEventsImpl.scoop
diff --git a/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop b/weinre.web/modules-scoop/weinre/client/WeinreExtraTargetEventsImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop
rename to weinre.web/modules-scoop/weinre/client/WeinreExtraTargetEventsImpl.scoop
diff --git a/weinre.web/modules/weinre/common/Binding.scoop b/weinre.web/modules-scoop/weinre/common/Binding.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/Binding.scoop
rename to weinre.web/modules-scoop/weinre/common/Binding.scoop
diff --git a/weinre.web/modules/weinre/common/Callback.scoop b/weinre.web/modules-scoop/weinre/common/Callback.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/Callback.scoop
rename to weinre.web/modules-scoop/weinre/common/Callback.scoop
diff --git a/weinre.web/modules/weinre/common/EventListeners.scoop b/weinre.web/modules-scoop/weinre/common/EventListeners.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/EventListeners.scoop
rename to weinre.web/modules-scoop/weinre/common/EventListeners.scoop
diff --git a/weinre.web/modules/weinre/common/Ex.scoop b/weinre.web/modules-scoop/weinre/common/Ex.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/Ex.scoop
rename to weinre.web/modules-scoop/weinre/common/Ex.scoop
diff --git a/weinre.web/modules/weinre/common/IDGenerator.scoop b/weinre.web/modules-scoop/weinre/common/IDGenerator.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/IDGenerator.scoop
rename to weinre.web/modules-scoop/weinre/common/IDGenerator.scoop
diff --git a/weinre.web/modules/weinre/common/IDLTools.scoop b/weinre.web/modules-scoop/weinre/common/IDLTools.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/IDLTools.scoop
rename to weinre.web/modules-scoop/weinre/common/IDLTools.scoop
diff --git a/weinre.web/modules/weinre/common/MessageDispatcher.scoop b/weinre.web/modules-scoop/weinre/common/MessageDispatcher.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/MessageDispatcher.scoop
rename to weinre.web/modules-scoop/weinre/common/MessageDispatcher.scoop
diff --git a/weinre.web/modules/weinre/common/Native.scoop b/weinre.web/modules-scoop/weinre/common/Native.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/Native.scoop
rename to weinre.web/modules-scoop/weinre/common/Native.scoop
diff --git a/weinre.web/modules/weinre/common/StackTrace.scoop b/weinre.web/modules-scoop/weinre/common/StackTrace.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/StackTrace.scoop
rename to weinre.web/modules-scoop/weinre/common/StackTrace.scoop
diff --git a/weinre.web/modules/weinre/common/WebSocketXhr.scoop b/weinre.web/modules-scoop/weinre/common/WebSocketXhr.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/WebSocketXhr.scoop
rename to weinre.web/modules-scoop/weinre/common/WebSocketXhr.scoop
diff --git a/weinre.web/modules/weinre/common/Weinre.scoop b/weinre.web/modules-scoop/weinre/common/Weinre.scoop
similarity index 100%
rename from weinre.web/modules/weinre/common/Weinre.scoop
rename to weinre.web/modules-scoop/weinre/common/Weinre.scoop
diff --git a/weinre.web/modules/weinre/target/CSSStore.scoop b/weinre.web/modules-scoop/weinre/target/CSSStore.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/CSSStore.scoop
rename to weinre.web/modules-scoop/weinre/target/CSSStore.scoop
diff --git a/weinre.web/modules/weinre/target/CheckForProblems.scoop b/weinre.web/modules-scoop/weinre/target/CheckForProblems.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/CheckForProblems.scoop
rename to weinre.web/modules-scoop/weinre/target/CheckForProblems.scoop
diff --git a/weinre.web/modules/weinre/target/Console.scoop b/weinre.web/modules-scoop/weinre/target/Console.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/Console.scoop
rename to weinre.web/modules-scoop/weinre/target/Console.scoop
diff --git a/weinre.web/modules/weinre/target/ElementHighlighter.scoop b/weinre.web/modules-scoop/weinre/target/ElementHighlighter.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/ElementHighlighter.scoop
rename to weinre.web/modules-scoop/weinre/target/ElementHighlighter.scoop
diff --git a/weinre.web/modules/weinre/target/InjectedScriptHostImpl.scoop b/weinre.web/modules-scoop/weinre/target/InjectedScriptHostImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/InjectedScriptHostImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/InjectedScriptHostImpl.scoop
diff --git a/weinre.web/modules/weinre/target/NodeStore.scoop b/weinre.web/modules-scoop/weinre/target/NodeStore.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/NodeStore.scoop
rename to weinre.web/modules-scoop/weinre/target/NodeStore.scoop
diff --git a/weinre.web/modules/weinre/target/SqlStepper.scoop b/weinre.web/modules-scoop/weinre/target/SqlStepper.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/SqlStepper.scoop
rename to weinre.web/modules-scoop/weinre/target/SqlStepper.scoop
diff --git a/weinre.web/modules/weinre/target/Target.scoop b/weinre.web/modules-scoop/weinre/target/Target.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/Target.scoop
rename to weinre.web/modules-scoop/weinre/target/Target.scoop
diff --git a/weinre.web/modules/weinre/target/Timeline.scoop b/weinre.web/modules-scoop/weinre/target/Timeline.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/Timeline.scoop
rename to weinre.web/modules-scoop/weinre/target/Timeline.scoop
diff --git a/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop b/weinre.web/modules-scoop/weinre/target/WeinreExtraClientCommandsImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WeinreExtraClientCommandsImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop b/weinre.web/modules-scoop/weinre/target/WeinreTargetEventsImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WeinreTargetEventsImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiCSSImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiCSSImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiCSSImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiCSSImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiConsoleImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiConsoleImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiConsoleImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiConsoleImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiDOMImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiDOMImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiDOMImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiDOMImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiDOMStorageImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiDOMStorageImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiDatabaseImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiDatabaseImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiInspectorImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiInspectorImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiInspectorImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiInspectorImpl.scoop
diff --git a/weinre.web/modules/weinre/target/WiRuntimeImpl.scoop b/weinre.web/modules-scoop/weinre/target/WiRuntimeImpl.scoop
similarity index 100%
rename from weinre.web/modules/weinre/target/WiRuntimeImpl.scoop
rename to weinre.web/modules-scoop/weinre/target/WiRuntimeImpl.scoop
diff --git a/weinre.web/modules/weinre/client/Client.coffee b/weinre.web/modules/weinre/client/Client.coffee
new file mode 100644
index 0000000..eecd963
--- /dev/null
+++ b/weinre.web/modules/weinre/client/Client.coffee
@@ -0,0 +1,140 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Native            = require('../common/Native')
+IDLTools          = require('../common/IDLTools')
+Callback          = require('../common/Callback')
+Weinre            = require('../common/Weinre')
+MessageDispatcher = require('../common/MessageDispatcher')
+Binding           = require('../common/Binding')
+IDGenerator       = require('../common/IDGenerator')
+
+InspectorBackendImpl        = require('./InspectorBackendImpl')
+InspectorFrontendHostImpl   = require('./InspectorFrontendHostImpl')
+WeinreClientEventsImpl      = require('./WeinreClientEventsImpl')
+WeinreExtraTargetEventsImpl = require('./WeinreExtraTargetEventsImpl')
+RemotePanel                 = require('./RemotePanel')
+
+AutoConnect = true
+
+Weinre.showNotImplemented()
+
+#-------------------------------------------------------------------------------
+module.exports = class Client
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    initialize: ->
+        window.addEventListener 'load', Binding(this, 'onLoaded'), false
+
+        messageDispatcher = new MessageDispatcher('../ws/client', @_getId())
+        Weinre.messageDispatcher = messageDispatcher
+
+        InspectorBackendImpl.setupProxies()
+
+        Weinre.WeinreClientCommands      = messageDispatcher.createProxy('WeinreClientCommands')
+        Weinre.WeinreExtraClientCommands = messageDispatcher.createProxy('WeinreExtraClientCommands')
+
+        messageDispatcher.registerInterface(
+            'WeinreExtraTargetEvents', new WeinreExtraTargetEventsImpl(), false
+        )
+
+        messageDispatcher.registerInterface(
+            'WebInspector', WebInspector, false
+        )
+
+        WebInspector.mainResource = {}
+        WebInspector.mainResource.url = location.href
+
+    #---------------------------------------------------------------------------
+    _getId: ->
+        hash = location.href.split('#')[1]
+        return hash if hash
+        'anonymous'
+
+    #---------------------------------------------------------------------------
+    uiAvailable: ->
+        WebInspector.panels and WebInspector.panels.remote
+
+    #---------------------------------------------------------------------------
+    autoConnect: (value) ->
+        AutoConnect = not not value if arguments.length >= 1
+        AutoConnect
+
+    #---------------------------------------------------------------------------
+    _installRemotePanel: ->
+        WebInspector.panels.remote = new RemotePanel()
+
+        panel   = WebInspector.panels.remote
+        toolbar = document.getElementById('toolbar')
+
+        WebInspector.addPanelToolbarIcon toolbar, panel, toolbar.childNodes[1]
+        WebInspector.panelOrder.unshift WebInspector.panelOrder.pop()
+        WebInspector.currentPanel = panel
+
+        toolButtonsToHide = [ 'scripts' ]
+        for toolButtonToHide in toolButtonsToHide
+            continue unless WebInspector.panels[toolButtonToHide]
+            continue unless WebInspector.panels[toolButtonToHide].toolbarItem
+
+            WebInspector.panels[toolButtonToHide].toolbarItem.style.display = 'none'
+
+        button = document.getElementById('dock-status-bar-item')
+        button.style.display = 'none' if button
+
+    #---------------------------------------------------------------------------
+    onLoaded: ->
+        Weinre.WeinreClientCommands.registerClient Binding(this, @cb_registerClient)
+
+        @_installRemotePanel()
+
+        messageDispatcher = Weinre.messageDispatcher
+        messageDispatcher.registerInterface 'WeinreClientEvents', new WeinreClientEventsImpl(this), false
+        messageDispatcher.registerInterface 'InspectorFrontendHost', InspectorFrontendHost, false
+
+    #---------------------------------------------------------------------------
+    cb_registerClient: (clientDescription) ->
+        Weinre.clientDescription = clientDescription
+
+        if @uiAvailable()
+            WebInspector.panels.remote.setCurrentClient clientDescription.channel
+            WebInspector.panels.remote.afterInitialConnection()
+
+        Weinre.messageDispatcher.getWebSocket().addEventListener 'close', Binding(this, @cb_webSocketClosed)
+
+    #---------------------------------------------------------------------------
+    cb_webSocketClosed: ->
+        Native.setTimeout (->
+            WebInspector.panels.remote.connectionClosed()
+            WebInspector.currentPanel = WebInspector.panels.remote
+        ), 1000
+
+    #---------------------------------------------------------------------------
+    @main: ->
+        Weinre.client = new Client()
+        Weinre.client.initialize()
+
+        window.installWebInspectorAPIsource = installWebInspectorAPIsource
+
+#-------------------------------------------------------------------------------
+installWebInspectorAPIsource = () ->
+      return if 'webInspector' of window
+
+      extensionAPI = window.parent.InspectorFrontendHost.getExtensionAPI()
+      extensionAPI = extensionAPI.replace('location.hostname + location.port', "location.hostname + ':' + location.port")
+
+      id = IDGenerator.next()
+
+      console.log "installing webInspector with injectedScriptId: #{id}"
+
+      extensionAPI += "(null, null, #{id})"
+      extensionAPI
+
+#-------------------------------------------------------------------------------
+require('../common/MethodNamer').setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/ConnectorList.coffee b/weinre.web/modules/weinre/client/ConnectorList.coffee
new file mode 100644
index 0000000..065787a
--- /dev/null
+++ b/weinre.web/modules/weinre/client/ConnectorList.coffee
@@ -0,0 +1,138 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+dt = require('./DOMTemplates')
+
+#-------------------------------------------------------------------------------
+module.exports = class ConnectorList
+
+    constructor: (title) ->
+        @connectors = {}
+
+        @noneItem     = dt.LI("none")
+        @ulConnectors = dt.UL(@noneItem)
+        @div          = dt.DIV(dt.H1(title), @ulConnectors)
+
+        @noneItem.addStyleClass "weinre-connector-item"
+
+    #---------------------------------------------------------------------------
+    getElement: ->
+        @div
+
+    #---------------------------------------------------------------------------
+    add: (connector) ->
+        return if @connectors[connector.channel]
+
+        @connectors[connector.channel] = connector
+
+        li = @getListItem(connector)
+        li.addStyleClass "weinre-fadeable"
+
+        @noneItem.style.display = "none"
+
+        insertionPoint = @getConnectorInsertionPoint(connector)
+        unless insertionPoint
+            @ulConnectors.appendChild li
+        else
+            @ulConnectors.insertBefore li, insertionPoint
+
+    #---------------------------------------------------------------------------
+    get: (channel) ->
+        @connectors[channel]
+
+    #---------------------------------------------------------------------------
+    getNewestConnectorChannel: (ignoring) ->
+        newest = 0
+        for connectorChannel of @connectors
+            continue if connectorChannel == ignoring
+            newest = connectorChannel if connectorChannel > newest
+
+        return null if newest == 0
+        newest
+
+    #---------------------------------------------------------------------------
+    getConnectorInsertionPoint: (connector) ->
+        i = 0
+
+        for childNode in @ulConnectors.childNodes
+            continue if null == childNode.connectorChannel
+            return childNode if childNode.connectorChannel < connector.channel
+
+        null
+
+    #---------------------------------------------------------------------------
+    remove: (channel, fast) ->
+        self = this
+
+        element = @getConnectorElement(channel)
+        return unless element
+
+        connector = @connectors[channel]
+        connector.closed = true if connector
+        delete @connectors[channel]
+
+        if fast
+            @_remove element
+        else
+            @setState element, "closed"
+            element.addStyleClass "weinre-fade"
+            window.setTimeout (->
+                self._remove element
+            ), 5000
+
+    #---------------------------------------------------------------------------
+    _remove: (element) ->
+        @ulConnectors.removeChild element
+        @noneItem.style.display = "list-item" if @getConnectors().length == 0
+
+    #---------------------------------------------------------------------------
+    removeAll: () ->
+        for connector in @getConnectors()
+            @remove connector.channel, true
+
+    #---------------------------------------------------------------------------
+    getConnectors: () ->
+        result = []
+
+        for channel of @connectors
+            continue unless @connectors.hasOwnProperty(channel)
+            result.push @connectors[channel]
+
+        result
+
+    #---------------------------------------------------------------------------
+    getConnectorElement: (channel) ->
+        connector = @connectors[channel]
+        return null unless connector
+        connector.element
+
+    #---------------------------------------------------------------------------
+    setCurrent: (channel) ->
+        for connector in @getConnectors()
+            connector.element.removeStyleClass "current"
+
+        element = @getConnectorElement(channel)
+        return if null == element
+        element.addStyleClass "current"
+
+    #---------------------------------------------------------------------------
+    setState: (channel, state) ->
+        if typeof channel == "string"
+            element = @getConnectorElement(channel)
+        else
+            element = channel
+
+        return unless element
+        element.removeStyleClass "error"
+        element.removeStyleClass "closed"
+        element.removeStyleClass "connected"
+        element.removeStyleClass "not-connected"
+        element.addStyleClass state
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/DOMTemplates.coffee b/weinre.web/modules/weinre/client/DOMTemplates.coffee
new file mode 100644
index 0000000..a3db299
--- /dev/null
+++ b/weinre.web/modules/weinre/client/DOMTemplates.coffee
@@ -0,0 +1,57 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex = require('../common/Ex')
+
+#-------------------------------------------------------------------------------
+getElementFunction = (elementName) ->
+      ->
+          element = document.createElement(elementName)
+
+          args = [].slice.call(arguments)
+          for argument in args
+              if argument.nodeType
+                  addToElement_Node element, argument
+              else if typeof argument == "string"
+                  addToElement_String element, argument
+              else if typeof argument == "object"
+                  addToElement_Object element, argument
+              else
+                  throw new Ex(arguments, "invalid value passed to DOMTemplates.#{elementName}(): " + argument)
+
+          element
+
+#-------------------------------------------------------------------------------
+addToElement_String = (element, aString) ->
+      addToElement_Node element, document.createTextNode(aString)
+
+#-------------------------------------------------------------------------------
+addToElement_Node = (element, anElement) ->
+      element.appendChild anElement
+
+#-------------------------------------------------------------------------------
+addToElement_Object = (element, anObject) ->
+      for key of anObject
+          continue unless anObject.hasOwnProperty(key)
+
+          val = anObject[key]
+          if key.substr(0, 1) == "$"
+              actualKey = key.substr(1)
+              element[actualKey] = val
+          else
+              element.setAttribute key, val
+
+#-------------------------------------------------------------------------------
+elementNames = 'H1 H2 H3 H4 H5 H6 UL OL DL LI DT DD SPAN DIV A B I TT P HR BR PRE IMG CANVAS TABLE TR TD FORM INPUT BUTTON SELECT OPTGROUP OPTION TEXTAREA'
+elementNames = elementNames.split(' ')
+
+for elementName in elementNames
+    exports[elementName] = getElementFunction(elementName)
+    exports[elementName].name        = "#{elementName}"
+    exports[elementName].displayName = "#{elementName}"
+    exports[elementName].signature   = "DOMTemplates.#{elementName}"
diff --git a/weinre.web/modules/weinre/client/ExtensionRegistryImpl.coffee b/weinre.web/modules/weinre/client/ExtensionRegistryImpl.coffee
new file mode 100644
index 0000000..4303d2d
--- /dev/null
+++ b/weinre.web/modules/weinre/client/ExtensionRegistryImpl.coffee
@@ -0,0 +1,35 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex      = require('../common/Ex')
+Binding = require('../common/Binding')
+Weinre  = require('../common/Weinre')
+
+extensions = []
+
+#-------------------------------------------------------------------------------
+module.exports = class ExtensionRegistryImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    getExtensionsAsync: ->
+        return if extensions.length
+        Weinre.WeinreClientCommands.getExtensions Binding(this, @_cb_getExtensions)
+
+    #---------------------------------------------------------------------------
+    _cb_getExtensions: (extensionsResult) ->
+        extensions = extensionsResult
+        @_installExtensions()
+
+    #---------------------------------------------------------------------------
+    _installExtensions: ->
+        WebInspector.addExtensions extensions
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/InspectorBackendImpl.coffee b/weinre.web/modules/weinre/client/InspectorBackendImpl.coffee
new file mode 100644
index 0000000..1dcf45e
--- /dev/null
+++ b/weinre.web/modules/weinre/client/InspectorBackendImpl.coffee
@@ -0,0 +1,70 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex                = require('../common/Ex')
+IDLTools          = require('../common/IDLTools')
+MessageDispatcher = require('../common/MessageDispatcher')
+Weinre            = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class InspectorBackendImpl
+
+    constructor: ->
+        @registeredDomainDispatchers = {}
+        MessageDispatcher.setInspectorBackend this
+
+    #---------------------------------------------------------------------------
+    @setupProxies: ->
+        intfNames = [
+            "ApplicationCache"
+            "BrowserDebugger"
+            "CSS"
+            "Console"
+            "DOM"
+            "DOMStorage"
+            "Database"
+            "Debugger"
+            "InjectedScript"
+            "Inspector"
+            "Network"
+            "Profiler"
+            "Runtime"
+        ]
+
+        for intfName in intfNames
+            proxy = Weinre.messageDispatcher.createProxy(intfName)
+
+            if window[intfName]
+                throw new Ex(arguments, "backend interface '#{intfName}' already created")
+
+            intf = IDLTools.getIDL(intfName)
+            unless intf
+                throw new Ex(arguments, "interface not registered: '#{intfName}'")
+
+            window[intfName] = {}
+            for method in intf.methods
+                proxyMethod = InspectorBackendImpl.getProxyMethod(proxy, method)
+                InspectorBackendImpl::[method.name] = proxyMethod
+                window[intfName][method.name] = proxyMethod
+
+    #---------------------------------------------------------------------------
+    @getProxyMethod: (proxy, method) ->
+        ->
+            proxy[method.name].apply proxy, arguments
+
+    #---------------------------------------------------------------------------
+    registerDomainDispatcher: (name, intf) ->
+        @registeredDomainDispatchers[name] = intf
+
+    #---------------------------------------------------------------------------
+    getRegisteredDomainDispatcher: (name) ->
+        return null unless @registeredDomainDispatchers.hasOwnProperty(name)
+        @registeredDomainDispatchers[name]
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee
new file mode 100644
index 0000000..0c71e63
--- /dev/null
+++ b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee
@@ -0,0 +1,87 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre   = require('../common/Weinre')
+IDLTools = require('../common/IDLTools')
+
+_extensionAPI = null
+
+#-------------------------------------------------------------------------------
+module.exports = class InspectorFrontendHostImpl
+
+    constructor: ->
+        @_getPlatformAndPort()
+
+    #---------------------------------------------------------------------------
+    loaded: ->
+
+    #---------------------------------------------------------------------------
+    localizedStringsURL: ->
+        "nls/English.lproj/localizedStrings.js"
+
+    #---------------------------------------------------------------------------
+    hiddenPanels: ->
+        "audits,profiles,network"
+
+    #---------------------------------------------------------------------------
+    platform: ->
+        "weinre"
+
+    #---------------------------------------------------------------------------
+    port: ->
+        "weinre"
+
+    #---------------------------------------------------------------------------
+    sendMessageToBackend: (message) ->
+        object = JSON.parse(message)
+        object[1] = "<long script elided>" if object[0] == "setInjectedScriptSource"
+        Weinre.logInfo arguments.callee.name + "(#{JSON.stringify(object, null, 4)})"
+
+    #---------------------------------------------------------------------------
+    setExtensionAPI: (extensionAPI) ->
+        _extensionAPI = extensionAPI
+
+    #---------------------------------------------------------------------------
+    getExtensionAPI: ->
+        _extensionAPI
+
+    #---------------------------------------------------------------------------
+    inspectedURLChanged: ->
+
+    #---------------------------------------------------------------------------
+    _getPlatformAndPort: ->
+        @_platform = "weinre"
+        @_platformFlavor = "weinre"
+        @_port = "weinre"
+        return if true
+        uas = navigator.userAgent
+        if uas.match(/mac os x/i)
+            @_platform = "mac"
+        else if uas.match(/macintosh/i)
+            @_platform = "mac"
+        else if uas.match(/linux/i)
+            @_platform = "linux"
+        else @_platform = "windows" if uas.match(/windows/i)
+        url = window.location.href
+        splits = url.split("#", 2)
+        if splits.length > 1
+            properties = splits[1]
+            properties = properties.split("&")
+            for property in properties
+                pieces = property.split("=")
+                if pieces.length > 1
+                    key = pieces[0]
+                    val = pieces[1]
+                    if key == "platform"
+                        @_platform = val
+                    else if key == "platformFlavor"
+                        @_platformFlavor = val
+                    else @_port = val if key == "port"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/RemotePanel.coffee b/weinre.web/modules/weinre/client/RemotePanel.coffee
new file mode 100644
index 0000000..a6c6110
--- /dev/null
+++ b/weinre.web/modules/weinre/client/RemotePanel.coffee
@@ -0,0 +1,211 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Binding       = require('../common/Binding')
+Weinre        = require('../common/Weinre')
+
+ConnectorList = require('./ConnectorList')
+DT            = require('./DOMTemplates')
+
+# fix WebInspector.Panel's prototype so our super call works
+WebInspector.Panel.prototype.constructor = WebInspector.Panel
+
+#-------------------------------------------------------------------------------
+module.exports = class RemotePanel extends WebInspector.Panel
+
+    RemotePanel::__defineGetter__("toolbarItemClass", -> "remote")
+    RemotePanel::__defineGetter__("toolbarItemLabel", -> "Remote")
+    RemotePanel::__defineGetter__("statusBarItems", -> [])
+    RemotePanel::__defineGetter__("defaultFocusedElement", -> @contentElement)
+
+    constructor: ->
+        super "remote"
+        @initialize()
+
+    #---------------------------------------------------------------------------
+    initialize: () ->
+        div = DT.DIV()
+        div.style.position = "absolute"
+        div.style.top = "1em"
+        div.style.right = "1em"
+        div.style.left = "1em"
+        div.style.bottom = "1em"
+        div.style.overflow = "auto"
+        icon = DT.IMG(src: "../images/weinre-icon-128x128.png")
+        icon.style.float = "right"
+        div.appendChild icon
+        @targetList = new TargetList()
+        @clientList = new ClientList()
+        div.appendChild @targetList.getElement()
+        div.appendChild @clientList.getElement()
+        @serverProperties = DT.DIV($className: "weinreServerProperties")
+        div.appendChild DT.H1("Server Properties")
+        div.appendChild @serverProperties
+        @element.appendChild div
+        @reset()
+
+    #---------------------------------------------------------------------------
+    addClient: (client) ->
+        @clientList.add client
+
+
+    addTarget: (target) ->
+        @targetList.add target
+
+
+    getTarget: (channel) ->
+        @targetList.get channel
+
+
+    removeClient: (channel) ->
+        @clientList.remove channel
+
+
+    removeTarget: (channel) ->
+        @targetList.remove channel
+
+
+    setCurrentClient: (channel) ->
+        @clientList.setCurrent channel
+
+
+    setCurrentTarget: (channel) ->
+        @targetList.setCurrent channel
+
+
+    setClientState: (channel, state) ->
+        @clientList.setState channel, state
+
+
+    setTargetState: (channel, state) ->
+        @targetList.setState channel, state
+
+    #---------------------------------------------------------------------------
+    getNewestTargetChannel: (ignoring) ->
+        @targetList.getNewestConnectorChannel ignoring
+
+    #---------------------------------------------------------------------------
+    afterInitialConnection: ->
+        @clientList.afterInitialConnection()
+
+    #---------------------------------------------------------------------------
+    reset: ->
+        @clientList.removeAll()
+        @targetList.removeAll()
+        Weinre.WeinreClientCommands.getTargets Binding(this, "cb_getTargets")
+        Weinre.WeinreClientCommands.getClients Binding(this, "cb_getClients")
+
+    #---------------------------------------------------------------------------
+    connectionClosed: ->
+        @clientList.removeAll()
+        @targetList.removeAll()
+
+    #---------------------------------------------------------------------------
+    cb_getTargets: (targets) ->
+        for target in targets
+            @addTarget target
+
+        return unless Weinre.client.autoConnect()
+        newestTargetChannel = @getNewestTargetChannel()
+        return unless newestTargetChannel
+        return unless Weinre.messageDispatcher
+        Weinre.WeinreClientCommands.connectTarget Weinre.messageDispatcher.channel, newestTargetChannel
+
+    #---------------------------------------------------------------------------
+    cb_getClients: (clients) ->
+        for client in clients
+            @addClient client
+
+    #---------------------------------------------------------------------------
+    show: ->
+        super()
+
+    #---------------------------------------------------------------------------
+    hide: () ->
+        super()
+
+    #---------------------------------------------------------------------------
+    setServerProperties: (properties) ->
+        table = "<table>"
+        keys = []
+        for key of properties
+            keys.push key
+        keys = keys.sort()
+        for key in keys
+            val = properties[key]
+            if typeof val == "string"
+                val = val.escapeHTML()
+            else
+                finalVal = ""
+                for aVal in val
+                    finalVal += "<li>" + aVal.escapeHTML()
+
+                val = "<ul>#{finalVal}</ul>"
+            table += "<tr class='weinre-normal-text-size'><td valign='top'>#{key.escapeHTML()}: <td>" + val
+
+        table += "</table>"
+        @serverProperties.innerHTML = table
+
+#-------------------------------------------------------------------------------
+class TargetList extends ConnectorList
+
+    constructor: ->
+        super "Targets"
+
+    #---------------------------------------------------------------------------
+    getListItem: (target) ->
+        self = this
+        text = target.hostName + " [channel: #{target.channel} id: #{target.id}]" + " - " + target.url
+        item = DT.LI($connectorChannel: target.channel, text)
+        item.addStyleClass "weinre-connector-item"
+        item.addStyleClass "target"
+        item.addEventListener "click", ((e) ->
+            self.connectToTarget target.channel, e
+        ), false
+        target.element = item
+        item
+
+    #---------------------------------------------------------------------------
+    connectToTarget: (targetChannel, event) ->
+        if event
+            event.preventDefault()
+            event.stopPropagation()
+        target = @connectors[targetChannel]
+        return false unless target
+        return false if target.closed
+        Weinre.WeinreClientCommands.connectTarget Weinre.messageDispatcher.channel, targetChannel
+        false
+
+#-------------------------------------------------------------------------------
+class ClientList extends ConnectorList
+
+    constructor: ->
+        super "Clients"
+        @noneItem.innerHTML = "Waiting for connection..."
+
+    #---------------------------------------------------------------------------
+    afterInitialConnection: () ->
+        @noneItem.innerHTML = "Connection lost, reload this page to reconnect."
+        @noneItem.addStyleClass "error"
+
+    #---------------------------------------------------------------------------
+    getListItem: (client) ->
+        text = client.hostName + " [channel: #{client.channel} id: #{client.id}]"
+        item = DT.LI($connectorChannel: client.channel, text)
+        item.addStyleClass "weinre-connector-item"
+        item.addStyleClass "client"
+
+        if Weinre.messageDispatcher
+            if client.channel == Weinre.messageDispatcher.channel
+                item.addStyleClass "current"
+
+        client.element = item
+        item
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.coffee b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.coffee
new file mode 100644
index 0000000..a3e4038
--- /dev/null
+++ b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.coffee
@@ -0,0 +1,91 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Callback                    = require('../common/Callback')
+Weinre                      = require('../common/Weinre')
+
+WeinreExtraTargetEventsImpl = require('./WeinreExtraTargetEventsImpl')
+
+titleNotConnected    = "weinre: target not connected"
+titleConnectedPrefix = "weinre: "
+
+document.title = titleNotConnected
+
+#-------------------------------------------------------------------------------
+module.exports = class WeinreClientEventsImpl
+
+    constructor: (client) ->
+        @client = client
+
+    #---------------------------------------------------------------------------
+    clientRegistered: (clientDescription) ->
+        WebInspector.panels.remote.addClient clientDescription if @client.uiAvailable()
+
+    #---------------------------------------------------------------------------
+    targetRegistered: (targetDescription) ->
+        WebInspector.panels.remote.addTarget targetDescription if @client.uiAvailable()
+        return unless Weinre.client.autoConnect()
+        return unless Weinre.messageDispatcher
+
+        Weinre.WeinreClientCommands.connectTarget Weinre.messageDispatcher.channel, targetDescription.channel
+
+    #---------------------------------------------------------------------------
+    clientUnregistered: (clientChannel) ->
+        WebInspector.panels.remote.removeClient clientChannel if @client.uiAvailable()
+
+    #---------------------------------------------------------------------------
+    targetUnregistered: (targetChannel) ->
+        WebInspector.panels.remote.removeTarget targetChannel if @client.uiAvailable()
+
+    #---------------------------------------------------------------------------
+    connectionCreated: (clientChannel, targetChannel) ->
+        if @client.uiAvailable()
+            WebInspector.panels.remote.setClientState clientChannel, "connected"
+            WebInspector.panels.remote.setTargetState targetChannel, "connected"
+
+        return unless clientChannel == Weinre.messageDispatcher.channel
+
+        WebInspector.panels.elements.reset()
+        WebInspector.panels.timeline._clearPanel()
+        WebInspector.panels.resources.reset()
+
+        target = WebInspector.panels.remote.getTarget(targetChannel)
+        document.title = titleConnectedPrefix + target.url
+        WebInspector.inspectedURLChanged target.url
+
+        Weinre.WeinreExtraClientCommands.getDatabases (databaseRecords) ->
+            WeinreExtraTargetEventsImpl.addDatabaseRecords databaseRecords
+
+    #---------------------------------------------------------------------------
+    connectionDestroyed: (clientChannel, targetChannel) ->
+        if @client.uiAvailable()
+            WebInspector.panels.remote.setClientState clientChannel, "not-connected"
+            WebInspector.panels.remote.setTargetState targetChannel, "not-connected"
+
+        return unless clientChannel == Weinre.messageDispatcher.channel
+
+        document.title = titleNotConnected
+        return unless Weinre.client.autoConnect()
+        return unless @client.uiAvailable()
+
+        nextTargetChannel = WebInspector.panels.remote.getNewestTargetChannel(targetChannel)
+        return unless nextTargetChannel
+
+        Weinre.WeinreClientCommands.connectTarget Weinre.messageDispatcher.channel, nextTargetChannel
+        Weinre.logInfo "autoconnecting to " + nextTargetChannel
+
+    #---------------------------------------------------------------------------
+    sendCallback: (callbackId, result) ->
+        Callback.invoke callbackId, result
+
+    #---------------------------------------------------------------------------
+    serverProperties: (properties) ->
+        WebInspector.panels.remote.setServerProperties properties if @client.uiAvailable()
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.coffee b/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.coffee
new file mode 100644
index 0000000..66c88d3
--- /dev/null
+++ b/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.coffee
@@ -0,0 +1,35 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+module.exports = class WeinreExtraTargetEventsImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    databaseOpened: (databaseRecord) ->
+        WeinreExtraTargetEventsImpl.addDatabaseRecords [ databaseRecord ]
+
+    #---------------------------------------------------------------------------
+    @addDatabaseRecords: (databaseRecords) ->
+        return unless WebInspector.panels
+        return unless WebInspector.panels.resources
+        return unless WebInspector.panels.resources._databases
+
+        existingDbs = WebInspector.panels.resources._databases
+        existingDbNames = {}
+
+        for existingDb in existingDbs
+            existingDbNames[existingDb.name] = existingDb
+
+        for databaseRecord in databaseRecords
+            continue if existingDbNames[databaseRecord.name]
+            database = new WebInspector.Database(databaseRecord.id, databaseRecord.domain, databaseRecord.name, databaseRecord.version)
+            WebInspector.panels.resources.addDatabase database
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/Binding.coffee b/weinre.web/modules/weinre/common/Binding.coffee
new file mode 100644
index 0000000..ad7ab14
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Binding.coffee
@@ -0,0 +1,26 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex = require('./Ex')
+
+#-------------------------------------------------------------------------------
+module.exports = class Binding
+
+    constructor: (receiver, method) ->
+        if not receiver
+            throw new Ex(arguments, "receiver argument for Binding constructor was null")
+
+        method = receiver[method] if typeof (method) == "string"
+
+        if typeof (method) is not "function"
+            throw new Ex(arguments, "method argument didn't specify a function")
+
+        return -> method.apply(receiver, [].slice.call(arguments))
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/Callback.coffee b/weinre.web/modules/weinre/common/Callback.coffee
new file mode 100644
index 0000000..ee2baf2
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Callback.coffee
@@ -0,0 +1,72 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex = require('./Ex')
+
+CallbackTable    = {}
+CallbackIndex    = 1
+ConnectorChannel = "???"
+
+#-------------------------------------------------------------------------------
+module.exports = class Callback
+
+    constructor: ->
+        throw new Ex(arguments, "this class is not intended to be instantiated")
+
+    #---------------------------------------------------------------------------
+    @setConnectorChannel: (connectorChannel) ->
+        ConnectorChannel = "" + connectorChannel
+
+    #---------------------------------------------------------------------------
+    @register: (callback) ->
+        callback = [ null, callback ] if typeof callback == "function"
+        unless typeof callback.slice == "function"
+            throw new Ex(arguments, "callback must be an array or function")
+
+        receiver = callback[0]
+        func = callback[1]
+        data = callback.slice(2)
+        func = receiver[func] if typeof func == "string"
+
+        unless typeof func == "function"
+            throw new Ex(arguments, "callback function was null or not found")
+
+        index = ConnectorChannel + "::" + CallbackIndex
+
+        CallbackIndex++
+        CallbackIndex = 1 if CallbackIndex >= 65536 * 65536
+        CallbackTable[index] = [ receiver, func, data ]
+
+        index
+
+    #---------------------------------------------------------------------------
+    @deregister: (index) ->
+        delete CallbackTable[index]
+
+    #---------------------------------------------------------------------------
+    @invoke: (index, args) ->
+        callback = CallbackTable[index]
+
+        unless callback
+            throw new Ex(arguments, "callback #{index} not registered or already invoked")
+
+        receiver = callback[0]
+        func = callback[1]
+        args = callback[2].concat(args)
+
+        try
+            func.apply receiver, args
+        catch e
+            funcName = func.name || func.signature
+            funcName = "<unnamed>" unless funcName
+            require("./Weinre").logError arguments.callee.signature + " exception invoking callback: #{funcName}(#{args.join(',')}): " + e
+        finally
+            Callback.deregister index
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/EventListeners.coffee b/weinre.web/modules/weinre/common/EventListeners.coffee
new file mode 100644
index 0000000..535b48f
--- /dev/null
+++ b/weinre.web/modules/weinre/common/EventListeners.coffee
@@ -0,0 +1,56 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex     = require('./Ex')
+Weinre = require('./Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class EventListeners
+
+    constructor: ->
+        @listeners = []
+
+    #---------------------------------------------------------------------------
+    add: (listener, useCapture) ->
+        @listeners.push [ listener, useCapture ]
+
+    #---------------------------------------------------------------------------
+    remove: (listener, useCapture) ->
+        listeners = @listeners.slice()
+
+        for _listener in listeners
+            continue unless _listener[0] == listener
+            continue unless _listener[1] == useCapture
+
+            @_listeners.splice i, 1
+            return
+
+    #---------------------------------------------------------------------------
+    fire: (event) ->
+        listeners = @listeners.slice()
+        for listener in listeners
+            listener = listener[0]
+
+            if typeof listener is "function"
+                try
+                    listener.call null, event
+                catch e
+                    Weinre.logError "#{arguments.callee.name} invocation exception: #{e}"
+                continue
+
+            if typeof listener?.handleEvent isnt "function"
+                throw new Ex(arguments, "listener does not implement the handleEvent() method")
+
+            try
+                listener.handleEvent.call listener, event
+            catch e
+                Weinre.logError "#{arguments.callee.name} invocation exception: #{e}"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
+
diff --git a/weinre.web/modules/weinre/common/Ex.coffee b/weinre.web/modules/weinre/common/Ex.coffee
new file mode 100644
index 0000000..e5c1591
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Ex.coffee
@@ -0,0 +1,31 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+StackTrace = require('./StackTrace')
+
+#-------------------------------------------------------------------------------
+module.exports = class Ex
+
+    constructor: (args, message) ->
+        if not args or not args.callee
+            throw Ex(arguments, "first parameter must be an Arguments object")
+
+        StackTrace.dump args
+        message = "threw error: " + message if message instanceof Error
+        new Error(prefix(args, message))
+
+#-------------------------------------------------------------------------------
+prefix = (args, string) ->
+      return args.callee.signature   + ": " + string if args.callee.signature
+      return args.callee.displayName + ": " + string if args.callee.displayName
+      return args.callee.name        + ": " + string if args.callee.name
+
+      "<anonymous>" + ": " + string
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/IDGenerator.coffee b/weinre.web/modules/weinre/common/IDGenerator.coffee
new file mode 100644
index 0000000..0a4f599
--- /dev/null
+++ b/weinre.web/modules/weinre/common/IDGenerator.coffee
@@ -0,0 +1,41 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+nextIdValue = 1
+idName      = "__weinre__id"
+
+module.exports = class IDGenerator
+
+    #---------------------------------------------------------------------------
+    @checkId: (object) ->
+        object[idName]
+
+    #---------------------------------------------------------------------------
+    @getId: (object, map) ->
+        id = IDGenerator.checkId(object)
+
+        unless id
+            id = nextId()
+            object[idName] = id
+
+        map[id] = object if map
+
+        id
+
+    #---------------------------------------------------------------------------
+    @next: ->
+        nextId()
+
+#-------------------------------------------------------------------------------
+nextId = () ->
+      result = nextIdValue
+      nextIdValue += 1
+      result
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/IDLTools.coffee b/weinre.web/modules/weinre/common/IDLTools.coffee
new file mode 100644
index 0000000..a989836
--- /dev/null
+++ b/weinre.web/modules/weinre/common/IDLTools.coffee
@@ -0,0 +1,112 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex       = require('./Ex')
+Callback = require('./Callback')
+
+IDLs = {}
+
+#-------------------------------------------------------------------------------
+module.exports = class IDLTools
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        throw new Ex(arguments, "this class is not intended to be instantiated")
+
+    #---------------------------------------------------------------------------
+    @addIDLs: (idls) ->
+        for idl in idls
+            for intf in idl.interfaces
+                IDLs[intf.name] = intf
+                intf.module = idl.name
+
+    #---------------------------------------------------------------------------
+    @getIDL: (name) ->
+        IDLs[name]
+
+    #---------------------------------------------------------------------------
+    @getIDLsMatching: (regex) ->
+        results = []
+
+        for intfName of IDLs
+            intf = IDLs[intfName]
+            results.push intf if intfName.match(regex)
+
+        results
+
+    #---------------------------------------------------------------------------
+    @validateAgainstIDL: (klass, interfaceName) ->
+        intf          = IDLTools.getIDL(interfaceName)
+        messagePrefix = "IDL validation for #{interfaceName}: "
+        if null == intf
+            throw new Ex(arguments, messagePrefix + "idl not found: '#{interfaceName}'")
+
+        errors = []
+        for intfMethod in intf.methods
+            classMethod = klass::[intfMethod.name]
+            printName = klass.name + "::" + intfMethod.name
+
+            if null == classMethod
+                errors.push messagePrefix + "method not implemented: '#{printName}'"
+                continue
+
+            unless classMethod.length == intfMethod.parameters.length
+                unless classMethod.length == intfMethod.parameters.length + 1
+                    errors.push messagePrefix + "wrong number of parameters: '#{printName}'"
+                    continue
+
+        for propertyName of klass::
+            continue if klass::hasOwnProperty(propertyName)
+            continue if propertyName.match(/^_.*/)
+            printName = klass.name + "::" + propertyName
+
+            unless intf.methods[propertyName]
+                errors.push messagePrefix + "method should not be implemented: '#{printName}'"
+                continue
+
+        return unless errors.length
+
+        for error in errors
+            require("./Weinre").logError error
+
+    #---------------------------------------------------------------------------
+    @buildProxyForIDL: (proxyObject, interfaceName) ->
+        intf = IDLTools.getIDL(interfaceName)
+        messagePrefix = "building proxy for IDL #{interfaceName}: "
+
+        if null == intf
+            throw new Ex(arguments, messagePrefix + "idl not found: '#{interfaceName}'")
+
+        for intfMethod in intf.methods
+            proxyObject[intfMethod.name] = getProxyMethod(intf, intfMethod)
+
+#-------------------------------------------------------------------------------
+getProxyMethod = (intf, method) ->
+      result = proxyMethod = ->
+
+          callbackId = null
+          args = [].slice.call(arguments)
+
+          if args.length > 0
+              if typeof args[args.length - 1] == "function"
+                  callbackId = Callback.register(args[args.length - 1])
+                  args = args.slice(0, args.length - 1)
+
+          while args.length < method.parameters.length
+              args.push null
+
+          args.push callbackId
+          @__invoke intf.name, method.name, args
+
+      result.displayName = intf.name + "__" + method.name
+
+      result
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
+
diff --git a/weinre.web/modules/weinre/common/MessageDispatcher.coffee b/weinre.web/modules/weinre/common/MessageDispatcher.coffee
new file mode 100644
index 0000000..5f1a4be
--- /dev/null
+++ b/weinre.web/modules/weinre/common/MessageDispatcher.coffee
@@ -0,0 +1,179 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre       = require('./Weinre')
+WebSocketXhr = require('./WebSocketXhr')
+IDLTools     = require('./IDLTools')
+Binding      = require('./Binding')
+Ex           = require('./Ex')
+Callback     = require('./Callback')
+
+Verbose = false
+InspectorBackend = null
+
+#-------------------------------------------------------------------------------
+module.exports = class MessageDispatcher
+
+    #---------------------------------------------------------------------------
+    constructor: (url, id) ->
+        id = "anonymous" unless id
+        @_url = url
+        @_id = id
+        @error = null
+        @_opening = false
+        @_opened = false
+        @_closed = false
+        @_interfaces = {}
+        @_open()
+
+    #---------------------------------------------------------------------------
+    @setInspectorBackend: (inspectorBackend) ->
+        InspectorBackend = inspectorBackend
+
+    #---------------------------------------------------------------------------
+    @verbose: (value) ->
+        Verbose = not not value if arguments.length >= 1
+        Verbose
+
+    #---------------------------------------------------------------------------
+    _open: ->
+        return if @_opened or @_opening
+        throw new Ex(arguments, "socket has already been closed") if @_closed
+        @_opening = true
+        @_socket = new WebSocketXhr(@_url, @_id)
+        @_socket.addEventListener "open", Binding(this, "_handleOpen")
+        @_socket.addEventListener "error", Binding(this, "_handleError")
+        @_socket.addEventListener "message", Binding(this, "_handleMessage")
+        @_socket.addEventListener "close", Binding(this, "_handleClose")
+
+    #---------------------------------------------------------------------------
+    close: ->
+        return if @_closed
+        @_opened = false
+        @_closed = true
+        @_socket.close()
+
+    #---------------------------------------------------------------------------
+    send: (data) ->
+        @_socket.send data
+
+    #---------------------------------------------------------------------------
+    getWebSocket: ->
+        @_socket
+
+    #---------------------------------------------------------------------------
+    registerInterface: (intfName, intf, validate) ->
+        if validate
+            IDLTools.validateAgainstIDL intf.constructor, intfName
+
+        if @_interfaces[intfName]
+            throw new Ex(arguments, "interface #{intfName} has already been registered")
+
+        @_interfaces[intfName] = intf
+
+    #---------------------------------------------------------------------------
+    createProxy: (intfName) ->
+        proxy = {}
+        IDLTools.buildProxyForIDL proxy, intfName
+        self = this
+
+        proxy.__invoke = __invoke = (intfName, methodName, args) ->
+            self._sendMethodInvocation intfName, methodName, args
+
+        proxy
+
+    #---------------------------------------------------------------------------
+    _sendMethodInvocation: (intfName, methodName, args) ->
+        unless typeof intfName == "string"
+            throw new Ex(arguments, "expecting intf parameter to be a string")
+
+        unless typeof methodName == "string"
+            throw new Ex(arguments, "expecting method parameter to be a string")
+
+        data =
+            interface: intfName
+            method:    methodName
+            args:      args
+
+        data = JSON.stringify(data)
+        @_socket.send data
+
+        if Verbose
+            Weinre.logDebug @constructor.name + "[#{@_url}]: send #{intfName}.#{methodName}(#{JSON.stringify(args)})"
+
+    #---------------------------------------------------------------------------
+    getState: ->
+        return "opening" if @_opening
+        return "opened"  if @_opened
+        return "closed"  if @_closed
+        "unknown"
+
+    #---------------------------------------------------------------------------
+    isOpen: ->
+        @_opened == true
+
+    #---------------------------------------------------------------------------
+    _handleOpen: (event) ->
+        @_opening = false
+        @_opened = true
+        @channel = event.channel
+        Callback.setConnectorChannel @channel
+
+        if Verbose
+            Weinre.logDebug @constructor.name + "[#{@_url}]: opened"
+
+    #---------------------------------------------------------------------------
+    _handleError: (message) ->
+        @error = message
+        @close()
+
+        if Verbose
+            Weinre.logDebug @constructor.name + "[#{@_url}]: error: " + message
+
+    #---------------------------------------------------------------------------
+    _handleMessage: (message) ->
+        try
+            data = JSON.parse(message.data)
+        catch e
+            throw new Ex(arguments, "invalid JSON data received: #{e}: '#{message.data}'")
+
+        intfName = data["interface"]
+        methodName = data.method
+        args = data.args
+        methodSignature = intfName + ".#{methodName}()"
+        intf = @_interfaces.hasOwnProperty(intfName) and @_interfaces[intfName]
+
+        if not intf and InspectorBackend and intfName.match(/.*Notify/)
+            intf = InspectorBackend.getRegisteredDomainDispatcher(intfName.substr(0, intfName.length - 6))
+
+        unless intf
+            Weinre.logWarning "weinre: request for non-registered interface:" + methodSignature
+            return
+
+        methodSignature = intf.constructor.name + ".#{methodName}()"
+        method = intf[methodName]
+
+        unless typeof method == "function"
+            Weinre.notImplemented methodSignature
+            return
+        try
+            method.apply intf, args
+        catch e
+            Weinre.logError "weinre: invocation exception on #{methodSignature}: " + e
+
+        if Verbose
+            Weinre.logDebug @constructor.name + "[#{@_url}]: recv #{intfName}.#{methodName}(#{JSON.stringify(args)})"
+
+    #---------------------------------------------------------------------------
+    _handleClose: ->
+        @_reallyClosed = true
+        if Verbose
+            Weinre.logDebug @constructor.name + "[#{@_url}]: closed"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/MethodNamer.coffee b/weinre.web/modules/weinre/common/MethodNamer.coffee
new file mode 100644
index 0000000..4d5c755
--- /dev/null
+++ b/weinre.web/modules/weinre/common/MethodNamer.coffee
@@ -0,0 +1,25 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+module.exports = class MethodNamer
+
+    @setNamesForClass: (aClass) ->
+        for own key, val of aClass
+            if typeof(val) is "function"
+                val.signature   = "#{aClass.name}::#{key}"
+                val.displayName = key
+                val.name        = key
+
+        for own key, val of aClass.prototype
+            if typeof(val) is "function"
+                val.signature   = "#{aClass.name}.#{key}"
+                val.displayName = key
+                val.name        = key
+
+#-------------------------------------------------------------------------------
+MethodNamer.setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/Native.coffee b/weinre.web/modules/weinre/common/Native.coffee
new file mode 100644
index 0000000..04ea3c3
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Native.coffee
@@ -0,0 +1,43 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+exports.original = {}
+
+exports.original.clearInterval             = window.clearInterval
+exports.original.clearTimeout              = window.clearTimeout
+exports.original.setTimeout                = window.setTimeout
+exports.original.setInterval               = window.setInterval
+exports.original.XMLHttpRequest            = window.XMLHttpRequest
+exports.original.XMLHttpRequest_open       = window.XMLHttpRequest.prototype.open
+exports.original.LocalStorage_setItem      = window.localStorage?.setItem
+exports.original.LocalStorage_removeItem   = window.localStorage?.removeItem
+exports.original.LocalStorage_clear        = window.localStorage?.clear
+exports.original.SessionStorage_setItem    = window.sessionStorage?.setItem
+exports.original.SessionStorage_removeItem = window.sessionStorage?.removeItem
+exports.original.SessionStorage_clear      = window.sessionStorage?.clear
+exports.original.openDatabase              = window.openDatabase
+
+exports.clearInterval             = -> exports.original.clearInterval.apply( window, [].slice.call(arguments))
+exports.clearTimeout              = -> exports.original.clearTimeout.apply(  window, [].slice.call(arguments))
+exports.setInterval               = -> exports.original.setInterval.apply(   window, [].slice.call(arguments))
+exports.setTimeout                = -> exports.original.setTimeout.apply(    window, [].slice.call(arguments))
+exports.XMLHttpRequest            = -> new exports.original.XMLHttpRequest()
+exports.XMLHttpRequest_open       = -> exports.original.XMLHttpRequest_open.apply(this, [].slice.call(arguments))
+exports.LocalStorage_setItem      = -> exports.original.LocalStorage_setItem.apply(      window.localStorage,   [].slice.call(arguments))
+exports.LocalStorage_removeItem   = -> exports.original.LocalStorage_removeItem.apply(   window.localStorage,   [].slice.call(arguments))
+exports.LocalStorage_clear        = -> exports.original.LocalStorage_clear.apply(        window.localStorage,   [].slice.call(arguments))
+exports.SessionStorage_setItem    = -> exports.original.SessionStorage_setItem.apply(    window.sessionStorage, [].slice.call(arguments))
+exports.SessionStorage_removeItem = -> exports.original.SessionStorage_removeItem.apply( window.sessionStorage, [].slice.call(arguments))
+exports.SessionStorage_clear      = -> exports.original.SessionStorage_clear.apply(      window.sessionStorage, [].slice.call(arguments))
+exports.openDatabase              = -> exports.original.openDatabase.apply(              window,                [].slice.call(arguments))
+
+for own key, val of exports
+    if typeof(val) is "function"
+        val.signature   = "Native::#{key}"
+        val.displayName = key
+        val.name        = key
diff --git a/weinre.web/modules/weinre/common/StackTrace.coffee b/weinre.web/modules/weinre/common/StackTrace.coffee
new file mode 100644
index 0000000..b7ace3f
--- /dev/null
+++ b/weinre.web/modules/weinre/common/StackTrace.coffee
@@ -0,0 +1,55 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+module.exports = class StackTrace
+
+    constructor: (args) ->
+        if not args or not args.callee
+            throw Error("first parameter to #{arguments.callee.signature} must be an Arguments object")
+
+        @trace = getTrace(args)
+
+    #---------------------------------------------------------------------------
+    @dump: (args) ->
+        args = args or arguments
+        stackTrace = new StackTrace(args)
+        stackTrace.dump()
+
+    #---------------------------------------------------------------------------
+    dump: ->
+        console.log "StackTrace:"
+        for frame in @trace
+            console.log "    " + frame
+
+#-------------------------------------------------------------------------------
+getTrace = (args) ->
+      result = []
+      visitedFuncs = []
+      func = args.callee
+
+      while func
+          if func.signature
+              result.push func.signature
+          else if func.displayName
+              result.push func.displayName
+          else if func.name
+              result.push func.name
+          else
+              result.push "<anonymous>"
+
+          unless -1 == visitedFuncs.indexOf(func)
+              result.push "... recursion"
+              return result
+
+          visitedFuncs.push func
+          func = func.caller
+
+      result
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/WebSocketXhr.coffee b/weinre.web/modules/weinre/common/WebSocketXhr.coffee
new file mode 100644
index 0000000..acc1f90
--- /dev/null
+++ b/weinre.web/modules/weinre/common/WebSocketXhr.coffee
@@ -0,0 +1,202 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex             = require('./Ex')
+Weinre         = require('./Weinre')
+EventListeners = require('./EventListeners')
+Native         = require('./Native')
+
+XMLHttpRequest = Native.XMLHttpRequest
+
+#-------------------------------------------------------------------------------
+module.exports = class WebSocketXhr
+
+    @CONNECTING = 0
+    @OPEN       = 1
+    @CLOSING    = 2
+    @CLOSED     = 3
+
+    #---------------------------------------------------------------------------
+    constructor: (url, id) ->
+        @initialize url, id
+
+    #---------------------------------------------------------------------------
+    initialize: (url, id) ->
+        id = "anonymous" unless id
+        @readyState = WebSocketXhr.CONNECTING
+        @_url = url
+        @_id = id
+        @_urlChannel = null
+        @_queuedSends = []
+        @_sendInProgress = true
+
+        @_listeners =
+            open:    new EventListeners()
+            message: new EventListeners()
+            error:   new EventListeners()
+            close:   new EventListeners()
+
+        @_getChannel()
+
+    #---------------------------------------------------------------------------
+    _getChannel: ->
+        body = JSON.stringify(id: @_id)
+        @_xhr @_url, "POST", body, @_handleXhrResponseGetChannel
+
+    #---------------------------------------------------------------------------
+    _handleXhrResponseGetChannel: (xhr) ->
+        return @_handleXhrResponseError(xhr) unless xhr.status == 200
+
+        try
+            object = JSON.parse(xhr.responseText)
+        catch e
+            @_fireEventListeners "error", message: "non-JSON response from channel open request"
+            @close()
+            return
+
+        unless object.channel
+            @_fireEventListeners "error", message: "channel open request did not include a channel"
+            @close()
+            return
+
+        @_urlChannel = @_url + "/" + object.channel
+        @readyState = WebSocketXhr.OPEN
+
+        @_fireEventListeners "open",
+            message: "open"
+            channel: object.channel
+
+        @_sendInProgress = false
+        @_sendQueued()
+        @_readLoop()
+
+    #---------------------------------------------------------------------------
+    _readLoop: ->
+        return if @readyState == WebSocketXhr.CLOSED
+        return if @readyState == WebSocketXhr.CLOSING
+
+        @_xhr @_urlChannel, "GET", "", @_handleXhrResponseGet
+
+    #---------------------------------------------------------------------------
+    _handleXhrResponseGet: (xhr) ->
+        self = this
+        return @_handleXhrResponseError(xhr) unless xhr.status == 200
+
+        try
+            datum = JSON.parse(xhr.responseText)
+        catch e
+            @readyState = WebSocketXhr.CLOSED
+            @_fireEventListeners "error", message: "non-JSON response from read request"
+            return
+
+        Native.setTimeout (->
+            self._readLoop()
+        ), 0
+
+        for data in datum
+            self._fireEventListeners "message", data: data
+
+    #---------------------------------------------------------------------------
+    send: (data) ->
+        unless typeof data == "string"
+            throw new Ex(arguments, @constructor.name + "." + @caller)
+
+        @_queuedSends.push data
+        return if @_sendInProgress
+        @_sendQueued()
+
+    #---------------------------------------------------------------------------
+    _sendQueued: ->
+        return if @_queuedSends.length == 0
+        return if @readyState == WebSocketXhr.CLOSED
+        return if @readyState == WebSocketXhr.CLOSING
+
+        datum = JSON.stringify(@_queuedSends)
+        @_queuedSends = []
+        @_sendInProgress = true
+        @_xhr @_urlChannel, "POST", datum, @_handleXhrResponseSend
+
+    #---------------------------------------------------------------------------
+    _handleXhrResponseSend: (xhr) ->
+        httpSocket = this
+        return @_handleXhrResponseError(xhr) unless xhr.status == 200
+
+        @_sendInProgress = false
+
+        Native.setTimeout (->
+            httpSocket._sendQueued()
+        ), 0
+
+    #---------------------------------------------------------------------------
+    close: ->
+        @_sendInProgress = true
+        @readyState = WebSocketXhr.CLOSING
+
+        @_fireEventListeners "close",
+            message: "closing"
+            wasClean: true
+
+        @readyState = WebSocketXhr.CLOSED
+
+    #---------------------------------------------------------------------------
+    addEventListener: (type, listener, useCapture) ->
+        @_getListeners(type).add listener, useCapture
+
+    #---------------------------------------------------------------------------
+    removeEventListener: (type, listener, useCapture) ->
+        @_getListeners(type).remove listener, useCapture
+
+    #---------------------------------------------------------------------------
+    _fireEventListeners: (type, event) ->
+        return if @readyState == WebSocketXhr.CLOSED
+
+        event.target = this
+        @_getListeners(type).fire event
+
+    #---------------------------------------------------------------------------
+    _getListeners: (type) ->
+        listeners = @_listeners[type]
+        if null == listeners
+            throw new Ex(arguments, "invalid event listener type: '#{type}'")
+        listeners
+
+    #---------------------------------------------------------------------------
+    _handleXhrResponseError: (xhr) ->
+        if xhr.status == 404
+            @close()
+            return
+
+        @_fireEventListeners "error",
+            target: this
+            status: xhr.status
+            message: "error from XHR invocation: " + xhr.statusText
+
+        Weinre.logError "error from XHR invocation: #{xhr.status}: " + xhr.statusText
+
+    #---------------------------------------------------------------------------
+    _xhr: (url, method, data, handler) ->
+        if null == handler
+            throw new Ex(arguments, "handler must not be null")
+
+        xhr = new XMLHttpRequest()
+        xhr.httpSocket = this
+        xhr.httpSocketHandler = handler
+        xhr.onreadystatechange = _xhrEventHandler
+        xhr.open method, url, true
+        xhr.setRequestHeader "Content-Type", "text/plain"
+        xhr.send data
+
+#-------------------------------------------------------------------------------
+_xhrEventHandler = (event) ->
+      xhr = event.target
+      return unless xhr.readyState == 4
+
+      xhr.httpSocketHandler.call xhr.httpSocket, xhr
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/common/Weinre.coffee b/weinre.web/modules/weinre/common/Weinre.coffee
new file mode 100644
index 0000000..52bc180
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Weinre.coffee
@@ -0,0 +1,97 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex         = require('./Ex')
+IDLTools   = require('./IDLTools')
+StackTrace = require('./StackTrace')
+
+_notImplemented     = {}
+_showNotImplemented = false
+CSSProperties       = []
+logger              = null
+
+#-------------------------------------------------------------------------------
+module.exports = class Weinre
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        throw new Ex(arguments, "this class is not intended to be instantiated")
+
+    #---------------------------------------------------------------------------
+    @addIDLs: (idls) ->
+        IDLTools.addIDLs idls
+
+    #---------------------------------------------------------------------------
+    @addCSSProperties: (cssProperties) ->
+        CSSProperties = cssProperties
+
+    #---------------------------------------------------------------------------
+    @getCSSProperties: ->
+        CSSProperties
+
+    #---------------------------------------------------------------------------
+    @deprecated: () ->
+        StackTrace.dump arguments
+
+    #---------------------------------------------------------------------------
+    @notImplemented: (thing) ->
+        return if _notImplemented[thing]
+
+        _notImplemented[thing] = true
+        return unless _showNotImplemented
+
+        Weinre.logWarning thing + " not implemented"
+
+    #---------------------------------------------------------------------------
+    @showNotImplemented: () ->
+        _showNotImplemented = true
+
+        for key of _notImplemented
+            Weinre.logWarning key + " not implemented"
+
+    #---------------------------------------------------------------------------
+    @logError: (message) ->
+        getLogger().logError message
+
+    #---------------------------------------------------------------------------
+    @logWarning: (message) ->
+        getLogger().logWarning message
+
+    #---------------------------------------------------------------------------
+    @logInfo: (message) ->
+        getLogger().logInfo message
+
+    #---------------------------------------------------------------------------
+    @logDebug: (message) ->
+        getLogger().logDebug message
+
+#-------------------------------------------------------------------------------
+class ConsoleLogger
+    logError:   (message) -> console.log "error: #{message}"
+    logWarning: (message) -> console.log "warning: #{message}"
+    logInfo:    (message) -> console.log "info: #{message}"
+    logDebug:   (message) -> console.log "debug: #{message}"
+
+consoleLogger = new ConsoleLogger()
+
+#-------------------------------------------------------------------------------
+getLogger = () ->
+    return logger if logger
+
+    if Weinre.client
+        logger = Weinre.WeinreClientCommands
+        return logger
+
+    if Weinre.target
+        logger = Weinre.WeinreTargetCommands
+        return logger
+
+    consoleLogger
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/CSSStore.coffee b/weinre.web/modules/weinre/target/CSSStore.coffee
new file mode 100644
index 0000000..27ce891
--- /dev/null
+++ b/weinre.web/modules/weinre/target/CSSStore.coffee
@@ -0,0 +1,346 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+IDGenerator = require('../common/IDGenerator')
+Weinre      = require('../common/Weinre')
+
+Properties = []
+
+_elementMatchesSelector = null
+
+#-------------------------------------------------------------------------------
+module.exports = class CSSStore
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        @styleSheetMap = {}
+        @styleRuleMap  = {}
+        @styleDeclMap  = {}
+        @testElement   = document.createElement("div")
+
+    #---------------------------------------------------------------------------
+    @addCSSProperties: (properties) ->
+        Properties = properties
+
+    #---------------------------------------------------------------------------
+    getInlineStyle: (node) ->
+        styleObject = @_buildMirrorForStyle(node.style, true)
+
+        for cssProperty in styleObject.cssProperties
+            cssProperty.status = "style"
+
+        styleObject
+
+    #---------------------------------------------------------------------------
+    getComputedStyle: (node) ->
+        return {} unless node
+        return {} unless node.nodeType == Node.ELEMENT_NODE
+
+        styleObject = @_buildMirrorForStyle(window.getComputedStyle(node), false)
+        styleObject
+
+    #---------------------------------------------------------------------------
+    getMatchedCSSRules: (node) ->
+        result = []
+
+        for styleSheet in document.styleSheets
+            continue unless styleSheet.cssRules
+
+            for cssRule in styleSheet.cssRules
+                continue unless _elementMatchesSelector(node, cssRule.selectorText)
+                object = {}
+                object.ruleId = @_getStyleRuleId(cssRule)
+                object.selectorText = cssRule.selectorText
+                object.style = @_buildMirrorForStyle(cssRule.style, true)
+                result.push object
+
+        result
+
+    #---------------------------------------------------------------------------
+    getStyleAttributes: (node) ->
+        result = {}
+        result
+
+    #---------------------------------------------------------------------------
+    getPseudoElements: (node) ->
+        result = []
+        result
+
+    #---------------------------------------------------------------------------
+    setPropertyText: (styleId, propertyIndex, text, overwrite) ->
+        styleDecl = Weinre.cssStore._getStyleDecl(styleId)
+
+        unless styleDecl
+          Weinre.logWarning "requested style not available: " + styleId
+          return null
+
+        mirror = styleDecl.__weinre__mirror
+        unless mirror
+          Weinre.logWarning "requested mirror not available: " + styleId
+          return null
+
+        properties = mirror.cssProperties
+        propertyMirror = @_parseProperty(text)
+
+        if null == propertyMirror
+          @_removePropertyFromMirror mirror, propertyIndex
+          properties = mirror.cssProperties
+
+        else
+          @_removePropertyFromMirror mirror, propertyIndex
+          properties = mirror.cssProperties
+          propertyIndices = {}
+          i = 0
+
+          while i < properties.length
+            propertyIndices[properties[i].name] = i
+            i++
+          i = 0
+
+          while i < propertyMirror.cssProperties.length
+            if propertyIndices[propertyMirror.cssProperties[i].name]?
+              properties[propertyIndices[propertyMirror.cssProperties[i].name]] = propertyMirror.cssProperties[i]
+            else
+              properties.push propertyMirror.cssProperties[i]
+            i++
+
+          for key of propertyMirror.shorthandValues
+            mirror.shorthandValues[key] = propertyMirror.shorthandValues[key]
+
+        properties.sort compare = (p1, p2) ->
+          if p1.name < p2.name
+            -1
+          else if p1.name > p2.name
+            1
+          else
+            0
+
+        @_setStyleFromMirror styleDecl
+        mirror
+
+    #---------------------------------------------------------------------------
+    _removePropertyFromMirror: (mirror, index) ->
+        properties = mirror.cssProperties
+        return if index >= properties.length
+        property = properties[index]
+        properties[index] = null
+
+        if mirror.shorthandValues[property.name]
+            delete mirror.shorthandValues[property.name]
+
+            i = 0
+
+            while i < properties.length
+                if properties[i]
+                    if properties[i].shorthandName == property.name
+                        properties[i] = null
+                i++
+
+        newProperties = []
+        i = 0
+
+        while i < properties.length
+            newProperties.push properties[i] if properties[i]
+            i++
+
+        mirror.cssProperties = newProperties
+
+    #---------------------------------------------------------------------------
+    toggleProperty: (styleId, propertyIndex, disable) ->
+        styleDecl = Weinre.cssStore._getStyleDecl(styleId)
+        unless styleDecl
+            Weinre.logWarning "requested style not available: " + styleId
+            return null
+
+        mirror = styleDecl.__weinre__mirror
+        unless mirror
+            Weinre.logWarning "requested mirror not available: " + styleId
+            return null
+
+        cssProperty = mirror.cssProperties[propertyIndex]
+        unless cssProperty
+            Weinre.logWarning "requested property not available: #{styleId}: " + propertyIndex
+            return null
+
+        if disable
+            cssProperty.status = "disabled"
+        else
+            cssProperty.status = "active"
+
+        @_setStyleFromMirror styleDecl
+        mirror
+
+    #---------------------------------------------------------------------------
+    _setStyleFromMirror: (styleDecl) ->
+        cssText = []
+        cssProperties = styleDecl.__weinre__mirror.cssProperties
+        cssText = ""
+
+        for property in cssProperties
+            continue unless property.parsedOk
+            continue if property.status == "disabled"
+            continue if property.shorthandName
+
+            cssText += property.name + ": " + property.value
+            if property.priority == "important"
+                cssText += " !important; "
+            else
+                cssText += "; "
+
+        styleDecl.cssText = cssText
+
+    #---------------------------------------------------------------------------
+    _buildMirrorForStyle: (styleDecl, bind) ->
+        result =
+          properties: {}
+          cssProperties: []
+
+        return result unless styleDecl
+        if bind
+          result.styleId = @_getStyleDeclId(styleDecl)
+          styleDecl.__weinre__mirror = result
+
+        result.properties.width = styleDecl.getPropertyValue("width") or ""
+        result.properties.height = styleDecl.getPropertyValue("height") or ""
+        result.cssText = styleDecl.cssText
+        result.shorthandValues = {}
+
+        properties = []
+        if styleDecl
+          i = 0
+
+          while i < styleDecl.length
+            property = {}
+            name = styleDecl.item(i)
+            property.name          = name
+            property.priority      = styleDecl.getPropertyPriority(name)
+            property.implicit      = styleDecl.isPropertyImplicit(name)
+            property.shorthandName = styleDecl.getPropertyShorthand(name) or ""
+            property.status        = (if property.shorthandName then "style" else "active")
+            property.parsedOk      = true
+            property.value         = styleDecl.getPropertyValue(name)
+            properties.push property
+
+            if property.shorthandName
+              shorthandName = property.shorthandName
+
+              unless result.shorthandValues[shorthandName]
+                result.shorthandValues[shorthandName] = styleDecl.getPropertyValue(shorthandName)
+                property = {}
+                property.name          = shorthandName
+                property.priority      = styleDecl.getPropertyPriority(shorthandName)
+                property.implicit      = styleDecl.isPropertyImplicit(shorthandName)
+                property.shorthandName = ""
+                property.status        = "active"
+                property.parsedOk      = true
+                property.value         = styleDecl.getPropertyValue(name)
+                properties.push property
+            i++
+
+        properties.sort (p1, p2) ->
+          if p1.name < p2.name
+            -1
+          else if p1.name > p2.name
+            1
+          else
+            0
+
+        result.cssProperties = properties
+        result
+
+    #---------------------------------------------------------------------------
+    _parseProperty: (string) ->
+        testStyleDecl = @testElement.style
+
+        try
+            testStyleDecl.cssText = string
+            unless testStyleDecl.cssText == ""
+                return @_buildMirrorForStyle(testStyleDecl, false)
+
+        propertyPattern = /\s*(.+)\s*:\s*(.+)\s*(!important)?\s*;/
+        match = propertyPattern.exec(string)
+        return null unless match
+
+        match[3] = (if (match[3] == "!important") then "important" else "")
+
+        property = {}
+        property.name          = match[1]
+        property.priority      = match[3]
+        property.implicit      = true
+        property.shorthandName = ""
+        property.status        = "inactive"
+        property.parsedOk      = false
+        property.value         = match[2]
+
+        result = {}
+        result.width           = 0
+        result.height          = 0
+        result.shorthandValues = 0
+        result.cssProperties   = [ property ]
+
+        result
+
+    #---------------------------------------------------------------------------
+    _getStyleSheet: (id) ->
+        _getMappableObject id, @styleSheetMap
+
+    #---------------------------------------------------------------------------
+    _getStyleSheetId: (styleSheet) ->
+        _getMappableId styleSheet, @styleSheetMap
+
+    #---------------------------------------------------------------------------
+    _getStyleRule: (id) ->
+        _getMappableObject id, @styleRuleMap
+
+    #---------------------------------------------------------------------------
+    _getStyleRuleId: (styleRule) ->
+        _getMappableId styleRule, @styleRuleMap
+
+    #---------------------------------------------------------------------------
+    _getStyleDecl: (id) ->
+        _getMappableObject id, @styleDeclMap
+
+    #---------------------------------------------------------------------------
+    _getStyleDeclId: (styleDecl) ->
+        _getMappableId styleDecl, @styleDeclMap
+
+#-------------------------------------------------------------------------------
+_getMappableObject = (id, map) ->
+      map[id]
+
+#-------------------------------------------------------------------------------
+_getMappableId = (object, map) ->
+      IDGenerator.getId object, map
+
+#-------------------------------------------------------------------------------
+_mozMatchesSelector = (element, selector) ->
+      return false unless element.mozMatchesSelector
+      element.mozMatchesSelector selector
+
+#-------------------------------------------------------------------------------
+_webkitMatchesSelector = (element, selector) ->
+      return false unless element.webkitMatchesSelector
+      element.webkitMatchesSelector selector
+
+#-------------------------------------------------------------------------------
+_fallbackMatchesSelector = (element, selector) ->
+      false
+
+#-------------------------------------------------------------------------------
+if      (Element.prototype.webkitMatchesSelector)
+    _elementMatchesSelector = _webkitMatchesSelector
+
+else if (Element.prototype.mozMatchesSelector)
+    _elementMatchesSelector = _mozMatchesSelector
+
+else
+    _elementMatchesSelector = _fallbackMatchesSelector
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
+
diff --git a/weinre.web/modules/weinre/target/CheckForProblems.coffee b/weinre.web/modules/weinre/target/CheckForProblems.coffee
new file mode 100644
index 0000000..cb30171
--- /dev/null
+++ b/weinre.web/modules/weinre/target/CheckForProblems.coffee
@@ -0,0 +1,30 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+module.exports = class CheckForProblems
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    @check: ->
+        checkForOldPrototypeVersion()
+
+#-------------------------------------------------------------------------------
+checkForOldPrototypeVersion = () ->
+      badVersion = false
+      return if typeof Prototype == "undefined"
+      return unless Prototype.Version
+
+      badVersion = true if Prototype.Version.match(/^1\.5.*/)
+      badVersion = true if Prototype.Version.match(/^1\.6.*/)
+
+      if badVersion
+          alert "Sorry, weinre is not support in versions of Prototype earlier than 1.7"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/Console.coffee b/weinre.web/modules/weinre/target/Console.coffee
new file mode 100644
index 0000000..800c84f
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Console.coffee
@@ -0,0 +1,164 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre   = require('../common/Weinre')
+Timeline = require('../target/Timeline')
+
+UsingRemote = false
+
+RemoteConsole   = null
+OriginalConsole = null
+
+MessageSource =
+    HTML:  0
+    WML:   1
+    XML:   2
+    JS:    3
+    CSS:   4
+    Other: 5
+
+MessageType =
+    Log:                 0
+    Object:              1
+    Trace:               2
+    StartGroup:          3
+    StartGroupCollapsed: 4
+    EndGroup:            5
+    Assert:              6
+    UncaughtException:   7
+    Result:              8
+
+MessageLevel =
+    Tip:     0
+    Log:     1
+    Warning: 2
+    Error:   3
+    Debug:   4
+
+#-------------------------------------------------------------------------------
+module.exports = class Console
+
+    #---------------------------------------------------------------------------
+    Console::__defineGetter__("original", -> OriginalConsole)
+
+    #---------------------------------------------------------------------------
+    @useRemote: (value) ->
+        return UsingRemote if arguments.length == 0
+
+        oldValue = UsingRemote
+        UsingRemote = not not value
+
+        if UsingRemote
+            window.console = RemoteConsole
+        else
+            window.console = OriginalConsole
+
+        oldValue
+
+    #---------------------------------------------------------------------------
+    _generic: (level, messageParts) ->
+        message = messageParts[0].toString()
+        parameters = []
+
+        for messagePart in messageParts
+            parameters.push Weinre.injectedScript.wrapObjectForConsole(messagePart, true)
+
+        payload =
+            source: MessageSource.JS
+            type: MessageType.Log
+            level: level
+            message: message
+            parameters: parameters
+
+        Weinre.wi.ConsoleNotify.addConsoleMessage payload
+
+    #---------------------------------------------------------------------------
+    log: ->
+        @_generic MessageLevel.Log, [].slice.call(arguments)
+
+    #---------------------------------------------------------------------------
+    debug: ->
+        @_generic MessageLevel.Debug, [].slice.call(arguments)
+
+    #---------------------------------------------------------------------------
+    error: ->
+        @_generic MessageLevel.Error, [].slice.call(arguments)
+
+    #---------------------------------------------------------------------------
+    info: ->
+        @_generic MessageLevel.Log, [].slice.call(arguments)
+
+    #---------------------------------------------------------------------------
+    warn: ->
+        @_generic MessageLevel.Warning, [].slice.call(arguments)
+
+    #---------------------------------------------------------------------------
+    dir: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    dirxml: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    trace: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    assert: (condition) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    count: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    markTimeline: (message) ->
+        Timeline.addRecord_Mark message
+
+    #---------------------------------------------------------------------------
+    lastWMLErrorMessage: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    profile: (title) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    profileEnd: (title) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    time: (title) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    timeEnd: (title) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    group: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    groupCollapsed: ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    groupEnd: ->
+        Weinre.notImplemented arguments.callee.signature
+
+#-------------------------------------------------------------------------------
+RemoteConsole   = new Console()
+OriginalConsole = window.console
+
+RemoteConsole.__original   = OriginalConsole
+OriginalConsole.__original = OriginalConsole
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/ElementHighlighter.coffee b/weinre.web/modules/weinre/target/ElementHighlighter.coffee
new file mode 100644
index 0000000..b1a7ccb
--- /dev/null
+++ b/weinre.web/modules/weinre/target/ElementHighlighter.coffee
@@ -0,0 +1,119 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Binding = require('../common/Binding')
+Weinre  = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class ElementHighlighter
+
+    ElementHighlighter::__defineGetter__("element", -> @boxMargin)
+
+    constructor: ->
+        @boxMargin  = document.createElement("div")
+        @boxBorder  = document.createElement("div")
+        @boxPadding = document.createElement("div")
+        @boxContent = document.createElement("div")
+
+        @boxMargin.appendChild  @boxBorder
+        @boxBorder.appendChild  @boxPadding
+        @boxPadding.appendChild @boxContent
+
+        @boxMargin.style.backgroundColor  = "#FCC"
+        @boxBorder.style.backgroundColor  = "#000"
+        @boxPadding.style.backgroundColor = "#CFC"
+        @boxContent.style.backgroundColor = "#CCF"
+
+        @boxMargin.style.opacity       = @boxBorder.style.opacity       = @boxPadding.style.opacity       = @boxContent.style.opacity       = 0.6
+        @boxMargin.style.position      = @boxBorder.style.position      = @boxPadding.style.position      = @boxContent.style.position      = "absolute"
+        @boxMargin.style.borderWidth   = @boxBorder.style.borderWidth   = @boxPadding.style.borderWidth   = @boxContent.style.borderWidth   = "thin"
+        @boxMargin.style.borderStyle   = @boxBorder.style.borderStyle   = @boxPadding.style.borderStyle   = @boxContent.style.borderStyle   = "solid"
+        @boxMargin.__weinreHighlighter = @boxBorder.__weinreHighlighter = @boxPadding.__weinreHighlighter = @boxContent.__weinreHighlighter = true
+
+        @boxMargin.style.display = "none"
+        document.body.appendChild @boxMargin
+
+    #---------------------------------------------------------------------------
+    on: (element) ->
+        return if null == element
+        return unless element.nodeType == Node.ELEMENT_NODE
+
+        @calculateMetrics element
+        @boxMargin.style.display = "block"
+
+    #---------------------------------------------------------------------------
+    off: ->
+        @boxMargin.style.display = "none"
+
+    #---------------------------------------------------------------------------
+    calculateMetrics: (element) ->
+        metrics = getMetrics(element)
+
+        @boxMargin.style.top     = metrics.y + "px"
+        @boxMargin.style.left    = metrics.x + "px"
+        @boxMargin.style.height  = metrics.height + "px"
+        @boxMargin.style.width   = metrics.width + "px"
+        @boxBorder.style.top     = metrics.marginTop + "px"
+        @boxBorder.style.left    = metrics.marginLeft + "px"
+        @boxBorder.style.bottom  = metrics.marginBottom + "px"
+        @boxBorder.style.right   = metrics.marginRight + "px"
+        @boxPadding.style.top    = metrics.borderTop + "px"
+        @boxPadding.style.left   = metrics.borderLeft + "px"
+        @boxPadding.style.bottom = metrics.borderBottom + "px"
+        @boxPadding.style.right  = metrics.borderRight + "px"
+        @boxContent.style.top    = metrics.paddingTop + "px"
+        @boxContent.style.left   = metrics.paddingLeft + "px"
+        @boxContent.style.bottom = metrics.paddingBottom + "px"
+        @boxContent.style.right  = metrics.paddingRight + "px"
+
+#-------------------------------------------------------------------------------
+getMetrics = (element) ->
+      result = {}
+      left = 0
+      top  = 0
+      el   = element
+
+      loop
+          left += el.offsetLeft
+          top += el.offsetTop
+          break unless el = el.offsetParent
+
+      result.x = left
+      result.y = top
+
+      cStyle = document.defaultView.getComputedStyle(element)
+
+      result.width         = fromPx(cStyle["width"])
+      result.height        = fromPx(cStyle["height"])
+      result.marginLeft    = fromPx(cStyle["margin-left"])
+      result.marginRight   = fromPx(cStyle["margin-right"])
+      result.marginTop     = fromPx(cStyle["margin-top"])
+      result.marginBottom  = fromPx(cStyle["margin-bottom"])
+      result.borderLeft    = fromPx(cStyle["border-left-width"])
+      result.borderRight   = fromPx(cStyle["border-right-width"])
+      result.borderTop     = fromPx(cStyle["border-top-width"])
+      result.borderBottom  = fromPx(cStyle["border-bottom-width"])
+      result.paddingLeft   = fromPx(cStyle["padding-left"])
+      result.paddingRight  = fromPx(cStyle["padding-right"])
+      result.paddingTop    = fromPx(cStyle["padding-top"])
+      result.paddingBottom = fromPx(cStyle["padding-bottom"])
+
+      result.width  += result.marginLeft + result.marginRight  + result.borderRight  + result.paddingLeft + result.paddingRight
+      result.height += result.marginTop  + result.marginBottom + result.borderBottom + result.paddingTop  + result.paddingBottom
+
+      result.x -= result.marginLeft
+      result.y -= result.marginTop
+
+      result
+
+#-------------------------------------------------------------------------------
+fromPx = (string) ->
+      parseInt string.replace(/px$/, "")
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/InjectedScriptHostImpl.coffee b/weinre.web/modules/weinre/target/InjectedScriptHostImpl.coffee
new file mode 100644
index 0000000..a4ba59f
--- /dev/null
+++ b/weinre.web/modules/weinre/target/InjectedScriptHostImpl.coffee
@@ -0,0 +1,50 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class InjectedScriptHostImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    clearConsoleMessages: (callback) ->
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    nodeForId: (nodeId, callback) ->
+        Weinre.nodeStore.getNode nodeId
+
+    #---------------------------------------------------------------------------
+    pushNodePathToFrontend: (node, withChildren, selectInUI, callback) ->
+        nodeId = Weinre.nodeStore.getNodeId(node)
+        children = Weinre.nodeStore.serializeNode(node, 1)
+        Weinre.wi.DOMNotify.setChildNodes nodeId, children
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    inspectedNode: (num, callback) ->
+        nodeId = Weinre.nodeStore.getInspectedNode(num)
+        nodeId
+
+    #---------------------------------------------------------------------------
+    internalConstructorName: (object) ->
+        ctor = object.constructor
+        ctorName = ctor.fullClassName or ctor.displayName or ctor.name
+        return ctorName if ctorName and (ctorName != "Object")
+
+        pattern = /\[object (.*)\]/
+        match = pattern.exec(ctor.toString())
+
+        return match[1] if match
+
+        "Object"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/NodeStore.coffee b/weinre.web/modules/weinre/target/NodeStore.coffee
new file mode 100644
index 0000000..edd0269
--- /dev/null
+++ b/weinre.web/modules/weinre/target/NodeStore.coffee
@@ -0,0 +1,219 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre      = require('../common/Weinre')
+IDGenerator = require('../common/IDGenerator')
+
+#-------------------------------------------------------------------------------
+module.exports = class NodeStore
+
+    constructor: ->
+        @__nodeMap      = {}
+        @__nodeDataMap  = {}
+        @inspectedNodes = []
+
+        document.addEventListener "DOMSubtreeModified",       handleDOMSubtreeModified, false
+        document.addEventListener "DOMNodeInserted",          handleDOMNodeInserted, false
+        document.addEventListener "DOMNodeRemoved",           handleDOMNodeRemoved, false
+        document.addEventListener "DOMAttrModified",          handleDOMAttrModified, false
+        document.addEventListener "DOMCharacterDataModified", handleDOMCharacterDataModified, false
+
+    #---------------------------------------------------------------------------
+    addInspectedNode: (nodeId) ->
+        @inspectedNodes.unshift nodeId
+        @inspectedNodes = @inspectedNodes.slice(0, 5) if @inspectedNodes.length > 5
+
+    #---------------------------------------------------------------------------
+    getInspectedNode: (index) ->
+        @inspectedNodes[index]
+
+    #---------------------------------------------------------------------------
+    getNode: (nodeId) ->
+        @__nodeMap[nodeId]
+
+    #---------------------------------------------------------------------------
+    checkNodeId: (node) ->
+        IDGenerator.checkId node
+
+    #---------------------------------------------------------------------------
+    getNodeId: (node) ->
+        id = @checkNodeId(node)
+        return id if id
+        IDGenerator.getId node, @__nodeMap
+
+    #---------------------------------------------------------------------------
+    getNodeData: (nodeId, depth) ->
+        @serializeNode @getNode(nodeId), depth
+
+    #---------------------------------------------------------------------------
+    getPreviousSiblingId: (node) ->
+        while true
+            sib = node.previousSibling
+            return 0 unless sib
+
+            id = @checkNodeId(sib)
+            return id if id
+
+            node = sib
+
+    #---------------------------------------------------------------------------
+    nextNodeId: () ->
+        "" + IDGenerator.next()
+
+    #---------------------------------------------------------------------------
+    serializeNode: (node, depth) ->
+        nodeName = ""
+        nodeValue = null
+        localName = null
+        id = @getNodeId(node)
+
+        switch node.nodeType
+
+            when Node.TEXT_NODE, Node.COMMENT_NODE, Node.CDATA_SECTION_NODE
+                nodeValue = node.nodeValue
+
+            when Node.ATTRIBUTE_NODE
+                localName = node.localName
+
+            when Node.DOCUMENT_FRAGMENT_NODE
+                break
+
+            else
+                nodeName  = node.nodeName
+                localName = node.localName
+
+        nodeData =
+          id:        id
+          nodeType:  node.nodeType
+          nodeName:  nodeName
+          localName: localName
+          nodeValue: nodeValue
+
+        if node.nodeType == Node.ELEMENT_NODE or node.nodeType == Node.DOCUMENT_NODE or node.nodeType == Node.DOCUMENT_FRAGMENT_NODE
+          nodeData.childNodeCount = @childNodeCount(node)
+          children = @serializeNodeChildren(node, depth)
+          nodeData.children = children if children.length
+
+          if node.nodeType == Node.ELEMENT_NODE
+            nodeData.attributes = []
+            i = 0
+
+            while i < node.attributes.length
+              nodeData.attributes.push node.attributes[i].nodeName
+              nodeData.attributes.push node.attributes[i].nodeValue
+              i++
+
+          else
+            if node.nodeType == Node.DOCUMENT_NODE
+              nodeData.documentURL = window.location.href
+
+        else if node.nodeType == Node.DOCUMENT_TYPE_NODE
+          nodeData.publicId       = node.publicId
+          nodeData.systemId       = node.systemId
+          nodeData.internalSubset = node.internalSubset
+
+        else if node.nodeType == Node.ATTRIBUTE_NODE
+          nodeData.name  = node.nodeName
+          nodeData.value = node.nodeValue
+
+        nodeData
+
+    #---------------------------------------------------------------------------
+    serializeNodeChildren: (node, depth) ->
+        result   = []
+        childIds = @childNodeIds(node)
+
+        if depth == 0
+            if childIds.length == 1
+                childNode = @getNode(childIds[0])
+                if childNode.nodeType == Node.TEXT_NODE
+                    result.push @serializeNode(childNode)
+            return result
+
+        depth--
+        i = 0
+
+        while i < childIds.length
+            result.push @serializeNode(@getNode(childIds[i]), depth)
+            i++
+
+        result
+
+    #---------------------------------------------------------------------------
+    childNodeCount: (node) ->
+        @childNodeIds(node).length
+
+    #---------------------------------------------------------------------------
+    childNodeIds: (node) ->
+        ids = []
+        i = 0
+
+        for childNode in node.childNodes
+            continue if @isToBeSkipped(childNode)
+            ids.push @getNodeId(childNode)
+
+        ids
+
+    #---------------------------------------------------------------------------
+    isToBeSkipped: (node) ->
+        return true unless node
+        return true if node.__weinreHighlighter
+        return false unless node.nodeType == Node.TEXT_NODE
+
+        not not node.nodeValue.match(/^\s*$/)
+
+#-------------------------------------------------------------------------------
+handleDOMSubtreeModified = (event) ->
+      return unless event.attrChange
+      NodeStore.handleDOMAttrModified event
+
+#-------------------------------------------------------------------------------
+handleDOMNodeInserted = (event) ->
+      targetId = Weinre.nodeStore.checkNodeId(event.target)
+      parentId = Weinre.nodeStore.checkNodeId(event.relatedNode)
+
+      return unless parentId
+
+      child    = Weinre.nodeStore.serializeNode(event.target, 0)
+      previous = Weinre.nodeStore.getPreviousSiblingId(event.target)
+      Weinre.wi.DOMNotify.childNodeInserted parentId, previous, child
+
+#-------------------------------------------------------------------------------
+handleDOMNodeRemoved = (event) ->
+      targetId = Weinre.nodeStore.checkNodeId(event.target)
+      parentId = Weinre.nodeStore.checkNodeId(event.relatedNode)
+      return unless parentId
+
+      if targetId
+          Weinre.wi.DOMNotify.childNodeRemoved parentId, targetId
+      else
+          childCount = Weinre.nodeStore.childNodeCount(event.relatedNode)
+          Weinre.wi.DOMNotify.childNodeCountUpdated parentId, childCount
+
+#-------------------------------------------------------------------------------
+handleDOMAttrModified = (event) ->
+      targetId = Weinre.nodeStore.checkNodeId(event.target)
+      return unless targetId
+      attrs = []
+      i = 0
+
+      while i < event.target.attributes.length
+          attrs.push event.target.attributes[i].name
+          attrs.push event.target.attributes[i].value
+          i++
+
+      Weinre.wi.DOMNotify.attributesUpdated targetId, attrs
+
+#-------------------------------------------------------------------------------
+handleDOMCharacterDataModified = (event) ->
+      targetId = Weinre.nodeStore.checkNodeId(event.target)
+      return unless targetId
+      Weinre.wi.DOMNotify.characterDataModified targetId, event.newValue
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/SqlStepper.coffee b/weinre.web/modules/weinre/target/SqlStepper.coffee
new file mode 100644
index 0000000..82b5997
--- /dev/null
+++ b/weinre.web/modules/weinre/target/SqlStepper.coffee
@@ -0,0 +1,91 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Binding = require('../common/Binding')
+
+#-------------------------------------------------------------------------------
+module.exports = class SqlStepper
+
+    constructor: (steps) ->
+        return new SqlStepper(steps) unless (this instanceof SqlStepper)
+
+        @__context = {}
+
+        context = @__context
+        context.steps = steps
+
+    #---------------------------------------------------------------------------
+    run: (db, errorCallback) ->
+        context = @__context
+        if context.hasBeenRun
+            throw new Ex(arguments, "stepper has already been run")
+
+        context.hasBeenRun       = true
+        context.db               = db
+        context.errorCallback    = errorCallback
+        context.nextStep         = 0
+        context.ourErrorCallback = new Binding(this, ourErrorCallback)
+        context.runStep          = new Binding(this, runStep)
+
+        @executeSql = new Binding(this, executeSql)
+
+        db.transaction context.runStep
+
+    #---------------------------------------------------------------------------
+    @example: (db, id) ->
+        step1 = ->
+            @executeSql "SELECT name FROM sqlite_master WHERE type='table'"
+
+        step2 = (resultSet) ->
+            rows = resultSet.rows
+            result = []
+
+            i = 0
+            while i < rows.length
+                name = rows.item(i).name
+                if name == "__WebKitDatabaseInfoTable__"
+                    i++
+                    continue
+
+                result.push name
+                i++
+
+            console.log "[#{@id}] table names: " + result.join(", ")
+
+        errorCb = (sqlError) ->
+            console.log "[#{@id}] sql error:#{sqlError.code}: " + sqlError.message
+
+        stepper = new SqlStepper([ step1, step2 ])
+        stepper.id = id
+        stepper.run db, errorCb
+
+
+#-------------------------------------------------------------------------------
+executeSql = (statement, data) ->
+      context = @__context
+      context.tx.executeSql statement, data, context.runStep, context.ourErrorCallback
+
+#-------------------------------------------------------------------------------
+ourErrorCallback = (tx, sqlError) ->
+      context = @__context
+      context.errorCallback.call this, sqlError
+
+#-------------------------------------------------------------------------------
+runStep = (tx, resultSet) ->
+      context = @__context
+      return if context.nextStep >= context.steps.length
+
+      context.tx = tx
+      context.currentStep = context.nextStep
+      context.nextStep++
+
+      step = context.steps[context.currentStep]
+      step.call this, resultSet
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/Target.coffee b/weinre.web/modules/weinre/target/Target.coffee
new file mode 100644
index 0000000..130a05b
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Target.coffee
@@ -0,0 +1,202 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Native                        = require('../common/Native')
+Ex                            = require('../common/Ex')
+Binding                       = require('../common/Binding')
+Callback                      = require('../common/Callback')
+MessageDispatcher             = require('../common/MessageDispatcher')
+Weinre                        = require('../common/Weinre')
+
+CheckForProblems              = require('./CheckForProblems')
+NodeStore                     = require('./NodeStore')
+CSSStore                      = require('./CSSStore')
+ElementHighlighter            = require('./ElementHighlighter')
+InjectedScriptHostImpl        = require('./InjectedScriptHostImpl')
+WeinreTargetEventsImpl        = require('./WeinreTargetEventsImpl')
+WeinreExtraClientCommandsImpl = require('./WeinreExtraClientCommandsImpl')
+WiConsoleImpl                 = require('./WiConsoleImpl')
+WiCSSImpl                     = require('./WiCSSImpl')
+WiDatabaseImpl                = require('./WiDatabaseImpl')
+WiDOMImpl                     = require('./WiDOMImpl')
+WiDOMStorageImpl              = require('./WiDOMStorageImpl')
+WiInspectorImpl               = require('./WiInspectorImpl')
+WiRuntimeImpl                 = require('./WiRuntimeImpl')
+
+#-------------------------------------------------------------------------------
+module.exports = class Target
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    @main: ->
+        CheckForProblems.check()
+        Weinre.target = new Target()
+        Weinre.target.initialize()
+
+        Weinre.addCSSProperties = addCSSProperties = (properties) ->
+            CSSStore.addCSSProperties properties
+
+    #----------------------------------------------------------------------------
+    setWeinreServerURLFromScriptSrc: (element) ->
+        return if window.WeinreServerURL
+
+        if element
+            pattern = /(http:\/\/(.*?)\/)/
+            match = pattern.exec(element.src)
+            if match
+                window.WeinreServerURL = match[1]
+                return
+
+        message = "unable to calculate the weinre server url; explicity set the variable window.WeinreServerURL instead"
+        alert message
+        throw new Ex(arguments, message)
+
+    #---------------------------------------------------------------------------
+    setWeinreServerIdFromScriptSrc: (element) ->
+        return if window.WeinreServerId
+
+        element = @getTargetScriptElement()
+        hash    = "anonymous"
+
+        if element
+            attempt = element.src.split("#")[1]
+            if attempt
+                hash = attempt
+            else
+                attempt = location.hash.split("#")[1]
+                hash = attempt if attempt
+
+        window.WeinreServerId = hash
+
+    #---------------------------------------------------------------------------
+    getTargetScriptElement: ->
+        elements = document.getElementsByTagName("script")
+        scripts = [ "Target.", "target-script.", "target-script-min." ]
+        i = 0
+
+        while i < elements.length
+            element = elements[i]
+            j = 0
+            while j < scripts.length
+                return element unless -1 == element.src.indexOf("/" + scripts[j])
+                j++
+            i++
+
+    #---------------------------------------------------------------------------
+    initialize: () ->
+        self = this
+
+        element = @getTargetScriptElement()
+
+        @setWeinreServerURLFromScriptSrc element
+        @setWeinreServerIdFromScriptSrc element
+
+        window.WeinreServerURL += "/" unless window.WeinreServerURL[window.WeinreServerURL.length - 1] == "/"
+        injectedScriptHost = new InjectedScriptHostImpl()
+        Weinre.injectedScript = injectedScriptConstructor(injectedScriptHost, window, 0, "?")
+
+        window.addEventListener "load", Binding(this, "onLoaded"), false
+        document.addEventListener "DOMContentLoaded", Binding(this, "onDOMContent"), false
+
+        @_startTime = currentTime()
+        if document.readyState == "loaded"
+            setTimeout (->
+                self.onDOMContent()
+            ), 10
+
+        if document.readyState == "complete"
+            setTimeout (->
+                self.onDOMContent()
+            ), 10
+            setTimeout (->
+                self.onLoaded()
+            ), 20
+
+#        MessageDispatcher.verbose(true)
+        messageDispatcher = new MessageDispatcher(window.WeinreServerURL + "ws/target", window.WeinreServerId)
+        Weinre.messageDispatcher = messageDispatcher
+
+        Weinre.wi = {}
+        Weinre.wi.Console    = new WiConsoleImpl()
+        Weinre.wi.CSS        = new WiCSSImpl()
+        Weinre.wi.Database   = new WiDatabaseImpl()
+        Weinre.wi.DOM        = new WiDOMImpl()
+        Weinre.wi.DOMStorage = new WiDOMStorageImpl()
+        Weinre.wi.Inspector  = new WiInspectorImpl()
+        Weinre.wi.Runtime    = new WiRuntimeImpl()
+
+        messageDispatcher.registerInterface "Console",    Weinre.wi.Console, false
+        messageDispatcher.registerInterface "CSS",        Weinre.wi.CSS, false
+        messageDispatcher.registerInterface "Database",   Weinre.wi.Database, false
+        messageDispatcher.registerInterface "DOM",        Weinre.wi.DOM, false
+        messageDispatcher.registerInterface "DOMStorage", Weinre.wi.DOMStorage, false
+        messageDispatcher.registerInterface "Inspector",  Weinre.wi.Inspector, false
+        messageDispatcher.registerInterface "Runtime",    Weinre.wi.Runtime, false
+
+        messageDispatcher.registerInterface "WeinreExtraClientCommands", new WeinreExtraClientCommandsImpl(), true
+        messageDispatcher.registerInterface "WeinreTargetEvents",        new WeinreTargetEventsImpl(), true
+
+        Weinre.wi.ApplicationCacheNotify = messageDispatcher.createProxy("ApplicationCacheNotify")
+        Weinre.wi.ConsoleNotify          = messageDispatcher.createProxy("ConsoleNotify")
+        Weinre.wi.DOMNotify              = messageDispatcher.createProxy("DOMNotify")
+        Weinre.wi.DOMStorageNotify       = messageDispatcher.createProxy("DOMStorageNotify")
+        Weinre.wi.DatabaseNotify         = messageDispatcher.createProxy("DatabaseNotify")
+        Weinre.wi.InspectorNotify        = messageDispatcher.createProxy("InspectorNotify")
+        Weinre.wi.TimelineNotify         = messageDispatcher.createProxy("TimelineNotify")
+        Weinre.WeinreTargetCommands      = messageDispatcher.createProxy("WeinreTargetCommands")
+        Weinre.WeinreExtraTargetEvents   = messageDispatcher.createProxy("WeinreExtraTargetEvents")
+
+        messageDispatcher.getWebSocket().addEventListener "open", Binding(this, @cb_webSocketOpened)
+
+        Weinre.nodeStore = new NodeStore()
+        Weinre.cssStore  = new CSSStore()
+
+        window.addEventListener "error", ((e) ->
+            Target.handleError e
+        ), false
+
+    #---------------------------------------------------------------------------
+    @handleError: (event) ->
+        filename = event.filename or "[unknown filename]"
+        lineno   = event.lineno   or "[unknown lineno]"
+        message  = event.message  or "[unknown message]"
+
+        Weinre.logError "error occurred: #{filename}:#{lineno}: " + message
+
+    #---------------------------------------------------------------------------
+    cb_webSocketOpened: () ->
+        Weinre.WeinreTargetCommands.registerTarget window.location.href, Binding(this, @cb_registerTarget)
+
+    #---------------------------------------------------------------------------
+    cb_registerTarget: (targetDescription) ->
+        Weinre.targetDescription = targetDescription
+
+    #---------------------------------------------------------------------------
+    onLoaded: ->
+        Weinre.wi.InspectorNotify.loadEventFired currentTime() - @_startTime
+
+    #---------------------------------------------------------------------------
+    onDOMContent: ->
+        Weinre.wi.InspectorNotify.domContentEventFired currentTime() - @_startTime
+
+    #---------------------------------------------------------------------------
+    setDocument: () ->
+        Weinre.elementHighlighter = new ElementHighlighter()
+
+        nodeId   = Weinre.nodeStore.getNodeId(document)
+        nodeData = Weinre.nodeStore.getNodeData(nodeId, 2)
+        Weinre.wi.DOMNotify.setDocument nodeData
+
+#-------------------------------------------------------------------------------
+currentTime = () ->
+      (new Date().getMilliseconds()) / 1000.0
+
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/Timeline.coffee b/weinre.web/modules/weinre/target/Timeline.coffee
new file mode 100644
index 0000000..488f0e9
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Timeline.coffee
@@ -0,0 +1,348 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Ex          = require('../common/Ex')
+Weinre      = require('../common/Weinre')
+IDGenerator = require('../common/IDGenerator')
+StackTrace  = require('../common/StackTrace')
+Native      = require('../common/Native')
+
+Running = false
+
+TimerTimeouts  = {}
+TimerIntervals = {}
+
+TimelineRecordType =
+    EventDispatch:            0
+    Layout:                   1
+    RecalculateStyles:        2
+    Paint:                    3
+    ParseHTML:                4
+    TimerInstall:             5
+    TimerRemove:              6
+    TimerFire:                7
+    XHRReadyStateChange:      8
+    XHRLoad:                  9
+    EvaluateScript:          10
+    Mark:                    11
+    ResourceSendRequest:     12
+    ResourceReceiveResponse: 13
+    ResourceFinish:          14
+    FunctionCall:            15
+    ReceiveResourceData:     16
+    GCEvent:                 17
+    MarkDOMContent:          18
+    MarkLoad:                19
+    ScheduleResourceRequest: 20
+
+
+#-------------------------------------------------------------------------------
+module.exports = class Timeline
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    @start: ->
+        Running = true
+
+    #---------------------------------------------------------------------------
+    @stop: ->
+        Running = false
+
+    #---------------------------------------------------------------------------
+    @isRunning: ->
+        Running
+
+    #---------------------------------------------------------------------------
+    @addRecord_Mark: (message) ->
+        return unless Timeline.isRunning()
+
+        record = {}
+        record.type      = TimelineRecordType.Mark
+        record.category  = name: "scripting"
+        record.startTime = Date.now()
+        record.data      = message: message
+
+        addStackTrace record, 3
+
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @addRecord_EventDispatch: (event, name, category) ->
+        return unless Timeline.isRunning()
+
+        category = "scripting" unless category
+        record = {}
+        record.type      = TimelineRecordType.EventDispatch
+        record.category  = name: category
+        record.startTime = Date.now()
+        record.data      = type: event.type
+
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @addRecord_TimerInstall: (id, timeout, singleShot) ->
+        return unless Timeline.isRunning()
+
+        record = {}
+        record.type      = TimelineRecordType.TimerInstall
+        record.category  = name: "scripting"
+        record.startTime = Date.now()
+        record.data =
+            timerId:    id
+            timeout:    timeout
+            singleShot: singleShot
+
+        addStackTrace record, 4
+
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @addRecord_TimerRemove: (id, timeout, singleShot) ->
+        return unless Timeline.isRunning()
+
+        record = {}
+        record.type      = TimelineRecordType.TimerRemove
+        record.category  = name: "scripting"
+        record.startTime = Date.now()
+        record.data =
+            timerId:    id
+            timeout:    timeout
+            singleShot: singleShot
+
+        addStackTrace record, 4
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @addRecord_TimerFire: (id, timeout, singleShot) ->
+        return unless Timeline.isRunning()
+
+        record = {}
+        record.type      = TimelineRecordType.TimerFire
+        record.category  = name: "scripting"
+        record.startTime = Date.now()
+        record.data =
+            timerId:    id
+            timeout:    timeout
+            singleShot: singleShot
+
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @addRecord_XHRReadyStateChange: (method, url, id, xhr) ->
+        return unless Timeline.isRunning()
+
+        if xhr.readyState == XMLHttpRequest.OPENED
+            record =
+                type:      TimelineRecordType.ResourceSendRequest
+                category:  name: "loading"
+                startTime: Date.now()
+                data:
+                    identifier:    id
+                    url:           url
+                    requestMethod: method
+
+        else if xhr.readyState == XMLHttpRequest.DONE
+            record =
+                type: TimelineRecordType.ResourceReceiveResponse
+                category: name: "loading"
+                startTime: Date.now()
+                data:
+                    identifier:            id
+                    statusCode:            xhr.status
+                    mimeType:              xhr.getResponseHeader("Content-Type")
+                    expectedContentLength: xhr.getResponseHeader("Content-Length")
+                    url:                   url
+        else
+            return
+
+        Weinre.wi.TimelineNotify.addRecordToTimeline record
+
+    #---------------------------------------------------------------------------
+    @installGlobalListeners: ->
+        if applicationCache
+            applicationCache.addEventListener "checking", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.checking", "loading"
+            ), false
+
+            applicationCache.addEventListener "error", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.error", "loading"
+            ), false
+
+            applicationCache.addEventListener "noupdate", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.noupdate", "loading"
+            ), false
+
+            applicationCache.addEventListener "downloading", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.downloading", "loading"
+            ), false
+
+            applicationCache.addEventListener "progress", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.progress", "loading"
+            ), false
+
+            applicationCache.addEventListener "updateready", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.updateready", "loading"
+            ), false
+
+            applicationCache.addEventListener "cached", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.cached", "loading"
+            ), false
+
+            applicationCache.addEventListener "obsolete", ((e) ->
+                Timeline.addRecord_EventDispatch e, "applicationCache.obsolete", "loading"
+            ), false
+
+        window.addEventListener "error", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.error"
+        ), false
+
+        window.addEventListener "hashchange", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.hashchange"
+        ), false
+
+        window.addEventListener "message", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.message"
+        ), false
+
+        window.addEventListener "offline", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.offline"
+        ), false
+
+        window.addEventListener "online", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.online"
+        ), false
+
+        window.addEventListener "scroll", ((e) ->
+            Timeline.addRecord_EventDispatch e, "window.scroll"
+        ), false
+
+    #---------------------------------------------------------------------------
+    @installFunctionWrappers: ->
+        window.clearInterval        = wrapped_clearInterval
+        window.clearTimeout         = wrapped_clearTimeout
+        window.setTimeout           = wrapped_setTimeout
+        window.setInterval          = wrapped_setInterval
+        window.XMLHttpRequest::open = wrapped_XMLHttpRequest_open
+        window.XMLHttpRequest       = wrapped_XMLHttpRequest
+
+
+#-------------------------------------------------------------------------------
+addStackTrace = (record, skip) ->
+      skip = 1 unless skip
+      trace = new StackTrace(arguments).trace
+      record.stackTrace = []
+      i = skip
+
+      while i < trace.length
+          record.stackTrace.push
+              functionName: trace[i]
+              scriptName:   ""
+              lineNumber:   ""
+          i++
+
+#-------------------------------------------------------------------------------
+wrapped_setInterval = (code, interval) ->
+      code = instrumentedTimerCode(code, interval, false)
+
+      id = Native.setInterval(code, interval)
+      code.__timerId = id
+      addTimer id, interval, false
+
+      id
+
+#-------------------------------------------------------------------------------
+wrapped_setTimeout = (code, delay) ->
+      code = instrumentedTimerCode(code, delay, true)
+
+      id = Native.setTimeout(code, delay)
+      code.__timerId = id
+      addTimer id, delay, true
+
+      id
+
+#-------------------------------------------------------------------------------
+wrapped_clearInterval = (id) ->
+      result = Native.clearInterval(id)
+      removeTimer id, false
+
+      result
+
+#-------------------------------------------------------------------------------
+wrapped_clearTimeout = (id) ->
+      result = Native.clearTimeout(id)
+      removeTimer id, true
+
+      result
+
+#-------------------------------------------------------------------------------
+addTimer = (id, timeout, singleShot) ->
+      timerSet = (if singleShot then TimerTimeouts else TimerIntervals)
+
+      timerSet[id] =
+          id: id
+          timeout: timeout
+          singleShot: singleShot
+
+      Timeline.addRecord_TimerInstall id, timeout, singleShot
+
+#-------------------------------------------------------------------------------
+removeTimer = (id, singleShot) ->
+      timerSet = (if singleShot then TimerTimeouts else TimerIntervals)
+      timer = timerSet[id]
+      return unless timer
+
+      Timeline.addRecord_TimerRemove id, timer.timeout, singleShot
+      delete timerSet[id]
+
+#-------------------------------------------------------------------------------
+instrumentedTimerCode = (code, timeout, singleShot) ->
+      return code unless typeof (code) == "function"
+
+      instrumentedCode = ->
+          result = code()
+          id = arguments.callee.__timerId
+          Timeline.addRecord_TimerFire id, timeout, singleShot
+          result
+
+      instrumentedCode
+
+#-------------------------------------------------------------------------------
+wrapped_XMLHttpRequest = () ->
+      xhr = new Native.XMLHttpRequest()
+      IDGenerator.getId xhr
+      xhr.addEventListener "readystatechange", getXhrEventHandler(xhr), false
+      xhr
+
+wrapped_XMLHttpRequest.UNSENT           = 0
+wrapped_XMLHttpRequest.OPENED           = 1
+wrapped_XMLHttpRequest.HEADERS_RECEIVED = 2
+wrapped_XMLHttpRequest.LOADING          = 3
+wrapped_XMLHttpRequest.DONE             = 4
+
+#-------------------------------------------------------------------------------
+wrapped_XMLHttpRequest_open = () ->
+      xhr = this
+
+      xhr.__weinre_method = arguments[0]
+      xhr.__weinre_url = arguments[1]
+      result = Native.XMLHttpRequest_open.apply(xhr, [].slice.call(arguments))
+
+      result
+
+#-------------------------------------------------------------------------------
+getXhrEventHandler = (xhr) ->
+      (event) ->
+          Timeline.addRecord_XHRReadyStateChange xhr.__weinre_method, xhr.__weinre_url, IDGenerator.getId(xhr), xhr
+
+#-------------------------------------------------------------------------------
+Timeline.installGlobalListeners()
+Timeline.installFunctionWrappers()
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.coffee b/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.coffee
new file mode 100644
index 0000000..e638355
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.coffee
@@ -0,0 +1,27 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre         = require('../common/Weinre')
+WiDatabaseImpl = require('./WiDatabaseImpl')
+Console        = require('./Console')
+
+#-------------------------------------------------------------------------------
+module.exports = class WeinreExtraClientCommandsImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    getDatabases: (callback) ->
+        return unless callback
+        result = WiDatabaseImpl.getDatabases()
+
+        Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
+
diff --git a/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.coffee b/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.coffee
new file mode 100644
index 0000000..65bb187
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.coffee
@@ -0,0 +1,40 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre   = require('../common/Weinre')
+Callback = require('../common/Callback')
+Console  = require('./Console')
+
+#-------------------------------------------------------------------------------
+module.exports = class WeinreTargetEventsImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    connectionCreated: (clientChannel, targetChannel) ->
+        message = "weinre: target #{targetChannel} connected to client " + clientChannel
+        Weinre.logInfo message
+
+        oldValue = Console.useRemote(true)
+        Weinre.target.setDocument()
+        Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
+        Weinre.wi.DOMStorage.initialize()
+
+    #---------------------------------------------------------------------------
+    connectionDestroyed: (clientChannel, targetChannel) ->
+        message = "weinre: target #{targetChannel} disconnected from client " + clientChannel
+        Weinre.logInfo message
+
+        oldValue = Console.useRemote(false)
+
+    #---------------------------------------------------------------------------
+    sendCallback: (callbackId, result) ->
+        Callback.invoke callbackId, result
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiCSSImpl.coffee b/weinre.web/modules/weinre/target/WiCSSImpl.coffee
new file mode 100644
index 0000000..e12efde
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiCSSImpl.coffee
@@ -0,0 +1,122 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiCSSImpl
+
+    constructor: ->
+        @dummyComputedStyle = false
+
+    #---------------------------------------------------------------------------
+    getStylesForNode: (nodeId, callback) ->
+        result = {}
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        if @dummyComputedStyle
+            computedStyle =
+                styleId: null
+                properties: []
+                shorthandValues: []
+                cssProperties: []
+        else
+            computedStyle = Weinre.cssStore.getComputedStyle(node)
+
+        result =
+            inlineStyle:     Weinre.cssStore.getInlineStyle(node)
+            computedStyle:   computedStyle
+            matchedCSSRules: Weinre.cssStore.getMatchedCSSRules(node)
+            styleAttributes: Weinre.cssStore.getStyleAttributes(node)
+            pseudoElements:  Weinre.cssStore.getPseudoElements(node)
+            inherited:       []
+
+        parentNode = node.parentNode
+        while parentNode
+            parentStyle =
+                inlineStyle:     Weinre.cssStore.getInlineStyle(parentNode)
+                matchedCSSRules: Weinre.cssStore.getMatchedCSSRules(parentNode)
+
+            result.inherited.push parentStyle
+            parentNode = parentNode.parentNode
+
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getComputedStyleForNode: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        result = Weinre.cssStore.getComputedStyle(node)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getInlineStyleForNode: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        result = Weinre.cssStore.getInlineStyle(node)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getAllStyles: (callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    getStyleSheet: (styleSheetId, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    getStyleSheetText: (styleSheetId, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    setStyleSheetText: (styleSheetId, text, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    setPropertyText: (styleId, propertyIndex, text, overwrite, callback) ->
+        result = Weinre.cssStore.setPropertyText(styleId, propertyIndex, text, overwrite)
+        Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ] if callback
+
+    #---------------------------------------------------------------------------
+    toggleProperty: (styleId, propertyIndex, disable, callback) ->
+        result = Weinre.cssStore.toggleProperty(styleId, propertyIndex, disable)
+        Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ] if callback
+
+    #---------------------------------------------------------------------------
+    setRuleSelector: (ruleId, selector, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    addRule: (contextNodeId, selector, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    getSupportedCSSProperties: (callback) ->
+        Weinre.getCSSProperties()
+
+    #---------------------------------------------------------------------------
+    querySelectorAll: (documentId, selector, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiConsoleImpl.coffee b/weinre.web/modules/weinre/target/WiConsoleImpl.coffee
new file mode 100644
index 0000000..35ab3f7
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiConsoleImpl.coffee
@@ -0,0 +1,36 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiConsoleImpl
+
+    constructor: ->
+        @messagesEnabled = true
+
+    #---------------------------------------------------------------------------
+    setConsoleMessagesEnabled: (enabled, callback) ->
+        oldValue = @messagesEnabled
+        @messagesEnabled = enabled
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ oldValue ]
+
+    #---------------------------------------------------------------------------
+    clearConsoleMessages: (callback) ->
+        Weinre.wi.ConsoleNotify.consoleMessagesCleared()
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, []
+
+    #---------------------------------------------------------------------------
+    setMonitoringXHREnabled: (enabled, callback) ->
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, []
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiDOMImpl.coffee b/weinre.web/modules/weinre/target/WiDOMImpl.coffee
new file mode 100644
index 0000000..675b5b9
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiDOMImpl.coffee
@@ -0,0 +1,155 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiDOMImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    getChildNodes: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        children = Weinre.nodeStore.serializeNodeChildren(node, 1)
+        Weinre.wi.DOMNotify.setChildNodes nodeId, children
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    setAttribute: (elementId, name, value, callback) ->
+        element = Weinre.nodeStore.getNode(elementId)
+
+        unless element
+            Weinre.logWarning arguments.callee.signature + " passed an invalid elementId: " + elementId
+            return
+
+        element.setAttribute name, value
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    removeAttribute: (elementId, name, callback) ->
+        element = Weinre.nodeStore.getNode(elementId)
+
+        unless element
+            Weinre.logWarning arguments.callee.signature + " passed an invalid elementId: " + elementId
+            return
+
+        element.removeAttribute name
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    setTextNodeValue: (nodeId, value, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        node.nodeValue = value
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    getEventListenersForNode: (nodeId, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    copyNode: (nodeId, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    removeNode: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        unless node.parentNode
+            Weinre.logWarning arguments.callee.signature + " passed a parentless node: " + node
+            return
+
+        node.parentNode.removeChild node
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    changeTagName: (nodeId, newTagName, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    getOuterHTML: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        value = node.outerHTML
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ value ]
+
+    #---------------------------------------------------------------------------
+    setOuterHTML: (nodeId, outerHTML, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        node.outerHTML = outerHTML
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    addInspectedNode: (nodeId, callback) ->
+        Weinre.nodeStore.addInspectedNode nodeId
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    performSearch: (query, runSynchronously, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    searchCanceled: (callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    pushNodeByPathToFrontend: (path, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+    #---------------------------------------------------------------------------
+    resolveNode: (nodeId, callback) ->
+        result = Weinre.injectedScript.resolveNode(nodeId)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getNodeProperties: (nodeId, propertiesArray, callback) ->
+        propertiesArray = JSON.stringify(propertiesArray)
+        result = Weinre.injectedScript.getNodeProperties(nodeId, propertiesArray)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getNodePrototypes: (nodeId, callback) ->
+        result = Weinre.injectedScript.getNodePrototypes(nodeId)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    pushNodeToFrontend: (objectId, callback) ->
+        objectId = JSON.stringify(objectId)
+        result = Weinre.injectedScript.pushNodeToFrontend(objectId)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee b/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee
new file mode 100644
index 0000000..23ef0ad
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee
@@ -0,0 +1,137 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+Native = require('../common/Native')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiDOMStorageImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    getDOMStorageEntries: (storageId, callback) ->
+        storageArea = _getStorageArea(storageId)
+
+        unless storageArea
+            Weinre.logWarning arguments.callee.signature + " passed an invalid storageId: " + storageId
+            return
+
+        result = []
+        length = storageArea.length
+        i = 0
+
+        while i < length
+            key = storageArea.key(i)
+            val = storageArea.getItem(key)
+            result.push [ key, val ]
+            i++
+
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    setDOMStorageItem: (storageId, key, value, callback) ->
+        storageArea = _getStorageArea(storageId)
+
+        unless storageArea
+            Weinre.logWarning arguments.callee.signature + " passed an invalid storageId: " + storageId
+            return
+
+        result = true
+        try
+            if storageArea == window.localStorage
+                Native.LocalStorage_setItem key, value
+            else Native.SessionStorage_setItem key, value if storageArea == window.sessionStorage
+        catch e
+            result = false
+
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    removeDOMStorageItem: (storageId, key, callback) ->
+        storageArea = _getStorageArea(storageId)
+
+        unless storageArea
+            Weinre.logWarning arguments.callee.signature + " passed an invalid storageId: " + storageId
+            return
+
+        result = true
+        try
+            if storageArea == window.localStorage
+                Native.LocalStorage_removeItem key
+            else
+                if storageArea == window.sessionStorage
+                    Native.SessionStorage_removeItem key
+        catch e
+            result = false
+
+        Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ] if callback
+
+    #---------------------------------------------------------------------------
+    initialize: ->
+        if window.localStorage
+            Weinre.wi.DOMStorageNotify.addDOMStorage
+                id: 1
+                host: window.location.host
+                isLocalStorage: true
+
+            window.localStorage.setItem = (key, value) ->
+                Native.LocalStorage_setItem key, value
+                _storageEventHandler storageArea: window.localStorage
+
+            window.localStorage.removeItem = (key) ->
+                Native.LocalStorage_removeItem key
+                _storageEventHandler storageArea: window.localStorage
+
+            window.localStorage.clear = ->
+                Native.LocalStorage_clear()
+                _storageEventHandler storageArea: window.localStorage
+
+        if window.sessionStorage
+            Weinre.wi.DOMStorageNotify.addDOMStorage
+                id: 2
+                host: window.location.host
+                isLocalStorage: false
+
+            window.sessionStorage.setItem = (key, value) ->
+                Native.SessionStorage_setItem key, value
+                _storageEventHandler storageArea: window.sessionStorage
+
+            window.sessionStorage.removeItem = (key) ->
+                Native.SessionStorage_removeItem key
+                _storageEventHandler storageArea: window.sessionStorage
+
+            window.sessionStorage.clear = ->
+                Native.SessionStorage_clear()
+                _storageEventHandler storageArea: window.sessionStorage
+
+        document.addEventListener "storage", _storageEventHandler, false
+
+#-------------------------------------------------------------------------------
+_getStorageArea = (storageId) ->
+      if storageId == 1
+          return window.localStorage
+      else return window.sessionStorage if storageId == 2
+
+      null
+
+#-------------------------------------------------------------------------------
+_storageEventHandler = (event) ->
+      if event.storageArea == window.localStorage
+          storageId = 1
+      else if event.storageArea == window.sessionStorage
+          storageId = 2
+      else
+          return
+
+      Weinre.wi.DOMStorageNotify.updateDOMStorage storageId
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee b/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee
new file mode 100644
index 0000000..e458615
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee
@@ -0,0 +1,158 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre      = require('../common/Weinre')
+Native      = require('../common/Native')
+IDGenerator = require('../common/IDGenerator')
+SqlStepper  = require('./SqlStepper')
+
+id2db   = {}
+name2db = {}
+
+#-------------------------------------------------------------------------------
+module.exports = class WiDatabaseImpl
+
+    constructor: ->
+        return unless window.openDatabase
+        window.openDatabase = wrappedOpenDatabase
+
+    #---------------------------------------------------------------------------
+    @getDatabases: ->
+        result = []
+        for id of id2db
+            result.push id2db[id]
+        result
+
+    #---------------------------------------------------------------------------
+    getDatabaseTableNames: (databaseId, callback) ->
+        db = dbById(databaseId)
+        return unless db
+
+        stepper = SqlStepper([ getTableNames_step_1, getTableNames_step_2 ])
+        stepper.callback = callback
+        stepper.run db, logSqlError
+
+    #---------------------------------------------------------------------------
+    executeSQL: (databaseId, query, callback) ->
+        db = dbById(databaseId)
+        return unless db
+
+        txid = Weinre.targetDescription.channel + "-" + IDGenerator.next()
+
+        stepper = SqlStepper([ executeSQL_step_1, executeSQL_step_2 ])
+        stepper.txid     = txid
+        stepper.query    = query
+        stepper.callback = callback
+
+        stepper.run db, executeSQL_error
+
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ true, txid ]
+
+#-------------------------------------------------------------------------------
+logSqlError = (sqlError) ->
+      console.log "SQL Error #{sqlError.code}: " + sqlError.message
+
+#-------------------------------------------------------------------------------
+getTableNames_step_1 = () ->
+      @executeSql "SELECT name FROM sqlite_master WHERE type='table'"
+
+#-------------------------------------------------------------------------------
+getTableNames_step_2 = (resultSet) ->
+      rows = resultSet.rows
+      result = []
+
+      i = 0
+      while i < rows.length
+          name = rows.item(i).name
+          if name == "__WebKitDatabaseInfoTable__"
+            i++
+            continue
+
+          result.push name
+          i++
+
+      Weinre.WeinreTargetCommands.sendClientCallback @callback, [ result ]
+
+#-------------------------------------------------------------------------------
+executeSQL_step_1 = () ->
+      @executeSql @query
+
+#-------------------------------------------------------------------------------
+executeSQL_step_2 = (resultSet) ->
+      columnNames = []
+      values = []
+      rows = resultSet.rows
+      i = 0
+
+      while i < rows.length
+          row = rows.item(i)
+          if i == 0
+              for propName of row
+                  columnNames.push propName
+          j = 0
+
+          while j < columnNames.length
+              values.push row[columnNames[j]]
+              j++
+          i++
+
+      Weinre.wi.DatabaseNotify.sqlTransactionSucceeded @txid, columnNames, values
+
+#-------------------------------------------------------------------------------
+executeSQL_error = (sqlError) ->
+      error =
+          code: sqlError.code
+          message: sqlError.message
+
+      Weinre.wi.DatabaseNotify.sqlTransactionFailed @txid, error
+
+#-------------------------------------------------------------------------------
+wrappedOpenDatabase = (name, version, displayName, estimatedSize, creationCallback) ->
+      db = Native.openDatabase(name, version, displayName, estimatedSize, creationCallback)
+      dbAdd db, name, version
+      db
+
+#-------------------------------------------------------------------------------
+dbById = (id) ->
+      record = id2db[id]
+      return null unless record
+      record.db
+
+#-------------------------------------------------------------------------------
+dbRecordById = (id) ->
+      id2db[id]
+
+#-------------------------------------------------------------------------------
+dbRecordByName = (name) ->
+      name2db[name]
+
+#-------------------------------------------------------------------------------
+dbAdd = (db, name, version) ->
+      record = dbRecordByName(name)
+      return record if record
+
+      record = {}
+      record.id      = IDGenerator.next()
+      record.domain  = window.location.origin
+      record.name    = name
+      record.version = version
+      record.db      = db
+
+      id2db[record.id] = record
+      name2db[name] = record
+
+      payload = {}
+      payload.id      = record.id
+      payload.domain  = record.domain
+      payload.name    = name
+      payload.version = version
+      Weinre.WeinreExtraTargetEvents.databaseOpened payload
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiInspectorImpl.coffee b/weinre.web/modules/weinre/target/WiInspectorImpl.coffee
new file mode 100644
index 0000000..daef844
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiInspectorImpl.coffee
@@ -0,0 +1,51 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre   = require('../common/Weinre')
+Timeline = require('../target/Timeline')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiInspectorImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    reloadPage: (callback) ->
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+        window.location.reload()
+
+    #---------------------------------------------------------------------------
+    highlightDOMNode: (nodeId, callback) ->
+        node = Weinre.nodeStore.getNode(nodeId)
+
+        unless node
+            Weinre.logWarning arguments.callee.signature + " passed an invalid nodeId: " + nodeId
+            return
+
+        Weinre.elementHighlighter.on node
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    hideDOMNodeHighlight: (callback) ->
+        Weinre.elementHighlighter.off()
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    startTimelineProfiler: (callback) ->
+        Timeline.start()
+        Weinre.wi.TimelineNotify.timelineProfilerWasStarted()
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+    #---------------------------------------------------------------------------
+    stopTimelineProfiler: (callback) ->
+        Timeline.stop()
+        Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
+        Weinre.WeinreTargetCommands.sendClientCallback callback if callback
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiRuntimeImpl.coffee b/weinre.web/modules/weinre/target/WiRuntimeImpl.coffee
new file mode 100644
index 0000000..14c2e77
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiRuntimeImpl.coffee
@@ -0,0 +1,49 @@
+
+#---------------------------------------------------------------------------------
+# weinre is available under *either* the terms of the modified BSD license *or* the
+# MIT License (2008). See http:#opensource.org/licenses/alphabetical for full text.
+#
+# Copyright (c) 2010, 2011 IBM Corporation
+#---------------------------------------------------------------------------------
+
+Weinre = require('../common/Weinre')
+
+#-------------------------------------------------------------------------------
+module.exports = class WiRuntimeImpl
+
+    constructor: ->
+
+    #---------------------------------------------------------------------------
+    evaluate: (expression, objectGroup, includeCommandLineAPI, callback) ->
+        result = Weinre.injectedScript.evaluate(expression, objectGroup, includeCommandLineAPI)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getCompletions: (expression, includeCommandLineAPI, callback) ->
+        result = Weinre.injectedScript.getCompletions(expression, includeCommandLineAPI)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    getProperties: (objectId, ignoreHasOwnProperty, abbreviate, callback) ->
+        objectId = JSON.stringify(objectId)
+        result = Weinre.injectedScript.getProperties(objectId, ignoreHasOwnProperty, abbreviate)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    setPropertyValue: (objectId, propertyName, expression, callback) ->
+        objectId = JSON.stringify(objectId)
+        result = Weinre.injectedScript.setPropertyValue(objectId, propertyName, expression)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+    #---------------------------------------------------------------------------
+    releaseWrapperObjectGroup: (injectedScriptId, objectGroup, callback) ->
+        result = Weinre.injectedScript.releaseWrapperObjectGroup(objectGroupName)
+        if callback
+            Weinre.WeinreTargetCommands.sendClientCallback callback, [ result ]
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)