Merge branch 'develop'
diff --git a/README.md b/README.md
index 1a98afb..6860620 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
 For more information on weinre, please visit
-[http://phonegap.github.com/weinre/](http://phonegap.github.com/weinre/)
\ No newline at end of file
+[https://github.com/callback/callback-weinre/](https://github.com/callback/callback-weinre/)
\ No newline at end of file
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/README.md b/weinre.build/README.md
index c4b08a4..e7a43c9 100644
--- a/weinre.build/README.md
+++ b/weinre.build/README.md
@@ -1,36 +1,29 @@
 building weinre
 ===============================================================================
 
-weinre is currently built using Eclipse.  You will need a fairly recent version of Eclipse with the Java IDE tooling installed to use it.
-
-weinre is made up of five Eclipse projects:
+The weinre source base consists of file folders:
 
 - `weinre.application`
 - `weinre.build`
 - `weinre.doc`
 - `weinre.server`
 - `weinre.web`
-   
-When initially loaded into Eclipse you will find many errors in the projects. This is due to the fact that libraries that weinre depends on have not yet been loaded into the workspace.  To load these libraries, run the Ant script `weinre.build/build.xml` from the weinre.build directory.
 
-Ensure the property `USE_JAVAC` is NOT set in the personal.properties file. If this property is set, then the java files in the projects will be compiled with javac instead of being assumed to be built with Eclipse, which may cause problems since Eclipse is also compiling the java files.
+weinre was originally built using Eclipse, and still maintains Eclipse meta-data so that it can be developed in Eclipse.  Each of the folders above maps into a separate project.  Note however that the Eclipse-y-ness of the weinre source base is no longer maintained.  Please open a bug if there are problems.
 
-When the Ant script completes successfully, a full build will have taken place.  The various red X's another issues from Eclipse should be gone. If not:
+Before running a build, you should copy the `weinre.build/sample.personal.properties` file to the file `weinre.build/personal.properties`, and then customize that file.
 
-- select all projects, right click context menu/Refresh
-- use the Eclipse menu item Project/Clean ... 
+weinre requires additional code to produce the final jar.  These dependencies will be downloaded the first time you run a build, and then won't be downloaded for subsequent builds.  You can explicitly get the dependencies by running the `weinre.build/get-vendor.xml` file in Ant, as follows:
 
-This should make everything right.
-
-You may want to set the Eclipse preference General/Workspace/Refresh automatically  to true (checked) so you don't have to Refresh and Clean.
+    ant -f get-vendor.xml
 
 Various transient directories in this project (weinre.build) will be created after the build.  They are set to not be stored in the SCM.  They include:
 
-- out
-- cached
-- tmp
-- vendor
-   
+- `out`
+- `cached`
+- `tmp`
+- `vendor`
+
 You can delete them whenever you wish, or use the `"clean"` target of the  `weinre.build/build.xml` file to delete them.  Deleting them will cause the build to take longer, to rebuild what you deleted.
 
 The `weinre.build/out` directory in particular contains the final build artifacts:
@@ -38,22 +31,17 @@
 -  `weinre.build/out/archives/weinre-doc-{version}.zip`
 
    contains the HTML doc for weinre
-   
+
 -  `weinre.build/out/archives/weinre-jar-{version}.zip`
-   
+
    contains the platform-portable weinre.jar file
-      
+
 -  `weinre.build/out/archives/weinre-mac-{version}.zip`
-   
+
    contains the Mac OS X weinre.app application
-      
+
 -  `weinre.build/out/archives/weinre-src-{version}.zip`
-   
+
    contains the source of the projects (copy of what's in the SCM)
- 
-To build while you are developing the weinre code, you can use the quicker-to-build `"build-dev"` target of `weinre.build/build.xml` . This will not build the jars or archives, just rebuilds the bits neccessary to run the server from the Eclipse workspace.  A shell script using a Python utility is provided called `"build-continuous.sh"` which you can run and it will run the `"build-dev"` target whenever a relevant source file has changed in the workspace.
 
-building without eclipse
--------------------------------------------------------------------------------
-
-Ensure the property `USE_JAVAC` is set in the `personal.properties` file. If this property is set, then the java files in the projects will be compiled with javac instead of being assumed to be built with Eclipse.
+To build while you are developing the weinre code, you can use the quicker-to-build `"build-dev"` target of `weinre.build/build.xml` . This will not build the jars or archives, just rebuilds the bits necessary to run the server transient output directories.
diff --git a/weinre.build/build-continuous.sh b/weinre.build/build-continuous.sh
index 7092b1a..8d451a5 100755
--- a/weinre.build/build-continuous.sh
+++ b/weinre.build/build-continuous.sh
@@ -18,7 +18,7 @@
 # run-when-changed: https://gist.github.com/240922
 #-------------------------------------------------------------
 
-run-when-changed "ant build-dev" \
+run-when-changed "ant build" \
   ../weinre.application \
   ../weinre.build/*.xml \
   ../weinre.build/*.properties \
diff --git a/weinre.build/build.properties b/weinre.build/build.properties
index 1f3fdbf..66e4d91 100644
--- a/weinre.build/build.properties
+++ b/weinre.build/build.properties
@@ -1,14 +1,14 @@
 # ---
 # 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 version
 #-----------------------------------------------------------
-WEINRE_VERSION: 1.5.0
+WEINRE_VERSION: 1.6.0
 
 #-----------------------------------------------------------
 # some common locations used in the ant scripts
diff --git a/weinre.build/build.xml b/weinre.build/build.xml
index 1826ebd..7e72ae8 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,13 @@
     <target name="clean">
         <delete dir="${OUT}"/>
         <mkdir  dir="${OUT}"/> <!-- this is an Eclipse source folder! -->
-        
+
         <delete dir="${TMP}"/>
         <delete dir="${CACHED}"/>
         <delete dir="${VENDOR}"/>
+        <delete dir="node_modules"/>
     </target>
-    
+
     <!-- ============================================================
          development-time build
          ============================================================ -->
@@ -66,14 +67,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 +86,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"/>
@@ -96,11 +97,11 @@
          compile java code
          ============================================================ -->
 
-    <target name="compile-java" if="USE_JAVAC">
+    <target name="compile-java" unless="USE_ECLIPSE">
         <delete dir="../${PROJECT_SERVER}/bin"/>
         <mkdir  dir="../${PROJECT_SERVER}/bin"/>
 
-        <javac 
+        <javac
             srcdir            = "../${PROJECT_SERVER}/src"
             destdir           = "../${PROJECT_SERVER}/bin"
             debug             = "true"
@@ -113,11 +114,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 +130,9 @@
             <classpath path="../${PROJECT_BUILD}/vendor/swt/cocoa-macosx-x86_64/swt.jar"/>
             <compilerarg value="-Xlint"/>
         </javac>
-        
+
     </target>
-        
+
     <!-- ============================================================
          build vendor libs
          ============================================================ -->
@@ -139,7 +140,7 @@
     <target name="build-vendor" unless="VENDOR_FOUND">
         <ant antfile="get-vendor.xml"/>
     </target>
-        
+
     <!-- ============================================================
          basic web resources
          ============================================================ -->
@@ -147,22 +148,33 @@
     <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  file="${TMP}/modules"/>
-            <arg  file="../${PROJECT_WEB}/modules"/>
+        <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>
-            
-        <copy todir="${TMP}/modules">
-            <fileset file="${VENDOR}/scooj/scooj.js"/>
-        </copy>
-        
+
+        <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>
+
         <echo message="transportd-izing modules"/>
         <exec executable="python" failonerror="true" failifexecutionfails="true">
             <arg  file="vendor/modjewel/module2transportd.py"/>
@@ -175,11 +187,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 +205,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}"/>
@@ -220,20 +232,21 @@
                 <filter token="WEBKIT_VERSION"             value="${WEBKIT_VERSION}"/>
                 <filter token="SWT_VERSION"                value="${SWT_VERSION}"/>
                 <filter token="JETTY_VERSION"              value="${JETTY_VERSION}"/>
-                <filter token="SCOOJ_VERSION"              value="${SCOOJ_VERSION}"/>
                 <filter token="MODJEWEL_VERSION"           value="${MODJEWEL_VERSION}"/>
                 <filter token="JAVAX_SERVLET_VERSION"      value="${JAVAX_SERVLET_VERSION}"/>
                 <filter token="JAVAX_SERVLET_VERSION_IMPL" value="${JAVAX_SERVLET_VERSION_IMPL}"/>
             </filterset>
         </copy>
 
+<!--
         <echo message="building add-css-properties.js"/>
         <exec executable="python" failonerror="true" failifexecutionfails="true">
             <arg  file="scripts/build-css-properties.py"/>
             <arg value="vendor/webkit/WebCore/CSSPropertyNames.in"/>
             <arg value="${WEB}/add-css-properties.js"/>
         </exec>
-        
+-->
+
     </target>
 
     <!-- ============================================================
@@ -243,29 +256,29 @@
     <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}"/>
-        <build-html name="License"      oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
-        <build-html name="Running"      oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
-        <build-html name="MultiUser"    oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
-        <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}"/>
-        
+
+        <build-html name="Building"      title="Building"       oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="Home"          title="Home"           oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="Installing"    title="Installing"     oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="License"       title="License"        oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="Running"       title="Running"        oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="MultiUser"     title="Muli-User"      oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="Security"      title="Security"       oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="UserInterface" title="User Interface" oDir="${WEB}/doc" iDir="../${PROJECT_DOC}"/>
+        <build-html name="ChangeLog"     title="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 +288,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 +311,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 +342,23 @@
          ============================================================ -->
 
     <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 +371,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 +425,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 +450,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 +459,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 +499,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 +515,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"/>
@@ -517,11 +529,11 @@
         <mkdir  dir="${OUT}/files/weinre-src"/>
         <copy todir="${OUT}/files/weinre-src" file="${OUT}/build-info.txt" />
         <copy todir="${OUT}/files/weinre-src/weinre.application"> <fileset dir="../weinre.application" excludes="**/bin/"/></copy>
-        <copy todir="${OUT}/files/weinre-src/weinre.build">       <fileset dir="../weinre.build"       excludes="**/bin/, **/out/, **/cached/, **/vendor/, **/tmp/"/></copy>
+        <copy todir="${OUT}/files/weinre-src/weinre.build">       <fileset dir="../weinre.build"       excludes="**/bin/, **/out/, **/cached/, **/vendor/, **/tmp/, **/node_modules/"/></copy>
         <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 +541,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 +571,7 @@
     <!-- ============================================================
          macro to compile IDL to JSON
          ============================================================ -->
-        
+
     <macrodef name="simple-idl2json">
        <attribute name="name"/>
        <attribute name="iDir"/>
@@ -575,13 +587,14 @@
            </exec>
        </sequential>
     </macrodef>
-                    
+
     <!-- ============================================================
          macro to compile HTML
          ============================================================ -->
-        
+
     <macrodef name="build-html">
        <attribute name="name"/>
+       <attribute name="title"/>
        <attribute name="iDir"/>
        <attribute name="oDir"/>
        <element name="args" optional="true" implicit="true"/>
@@ -593,13 +606,13 @@
                <filelist dir="@{iDir}" files="boilerplate-trailer.html.txt"/>
                <filterchain>
                   <replacetokens>
-                    <token key="PAGE_NAME" value="@{name}"/>
+                    <token key="PAGE_NAME" value="@{title}"/>
                   </replacetokens>
                 </filterchain>
            </concat>
            <echo>generated @{oDir}/@{name}.html</echo>
        </sequential>
     </macrodef>
-                    
+
     <!-- ============================================================ -->
 </project>
diff --git a/weinre.build/debug.sh b/weinre.build/debug.sh
index 48774e1..3542f2d 100755
--- a/weinre.build/debug.sh
+++ b/weinre.build/debug.sh
@@ -10,6 +10,4 @@
 	-Dfile.encoding=UTF-8 \
 	-classpath $CP \
 	weinre.server.Main \
-	--verbose      true \
 	--deathTimeout 120
-	
\ No newline at end of file
diff --git a/weinre.build/get-vendor.xml b/weinre.build/get-vendor.xml
index 4e2b477..259dfaf 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}"/>
 
@@ -25,21 +25,21 @@
         <antcall target="get-cli"/>
         <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 +47,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 +72,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 +81,105 @@
                 <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 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="--non-interactive"/>
+            <arg value="--trust-server-cert"/>
             <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="--non-interactive"/>
+            <arg value="--trust-server-cert"/>
             <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="--non-interactive"/>
+            <arg value="--trust-server-cert"/>
             <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 +188,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/sample.personal.properties b/weinre.build/sample.personal.properties
index 82e22f8..22f7204 100644
--- a/weinre.build/sample.personal.properties
+++ b/weinre.build/sample.personal.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) 2010, 2011 IBM Corporation
 # ---
 
@@ -9,26 +9,24 @@
 # example of a personal.properties file
 #------------------------------------------------------------------------------
 # Create a file called personal.properties in this directory, and it will
-# you to override property settings during the build.  
-# 
+# you to override property settings during the build.
+#
 # The personal.properties file is set to not be stored in the SCM.
 #------------------------------------------------------------------------------
 
+#------------------------------------------------------------------------------
+# your name, which will be included in the build artifacts
+#------------------------------------------------------------------------------
 BUILDER:      Your Name in Lights
 
-USE_GROWL:    true
-
-USE_JAVAC:    false
+#------------------------------------------------------------------------------
+# Set to anything to enable the build posting a "done" message to Growl.
+# Delete or comment out to not use Growl.
+#------------------------------------------------------------------------------
+USE_GROWL:    yup
 
 #------------------------------------------------------------------------------
-# the libraries commented out below are particularly slow to get from their
-# originating locations, and are good to cache locally.  The definitions 
-# override those in vendor.properties
+# Set to anything to enable compiling the Java code with Eclipse.
+# Delete or comment out to compile the Java code with javac.
 #------------------------------------------------------------------------------
-
-#CACHED_LIBRARIES:          file:///Users/me/Sites/weinre-cached-libraries
-
-#SWT_URL_PREFIX:            ${CACHED_LIBRARIES}
-#JETTY_URL_PREFIX:          ${CACHED_LIBRARIES}/jetty
-#JAVAX_SERVLET_URL_PREFIX:  ${CACHED_LIBRARIES}/jetty
-#JSON4J_URL_PREFIX:         ${CACHED_LIBRARIES}/wink
+#USE_ECLIPSE:  yup
diff --git a/weinre.build/scripts/build-client-html.py b/weinre.build/scripts/build-client-html.py
index 690572a..a06fc05 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'
@@ -54,29 +54,28 @@
                 '<script type="text/javascript" src="weinre/hacks.js"></script>\n',
                 '<script type="text/javascript" src="../modjewel-require.js"></script>\n',
                 '<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 +83,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/build-target-scripts.py b/weinre.build/scripts/build-target-scripts.py
index c74c2e7..64d1923 100644
--- a/weinre.build/scripts/build-target-scripts.py
+++ b/weinre.build/scripts/build-target-scripts.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
 # ---
 
@@ -15,80 +15,92 @@
 
 #--------------------------------------------------------------------
 def main():
-    
+
     #----------------------------------------------------------------
-    if len(sys.argv) < 4:
-        error("expecting parameters piecesHtmlFile srcDir outputDir")
-    
-    iFileName   = sys.argv[1]
-    srcDirName  = sys.argv[2]
-    oDirName    = sys.argv[3]
-    
-    if not os.path.exists(iFileName):   error("input file not found: '" + iFileName + "'")
+    if len(sys.argv) < 3:
+        error("expecting parameters srcDir outputDir")
+
+    srcDirName  = sys.argv[1]
+    oDirName    = sys.argv[2]
+
     if not os.path.exists(srcDirName):  error("source directory not found: '" + srcDirName + "'")
     if not os.path.isdir(srcDirName):   error("source directory not a directory: '" + srcDirName + "'")
     if not os.path.exists(oDirName):    error("output directory not found: '" + oDirName + "'")
     if not os.path.isdir(oDirName):     error("output directory not a directory: '" + oDirName + "'")
-    
-    #----------------------------------------------------------------
-    with open(iFileName, "r") as iFile:
-        lines = iFile.readlines()
-        
+
     #----------------------------------------------------------------
     scripts     = []
     scriptNames = {}
     scriptSrc   = {}
     scriptMin   = {}
-    scriptSrcPattern = re.compile(r'.*?<script\s+src\s*=\s*"/(.*?)"\s*>\s*</script>.*')
-    
-    for line in lines:
-        match = scriptSrcPattern.match(line)
-        if not match: continue
-        
-        baseScriptFile = match.group(1)
+
+    includedFiles = []
+    includedFiles.append("modjewel-require.js")
+
+    entries = os.listdir(os.path.join(srcDirName, "weinre/common"))
+    for entry in entries:
+        includedFiles.append("weinre/common/%s" % entry)
+
+    entries = os.listdir(os.path.join(srcDirName, "weinre/target"))
+    for entry in entries:
+        includedFiles.append("weinre/target/%s" % entry)
+
+    includedFiles.append("interfaces/all-json-idls-min.js")
+
+    for includedFile in includedFiles:
+        baseScriptFile = includedFile
         scriptFile = os.path.join(srcDirName, baseScriptFile)
-        if scriptFile == "weinre-demo.js": continue
-        if not os.path.exists(scriptFile):   error("script file not found: '" + scriptFile + "'")
-        
+        if not os.path.exists(scriptFile):
+            error("script file not found: '" + scriptFile + "'")
+
         scripts.append(scriptFile)
         scriptNames[scriptFile] = baseScriptFile
 
         with open(scriptFile, "r") as iFile:
             scriptSrc[scriptFile] = iFile.read()
-            
+
         scriptMin[scriptFile] = min(scriptSrc[scriptFile])
 
         # log("read: %s" % scriptFile)
 
     #----------------------------------------------------------------
     oFileName = os.path.join(oDirName, "target-script.js")
-    writeMergedFile(oFileName, scripts, scriptNames, scriptSrc)
+    writeMergedFile(oFileName, scripts, scriptNames, scriptSrc, True)
 
     #----------------------------------------------------------------
     oFileName = os.path.join(oDirName, "target-script-min.js")
-    writeMergedFile(oFileName, scripts, scriptNames, scriptMin)
+    writeMergedFile(oFileName, scripts, scriptNames, scriptMin, False)
 
 #--------------------------------------------------------------------
-def writeMergedFile(oFileName, scripts, scriptNames, srcs):
-
+def writeMergedFile(oFileName, scripts, scriptNames, srcs, useEval):
     lines = []
     lines.append(";(function(){")
-    
-    for script in scripts:
-        lines.append("//==================================================")
-        lines.append("// file: " + scriptNames[script])
-        lines.append("//==================================================")
-        lines.append(srcs[script])
-        lines.append(";")
-        lines.append("")
 
-    lines.append("require('weinre/target/Target').getClass().main()")
+    for script in scripts:
+
+        src     = srcs[script]
+        srcName = scriptNames[script]
+        if not useEval:
+            lines.append("// %s" % srcName)
+            lines.append(src)
+            lines.append(";")
+        else:
+            src = "%s\n//@ sourceURL=%s" % (src, srcName)
+            lines.append(";eval(%s)" % json.dumps(src))
+
+        if srcName == "modjewel-require.js":
+            lines.append("require('modjewel').warnOnRecursiveRequire(true);")
+            if not useEval:
+                lines.append("")
+
+    lines.append("// require('weinre/common/Weinre').showNotImplemented();")
+    lines.append("require('weinre/target/Target').main()")
     lines.append("})();")
     targetScript = "\n".join(lines)
-    
+
     with open(oFileName, "w") as oFile:
         oFile.write(targetScript)
-    
+
     log("generated: %s" % oFileName)
 
 #--------------------------------------------------------------------
@@ -102,7 +114,7 @@
     script = patternCommentCPP.sub( "", script)
     script = patternIndent.sub(     "", script)
     script = patternBlankLine.sub(  "", script)
-    
+
     return script
 
 #--------------------------------------------------------------------
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..386e4c8 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
@@ -70,11 +70,10 @@
 SWT_URL_PREFIX:    http://archive.eclipse.org/eclipse/downloads/drops/R-${SWT_VERSION}
 
 #-----------------------------------------------------------
-# location of scooj
+# location of CoffeeScript
 #-----------------------------------------------------------
 
-SCOOJ_VERSION:    8f3534ba5a1b2fb8ea82a24eb0acedfcc6aca7b4
-SCOOJ_URL_PREFIX: https://github.com/pmuellr/scooj/raw/${SCOOJ_VERSION}
+COFFEESCRIPT_VERSION:    1.1.2
 
 #-----------------------------------------------------------
 # location of json2
diff --git a/weinre.doc/Building.body.html b/weinre.doc/Building.body.html
index 401fe8d..79ab67f 100644
--- a/weinre.doc/Building.body.html
+++ b/weinre.doc/Building.body.html
@@ -1,21 +1,14 @@
 <!--
  * 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
 -->
 
-<p>The source repository for weinre is maintained at 
-<a href="https://github.com/phonegap/weinre">GitHub</a>.
+<p>The source repository for weinre is maintained at
+<a href="https://github.com/callback/callback-weinre">GitHub</a>.
 
-<p>To build the current version of <span class="weinre">weinre</span>, you will 
-probably want to use Eclipse.  If that's not an option, there is some 
-experimental support in the main ant build script to compile the Java code 
-outside of Eclipse.  Explore the <tt>build.xml</tt> file to see how that's
-done.
-
-<p>At development time, <span class="weinre">weinre</span> consists of 5 Eclipse
-projects.  See the file <tt>README.md</tt> in the
-<tt>weinre.build</tt> project for more instructions on
+<p>At development time, <span class="weinre">weinre</span> consists of 5
+directories / Eclipse projects.  See the file <tt>README.md</tt> in the
+<tt>weinre.build</tt> directory for more instructions on
 how to perform a build.
-
diff --git a/weinre.doc/ChangeLog.body.html b/weinre.doc/ChangeLog.body.html
index 432c26c..a5f5318 100644
--- a/weinre.doc/ChangeLog.body.html
+++ b/weinre.doc/ChangeLog.body.html
@@ -1,14 +1,74 @@
 <!--
  * 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
 -->
 
 <!-- ======================================================================= -->
 <h2>Downloads</h2>
 
-<p><a href="https://github.com/phonegap/weinre/downloads">https://github.com/phonegap/weinre/downloads</a>
+<p><a href="https://github.com/callback/callback-weinre/downloads">https://github.com/callback/callback-weinre/downloads</a>
+
+<!-- ======================================================================= -->
+<h2>2011/10/28 - version 1.6.0</h2>
+
+<ul>
+
+<li><p>The Network panel has been added to weinre, which will display information
+for any XHRs that you issue.
+
+<li><p>Error handling for some built-in callbacks has been added.  The support
+is very similar to what's provided in the
+<a href="https://github.com/pmuellr/log-callback-error">log-callback-error project</a>,
+though the implementation is different.
+
+<li><p>The element highlighter has been modified to show brighter colors.
+The colors also now more closely match the Web Inspector colors.
+Currently, there are some artifacts in the corners in some cases, but it's not
+clear to me this is a huge problem - in fact, maybe it's a feachur.  ~shrug~
+As part of this clean up, I've made the element highlighter pluggable.  If you
+think you can do better, it should be pretty straight-forward to code up a
+new one.  Bring it on.
+
+<li><p>The JavaScript code for weinre has been converted from the "scoop" DSL
+format to CoffeeScript.  You no longer have a valid excuse for not hacking the
+code.
+
+<li><p>The "Test Drive" page of the doc has been removed and replaced with
+the new "User Interface" page.
+
+<li><p>weinre now adds an "error" listener to window.  For JavaScript engines
+that support "window onerror", you will now see a message logged to the console
+when an error occurs anywhere in your JavaScript code.  Unfortunately, just
+as support for "window onerror" is becoming available in JavaScript, the browsers
+are neutering it's support.  Depending on your browser, you may or may not
+see these errors reported, and even if you do, they may not give you <b>any</b>
+useful information.  Thanks to Ryan Seddon for supplying the following
+backgrounder links for your enjoyment:
+
+<ul>
+<li><a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-September/033252.html">http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-September/033252.html</a>
+<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=568564">https://bugzilla.mozilla.org/show_bug.cgi?id=568564</a>
+<li><a href="http://www.w3.org/Bugs/Public/show_bug.cgi?id=14177">http://www.w3.org/Bugs/Public/show_bug.cgi?id=14177</a>
+<li><a href="https://bugs.webkit.org/show_bug.cgi?id=70574">https://bugs.webkit.org/show_bug.cgi?id=70574</a>
+</ul>
+
+</ul>
+
+<p>issues resolved:
+<ul>
+<li> <a href="https://issues.apache.org/jira/browse/CB-1">   issue apache/callback issue 1</a> - update the doc
+<li> <a href="https://github.com/phonegap/weinre/issues/41"> issue phonegap/weinre 41</a> - add support for XHRs on Network panel
+<li> <a href="https://github.com/phonegap/weinre/issues/40"> issue phonegap/weinre 40</a> - clean up spurious messages in the server console
+<li> <a href="https://github.com/phonegap/weinre/issues/38"> issue phonegap/weinre 38</a> - cleanup some of the exception handler bits
+<li> <a href="https://github.com/phonegap/weinre/issues/37"> issue phonegap/weinre 37</a> - Timeout function does not pass arguments
+<li> <a href="https://github.com/phonegap/weinre/issues/35"> issue phonegap/weinre 35</a> - convert JavaScript in weinre from scoop to CoffeeScript
+<li> <a href="https://github.com/phonegap/weinre/issues/20"> issue phonegap/weinre 20</a> - add support for window.onerror
+<li> <a href="https://github.com/phonegap/weinre/issues/19"> issue phonegap/weinre 19</a> - 404 error on /client/index.html in Mac app (incomplete build?)
+<li> <a href="https://github.com/phonegap/weinre/issues/11"> issue phonegap/weinre 11</a> - provide better error handling - by hook or by crook
+<li> <a href="https://github.com/phonegap/weinre/issues/10"> issue phonegap/weinre 10</a> - element highlighter code in the target is just awful
+</ul>
 
 <!-- ======================================================================= -->
 <h2>2011/07/22 - version 1.5.0</h2>
@@ -23,22 +83,22 @@
 in other browsing sessions will not be displayed until they are first opened.
 
 <p>Clicking on a database table will dump the database contents to the panel.
-Clicking on the database itself will provide a prompter for you to run SQL 
-commands against that database.  For example, entering 
-<tt>"select * from YourTableNameHere"</tt> 
+Clicking on the database itself will provide a prompter for you to run SQL
+commands against that database.  For example, entering
+<tt>"select * from YourTableNameHere"</tt>
 will dump the table contents to the panel.
 
 <li><p>Some of the console functions, like <tt>console.log()</tt>, were not handling
 object arguments correctly.  You should now see a an object display.  For example,
-typing <tt>"window"</tt> or <tt>"console.log(window)"</tt> into the console 
+typing <tt>"window"</tt> or <tt>"console.log(window)"</tt> into the console
 will show you the same object display for the window.
 
-<li><p>The ant build script did not have the correctly-cased file names for some 
+<li><p>The ant build script did not have the correctly-cased file names for some
 of the files, and the build failed.  This has been fixed.  You should be able
 to do a build on Linux, and other case-sensitive file systems.
 
 <li><p>While weinre already removes some of the Web Inspector panels wholesale,
-there were still a number of meaningless gee-gaws on the panels that we do show.  
+there were still a number of meaningless gee-gaws on the panels that we do show.
 Those have been surgically removed with CSS.
 
 </ul>
diff --git a/weinre.doc/Home.body.html b/weinre.doc/Home.body.html
index 5cf5dc2..f5ef7de 100644
--- a/weinre.doc/Home.body.html
+++ b/weinre.doc/Home.body.html
@@ -2,110 +2,106 @@
 <!--
  * 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
 -->
 
-<p><span class="weinre">weinre</span> is <b>We</b>b <b>In</b>spector <b>Re</b>mote.
+<p><span class="weinre">weinre</span> is <b>WE</b>b <b>IN</b>spector <b>RE</b>mote.
 Pronounced like the word "winery".  Or maybe like the word "weiner".  Who knows,
 really.
 
-<p>Interesting places:
-<ul>
-<li>GitHub: <a href="https://github.com/phonegap/weinre">https://github.com/phonegap/weinre</a>
-<li>Google Group: <a href="http://groups.google.com/group/weinre">http://groups.google.com/group/weinre</a>
-<li>weinre documentation: <a href="http://phonegap.github.com/weinre">http://phonegap.github.com/weinre</a>
-<li>Google Chrome Developer Tools: <a href="http://code.google.com/chrome/devtools/docs/overview.html">http://code.google.com/chrome/devtools/docs/overview.html</a>
-</ul>
-
-<p>It's a debugger for web pages, like 
+<p><span class="weinre">weinre</span> is a debugger for web pages, like
 FireBug (for FireFox)
-and 
+and
 Web Inspector (for WebKit-based browsers), except it's designed
 to work <b>remotely</b>, and in particular, to <b>allow you debug web pages
 on a mobile device such as a phone</b>.
 
-<p>If you aren't familiar with FireBug or Web Inspector, 
+<p>Interesting places to visit:
+<table>
+<tr><td>- videos at YouTube:               <td><a href="http://www.youtube.com/results?search_query=weinre">http://www.youtube.com/results?search_query=weinre</a>
+<tr><td>- this documentation at GitHub:    <td><a href="http://callback.github.com/callback-weinre">http://callback.github.com/callback-weinre</a>
+<tr><td>- source at GitHub:                <td><a href="https://github.com/callback/callback-weinre">https://github.com/callback/callback-weinre</a>
+<tr><td>- discussion at Google Group:      <td><a href="http://groups.google.com/group/weinre">http://groups.google.com/group/weinre</a>
+</table>
+
+<p>More information on <span class="weinre">weinre</span>'s big brothers, Web Inspector / Google Chrome
+Developer Tools, is available at
+<a href="http://code.google.com/chrome/devtools/docs/overview.html">Google's Chrome Dev Tools pages</a>
+and
+<a href="http://developer.apple.com/library/safari/#documentation/appleapplications/Conceptual/Safari_Developer_Guide/DebuggingYourWebsite/DebuggingYourWebsite.html">Apple's Web Inspector pages</a>.
+
+<p>If you aren't familiar with FireBug or Web Inspector,
 <span class="weinre">weinre</span> isn't going to make too much sense to you.
 <span class="weinre">weinre</span> reuses the user interface code from the
 <a href="http://trac.webkit.org/wiki/WebInspector">Web Inspector project at WebKit</a>,
-so if you've used Safari's Web Inspector or Chrome's Developer Tools,  
+so if you've used Safari's Web Inspector or Chrome's Developer Tools,
 <span class="weinre">weinre</span> will be very familiar.  If you're not
-familiar with Web Inspector, the link above to Google Chrome Developer Tools
-provides some documentation Real Web Inspector, which <span class="weinre">weinre</span> is based on.
+familiar with Web Inspector, the links above to Google's and Apple's documentation
+will be useful.
 
-<!--
-<div class="note">
-<p>NOTE: Please note that <span class="weinre">weinre</span> is still under development and 
-is not fully operational.  
-The current version only supports the following functionality:
+<p>Here's an example session using <span class="weinre">weinre</span>:
 
-<ul class="spaced">
-<li>basic console interaction, to capture <tt>console.log()</tt> messages, and 
-interactively execute JavaScript in the web page
-<li>displaying the DOM in the elements panel
-</ul>
+<p><img src="images/weinre-demo.jpg">
 
-</div>
--->
+<p>Two screen captures are shown above.  On the left is the
+<span class="weinre">weinre</span> <b>client</b> application, displaying debug
+information.
+On the right is the <b>target</b> web page, running in Mobile Safari in the iOS Simulator.
 
-<p>Here's what you can do with it:
+<p>In normal usage, you will be running the <b>client</b> application in a browser
+on your desktop/laptop, and running a <b>target</b> web page on your mobile device.
 
-<p><img src="images/gmail-green-target-client.jpg">
-
-<p>Two screen captures are shown above.  
-
-<p>The screen capture on the left is from an 
-iPod Touch, running the Mobile Safari app, visiting a browser-based email web site
-you may be familiar with.
-
-<p>The screen capture on the right is the from a laptop computer, running the
-<span class="weinre">weinre</span> debug client, debugging the web page on the left.  The DOM being displayed
-in the elements panel is from the HTML being displayed in the web browser on the iPod Touch.
-
-<p>You might think that green background in the email client is a bit horrifying.
-It's not normally green.  I made it green by executing the following JavaScript code
-in the <span class="weinre">weinre</span> console area (as you can see above):
+<p>In the session above, the debugger is display the Elements panel, with the
+embedded console expanded in the bottom third of the window.  In the console
+area, we've entered two statements:
 
 <pre>
-document.body.style.background = "green"
+document.body.style.backgroundColor
+document.body.style.backgroundColor = "pink"
 </pre>
 
+<p>The result of running the first statement shows the value of the body's background style is not set.
+The second statement sets the body's background to pink.  That horrifying pink
+background is then rendered in the <b>target</b>'s window.
+
+<p>In addition, an <tt>h1</tt> element is selected in the upper third of the
+Elements panel, which causes the side panels on the right to be filled in with
+relevant information.  In this case, the matched CSS rules for that <tt>h1</tt>
+element are being shown.
+
 <p>If you are familiar with WebKit's Web Inspector,
 a partial list of differences between it and <span class="weinre">weinre</span>
 are listed below:
 
 <ul class="spaced">
 
-<li><span class="weinre">weinre</span> does not make use of any 'native' code, 
+<li><span class="weinre">weinre</span> does not make use of any 'native' code,
 it's all JavaScript (and currently Java code for the 'server').
 
-<li>Because <span class="weinre">weinre</span> doesn't use 'native' code, 
+<li>Because <span class="weinre">weinre</span> doesn't use 'native' code,
 it's functionality is limited.  For instance, source level debug of JavaScript
-is not possible.
-  
-<li>Because <span class="weinre">weinre</span> doesn't use 'native' code, 
+is not <span class="strike">possible</span>
+<a href="http://lexandera.com/aardwolf/">easy</a>.
+
+<li>Because <span class="weinre">weinre</span> doesn't use 'native' code,
 the debug target code will run on browsers
 without specialized debug support.  For instance, a browser running on your phone.
-  
-<li><span class="weinre">weinre</span> supports 'remote' interaction, 
-so you can run the debugger user interface on one 
+
+<li><span class="weinre">weinre</span> supports 'remote' interaction,
+so you can run the debugger user interface on one
 machine and can debug a web page running
 on another machine.  For instance, debug a web page displayed on your phone from your
 laptop.
-  
-<li>Because <span class="weinre">weinre</span> supports 'remote' interaction, 
+
+<li>Because <span class="weinre">weinre</span> supports 'remote' interaction,
 multiple debug clients can be debugging the same debug target at the same time.
 
 <li>One thing <b>not</b> different from Web Inspector is that the debug client
-user interface only runs on WebKit-based browsers.  Sorry folks.
+user interface only runs on WebKit-based browsers.
 
 </ul>
 
-<p>Find out more by clicking one of the links at the top or bottom of the page 
-- to see a walkthrough of
-how to use it, visit the <a href="TestDrive.html">Test Drive</a>.
-
 <!-- ======================================================== -->
 <h2>Supported Libraries and Platforms</h2>
 
@@ -127,7 +123,7 @@
 <li>versions of Prototype.js before version 1.7 are not supported,
 as they do not support the <tt>JSON.stringify()</tt> API correctly.  You
 will get an <tt>alert()</tt> in the web page you are debugging if you
-attempt to debug it with weinre and you are using an unsupported
+attempt to debug it with <span class="weinre">weinre</span> and you are using an unsupported
 version of Prototype.js.
 </ul>
 
@@ -137,7 +133,7 @@
 <ul>
 <li>weinre Mac application - Mac OS X 10.6 64-bit
 <li>Google Chrome 8.x
-<li>Apple Safari 5.x 
+<li>Apple Safari 5.x
 </ul>
 
 <h3>Platforms supported - debug target</h3>
diff --git a/weinre.doc/Installing.body.html b/weinre.doc/Installing.body.html
index a723711..b94638a 100644
--- a/weinre.doc/Installing.body.html
+++ b/weinre.doc/Installing.body.html
@@ -1,13 +1,13 @@
 <!--
  * 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
 -->
 
 <p><span class="weinre">weinre</span> is shipped as two archives
 available at
-<a href="https://github.com/phonegap/weinre/archives/master">https://github.com/phonegap/weinre/archives/master</a>
+<a href="https://github.com/callback/callback-weinre/archives/master">https://github.com/callback/callback-weinre/archives/master</a>
 
 <ul class="spaced">
 
@@ -19,6 +19,6 @@
 
 </ul>
 
-<p>Install the .jar and/or Mac OS X application wherever you wish.  
+<p>Install the .jar and/or Mac OS X application wherever you wish.
 At runtime, the .jar file is not sensive to the directory
 it is installed in, or the current directory when it's launched.
diff --git a/weinre.doc/License.body.html b/weinre.doc/License.body.html
index 2884bba..975309d 100644
--- a/weinre.doc/License.body.html
+++ b/weinre.doc/License.body.html
@@ -1,42 +1,39 @@
 <!--
  * 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
 -->
 
 <p>weinre contains code from the following other projects:
 
-<ul class="spaced"> 
+<ul class="spaced">
 
-<li>Files from <a href="http://webkit.org/">WebKit</a>.  
+<li>Files from <a href="http://webkit.org/">WebKit</a>.
 Web Inspector code.  Although WebKit in general contains both BSD and
 LGPL code, only BSD licensed code is used by weinre.
 
-<li>Files from <a href="http://www.eclipse.org/jetty/">Eclipse Jetty</a>.  
+<li>Files from <a href="http://www.eclipse.org/jetty/">Eclipse Jetty</a>.
 HTTP server.  License: ASLv2 and EPLv1
 
-<li>Files from <a href="http://www.eclipse.org/swt/">Eclipse SWT</a>.  
+<li>Files from <a href="http://www.eclipse.org/swt/">Eclipse SWT</a>.
 Platform UI toolkit.  License: EPLv1
 
-<li>Files from <a href="https://github.com/pmuellr/scooj">scooj</a>.  
-JavaScript OO wrapper.  License: MIT
-
-<li>Files from <a href="https://github.com/pmuellr/modjewel">modjewel</a>.  
+<li>Files from <a href="https://github.com/pmuellr/modjewel">modjewel</a>.
 CommonJS runtime for the browser.  License: MIT
 
-<li>Files from <a href="http://commons.apache.org/cli/">Apache Commons CLI</a>.  
+<li>Files from <a href="http://commons.apache.org/cli/">Apache Commons CLI</a>.
 Command-line parser.  License: ASLv2
 
-<li>Files from <a href="http://incubator.apache.org/wink/">Apache Wink</a>.  
+<li>Files from <a href="http://incubator.apache.org/wink/">Apache Wink</a>.
 JSON codecs.  License: ASLv2
 
 </ul>
 
-<p>The source for weinre itself is available under either 
-the terms of the modified BSD license 
-or 
-the MIT License. 
-See 
-<a href="http://opensource.org/licenses/alphabetical">http://opensource.org/licenses/alphabetical</a> 
+<p>The source for weinre itself is available under either
+the terms of the modified BSD license
+or
+the MIT License.
+See
+<a href="http://opensource.org/licenses/alphabetical">http://opensource.org/licenses/alphabetical</a>
 for full text.
diff --git a/weinre.doc/MultiUser.body.html b/weinre.doc/MultiUser.body.html
index 460367a..065d892 100644
--- a/weinre.doc/MultiUser.body.html
+++ b/weinre.doc/MultiUser.body.html
@@ -1,12 +1,12 @@
 <!--
  * 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
 -->
 
 <p><span class="weinre">weinre</span> was originally designed so that each user
-that wanted to use <span class="weinre">weinre</span> would run their own 
+that wanted to use <span class="weinre">weinre</span> would run their own
 <span class="weinre">weinre</span> server.  The system has since been enhanced
 to support muliple users using the same server.
 
@@ -17,17 +17,17 @@
 id will be used in the client and target URLs of <span class="weinre">weinre</span>
 to scope your clients and targets from the clients and targets of other users.
 
-<p>There is no password, just an id.  There is no real security here.  
+<p>There is no password, just an id.  There is no real security here.
 The id is a secret shared between your
 debug target and client, but it is sent in the clear when your target and
 client connect to the server.  If someone else knows your <b>id</b>, they can
 connect to clients or targets you are running with that <b>id</b>.
 
-<p>So don't give your <b>id</b> to anyone else that you don't want to share 
+<p>So don't give your <b>id</b> to anyone else that you don't want to share
 with.
 
 <p>To keep your <b>id</b>'s familar but non-guessable, you might use a string
-consisting of your typical userid, followed by a dash, followed by another 
+consisting of your typical userid, followed by a dash, followed by another
 string not likely to be guessed by other people.
 
 <p>If you are paranoid, you can change your <b>id</b> every time you start
@@ -37,7 +37,7 @@
 <h2>Using your id with the client</h2>
 
 <p>Typically when start a <span class="weinre">weinre</span> client session
-by visiting a URL like 
+by visiting a URL like
 
 <pre>
 http://some.server.xyz/client/
@@ -45,8 +45,8 @@
 
 <p>To start
 a client with a particular id, append the hash character (<code>#</code>) and
-the id you want to use the URL.  For instance, to connect with the id 
-<code>itsReallyMe</code>, launch the client with the URL 
+the id you want to use the URL.  For instance, to connect with the id
+<code>itsReallyMe</code>, launch the client with the URL
 
 <pre>
 http://some.server.xyz/client/#itsReallyMe
@@ -78,16 +78,16 @@
 <ul class="spaced">
 
 <li>The <span class="weinre">weinre</span> server always runs in multi-user
-mode.  If you don't otherwise specify an <b>id</b>, the value of 
+mode.  If you don't otherwise specify an <b>id</b>, the value of
 <code>anonymous</code> will be used for the <b>id</b>.
 
 <li>The <b>id</b> is passed in the HTTP body of a POST request during the
 initial connection of the target and client to the server, and is not sent
 thereafter.  Thus, it should remain out of view in server logs and the like,
-though it will be visible for anyone who has access to the contents of 
+though it will be visible for anyone who has access to the contents of
 HTTP request bodies.
 
-<li>There is no programmatic interface on the <span class="weinre">weinre</span>
+<li>There is no programmatic interface in <span class="weinre">weinre</span>
 to list <b>id</b>'s in current use, for security reasons.  From the Remote panel
 of weinre, each connected client and target have their channel and id's listed,
 if you can't remember what id you used with the client.
diff --git a/weinre.doc/Running.body.html b/weinre.doc/Running.body.html
index 8af91d8..6a63add 100644
--- a/weinre.doc/Running.body.html
+++ b/weinre.doc/Running.body.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
 -->
 
@@ -19,26 +19,26 @@
 <dl class="spaced">
 
 <dt>Debug Server
-<dd>This is the HTTP server that you run from the <tt>weinre.jar</tt> file 
+<dd>This is the HTTP server that you run from the <tt>weinre.jar</tt> file
 or the Mac application.  It's the HTTP server that's used by the
 <b>Debug Client</b> and <b>Debug Target</b>.
 
 <dt>Debug Client
-<dd>This is the Web Inspector user interface; the web page which 
-displays the Elements and Console panels, for instance.  
+<dd>This is the Web Inspector user interface; the web page which
+displays the Elements and Console panels, for instance.
 
 <dt>Debug Target
 <dd>This is your web page that you want to debug.  This
 name (Debug Target) is also used to refer to the machine
 running the browser displaying the web page.  Since a design
-point of <span class="weinre">weinre</span> is to allow 
+point of <span class="weinre">weinre</span> is to allow
 debugging applications on mobile devices, the debug
 target, when speaking of the machine, is your mobile
-device. 
+device.
 
 </dl>
 
-<p>Both the <b>Debug Client</b> and the <b>Debug Target</b> communicate to 
+<p>Both the <b>Debug Client</b> and the <b>Debug Target</b> communicate to
 the <b>Debug Server</b> via HTTP using <tt>XMLHttpRequest</tt> (XHR).
 
 <p>Typically, you run both the <b>Debug Client</b> and the <b>Debug Server</b>
@@ -47,7 +47,7 @@
 <p>When using the Mac application, the
 <b>Debug Client</b> and <b>Debug Server</b> are running
 in the same process.
-When you launch the application, 
+When you launch the application,
 the server starts, and then the browser included in the
 application is launched
 on the appropriate URL automagically.
@@ -75,7 +75,7 @@
 <!-- ======================================================== -->
 <h2>Running the debug server using the weinre.jar file</h2>
 
-<p>To run <span class="weinre">weinre</span> from the jar file, 
+<p>To run <span class="weinre">weinre</span> from the jar file,
 execute the following command:
 
 <pre>
@@ -101,11 +101,11 @@
 <div class="indent">
 <p>The ip address to bind the server to.
 <p>Default: <tt>localhost</tt>
- 
+
 <p>With the default of <tt>localhost</tt>, you won't be able to access
 the server from any machine other than the one the server is running on.  You will
 need to bind to another hostname / ip address to make the server accessible to other
-machines.  You can use the value <tt>-all-</tt> to indicate that you 
+machines.  You can use the value <tt>-all-</tt> to indicate that you
 want to bind to all interfaces available on the current machine.
 
 <p>Use <tt>ifconfig</tt> on Linux or the Mac, and <tt>ipconfig</tt>
@@ -120,7 +120,7 @@
 
 <li><tt>--reuseAddr [true | false]</tt>
 <div class="indent">
-<p>Sets the "reuseAddr" flag for Jetty.  
+<p>Sets the "reuseAddr" flag for Jetty.
 <p>Default: <tt>false</tt>
 
 <p>May be needed if you restart your server frequently.
@@ -133,10 +133,10 @@
 <p>Default: <tt>5</tt>
 
 <p>Can probably be set larger, but then your browser may
-time out.  Probably don't want to set it much lower than 
+time out.  Probably don't want to set it much lower than
 this as it will cause additional network traffic when
 no messages are being sent (it will cause more "empty"
-messages to be sent).  
+messages to be sent).
 </div>
 
 <li><tt>--deathTimeout [seconds]</tt>
@@ -152,10 +152,10 @@
 
 </ul>
 
-<p>You may also create a file named 
-<tt>server.properties</tt> in the 
+<p>You may also create a file named
+<tt>server.properties</tt> in the
 directory <tt>.weinre</tt> in your
-home directory (eg. 
+home directory (eg.
 <tt>~/.weinre/server.properties</tt>).
 The keys should be
 the same as the name of the options above,
@@ -166,17 +166,22 @@
 
 <pre>
 boundHost:    -all-
-httpPort:     8081 
-reuseAddr:    true 
-readTimeout:  1 
+httpPort:     8081
+reuseAddr:    true
+readTimeout:  1
 deathTimeout: 5
 </pre>
 
 <p>Command-line options override the options specified in the
-<tt>~/.weinre/server.properties</tt> file. 
+<tt>~/.weinre/server.properties</tt> file.
 
 <p>The server will run until you kill it.  Control-C will do the job.
 
+<p>Below is what you should see from the console after running
+<span class="weinre">weinre</span> from the <tt>weinre.jar</tt> file.
+
+<p><img src="images/shell.png">
+
 <!-- ======================================================== -->
 <h2>Running the debug server using the Mac application</h2>
 
@@ -199,8 +204,8 @@
 <!-- ======================================================== -->
 <h2>Running the debug server bound to something other than <tt>localhost</tt></h3>
 
-<p>Since the web page you want to debug (the debug target) is probably running on 
-a different machine (your mobile device) than the debug server (your desktop / laptop), 
+<p>Since the web page you want to debug (the debug target) is probably running on
+a different machine (your mobile device) than the debug server (your desktop / laptop),
 you <b>can't</b> use the
 default <tt>localhost</tt> value for the <tt>--boundHost</tt>
 option.  Instead, you will need to specify a host name / ip address
@@ -217,12 +222,12 @@
 using the URL <tt>http://localhost:[portNumber]/client/</tt>
 from the same machine the debug server is running on.  On that
 page, under <b>Server Properties</b>, is a list of bound hosts
-that the server believes it's available on.  
+that the server believes it's available on.
 
-<p>From the debug target device (eg, your mobile device), 
+<p>From the debug target device (eg, your mobile device),
 try accessing the server home page
 from all of those host names, using the URL
-<tt>http://[host name / ipaddress]:[portNumber]/</tt>.  
+<tt>http://[host name / ipaddress]:[portNumber]/</tt>.
 Once you find one
 that displays the server's home page correctly, remember
 that host name / ip address, we'll be using it in a minute.
@@ -245,21 +250,21 @@
 <p>To make your web page debuggable with <span class="weinre">weinre</span>,
 add the following line to your web page:
 <pre>
-&lt;script src="http://a.b.c:8081/target/target-script-min.js"&gt;&lt;/script&gt; 
+&lt;script src="http://a.b.c:8081/target/target-script-min.js"&gt;&lt;/script&gt;
 </pre>
 
 <p>You can now open a debug client by browsing to
-<tt>http://a.b.c:8081/client</tt> 
-(or probably 
+<tt>http://a.b.c:8081/client</tt>
+(or probably
 <tt>http://localhost:8081/client</tt>
-as well) on your desktop/laptop, 
-then reload your newly instrumented web page on your mobile device, 
+as well) on your desktop/laptop,
+then reload your newly instrumented web page on your mobile device,
 and see the two connect in the Remote panel of the debug client.
 
 <!-- ======================================================== -->
 <h2>Hardcoding host names / IP addresses in your web page?</h3>
 
-<p>Yeah, gross.  
+<p>Yeah, gross.
 
 <p>Besides being gross, there are a few practical problems with hardcoding the
 debug server address in your web page as well:
@@ -269,7 +274,7 @@
 <li>Your html file is probably stored in an SCM, and you
 don't really want to be storing that address in the SCM.
 It won't work for other people using that html file,
-for instance.  
+for instance.
 
 <p>But you don't want to be keeping the
 <tt>&lt;script src=""></tt> line in your SCM either.
@@ -277,13 +282,13 @@
 build process to add that line only for development
 builds, and presumably with a user-customizable URL.
 
-<li>The URL the debug server runs on may be 
-different depending on your location.  At work, you may use 
+<li>The URL the debug server runs on may be
+different depending on your location.  At work, you may use
 one IP address and host name; at home, you likely have a different
 IP address and host name.  You may not have a host name at all,
 just an IP address (which is fine - a host name isn't required,
 but usually makes things easier).  You may be running on a
-dynamically assigned IP address, in which case your IP 
+dynamically assigned IP address, in which case your IP
 changes every time you log into your network.
 
 <p>One way out of the dilemma is to use a dynamic
@@ -296,13 +301,13 @@
 can arrange to use the same host name for your debug server
 no matter what network you are connected to.
 
-</ul> 
+</ul>
 
 <!-- ======================================================== -->
 <h2>Using a bookmarklet</h2>
 
 <p>Some platforms support 'bookmarkets', which allow you to inject
-arbitrary JavaScript code into any web page running in your 
+arbitrary JavaScript code into any web page running in your
 browser.  Mobile Safari is one of these browsers.  It appears that
 the browser shipped with Android 2.2 and later also supports bookmarklets.
 
@@ -317,45 +322,3 @@
 <p>Once the bookmarklet is installed, you can debug any web page
 by visiting it, and selecting the bookmarklet from your bookmarks
 menu.
-
-<!-- ======================================================== -->
-<h2>Using the <tt>target/target-script-min.js</tt> file statically</h2>
-
-<p>If you'd like to use the debug target code statically, instead of
-downloading it from the server all the time, then you will need to do
-one more thing.  The debug target code computes the address of the
-debug server from the <tt>src</tt> attribute in the 
-<tt>&lt;script&gt;</tt> tag, and it won't
-be correct in the static case.  Instead you can set the
-following variable to be the URL of the debug server:
-
-<pre>
-window.WeinreServerURL
-</pre>
-
-<p>This URL overrides what's otherwise calculated from the debug target
-code itself.  You will need to set this value before the
-<tt>target/target-script-min.js</tt> script is loaded. 
-
-<!-- ======================================================== -->
-<h3>Future Enhancements</h3>
-
-<p>The calculation of the debug server url by the debug target is a bit fragile and
-ham-fisted.  We should look at additional ways to handle this.
-For instance, we could prompt the user for the debug server
-address, and then always using the debug target in a more static
-mode; eg, you'd include the <tt>target-script-min.js</tt> with all your
-other files in your application.
-
-<p>Running the debug server on the target device is another option.
-This shouldn't be a huge problem for Android, but is more of a problem
-on iOS.  By running the debug server on the target device, the 
-debug target can always connect to the debug server by using a
-<tt>localhost</tt> hostname.  The debug client, running on your
-desktop/laptop, would then need to connect to the target device
-to debug.  In practice this may be an easier scenario to work with,
-as the potentially changing IP address of the mobile device just
-means having to type a different URL in your web browser on your desktop / laptop
-when running the debug client. That's easier than hard-coding
-URLs in your web page, for instance.
- 
diff --git a/weinre.doc/Security.body.html b/weinre.doc/Security.body.html
index ab1d4ad..14e1ee3 100644
--- a/weinre.doc/Security.body.html
+++ b/weinre.doc/Security.body.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
 -->
 
@@ -16,10 +16,10 @@
 no level of authentication for requests.
 
 <p>The primary security exposure with <span class="weinre">weinre</span> is via
-the debug server.  
+the debug server.
 
 <p>Currently, the server only reads files from the <tt>weinre.jar</tt> file,
-and from the <tt>~/.weinre/</tt> directory (for property files).  
+and from the <tt>~/.weinre/</tt> directory (for property files).
 The only thing the server writes to is <tt>stdout</tt> and <tt>stderr</tt>.
 
 <p>If you use the default <tt>--boundHost</tt> option value of
@@ -27,16 +27,16 @@
 server can communicate with the debug server.  This probably isn't a big
 deal, since presumably you control the software running on that machine.
 
-<p>If you use a non-default <tt>--boundHost</tt> option value, 
+<p>If you use a non-default <tt>--boundHost</tt> option value,
 then <b>any software on any machine that can access that specified
 host can communicate with the debug server</b>.  This is a much bigger
 deal.
 
 <p>The most obvious exposure with using <tt>--boundHost</tt> and
-a specific hostname / ip address, is that any debug client or 
+a specific hostname / ip address, is that any debug client or
 debug target that can access that hostname / ip address can access
-the server.  For example, a rogue debug client could connect to 
-your debug target and fiddle about with it. 
+the server.  For example, a rogue debug client could connect to
+your debug target and fiddle about with it.
 
 <p>Other exposures include leaving a debug target injection
 script line (ie, <tt>&lt;script src="[...]/target/target-script.js"&gt;</tt>)
@@ -48,39 +48,6 @@
 
 <ul class="spaced">
 
-
-<li><b>use HTTPS with basic auth</b>
-
-<p>This shouldn't be a big problem with the debug client, but may be a pain in the ass
-since basic auth can be a pain in the ass (trying to log out of it).
-
-<p>On the debug target side, this is tougher, since the only HTTP access is done via
-XHR.  So, presumably, we will need to tack on the userid/password to XHR requests,
-unless the browsers do that for us.  That is, I assume the browsers don't throw up
-a password prompt when an XHR returns with an auth-required response code.
-
-<p>Storing userid/passwords on the client isn't going to be much fun.  How do we even
-get these from the user?
-
-<p>Note that there is also a 'server discovery' issue we may want to do something
-about.  That is, how does the debug target even know the URL of the server?
-If we want to allow the user to "enter it from the target", then entering the
-userid/password can fit into the same scheme, I guess.  
-
-<p>Will need some light-weight userid/password store for the server, if
-we do this.
- 
-<p>Will need to get the horrifying SSL keystore dance crap with Java
-in some kind of usable state.  Should we ship a working, basic self-signed keystore
-that we recommend user's replace?
- 
-<li><b>use HTTPS with OAuth</b>
-
-<p>Been a while, not sure if OAuth even helps with this,
-or how hard it would be to integrate support for it.
-
-<p>Same issue with HTTPS and the SSL keystore above.
-
-<li><b>other ideas?</b>
+<li>Let's chat
 
 </ul>
diff --git a/weinre.doc/UserInterface.body.html b/weinre.doc/UserInterface.body.html
new file mode 100644
index 0000000..b5a95cd
--- /dev/null
+++ b/weinre.doc/UserInterface.body.html
@@ -0,0 +1,234 @@
+<!--
+ * 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
+-->
+
+<p>This page describes the user interface provided by <span class="weinre">weinre</span>.
+
+<!-- ======================================================================= -->
+<h2>the server home page</h2>
+
+<p><img src="images/server-home.png">
+
+<p>The server home page is accessed at the root URL of the server, in this case
+<tt>http://localhost:8081/</tt>.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>This link will launch the
+<span class="weinre">weinre</span> client page, which is the debugger itself.
+Clicking this link will take you to the Remote panel, described below.
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>This is the URL of the script that you add to your
+web page to allow it to be debugged by the client.
+</table>
+
+<p>Additional links shown in this image are for this documentation, and some demo
+pages.
+
+<p>Further down on the page, not shown in the image, are:
+
+<ul>
+<li>The text of the <tt>&lt;script src=&gt;</tt> element you can copy and paste into your
+web page.
+<li>The source for a bookmarklet, to inject the debug code into any web
+page displayed in your browser.
+<li>Version numbers of <span class="weinre">weinre</span> and it's components.
+</ul>
+
+<!-- ======================================================================= -->
+<h2>the Remote panel</h2>
+
+<p><img src="images/panel-remote.png">
+
+<p>The Remote panel is the initial panel of <span class="weinre">weinre</span>,
+and displays some status regarding the server.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The top portion of all of the
+<span class="weinre">weinre</span> panels shows the panel switcher.  To switch
+to a different panel, click on the panel's button.  The current panel is displayed with
+a highlight, as shown above for the Remote panel.
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>The Targets section of this page lists targets that are currently connected
+to the server.  A target is a web page that you are debugging.
+In this case, there is one target connected the server.  Information
+about each target is displayed in the list, including host name / ip address, and
+the URL the target is displaying.
+</table>
+
+<p><table><tr><td><img src="images/circled-c.png">
+<td>The Clients section of this page lists the clients that are currently connected
+to the server.  A client is this user interface. Typically, there is just one - the client you are displaying
+at the time.
+</table>
+
+<p><table><tr><td><img src="images/circled-d.png">
+<td>The Server Properties section of this page displays the values of various server
+properties currently in effect.  Shown here are the hosts that are bound to the
+server - which host names / ip addresses that the server will respond to.
+</table>
+
+<p>The colors of the entries in the Targets and Clients list indicates their
+status.  Blue means the Target or Client is connected to the server, but not
+yet connected to a peer.  Green means the Target or Client is connected to a
+peer, and you can start debugging.  Red means the Target or Client has
+disconnected from the server, and will eventually fade away and be removed
+from the list.
+
+<p>In case there are multiple targets connected, you can switch which one you
+are debugging by clicking on the item.
+
+<!-- ======================================================================= -->
+<h2>the Elements panel</h2>
+
+<p><img src="images/panel-elements.png">
+
+<p>The Elements panel displays your HTML DOM in a tree fashion, and properties
+associated with selected elements.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The elements themselves may be expanded/collapsed via the disclosure buttons.
+Elements and text can be edited by double-clicking the item to be edited.
+Elements can also be deleted by selecting the element and then pressing the
+delete button.
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>The sidebar area displays information about the selected element.  In this case,
+the Styles sidebar is expanded, showing relevant CSS rules.  Some sidebars allow
+editing of the contents.  The Styles sidebar in particular allows you to add, edit
+and remove properties and values by double clicking on them.
+</table>
+
+<!-- ======================================================================= -->
+<h2>the Resources panel</h2>
+
+<p><img src="images/panel-resources.png">
+
+<p>The Resources panel shows WebSQL databases, and data stored in Local Storage
+and Session Storage.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The left hand side of the Resources panel is the navigator. Expanding
+the Databases entry will show all the Web SQL databases currently in use.
+Expanding a particular database will show all the tables in that database.
+
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>The right hand side is the editor/viewer for whatever is selected in the
+navigator pane.
+When a database is selected, a prompt will appear in the editor/viewer area
+where you can enter SQL statements to be executed.  The results are displayed
+inline.  When a table is selected, all the rows from the table will be displayed.
+When a Local Storage or Session Storage entry is selected, all the relevant keys
+and values will be displayed.  The keys and values in the Local Storage and
+Session Storage tables can be edited by double-clicking.
+</table>
+
+<p><table><tr><td><img src="images/circled-c.png">
+<td>The reload button will refresh the data from the target.
+</table>
+
+<!-- ======================================================================= -->
+<h2>the Network panel</h2>
+
+<p><img src="images/panel-network.png">
+
+<p>The Network panel shows you information regarding XHR requests that you've
+made.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>Each XHR will be shown in a separate row in the table.  Clicking the
+path name of the XHR request will show additional information for that
+request in the right side of the panel.
+To display the table again, click the (x)
+button in the left top corner of the information pane (not shown in the
+image above).
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>The "dots and lines" button can be used to change the table layout to
+show less information in a more compact format.
+</table>
+
+<p><table><tr><td><img src="images/circled-c.png">
+<td>The "not" button will clear the entries from the table.
+</table>
+
+<!-- ======================================================================= -->
+<h2>the Timeline panel</h2>
+
+<p><img src="images/panel-timeline.png">
+
+<p>The Timeline panel is used to show events that are occurring in the target.
+Currently only two types of events are shown: timers/intervals, and user-specified
+events.  Use <tt>console.markTimeline("A Label")</tt> to add a user-specified
+event to the timeline with the specified label.
+
+<p>Note that timing events are not collected unless you enable the "record"
+button (see below).
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The left hand pane of the Timeline panel shows the events that have occurred.
+Hovering over an entry will show additional detail about the event.
+</table>
+
+<p><table><tr><td><img src="images/circled-b.png">
+<td>The right hand pane of the Timeline panel shows the events as they occurred
+in time.  Some events have other events associated with them, which will be shown
+when the disclosure triangle is expanded.  The top-most time line of the right
+panel can have it's right and left hand edges moved to show a particular time
+range in more detail.
+</table>
+
+<p><table><tr><td><img src="images/circled-c.png">
+<td>The "dots and lines" button can be used to show and hide events that last
+less than a fixed time (currently 15ms).
+</table>
+
+<p><table><tr><td><img src="images/circled-d.png">
+<td>The "record" button is used to start or stop event collection.  When
+<span class="weinre">weinre</span> starts, the record button is off (black).
+When you click the record button to start collecting events, it will turn
+red.
+</table>
+
+<p><table><tr><td><img src="images/circled-e.png">
+<td>The "not" button will clear the current timing entries.
+</table>
+
+
+<!-- ======================================================================= -->
+<h2>the Console panel</h2>
+
+<p><img src="images/panel-console.png">
+
+<p>The Console panel is used to execute arbitrary JavaScript expressions/statements.  It
+also shows the output from various <tt>console</tt> methods, like
+<tt>console.log()</tt>.
+
+<p>In the example above, the JavaScript statements entered by the user are in
+blue, the output of the statements is just below it, and messages generated by
+<tt>console.log()</tt> are in black.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The "not" button will clear the console.
+</table>
+
+<p><img src="images/panel-remote-console.png">
+
+<p>The Console can also be embedded in every panel, by clicking the "console"
+button.
+
+<table><tr><td><img src="images/circled-a.png">
+<td>The "console" button will embed the console in the bottom half of the
+other panels, or hide it if it's currently embedded in the other panels.
+</table>
+
diff --git a/weinre.doc/boilerplate-header.html.txt b/weinre.doc/boilerplate-header.html.txt
index f237a28..73acd31 100644
--- a/weinre.doc/boilerplate-header.html.txt
+++ b/weinre.doc/boilerplate-header.html.txt
@@ -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
 -->
 
@@ -12,32 +12,18 @@
 <title>weinre - @PAGE_NAME@</title>
 <link rel="stylesheet" href="css/main.css" type="text/css">
 <link rel="shortcut icon" href="images/weinre-icon-64x64.png" />
-<script src="scripts/main.js"></script>
-<script type="text/javascript">
-
-  var _gaq = _gaq || [];
-  _gaq.push(['_setAccount', 'UA-736164-7']);
-  _gaq.push(['_trackPageview']);
-
-  (function() {
-    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-  })();
-
-</script>
 </head>
 
 <body>
 
-<a class="no-decoration" href="https://github.com/phonegap/weinre"><div class="ribbon">Fork weinre on GitHub</div></a>
+<a class="no-decoration" href="https://github.com/callback/callback-weinre"><div class="ribbon">Fork weinre on GitHub</div></a>
 
 <!-- ======================================================================= -->
 <h1 class="page-title">weinre - @PAGE_NAME@</h1>
 
 <div class="toc">
   <a href="Home.html">Home</a>
-- <a href="TestDrive.html">Test Drive</a>
+- <a href="UserInterface.html">User Interface</a>
 - <a href="Installing.html">Installing</a>
 - <a href="Running.html">Running</a>
 - <a href="MultiUser.html">Multi-User</a>
diff --git a/weinre.doc/boilerplate-trailer.html.txt b/weinre.doc/boilerplate-trailer.html.txt
index 7c782f5..f2244a0 100644
--- a/weinre.doc/boilerplate-trailer.html.txt
+++ b/weinre.doc/boilerplate-trailer.html.txt
@@ -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
 -->
 
@@ -11,7 +11,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 <p>
   <a href="Home.html">Home</a>
-- <a href="TestDrive.html">Test Drive</a>
+- <a href="UserInterface.html">User Interface</a>
 - <a href="Installing.html">Installing</a>
 - <a href="Running.html">Running</a>
 - <a href="MultiUser.html">Multi-User</a>
diff --git a/weinre.doc/css/main.css b/weinre.doc/css/main.css
index 850bd78..ba071e2 100644
--- a/weinre.doc/css/main.css
+++ b/weinre.doc/css/main.css
@@ -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
  */
 
@@ -17,8 +17,8 @@
     left:                   0;
     border-right:           thin solid #000;
     border-bottom:          thin solid #000;
-    -moz-border-radius-bottomright:     0.5em; 
-    -webkit-border-bottom-right-radius: 0.5em;   
+    -moz-border-radius-bottomright:     0.5em;
+    -webkit-border-bottom-right-radius: 0.5em;
     padding:                0.25em 0.5em;
     background-color:       #FF0;
     font-style:             italic;
@@ -33,6 +33,11 @@
 }
 
 /*-----------------------------------------------------------------*/
+.strike {
+    text-decoration:        line-through;
+}
+
+/*-----------------------------------------------------------------*/
 .weinre {
    color:                  #088;
    font-style:             italic;
@@ -123,9 +128,9 @@
     padding:               0.00em 0.5em;
     border-width:          1;
     border-style:          solid;
-    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;
 }
 
 /*-----------------------------------------------------------------*/
@@ -140,29 +145,29 @@
     margin-left:           1.4em;
     margin-right:          1.4em;
 	background:            #FFFFDF;
-	border-radius:         0.5em; 
-    -moz-border-radius:    0.5em; 
-    -webkit-border-radius: 0.5em;	
-	box-shadow:            0.3em 0.3em 0.3em 0em #DDD; 
-	-moz-box-shadow:       0.3em 0.3em 0.3em 0em #DDD; 
-	-webkit-box-shadow:    0.3em 0.3em 0.3em 0em #DDD; 
+	border-radius:         0.5em;
+    -moz-border-radius:    0.5em;
+    -webkit-border-radius: 0.5em;
+	box-shadow:            0.3em 0.3em 0.3em 0em #DDD;
+	-moz-box-shadow:       0.3em 0.3em 0.3em 0em #DDD;
+	-webkit-box-shadow:    0.3em 0.3em 0.3em 0em #DDD;
 }
 
 /*-----------------------------------------------------------------*/
 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;
 }
 
 /*-----------------------------------------------------------------*/
 kewl {
-	box-shadow:            0.3em 0.3em 0.3em 0em #DDD; 
-	-moz-box-shadow:       0.3em 0.3em 0.3em 0em #DDD; 
-	-webkit-box-shadow:    0.3em 0.3em 0.3em 0em #DDD; 
-	border-radius:         0.5em; 
-    -moz-border-radius:    0.5em; 
-    -webkit-border-radius: 0.5em;	
+	box-shadow:            0.3em 0.3em 0.3em 0em #DDD;
+	-moz-box-shadow:       0.3em 0.3em 0.3em 0em #DDD;
+	-webkit-box-shadow:    0.3em 0.3em 0.3em 0em #DDD;
+	border-radius:         0.5em;
+    -moz-border-radius:    0.5em;
+    -webkit-border-radius: 0.5em;
 }
diff --git a/weinre.doc/images/circled-a.png b/weinre.doc/images/circled-a.png
new file mode 100644
index 0000000..98c0558
--- /dev/null
+++ b/weinre.doc/images/circled-a.png
Binary files differ
diff --git a/weinre.doc/images/circled-b.png b/weinre.doc/images/circled-b.png
new file mode 100644
index 0000000..0d80f3c
--- /dev/null
+++ b/weinre.doc/images/circled-b.png
Binary files differ
diff --git a/weinre.doc/images/circled-c.png b/weinre.doc/images/circled-c.png
new file mode 100644
index 0000000..f876019
--- /dev/null
+++ b/weinre.doc/images/circled-c.png
Binary files differ
diff --git a/weinre.doc/images/circled-d.png b/weinre.doc/images/circled-d.png
new file mode 100644
index 0000000..8e58ca8
--- /dev/null
+++ b/weinre.doc/images/circled-d.png
Binary files differ
diff --git a/weinre.doc/images/circled-e.png b/weinre.doc/images/circled-e.png
new file mode 100644
index 0000000..f3a782a
--- /dev/null
+++ b/weinre.doc/images/circled-e.png
Binary files differ
diff --git a/weinre.doc/images/circled-f.png b/weinre.doc/images/circled-f.png
new file mode 100644
index 0000000..4998913
--- /dev/null
+++ b/weinre.doc/images/circled-f.png
Binary files differ
diff --git a/weinre.doc/images/circled-g.png b/weinre.doc/images/circled-g.png
new file mode 100644
index 0000000..888bcee
--- /dev/null
+++ b/weinre.doc/images/circled-g.png
Binary files differ
diff --git a/weinre.doc/images/circled-h.png b/weinre.doc/images/circled-h.png
new file mode 100644
index 0000000..174d731
--- /dev/null
+++ b/weinre.doc/images/circled-h.png
Binary files differ
diff --git a/weinre.doc/images/circled-i.png b/weinre.doc/images/circled-i.png
new file mode 100644
index 0000000..338c341
--- /dev/null
+++ b/weinre.doc/images/circled-i.png
Binary files differ
diff --git a/weinre.doc/images/circled-j.png b/weinre.doc/images/circled-j.png
new file mode 100644
index 0000000..6595d75
--- /dev/null
+++ b/weinre.doc/images/circled-j.png
Binary files differ
diff --git a/weinre.doc/images/gmail-green-target-client.jpg b/weinre.doc/images/gmail-green-target-client.jpg
deleted file mode 100644
index 778916a..0000000
--- a/weinre.doc/images/gmail-green-target-client.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/orig/panel-console.png b/weinre.doc/images/orig/panel-console.png
new file mode 100644
index 0000000..3a8d794
--- /dev/null
+++ b/weinre.doc/images/orig/panel-console.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-elements.png b/weinre.doc/images/orig/panel-elements.png
new file mode 100644
index 0000000..4b81020
--- /dev/null
+++ b/weinre.doc/images/orig/panel-elements.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-network.png b/weinre.doc/images/orig/panel-network.png
new file mode 100644
index 0000000..6cc5a59
--- /dev/null
+++ b/weinre.doc/images/orig/panel-network.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-remote-console.png b/weinre.doc/images/orig/panel-remote-console.png
new file mode 100644
index 0000000..14db266
--- /dev/null
+++ b/weinre.doc/images/orig/panel-remote-console.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-remote.png b/weinre.doc/images/orig/panel-remote.png
new file mode 100644
index 0000000..0a5374c
--- /dev/null
+++ b/weinre.doc/images/orig/panel-remote.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-resources.png b/weinre.doc/images/orig/panel-resources.png
new file mode 100644
index 0000000..2b1a2fc
--- /dev/null
+++ b/weinre.doc/images/orig/panel-resources.png
Binary files differ
diff --git a/weinre.doc/images/orig/panel-timeline.png b/weinre.doc/images/orig/panel-timeline.png
new file mode 100644
index 0000000..a5d7ff5
--- /dev/null
+++ b/weinre.doc/images/orig/panel-timeline.png
Binary files differ
diff --git a/weinre.doc/images/orig/server-home.png b/weinre.doc/images/orig/server-home.png
new file mode 100644
index 0000000..4a2628f
--- /dev/null
+++ b/weinre.doc/images/orig/server-home.png
Binary files differ
diff --git a/weinre.doc/images/orig/shell.png b/weinre.doc/images/orig/shell.png
new file mode 100644
index 0000000..d43f51c
--- /dev/null
+++ b/weinre.doc/images/orig/shell.png
Binary files differ
diff --git a/weinre.doc/images/panel-console.png b/weinre.doc/images/panel-console.png
new file mode 100644
index 0000000..5f43882
--- /dev/null
+++ b/weinre.doc/images/panel-console.png
Binary files differ
diff --git a/weinre.doc/images/panel-elements.png b/weinre.doc/images/panel-elements.png
new file mode 100644
index 0000000..c05defa
--- /dev/null
+++ b/weinre.doc/images/panel-elements.png
Binary files differ
diff --git a/weinre.doc/images/panel-network.png b/weinre.doc/images/panel-network.png
new file mode 100644
index 0000000..b2ddcae
--- /dev/null
+++ b/weinre.doc/images/panel-network.png
Binary files differ
diff --git a/weinre.doc/images/panel-remote-console.png b/weinre.doc/images/panel-remote-console.png
new file mode 100644
index 0000000..dcb9e9b
--- /dev/null
+++ b/weinre.doc/images/panel-remote-console.png
Binary files differ
diff --git a/weinre.doc/images/panel-remote.png b/weinre.doc/images/panel-remote.png
new file mode 100644
index 0000000..08f4749
--- /dev/null
+++ b/weinre.doc/images/panel-remote.png
Binary files differ
diff --git a/weinre.doc/images/panel-resources.png b/weinre.doc/images/panel-resources.png
new file mode 100644
index 0000000..e6f5bd2
--- /dev/null
+++ b/weinre.doc/images/panel-resources.png
Binary files differ
diff --git a/weinre.doc/images/panel-timeline.png b/weinre.doc/images/panel-timeline.png
new file mode 100644
index 0000000..f4136ab
--- /dev/null
+++ b/weinre.doc/images/panel-timeline.png
Binary files differ
diff --git a/weinre.doc/images/server-home.png b/weinre.doc/images/server-home.png
new file mode 100644
index 0000000..a52815f
--- /dev/null
+++ b/weinre.doc/images/server-home.png
Binary files differ
diff --git a/weinre.doc/images/shell.png b/weinre.doc/images/shell.png
new file mode 100644
index 0000000..d43f51c
--- /dev/null
+++ b/weinre.doc/images/shell.png
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-01-h.jpg b/weinre.doc/images/weinre-cli-01-h.jpg
deleted file mode 100644
index f4edc9a..0000000
--- a/weinre.doc/images/weinre-cli-01-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-01-p.jpg b/weinre.doc/images/weinre-cli-01-p.jpg
deleted file mode 100644
index f4edc9a..0000000
--- a/weinre.doc/images/weinre-cli-01-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-02-h.jpg b/weinre.doc/images/weinre-cli-02-h.jpg
deleted file mode 100644
index b083a76..0000000
--- a/weinre.doc/images/weinre-cli-02-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-02-p.jpg b/weinre.doc/images/weinre-cli-02-p.jpg
deleted file mode 100644
index edcefde..0000000
--- a/weinre.doc/images/weinre-cli-02-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-03-h.jpg b/weinre.doc/images/weinre-cli-03-h.jpg
deleted file mode 100644
index e8e4400..0000000
--- a/weinre.doc/images/weinre-cli-03-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-03-p.jpg b/weinre.doc/images/weinre-cli-03-p.jpg
deleted file mode 100644
index 52f2e73..0000000
--- a/weinre.doc/images/weinre-cli-03-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-04-h.jpg b/weinre.doc/images/weinre-cli-04-h.jpg
deleted file mode 100644
index da42f56..0000000
--- a/weinre.doc/images/weinre-cli-04-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-04-p.jpg b/weinre.doc/images/weinre-cli-04-p.jpg
deleted file mode 100644
index 49bd84d..0000000
--- a/weinre.doc/images/weinre-cli-04-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-05-h.jpg b/weinre.doc/images/weinre-cli-05-h.jpg
deleted file mode 100644
index 84f48a6..0000000
--- a/weinre.doc/images/weinre-cli-05-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-05-p.jpg b/weinre.doc/images/weinre-cli-05-p.jpg
deleted file mode 100644
index 84f48a6..0000000
--- a/weinre.doc/images/weinre-cli-05-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-06-h.jpg b/weinre.doc/images/weinre-cli-06-h.jpg
deleted file mode 100644
index 676fee1..0000000
--- a/weinre.doc/images/weinre-cli-06-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-06-p.jpg b/weinre.doc/images/weinre-cli-06-p.jpg
deleted file mode 100644
index d4d2b56..0000000
--- a/weinre.doc/images/weinre-cli-06-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-07-h.jpg b/weinre.doc/images/weinre-cli-07-h.jpg
deleted file mode 100644
index 2614375..0000000
--- a/weinre.doc/images/weinre-cli-07-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-cli-07-p.jpg b/weinre.doc/images/weinre-cli-07-p.jpg
deleted file mode 100644
index 2614375..0000000
--- a/weinre.doc/images/weinre-cli-07-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-con-01-h.jpg b/weinre.doc/images/weinre-con-01-h.jpg
deleted file mode 100644
index 2051127..0000000
--- a/weinre.doc/images/weinre-con-01-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-con-01-p.jpg b/weinre.doc/images/weinre-con-01-p.jpg
deleted file mode 100644
index 35ec12e..0000000
--- a/weinre.doc/images/weinre-con-01-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-con-02-h.jpg b/weinre.doc/images/weinre-con-02-h.jpg
deleted file mode 100644
index a7620e3..0000000
--- a/weinre.doc/images/weinre-con-02-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-con-02-p.jpg b/weinre.doc/images/weinre-con-02-p.jpg
deleted file mode 100644
index a7620e3..0000000
--- a/weinre.doc/images/weinre-con-02-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-demo.jpg b/weinre.doc/images/weinre-demo.jpg
new file mode 100644
index 0000000..efd5829
--- /dev/null
+++ b/weinre.doc/images/weinre-demo.jpg
Binary files differ
diff --git a/weinre.doc/images/weinre-frn-01-h.jpg b/weinre.doc/images/weinre-frn-01-h.jpg
deleted file mode 100644
index 82ad660..0000000
--- a/weinre.doc/images/weinre-frn-01-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-frn-01-p.jpg b/weinre.doc/images/weinre-frn-01-p.jpg
deleted file mode 100644
index f631e06..0000000
--- a/weinre.doc/images/weinre-frn-01-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-tar-01-h.jpg b/weinre.doc/images/weinre-tar-01-h.jpg
deleted file mode 100644
index 7663c0d..0000000
--- a/weinre.doc/images/weinre-tar-01-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-tar-01-p.jpg b/weinre.doc/images/weinre-tar-01-p.jpg
deleted file mode 100644
index 7663c0d..0000000
--- a/weinre.doc/images/weinre-tar-01-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-tar-02-h.jpg b/weinre.doc/images/weinre-tar-02-h.jpg
deleted file mode 100644
index c93cd71..0000000
--- a/weinre.doc/images/weinre-tar-02-h.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.doc/images/weinre-tar-02-p.jpg b/weinre.doc/images/weinre-tar-02-p.jpg
deleted file mode 100644
index c93cd71..0000000
--- a/weinre.doc/images/weinre-tar-02-p.jpg
+++ /dev/null
Binary files differ
diff --git a/weinre.server/src/weinre/server/Channel.java b/weinre.server/src/weinre/server/Channel.java
index 71294d2..0e19b12 100644
--- a/weinre.server/src/weinre/server/Channel.java
+++ b/weinre.server/src/weinre/server/Channel.java
@@ -1,13 +1,14 @@
 /*
  * 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
  */
 
 package weinre.server;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -21,9 +22,9 @@
 
 //-------------------------------------------------------------------
 public class Channel {
-    
+
     static public final String AnonymousId = "anonymous";
-    
+
     private String                pathPrefix;
     private String                name;
     private String                id;
@@ -35,7 +36,8 @@
     private Map<String,Object>    serviceMap;
     private String                remoteHost;
     private String                remoteAddress;
-    
+    private PrintWriter           messageLog;
+
     //---------------------------------------------------------------
     public Channel(String pathPrefix, String name, String id, String remoteHost, String remoteAddress) {
         this.pathPrefix         = pathPrefix;
@@ -49,52 +51,53 @@
         this.connector          = null;
         this.serviceMap         = new HashMap<String,Object>();
         this.lastRead           = System.currentTimeMillis();
+        this.messageLog         = Main.getSettings().getMessageLog();
     }
 
     //---------------------------------------------------------------
     public Connector getConnector() {
         return connector;
     }
-    
+
     //---------------------------------------------------------------
     public String getRemoteHost() {
         return remoteHost;
     }
-    
+
     //---------------------------------------------------------------
     public String getRemoteAddress() {
         return remoteAddress;
     }
-    
+
     //---------------------------------------------------------------
     protected void _setConnector(Connector connector) {
         this.connector = connector;
     }
-    
+
     //---------------------------------------------------------------
     public void sendCallback(String intfName, String callbackId, Object... args) throws IOException {
         if (callbackId == null) return;
-        
+
         List<Object> innerArgs = new ArrayList<Object>();
         innerArgs.add(callbackId);
         innerArgs.add(Arrays.asList(args));
-        
+
         sendEvent(intfName, "sendCallback", innerArgs.toArray());
     }
-    
+
     //---------------------------------------------------------------
     public void sendEvent(String intfName, String methodName, Object... args) {
         Main.debug(getName() + ": send " + intfName + "." + methodName + "()");
-        
+
         JSONObject response = new JSONObject();
-        
+
         String responseString;
         try {
             response.put("interface", intfName);
             response.put("method", methodName);
-            
+
             List<Object> passedArgs = new ArrayList<Object>(Arrays.asList(args));
-            
+
             response.put("args", passedArgs);
 
             responseString = response.toString();
@@ -103,10 +106,10 @@
             Main.warn("IOException serializing message for " + intfName + "." + methodName);
             return;
         }
-        
+
         this.postResponse(responseString);
     }
-    
+
     //---------------------------------------------------------------
     public Object getService(String name) {
         try {
@@ -119,86 +122,105 @@
             throw new RuntimeException(e);
         }
     }
-    
+
     //---------------------------------------------------------------
     @SuppressWarnings("rawtypes")
     private Object getService_(String name) throws InstantiationException, IllegalAccessException {
         if (serviceMap.containsKey(name)) return serviceMap.get(name);
-        
+
         String klassName = "weinre.server.service." + name;
         Class  klass = null;
         try {
             klass = Class.forName(klassName);
         }
         catch (ClassNotFoundException e) {
-            Main.debug("service class not found: " + klassName);
+            // Main.debug("service class not found: " + klassName);
             serviceMap.put(name, null);
             return null;
         }
-        
+
         Object result = klass.newInstance();
         serviceMap.put(name, result);
-        Main.debug("loaded service class: " + klassName);
+        // Main.debug("loaded service class: " + klassName);
         return result;
     }
-    
+
     //---------------------------------------------------------------
     public void close() {
         isClosed = true;
         requestQueue.shutdown();
         responseQueue.shutdown();
-        
+
         ChannelManager.$.deregisterChannel(name);
     }
-    
+
     //---------------------------------------------------------------
     public boolean isClosed() {
         return isClosed;
     }
-    
+
     //---------------------------------------------------------------
     public String getPathPrefix() {
         return pathPrefix;
     }
-    
+
     //---------------------------------------------------------------
     public String getName() {
         return name;
     }
-    
+
     //---------------------------------------------------------------
     public String getId() {
         return id;
     }
-    
+
     //---------------------------------------------------------------
     public long getLastRead() {
         return lastRead;
     }
-    
+
     //---------------------------------------------------------------
     public void updateLastRead() {
         lastRead = System.currentTimeMillis();
     }
-    
+
     //---------------------------------------------------------------
     public void postRequest(String json) {
         if (isClosed()) return;
-        
+
         requestQueue.add(json);
+        log(json);
     }
 
     //---------------------------------------------------------------
     public void postResponse(String json) {
         if (isClosed()) return;
-        
+
         responseQueue.add(json);
+        log(json);
+    }
+
+    //---------------------------------------------------------------
+    private void log(String json) {
+        if (null == messageLog) return;
+
+        JSONObject jObject;
+        try {
+            jObject = new JSONObject(json);
+            jObject.put("_to", getName() + "#" + getId());
+        }
+        catch (JSONException e) {
+            return;
+        }
+
+        messageLog.print(jObject.toString(true));
+        messageLog.println(",");
     }
 
     //---------------------------------------------------------------
     public List<String> getRequests(int timeoutSeconds) throws InterruptedException {
         if (isClosed()) return new LinkedList<String>();
-        
+
         List<String> result = requestQueue.getAll(timeoutSeconds, TimeUnit.SECONDS);
 
         return result;
@@ -207,7 +229,7 @@
     //---------------------------------------------------------------
     public List<String> getResponses(int timeoutSeconds) throws InterruptedException {
         if (isClosed()) return new LinkedList<String>();
-        
+
         List<String> result = responseQueue.getAll(timeoutSeconds, TimeUnit.SECONDS);
 
         return result;
diff --git a/weinre.server/src/weinre/server/Main.java b/weinre.server/src/weinre/server/Main.java
index 1d8ae96..4968fee 100644
--- a/weinre.server/src/weinre/server/Main.java
+++ b/weinre.server/src/weinre/server/Main.java
@@ -8,6 +8,7 @@
 package weinre.server;
 
 import java.io.PrintStream;
+import java.io.PrintWriter;
 
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.util.log.Log;
@@ -56,6 +57,12 @@
         Settings = ServerSettings.getOptions(args);
         if (null == Settings) System.exit(0);
         
+        if (null != Settings.getMessageLog()) {
+            Settings.getMessageLog().println("[");
+        }
+        
+        Runtime.getRuntime().addShutdownHook(onShutdownThread());
+        
         consoleStdout = ConsoleOutputStream.newPrintStream(this, System.out, true);
         consoleStderr = ConsoleOutputStream.newPrintStream(this, System.err, false);
 
@@ -71,6 +78,19 @@
     }
     
     //---------------------------------------------------------------
+    private Thread onShutdownThread() {
+        return new Thread(new Runnable() {
+            public void run() {
+                PrintWriter messageLog = Settings.getMessageLog();
+                if (null == messageLog) return;
+                
+                messageLog.println("null ]");
+                messageLog.close();
+            }
+        });
+    }
+    
+    //---------------------------------------------------------------
     public int exit() {
         if (null != server) {
             try {
diff --git a/weinre.server/src/weinre/server/MessageHandler.java b/weinre.server/src/weinre/server/MessageHandler.java
index 29942b2..bbddc9f 100644
--- a/weinre.server/src/weinre/server/MessageHandler.java
+++ b/weinre.server/src/weinre/server/MessageHandler.java
@@ -7,6 +7,7 @@
 
 package weinre.server;
 
+import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -18,6 +19,7 @@
 
 //-------------------------------------------------------------------
 public class MessageHandler {
+    private PrintWriter messageLog;
 
     //---------------------------------------------------------------
     static public void start() {
@@ -43,6 +45,7 @@
     
     //---------------------------------------------------------------
     private MessageHandler() {
+        messageLog = Main.getSettings().getMessageLog();
     }
     
     //---------------------------------------------------------------
@@ -70,6 +73,13 @@
                     try {
                         request = acc.getString(i);
                         accRequest = new JSONObject(request);
+                        accRequest.put("_from", channel.getName() + "#" + channel.getId());
+                        
+                        if (null != messageLog) {
+                            messageLog.print(accRequest.toString(true));
+                            messageLog.println(",");
+                        }
+                        
                     }
                     catch (JSONException e) {
                         Main.warn("error parsing request: " + e + ": '" + request + "'");
diff --git a/weinre.server/src/weinre/server/ServerSettings.java b/weinre.server/src/weinre/server/ServerSettings.java
index b7a63b5..a2c73c7 100644
--- a/weinre.server/src/weinre/server/ServerSettings.java
+++ b/weinre.server/src/weinre/server/ServerSettings.java
@@ -7,6 +7,10 @@
 
 package weinre.server;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
@@ -38,6 +42,7 @@
     private int         readTimeoutSeconds = 5;
     private int         deathTimeoutSeconds;
     private Properties  fileProperties;
+    private PrintWriter messageLog;
     
     //---------------------------------------------------------------
     static public ServerSettings getOptions(String[] commandLine) {
@@ -65,6 +70,7 @@
         options.addOption("reuseAddr",    true,  "force bind the port if already bound [true|false]");
         options.addOption("readTimeout",  true,  "seconds before timing out HTTP GETs");
         options.addOption("deathTimeout", true,  "seconds before considering connector dead");
+        options.addOption("messageLog",   true,  "file to log messages to");
        
         return options;
     }
@@ -126,6 +132,7 @@
         reuseAddr           = getBooleanFromOption(commandLine, "reuseAddr",    reuseAddr);
         readTimeoutSeconds  = getIntFromOption(commandLine,     "readTimeout",  readTimeoutSeconds,   0, 0x00FFFFFF);
         deathTimeoutSeconds = getIntFromOption(commandLine,     "deathTimeout", readTimeoutSeconds*3, 0, 0x00FFFF);
+        messageLog          = getPrintWriterFromOption(commandLine, "messageLog");
 
         // handle verbose logging
         if (commandLine.hasOption("verbose")) {
@@ -188,7 +195,29 @@
 
         return result;
     }
-    
+
+    //---------------------------------------------------------------
+    private PrintWriter getPrintWriterFromOption(CommandLine commandLine, String name) {
+        
+        String fileName = commandLine.getOptionValue(name);
+        if (null == fileName) {
+            fileName = fileProperties.getProperty(name);
+        }
+
+        if (null == fileName) return null;
+        
+        File file = new File(fileName);
+        
+        try {
+            FileWriter fileWriter = new FileWriter(file);
+            return new PrintWriter(fileWriter);
+        } 
+        catch (IOException e) {
+            error(name + " parameter file name '" + fileName + "' cannot be opened for writing.");
+            return null;
+        }
+    }
+
     //---------------------------------------------------------------
     private void error(String message) {
         System.out.println("error with command-line option: " + message);
@@ -266,7 +295,12 @@
     public boolean reuseAddr() {
         return reuseAddr;
     }
-    
+
+    //---------------------------------------------------------------
+    public PrintWriter getMessageLog() {
+        return messageLog;
+    }
+
     //---------------------------------------------------------------
     public String getNiceHostName() {
         String hostName = getBoundHostValue();
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/client/weinre/check-for-webkit.js b/weinre.web/client/weinre/check-for-webkit.js
index 7077ec8..249e80d 100644
--- a/weinre.web/client/weinre/check-for-webkit.js
+++ b/weinre.web/client/weinre/check-for-webkit.js
@@ -1,10 +1,10 @@
 /*
  * 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
  */
 
 if (!navigator.userAgent.match(/webkit/i)) {
-	alert("This web page is only designed to work in WebKit-based browsers.  Sorry.  Best of luck.")
+    alert("This web page is only designed to work in WebKit-based browsers.  Sorry.  Best of luck.")
 }
diff --git a/weinre.web/client/weinre/client.css b/weinre.web/client/weinre/client.css
index 196a9cb..383cf96 100644
--- a/weinre.web/client/weinre/client.css
+++ b/weinre.web/client/weinre/client.css
@@ -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
  */
 
@@ -20,46 +20,46 @@
 }
 
 .panel.resources .cookie-storage-tree-item {
-	display: none;
+    display: none;
 }
 
 .panel.resources .frame-storage-tree-item {
-	display: none;
+    display: none;
 }
 
 .panel.resources .application-cache-storage-tree-item {
-	display: none;
+    display: none;
 }
 
 .panel.timeline .resources-size-graph-sidebar-item {
-	display: none;
+    display: none;
 }
 
 #main-status-bar .timeline-category-rendering {
-	display: none;
+    display: none;
 }
 
 #search {
-	display: none;
+    display: none;
 }
 
 #search-toolbar-label {
-	display: none;
+    display: none;
 }
 
 .weinre-connector-item {
     font-size: 140%;
 }
- 
+
 .weinre-connector-item.target {
-    text-decoration: underline; 
+    text-decoration: underline;
     color: blue;
 }
- 
+
 .weinre-connector-item.target.connected {
     color: #0D0;
 }
- 
+
 .weinre-connector-item.closed {
     color: #B00;
 }
diff --git a/weinre.web/demo/weinre-demo-min.html b/weinre.web/demo/weinre-demo-min.html
index 95532ee..61e5070 100644
--- a/weinre.web/demo/weinre-demo-min.html
+++ b/weinre.web/demo/weinre-demo-min.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
 -->
 
@@ -10,13 +10,9 @@
 <meta name="viewport" content="user-scalable=no, width=device-width, height=device-height">
 <title>weinre demo</title>
 <link rel="stylesheet" href="weinre-demo.css">
-<script src="weinre-demo.js"></script>
+<script>try {window.WeinreServerId = location.hash.split("#")[1]} catch(e) {window.WeinreServerId = "anonymous"}</script>
 <script src="/target/target-script-min.js"></script>
-
-<script type="text/javascript">
-    require("weinre/common/Weinre").getClass().showNotImplemented()
-</script>
-
+<script src="weinre-demo.js"></script>
 </head>
 
 <body onload="onLoad()">
@@ -27,9 +23,18 @@
 <h1 style="color:red">this is a red h1</h1>
 <p>Some text, <i>some italic text</i>, and <b>some bold text</b>.
 
-<div id="metrics">a div</div>
+<hr>
+<p>test exceptions in callbacks:
+<div style="margin-left:1em">
+    <input id="button-error-DOM-event"    type="button" value="DOM addEventHandler">
+<br><input id="button-error-window-event" type="button" value="window addEventHandler">
+<br><input id="button-error-XHR-event"    type="button" value="XHR addEventHandler">
+<br><input id="button-error-setTimeout"   type="button" value="setTimeout()">
+<br><input id="button-error-setInterval"  type="button" value="setInterval()">
+</div>
 
-<input id="button-clear-output" type="button" value="clear output">
+<hr>
+<p><input id="button-clear-output" type="button" value="clear output">
 <div id="output"></div>
 </body>
 
diff --git a/weinre.web/demo/weinre-demo-pieces.html b/weinre.web/demo/weinre-demo-pieces.html
deleted file mode 100644
index 51b261f..0000000
--- a/weinre.web/demo/weinre-demo-pieces.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!--
- * 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
--->
-
-<html>
-<head>
-<meta name="viewport" content="user-scalable=no, width=device-width, height=device-height">
-<title>weinre demo</title>
-
-<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>
-<script src="/weinre/common/IDLTools.transportd.js"></script>
-<script src="/weinre/common/MessageDispatcher.transportd.js"></script>
-<script src="/weinre/common/WebSocketXhr.transportd.js"></script>
-<script src="/weinre/common/Binding.transportd.js"></script>
-<script src="/weinre/common/Callback.transportd.js"></script>
-<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/target/Console.transportd.js"></script>
-<script src="/add-css-properties.js"></script>
-<script src="/weinre/target/CheckForProblems.transportd.js"></script>
-<script src="/weinre/target/WiConsoleImpl.transportd.js"></script>
-<script src="/weinre/target/WiCSSImpl.transportd.js"></script>
-<script src="/weinre/target/WiDatabaseImpl.transportd.js"></script>
-<script src="/weinre/target/WiDOMImpl.transportd.js"></script>
-<script src="/weinre/target/WiDOMStorageImpl.transportd.js"></script>
-<script src="/weinre/target/WiInspectorImpl.transportd.js"></script>
-<script src="/weinre/target/WiRuntimeImpl.transportd.js"></script>
-<script src="/weinre/target/WeinreTargetEventsImpl.transportd.js"></script>
-<script src="/weinre/target/NodeStore.transportd.js"></script>
-<script src="/weinre/target/CSSStore.transportd.js"></script>
-<script src="/weinre/target/InjectedScriptHostImpl.transportd.js"></script>
-<script src="/weinre/target/Target.transportd.js"></script>
-<script src="/weinre/target/ElementHighlighter.transportd.js"></script>
-<script src="/weinre/target/InjectedScript.js"></script>
-<script src="/weinre/target/Timeline.transportd.js"></script>
-<script src="/weinre/target/SqlStepper.transportd.js"></script>
-<script src="/weinre/target/WeinreExtraClientCommandsImpl.transportd.js"></script>
-<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()
-</script>
-
-<link rel="stylesheet" href="weinre-demo.css">
-<script src="weinre-demo.js"></script>
-</head>
-
-<body onload="onLoad()">
-<input id="button-start-stuff" type="button" value="start stuff">
-
-<h1>this is a green h1</h1>
-<h1 class="blue">this is a blue h1</h1>
-<h1 style="color:red">this is a red h1</h1>
-<p>Some text, <i>some italic text</i>, and <b>some bold text</b>.
-
-<div id="metrics">a div</div>
-
-<input id="button-clear-output" type="button" value="clear output">
-<div id="output"></div>
-</body>
-
-</html>
diff --git a/weinre.web/demo/weinre-demo.html b/weinre.web/demo/weinre-demo.html
index 5c93023..58e003b 100644
--- a/weinre.web/demo/weinre-demo.html
+++ b/weinre.web/demo/weinre-demo.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
 -->
 
@@ -10,12 +10,9 @@
 <meta name="viewport" content="user-scalable=no, width=device-width, height=device-height">
 <title>weinre demo</title>
 <link rel="stylesheet" href="weinre-demo.css">
-<script src="weinre-demo.js"></script>
+<script>try {window.WeinreServerId = location.hash.split("#")[1]} catch(e) {window.WeinreServerId = "anonymous"}</script>
 <script src="/target/target-script.js"></script>
-
-<script type="text/javascript">
-    require("weinre/common/Weinre").getClass().showNotImplemented()
-</script>
+<script src="weinre-demo.js"></script>
 </head>
 
 <body onload="onLoad()">
@@ -26,9 +23,18 @@
 <h1 style="color:red">this is a red h1</h1>
 <p>Some text, <i>some italic text</i>, and <b>some bold text</b>.
 
-<div id="metrics">a div</div>
+<hr>
+<p>test exceptions in callbacks:
+<div style="margin-left:1em">
+    <input id="button-error-DOM-event"    type="button" value="DOM addEventHandler">
+<br><input id="button-error-window-event" type="button" value="window addEventHandler">
+<br><input id="button-error-XHR-event"    type="button" value="XHR addEventHandler">
+<br><input id="button-error-setTimeout"   type="button" value="setTimeout()">
+<br><input id="button-error-setInterval"  type="button" value="setInterval()">
+</div>
 
-<input id="button-clear-output" type="button" value="clear output">
+<hr>
+<p><input id="button-clear-output" type="button" value="clear output">
 <div id="output"></div>
 </body>
 
diff --git a/weinre.web/demo/weinre-demo.js b/weinre.web/demo/weinre-demo.js
index 32c72b5..b95a50f 100644
--- a/weinre.web/demo/weinre-demo.js
+++ b/weinre.web/demo/weinre-demo.js
@@ -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
  */
 
@@ -10,10 +10,15 @@
 
 var buttonStartStuff
 var buttonClearOutput
-var outputElement 
+var buttonErrorDOMEvent
+var buttonErrorWindowEvent
+var buttonErrorXHREvent
+var buttonErrorSetTimeout
+var buttonErrorSetInterval
+var outputElement
 var storageIndex = 0
 var db
-var otherDB 
+var otherDB
 
 // set the id based on the hash
 var hash = location.href.split("#")[1]
@@ -22,43 +27,89 @@
 
 //------------------------------------------------------------------------------
 function onLoad() {
-    if (!buttonStartStuff)  buttonStartStuff  = document.getElementById("button-start-stuff")
-    if (!buttonClearOutput) buttonClearOutput = document.getElementById("button-clear-output")
-    if (!outputElement)     outputElement     = document.getElementById("output")
-    
+    if (!buttonStartStuff)       buttonStartStuff       = document.getElementById("button-start-stuff")
+    if (!buttonClearOutput)      buttonClearOutput      = document.getElementById("button-clear-output")
+    if (!buttonErrorDOMEvent)    buttonErrorDOMEvent    = document.getElementById("button-error-DOM-event")
+    if (!buttonErrorWindowEvent) buttonErrorWindowEvent = document.getElementById("button-error-window-event")
+    if (!buttonErrorXHREvent)    buttonErrorXHREvent    = document.getElementById("button-error-XHR-event")
+    if (!buttonErrorSetTimeout)  buttonErrorSetTimeout  = document.getElementById("button-error-setTimeout")
+    if (!buttonErrorSetInterval) buttonErrorSetInterval = document.getElementById("button-error-setInterval")
+    if (!outputElement)          outputElement          = document.getElementById("output")
+
     buttonStartStuff.addEventListener("click", function() {
         lastClickTime = new Date().toString()
         if (db) db.transaction(addClick)
-        
+
         openTheOtherDatabase()
-        
+
         if (!started) {
             buttonStartStuff.value = "stop stuff"
             startStuff()
         }
         else {
             buttonStartStuff.value = "start stuff"
-            stopStuff()      
+            stopStuff()
         }
         started = !started
     })
-    
+
+    buttonErrorDOMEvent.addEventListener("click", function buttonClicked() {
+        willThrowError()
+    })
+
+    buttonErrorWindowEvent.addEventListener("click", function() {
+        var event = document.createEvent("Events")
+        event.initEvent("demo", true, true)
+        window.dispatchEvent(event)
+    })
+
+    window.addEventListener("demo", willThrowError)
+
+    buttonErrorXHREvent.addEventListener("click", function() {
+        var xhr = new XMLHttpRequest()
+        xhr.addEventListener("readystatechange", willThrowError)
+        xhr.open("GET", "something.that.doesn't.exist")
+        xhr.send()
+    })
+
+    buttonErrorSetTimeout.addEventListener("click", function() {
+        setTimeout(willThrowError, 1000)
+    })
+
+    buttonErrorSetInterval.addEventListener("click", function() {
+        var intervalID
+
+        setTimeout(function() {clearInterval(intervalID)}, 3000)
+
+        intervalID = setInterval(willThrowError, 1000)
+    })
+
     buttonClearOutput.addEventListener("click", function() {
         outputElement.innerHTML = ""
     })
-    
+
     openTheDatabase()
 }
 
 //------------------------------------------------------------------------------
+function willThrowError() {
+    throwsError()
+}
+
+function throwsError() {
+    x = null
+    x.doSomething()
+}
+
+//------------------------------------------------------------------------------
 var interval
 
 function startStuff() {
     if (window.localStorage)   window.localStorage.clear()
     if (window.sessionStorage) window.sessionStorage.clear()
-    
+
     storageIndex = 0
-    
+
     interval = setInterval(intervalStuff, 1000)
 }
 
@@ -70,38 +121,33 @@
 function intervalStuff() {
 
     var message = "doing interval stuff at " + new Date()
-    
+
     // add a timeout
     setTimeout(function() { console.log(message)}, 333)
-    
+
     // add a timeline marker
     console.markTimeline(message)
-    
+
     // write to local- and sessionStorage
     if (window.localStorage) {
         var smessage = message + " (local)"
         window.localStorage.setItem(  "item-" + storageIndex, smessage)
     }
-    
+
     if (window.sessionStorage) {
         var smessage = message + " (session)"
         window.sessionStorage.setItem("item-" + storageIndex, smessage)
     }
     storageIndex++
-    
+
     // write the message to the page
     output(message)
-    
+
     // do an XHR
     var xhr = new XMLHttpRequest()
     // xhr.addEventListener("readystatechange", function() {logXhr(this)})
     xhr.open("GET", "../target/target-script.js", true)
     xhr.send()
-    
-    // cause an error
-    var empty = null
-    empty.x = 1
-    
 }
 
 //------------------------------------------------------------------------------
@@ -132,7 +178,7 @@
 function createDatabase(tx) {
     var schema = "clicks (id integer primary key, date text)"
     var sql = "create table if not exists " + schema
-    
+
     tx.executeSql(sql, null, clearDatabase, sqlError);
 }
 
@@ -140,7 +186,7 @@
 function createDatabase_other(tx) {
     var schema = "clicks_other (id integer primary key, other text)"
     var sql = "create table if not exists " + schema
-    
+
     tx.executeSql(sql, null, null, sqlError);
 }
 
@@ -155,7 +201,7 @@
 //------------------------------------------------------------------------------
 function openTheOtherDatabase() {
     if (otherDB) return
-    
+
     if (window.openDatabase) {
         otherDB = window.openDatabase("clicks_other_db", "1.0", "clicks_other_db", 8192)
         otherDB.transaction(createDatabase_other)
diff --git a/weinre.web/index.html b/weinre.web/index.html
index ed4f2a8..a4861d5 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
 -->
 
@@ -14,15 +14,15 @@
 <link rel="shortcut icon" href="images/weinre-icon-64x64.png" />
 <script src="versions.js"></script>
 <style>
-.hilite {
+.underline {
     border-bottom:          solid;
 }
 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;
@@ -39,15 +39,15 @@
 </style>
 </head>
 <body>
-<h1>weinre</h1>
-<p><span class="hilite">we</span>b <span class="hilite">in</span>spector <span class="hilite">re</span>mote
+<h1>weinre - <span class="underline">we</span>b <span class="underline">in</span>spector <span class="underline">re</span>mote</h1>
+
 
 <h2>Access Points</h2>
 
 <table>
 <tr><td>debug client user interface: <td> <span id="url-client-ui">???</span>
 <tr><td>documentation:               <td> <span id="url-target-documentation">???</span>
-<tr><td>target demo:                 <td> <span id="url-target-demo-min">???</span> <br><i>(<span id="url-target-demo-pieces"></span>)</i>
+<tr><td>target demo:                 <td> <span id="url-target-demo-min">???</span> <br><span id="url-target-demo"></span>
 </table>
 
 <h2>Target Script</h2>
@@ -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>
@@ -96,11 +96,10 @@
 <tr><td>apache cli:     <td> <span id="version-cli">???</span>
 <tr><td>apache json4j:  <td> <span id="version-json4j">???</span>
 <tr><td>json2:          <td> <span id="version-json2">???</span>
-<tr><td>scooj:          <td> <span id="version-scooj">???</span>
 <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/index.js b/weinre.web/index.js
index 3948f97..7e248a6 100644
--- a/weinre.web/index.js
+++ b/weinre.web/index.js
@@ -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
  */
 
@@ -20,7 +20,6 @@
 replaceURL("url-interfaces",             buildHttpURL("interfaces/interfaces.html"))
 replaceURL("url-target-demo",            buildHttpURL("demo/weinre-demo.html#" + weinre_id))
 replaceURL("url-target-demo-min",        buildHttpURL("demo/weinre-demo-min.html#" + weinre_id))
-replaceURL("url-target-demo-pieces",     buildHttpURL("demo/weinre-demo-pieces.html#" + weinre_id), "pieces version")
 replaceURL("url-target-script",          buildHttpURL("target/target-script-min.js#" + weinre_id))
 replaceURL("url-target-bookmarklet",     getTargetBookmarklet(), "weinre target debug")
 replaceURL("url-target-documentation",   buildHttpURL("doc/"))
@@ -36,7 +35,6 @@
 replaceText("version-json4j",    Weinre.Versions.json4j)
 replaceText("version-json2",     Weinre.Versions.json2)
 replaceText("version-swt",       Weinre.Versions.swt)
-replaceText("version-scooj",     Weinre.Versions.scooj)
 replaceText("version-modjewel",  Weinre.Versions.modjewel)
 
 replaceText("target-bookmarklet-src-pre",       getTargetBookmarklet())
@@ -48,19 +46,19 @@
 function buildHttpURL(uri) {
     var port     = weinre_port
     var pathname = weinre_pathname
-    
+
     if (pathname == "/index.html") pathname = "/"
-        
+
     if (weinre_protocol == "file:") {
         return uri
     }
-    
+
     else if (weinre_protocol == "http:") {
         if (port != "") port = ":" + port
-        
+
         return weinre_protocol + "//" + weinre_host + port + pathname + uri
     }
-    
+
     else if (protocol == "https:") {
         if (port != "") port = ":" + port
 
@@ -98,6 +96,6 @@
 //      alert("error: can't find element with id '" + id + "'")
         return
     }
-    
+
     element.innerHTML = text
 }
diff --git a/weinre.web/interfaces/interfaces.html b/weinre.web/interfaces/interfaces.html
index 80a775c..107df67 100644
--- a/weinre.web/interfaces/interfaces.html
+++ b/weinre.web/interfaces/interfaces.html
@@ -3,10 +3,10 @@
 <!--
  * 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
 -->
- 
+
 <html>
 
     <head>
@@ -14,15 +14,15 @@
         <link rel="stylesheet" href="interfaces.css">
         <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/Weinre.transportd.js"></script>
         <script src="/weinre/common/Ex.transportd.js"></script>
         <script src="/weinre/common/StackTrace.transportd.js"></script>
         <script src="/weinre/common/IDLTools.transportd.js"></script>
         <script src="/weinre/common/Callback.transportd.js"></script>
-        
+        <script src="/weinre/common/MethodNamer.transportd.js"></script>
+
         <script src="all-json-idls.js"></script>
-        
+
         <script src="interfaces.js"></script>
     </head>
 
@@ -31,7 +31,7 @@
 
         <div class="noprint">
             show:
-            <label><input id="show-Idl"        type="checkbox" checked="on"> IDL</label> / 
+            <label><input id="show-Idl"        type="checkbox" checked="on"> IDL</label> /
             <label><input id="show-JavaScript" type="checkbox" checked="on"> JavaScript</label> /
             <label><input id="show-Java"       type="checkbox" checked="on"> Java</label>
         </div>
@@ -52,7 +52,7 @@
                     [ nothing selected ]
                 </div>
             </div>
-        </div> 
+        </div>
     </body>
 
 </html>
diff --git a/weinre.web/interfaces/interfaces.js b/weinre.web/interfaces/interfaces.js
index 08d60fe..8b9e7de 100644
--- a/weinre.web/interfaces/interfaces.js
+++ b/weinre.web/interfaces/interfaces.js
@@ -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
  */
 
@@ -22,7 +22,7 @@
 
 var NativeTypes = "int any number boolean string void".split(" ")
 
-var IDLTools = require("weinre/common/IDLTools").getClass()
+var IDLTools = require("weinre/common/IDLTools")
 
 if (!window.localStorage) {
     window.localStorage = {
@@ -39,7 +39,7 @@
     e_showIdl        = document.getElementById("show-Idl")
     e_showJavaScript = document.getElementById("show-JavaScript")
     e_showJava       = document.getElementById("show-Java")
-    
+
     setUpShowCheckBoxes()
     populateInterfacesList(IDLTools.getIDLsMatching(/.*/))
 }
@@ -54,23 +54,23 @@
 //-----------------------------------------------------------------------------
 function setUpShowCheckBox(element, key) {
     var value = localStorage.getItem(key)
-    if (null == value) 
+    if (null == value)
         value = true
     else
         value = (value == "true")
-    
+
     element.checked    = value
     element.storageKey = key
-    
+
     element.addEventListener("click", el_showCheckBoxClicked, false)
 }
 
 //-----------------------------------------------------------------------------
 function el_showCheckBoxClicked(event) {
     var element = event.target
-    
+
     localStorage.setItem(element.storageKey, element.checked)
-    
+
     reapplyDisplayStyle("." + element.storageKey, element.checked)
 }
 
@@ -85,25 +85,25 @@
 function reapplyDisplayStyle(className, value) {
     value = value ? "block" : "none"
     ;[].slice.call(document.querySelectorAll(className)).forEach(function(element) {
-        element.style.display = value 
+        element.style.display = value
     })
 }
 
 //-----------------------------------------------------------------------------
 function populateInterfacesList(intfs) {
     e_interfaceList.innerHTML = ""
-        
+
     fixedIntfs = []
     intfs.forEach(function(intf){
         fixedIntfs.push(intf.name)
         Interfaces[intf.name] = intf
-            
+
         if (!intf.methods)    intf.methods    = []
         if (!intf.attributes) intf.attributes = []
     })
-    
+
     intfs = fixedIntfs
-    
+
     intfs.sort()
     intfs.forEach(function(intf){
         var a  = document.createElement("a")
@@ -111,7 +111,7 @@
         a.innerHTML     = intf
         a.interfaceName = intf
         a.addEventListener("click", el_interfaceClicked, false)
-        
+
         var li = document.createElement("li")
         li.appendChild(a)
         e_interfaceList.appendChild(li)
@@ -128,17 +128,17 @@
 //-----------------------------------------------------------------------------
 function showInterface(interfaceName) {
     var intf = Interfaces[interfaceName]
-    
+
     e_interfaceName.innerHTML = interfaceName
-    
+
     var html = []
 
     showInterfaceIdl(intf, html)
     showInterfaceJavaScript(intf, html)
     showInterfaceJava(intf, html)
-    
+
     e_interfaceBody.innerHTML = html.join("\n")
-    
+
     reapplyDisplayStyles()
 }
 
@@ -148,17 +148,17 @@
 function showInterfaceIdl(intf, html) {
     html.push("<div class='show-Idl'><h3>IDL</h3><pre>")
     html.push("interface {")
-    
+
     intf.methods.forEach(function(method){
         showInterfaceIdlMethod(method, html)
     })
-    
+
     if (intf.attributes.length > 0) html.push("<table>")
     intf.attributes.forEach(function(attribute){
         showInterfaceIdlAttribute(attribute, html)
     })
     if (intf.attributes.length > 0) html.push("</table>")
-    
+
     html.push("};")
     html.push("</pre></div>")
 }
@@ -166,9 +166,9 @@
 //-----------------------------------------------------------------------------
 function showInterfaceIdlMethod(method, html) {
     var line = "\n   "
-    
+
     var allParameters = method.parameters.concat(method.callbackParameters)
-    
+
     line += getIdlType(method.returns)
     line += " <span class='methodName'>" + method.name + "</span> (" + getIdlParameterList(allParameters) + ");"
     html.push(line)
@@ -177,9 +177,9 @@
 //-----------------------------------------------------------------------------
 function getIdlParameterList(parameters) {
     var result = []
-    
+
     if (parameters.length == 0) return "";
-    
+
     result.push("<table>")
     parameters.forEach(function(parameter, index, list){
         var comma = (index == list.length-1) ? "" : ","
@@ -188,7 +188,7 @@
         result.push("<td>" + getIdlType(parameter.type))
         result.push("<td>" + "<span class='parameterName tdIndent'>" + parameter.name + comma + "</span>")
     })
-    
+
     result.push("</table>")
     return result.join("\n") + Indent
 }
@@ -196,19 +196,19 @@
 //-----------------------------------------------------------------------------
 function getIdlType(type) {
     var result
-    
-    
+
+
     if (-1 == NativeTypes.indexOf(type.name)) {
         result = "<a href='javascript:showInterface(\"" + type.name + "\"); void(0);'>" + type.name + "</a>"
     }
     else {
         result = type.name
     }
-    
+
     for (var i=0; i<type.rank; i++) {
         result += "[]"
     }
-    
+
     return "<span class='type'>" + result + "</span>"
 }
 
@@ -224,8 +224,8 @@
 //-----------------------------------------------------------------------------
 function getJavaType(type) {
     var result
-    
-    
+
+
     if (-1 == NativeTypes.indexOf(type.name)) {
         result = "<a href='javascript:showInterface(\"" + type.name + "\"); void(0);'>" + type.name + "</a>"
     }
@@ -236,11 +236,11 @@
             console.log("Unable to translate IDL type to Java: " + type.name)
         }
     }
-    
+
     for (var i=0; i<type.rank; i++) {
         result += "[]"
     }
-    
+
     return "<span class='type'>" + result + "</span>"
 }
 
@@ -248,34 +248,34 @@
 //-----------------------------------------------------------------------------
 function showInterfaceIdlAttribute(attribute, html) {
     var line = "<tr><td>" + Indent + "attribute "
-        
+
     line += getIdlType(attribute.type)
     line += "<td><span class='attributeName tdIndent'>" + attribute.name + "</span>;"
     html.push(line)
-    
+
 }
 
 //-----------------------------------------------------------------------------
 function showInterfaceJavaScript(intf, html) {
     html.push("<div class='show-JavaScript'><h3>JavaScript</h3><pre>")
-    
+
     var line = ""
-        
+
     line += "\n//-----------------------------------------------------------------------------"
-    line += "\n<span class='interfaceName'>class " + intf.name + "</span>"        
+    line += "\n<span class='interfaceName'>class " + intf.name + "</span>"
     html.push(line)
-    
+
     intf.methods.forEach(function(method){
         showInterfaceJavaScriptMethod(intf, method, html)
     })
-    
+
     html.push("</pre></div>")
 }
 
 //-----------------------------------------------------------------------------
 function showInterfaceJavaScriptMethod(intf, method, html) {
     var line = ""
-        
+
     line += "\n//-----------------------------------------------------------------------------"
     line += "\n<span class='methodName'>method " + method.name + "</span>(" + getJavaScriptParameterListSimple(method.parameters, method.returns) + ")"
     line += "\n    // callback: function(" + getJavaScriptCallbackParameterListSimple(method.callbackParameters) + ")"
@@ -287,7 +287,7 @@
 //-----------------------------------------------------------------------------
 function getJavaScriptParameterList(parameters, returnType) {
     var result = []
-    
+
     result.push("<table>")
     parameters.forEach(function(parameter){
         result.push("<tr>")
@@ -298,7 +298,7 @@
     result.push("<tr>")
     result.push("<td>" + Indent2 + "<span class='parameterName'>callback</span>")
     result.push("<td><span class='tdIndent'>// function(error, " + getIdlType(returnType) + ")</span>")
-    
+
     result.push("</table>")
     return result.join("\n") + Indent
 }
@@ -306,7 +306,7 @@
 //-----------------------------------------------------------------------------
 function getJavaScriptParameterListSimple(parameters, returnType) {
     var result = []
-    
+
     parameters.forEach(function(parameter){
         if (parameter.out) return
         result.push("<span class='type'>/*" + getIdlType(parameter.type) + "*/ </span><span class='parameterName'>" + parameter.name + "</span>")
@@ -319,7 +319,7 @@
 //-----------------------------------------------------------------------------
 function getJavaScriptCallbackParameterListSimple(parameters) {
     var result = []
-    
+
     parameters.forEach(function(parameter){
         if (!parameter.out) return
         result.push("/*" + getIdlType(parameter.type) + "*/ "+ parameter.name)
@@ -342,7 +342,7 @@
 //-----------------------------------------------------------------------------
 function showInterfaceJavaMethod(intf, method, html) {
     var line = ""
-        
+
     line += "\n    /**"
     line += "\n     * "
     line += "\n     */"
@@ -358,7 +358,7 @@
 //-----------------------------------------------------------------------------
 function getJavaParameterList(parameters, returnType) {
     var result = []
-    
+
     result.push("<table>")
     parameters.forEach(function(parameter){
         result.push("<tr>")
@@ -369,7 +369,7 @@
     result.push("<tr>")
     result.push("<td>" + Indent2 + "<span class='parameterName'>callback</span>")
     result.push("<td><span class='tdIndent'>// function(error, " + getIdlType(returnType) + ")</span>")
-    
+
     result.push("</table>")
     return result.join("\n") + Indent
 }
@@ -392,16 +392,16 @@
 //-----------------------------------------------------------------------------
 function getJavaCallbackParameterListSimple(parameters) {
     var result = []
-    
+
     parameters.forEach(function(parameter){
         if (!parameter.out) return
         result.push("/*" + getJavaType(parameter.type)  + " " + parameter.name + "*/ (Object) null")
     })
 
     result = result.join(", ")
-    
+
     if (result != "") result = ", " + result
-    
+
     return result
 }
 
diff --git a/weinre.web/modules/weinre/client/Client.coffee b/weinre.web/modules/weinre/client/Client.coffee
new file mode 100644
index 0000000..514ba97
--- /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
+#---------------------------------------------------------------------------------
+
+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: ->
+
+        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/Client.scoop b/weinre.web/modules/weinre/client/Client.scoop
deleted file mode 100644
index 73c499a..0000000
--- a/weinre.web/modules/weinre/client/Client.scoop
+++ /dev/null
@@ -1,161 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Native
-
-requireClass ../common/IDLTools
-requireClass ../common/Callback
-requireClass ../common/Weinre
-requireClass ../common/MessageDispatcher
-requireClass ../common/Binding
-requireClass ../common/IDGenerator
-
-requireClass ./InspectorBackendImpl
-requireClass ./InspectorFrontendHostImpl
-requireClass ./WeinreClientEventsImpl
-requireClass ./WeinreExtraTargetEventsImpl
-requireClass ./RemotePanel
-
-//-----------------------------------------------------------------------------
-class Client
-
-//-----------------------------------------------------------------------------
-init
-    var AutoConnect = true
-    
-    Weinre.showNotImplemented()
-
-//-----------------------------------------------------------------------------
-method initialize
-
-    // validate InspectorFrontendHost against IDL
-    // IDLTools.validateAgainstIDL(InspectorFrontendHostImpl, 'InspectorFrontendHost')
-
-    // add a load handler for the window
-    window.addEventListener("load", Binding(this, "onLoaded"), false)
-
-    // create the socket
-    var messageDispatcher = new MessageDispatcher("../ws/client", this._getId())
-    Weinre.messageDispatcher = messageDispatcher
-
-    // finish setting up InspectorBackend
-    InspectorBackendImpl.setupProxies()
-
-    // create the client commands proxy
-    Weinre.WeinreClientCommands = messageDispatcher.createProxy("WeinreClientCommands")
-    Weinre.WeinreExtraClientCommands = messageDispatcher.createProxy("WeinreExtraClientCommands")
-
-    messageDispatcher.registerInterface("WeinreExtraTargetEvents", new WeinreExtraTargetEventsImpl(), false)
-    
-    // register WebInspector interface
-    messageDispatcher.registerInterface("WebInspector", WebInspector, false)
-    
-    // hack
-    WebInspector.mainResource = {}
-    WebInspector.mainResource.url = location.href
-
-//-----------------------------------------------------------------------------
-method _getId
-    var hash = location.href.split("#")[1]
-    if (hash) return hash
-    return "anonymous"
-
-//-----------------------------------------------------------------------------
-method uiAvailable
-    return WebInspector.panels && WebInspector.panels.remote
-    
-//-----------------------------------------------------------------------------
-method autoConnect(value)
-    if (arguments.length >= 1) {
-        AutoConnect = !!value
-    }
-
-    return AutoConnect
-
-//-----------------------------------------------------------------------------
-method _installRemotePanel
-    WebInspector.panels.remote = new RemotePanel();
-    
-    var panel   = WebInspector.panels.remote;
-    
-    var toolbar = document.getElementById("toolbar")
-    WebInspector.addPanelToolbarIcon(toolbar, panel, toolbar.childNodes[1])
-    WebInspector.panelOrder.unshift(WebInspector.panelOrder.pop())
-    
-    WebInspector.currentPanel = panel
-    
-    var toolButtonsToHide = ["scripts"]
-    toolButtonsToHide.forEach(function(toolButtonToHide){
-        if (!WebInspector.panels[toolButtonToHide]) return
-        if (!WebInspector.panels[toolButtonToHide].toolbarItem) return
-        WebInspector.panels[toolButtonToHide].toolbarItem.style.display = "none"
-    })
-    
-    var button = document.getElementById("dock-status-bar-item")
-    if (button) button.style.display = "none"
-
-//-----------------------------------------------------------------------------
-method onLoaded
-
-    Weinre.WeinreClientCommands.registerClient(Binding(this, this.cb_registerClient))
-
-    this._installRemotePanel()
-
-    var messageDispatcher = Weinre.messageDispatcher
-    
-//  Weinre.Socket.verbose(true)
-    
-    messageDispatcher.registerInterface("WeinreClientEvents",    new WeinreClientEventsImpl(this), false)
-    messageDispatcher.registerInterface("InspectorFrontendHost", InspectorFrontendHost, false)
-
-//-----------------------------------------------------------------------------
-method cb_registerClient(clientDescription)
-    Weinre.clientDescription = clientDescription
-    
-    if (this.uiAvailable()) {
-        WebInspector.panels.remote.setCurrentClient(clientDescription.channel)
-        WebInspector.panels.remote.afterInitialConnection()
-    }
-    
-    Weinre.messageDispatcher.getWebSocket().addEventListener("close", Binding(this, this.cb_webSocketClosed))
-
-//-----------------------------------------------------------------------------
-method cb_webSocketClosed
-    
-    // use a delay, otherwise reloading will cause this stuff to flash
-    // before page is actually reloaded
-    Native.setTimeout(function() {
-        WebInspector.panels.remote.connectionClosed()
-        WebInspector.currentPanel = WebInspector.panels.remote 
-    }, 1000)
-
-//-----------------------------------------------------------------------------
-function installWebInspectorAPIsource
-
-    if ("webInspector" in window) return
-    
-    var extensionAPI = window.parent.InspectorFrontendHost.getExtensionAPI()
-    extensionAPI = extensionAPI.replace("location.hostname + location.port", "location.hostname + ':' + location.port")
-    
-    // parms are: InjectedScriptHost, inspectedWindow, injectedScriptId
-    // InjectedScriptHost is not directly referenced
-    // inspectedWindow is not directly referenced
-    // injectedScriptId is used to scope object references, appears that
-    //     each extension should have a unique value for this
-    
-    var id = IDGenerator.next()
-    console.log("installing webInspector with injectedScriptId: " + id)
-    extensionAPI += "(null,null," + id + ")"
-    return extensionAPI
-
-//-----------------------------------------------------------------------------
-static method main
-    Weinre.client = new Client()
-    Weinre.client.initialize()
-
-    window.installWebInspectorAPIsource = installWebInspectorAPIsource
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/client/ConnectorList.coffee b/weinre.web/modules/weinre/client/ConnectorList.coffee
new file mode 100644
index 0000000..e1a50fb
--- /dev/null
+++ b/weinre.web/modules/weinre/client/ConnectorList.coffee
@@ -0,0 +1,139 @@
+
+#---------------------------------------------------------------------------------
+# 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/ConnectorList.scoop b/weinre.web/modules/weinre/client/ConnectorList.scoop
deleted file mode 100644
index b2ad6a5..0000000
--- a/weinre.web/modules/weinre/client/ConnectorList.scoop
+++ /dev/null
@@ -1,157 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./DOMTemplates as dt
-
-//-----------------------------------------------------------------------------
-class ConnectorList(title)
-    this.connectors   = {}
-    this.noneItem     = dt.LI("none")
-    this.ulConnectors = dt.UL(this.noneItem)
-    this.div          = dt.DIV(dt.H1(title), this.ulConnectors)
-    
-    this.noneItem.addStyleClass("weinre-connector-item")
-
-//-----------------------------------------------------------------------------
-method getElement
-    return this.div
-
-//-----------------------------------------------------------------------------
-method add(connector)
-    if (this.connectors[connector.channel]) return
-    
-    this.connectors[connector.channel] = connector
-    
-    var li = this.getListItem(connector)
-
-    if (this.noneItem.style.display != "none") {
-        this.noneItem.style.display = "none"
-    }
-    
-    li.addStyleClass("weinre-fadeable")
-    
-    var insertionPoint = this.getConnectorInsertionPoint(connector)
-    if (!insertionPoint) {
-        this.ulConnectors.appendChild(li)
-    }
-    else {
-        this.ulConnectors.insertBefore(li, insertionPoint)
-    }
-
-//-----------------------------------------------------------------------------
-method get(channel)
-    return this.connectors[channel]
-
-//-----------------------------------------------------------------------------
-method getNewestConnectorChannel(ignoring)
-    var newest = 0
-    
-    for (var connectorChannel in this.connectors) {
-        if (connectorChannel == ignoring) continue
-        if (connectorChannel > newest) newest = connectorChannel
-    }
-    
-    if (newest == 0) return null
-    return newest
-
-//-----------------------------------------------------------------------------
-method getConnectorInsertionPoint(connector)
-    for (var i=0; i<this.ulConnectors.childNodes.length; i++) {
-        var childNode = this.ulConnectors.childNodes[i]
-        if (null == childNode.connectorChannel) continue
-        if (childNode.connectorChannel < connector.channel) {
-            return childNode
-        }
-    }
-    
-    return null
-
-//-----------------------------------------------------------------------------
-method remove(channel, fast)
-    var self = this
-    var element = this.getConnectorElement(channel)
-    if (!element) return
-
-    var connector = this.connectors[channel]
-    if (connector) {
-        connector.closed = true
-    }
-    
-    delete this.connectors[channel]
-    
-    if (fast) {
-        this._remove(element)
-    }
-
-    else {
-        this.setState(element, "closed")
-        element.addStyleClass("weinre-fade")
-
-        window.setTimeout(function() {
-            self._remove(element)
-        }, 5000)
-    }
-
-//-----------------------------------------------------------------------------
-method _remove(element)
-    this.ulConnectors.removeChild(element)
-    if (this.getConnectors().length == 0) {
-        this.noneItem.style.display = "list-item"
-    }
-
-//-----------------------------------------------------------------------------
-method removeAll()
-    this.getConnectors().forEach(function(connector) {
-        this.remove(connector.channel, true)
-    }, this)
-
-//-----------------------------------------------------------------------------
-method getConnectors()
-    var result = []
-    for (var channel in this.connectors) {
-        if (!this.connectors.hasOwnProperty(channel)) continue
-        
-        result.push(this.connectors[channel])
-    }
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method getConnectorElement(channel)
-    var connector = this.connectors[channel]
-    if (!connector) return null
-    
-    return connector.element
-
-//-----------------------------------------------------------------------------
-method setCurrent(channel)
-    this.getConnectors().forEach(function(connector) {
-        connector.element.removeStyleClass("current")
-    })
-    
-    var element = this.getConnectorElement(channel)
-    if (null == element) return
-    
-    element.addStyleClass("current")
-
-//-----------------------------------------------------------------------------
-method setState(channel, state)
-    if (typeof channel == "string") {
-        var element = this.getConnectorElement(channel)
-    }
-    else {
-        element = channel
-    }
-    
-    if (!element) return
-    
-    element.removeStyleClass("error")
-    element.removeStyleClass("closed")
-    element.removeStyleClass("connected")
-    element.removeStyleClass("not-connected")
-    element.addStyleClass(state)
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/DOMTemplates.scoop b/weinre.web/modules/weinre/client/DOMTemplates.scoop
deleted file mode 100644
index 566c1b4..0000000
--- a/weinre.web/modules/weinre/client/DOMTemplates.scoop
+++ /dev/null
@@ -1,74 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-// creates functions of the form H1, H2, etc, as static methods that create a new element
-// of the specified name.  The function can take a variable number of arguments,
-// which are either child elements, strings, attributes, or properties of the element.
-//
-// - arguments which are nodes become children
-// - arguments which are strings become text node children
-// - arguments which are objects have the object's properties added as:
-//    - attributes if the property name does not begin with "$"
-//    - properties if the property name begins with "$" 
-//      (the resulting property name doesn't include "$")
-//-----------------------------------------------------------------------------
-
-requireClass ../common/Ex
-
-//-----------------------------------------------------------------------------
-class DOMTemplates
-
-//-----------------------------------------------------------------------------
-function getElementFunction(elementName)
-    return function() {
-        var element = document.createElement(elementName)
-        
-        var args = [].slice.call(arguments)
-        args.forEach(function(argument) {
-            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)
-        })
-        
-        return element
-    }
-
-//-----------------------------------------------------------------------------
-function addToElement_String(element, aString)
-    addToElement_Node(element, document.createTextNode(aString))
-
-//-----------------------------------------------------------------------------
-function addToElement_Node(element, anElement)
-    element.appendChild(anElement)
-
-//-----------------------------------------------------------------------------
-function addToElement_Object(element, anObject)
-    for (var key in anObject) {
-        if (!anObject.hasOwnProperty(key)) continue
-        
-        var val = anObject[key]
-        
-        if (key.substr(0,1) == "$") {
-            var actualKey = key.substr(1)
-            element[actualKey] = val
-        }
-        else {
-            element.setAttribute(key, val)
-        }
-    }
-
-//-----------------------------------------------------------------------------
-init 
-    var 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(" ")
-    
-    elementNames.forEach(function(elementName) {
-        DOMTemplates[elementName] = getElementFunction(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/ExtensionRegistryImpl.scoop b/weinre.web/modules/weinre/client/ExtensionRegistryImpl.scoop
deleted file mode 100644
index bee7c42..0000000
--- a/weinre.web/modules/weinre/client/ExtensionRegistryImpl.scoop
+++ /dev/null
@@ -1,35 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Ex
-requireClass ../common/Binding
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class ExtensionRegistryImpl
-
-//-----------------------------------------------------------------------------
-init
-    var extensions = []
-
-//-----------------------------------------------------------------------------
-method getExtensionsAsync
-    if (extensions.length) return
-    
-    Weinre.WeinreClientCommands.getExtensions(Binding(this, this._cb_getExtensions))
-        
-//-----------------------------------------------------------------------------
-method _cb_getExtensions(extensionsResult)
-    extensions = extensionsResult
-    this._installExtensions()
-    
-//-----------------------------------------------------------------------------
-method _installExtensions    
-    WebInspector.addExtensions(extensions)
-    
-    
\ No newline at end of file
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/InspectorBackendImpl.scoop b/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
deleted file mode 100644
index a39ee09..0000000
--- a/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
+++ /dev/null
@@ -1,73 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Ex
-requireClass ../common/IDLTools
-requireClass ../common/MessageDispatcher
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class InspectorBackendImpl
-    this.registeredDomainDispatchers = {}
-    MessageDispatcher.setInspectorBackend(this)
-
-//-----------------------------------------------------------------------------
-static method setupProxies
-
-    var intfNames = [
-        "ApplicationCache",
-        "BrowserDebugger",
-        "CSS",
-        "Console",
-        "DOM",
-        "DOMStorage",
-        "Database",
-        "Debugger",
-        "InjectedScript",
-        "Inspector",
-        "Network",
-        "Profiler",
-        "Runtime"
-    ]
-
-    intfNames.forEach(function(intfName) {
-        var proxy = Weinre.messageDispatcher.createProxy(intfName)
-        if (window[intfName]) {
-            throw new Ex(arguments, "backend interface '" + intfName + "' already created")
-        }
-        
-        var intf = IDLTools.getIDL(intfName)
-        if (!intf) {
-            throw new Ex(arguments, "interface not registered: '" + intfName + "'")
-        }
-
-        window[intfName] = {}
-        
-        intf.methods.forEach(function(method) {
-            var proxyMethod = InspectorBackendImpl.getProxyMethod(proxy, method)
-            InspectorBackendImpl.prototype[method.name] = proxyMethod
-            window[intfName][method.name] = proxyMethod
-        })
-    })
-    
-
-//-----------------------------------------------------------------------------
-static method getProxyMethod(proxy, method)
-    return function() {
-        return proxy[method.name].apply(proxy, arguments)
-    }
-
-//-----------------------------------------------------------------------------
-method registerDomainDispatcher(name, intf)
-    this.registeredDomainDispatchers[name] = intf
-    
-//-----------------------------------------------------------------------------
-method getRegisteredDomainDispatcher(name)
-    if (!this.registeredDomainDispatchers.hasOwnProperty(name)) return null
-    
-    return this.registeredDomainDispatchers[name]
diff --git a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee
new file mode 100644
index 0000000..b060469
--- /dev/null
+++ b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.coffee
@@ -0,0 +1,88 @@
+
+#---------------------------------------------------------------------------------
+# 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"
+        "audits,profiles"
+
+    #---------------------------------------------------------------------------
+    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/InspectorFrontendHostImpl.scoop b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
deleted file mode 100644
index cbdb6c1..0000000
--- a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../common/IDLTools
-
-//-----------------------------------------------------------------------------
-class InspectorFrontendHostImpl
-        
-    this._getPlatformAndPort()
-
-//-----------------------------------------------------------------------------
-init
-    var _extensionAPI
-    
-//-----------------------------------------------------------------------------
-method loaded
-
-//-----------------------------------------------------------------------------
-method localizedStringsURL
-    return "nls/English.lproj/localizedStrings.js";
-
-//-----------------------------------------------------------------------------
-method hiddenPanels
-//    return "audits,profiles,resources,network"
-    return "audits,profiles,network"
-//    return "audits,profiles,resources,scripts,timeline,network"
-
-//-----------------------------------------------------------------------------
-method platform
-    return "weinre"
-
-//-----------------------------------------------------------------------------
-method port
-    return "weinre"
-
-//-----------------------------------------------------------------------------
-method sendMessageToBackend(message)
-    var object = JSON.parse(message)
-    if (object[0] == "setInjectedScriptSource") {
-        object[1] = "<long script elided>"
-    }
-    Weinre.logInfo(arguments.callee.signature + "(" + JSON.stringify(object,null,4) + ")")
-
-//-----------------------------------------------------------------------------
-method setExtensionAPI(extensionAPI)
-    _extensionAPI = extensionAPI
-
-//-----------------------------------------------------------------------------
-method getExtensionAPI
-    return _extensionAPI
-
-//-----------------------------------------------------------------------------
-method inspectedURLChanged
-
-//-----------------------------------------------------------------------------
-method _getPlatformAndPort
-    this._platform       = "weinre"
-    this._platformFlavor = "weinre"
-    this._port           = "weinre"
-        
-    if (true) return
-    
-    // get values from user agent
-    var uas = navigator.userAgent
-    if      (uas.match(/mac os x/i))  this._platform = "mac"
-    else if (uas.match(/macintosh/i)) this._platform = "mac"
-    else if (uas.match(/linux/i))     this._platform = "linux"
-    else if (uas.match(/windows/i))   this._platform = "windows"
-        
-    // get values from hash value
-    var url = window.location.href
-    
-    var splits = url.split("#",2)
-    if (splits.length > 1) {
-        var properties = splits[1]
-        properties = properties.split("&")
-        
-        properties.forEach(function(property) {
-            var pieces = property.split("=")
-            if (pieces.length > 1) {
-                var key = pieces[0]
-                var val = pieces[1]
-                
-                if      (key == "platform")       this._platform       = val
-                else if (key == "platformFlavor") this._platformFlavor = val
-                else if (key == "port")           this._port           = val
-            }
-        })
-        
-    }
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/RemotePanel.scoop b/weinre.web/modules/weinre/client/RemotePanel.scoop
deleted file mode 100644
index fe7c5b8..0000000
--- a/weinre.web/modules/weinre/client/RemotePanel.scoop
+++ /dev/null
@@ -1,255 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Binding
-requireClass ../common/Weinre
-
-requireClass ./ConnectorList
-requireClass ./DOMTemplates as dt
-
-//-----------------------------------------------------------------------------
-class RemotePanel < WebInspector.Panel
-    super("remote")
-
-    this.initialize()
-
-//-----------------------------------------------------------------------------
-method initialize()
-    
-    // main div
-    var 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"
-
-    // create icon
-    var icon = dt.IMG({
-        src: "../images/weinre-icon-128x128.png"
-    })
-    
-    icon.style.float = "right"
-    div.appendChild(icon)
-        
-    // create target and client lists
-    this.targetList = new TargetList()
-    this.clientList = new ClientList()
-    
-    div.appendChild(this.targetList.getElement())
-    div.appendChild(this.clientList.getElement())
-    
-    // create server properties
-    this.serverProperties = dt.DIV({
-        $className: "weinreServerProperties"
-    })
-        
-    div.appendChild(dt.H1("Server Properties"))
-    div.appendChild(this.serverProperties)
-    
-    // add main div to page
-    this.element.appendChild(div)
-    this.reset();
-
-//-----------------------------------------------------------------------------
-method addClient(client) 
-    this.clientList.add(client)
-    
-method addTarget(target) 
-    this.targetList.add(target)
-
-method getTarget(channel)
-    return this.targetList.get(channel)
-    
-method removeClient(channel) 
-    this.clientList.remove(channel)
-    
-method removeTarget(channel) 
-    this.targetList.remove(channel)
-
-method setCurrentClient(channel) 
-    this.clientList.setCurrent(channel)
-    
-method setCurrentTarget(channel) 
-    this.targetList.setCurrent(channel)
-
-method setClientState(channel, state) 
-    this.clientList.setState(channel, state)
-    
-method setTargetState(channel, state) 
-    this.targetList.setState(channel, state)
-
-//-----------------------------------------------------------------------------
-method getNewestTargetChannel(ignoring)
-    return this.targetList.getNewestConnectorChannel(ignoring)
-
-//-----------------------------------------------------------------------------
-method afterInitialConnection
-    this.clientList.afterInitialConnection()
-
-//-----------------------------------------------------------------------------
-method reset
-    this.clientList.removeAll()
-    this.targetList.removeAll()
-    
-    Weinre.WeinreClientCommands.getTargets(Binding(this, "cb_getTargets"))
-    Weinre.WeinreClientCommands.getClients(Binding(this, "cb_getClients"))
-
-//-----------------------------------------------------------------------------
-method connectionClosed
-    this.clientList.removeAll()
-    this.targetList.removeAll()
-
-//-----------------------------------------------------------------------------
-method cb_getTargets(targets)
-    targets.forEach(function(target) {
-        this.addTarget(target)
-    }, this)
-   
-    if (!Weinre.client.autoConnect()) return
-    
-    var newestTargetChannel = this.getNewestTargetChannel()
-    if (!newestTargetChannel) return
-    if (!Weinre.messageDispatcher) return
-
-    Weinre.WeinreClientCommands.connectTarget(Weinre.messageDispatcher.channel, newestTargetChannel)
-    
-    
-//-----------------------------------------------------------------------------
-method cb_getClients(clients)
-    clients.forEach(function(client) {
-        this.addClient(client)
-    }, this)
-
-//-----------------------------------------------------------------------------
-getter toolbarItemClass
-    return "remote"
-
-//-----------------------------------------------------------------------------
-getter toolbarItemLabel
-    return "Remote"
-
-//-----------------------------------------------------------------------------
-getter statusBarItems
-    return []
-
-//-----------------------------------------------------------------------------
-getter defaultFocusedElement
-    return this.contentElement
-
-//-----------------------------------------------------------------------------
-method show
-    super()
-
-//-----------------------------------------------------------------------------
-method hide()
-    super()
-
-//-----------------------------------------------------------------------------
-method setServerProperties(properties)
-    var table = "<table>"
-    var keys = []
-    for (var key in properties) {
-        keys.push(key)
-    }
-    
-    keys = keys.sort()
-    keys.forEach(function(key) {
-        val = properties[key]
-        if (typeof val == "string") {
-            val = val.escapeHTML()
-        }
-        else {
-            var finalVal = ""
-            val.forEach(function(aVal){
-                finalVal += "<li>" + aVal.escapeHTML()
-            })
-            val = "<ul>" + finalVal + "</ul>"
-        }
-        table += "<tr class='weinre-normal-text-size'><td valign='top'>" + key.escapeHTML() + ": <td>" + val
-    })
-    table += "</table>"
-    this.serverProperties.innerHTML = table
-
-//=============================================================================
-//=============================================================================
-    
-//-----------------------------------------------------------------------------
-class TargetList < ConnectorList
-    super("Targets")
-
-//-----------------------------------------------------------------------------
-method getListItem(target)
-    var self = this
-    var text = target.hostName + " [channel: " + target.channel + " id: " + target.id + "]" + " - " + target.url 
-
-    var item = dt.LI(
-        { $connectorChannel: target.channel },
-        text
-    )
-    
-    item.addStyleClass("weinre-connector-item")
-    item.addStyleClass("target")
-    
-    item.addEventListener("click", function(e) {self.connectToTarget(target.channel, e)}, false)
-
-    target.element = item
-
-    return item
-
-//-----------------------------------------------------------------------------
-method connectToTarget(targetChannel, event)
-    if (event) {
-        event.preventDefault()
-        event.stopPropagation()
-    }
-    
-    var target = this.connectors[targetChannel]
-    if (!target) return false
-    if (target.closed) return false
-    
-    Weinre.WeinreClientCommands.connectTarget(Weinre.messageDispatcher.channel, targetChannel)
-    
-    return false
-
-//=============================================================================
-//=============================================================================
-
-//-----------------------------------------------------------------------------
-class ClientList < ConnectorList
-    super("Clients")
-
-    this.noneItem.innerHTML = "Waiting for connection..."
-
-//-----------------------------------------------------------------------------
-method afterInitialConnection()
-    this.noneItem.innerHTML = "Connection lost, reload this page to reconnect."
-    this.noneItem.addStyleClass("error")
-
-//-----------------------------------------------------------------------------
-method getListItem(client)
-    var text = client.hostName + " [channel: " + client.channel + " id: " + client.id  + "]"
-    
-    var 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
-    
-    return item
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/WeinreClientEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
deleted file mode 100644
index 7fd6a65..0000000
--- a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
+++ /dev/null
@@ -1,105 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Callback
-requireClass ../common/Weinre
-
-requireClass ./WeinreExtraTargetEventsImpl
-
-//-----------------------------------------------------------------------------
-class WeinreClientEventsImpl(client)
-    this.client = client
-
-//-----------------------------------------------------------------------------
-init
-    var titleNotConnected    = "weinre: target not connected"
-    var titleConnectedPrefix = "weinre: "
-    
-    document.title = titleNotConnected
-     
-//-----------------------------------------------------------------------------
-method clientRegistered(clientDescription)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.addClient(clientDescription)
-    }
-
-//-----------------------------------------------------------------------------
-method targetRegistered(targetDescription)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.addTarget(targetDescription)
-    }
-    
-    if (!Weinre.client.autoConnect()) return
-    if (!Weinre.messageDispatcher) return
-    
-    Weinre.WeinreClientCommands.connectTarget(Weinre.messageDispatcher.channel, targetDescription.channel)
-
-//-----------------------------------------------------------------------------
-method clientUnregistered(/*int*/ clientChannel)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.removeClient(clientChannel)
-    }
-
-//-----------------------------------------------------------------------------
-method targetUnregistered(/*int*/ targetChannel)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.removeTarget(targetChannel)
-    }
-
-//-----------------------------------------------------------------------------
-method connectionCreated(/*int*/ clientChannel, /*int*/ targetChannel)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.setClientState(clientChannel, "connected") 
-        WebInspector.panels.remote.setTargetState(targetChannel, "connected")
-    }
-    
-    if (clientChannel != Weinre.messageDispatcher.channel) return
-
-    WebInspector.panels.elements.reset()
-    WebInspector.panels.timeline._clearPanel()
-    WebInspector.panels.resources.reset()
-    
-    var target = WebInspector.panels.remote.getTarget(targetChannel)
-    document.title = titleConnectedPrefix + target.url
-    
-    WebInspector.inspectedURLChanged(target.url)
-
-    Weinre.WeinreExtraClientCommands.getDatabases(function(databaseRecords) {
-        WeinreExtraTargetEventsImpl.addDatabaseRecords(databaseRecords)
-    })
-    
-//-----------------------------------------------------------------------------
-method connectionDestroyed(/*int*/ clientChannel, /*int*/ targetChannel)
-
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.setClientState(clientChannel, "not-connected") 
-        WebInspector.panels.remote.setTargetState(targetChannel, "not-connected")
-    }
-    
-    if (clientChannel != Weinre.messageDispatcher.channel) return
-
-    document.title = titleNotConnected
-    
-    if (!Weinre.client.autoConnect()) return
-    if (!this.client.uiAvailable()) return
-    
-    var nextTargetChannel = WebInspector.panels.remote.getNewestTargetChannel(targetChannel)
-    if (!nextTargetChannel) return
-    
-    Weinre.WeinreClientCommands.connectTarget(Weinre.messageDispatcher.channel, nextTargetChannel)
-    Weinre.logInfo("autoconnecting to " + nextTargetChannel)
-
-//-----------------------------------------------------------------------------
-method sendCallback(/*int*/ callbackId, /*any*/ result)
-    Callback.invoke(callbackId, result)
-
-//-----------------------------------------------------------------------------
-method serverProperties(/*any*/ properties)
-    if (this.client.uiAvailable()) {
-        WebInspector.panels.remote.setServerProperties(properties)
-    }
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/client/WeinreExtraTargetEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop
deleted file mode 100644
index 76abaad..0000000
--- a/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-class WeinreExtraTargetEventsImpl
-
-//-----------------------------------------------------------------------------
-method databaseOpened(databaseRecord)
-    WeinreExtraTargetEventsImpl.addDatabaseRecords([databaseRecord])
-    
-//-----------------------------------------------------------------------------
-static method addDatabaseRecords(databaseRecords)
-    // databaseRecord.id
-    // databaseRecord.domain
-    // databaseRecord.name
-    // databaseRecord.version
-    
-    if (!WebInspector.panels) return
-    if (!WebInspector.panels.resources) return
-    if (!WebInspector.panels.resources._databases) return
-    
-    var existingDbs = WebInspector.panels.resources._databases
-    var existingDbNames = {}
-    
-    for (var i=0; i<existingDbs.length; i++) {
-        existingDbNames[existingDbs[i].name] = existingDbs[i]
-    }
-    
-    for (var i=0; i<databaseRecords.length; i++) {
-        if (existingDbNames[databaseRecords[i].name]) continue
-        
-        var databaseRecord = databaseRecords[i]
-        var database = new WebInspector.Database(
-            databaseRecord.id,
-            databaseRecord.domain,
-            databaseRecord.name,
-            databaseRecord.version
-            )
-        
-        WebInspector.panels.resources.addDatabase(database)
-    }
-    
-    
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/Binding.scoop b/weinre.web/modules/weinre/common/Binding.scoop
deleted file mode 100644
index 9db156b..0000000
--- a/weinre.web/modules/weinre/common/Binding.scoop
+++ /dev/null
@@ -1,20 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-
-//-----------------------------------------------------------------------------
-class Binding(receiver, method)
-
-    if (receiver == null) throw new Ex(arguments, "receiver argument for Binding constructor was null")
-    if (typeof(method) == "string") method = receiver[method]
-    if (typeof(method) != "function") throw new Ex(arguments, "method argument didn't specify a function")
-
-    return function() {
-        return method.apply(receiver, [].slice.call(arguments))
-    }
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/Callback.scoop b/weinre.web/modules/weinre/common/Callback.scoop
deleted file mode 100644
index b2edba1..0000000
--- a/weinre.web/modules/weinre/common/Callback.scoop
+++ /dev/null
@@ -1,69 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-
-//-----------------------------------------------------------------------------
-class Callback
-    throw new Ex(arguments, "this class is not intended to be instantiated")
-
-//-----------------------------------------------------------------------------
-init 
-    var CallbackTable    = {}
-    var CallbackIndex    = 1
-    var ConnectorChannel = "???"
-
-//-----------------------------------------------------------------------------
-static method setConnectorChannel(connectorChannel)
-    ConnectorChannel = ""  + connectorChannel
-
-//-----------------------------------------------------------------------------
-static method register(callback)
-    if (typeof callback == "function") callback = [null, callback]
-    if (typeof callback.slice != "function") throw new Ex(arguments, "callback must be an array or function")
-    
-    var receiver = callback[0]
-    var func     = callback[1]
-    var data     = callback.slice(2)
-    
-    if (typeof func == "string") func = receiver.func
-    if (typeof func != "function") throw new Ex(arguments, "callback function was null or not found")
-
-    var index = ConnectorChannel + "::" + CallbackIndex
-    
-    CallbackIndex++
-    if (CallbackIndex >= 65536 * 65536) CallbackIndex = 1
-    
-    CallbackTable[index] = [receiver, func, data]
-    
-    return index
-
-//-----------------------------------------------------------------------------
-static method deregister(index)
-    delete CallbackTable[index]
-
-//-----------------------------------------------------------------------------
-static method invoke(index, args)
-    var callback = CallbackTable[index]
-    if (!callback) throw new Ex(arguments, "callback " + index + " not registered or already invoked")
-    
-    var receiver = callback[0]
-    var func     = callback[1]
-    var args     = callback[2].concat(args)
-    
-    try {
-        func.apply(receiver,args)
-    }
-    catch (e) {
-        var funcName = func.name
-        if (!funcName) funcName = "<unnamed>"
-        require("./Weinre").getClass().logError(arguments.callee.signature + " exception invoking callback: " + funcName + "(" + args.join(",") + "): " + e)
-    }
-    finally {
-        Callback.deregister(index)
-    }
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/EventListeners.scoop b/weinre.web/modules/weinre/common/EventListeners.scoop
deleted file mode 100644
index 0c9d987..0000000
--- a/weinre.web/modules/weinre/common/EventListeners.scoop
+++ /dev/null
@@ -1,56 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-requireClass ./Weinre
-
-//-----------------------------------------------------------------------------
-class EventListeners
-    this._listeners = []
-
-//-----------------------------------------------------------------------------
-method add(listener, useCapture)
-    this._listeners.push([listener, useCapture])
-
-//-----------------------------------------------------------------------------
-method remove(listener, useCapture)
-    for (var i=0; i<this._listeners.length; i++) {
-        var listener = this._listeners[i]
-        if (listener[0] != listener) continue;
-        if (listener[1] != useCapture) continue;
-        
-        this._listeners.splice(i,1)
-        return
-    }
-
-//-----------------------------------------------------------------------------
-method fire(event)
-    this._listeners.slice().forEach(function(listener) {
-        var listener = listener[0]
-        
-        if (typeof listener == "function") {
-            try {
-                listener.call(null, event)
-            }
-            catch(e) {
-                Weinre.logError(arguments.callee.signature + " invocation exception: " + e)
-            }
-            return
-        }
-        
-        if (typeof listener.handleEvent != "function") {
-            throw new Ex(arguments, "listener does not implement the handleEvent() method")
-        }
-        
-        try {
-            listener.handleEvent.call(listener, event)
-        }
-        catch(e) {
-            Weinre.logError(arguments.callee.signature + " invocation exception: " + e)
-        }
-    })
diff --git a/weinre.web/modules/weinre/common/Ex.coffee b/weinre.web/modules/weinre/common/Ex.coffee
new file mode 100644
index 0000000..4062188
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Ex.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
+#---------------------------------------------------------------------------------
+
+StackTrace = require('./StackTrace')
+
+#-------------------------------------------------------------------------------
+module.exports = class Ex
+
+    #---------------------------------------------------------------------------
+    @catching: (func) ->
+        try
+            func.call(this)
+        catch e
+            console.log "runtime error: #{e}"
+            StackTrace.dump arguments
+
+    #---------------------------------------------------------------------------
+    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
+        message = prefix(args, message)
+        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/Ex.scoop b/weinre.web/modules/weinre/common/Ex.scoop
deleted file mode 100644
index e6d0bf7..0000000
--- a/weinre.web/modules/weinre/common/Ex.scoop
+++ /dev/null
@@ -1,30 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./StackTrace
-
-//-----------------------------------------------------------------------------
-class Ex(args, message)
-    if (!args || !args.callee) {
-        throw Ex(arguments, "first parameter must be an Arguments object") 
-    }
-    
-    StackTrace.dump(args)
-    
-    if (message instanceof Error) {
-        message = "threw error: " + message
-    }
-    
-    return new Error(prefix(args, message))
-    
-//-----------------------------------------------------------------------------
-function prefix(args, string)
-    if (args.callee.signature)   return args.callee.signature +   ": " + string
-    if (args.callee.displayName) return args.callee.displayName + ": " + string
-    if (args.callee.name)        return args.callee.name +        ": " + string
-    return "<anonymous>" +                                        ": " + string
diff --git a/weinre.web/modules/weinre/common/HookLib.coffee b/weinre.web/modules/weinre/common/HookLib.coffee
new file mode 100644
index 0000000..9f2b868
--- /dev/null
+++ b/weinre.web/modules/weinre/common/HookLib.coffee
@@ -0,0 +1,114 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+HookLib = exports
+
+#-------------------------------------------------------------------------------
+HookSites   = []
+IgnoreHooks = 0
+
+module.exports = class HookLib
+
+    #---------------------------------------------------------------------------
+    @addHookSite = (object, property) ->
+        getHookSite object, property, true
+
+    #---------------------------------------------------------------------------
+    @getHookSite = (object, property) ->
+        getHookSite object, property, false
+
+    #---------------------------------------------------------------------------
+    @ignoreHooks = (func) ->
+        try
+            IgnoreHooks++
+            result = func.call()
+        finally
+            IgnoreHooks--
+        result
+
+#-------------------------------------------------------------------------------
+getHookSite = (object, property, addIfNotFound) ->
+    i = 0
+
+    for hookSite in HookSites
+        continue  unless hookSite.object == object
+        continue  unless hookSite.property == property
+        return hookSite
+
+    return null unless addIfNotFound
+
+    hookSite = new HookSite(object, property)
+    HookSites.push hookSite
+    hookSite
+
+#-------------------------------------------------------------------------------
+class HookSite
+
+    #---------------------------------------------------------------------------
+    constructor: (object, property) ->
+        @object = object
+        @property = property
+        @target = object[property]
+        @hookss = []
+
+        hookedFunction   = getHookedFunction(@target, this)
+        object[property] = hookedFunction
+
+    #---------------------------------------------------------------------------
+    addHooks: (hooks) ->
+        @hookss.push hooks
+
+    #---------------------------------------------------------------------------
+    removeHooks: (hooks) ->
+        for i in [0..@hookss.length]
+            if @hookss[i] == hooks
+                @hookss.splice i, 1
+                return
+
+#-------------------------------------------------------------------------------
+getHookedFunction = (func, hookSite) ->
+    hookedFunction = ->
+
+        callBeforeHooks hookSite, this, arguments
+        try
+            result = func.apply(this, arguments)
+        catch e
+            callExceptHooks hookSite, this, arguments, e
+            throw e
+        finally
+            callAfterHooks hookSite, this, arguments, result
+
+        result
+
+    hookedFunction.displayName = func.displayName || func.name
+
+    hookedFunction
+
+#-------------------------------------------------------------------------------
+callBeforeHooks = (hookSite, receiver, args) ->
+    return if IgnoreHooks > 0
+
+    for hooks in hookSite.hookss
+        hooks.before.call hooks, receiver, args if hooks.before
+
+#-------------------------------------------------------------------------------
+callAfterHooks = (hookSite, receiver, args, result) ->
+    return if IgnoreHooks > 0
+
+    for hooks in hookSite.hookss
+        hooks.after.call hooks, receiver, args, result if hooks.after
+
+#-------------------------------------------------------------------------------
+callExceptHooks = (hookSite, receiver, args, e) ->
+    return if IgnoreHooks > 0
+
+    for hooks in hookSite.hookss
+        hooks.except.call hooks, receiver, args, e if hooks.except
+
+#-------------------------------------------------------------------------------
+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/IDGenerator.scoop b/weinre.web/modules/weinre/common/IDGenerator.scoop
deleted file mode 100644
index 5af26cd..0000000
--- a/weinre.web/modules/weinre/common/IDGenerator.scoop
+++ /dev/null
@@ -1,53 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-class IDGenerator
-
-//-----------------------------------------------------------------------------
-init
-    var nextIdValue = 1
-    var idName      = "__weinre__id"
-
-//-----------------------------------------------------------------------------
-static method checkId(object)
-    return object[idName]
-    
-//-----------------------------------------------------------------------------
-static method getId(object, map)
-    var id = IDGenerator.checkId(object)
-    
-    if (!id) {
-        id = nextId()
-        
-        // note:
-        // attempted to use Object.defineProperty() to make
-        // the id property non-enumerable, etc, but doesn't 
-        // work in JSC (TypeError), and still shows up in
-        // Web Inspector property views anyway.
-        object[idName] = id
-    }
-    
-    if (map) {
-        if (map[id] != object) {
-            map[id] = object
-        }
-    }
-    
-    return id
-
-//-----------------------------------------------------------------------------
-static method next
-    return nextId()
-
-//-----------------------------------------------------------------------------
-function nextId
-    var result = nextIdValue
-    nextIdValue += 1
-    return result
-
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/IDLTools.scoop b/weinre.web/modules/weinre/common/IDLTools.scoop
deleted file mode 100644
index 9dcbe7a..0000000
--- a/weinre.web/modules/weinre/common/IDLTools.scoop
+++ /dev/null
@@ -1,123 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-requireClass ./Callback
-
-//-----------------------------------------------------------------------------
-class IDLTools
-    throw new Ex(arguments, "this class is not intended to be instantiated")
-
-//-----------------------------------------------------------------------------
-init
-    var IDLs = {}
-    IDLTools._idls = IDLs
-
-//-----------------------------------------------------------------------------
-static method addIDLs(idls)
-    idls.forEach(function(idl){
-        idl.interfaces.forEach(function(intf){
-            IDLs[intf.name] = intf
-            intf.module = idl.name
-        })
-    })
-
-//-----------------------------------------------------------------------------
-static method getIDL(name)
-    return IDLs[name]
-
-//-----------------------------------------------------------------------------
-static method getIDLsMatching(regex)
-    var results = []
-    
-    for (var intfName in IDLs) {
-        var intf = IDLs[intfName]
-        if (intfName.match(regex)) {
-            results.push(intf)
-        }
-    }
-    return results
-
-//-----------------------------------------------------------------------------
-static method validateAgainstIDL(klass, interfaceName)
-    var intf = IDLTools.getIDL(interfaceName)
-    var messagePrefix = "IDL validation for " + interfaceName + ": "
-    if (null == intf) throw new Ex(arguments, messagePrefix + "idl not found: '" + interfaceName + "'")
-    
-    var errors = []
-    // check that class implements all the methods
-    intf.methods.forEach(function(intfMethod) {
-        var classMethod  = klass.prototype[intfMethod.name]
-        var printName    = klass.name + "::" + intfMethod.name
-        
-        if (null == classMethod) {
-            errors.push(messagePrefix + "method not implemented: '" + printName + "'")
-            return
-        }
-
-        if (classMethod.length != intfMethod.parameters.length) {
-            if (classMethod.length != intfMethod.parameters.length + 1) {
-                errors.push(messagePrefix + "wrong number of parameters: '" + printName + "'")
-                return
-            }
-        }
-    })
-    
-    // check that class doesn't implement extra methods
-    for (var propertyName in klass.prototype) {
-        if (klass.prototype.hasOwnProperty(propertyName)) continue
-        if (propertyName.match(/^_.*/)) continue
-        
-        var printName = klass.name + "::" + propertyName
-        
-        if (!intf.methods[propertyName]) {
-            errors.push(messagePrefix + "method should not be implemented: '" + printName + "'")
-            continue
-        }
-    }
-    
-    if (!errors.length) return
-    
-    errors.forEach(function(error){
-        require("./Weinre").getClass().logError(error)
-    })
-
-//-----------------------------------------------------------------------------
-static method buildProxyForIDL(proxyObject, interfaceName)
-    var intf = IDLTools.getIDL(interfaceName)
-    var messagePrefix = "building proxy for IDL " + interfaceName + ": "
-    if (null == intf) throw new Ex(arguments, messagePrefix + "idl not found: '" + interfaceName + "'")
-    
-    intf.methods.forEach(function(intfMethod) {
-        proxyObject[intfMethod.name] = getProxyMethod(intf, intfMethod)
-    })
-
-//-----------------------------------------------------------------------------
-function getProxyMethod(intf, method)
-    var result = function proxyMethod() {
-        var callbackId = null
-        var 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)
-
-        this.__invoke(intf.name, method.name, args)
-    }
-    
-    result.displayName = intf.name + "__" + method.name
-    return result
diff --git a/weinre.web/modules/weinre/common/MessageDispatcher.coffee b/weinre.web/modules/weinre/common/MessageDispatcher.coffee
new file mode 100644
index 0000000..a732cad
--- /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.notImplemented "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/MessageDispatcher.scoop b/weinre.web/modules/weinre/common/MessageDispatcher.scoop
deleted file mode 100644
index c207e01..0000000
--- a/weinre.web/modules/weinre/common/MessageDispatcher.scoop
+++ /dev/null
@@ -1,204 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Weinre
-requireClass ./WebSocketXhr
-requireClass ./IDLTools
-requireClass ./Binding
-requireClass ./Ex
-requireClass ./Callback
-
-//-----------------------------------------------------------------------------
-class MessageDispatcher(url, id)
-    if (!id) {
-        id = "anonymous"
-    }
-
-    this._url        = url
-    this._id         = id
-    
-    this.error       = null
-    this._opening    = false
-    this._opened     = false
-    this._closed     = false
-    this._interfaces = {}
-    
-    this._open()
-
-//-----------------------------------------------------------------------------
-init
-    var Verbose = false
-    var InspectorBackend
-
-//-----------------------------------------------------------------------------
-static method setInspectorBackend(inspectorBackend)
-    InspectorBackend = inspectorBackend
-    
-//-----------------------------------------------------------------------------
-static method verbose(value)
-    if (arguments.length >= 1) {
-        Verbose = !!value
-    }
-    
-    return Verbose
-
-//-----------------------------------------------------------------------------
-method _open
-    if (this._opened || this._opening) return
-    if (this._closed) throw new Ex(arguments, "socket has already been closed")
-    
-    this._opening = true 
-    this._socket = new WebSocketXhr(this._url, this._id)
-    this._socket.addEventListener("open",    Binding(this, "_handleOpen"))
-    this._socket.addEventListener("error",   Binding(this, "_handleError"))
-    this._socket.addEventListener("message", Binding(this, "_handleMessage"))
-    this._socket.addEventListener("close",   Binding(this, "_handleClose"))
-
-//-----------------------------------------------------------------------------
-method close
-    if (this._closed) return
-    
-    this._opened = false
-    this._closed = true
-    
-    this._socket.close()
-
-//-----------------------------------------------------------------------------
-method send(data)
-    this._socket.send(data)
-
-//-----------------------------------------------------------------------------
-method getWebSocket
-    return this._socket
-
-//-----------------------------------------------------------------------------
-method registerInterface(intfName, intf, validate)
-    if (validate) IDLTools.validateAgainstIDL(intf.constructor, intfName)
-    
-    if (this._interfaces[intfName]) throw new Ex(arguments, "interface " + intfName + " has already been registered")
-    
-    this._interfaces[intfName] = intf
-
-//-----------------------------------------------------------------------------
-method createProxy(intfName)
-    var proxy = {}
-    
-    IDLTools.buildProxyForIDL(proxy, intfName)
-
-    var self = this
-    proxy.__invoke = function __invoke(intfName, methodName, args) {
-        self._sendMethodInvocation(intfName, methodName, args)
-    }
-    
-    return proxy
-
-//-----------------------------------------------------------------------------
-method _sendMethodInvocation(intfName, methodName, args)
-    if (typeof intfName   != "string") throw new Ex(arguments, "expecting intf parameter to be a string")
-    if (typeof methodName != "string") throw new Ex(arguments, "expecting method parameter to be a string")
-    
-    var data = {
-            "interface": intfName,
-            "method":    methodName,
-            "args":      args
-    }
-    
-    data = JSON.stringify(data)
-    
-    this._socket.send(data)
-    
-    if (Verbose) {
-        Weinre.logDebug(this.constructor.name + "[" + this._url + "]: send " + intfName + "." + methodName + "(" + JSON.stringify(args) + ")")
-    }
-
-//-----------------------------------------------------------------------------
-method getState
-    if (this._opening) return "opening"
-    if (this._opened)  return "opened"
-    if (this._closed)  return "closed"
-    return "unknown"
-
-//-----------------------------------------------------------------------------
-method isOpen
-    return this._opened == true
-
-//-----------------------------------------------------------------------------
-method _handleOpen(event)
-    this._opening = false
-    this._opened  = true
-    this.channel  = event.channel
-    
-    Callback.setConnectorChannel(this.channel)
-    
-    if (Verbose) {
-        Weinre.logDebug(this.constructor.name + "[" + this._url + "]: opened")
-    }
-
-//-----------------------------------------------------------------------------
-method _handleError(message)
-    this.error = message
-    this.close()
-
-    if (Verbose) {
-        Weinre.logDebug(this.constructor.name + "[" + this._url + "]: error: " + message)
-    }
-
-//-----------------------------------------------------------------------------
-method _handleMessage(message)
-    var data
-    
-    try {
-        data = JSON.parse(message.data)
-    }
-    catch (e) {
-        throw new Ex(arguments, "invalid JSON data received: " + e + ": '" + message.data + "'")
-    }
-
-    var intfName   = data["interface"]
-    var methodName = data.method
-    var args       = data.args
-
-    var methodSignature = intfName + "." + methodName + "()"
-    
-    var intf = this._interfaces.hasOwnProperty(intfName) && this._interfaces[intfName]
-    
-    if (!intf && InspectorBackend && intfName.match(/.*Notify/)) {
-        intf = InspectorBackend.getRegisteredDomainDispatcher(intfName.substr(0,intfName.length-6))
-    }
-    
-    if (!intf) {
-        Weinre.logWarning("weinre: request for non-registered interface:" + methodSignature)
-        return
-    }
-    
-    methodSignature = intf.constructor.name + "." + methodName + "()"
-    
-    var method = intf[methodName]
-    if (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(this.constructor.name + "[" + this._url + "]: recv " + intfName + "." + methodName + "(" + JSON.stringify(args) + ")")
-    }
-
-//-----------------------------------------------------------------------------
-method _handleClose
-    this._reallyClosed = true
-    
-    if (Verbose) {
-        Weinre.logDebug(this.constructor.name + "[" + this._url + "]: closed")
-    }
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.scoop b/weinre.web/modules/weinre/common/Native.scoop
deleted file mode 100644
index af28f0b..0000000
--- a/weinre.web/modules/weinre/common/Native.scoop
+++ /dev/null
@@ -1,43 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-class Native
-
-//-----------------------------------------------------------------------------
-init
-    Native.original = {}
-
-    Native.original.clearInterval             = window.clearInterval
-    Native.original.clearTimeout              = window.clearTimeout
-    Native.original.setTimeout                = window.setTimeout
-    Native.original.setInterval               = window.setInterval
-    Native.original.XMLHttpRequest            = window.XMLHttpRequest
-    Native.original.XMLHttpRequest_open       = window.XMLHttpRequest.prototype.open
-    Native.original.LocalStorage_setItem      = window.localStorage   ? window.localStorage.setItem      : null
-    Native.original.LocalStorage_removeItem   = window.localStorage   ? window.localStorage.removeItem   : null
-    Native.original.LocalStorage_clear        = window.localStorage   ? window.localStorage.clear        : null
-    Native.original.SessionStorage_setItem    = window.sessionStorage ? window.sessionStorage.setItem    : null
-    Native.original.SessionStorage_removeItem = window.sessionStorage ? window.sessionStorage.removeItem : null
-    Native.original.SessionStorage_clear      = window.sessionStorage ? window.sessionStorage.clear      : null
-    Native.original.openDatabase              = window.openDatabase
-
-    Native.clearInterval             = function() { return Native.original.clearInterval.apply( window, [].slice.call(arguments))}
-    Native.clearTimeout              = function() { return Native.original.clearTimeout.apply(  window, [].slice.call(arguments))}
-    Native.setInterval               = function() { return Native.original.setInterval.apply(   window, [].slice.call(arguments))}
-    Native.setTimeout                = function() { return Native.original.setTimeout.apply(    window, [].slice.call(arguments))}
-    Native.XMLHttpRequest            = function() { return new Native.original.XMLHttpRequest()}
-    Native.XMLHttpRequest_open       = function() { return Native.original.XMLHttpRequest_open.apply(this, [].slice.call(arguments))}
-    Native.LocalStorage_setItem      = function() { return Native.original.LocalStorage_setItem.apply(      window.localStorage,   [].slice.call(arguments))}
-    Native.LocalStorage_removeItem   = function() { return Native.original.LocalStorage_removeItem.apply(   window.localStorage,   [].slice.call(arguments))}
-    Native.LocalStorage_clear        = function() { return Native.original.LocalStorage_clear.apply(        window.localStorage,   [].slice.call(arguments))}
-    Native.SessionStorage_setItem    = function() { return Native.original.SessionStorage_setItem.apply(    window.sessionStorage, [].slice.call(arguments))}
-    Native.SessionStorage_removeItem = function() { return Native.original.SessionStorage_removeItem.apply( window.sessionStorage, [].slice.call(arguments))}
-    Native.SessionStorage_clear      = function() { return Native.original.SessionStorage_clear.apply(      window.sessionStorage, [].slice.call(arguments))}
-    Native.openDatabase              = function() { return Native.original.openDatabase.apply( window, [].slice.call(arguments))}
-
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/StackTrace.scoop b/weinre.web/modules/weinre/common/StackTrace.scoop
deleted file mode 100644
index a46b602..0000000
--- a/weinre.web/modules/weinre/common/StackTrace.scoop
+++ /dev/null
@@ -1,55 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-class StackTrace(args)
-    if (!args || !args.callee) {
-        throw Error("first parameter to " + arguments.callee.signature + " must be an Arguments object") 
-    }
-    
-    this.trace = getTrace(args)
-
-//-----------------------------------------------------------------------------
-static method dump(args)
-    args = args || arguments
-    
-    var stackTrace = new StackTrace(args)
-    stackTrace.dump()
-
-//-----------------------------------------------------------------------------
-method dump
-    console.log("StackTrace:")
-    this.trace.forEach(function(frame) {
-        console.log("    " + frame)
-    })
-    
-//-----------------------------------------------------------------------------
-function getTrace(args)
-
-    var result = []
-    var visitedFuncs = []
-    
-    var 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>")
-
-        if (-1 != visitedFuncs.indexOf(func)) {
-            result.push("... recursion")
-            return result
-        }
-        
-        visitedFuncs.push(func)
-        
-        func = func.caller
-    }
-    
-    return result
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/common/WebSocketXhr.coffee b/weinre.web/modules/weinre/common/WebSocketXhr.coffee
new file mode 100644
index 0000000..00c741e
--- /dev/null
+++ b/weinre.web/modules/weinre/common/WebSocketXhr.coffee
@@ -0,0 +1,207 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+HookLib        = require('./HookLib')
+EventListeners = require('./EventListeners')
+
+#-------------------------------------------------------------------------------
+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
+
+        HookLib.ignoreHooks ->
+            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
+
+        HookLib.ignoreHooks ->
+            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
+
+        HookLib.ignoreHooks ->
+            xhr.open method, url, true
+
+        xhr.setRequestHeader "Content-Type", "text/plain"
+
+        HookLib.ignoreHooks ->
+            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/WebSocketXhr.scoop b/weinre.web/modules/weinre/common/WebSocketXhr.scoop
deleted file mode 100644
index 813e6a9..0000000
--- a/weinre.web/modules/weinre/common/WebSocketXhr.scoop
+++ /dev/null
@@ -1,211 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-requireClass ./Weinre
-requireClass ./EventListeners
-requireClass ./Native
-
-//-----------------------------------------------------------------------------
-class WebSocketXhr(url, id)
-    this.initialize(url, id)
-    
-//-----------------------------------------------------------------------------
-init
-    var XMLHttpRequest = Native.XMLHttpRequest
-    
-    WebSocketXhr.CONNECTING = 0
-    WebSocketXhr.OPEN       = 1
-    WebSocketXhr.CLOSING    = 2
-    WebSocketXhr.CLOSED     = 3
-
-//-----------------------------------------------------------------------------
-method initialize(url, id)
-    if (!id) {
-        id = "anonymous"
-    }
-    
-    this.readyState      = WebSocketXhr.CONNECTING 
-    this._url            = url
-    this._id             = id
-    this._urlChannel     = null
-    this._queuedSends    = []
-    this._sendInProgress = true
-    
-    this._listeners = {
-        open:    new EventListeners(),
-        message: new EventListeners(),
-        error:   new EventListeners(),
-        close:   new EventListeners()
-    }
-    
-    this._getChannel()
-
-//-----------------------------------------------------------------------------
-method _getChannel
-    var body = JSON.stringify({ id: this._id})
-    this._xhr(this._url, "POST", body, this._handleXhrResponseGetChannel)
-
-//-----------------------------------------------------------------------------
-method _handleXhrResponseGetChannel(xhr)
-    if (xhr.status != 200) return this._handleXhrResponseError(xhr)
-
-    try {
-        var object = JSON.parse(xhr.responseText)
-    }
-    catch (e) {
-        this._fireEventListeners("error", {message: "non-JSON response from channel open request"})
-        this.close()
-        return
-    }
-    
-    if (!object.channel) {
-        this._fireEventListeners("error", {message: "channel open request did not include a channel"})
-        this.close()
-        return
-    }
-    
-    this._urlChannel = this._url + "/" + object.channel
-    this.readyState = WebSocketXhr.OPEN
-
-    this._fireEventListeners("open", { message: "open", channel: object.channel })
-    
-    this._sendInProgress = false
-    this._sendQueued()
-    
-    this._readLoop()
-
-//-----------------------------------------------------------------------------
-method _readLoop
-    if (this.readyState == WebSocketXhr.CLOSED) return
-    if (this.readyState == WebSocketXhr.CLOSING) return
-    
-    this._xhr(this._urlChannel, "GET", "", this._handleXhrResponseGet)
-
-//-----------------------------------------------------------------------------
-method _handleXhrResponseGet(xhr)
-    var self = this
-    
-    if (xhr.status != 200) return this._handleXhrResponseError(xhr)
-
-    try {
-        var datum = JSON.parse(xhr.responseText)
-    }
-    catch (e) {
-        this.readyState = WebSocketXhr.CLOSED
-        
-        this._fireEventListeners("error", {
-            message: "non-JSON response from read request"
-        })
-        return
-    }
-    
-    Native.setTimeout(function() {self._readLoop()}, 0)
-
-    datum.forEach(function(data) {
-        self._fireEventListeners("message", {data: data})
-    })
-
-//-----------------------------------------------------------------------------
-method send(data)
-    if (typeof data != "string") throw new Ex(arguments, this.constructor.name + "." + this.caller)
-
-    this._queuedSends.push(data)
-    
-    if (this._sendInProgress) return
-    this._sendQueued();
-
-//-----------------------------------------------------------------------------
-method _sendQueued
-    if (this._queuedSends.length == 0) return
-    if (this.readyState == WebSocketXhr.CLOSED) return
-    if (this.readyState == WebSocketXhr.CLOSING) return
-    
-    datum = JSON.stringify(this._queuedSends)
-    this._queuedSends = []
-    
-    this._sendInProgress = true
-    
-    this._xhr(this._urlChannel, "POST", datum, this._handleXhrResponseSend)
-
-//-----------------------------------------------------------------------------
-method _handleXhrResponseSend(xhr)
-    var httpSocket = this
-    
-    if (xhr.status != 200) return this._handleXhrResponseError(xhr)
-    
-    this._sendInProgress = false
-    Native.setTimeout(function() {httpSocket._sendQueued()}, 0)
-
-//-----------------------------------------------------------------------------
-method close
-    this._sendInProgress = true
-    this.readyState = WebSocketXhr.CLOSING
-    this._fireEventListeners("close", {
-        message: "closing",
-        wasClean: true
-    })
-    this.readyState = WebSocketXhr.CLOSED
-
-//-----------------------------------------------------------------------------
-method addEventListener(type, listener, useCapture)
-    this._getListeners(type).add(listener, useCapture)
-
-//-----------------------------------------------------------------------------
-method removeEventListener(type, listener, useCapture)
-    this._getListeners(type).remove(listener, useCapture)
-
-//-----------------------------------------------------------------------------
-method _fireEventListeners(type, event)
-    if (this.readyState == WebSocketXhr.CLOSED) return
-    
-    event.target = this
-    this._getListeners(type).fire(event)
-
-//-----------------------------------------------------------------------------
-method _getListeners(type)
-    var listeners = this._listeners[type]
-    if (null == listeners) throw new Ex(arguments, "invalid event listener type: '" + type + "'")
-    
-    return listeners
-
-//-----------------------------------------------------------------------------
-method _handleXhrResponseError(xhr)
-    if (xhr.status == 404) {
-        this.close()
-        return
-    }
-    
-    this._fireEventListeners("error", {
-        target: this,
-        status: xhr.status,
-        message: "error from XHR invocation: " + xhr.statusText
-    })
-    
-    Weinre.logError("error from XHR invocation: " + xhr.status + ": " + xhr.statusText)
-
-//-----------------------------------------------------------------------------
-method _xhr(url, method, data, handler)
-    if (null == handler) throw new Ex(arguments, "handler must not be null")
-    
-    var 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)
-
-//-----------------------------------------------------------------------------
-function _xhrEventHandler(event)
-    var xhr = event.target
-    if (xhr.readyState != 4) return
-    
-    xhr.httpSocketHandler.call(xhr.httpSocket, xhr) 
diff --git a/weinre.web/modules/weinre/common/Weinre.coffee b/weinre.web/modules/weinre/common/Weinre.coffee
new file mode 100644
index 0000000..decb80b
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Weinre.coffee
@@ -0,0 +1,88 @@
+
+#---------------------------------------------------------------------------------
+# 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
+logger              = null
+
+#-------------------------------------------------------------------------------
+module.exports = class Weinre
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        throw new Ex(arguments, "this class is not intended to be instantiated")
+
+    #---------------------------------------------------------------------------
+    @addIDLs: (idls) ->
+        IDLTools.addIDLs idls
+
+    #---------------------------------------------------------------------------
+    @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/common/Weinre.scoop b/weinre.web/modules/weinre/common/Weinre.scoop
deleted file mode 100644
index 75c37d0..0000000
--- a/weinre.web/modules/weinre/common/Weinre.scoop
+++ /dev/null
@@ -1,80 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ./Ex
-requireClass ./IDLTools
-requireClass ./StackTrace
-
-//-----------------------------------------------------------------------------
-class Weinre
-    throw new Ex(arguments, "this class is not intended to be instantiated")
-
-//-----------------------------------------------------------------------------
-init 
-    var _notImplemented     = {}
-    var _showNotImplemented = false
-    var CSSProperties       = []
-    var logger              = null
-
-//-----------------------------------------------------------------------------
-static method addIDLs(idls)
-    IDLTools.addIDLs(idls)
-
-//-----------------------------------------------------------------------------
-static method addCSSProperties(cssProperties)
-    CSSProperties = cssProperties
-
-//-----------------------------------------------------------------------------
-static method getCSSProperties
-    return CSSProperties
-
-//-----------------------------------------------------------------------------
-static method deprecated()
-    StackTrace.dump(arguments)
-    
-//-----------------------------------------------------------------------------
-static method notImplemented(thing)
-    if (_notImplemented[thing]) return
-    
-    _notImplemented[thing] = true
-    
-    if (!_showNotImplemented) return
-    Weinre.logWarning(thing + " not implemented")
-
-//-----------------------------------------------------------------------------
-static method showNotImplemented()
-    _showNotImplemented = true
-    for (var key in _notImplemented) {
-        Weinre.logWarning(key + " not implemented")
-    }
-    
-//-----------------------------------------------------------------------------
-static method logError(message)
-    getLogger().logError(message)
-
-//-----------------------------------------------------------------------------
-static method logWarning(message)
-    getLogger().logWarning(message)
-
-//-----------------------------------------------------------------------------
-static method logInfo(message)
-    getLogger().logInfo(message)
-
-//-----------------------------------------------------------------------------
-static method logDebug(message)
-    getLogger().logDebug(message)
-
-//-----------------------------------------------------------------------------
-function getLogger()
-    if (logger) return logger
-    
-    if      (Weinre.client) logger = Weinre.WeinreClientCommands
-    else if (Weinre.target) logger = Weinre.WeinreTargetCommands
-    
-    return logger
-    
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/CSSStore.coffee b/weinre.web/modules/weinre/target/CSSStore.coffee
new file mode 100644
index 0000000..b1b350e
--- /dev/null
+++ b/weinre.web/modules/weinre/target/CSSStore.coffee
@@ -0,0 +1,340 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+
+_elementMatchesSelector = null
+
+#-------------------------------------------------------------------------------
+module.exports = class CSSStore
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        @styleSheetMap = {}
+        @styleRuleMap  = {}
+        @styleDeclMap  = {}
+        @testElement   = document.createElement("div")
+
+    #---------------------------------------------------------------------------
+    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/CSSStore.scoop b/weinre.web/modules/weinre/target/CSSStore.scoop
deleted file mode 100644
index 5d7575a..0000000
--- a/weinre.web/modules/weinre/target/CSSStore.scoop
+++ /dev/null
@@ -1,391 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/IDGenerator
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class CSSStore
-    this.styleSheetMap = {}
-    this.styleRuleMap  = {}
-    this.styleDeclMap  = {}
-    this.testElement = document.createElement("div")
-
-//-----------------------------------------------------------------------------
-init
-    var Properties = []
-
-//-----------------------------------------------------------------------------
-static method addCSSProperties(properties)
-    Properties = properties
-
-//-----------------------------------------------------------------------------
-method getInlineStyle(node)
-    var styleObject = this._buildMirrorForStyle(node.style, true)
-    for (var i=0; i<styleObject.cssProperties.length; i++) {
-        styleObject.cssProperties[i].status = "style"
-    }
-    return styleObject
-
-//-----------------------------------------------------------------------------
-method getComputedStyle(node)
-    if (!node) return {}
-    if (node.nodeType != Node.ELEMENT_NODE) return {}
-    
-    var styleObject = this._buildMirrorForStyle(window.getComputedStyle(node), false)
-    return styleObject
-
-//-----------------------------------------------------------------------------
-method getMatchedCSSRules(node)
-    var result = []
-    
-    for (var i=0; i<document.styleSheets.length; i++) {
-        var styleSheet = document.styleSheets[i]
-        
-        if (!styleSheet.cssRules) continue
-        
-        for (var j=0; j<styleSheet.cssRules.length; j++) {
-            var cssRule = styleSheet.cssRules[j]
-            
-            if (!_elementMatchesSelector(node, cssRule.selectorText)) continue
-            
-            var object = {}
-            
-            object.ruleId = this._getStyleRuleId(cssRule)
-            object.selectorText = cssRule.selectorText
-            object.style = this._buildMirrorForStyle(cssRule.style, true)
-            result.push(object)
-        }
-    }
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method getStyleAttributes(node)
-    var result = {}
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method getPseudoElements(node)
-    var result = []
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method setPropertyText(styleId, propertyIndex, text, overwrite)
-    var styleDecl = Weinre.cssStore._getStyleDecl(styleId)
-    
-    if (!styleDecl) {
-        Weinre.logWarning("requested style not available: " + styleId)
-        return null
-    }
-
-    var mirror = styleDecl.__weinre__mirror
-    if (!mirror) {
-        Weinre.logWarning("requested mirror not available: " + styleId)
-        return null
-    }
-
-    var properties = mirror.cssProperties
-    
-    // parse the css text
-    var propertyMirror = this._parseProperty(text)
-    
-    // remove property
-    if (null == propertyMirror) {
-        this._removePropertyFromMirror(mirror, propertyIndex)
-        properties = mirror.cssProperties
-    }
-    
-    // add or replace property
-    else {
-        // if replacing, remove the old one
-        this._removePropertyFromMirror(mirror, propertyIndex)
-        properties = mirror.cssProperties
-
-        // index properties by name
-        var propertyIndices = {}
-        for (var i=0; i<properties.length; i++) {
-            propertyIndices[properties[i].name] = i
-        }
-
-        // add the new ones, or replacing ones    
-        for (var i=0; i<propertyMirror.cssProperties.length; i++) {
-            if (propertyIndices[propertyMirror.cssProperties[i].name] != null) {
-                // property already exists, just replace it
-                properties[propertyIndices[propertyMirror.cssProperties[i].name]] = propertyMirror.cssProperties[i]
-            }
-            else {
-                // new property, add it
-                properties.push(propertyMirror.cssProperties[i])
-            }
-        }
-    
-        for (var key in propertyMirror.shorthandValues) {
-            mirror.shorthandValues[key] = propertyMirror.shorthandValues[key]
-        }
-    }
-    
-    properties.sort(function(p1,p2) {
-        if      (p1.name < p2.name) return -1
-        else if (p1.name > p2.name) return  1
-        else return 0
-    })
-    
-    this._setStyleFromMirror(styleDecl)
-    
-    return mirror
-
-//-----------------------------------------------------------------------------
-method _removePropertyFromMirror(mirror, index)
-    var properties = mirror.cssProperties
-    
-    if (index >= properties.length) return
-    
-    var property = properties[index]
-
-    properties[index] = null
-    
-    if (mirror.shorthandValues[property.name]) {
-        delete mirror.shorthandValues[property.name]
-        
-        for (var i=0; i<properties.length; i++) {
-            if (properties[i]) {
-                if (properties[i].shorthandName == property.name) {
-                    properties[i] = null
-                }
-            }
-        }
-    }
-    
-    var newProperties = []
-    
-    for (var i=0; i<properties.length; i++) {
-        if (properties[i]) newProperties.push(properties[i])
-    }
-    
-    mirror.cssProperties = newProperties
-
-//-----------------------------------------------------------------------------
-method toggleProperty(styleId, propertyIndex, disable)
-    var styleDecl = Weinre.cssStore._getStyleDecl(styleId)
-    
-    if (!styleDecl) {
-        Weinre.logWarning("requested style not available: " + styleId)
-        return null
-    }
-    
-    var mirror = styleDecl.__weinre__mirror
-    if (!mirror) {
-        Weinre.logWarning("requested mirror not available: " + styleId)
-        return null
-    }
-    
-    var cssProperty = mirror.cssProperties[propertyIndex]
-    if (!cssProperty) {
-        Weinre.logWarning("requested property not available: " + styleId + ": " + propertyIndex)
-        return null
-    }
-    
-    if (disable) {
-        cssProperty.status = "disabled"
-    }
-    
-    else {
-        cssProperty.status = "active"
-    }
-
-    this._setStyleFromMirror(styleDecl)
-    
-    return mirror
-
-//-----------------------------------------------------------------------------
-method _setStyleFromMirror(styleDecl)
-    var cssText = []
-    
-    var cssProperties = styleDecl.__weinre__mirror.cssProperties
-    
-    var cssText = ""
-    for (var i=0; i<cssProperties.length; i++) {
-        var property = cssProperties[i]
-        
-        if (!property.parsedOk) continue
-        if (property.status == "disabled") continue
-        if (property.shorthandName) continue
-        
-        cssText += property.name + ": " + property.value
-        if (property.priority == "important") {
-            cssText += " !important; "
-        }
-        else {
-            cssText += "; "
-        }
-    }
-    
-    styleDecl.cssText = cssText
-
-//-----------------------------------------------------------------------------
-method _buildMirrorForStyle(styleDecl, bind)
-    var result = {
-        properties:    {},
-        cssProperties: []
-    }
-    
-    if (!styleDecl) return result
-    
-    if (bind) {
-        result.styleId = this._getStyleDeclId(styleDecl)
-        styleDecl.__weinre__mirror = result
-    }
-    
-    result.properties.width  = styleDecl.getPropertyValue("width")  || ""
-    result.properties.height = styleDecl.getPropertyValue("height") || ""
-    result.cssText           = styleDecl.cssText 
-
-    result.shorthandValues = {}
-
-    var properties = []
-    
-    if (styleDecl) {
-        for (var i=0; i < styleDecl.length; i++) {
-            var property = {}
-            var name = styleDecl.item(i)
-
-            property.name          = name
-            property.priority      = styleDecl.getPropertyPriority(name)
-            property.implicit      = styleDecl.isPropertyImplicit(name)
-            property.shorthandName = styleDecl.getPropertyShorthand(name) || ""
-            property.status        = property.shorthandName ? "style" : "active"
-            property.parsedOk      = true
-            property.value         = styleDecl.getPropertyValue(name)
-            
-            properties.push(property);
-            
-            if (property.shorthandName) {
-                var shorthandName = property.shorthandName
-                if (!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);
-                }
-            }
-        }
-    }
-    
-    properties.sort(function(p1,p2) {
-        if      (p1.name < p2.name) return -1
-        else if (p1.name > p2.name) return  1
-        else return 0
-    })
-    
-    result.cssProperties   = properties
-
-    return result
-
-//-----------------------------------------------------------------------------
-method _parseProperty(string)
-    var testStyleDecl = this.testElement.style
-    
-    try {
-        testStyleDecl.cssText = string
-        
-        if (testStyleDecl.cssText != "") {
-            return this._buildMirrorForStyle(testStyleDecl, false)
-        }
-    }
-    catch(e) {
-    }
-
-    var propertyPattern = /\s*(.+)\s*:\s*(.+)\s*(!important)?\s*;/
-    var match = propertyPattern.exec(string)
-    if (!match) return null
-
-    match[3] = (match[3] == "!important") ? "important" : ""
-
-    var property = {}
-
-    property.name          = match[1]
-    property.priority      = match[3]
-    property.implicit      = true
-    property.shorthandName = ""
-    property.status        = "inactive"
-    property.parsedOk      = false
-    property.value         = match[2]
-
-    var result = {}
-    
-    result.width           = 0
-    result.height          = 0
-    result.shorthandValues = 0
-    result.cssProperties   = [ property ]
-
-    return result
-
-//-----------------------------------------------------------------------------
-method _getStyleSheet(id)
-    return _getMappableObject(id, this.styleSheetMap)
-
-//-----------------------------------------------------------------------------
-method _getStyleSheetId(styleSheet)
-    return _getMappableId(styleSheet, this.styleSheetMap)
-
-//-----------------------------------------------------------------------------
-method _getStyleRule(id)
-    return _getMappableObject(id, this.styleRuleMap)
-
-//-----------------------------------------------------------------------------
-method _getStyleRuleId(styleRule)
-    return _getMappableId(styleRule, this.styleRuleMap)
-
-//-----------------------------------------------------------------------------
-method _getStyleDecl(id)
-    return _getMappableObject(id, this.styleDeclMap)
-
-//-----------------------------------------------------------------------------
-method _getStyleDeclId(styleDecl)
-    return _getMappableId(styleDecl, this.styleDeclMap)
-
-//-----------------------------------------------------------------------------
-function _getMappableObject(id, map)
-    return map[id]
-
-//-----------------------------------------------------------------------------
-function _getMappableId(object, map)
-    return IDGenerator.getId(object, map)
-    
-//-----------------------------------------------------------------------------
-function _mozMatchesSelector(element, selector)
-    if (!element.mozMatchesSelector) return false
-    return element.mozMatchesSelector(selector)
-
-//-----------------------------------------------------------------------------
-function _webkitMatchesSelector(element, selector)
-    if (!element.webkitMatchesSelector) return false
-    return element.webkitMatchesSelector(selector)
-
-//-----------------------------------------------------------------------------
-function _fallbackMatchesSelector(element, selector)
-    return false
-    
-//-----------------------------------------------------------------------------
-init
-    var _elementMatchesSelector
-    
-    if      (Element.prototype.webkitMatchesSelector) _elementMatchesSelector = _webkitMatchesSelector 
-    else if (Element.prototype.mozMatchesSelector)    _elementMatchesSelector = _mozMatchesSelector
-    else                                              _elementMatchesSelector = _fallbackMatchesSelector
\ No newline at end of file
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/CheckForProblems.scoop b/weinre.web/modules/weinre/target/CheckForProblems.scoop
deleted file mode 100644
index dc25ab8..0000000
--- a/weinre.web/modules/weinre/target/CheckForProblems.scoop
+++ /dev/null
@@ -1,28 +0,0 @@
-
-/*
- * 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
- */
-
-//-----------------------------------------------------------------------------
-class CheckForProblems
-
-//-----------------------------------------------------------------------------
-static method check
-    checkForOldPrototypeVersion()
-
-//-----------------------------------------------------------------------------
-function checkForOldPrototypeVersion
-    var badVersion = false
-
-    if (typeof Prototype == "undefined") return
-    if (!Prototype.Version) return
-    
-    if (Prototype.Version.match(/^1\.5.*/)) badVersion = true
-    if (Prototype.Version.match(/^1\.6.*/)) badVersion = true
-    
-    if (badVersion) {
-        alert("Sorry, weinre is not support in versions of Prototype earlier than 1.7")
-    }
\ No newline at end of file
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/Console.scoop b/weinre.web/modules/weinre/target/Console.scoop
deleted file mode 100644
index 3d95827..0000000
--- a/weinre.web/modules/weinre/target/Console.scoop
+++ /dev/null
@@ -1,169 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../target/Timeline
-
-//-----------------------------------------------------------------------------
-class Console
-
-//-----------------------------------------------------------------------------
-init
-    var UsingRemote = false
-    
-    var RemoteConsole   = new Console()
-    var OriginalConsole = window.console
-    
-    RemoteConsole.__original   = OriginalConsole
-    OriginalConsole.__original = OriginalConsole
-    
-    var MessageSource = {
-        HTML: 0,
-        WML: 1,
-        XML: 2,
-        JS: 3,
-        CSS: 4,
-        Other: 5
-    }
-    
-    var MessageType = {
-        Log: 0,
-        Object: 1,
-        Trace: 2,
-        StartGroup: 3,
-        StartGroupCollapsed: 4,
-        EndGroup: 5,
-        Assert: 6,
-        UncaughtException: 7,
-        Result: 8
-    }
-    
-    var MessageLevel = {
-        Tip: 0,
-        Log: 1,
-        Warning: 2,
-        Error: 3,
-        Debug: 4
-    }
-
-//-----------------------------------------------------------------------------
-static getter original
-    return OriginalConsole
-
-//-----------------------------------------------------------------------------
-static method useRemote(value)
-    if (arguments.length == 0) return UsingRemote
-    
-    var oldValue = UsingRemote
-    
-    UsingRemote = !!value
-    
-    if (UsingRemote) 
-        window.console = RemoteConsole
-    else
-        window.console = OriginalConsole
-        
-    return oldValue
-
-//-----------------------------------------------------------------------------
-method _generic(level, messageParts)
-    
-    var message = messageParts[0].toString()
-    
-    var parameters = []
-    for (var i=0; i<messageParts.length; i++) {
-        parameters.push(
-            Weinre.injectedScript.wrapObjectForConsole(messageParts[i], true)
-        )
-    }
-    
-    var payload = {
-        source:      MessageSource.JS,
-        type:        MessageType.Log,
-        level:       level,
-        message:     message,
-        parameters:  parameters
-    }
-    
-    Weinre.wi.ConsoleNotify.addConsoleMessage(payload)
-
-//-----------------------------------------------------------------------------
-method log
-    this._generic(MessageLevel.Log, [].slice.call(arguments)) 
-
-//-----------------------------------------------------------------------------
-method debug
-    this._generic(MessageLevel.Debug, [].slice.call(arguments)) 
-
-//-----------------------------------------------------------------------------
-method error
-    this._generic(MessageLevel.Error, [].slice.call(arguments)) 
-
-//-----------------------------------------------------------------------------
-method info
-    this._generic(MessageLevel.Log, [].slice.call(arguments)) 
-
-//-----------------------------------------------------------------------------
-method warn
-    this._generic(MessageLevel.Warning, [].slice.call(arguments)) 
-
-//-----------------------------------------------------------------------------
-method dir
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method dirxml
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method trace
-    Weinre.notImplemented(arguments.callee.signature)
-    
-//-----------------------------------------------------------------------------
-method assert(condition)
-    Weinre.notImplemented(arguments.callee.signature)
-    
-//-----------------------------------------------------------------------------
-method count
-    Weinre.notImplemented(arguments.callee.signature)
-    
-//-----------------------------------------------------------------------------
-method markTimeline(message)
-    Timeline.addRecord_Mark(message)
-    
-//-----------------------------------------------------------------------------
-method lastWMLErrorMessage
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method profile(title)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method profileEnd(title)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method time(title)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method timeEnd(title)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method group
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method groupCollapsed
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method groupEnd
-    Weinre.notImplemented(arguments.callee.signature)
diff --git a/weinre.web/modules/weinre/target/ElementHighlighter.coffee b/weinre.web/modules/weinre/target/ElementHighlighter.coffee
new file mode 100644
index 0000000..52d0aed
--- /dev/null
+++ b/weinre.web/modules/weinre/target/ElementHighlighter.coffee
@@ -0,0 +1,99 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+canvasAvailable           = null
+highlighterClass          = null
+currentHighlighterElement = null
+
+#-------------------------------------------------------------------------------
+module.exports = class ElementHighlighter
+
+    #---------------------------------------------------------------------------
+    @create: ->
+        highlighterClass ?= require('./ElementHighlighterDivs2')
+
+        new highlighterClass()
+
+    #---------------------------------------------------------------------------
+    constructor: ->
+        @hElement = @createHighlighterElement()
+        @hElement.__weinreHighlighter = true
+        @hElement.style.display = "none"
+        @hElement.style.zIndex  = 10 * 1000 * 1000
+
+        if currentHighlighterElement
+            document.body.removeChild currentHighlighterElement
+
+        currentHighlighterElement = @hElement
+
+        document.body.appendChild @hElement
+
+    #---------------------------------------------------------------------------
+    on: (element) ->
+        return if null == element
+        return unless element.nodeType == Node.ELEMENT_NODE
+
+        @redraw getMetricsForElement(element)
+
+        @hElement.style.display = "block"
+
+    #---------------------------------------------------------------------------
+    off: ->
+        @hElement.style.display = "none"
+
+#-------------------------------------------------------------------------------
+getMetricsForElement = (element) ->
+      metrics = {}
+
+      left = 0
+      top  = 0
+      el   = element
+
+      loop
+          left += el.offsetLeft
+          top += el.offsetTop
+          break unless el = el.offsetParent
+
+      metrics.x = left
+      metrics.y = top
+
+      cStyle = document.defaultView.getComputedStyle(element)
+
+      metrics.width         = element.offsetWidth
+      metrics.height        = element.offsetHeight
+      metrics.marginLeft    = fromPx(cStyle["margin-left"])
+      metrics.marginRight   = fromPx(cStyle["margin-right"])
+      metrics.marginTop     = fromPx(cStyle["margin-top"])
+      metrics.marginBottom  = fromPx(cStyle["margin-bottom"])
+      metrics.borderLeft    = fromPx(cStyle["border-left-width"])
+      metrics.borderRight   = fromPx(cStyle["border-right-width"])
+      metrics.borderTop     = fromPx(cStyle["border-top-width"])
+      metrics.borderBottom  = fromPx(cStyle["border-bottom-width"])
+      metrics.paddingLeft   = fromPx(cStyle["padding-left"])
+      metrics.paddingRight  = fromPx(cStyle["padding-right"])
+      metrics.paddingTop    = fromPx(cStyle["padding-top"])
+      metrics.paddingBottom = fromPx(cStyle["padding-bottom"])
+
+      metrics.x -= metrics.marginLeft
+      metrics.y -= metrics.marginTop
+
+      metrics
+
+#-------------------------------------------------------------------------------
+fromPx = (string) ->
+      parseInt string.replace(/px$/, "")
+
+#-------------------------------------------------------------------------------
+supportsCanvas = () ->
+    element = document.createElement('canvas')
+    return false unless element.getContext
+    return true if element.getContext('2d')
+    return false
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/ElementHighlighter.scoop b/weinre.web/modules/weinre/target/ElementHighlighter.scoop
deleted file mode 100644
index d74cb4f..0000000
--- a/weinre.web/modules/weinre/target/ElementHighlighter.scoop
+++ /dev/null
@@ -1,156 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Binding
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class ElementHighlighter
-    this.boxMargin  = document.createElement("div")
-    this.boxBorder  = document.createElement("div")
-    this.boxPadding = document.createElement("div")
-    this.boxContent = document.createElement("div")
-    
-    this.boxMargin.appendChild(this.boxBorder)
-    this.boxBorder.appendChild(this.boxPadding)
-    this.boxPadding.appendChild(this.boxContent)
-    
-    this.boxMargin.style.backgroundColor  = "#FCC"
-    this.boxBorder.style.backgroundColor  = "#000"
-    this.boxPadding.style.backgroundColor = "#CFC"
-    this.boxContent.style.backgroundColor = "#CCF"
-    
-    this.boxMargin.style.opacity =
-    this.boxBorder.style.opacity =
-    this.boxPadding.style.opacity =
-    this.boxContent.style.opacity = 0.6
-    
-    this.boxMargin.style.position =
-    this.boxBorder.style.position =
-    this.boxPadding.style.position =
-    this.boxContent.style.position = "absolute"
-    
-    this.boxMargin.style.borderWidth =
-    this.boxBorder.style.borderWidth =
-    this.boxPadding.style.borderWidth =
-    this.boxContent.style.borderWidth = "thin"
-        
-    this.boxMargin.style.borderStyle =
-    this.boxBorder.style.borderStyle =
-    this.boxPadding.style.borderStyle =
-    this.boxContent.style.borderStyle = "solid"
-                
-    this.boxMargin.__weinreHighlighter =
-    this.boxBorder.__weinreHighlighter =
-    this.boxPadding.__weinreHighlighter =
-    this.boxContent.__weinreHighlighter = true
-                        
-    this.boxMargin.style.display = "none"
-        
-    document.body.appendChild(this.boxMargin)
-    
-//-----------------------------------------------------------------------------
-method on(element)
-    if (null == element) return
-    if (element.nodeType != Node.ELEMENT_NODE) return
-    
-    this.calculateMetrics(element)
-    this.boxMargin.style.display = "block"
-
-//-----------------------------------------------------------------------------
-method off
-    this.boxMargin.style.display = "none"
-
-//-----------------------------------------------------------------------------
-getter element
-    return this.boxMargin
-
-//-----------------------------------------------------------------------------
-method calculateMetrics(element)
-
-    var metrics = getMetrics(element)
-    
-    this.boxMargin.style.top     = metrics.y      + "px"
-    this.boxMargin.style.left    = metrics.x      + "px"
-    this.boxMargin.style.height  = metrics.height + "px"
-    this.boxMargin.style.width   = metrics.width  + "px"
-
-    this.boxBorder.style.top     = metrics.marginTop    + "px"
-    this.boxBorder.style.left    = metrics.marginLeft   + "px"
-    this.boxBorder.style.bottom  = metrics.marginBottom + "px"
-    this.boxBorder.style.right   = metrics.marginRight  + "px"
-
-    this.boxPadding.style.top    = metrics.borderTop    + "px"
-    this.boxPadding.style.left   = metrics.borderLeft   + "px"
-    this.boxPadding.style.bottom = metrics.borderBottom + "px"
-    this.boxPadding.style.right  = metrics.borderRight  + "px"
-    
-    this.boxContent.style.top    = metrics.paddingTop    + "px"
-    this.boxContent.style.left   = metrics.paddingLeft   + "px"
-    this.boxContent.style.bottom = metrics.paddingBottom + "px"
-    this.boxContent.style.right  = metrics.paddingRight  + "px"
-    
-//-----------------------------------------------------------------------------
-function getMetrics(element)
-    var result = {}
-    
-    // get the x,y position
-    var left = 0
-    var top  = 0
-        
-    var el = element
-    do {
-        left += el.offsetLeft
-        top  += el.offsetTop
-    } while (el = el.offsetParent)
-    
-    result.x = left
-    result.y = top
-
-    // get the computed style
-    var 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
-
-    return result
-    
-//-----------------------------------------------------------------------------
-function fromPx(string) 
-    return parseInt(string.replace(/px$/,""))
diff --git a/weinre.web/modules/weinre/target/ElementHighlighterDivs2.coffee b/weinre.web/modules/weinre/target/ElementHighlighterDivs2.coffee
new file mode 100644
index 0000000..8185160
--- /dev/null
+++ b/weinre.web/modules/weinre/target/ElementHighlighterDivs2.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
+#---------------------------------------------------------------------------------
+
+ElementHighlighter = require('./ElementHighlighter')
+
+# from Web Inspector
+ColorMargin   =  'rgba(246, 178, 107, 0.66)'
+ColorBorder   =  'rgba(255, 229, 153, 0.66)'
+ColorPadding  =  'rgba(147, 196, 125, 0.55)'
+ColorContent  =  'rgba(111, 168, 220, 0.66)'
+
+# overrides
+ColorBorder   =  'rgba(255, 255, 153, 0.40)'
+ColorPadding  =  'rgba(  0, 255,   0, 0.20)'
+ColorContent  =  'rgba(  0,   0, 255, 0.30)'
+
+#-------------------------------------------------------------------------------
+module.exports = class ElementHighlighterDivs2 extends ElementHighlighter
+
+    #---------------------------------------------------------------------------
+    createHighlighterElement: ->
+
+        @hElement1 = document.createElement("weinreHighlighter")
+        @hElement1.style.position = 'absolute'
+        @hElement1.style.overflow = 'hidden'
+
+        @hElement2 = document.createElement("weinreHighlighter")
+        @hElement2.style.position = 'absolute'
+        @hElement2.style.display  = 'block'
+        @hElement2.style.overflow = 'hidden'
+
+        @hElement1.appendChild @hElement2
+
+        @hElement1.style.borderTopStyle    = 'solid'
+        @hElement1.style.borderLeftStyle   = 'solid'
+        @hElement1.style.borderBottomStyle = 'solid'
+        @hElement1.style.borderRightStyle  = 'solid'
+        @hElement1.style.borderTopColor    = ColorMargin
+        @hElement1.style.borderLeftColor   = ColorMargin
+        @hElement1.style.borderBottomColor = ColorMargin
+        @hElement1.style.borderRightColor  = ColorMargin
+        @hElement1.style.backgroundColor   = ColorBorder
+
+        @hElement2.style.borderTopStyle    = 'solid'
+        @hElement2.style.borderLeftStyle   = 'solid'
+        @hElement2.style.borderBottomStyle = 'solid'
+        @hElement2.style.borderRightStyle  = 'solid'
+        @hElement2.style.borderTopColor    = ColorPadding
+        @hElement2.style.borderLeftColor   = ColorPadding
+        @hElement2.style.borderBottomColor = ColorPadding
+        @hElement2.style.borderRightColor  = ColorPadding
+        @hElement2.style.backgroundColor   = ColorContent
+
+        @hElement1.style.outline           = 'black solid thin'
+
+        @hElement1
+
+    #---------------------------------------------------------------------------
+    redraw: (metrics) ->
+
+        @hElement1.style.top               = px metrics.y
+        @hElement1.style.left              = px metrics.x
+        @hElement1.style.height            = px metrics.height
+        @hElement1.style.width             = px metrics.width
+
+        @hElement1.style.borderTopWidth    = px metrics.marginTop
+        @hElement1.style.borderLeftWidth   = px metrics.marginLeft
+        @hElement1.style.borderBottomWidth = px metrics.marginBottom
+        @hElement1.style.borderRightWidth  = px metrics.marginRight
+
+        @hElement2.style.top               = px metrics.borderTop
+        @hElement2.style.left              = px metrics.borderLeft
+        @hElement2.style.bottom            = px metrics.borderBottom
+        @hElement2.style.right             = px metrics.borderRight
+
+        @hElement2.style.borderTopWidth    = px metrics.paddingTop
+        @hElement2.style.borderLeftWidth   = px metrics.paddingLeft
+        @hElement2.style.borderBottomWidth = px metrics.paddingBottom
+        @hElement2.style.borderRightWidth  = px metrics.paddingRight
+
+#-------------------------------------------------------------------------------
+px = (value) ->
+    "#{value}px"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/ExceptionalCallbacks.coffee b/weinre.web/modules/weinre/target/ExceptionalCallbacks.coffee
new file mode 100644
index 0000000..0ab94ee
--- /dev/null
+++ b/weinre.web/modules/weinre/target/ExceptionalCallbacks.coffee
@@ -0,0 +1,100 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+HookSites = require('./HookSites')
+
+#-------------------------------------------------------------------------------
+module.exports = class ExceptionalCallbacks
+
+    @addHooks: ->
+        addHookTimer HookSites.window_setInterval, callSite_setInterval
+        addHookTimer HookSites.window_setTimeout,  callSite_setTimeout
+
+        addHookEventListener HookSites.window_addEventListener,         callSite_windowAEL
+        addHookEventListener HookSites.Node_addEventListener,           callSite_nodeAEL
+        addHookEventListener HookSites.XMLHttpRequest_addEventListener, callSite_xhrAEL
+
+#-------------------------------------------------------------------------------
+addHookTimer = (hookSite, formatter) ->
+    hookSite.addHooks
+        before:  (receiver, args) ->
+            code = args[0]
+            return unless typeof(code) is "function"
+
+            millis   = args[1]
+            callSite = formatter(millis, code)
+
+            args[0] = instrumentedCallback(code, callSite)
+
+#-------------------------------------------------------------------------------
+addHookEventListener = (hookSite, formatter) ->
+    hookSite.addHooks
+        before:  (receiver, args) ->
+            code = args[1]
+            return unless typeof(code) is "function"
+
+            event    = args[0]
+            callSite = formatter(event, code, receiver)
+
+            args[1] = instrumentedCallback(code, callSite)
+
+#-------------------------------------------------------------------------------
+instrumentedCallback = (code, callSite) ->
+    return code unless typeof(code) is "function"
+
+    instrumentedCode = ->
+        try
+            return code.apply(this, arguments)
+        catch e
+            console.error "exception in callback: #{e}"
+            console.error "  callsite: #{callSite}"
+
+            if e.stack
+                console.error "stack at time of exception:"
+                console.error e.stack
+
+            throw e
+
+    instrumentedCode
+
+#-------------------------------------------------------------------------------
+callSite_setTimeout = (time, func) ->
+    "setTimeout(#{getFunctionName(func)}, #{time})"
+
+#-------------------------------------------------------------------------------
+callSite_setInterval = (time, func) ->
+    "setInterval(#{getFunctionName(func)}, #{time})"
+
+#-------------------------------------------------------------------------------
+callSite_windowAEL = (event, func) ->
+    "window.addEventListener('#{event}', #{getFunctionName(func)})"
+
+#-------------------------------------------------------------------------------
+callSite_nodeAEL = (event, func, node) ->
+    node = node.nodeName if node.nodeName
+    "#{node}.addEventListener('#{event}', #{getFunctionName(func)})"
+
+#-------------------------------------------------------------------------------
+callSite_xhrAEL = (event, func) ->
+    "XMLHttpRequest.addEventListener('#{event}', #{getFunctionName(func)})"
+
+#-------------------------------------------------------------------------------
+getFunctionName = (func) ->
+    return func.displayName if func.displayName
+    return func.name if func.name
+    return '<anonymous>'
+
+#-------------------------------------------------------------------------------
+getStackTrace = (e) ->
+    return e.stack if e.stack
+
+    return null
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/HookSites.coffee b/weinre.web/modules/weinre/target/HookSites.coffee
new file mode 100644
index 0000000..13dedb1
--- /dev/null
+++ b/weinre.web/modules/weinre/target/HookSites.coffee
@@ -0,0 +1,39 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+HookLib = require('../common/HookLib')
+
+#-------------------------------------------------------------------------------
+module.exports = class HookSites
+
+#---------------------------------------------------------------------------
+HookSites.window_clearInterval            = HookLib.addHookSite window, "clearInterval"
+HookSites.window_clearTimeout             = HookLib.addHookSite window, "clearTimeout"
+HookSites.window_setInterval              = HookLib.addHookSite window, "setInterval"
+HookSites.window_setTimeout               = HookLib.addHookSite window, "setTimeout"
+HookSites.window_addEventListener         = HookLib.addHookSite window, "addEventListener"
+HookSites.Node_addEventListener           = HookLib.addHookSite Node.prototype, "addEventListener"
+HookSites.XMLHttpRequest_open             = HookLib.addHookSite XMLHttpRequest.prototype, "open"
+HookSites.XMLHttpRequest_send             = HookLib.addHookSite XMLHttpRequest.prototype, "send"
+HookSites.XMLHttpRequest_addEventListener = HookLib.addHookSite XMLHttpRequest.prototype, "addEventListener"
+
+if window.openDatabase
+    HookSites.window_openDatabase = HookLib.addHookSite window, "openDatabase"
+
+if window.localStorage
+    HookSites.LocalStorage_setItem    = HookLib.addHookSite window.localStorage, "setItem"
+    HookSites.LocalStorage_removeItem = HookLib.addHookSite window.localStorage, "removeItem"
+    HookSites.LocalStorage_clear      = HookLib.addHookSite window.localStorage, "clear"
+
+if window.sessionStorage
+    HookSites.SessionStorage_setItem    = HookLib.addHookSite window.sessionStorage, "setItem"
+    HookSites.SessionStorage_removeItem = HookLib.addHookSite window.sessionStorage, "removeItem"
+    HookSites.SessionStorage_clear      = HookLib.addHookSite window.sessionStorage, "clear"
+
+#-------------------------------------------------------------------------------
+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..d1553c6
--- /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(object.toString())
+
+        return match[1] if match
+
+        "Object"
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/InjectedScriptHostImpl.scoop b/weinre.web/modules/weinre/target/InjectedScriptHostImpl.scoop
deleted file mode 100644
index 0c9b8af..0000000
--- a/weinre.web/modules/weinre/target/InjectedScriptHostImpl.scoop
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class InjectedScriptHostImpl
-
-//-----------------------------------------------------------------------------
-method clearConsoleMessages(callback)
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method nodeForId(/*int*/ nodeId, callback)
-    return Weinre.nodeStore.getNode(nodeId)
-
-//-----------------------------------------------------------------------------
-method pushNodePathToFrontend(/*any*/ node, /*boolean*/ withChildren, /*boolean*/ selectInUI, callback)
-    // callback: function()
-    var nodeId = Weinre.nodeStore.getNodeId(node)
-    var children = Weinre.nodeStore.serializeNode(node, 1)
-    Weinre.wi.DOMNotify.setChildNodes(nodeId, children)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method inspectedNode(/*int*/ num, callback)
-    // callback: function()
-    var nodeId = Weinre.nodeStore.getInspectedNode(num)
-    return nodeId
-
-//-----------------------------------------------------------------------------
-method internalConstructorName(object)
-    var ctor = object.constructor
-    
-    var ctorName = ctor.fullClassName || ctor.displayName || ctor.name
-    if (ctorName && (ctorName != "Object")) return ctorName
-    
-    var pattern = /\[object (.*)\]/
-    var match = pattern.exec(ctor.toString())
-    if (match) return match[1]
-    
-    return "Object"
diff --git a/weinre.web/modules/weinre/target/NetworkRequest.coffee b/weinre.web/modules/weinre/target/NetworkRequest.coffee
new file mode 100644
index 0000000..64849d1
--- /dev/null
+++ b/weinre.web/modules/weinre/target/NetworkRequest.coffee
@@ -0,0 +1,176 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+StackTrace  = require('../common/StackTrace')
+IDGenerator = require('../common/IDGenerator')
+HookLib     = require('../common/HookLib')
+Weinre      = require('../common/Weinre')
+Ex          = require('../common/Ex')
+HookSites   = require('./HookSites')
+
+Loader =
+    url:      window.location.href
+    frameId:  0
+    loaderId: 0
+
+#-------------------------------------------------------------------------------
+module.exports = class NetworkRequest
+
+    #---------------------------------------------------------------------------
+    constructor: (@xhr, @id, @method, @url, @stackTrace) ->
+
+    #---------------------------------------------------------------------------
+    handleSend: (data) ->
+        Weinre.wi.NetworkNotify.identifierForInitialRequest(@id, @url, Loader, @stackTrace)
+
+        time             = Date.now() / 1000.0
+        request          = getRequest(@url, @method, @xhr, data)
+        redirectResponse = {isNull: true}
+
+        Weinre.wi.NetworkNotify.willSendRequest(@id, time, request, redirectResponse)
+
+    #---------------------------------------------------------------------------
+    handleHeadersReceived: ->
+        time     = Date.now() / 1000.0
+        response = getResponse(@xhr)
+        Weinre.wi.NetworkNotify.didReceiveResponse(@id, time, "XHR", response)
+
+    #---------------------------------------------------------------------------
+    handleLoading: ->
+
+    #---------------------------------------------------------------------------
+    handleDone: ->
+        sourceString = @xhr.responseText
+        Weinre.wi.NetworkNotify.setInitialContent(@id, sourceString, "XHR")
+
+        time       = Date.now() / 1000.0
+        status     = @xhr.status
+        status     = 200 if status == 0
+        statusText = @xhr.statusText
+
+        success = status >= 200 and status < 300
+
+        if success
+            Weinre.wi.NetworkNotify.didFinishLoading(@id, time)
+        else
+            description = "#{status} - #{statusText}"
+            Weinre.wi.NetworkNotify.didFailLoading(@id, time, description)
+
+    #---------------------------------------------------------------------------
+    @installNativeHooks: ->
+
+        #-----------------------------------------------------------------------
+        HookSites.XMLHttpRequest_open.addHooks
+
+            before:  (receiver, args) ->
+                xhr = receiver
+
+                method = args[0]
+                url    = args[1]
+                id     = IDGenerator.next()
+
+                rawStackTrace = new StackTrace(args).trace.slice(1)
+
+                stackTrace = []
+                for frame in rawStackTrace
+                    stackTrace.push({functionName: frame})
+
+                xhr.__weinreNetworkRequest__ = new NetworkRequest(xhr, id, method, url, stackTrace)
+
+                HookLib.ignoreHooks ->
+                    xhr.addEventListener "readystatechange", getXhrEventHandler(xhr), false
+
+        #-----------------------------------------------------------------------
+        HookSites.XMLHttpRequest_send.addHooks
+
+            before:  (receiver, args) ->
+                xhr  = receiver
+                data = args[0]
+                nr   = xhr.__weinreNetworkRequest__
+                return unless nr
+
+                nr.handleSend(data)
+
+#-------------------------------------------------------------------------------
+getRequest = (url, method, xhr, data) ->
+
+    return {
+        url:              url
+        httpMethod:       method
+        httpHeaderFields: {}
+        requestFormData:  getFormData(url, data)
+    }
+
+#-------------------------------------------------------------------------------
+getResponse = (xhr) ->
+    contentType = xhr.getResponseHeader("Content-Type")
+
+    [contentType, encoding] = splitContentType(contentType)
+
+    headers = getHeaders(xhr)
+
+    return {
+        mimeType: contentType
+        expectedContentLength: contentType
+        textEncodingName:      encoding
+        httpStatusCode:        xhr.status
+        httpStatusText:        xhr.statusText
+        httpHeaderFields:      headers
+        connectionReused:      false
+        connectionID:          0
+        wasCached:             false
+    }
+
+#-------------------------------------------------------------------------------
+getHeaders = (xhr) ->
+    string = xhr.getAllResponseHeaders()
+    lines = string.split('\r\n')
+
+    result = {}
+    for line in lines
+        line = trim(line)
+        break if line == ""
+
+        [key, val] = line.split(':', 2)
+        result[trim(key)] = trim(val)
+
+    result
+
+#-------------------------------------------------------------------------------
+trim = (string) ->
+    string.replace(/^\s+|\s+$/g, '')
+
+#-------------------------------------------------------------------------------
+getFormData = (url, data) ->
+    return data if data
+
+    pattern = /.*?\?(.*?)(#.*)?$/
+    match = url.match(pattern)
+    return match[1] if match
+
+    return ""
+
+#-------------------------------------------------------------------------------
+splitContentType = (contentType) ->
+    pattern = /\s*(.*?)\s*(;\s*(.*))?\s*$/
+    match = contentType.match(pattern)
+    return [contentType, ""] unless match
+
+    return [match[1], match[3]]
+
+#-------------------------------------------------------------------------------
+getXhrEventHandler = (xhr) ->
+    ->
+        nr = xhr.__weinreNetworkRequest__
+        return unless nr
+
+        switch xhr.readyState
+            when 2 then nr.handleHeadersReceived()
+            when 3 then nr.handleLoading()
+            when 4 then nr.handleDone()
+
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/NodeStore.scoop b/weinre.web/modules/weinre/target/NodeStore.scoop
deleted file mode 100644
index f8b288f..0000000
--- a/weinre.web/modules/weinre/target/NodeStore.scoop
+++ /dev/null
@@ -1,235 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../common/IDGenerator
-
-//-----------------------------------------------------------------------------
-class NodeStore
-    this.__nodeMap      = {}
-    this.__nodeDataMap  = {}
-    this.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)
-
-//-----------------------------------------------------------------------------
-method addInspectedNode(nodeId)
-    this.inspectedNodes.unshift(nodeId)
-    if (this.inspectedNodes.length > 5) {
-        this.inspectedNodes = this.inspectedNodes.slice(0,5)
-    }
-
-//-----------------------------------------------------------------------------
-method getInspectedNode(index)
-    return this.inspectedNodes[index]
-
-//-----------------------------------------------------------------------------
-method getNode(nodeId)
-    return this.__nodeMap[nodeId]
-
-//-----------------------------------------------------------------------------
-method checkNodeId(node)
-    return IDGenerator.checkId(node)
-
-//-----------------------------------------------------------------------------
-method getNodeId(node)
-    var id = this.checkNodeId(node)
-    if (id) {
-        return id
-    }
-
-    return IDGenerator.getId(node, this.__nodeMap)    
-
-//-----------------------------------------------------------------------------
-method getNodeData(nodeId, depth)
-    return this.serializeNode(this.getNode(nodeId), depth)
-
-//-----------------------------------------------------------------------------
-method getPreviousSiblingId(node)
-    while (true) {
-        var sib = node.previousSibling
-        if (!sib) return 0
-        
-        var id = this.checkNodeId(sib)
-        if (id) return id
-        
-        node = sib
-    }
-
-//-----------------------------------------------------------------------------
-method nextNodeId()
-    return "" + IDGenerator.next()
-
-//-----------------------------------------------------------------------------
-method serializeNode(node, depth)
-    var nodeName  = ""
-    var nodeValue = null
-    var localName = null
-    var id = this.getNodeId(node) 
-    
-    switch(node.nodeType) {
-        case Node.TEXT_NODE:    
-        case Node.COMMENT_NODE:
-        case Node.CDATA_SECTION_NODE:
-            nodeValue = node.nodeValue
-            break
-        case Node.ATTRIBUTE_NODE:
-            localName = node.localName
-            break
-        case Node.DOCUMENT_FRAGMENT_NODE:
-            break
-        case Node.DOCUMENT_NODE:
-        case Node.ELEMENT_NODE:
-        default:
-            nodeName  = node.nodeName
-            localName = node.localName
-            break
-    }
-    
-    var nodeData = {
-        id:        id,
-        nodeType:  node.nodeType,
-        nodeName:  nodeName,
-        localName: localName,
-        nodeValue: nodeValue
-    }
-    
-    if (node.nodeType == Node.ELEMENT_NODE || node.nodeType == Node.DOCUMENT_NODE || node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
-        nodeData.childNodeCount = this.childNodeCount(node)
-        var children = this.serializeNodeChildren(node, depth)
-        if (children.length) {
-            nodeData.children = children
-        }
-        
-        if (node.nodeType == Node.ELEMENT_NODE) {
-            nodeData.attributes = []
-            for (var i=0; i<node.attributes.length; i++) {
-                nodeData.attributes.push(node.attributes[i].nodeName)
-                nodeData.attributes.push(node.attributes[i].nodeValue)
-            }
-        } 
-        
-        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
-    }
-
-    return nodeData
-
-//-----------------------------------------------------------------------------
-method serializeNodeChildren(node, depth)
-    var result   = []
-    var childIds = this.childNodeIds(node)
-    
-    if (depth == 0) {
-        if (childIds.length == 1) {
-            var childNode = this.getNode(childIds[0])
-            if (childNode.nodeType == Node.TEXT_NODE) {
-                result.push(this.serializeNode(childNode))
-            }
-        }
-        return result
-    }
-    
-    depth--;
-    for (var i=0; i<childIds.length; i++) {
-        result.push(this.serializeNode(this.getNode(childIds[i]), depth))
-    }
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method childNodeCount(node)
-    return this.childNodeIds(node).length
-
-//-----------------------------------------------------------------------------
-method childNodeIds(node)
-    var ids = []
-    
-    for (var i=0; i<node.childNodes.length; i++) {
-        if (this.isToBeSkipped(node.childNodes[i])) continue
-        ids.push(this.getNodeId(node.childNodes[i]))
-    }
-    
-    return ids
-
-//-----------------------------------------------------------------------------
-method isToBeSkipped(node)
-    if (!node) return true
-    if (node.__weinreHighlighter) return true 
-    if (node.nodeType != Node.TEXT_NODE) return false
-    return !!node.nodeValue.match(/^\s*$/) 
-
-//-----------------------------------------------------------------------------
-function handleDOMSubtreeModified(event)
-    if (!event.attrChange) return
-    
-    NodeStore.handleDOMAttrModified(event)
-
-//-----------------------------------------------------------------------------
-function handleDOMNodeInserted(event)
-    var targetId = Weinre.nodeStore.checkNodeId(event.target)
-    var parentId = Weinre.nodeStore.checkNodeId(event.relatedNode)
-    
-    if (!parentId) return
-    
-    var child = Weinre.nodeStore.serializeNode(event.target, 0)
-    var previous = Weinre.nodeStore.getPreviousSiblingId(event.target)
-    Weinre.wi.DOMNotify.childNodeInserted(parentId, previous, child)
-
-//-----------------------------------------------------------------------------
-function handleDOMNodeRemoved(event)
-    var targetId = Weinre.nodeStore.checkNodeId(event.target)
-    var parentId = Weinre.nodeStore.checkNodeId(event.relatedNode)
-    
-    if (!parentId) return
-    
-    if (targetId) {
-        Weinre.wi.DOMNotify.childNodeRemoved(parentId, targetId)
-    }
-    else {
-        var childCount = Weinre.nodeStore.childNodeCount(event.relatedNode)
-        Weinre.wi.DOMNotify.childNodeCountUpdated(parentId, childCount)
-    }
-
-//-----------------------------------------------------------------------------
-// This event is not actually fired in WebKit, but DOMSubtreeModified may
-// be fired for attribute changes.  Doesn't seem to be at the moment.
-function handleDOMAttrModified(event)
-    var targetId = Weinre.nodeStore.checkNodeId(event.target)
-    if (!targetId) return
-    
-    attrs = []
-    for (var i=0; i<event.target.attributes.length; i++) {
-        attrs.push(event.target.attributes[i].name)
-        attrs.push(event.target.attributes[i].value)
-    }
-    
-    Weinre.wi.DOMNotify.attributesUpdated(targetId, attrs)
-
-//-----------------------------------------------------------------------------
-function handleDOMCharacterDataModified(event)
-    var targetId = Weinre.nodeStore.checkNodeId(event.target)
-    if (!targetId) return
-    
-    Weinre.wi.DOMNotify.characterDataModified(targetId, event.newValue)
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/SqlStepper.scoop b/weinre.web/modules/weinre/target/SqlStepper.scoop
deleted file mode 100644
index 3789812..0000000
--- a/weinre.web/modules/weinre/target/SqlStepper.scoop
+++ /dev/null
@@ -1,92 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Binding
-
-//-----------------------------------------------------------------------------
-class SqlStepper(steps)
-    if (!(this instanceof SqlStepper)) return new SqlStepper(steps)
-    
-    this.__context = {}
-    
-    var context = this.__context
-    context.steps = steps
-    
-//-----------------------------------------------------------------------------
-method run(db, errorCallback)
-    var context = this.__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)
-    this.executeSql          = new Binding(this, executeSql)
-    
-    db.transaction(context.runStep)
-    
-//-----------------------------------------------------------------------------
-function executeSql(statement, data)
-    var context = this.__context
-    
-    context.tx.executeSql(statement, data, context.runStep, context.ourErrorCallback)
-
-//-----------------------------------------------------------------------------
-function ourErrorCallback(tx, sqlError)
-    var context = this.__context
-    
-    context.errorCallback.call(this, sqlError)
-    
-//-----------------------------------------------------------------------------
-function runStep(tx, resultSet)
-    var context = this.__context
-    
-    if (context.nextStep >= context.steps.length) return
-    
-    context.tx = tx
-    
-    context.currentStep = context.nextStep
-    context.nextStep++
-    
-    var step = context.steps[context.currentStep]
-    
-    step.call(this, resultSet)
-
-//-----------------------------------------------------------------------------
-static method example(db, id)
-    function step1() {
-        this.executeSql("SELECT name FROM sqlite_master WHERE type='table'")
-    }
-    
-    function step2(resultSet) {
-        var rows = resultSet.rows
-        var result = []
-        for (var i=0; i<rows.length; i++) {
-            var name = rows.item(i).name
-            if (name == "__WebKitDatabaseInfoTable__") continue
-        
-            result.push(name)
-        }
-        
-        console.log("[" + this.id + "] table names: " + result.join(", "))
-    }
-
-    function errorCb(sqlError) {
-        
-        console.log("[" + this.id + "] sql error:" + sqlError.code + ": " + sqlError.message)
-    }
-    
-    var stepper = new SqlStepper([step1, step2])
-    stepper.id = id
-    stepper.run(db, errorCb)
diff --git a/weinre.web/modules/weinre/target/Target.coffee b/weinre.web/modules/weinre/target/Target.coffee
new file mode 100644
index 0000000..2a843e0
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Target.coffee
@@ -0,0 +1,209 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+Callback                      = require('../common/Callback')
+MessageDispatcher             = require('../common/MessageDispatcher')
+Weinre                        = require('../common/Weinre')
+HookLib                       = require('../common/HookLib')
+
+CheckForProblems              = require('./CheckForProblems')
+NodeStore                     = require('./NodeStore')
+CSSStore                      = require('./CSSStore')
+ElementHighlighter            = require('./ElementHighlighter')
+ExceptionalCallbacks          = require('./ExceptionalCallbacks')
+InjectedScriptHostImpl        = require('./InjectedScriptHostImpl')
+NetworkRequest                = require('./NetworkRequest')
+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()
+
+    #----------------------------------------------------------------------------
+    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-script.js", "target-script-min.js" ]
+        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: () ->
+        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"
+            HookLib.ignoreHooks =>
+                setTimeout (=> this.onDOMContent()), 10
+
+        if document.readyState == "complete"
+            HookLib.ignoreHooks =>
+                setTimeout (=> this.onDOMContent()), 10
+                setTimeout (=> this.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.wi.NetworkNotify          = messageDispatcher.createProxy("NetworkNotify")
+        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
+
+        ExceptionalCallbacks.addHooks()
+        NetworkRequest.installNativeHooks()
+
+    #---------------------------------------------------------------------------
+    @handleError: (event) ->
+        filename = event.filename or "[unknown filename]"
+        lineno   = event.lineno   or "[unknown lineno]"
+        message  = event.message  or "[unknown message]"
+
+        console.error "error occurred: #{filename}:#{lineno}: #{message}"
+
+    #---------------------------------------------------------------------------
+    cb_webSocketOpened: () ->
+        Weinre.WeinreTargetCommands.registerTarget window.location.href, Binding(this, @cb_registerTarget)
+
+    #---------------------------------------------------------------------------
+    cb_registerTarget: (targetDescription) ->
+        Weinre.targetDescription = targetDescription
+
+    #---------------------------------------------------------------------------
+    onLoaded: ->
+        if not Weinre.wi.InspectorNotify
+            HookLib.ignoreHooks =>
+                setTimeout (=> this.onLoaded()), 10
+            return
+
+        Weinre.wi.InspectorNotify.loadEventFired currentTime() - @_startTime
+
+    #---------------------------------------------------------------------------
+    onDOMContent: ->
+        if not Weinre.wi.InspectorNotify
+            HookLib.ignoreHooks =>
+                setTimeout (=> this.onDOMContent()), 10
+            return
+
+        Weinre.wi.InspectorNotify.domContentEventFired currentTime() - @_startTime
+
+    #---------------------------------------------------------------------------
+    setDocument: () ->
+        Weinre.elementHighlighter = ElementHighlighter.create()
+
+        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/Target.scoop b/weinre.web/modules/weinre/target/Target.scoop
deleted file mode 100644
index bb3eeae..0000000
--- a/weinre.web/modules/weinre/target/Target.scoop
+++ /dev/null
@@ -1,212 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Native
-
-requireClass ../common/Ex
-requireClass ../common/Binding
-requireClass ../common/Callback
-requireClass ../common/MessageDispatcher
-requireClass ../common/Weinre
-
-requireClass ./CheckForProblems
-requireClass ./NodeStore
-requireClass ./CSSStore
-requireClass ./ElementHighlighter
-requireClass ./InjectedScriptHostImpl
-
-requireClass ./WeinreTargetEventsImpl
-requireClass ./WeinreExtraClientCommandsImpl
-
-requireClass ./WiConsoleImpl
-requireClass ./WiCSSImpl
-requireClass ./WiDatabaseImpl
-requireClass ./WiDOMImpl
-requireClass ./WiDOMStorageImpl
-requireClass ./WiInspectorImpl
-requireClass ./WiRuntimeImpl
-
-//-----------------------------------------------------------------------------
-class Target
-
-//-----------------------------------------------------------------------------
-static method main
-    CheckForProblems.check()
-    
-    Weinre.target = new Target()
-    Weinre.target.initialize()
-    
-    Weinre.addCSSProperties = function addCSSProperties(properties) {
-        CSSStore.addCSSProperties(properties)
-    }
-    
-//------------------------------------------------------------------------------
-method setWeinreServerURLFromScriptSrc(element)
-    if (window.WeinreServerURL) return
-
-    if (element) {
-        var pattern = /(http:\/\/(.*?)\/)/
-        var match   = pattern.exec(element.src)
-        if (match) {
-            window.WeinreServerURL = match[1]
-            return 
-        }
-    }
-    
-    var message = "unable to calculate the weinre server url; explicity set the variable window.WeinreServerURL instead" 
-    alert(message)
-    throw new Ex(arguments, message)
-
-
-//-----------------------------------------------------------------------------
-method setWeinreServerIdFromScriptSrc(element)
-    if (window.WeinreServerId) return
-
-    var element = this.getTargetScriptElement()
-
-    var hash = "anonymous"
-    if (element) {
-        var attempt = element.src.split("#")[1]
-        if (attempt) {
-            hash = attempt
-        }
-        else {
-            attempt = location.hash.split("#")[1]
-            if (attempt) {
-                hash = attempt
-            }
-        }
-    }
-
-    window.WeinreServerId = hash
-
-//-----------------------------------------------------------------------------
-method getTargetScriptElement
-
-    var elements = document.getElementsByTagName("script")
-
-    var scripts = ["Target.", "target-script.", "target-script-min."]
-    for (var i=0; i<elements.length; i++) {
-        var element = elements[i]
-        
-        for (j=0; j<scripts.length; j++) {
-            if (-1 != element.src.indexOf("/" + scripts[j])) {
-                return element
-            }
-        }
-    }
-
-//-----------------------------------------------------------------------------
-method initialize()
-    var self = this
-
-    var element = this.getTargetScriptElement()
-    this.setWeinreServerURLFromScriptSrc(element)
-    this.setWeinreServerIdFromScriptSrc(element)
-    
-    if (window.WeinreServerURL[window.WeinreServerURL.length-1] != "/") {
-        window.WeinreServerURL += "/"
-    }   
-    
-    var injectedScriptHost = new InjectedScriptHostImpl()
-    Weinre.injectedScript = injectedScriptConstructor(injectedScriptHost, window, 0, "?")
-    
-    window.addEventListener("load", Binding(this, "onLoaded"), false)
-    document.addEventListener("DOMContentLoaded", Binding(this, "onDOMContent"), false)
-    
-    this._startTime = currentTime()
-    
-    if (document.readyState == "loaded") {
-        setTimeout(function() { self.onDOMContent() }, 10)
-    }
-    
-    if (document.readyState == "complete") {
-        setTimeout(function() { self.onDOMContent() }, 10)
-        setTimeout(function() { self.onLoaded() }, 20)
-    }
-
-    var 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, this.cb_webSocketOpened))
-    
-    Weinre.nodeStore = new NodeStore()
-    Weinre.cssStore  = new CSSStore()
-    
-    window.addEventListener("error", function(e) {Target.handleError(e)}, false)
-    
-    
-//-----------------------------------------------------------------------------
-static method handleError(event)
-    var filename = event.filename || "[unknown filename]"
-    var lineno   = event.lineno   || "[unknown lineno]"
-    var message  = event.message  || "[unknown message]"
-
-    Weinre.logError("error occurred: " + filename + ":" + lineno + ": " + message)
-
-//-----------------------------------------------------------------------------
-method cb_webSocketOpened()
-    Weinre.WeinreTargetCommands.registerTarget(window.location.href, Binding(this, this.cb_registerTarget))
-
-//-----------------------------------------------------------------------------
-method cb_registerTarget(targetDescription)
-    Weinre.targetDescription    = targetDescription
-
-//-----------------------------------------------------------------------------
-method onLoaded
-    Weinre.wi.InspectorNotify.loadEventFired(currentTime() - this._startTime)
-
-//-----------------------------------------------------------------------------
-method onDOMContent
-    Weinre.wi.InspectorNotify.domContentEventFired(currentTime() - this._startTime)
-
-//-----------------------------------------------------------------------------
-method setDocument()
-    Weinre.elementHighlighter = new ElementHighlighter()
-    
-    var nodeId = Weinre.nodeStore.getNodeId(document)
-    var nodeData = Weinre.nodeStore.getNodeData(nodeId, 2)
-
-    Weinre.wi.DOMNotify.setDocument(nodeData)
-    
-//-----------------------------------------------------------------------------
-// WebKit's currentTime() seems to return time in seconds
-function currentTime()
-    return (new Date().getMilliseconds()) / 1000.0
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/Timeline.coffee b/weinre.web/modules/weinre/target/Timeline.coffee
new file mode 100644
index 0000000..f50922c
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Timeline.coffee
@@ -0,0 +1,317 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+HookLib     = require('../common/HookLib')
+HookSites   = require('./HookSites')
+
+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()
+
+        record = {}
+        record.startTime = Date.now()
+        record.category  = name: "loading"
+
+        if xhr.readyState == XMLHttpRequest.OPENED
+            record.type = TimelineRecordType.ResourceSendRequest
+            record.data =
+                identifier:    id
+                url:           url
+                requestMethod: method
+
+        else if xhr.readyState == XMLHttpRequest.DONE
+            record.type = TimelineRecordType.ResourceReceiveResponse
+            record.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
+
+    #---------------------------------------------------------------------------
+    @installNativeHooks: ->
+
+        #-----------------------------------------------------------------------
+        HookSites.window_setInterval.addHooks
+
+            before: (receiver, args) ->
+                code = args[0]
+                return unless typeof(code) is "function"
+
+                interval  = args[1]
+                code      = instrumentedTimerCode(code, interval, false)
+                args[0]   = code
+
+                @userData = {}
+                @userData.code     = code
+                @userData.interval = interval
+
+            after: (receiver, args, result) ->
+                code = @userData.code
+                return unless typeof(code) is "function"
+
+                id             = result
+                code.__timerId = id
+                addTimer id, @userData.interval, false
+
+        #-----------------------------------------------------------------------
+        HookSites.window_clearInterval.addHooks
+
+            before: (receiver, args) ->
+                id = args[0]
+                removeTimer id, false
+
+        #-----------------------------------------------------------------------
+        HookSites.window_setTimeout.addHooks
+
+            before: (receiver, args) ->
+                code = args[0]
+                return unless typeof(code) is "function"
+
+                interval  = args[1]
+                code      = instrumentedTimerCode(code, interval, true)
+                args[0]   = code
+
+                @userData = {}
+                @userData.code     = code
+                @userData.interval = interval
+
+            after: (receiver, args, result) ->
+                code = @userData.code
+                return unless typeof(code) is "function"
+
+                id             = result
+                code.__timerId = id
+                addTimer id, @userData.interval, true
+
+        #-----------------------------------------------------------------------
+        HookSites.window_clearTimeout.addHooks
+
+            before: (receiver, args) ->
+                id = args[0]
+                removeTimer id, true
+
+        #-----------------------------------------------------------------------
+        HookSites.XMLHttpRequest_open.addHooks
+
+            before:  (receiver, args) ->
+                xhr = receiver
+                IDGenerator.getId xhr
+
+                xhr.__weinre_method = args[0]
+                xhr.__weinre_url    = args[1]
+
+                xhr.addEventListener "readystatechange", getXhrEventHandler(xhr), false
+
+#-------------------------------------------------------------------------------
+getXhrEventHandler = (xhr) ->
+      (event) ->
+          Timeline.addRecord_XHRReadyStateChange xhr.__weinre_method, xhr.__weinre_url, IDGenerator.getId(xhr), xhr
+
+#-------------------------------------------------------------------------------
+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.apply(this, arguments)
+          id = arguments.callee.__timerId
+          Timeline.addRecord_TimerFire id, timeout, singleShot
+          result
+
+      instrumentedCode.displayName = code.name || code.displayName
+
+      instrumentedCode
+
+#-------------------------------------------------------------------------------
+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++
+
+#-------------------------------------------------------------------------------
+Timeline.installGlobalListeners()
+Timeline.installNativeHooks()
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/Timeline.scoop b/weinre.web/modules/weinre/target/Timeline.scoop
deleted file mode 100644
index 4e9629b..0000000
--- a/weinre.web/modules/weinre/target/Timeline.scoop
+++ /dev/null
@@ -1,326 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Ex
-requireClass ../common/Weinre
-requireClass ../common/IDGenerator
-requireClass ../common/StackTrace
-
-requireClass ../common/Native
-
-//-----------------------------------------------------------------------------
-class Timeline
-
-//-----------------------------------------------------------------------------
-static method start
-    Running = true
-
-//-----------------------------------------------------------------------------
-static method stop
-    Running = false
-
-//-----------------------------------------------------------------------------
-static method isRunning
-    return Running
-
-//-----------------------------------------------------------------------------
-static method addRecord_Mark(message)
-    if (!Timeline.isRunning()) return
-    
-    var 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)
-
-//-----------------------------------------------------------------------------
-static method addRecord_EventDispatch(event, name, category)
-    if (!Timeline.isRunning()) return
-    
-    if (!category) category = "scripting"
-    var record = {}
-
-    record.type      = TimelineRecordType.EventDispatch
-    record.category  = { name: category }
-    record.startTime = Date.now()
-    record.data      = { type: event.type }
-    
-    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
-    
-//-----------------------------------------------------------------------------
-static method addRecord_TimerInstall(id, timeout, singleShot)
-    if (!Timeline.isRunning()) return
-    
-    var 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)
-
-//-----------------------------------------------------------------------------
-static method addRecord_TimerRemove(id, timeout, singleShot)
-    if (!Timeline.isRunning()) return
-
-    var 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)
-
-//-----------------------------------------------------------------------------
-static method addRecord_TimerFire(id, timeout, singleShot)
-    if (!Timeline.isRunning()) return
-
-    var 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)
-
-//-----------------------------------------------------------------------------
-static method addRecord_XHRReadyStateChange(method, url, id, xhr)
-    if (!Timeline.isRunning()) return
-
-    var record
-    
-    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)
-
-//-----------------------------------------------------------------------------
-static method installGlobalListeners
-    if (applicationCache) {
-        applicationCache.addEventListener("checking",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.checking",    "loading")}, false)
-        applicationCache.addEventListener("error",       function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.error",       "loading")}, false)
-        applicationCache.addEventListener("noupdate",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.noupdate",    "loading")}, false)
-        applicationCache.addEventListener("downloading", function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.downloading", "loading")}, false)
-        applicationCache.addEventListener("progress",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.progress",    "loading")}, false)
-        applicationCache.addEventListener("updateready", function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.updateready", "loading")}, false)
-        applicationCache.addEventListener("cached",      function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.cached",      "loading")}, false)
-        applicationCache.addEventListener("obsolete",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.obsolete",    "loading")}, false)
-    }
-    
-    // window.addEventListener("deviceorientation", function(e) {Timeline.addRecord_EventDispatch("window.deviceorientation")}, false)
-    window.addEventListener("error",             function(e) {Timeline.addRecord_EventDispatch(e, "window.error")},             false)
-    window.addEventListener("hashchange",        function(e) {Timeline.addRecord_EventDispatch(e, "window.hashchange")},        false)
-    window.addEventListener("message",           function(e) {Timeline.addRecord_EventDispatch(e, "window.message")},           false)
-    window.addEventListener("offline",           function(e) {Timeline.addRecord_EventDispatch(e, "window.offline")},           false)
-    window.addEventListener("online",            function(e) {Timeline.addRecord_EventDispatch(e, "window.online")},            false)
-    window.addEventListener("scroll",            function(e) {Timeline.addRecord_EventDispatch(e, "window.scroll")},            false)
-    
-//-----------------------------------------------------------------------------
-static method installFunctionWrappers
-    window.clearInterval  = wrapped_clearInterval
-    window.clearTimeout   = wrapped_clearTimeout
-    window.setTimeout     = wrapped_setTimeout
-    window.setInterval    = wrapped_setInterval
-
-    window.XMLHttpRequest.prototype.open = wrapped_XMLHttpRequest_open
-    window.XMLHttpRequest                = wrapped_XMLHttpRequest
-
-//-----------------------------------------------------------------------------
-function addStackTrace(record, skip)
-    if (!skip) skip = 1
-    
-    var trace = new StackTrace(arguments).trace
-    
-    record.stackTrace = []
-    
-    for (var i=skip; i<trace.length; i++) {
-        record.stackTrace.push({
-            functionName: trace[i],
-            scriptName:   "",
-            lineNumber:   ""
-        })
-    }
-    
-//-----------------------------------------------------------------------------
-function wrapped_setInterval(code, interval)
-    var code = instrumentedTimerCode(code, interval, false)
-    var id = Native.setInterval(code, interval)
-    
-    code.__timerId = id
-    
-    addTimer(id, interval, false)
-
-    return id
-
-//-----------------------------------------------------------------------------
-function wrapped_setTimeout(code, delay)
-    var code = instrumentedTimerCode(code, delay, true)
-    var id   = Native.setTimeout(code, delay)
-
-    code.__timerId = id
-    
-    addTimer(id, delay, true)
-
-    return id
-
-//-----------------------------------------------------------------------------
-function wrapped_clearInterval(id)
-    var result = Native.clearInterval(id)
-    
-    removeTimer(id, false)
-    return result
-
-//-----------------------------------------------------------------------------
-function wrapped_clearTimeout(id)
-    var result = Native.clearTimeout(id)
-    
-    removeTimer(id, true)
-    return result
-
-//-----------------------------------------------------------------------------
-function addTimer(id, timeout, singleShot)
-    var timerSet = singleShot ? TimerTimeouts : TimerIntervals
-    
-    timerSet[id] = {
-         id:          id,
-         timeout:     timeout,
-         singleShot: singleShot
-    }
-    
-    Timeline.addRecord_TimerInstall(id, timeout, singleShot)
-    
-//-----------------------------------------------------------------------------
-function removeTimer(id, singleShot)
-    var timerSet = singleShot ? TimerTimeouts : TimerIntervals
-    var timer = timerSet[id]
-    
-    if (!timer) return
-    
-    Timeline.addRecord_TimerRemove(id, timer.timeout, singleShot)
-    
-    delete timerSet[id]
-    
-//-----------------------------------------------------------------------------
-function instrumentedTimerCode(code, timeout, singleShot)
-    if (typeof(code) != "function") return code
-    
-    var instrumentedCode = function() {
-        var result = code()
-        var id     = arguments.callee.__timerId
-
-        Timeline.addRecord_TimerFire(id, timeout, singleShot)
-
-        return result
-    }
-    
-    return instrumentedCode 
-
-//-----------------------------------------------------------------------------
-function wrapped_XMLHttpRequest
-    var xhr = new Native.XMLHttpRequest()
-    IDGenerator.getId(xhr)
-    xhr.addEventListener("readystatechange", getXhrEventHandler(xhr), false)
-    return xhr
-
-init
-    wrapped_XMLHttpRequest.UNSENT           = 0
-    wrapped_XMLHttpRequest.OPENED           = 1
-    wrapped_XMLHttpRequest.HEADERS_RECEIVED = 2
-    wrapped_XMLHttpRequest.LOADING          = 3
-    wrapped_XMLHttpRequest.DONE             = 4    
-
-//-----------------------------------------------------------------------------
-function wrapped_XMLHttpRequest_open()
-    var xhr = this
-    xhr.__weinre_method  = arguments[0]
-    xhr.__weinre_url     = arguments[1]
-    
-    var result = Native.XMLHttpRequest_open.apply(xhr, [].slice.call(arguments))
-    return result
-
-    
-//-----------------------------------------------------------------------------
-function getXhrEventHandler(xhr)
-    return function(event) {
-        Timeline.addRecord_XHRReadyStateChange(xhr.__weinre_method, xhr.__weinre_url, IDGenerator.getId(xhr), xhr)
-    }
-    
-//-----------------------------------------------------------------------------
-init 
-    var Running = false
-    
-    var TimerTimeouts  = {}
-    var TimerIntervals = {}
-
-    var 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
-    }
-
-    Timeline.installGlobalListeners()
-    Timeline.installFunctionWrappers()
-    
\ No newline at end of file
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/WeinreExtraClientCommandsImpl.scoop b/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop
deleted file mode 100644
index 28d6843..0000000
--- a/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-requireClass ./WiDatabaseImpl
-requireClass ./Console
-
-//-----------------------------------------------------------------------------
-class WeinreExtraClientCommandsImpl
-
-//-----------------------------------------------------------------------------
-method getDatabases(callback)
-    if (!callback) return
-    
-    var result = WiDatabaseImpl.getDatabases()
-    
-    Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-
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/WeinreTargetEventsImpl.scoop b/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
deleted file mode 100644
index 6f8a5e3..0000000
--- a/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
+++ /dev/null
@@ -1,39 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../common/Callback
-
-requireClass ./Console
-
-//-----------------------------------------------------------------------------
-class WeinreTargetEventsImpl
-
-//-----------------------------------------------------------------------------
-method connectionCreated(/*string*/ clientChannel, /*string*/ targetChannel)
-    var message = "weinre: target " + targetChannel + " connected to client " + clientChannel
-    
-    Weinre.logInfo(message)
-    var oldValue = Console.useRemote(true)
-    
-    // Weinre.showNotImplemented()
-
-    Weinre.target.setDocument()
-    Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
-    Weinre.wi.DOMStorage.initialize()
-
-//-----------------------------------------------------------------------------
-method connectionDestroyed(/*string*/ clientChannel, /*string*/ targetChannel)
-    var message = "weinre: target " + targetChannel + " disconnected from client " + clientChannel
-    
-    Weinre.logInfo(message)
-    var oldValue = Console.useRemote(false)
-    
-//-----------------------------------------------------------------------------
-method sendCallback(/*int*/ callbackId, /*any*/ result)
-    Callback.invoke(callbackId, result)
diff --git a/weinre.web/modules/weinre/target/WiCSSImpl.coffee b/weinre.web/modules/weinre/target/WiCSSImpl.coffee
new file mode 100644
index 0000000..c5e0932
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiCSSImpl.coffee
@@ -0,0 +1,118 @@
+
+#---------------------------------------------------------------------------------
+# 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
+
+    #---------------------------------------------------------------------------
+    querySelectorAll: (documentId, selector, callback) ->
+        Weinre.notImplemented arguments.callee.signature
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
diff --git a/weinre.web/modules/weinre/target/WiCSSImpl.scoop b/weinre.web/modules/weinre/target/WiCSSImpl.scoop
deleted file mode 100644
index 29e572e..0000000
--- a/weinre.web/modules/weinre/target/WiCSSImpl.scoop
+++ /dev/null
@@ -1,156 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class WiCSSImpl
-    this.dummyComputedStyle = false
-
-//-----------------------------------------------------------------------------
-method getStylesForNode(/*int*/ nodeId, callback)
-    var result = {}
-    
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-    
-    var computedStyle
-    
-    if (this.dummyComputedStyle) {
-        computedStyle = {
-            styleId:            null,
-            properties:         [],
-            shorthandValues:    [],
-            cssProperties:      []
-        }
-    }
-    else {
-        computedStyle =  Weinre.cssStore.getComputedStyle(node)
-    }
-    
-    var result = {
-        inlineStyle:     Weinre.cssStore.getInlineStyle(node),
-        computedStyle:   computedStyle,
-        matchedCSSRules: Weinre.cssStore.getMatchedCSSRules(node),
-        styleAttributes: Weinre.cssStore.getStyleAttributes(node),
-        pseudoElements:  Weinre.cssStore.getPseudoElements(node),
-        inherited:       []
-    }
-    
-    var parentNode = node.parentNode
-    
-    while (parentNode) {
-        var 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])
-    }
-
-//-----------------------------------------------------------------------------
-method getComputedStyleForNode(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    var result = Weinre.cssStore.getComputedStyle(node) 
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-
-//-----------------------------------------------------------------------------
-method getInlineStyleForNode(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    var result = Weinre.cssStore.getInlineStyle(node)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method getAllStyles(callback)
-    // callback: function(/*any[]*/ styleSheetIds)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method getStyleSheet(/*string*/ styleSheetId, callback)
-    // callback: function(/*any*/ styleSheet)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method getStyleSheetText(/*string*/ styleSheetId, callback)
-    // callback: function(/*string*/ url, /*string*/ text)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method setStyleSheetText(/*string*/ styleSheetId, /*string*/ text, callback)
-    // callback: function(/*boolean*/ success)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method setPropertyText(/*any*/ styleId, /*int*/ propertyIndex, /*string*/ text, /*boolean*/ overwrite, callback)
-    // callback: function(/*any*/ style)
-    
-    var result = Weinre.cssStore.setPropertyText(styleId, propertyIndex, text, overwrite)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-    
-//-----------------------------------------------------------------------------
-method toggleProperty(/*any*/ styleId, /*int*/ propertyIndex, /*boolean*/ disable, callback)
-
-    var result = Weinre.cssStore.toggleProperty(styleId, propertyIndex, disable)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method setRuleSelector(/*any*/ ruleId, /*string*/ selector, callback)
-    // callback: function(/*any*/ rule)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method addRule(/*int*/ contextNodeId, /*string*/ selector, callback)
-    // callback: function(/*any*/ rule)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
-//-----------------------------------------------------------------------------
-method getSupportedCSSProperties(callback)
-    return Weinre.getCSSProperties()
-
-
-//-----------------------------------------------------------------------------
-method querySelectorAll(/*int*/ documentId, /*string*/ selector, callback)
-    // callback: function(/*any[]*/ result)
-    Weinre.notImplemented(arguments.callee.signature)
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/WiConsoleImpl.scoop b/weinre.web/modules/weinre/target/WiConsoleImpl.scoop
deleted file mode 100644
index 8b8e6a4..0000000
--- a/weinre.web/modules/weinre/target/WiConsoleImpl.scoop
+++ /dev/null
@@ -1,36 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class WiConsoleImpl
-    this.messagesEnabled = true
-
-//-----------------------------------------------------------------------------
-method setConsoleMessagesEnabled(/*boolean*/ enabled, callback)
-    var oldValue = this.messagesEnabled
-    this.messagesEnabled = enabled
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [oldValue])
-    }
-
-//-----------------------------------------------------------------------------
-method clearConsoleMessages(callback)
-    Weinre.wi.ConsoleNotify.consoleMessagesCleared()
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [])
-    }
-
-//-----------------------------------------------------------------------------
-method setMonitoringXHREnabled(/*boolean*/ enabled, callback)
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [])
-    }
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/WiDOMImpl.scoop b/weinre.web/modules/weinre/target/WiDOMImpl.scoop
deleted file mode 100644
index 4e7fd2f..0000000
--- a/weinre.web/modules/weinre/target/WiDOMImpl.scoop
+++ /dev/null
@@ -1,188 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class WiDOMImpl
-
-//-----------------------------------------------------------------------------
-method getChildNodes(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature  + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    var children = Weinre.nodeStore.serializeNodeChildren(node, 1)
-    Weinre.wi.DOMNotify.setChildNodes(nodeId, children)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method setAttribute(/*int*/ elementId, /*string*/ name, /*string*/ value, callback)
-    var element = Weinre.nodeStore.getNode(elementId)
-    if (!element) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid elementId: " + elementId)
-        return
-    }
-
-    element.setAttribute(name, value)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method removeAttribute(/*int*/ elementId, /*string*/ name, callback)
-    var element = Weinre.nodeStore.getNode(elementId)
-    if (!element) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid elementId: " + elementId)
-        return
-    }
-
-    element.removeAttribute(name)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method setTextNodeValue(/*int*/ nodeId, /*string*/ value, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    node.nodeValue = value
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method getEventListenersForNode(/*int*/ nodeId, callback)
-    // callback: function(/*int*/ outNodeId, /*any[]*/ listenersArray)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method copyNode(/*int*/ nodeId, callback)
-    // callback: function()
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method removeNode(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    if (!node.parentNode) {
-        Weinre.logWarning(arguments.callee.signature + " passed a parentless node: " + node)
-        return
-    }
-
-    node.parentNode.removeChild(node)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method changeTagName(/*int*/ nodeId, /*string*/ newTagName, callback)
-    // callback: function(/*int*/ outNodeId)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method getOuterHTML(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    value = node.outerHTML
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [value])
-    }
-
-//-----------------------------------------------------------------------------
-method setOuterHTML(/*int*/ nodeId, /*string*/ outerHTML, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    node.outerHTML = outerHTML
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method addInspectedNode(/*int*/ nodeId, callback)
-    Weinre.nodeStore.addInspectedNode(nodeId)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-    
-//-----------------------------------------------------------------------------
-method performSearch(/*string*/ query, /*boolean*/ runSynchronously, callback)
-    // callback: function()
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method searchCanceled(callback)
-    // callback: function()
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method pushNodeByPathToFrontend(/*string*/ path, callback)
-    // callback: function(/*int*/ nodeId)
-    Weinre.notImplemented(arguments.callee.signature)
-
-//-----------------------------------------------------------------------------
-method resolveNode(/*int*/ nodeId, callback)
-    var result = Weinre.injectedScript.resolveNode(nodeId)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method getNodeProperties(/*int*/ nodeId, /*any[]*/ propertiesArray, callback)
-    var propertiesArray = JSON.stringify(propertiesArray)
-    var result = Weinre.injectedScript.getNodeProperties(nodeId, propertiesArray)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method getNodePrototypes(/*int*/ nodeId, callback)
-    var result = Weinre.injectedScript.getNodePrototypes(nodeId)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method pushNodeToFrontend(/*any*/ objectId, callback)
-    var objectId = JSON.stringify(objectId)
-    var result = Weinre.injectedScript.pushNodeToFrontend(objectId)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
diff --git a/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee b/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee
new file mode 100644
index 0000000..b52e9c0
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiDOMStorageImpl.coffee
@@ -0,0 +1,134 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+HookSites = require('./HookSites')
+
+#-------------------------------------------------------------------------------
+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
+            HookLib.ignoreHooks ->
+                if storageArea == window.localStorage
+                    localStorage.setItem key, value
+                else if storageArea == window.sessionStorage
+                    sessionStorage.setItem key, value
+        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
+            HookLib.ignoreHooks ->
+                if storageArea == window.localStorage
+                    localStorage.removeItem key
+                else if storageArea == window.sessionStorage
+                    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
+
+            HookSites.LocalStorage_setItem.addHooks
+                after: -> _storageEventHandler storageArea: window.localStorage
+
+            HookSites.LocalStorage_removeItem.addHooks
+                after: -> _storageEventHandler storageArea: window.localStorage
+
+            HookSites.LocalStorage_clear.addHooks
+                after: -> _storageEventHandler storageArea: window.localStorage
+
+        if window.sessionStorage
+            Weinre.wi.DOMStorageNotify.addDOMStorage
+                id: 2
+                host: window.location.host
+                isLocalStorage: false
+
+            HookSites.SessionStorage_setItem.addHooks
+                after: -> _storageEventHandler storageArea: window.sessionStorage
+
+            HookSites.SessionStorage_removeItem.addHooks
+                after: -> _storageEventHandler storageArea: window.sessionStorage
+
+            HookSites.SessionStorage_clear.addHooks
+                after: -> _storageEventHandler storageArea: window.sessionStorage
+
+        document.addEventListener "storage", _storageEventHandler, false
+
+#-------------------------------------------------------------------------------
+_getStorageArea = (storageId) ->
+      if storageId == 1
+          return window.localStorage
+      else if storageId == 2
+          return window.sessionStorage
+
+      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/WiDOMStorageImpl.scoop b/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
deleted file mode 100644
index b3d907f..0000000
--- a/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
+++ /dev/null
@@ -1,170 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../common/Native
-
-//-----------------------------------------------------------------------------
-class WiDOMStorageImpl
-
-//-----------------------------------------------------------------------------
-method getDOMStorageEntries(/*int*/ storageId, callback)
-    var storageArea = _getStorageArea(storageId)
-    
-    if (!storageArea) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid storageId: " + storageId)
-        return
-    }
-    
-    var result = []
-    
-    var length = storageArea.length
-    for (var i=0; i<length; i++) {
-        var key = storageArea.key(i)
-        var val = storageArea.getItem(key)
-        
-        result.push([key, val])    
-    }
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method setDOMStorageItem(/*int*/ storageId, /*string*/ key, /*string*/ value, callback)
-    var storageArea = _getStorageArea(storageId)
-    
-    if (!storageArea) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid storageId: " + storageId)
-        return
-    }
-    
-    var result = true
-    try {
-        if (storageArea == window.localStorage) {
-            Native.LocalStorage_setItem(key, value)
-        }
-        else if (storageArea == window.sessionStorage) {
-            Native.SessionStorage_setItem(key, value)
-        }
-    }
-    catch (e) {
-        result = false
-    }
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method removeDOMStorageItem(/*int*/ storageId, /*string*/ key, callback)
-    var storageArea = _getStorageArea(storageId)
-    
-    if (!storageArea) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid storageId: " + storageId)
-        return
-    }
-
-    var result = true
-    try {
-        if (storageArea == window.localStorage) {
-            Native.LocalStorage_removeItem(key)
-        }
-        else if (storageArea == window.sessionStorage) {
-            Native.SessionStorage_removeItem(key)
-        }
-    }
-    catch (e) {
-        result = false
-    }
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-    
-//-----------------------------------------------------------------------------
-function _getStorageArea(storageId)
-    if (storageId == 1) {
-        return window.localStorage
-    }
-    
-    else if (storageId == 2) {
-        return window.sessionStorage
-    }
-    
-    return null
-
-//-----------------------------------------------------------------------------
-method initialize
-
-    if (window.localStorage) {
-        Weinre.wi.DOMStorageNotify.addDOMStorage({
-            id:             1,
-            host:           window.location.host,
-            isLocalStorage: true
-        })
-        
-        window.localStorage.setItem = function(key, value) {
-            Native.LocalStorage_setItem(key, value)
-            _storageEventHandler({storageArea: window.localStorage})
-        }
-        
-        window.localStorage.removeItem = function(key) {
-            Native.LocalStorage_removeItem(key)
-            _storageEventHandler({storageArea: window.localStorage})
-        }
-        
-        window.localStorage.clear = function() {
-            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 = function(key, value) {
-            Native.SessionStorage_setItem(key, value)
-            _storageEventHandler({storageArea: window.sessionStorage})
-        }
-        
-        window.sessionStorage.removeItem = function(key) {
-            Native.SessionStorage_removeItem(key)
-            _storageEventHandler({storageArea: window.sessionStorage})
-        }
-        
-        window.sessionStorage.clear = function() {
-            Native.SessionStorage_clear()
-            _storageEventHandler({storageArea: window.sessionStorage})
-        }
-    }
-    
-    document.addEventListener("storage", _storageEventHandler, false)
-    
-//-----------------------------------------------------------------------------
-function _storageEventHandler(event)
-    var storageId
-    
-    if (event.storageArea == window.localStorage) {
-        storageId = 1
-    }
-    
-    else if (event.storageArea == window.sessionStorage) {
-        storageId = 2
-    }
-    
-    else {
-        return
-    }
-    
-    Weinre.wi.DOMStorageNotify.updateDOMStorage(storageId)
diff --git a/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee b/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee
new file mode 100644
index 0000000..0c58b13
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WiDatabaseImpl.coffee
@@ -0,0 +1,160 @@
+
+#---------------------------------------------------------------------------------
+# 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')
+HookSites   = require('./HookSites')
+SqlStepper  = require('./SqlStepper')
+
+id2db   = {}
+name2db = {}
+
+#-------------------------------------------------------------------------------
+module.exports = class WiDatabaseImpl
+
+    constructor: ->
+        return unless window.openDatabase
+
+        HookSites.window_openDatabase.addHooks
+            after: (receiver, args, db) ->
+                return if not db
+
+                name    = args[0]
+                version = args[1]
+
+                dbAdd db, name, version
+
+    #---------------------------------------------------------------------------
+    @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
+
+#-------------------------------------------------------------------------------
+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/WiDatabaseImpl.scoop b/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
deleted file mode 100644
index e9fa6df..0000000
--- a/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
+++ /dev/null
@@ -1,171 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../common/Native
-requireClass ../common/IDGenerator
-requireClass ./SqlStepper
-
-//-----------------------------------------------------------------------------
-class WiDatabaseImpl
-    if (!window.openDatabase) return
-    
-    window.openDatabase = wrappedOpenDatabase
-
-//-----------------------------------------------------------------------------
-static method getDatabases
-    var result = []
-    
-    for (var id in id2db) {
-        result.push(id2db[id])
-    }
-    
-    return result
-
-//-----------------------------------------------------------------------------
-function logSqlError(sqlError)
-    console.log("SQL Error " + sqlError.code + ": " + sqlError.message)
-
-//-----------------------------------------------------------------------------
-function getTableNames_step_1() 
-    this.executeSql("SELECT name FROM sqlite_master WHERE type='table'");
-
-//-----------------------------------------------------------------------------
-function getTableNames_step_2(resultSet) 
-    var rows = resultSet.rows
-    var result = []
-    for (var i=0; i<rows.length; i++) {
-        var name = rows.item(i).name
-        if (name == "__WebKitDatabaseInfoTable__") continue
-        
-        result.push(name)
-    }
-    
-    Weinre.WeinreTargetCommands.sendClientCallback(this.callback, [result])
-
-//-----------------------------------------------------------------------------
-method getDatabaseTableNames( databaseId, callback)
-    var db = dbById(databaseId)
-    if (!db) return
-  
-    var stepper = SqlStepper([
-        getTableNames_step_1, 
-        getTableNames_step_2
-    ])
-    
-    stepper.callback = callback
-    stepper.run(db, logSqlError)
-
-//-----------------------------------------------------------------------------
-function executeSQL_step_1()
-    this.executeSql(this.query);
-
-//-----------------------------------------------------------------------------
-function executeSQL_step_2(resultSet)
-    var columnNames = []
-    var values      = []
-    var rows        = resultSet.rows
-    
-    for (var i=0; i<rows.length; i++) {
-        var row = rows.item(i)
-        
-        if (i == 0) {
-            for (var propName in row) {
-                columnNames.push(propName)
-            }
-        }
-        
-        for (var j=0; j<columnNames.length; j++) {
-            values.push(row[columnNames[j]])
-        }
-    }
-
-    Weinre.wi.DatabaseNotify.sqlTransactionSucceeded(this.txid, columnNames, values)
-
-//-----------------------------------------------------------------------------
-function executeSQL_error(sqlError)
-    var error = {
-        code:    sqlError.code,
-        message: sqlError.message
-    }
-    
-    Weinre.wi.DatabaseNotify.sqlTransactionFailed(this.txid, error)
-
-//-----------------------------------------------------------------------------
-method executeSQL(/*int*/ databaseId, /*string*/ query, callback)
-    var db = dbById(databaseId)
-    if (!db) return
-    
-    var txid = Weinre.targetDescription.channel + "-" + IDGenerator.next()
-    
-    var 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])
-    }
-    
-
-//-----------------------------------------------------------------------------
-function wrappedOpenDatabase(name, version, displayName, estimatedSize, creationCallback)
-
-    var db = Native.openDatabase(name, version, displayName, estimatedSize, creationCallback)
-    
-    dbAdd(db, name, version)
-    
-    return db    
-    
-//-----------------------------------------------------------------------------
-init 
-    var id2db   = {}
-    var name2db = {}
-
-//-----------------------------------------------------------------------------
-function dbById(id) 
-    var record = id2db[id]
-    if (!record) return null
-    return record.db
-    
-//-----------------------------------------------------------------------------
-function dbRecordById(id) 
-    return id2db[id]
-    
-//-----------------------------------------------------------------------------
-function dbRecordByName(name) 
-    return name2db[name]
-    
-//-----------------------------------------------------------------------------
-function dbAdd(db, name, version)
-    
-    var record = dbRecordByName(name)
-    if (record) return 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
-
-    var payload = {}
-    payload.id      = record.id
-    payload.domain  = record.domain
-    payload.name    = name
-    payload.version = version
-    
-    Weinre.WeinreExtraTargetEvents.databaseOpened(payload)
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/WiInspectorImpl.scoop b/weinre.web/modules/weinre/target/WiInspectorImpl.scoop
deleted file mode 100644
index aed26ad..0000000
--- a/weinre.web/modules/weinre/target/WiInspectorImpl.scoop
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-requireClass ../target/Timeline
-
-//-----------------------------------------------------------------------------
-class WiInspectorImpl
-
-//-----------------------------------------------------------------------------
-method reloadPage(callback)
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-    window.location.reload()
-
-//-----------------------------------------------------------------------------
-method highlightDOMNode(/*int*/ nodeId, callback)
-    var node = Weinre.nodeStore.getNode(nodeId)
-    if (!node) {
-        Weinre.logWarning(arguments.callee.signature + " passed an invalid nodeId: " + nodeId)
-        return
-    }
-
-    Weinre.elementHighlighter.on(node)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method hideDOMNodeHighlight(callback)
-    Weinre.elementHighlighter.off()
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-//-----------------------------------------------------------------------------
-method startTimelineProfiler(callback)
-    Timeline.start()
-    
-    Weinre.wi.TimelineNotify.timelineProfilerWasStarted()
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
-
-//-----------------------------------------------------------------------------
-method stopTimelineProfiler(callback)
-    Timeline.stop()
-    
-    Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback)
-    }
-
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)
diff --git a/weinre.web/modules/weinre/target/WiRuntimeImpl.scoop b/weinre.web/modules/weinre/target/WiRuntimeImpl.scoop
deleted file mode 100644
index 44cd0e9..0000000
--- a/weinre.web/modules/weinre/target/WiRuntimeImpl.scoop
+++ /dev/null
@@ -1,55 +0,0 @@
-
-/*
- * 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
- */
-
-requireClass ../common/Weinre
-
-//-----------------------------------------------------------------------------
-class WiRuntimeImpl
-
-//-----------------------------------------------------------------------------
-method evaluate(/*string*/ expression, /*string*/ objectGroup, /*boolean*/ includeCommandLineAPI, callback)
-    var result = Weinre.injectedScript.evaluate(expression, objectGroup, includeCommandLineAPI)
-    
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method getCompletions(/*string*/ expression, /*boolean*/ includeCommandLineAPI, callback)
-    var result = Weinre.injectedScript.getCompletions(expression, includeCommandLineAPI)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method getProperties(/*any*/ objectId, /*boolean*/ ignoreHasOwnProperty, /*boolean*/ abbreviate, callback)
-    var objectId = JSON.stringify(objectId)
-    var result = Weinre.injectedScript.getProperties(objectId, ignoreHasOwnProperty, abbreviate)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method setPropertyValue(/*any*/ objectId, /*string*/ propertyName, /*string*/ expression, callback)
-    var objectId = JSON.stringify(objectId)
-    var result = Weinre.injectedScript.setPropertyValue(objectId, propertyName, expression)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-
-//-----------------------------------------------------------------------------
-method releaseWrapperObjectGroup(/*int*/ injectedScriptId, /*string*/ objectGroup, callback)
-    var result = Weinre.injectedScript.releaseWrapperObjectGroup(objectGroupName)
-
-    if (callback) {
-        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
-    }
-    
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/unused/ElementHighlighterCanvas.coffee b/weinre.web/modules/weinre/unused/ElementHighlighterCanvas.coffee
new file mode 100644
index 0000000..2549466
--- /dev/null
+++ b/weinre.web/modules/weinre/unused/ElementHighlighterCanvas.coffee
@@ -0,0 +1,80 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+ElementHighlighter = require('./ElementHighlighter')
+
+ColorMargin   =  'rgba(246, 178, 107, 0.66)'
+ColorBorder   =  'rgba(255, 229, 153, 0.66)'
+ColorPadding  =  'rgba(147, 196, 125, 0.55)'
+ColorContent  =  'rgba(111, 168, 220, 0.66)'
+
+#-------------------------------------------------------------------------------
+module.exports = class ElementHighlighterCanvas extends ElementHighlighter
+
+    #---------------------------------------------------------------------------
+    createHighlighterElement: ->
+
+        @canvas = document.createElement('canvas')
+        @canvas.style.position = 'absolute'
+        @canvas
+
+    #---------------------------------------------------------------------------
+    redraw: (m) ->
+
+        @canvas.width  = m.width
+        @canvas.height = m.height
+
+        style = @canvas.style
+
+        style.top     = m.y
+        style.left    = m.x
+        style.height  = m.height
+        style.width   = m.width
+
+        g = @canvas.getContext("2d")
+
+        #--------------------------------------
+        x = 0
+        y = 0
+        w = m.width
+        h = m.height
+        g.fillStyle = ColorMargin
+        g.fillRect(x,y,w,h)
+
+        #--------------------------------------
+        x += m.marginLeft
+        y += m.marginTop
+        w -= m.marginLeft + m.marginRight
+        h -= m.marginTop  + m.marginBottom
+
+        g.fillStyle = ColorBorder
+        g.clearRect(x,y,w,h)
+        g.fillRect(x,y,w,h)
+
+        #--------------------------------------
+        x += m.borderLeft
+        y += m.borderTop
+        w -= m.borderLeft + m.borderRight
+        h -= m.borderTop  + m.borderBottom
+
+        g.fillStyle = ColorPadding
+        g.clearRect(x,y,w,h)
+        g.fillRect(x,y,w,h)
+
+        #--------------------------------------
+        x += m.paddingLeft
+        y += m.paddingTop
+        w -= m.paddingLeft + m.paddingRight
+        h -= m.paddingTop  + m.paddingBottom
+
+        g.fillStyle = ColorContent
+        g.clearRect(x,y,w,h)
+        g.fillRect(x,y,w,h)
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/unused/ElementHighlighterDivs4.coffee b/weinre.web/modules/weinre/unused/ElementHighlighterDivs4.coffee
new file mode 100644
index 0000000..dea11a1
--- /dev/null
+++ b/weinre.web/modules/weinre/unused/ElementHighlighterDivs4.coffee
@@ -0,0 +1,60 @@
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+
+ElementHighlighter = require('./ElementHighlighter')
+
+#-------------------------------------------------------------------------------
+module.exports = class ElementHighlighterDivs4 extends ElementHighlighter
+
+    #---------------------------------------------------------------------------
+    createHighlighterElement: ->
+
+        @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
+
+    #---------------------------------------------------------------------------
+    redraw: (metrics) ->
+
+        @boxMargin.style.top     = metrics.y
+        @boxMargin.style.left    = metrics.x
+        @boxMargin.style.height  = metrics.height
+        @boxMargin.style.width   = metrics.width
+        @boxBorder.style.top     = metrics.marginTop
+        @boxBorder.style.left    = metrics.marginLeft
+        @boxBorder.style.bottom  = metrics.marginBottom
+        @boxBorder.style.right   = metrics.marginRight
+        @boxPadding.style.top    = metrics.borderTop
+        @boxPadding.style.left   = metrics.borderLeft
+        @boxPadding.style.bottom = metrics.borderBottom
+        @boxPadding.style.right  = metrics.borderRight
+        @boxContent.style.top    = metrics.paddingTop
+        @boxContent.style.left   = metrics.paddingLeft
+        @boxContent.style.bottom = metrics.paddingBottom
+        @boxContent.style.right  = metrics.paddingRight
+
+#-------------------------------------------------------------------------------
+require("../common/MethodNamer").setNamesForClass(module.exports)
\ No newline at end of file
diff --git a/weinre.web/tests/element-highlighter.html b/weinre.web/tests/element-highlighter.html
new file mode 100644
index 0000000..d59ef61
--- /dev/null
+++ b/weinre.web/tests/element-highlighter.html
@@ -0,0 +1,79 @@
+<html>
+
+<head>
+<title>weinre.web/tests/element-highlighter.html</title>
+<script>try {window.WeinreServerId = location.hash.split("#")[1]} catch(e) {window.WeinreServerId = "anonymous"}</script>
+<script src="/target/target-script.js"></script>
+</head>
+
+<body>
+
+<style>
+.red  { background-color: red }
+
+.m0   { margin:          0px; }
+.b0   { border:          0px solid gray; }
+.p0   { padding:         0px; }
+
+.m1   { margin:         20px; }
+.b1   { border:         20px solid gray; }
+.p1   { padding:        20px; }
+
+.m-1l { margin-left:   -20px; }
+.m-1r { margin-right:  -20px; }
+.m-1t { margin-top:    -20px; }
+.m-1b { margin-bottom: -20px; }
+
+</style>
+
+<h1> top-level </h1>
+<div class="red m0 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p1" style="width:50px; height:50px;">x</div>
+
+<h1> 2nd-level, single margin </h1>
+
+<div class="m1">
+<div class="red m0 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p1" style="width:50px; height:50px;">x</div>
+</div>
+
+<h1> 2nd-level, double margin </h1>
+
+<div class="m1"><div class="m1">
+<div class="red m0 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m0 b1 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 b1 p1" style="width:50px; height:50px;">x</div>
+</div></div>
+
+<h1> 2nd-level, double margin, negatives </h1>
+
+<div class="m1"><div class="m1">
+<div class="red m1 m-1l b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1r b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1t b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1b b1 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1b b0 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1t b0 p1" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1r b1 p0" style="width:50px; height:50px;">x</div>
+<div class="red m1 m-1l b1 p1" style="width:50px; height:50px;">x</div>
+</div></div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/weinre.web/tests/index.html b/weinre.web/tests/index.html
new file mode 100644
index 0000000..54b577a
--- /dev/null
+++ b/weinre.web/tests/index.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>weinre.web/tests</title>
+</head>
+<body>
+<ul>
+<li><a href="element-highlighter.html">element-highlighter</a>
+</ul>
+</body>
+</html>
\ No newline at end of file
diff --git a/weinre.web/versions.js b/weinre.web/versions.js
index 1b62da4..af1f435 100644
--- a/weinre.web/versions.js
+++ b/weinre.web/versions.js
@@ -1,22 +1,21 @@
 /*
  * 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
  */
 
  if (typeof Weinre == "undefined") Weinre = {};
- 
+
  Weinre.Versions = {
-    weinre:   "@WEINRE_VERSION@", 
-    build:    "@BUILD_NUMBER_DATE@", 
-    jetty:    "@JETTY_VERSION@", 
-    servlet:  "@JAVAX_SERVLET_VERSION@-@JAVAX_SERVLET_VERSION_IMPL@", 
+    weinre:   "@WEINRE_VERSION@",
+    build:    "@BUILD_NUMBER_DATE@",
+    jetty:    "@JETTY_VERSION@",
+    servlet:  "@JAVAX_SERVLET_VERSION@-@JAVAX_SERVLET_VERSION_IMPL@",
     webkit:   "@WEBKIT_VERSION@",
     cli:      "@CLI_VERSION@",
     json4j:   "@JSON4J_VERSION@",
     json2:    "@JSON2_VERSION@",
     swt:      "@SWT_VERSION@",
-    modjewel: "@MODJEWEL_VERSION@",
-    scooj:    "@SCOOJ_VERSION@"
+    modjewel: "@MODJEWEL_VERSION@"
  };