Merge branch 'release/1.3.0'
diff --git a/weinre.build/build.properties b/weinre.build/build.properties
index 2b833f7..3c0a0f1 100644
--- a/weinre.build/build.properties
+++ b/weinre.build/build.properties
@@ -8,7 +8,7 @@
 #-----------------------------------------------------------
 # weinre version
 #-----------------------------------------------------------
-WEINRE_VERSION: 1.2.0
+WEINRE_VERSION: 1.3.0
 
 #-----------------------------------------------------------
 # some common locations used in the ant scripts
diff --git a/weinre.build/debug.sh b/weinre.build/debug.sh
index 4abc85c..48774e1 100755
--- a/weinre.build/debug.sh
+++ b/weinre.build/debug.sh
@@ -10,4 +10,6 @@
 	-Dfile.encoding=UTF-8 \
 	-classpath $CP \
 	weinre.server.Main \
-	--verbose true
\ No newline at end of file
+	--verbose      true \
+	--deathTimeout 120
+	
\ No newline at end of file
diff --git a/weinre.build/vendor.properties b/weinre.build/vendor.properties
index 59aba29..fd37063 100644
--- a/weinre.build/vendor.properties
+++ b/weinre.build/vendor.properties
@@ -42,7 +42,7 @@
 #-----------------------------------------------------------
 
 JETTY_VERSION:              7.1.6.v20100715
-JETTY_URL_PREFIX:           http://repo1.maven.org/maven2/org/eclipse/jetty
+JETTY_URL_PREFIX:           https://oss.sonatype.org/content/groups/jetty/org/eclipse/jetty/
 
 JETTY_SRC_JAR_NAME:         jetty-all-server-${JETTY_VERSION}-sources.jar
 JETTY_SRC_URL:              ${JETTY_URL_PREFIX}/aggregate/jetty-all-server/${JETTY_VERSION}/${JETTY_SRC_JAR_NAME}
@@ -54,12 +54,12 @@
 # location of servlet API 
 #-----------------------------------------------------------
 
-JAVAX_SERVLET_VERSION:      2.5
+JAVAX_SERVLET_VERSION:      2.5-20110124
 JAVAX_SERVLET_VERSION_IMPL: 6.1H.8
-JAVAX_SERVLET_URL_PREFIX:   http://repo1.maven.org/maven2/org/mortbay/jetty
+JAVAX_SERVLET_URL_PREFIX:   https://oss.sonatype.org/content/groups/jetty/org/mortbay/jetty/servlet-api/
 
-JAVAX_SERVLET_JAR_NAME:     servlet-api-${JAVAX_SERVLET_VERSION}-${JAVAX_SERVLET_VERSION_IMPL}.jar
-JAVAX_SERVLET_URL:          ${JAVAX_SERVLET_URL_PREFIX}/servlet-api-${JAVAX_SERVLET_VERSION}/${JAVAX_SERVLET_VERSION_IMPL}/${JAVAX_SERVLET_JAR_NAME}
+JAVAX_SERVLET_JAR_NAME:     servlet-api-${JAVAX_SERVLET_VERSION}.jar
+JAVAX_SERVLET_URL:          ${JAVAX_SERVLET_URL_PREFIX}/${JAVAX_SERVLET_VERSION}/${JAVAX_SERVLET_JAR_NAME}
 
 #-----------------------------------------------------------
 # location of Eclipse SWT
@@ -67,7 +67,7 @@
 
 SWT_VERSION_MAJOR: 3.6
 SWT_VERSION:       ${SWT_VERSION_MAJOR}-201006080911
-SWT_URL_PREFIX:    http://download.eclipse.org/eclipse/downloads/drops/R-${SWT_VERSION}
+SWT_URL_PREFIX:    http://archive.eclipse.org/eclipse/downloads/drops/R-${SWT_VERSION}
 
 #-----------------------------------------------------------
 # location of scooj
diff --git a/weinre.doc/ChangeLog.body.html b/weinre.doc/ChangeLog.body.html
index e1760e1..c4f52ac 100644
--- a/weinre.doc/ChangeLog.body.html
+++ b/weinre.doc/ChangeLog.body.html
@@ -6,6 +6,23 @@
 -->
 
 <!-- ======================================================================= -->
+<h2>2011/04/05 - version 1.3.0</h2>
+<ul>
+<li> style editing now enabled in Elements panel
+<li> localStorage and sessionStorage views enabled in Resources panel
+<li> URLs to vendor repos updated for the weinre build
+<li> check for bad versions of Prototype.js
+</ul>
+
+<p>issues closed:
+<ul>
+<li><a href="https://github.com/pmuellr/weinre/issues/21">issue 21</a> - add support for localStorage
+<li><a href="https://github.com/pmuellr/weinre/issues/35">issue 35</a> - testing local files on IOS
+<li><a href="https://github.com/pmuellr/weinre/issues/36">issue 36</a> - element.style cannot be edited
+<li><a href="https://github.com/pmuellr/weinre/issues/44">issue 44</a> - update vendor repos
+</ul>
+
+<!-- ======================================================================= -->
 <h2>2011/03/12 - version 1.2.0</h2>
 <ul>
 <li> DOM elements are now updated live as they change in the target
diff --git a/weinre.doc/Home.body.html b/weinre.doc/Home.body.html
index 1660429..79c0896 100644
--- a/weinre.doc/Home.body.html
+++ b/weinre.doc/Home.body.html
@@ -97,32 +97,37 @@
 how to use it, visit the <a href="TestDrive.html">Test Drive</a>.
 
 <!-- ======================================================== -->
-<h2>Supported Platforms</h2>
+<h2>Supported Libraries and Platforms</h2>
+
+<p>Generally version numbers listed here are the earliest
+versions of the relevant thing that have been tested.
+Later versions of those things are also hopefully supported,
+but let us know.
+
+<h3>Libraries not supported</h3>
+
+<ul>
+<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
+version of Prototype.js.
+</ul>
 
 <h3>Debug client - the browser where the debugger user interface runs</h3>
 
 <ul>
 <li>weinre Mac application - Mac OS X 10.6 64-bit
-<li>Google Chrome 8.x beta
+<li>Google Chrome 8.x
 <li>Apple Safari 5.x 
-<li>others? please set us know
 </ul>
 
 <h3>Debug target - the browser with the page you are debugging</h3>
 
 <ul>
-<li>Android 2.3 Browser application
-<li>Android 2.3 w/PhoneGap 0.9.2
 <li>Android 2.2 Browser application
 <li>Android 2.2 w/PhoneGap 0.9.2
 <li>iOS 4.2.x Mobile Safari application
 <li>BlackBerry v6.x simulator
-<li>others? please set us know
-</ul>
-
-<h3>Platforms that do not work</h3>
-
-<ul>
-<li>others? please set us know
 </ul>
 
diff --git a/weinre.web/demo/weinre-demo-min.html b/weinre.web/demo/weinre-demo-min.html
index 3170577..9adcc3b 100644
--- a/weinre.web/demo/weinre-demo-min.html
+++ b/weinre.web/demo/weinre-demo-min.html
@@ -14,7 +14,6 @@
 
 <script type="text/javascript">
     require("weinre/common/Weinre").showNotImplemented()
-    require("weinre/target/Target").main()
 </script>
 
 <script src="weinre-demo.js"></script>
diff --git a/weinre.web/demo/weinre-demo-pieces.html b/weinre.web/demo/weinre-demo-pieces.html
index 2ef3573..2b87785 100644
--- a/weinre.web/demo/weinre-demo-pieces.html
+++ b/weinre.web/demo/weinre-demo-pieces.html
@@ -26,6 +26,7 @@
 <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>
@@ -45,7 +46,7 @@
 
 <script type="text/javascript">
     require("weinre/common/Weinre").showNotImplemented()
-    require("weinre/target/Target").main()
+    require('weinre/target/Target').main()
 </script>
 
 <script src="weinre-demo.js"></script>
diff --git a/weinre.web/demo/weinre-demo.css b/weinre.web/demo/weinre-demo.css
index f4715b2..ec6d164 100644
--- a/weinre.web/demo/weinre-demo.css
+++ b/weinre.web/demo/weinre-demo.css
@@ -6,7 +6,11 @@
  */
 
 h1 {
-    color: green;
+    color:         green;
+    margin:        0.5em;
+    margin-left:   1.0em;
+    padding:       0.4em;
+    padding-left : 0.8em;
 }
 
 .blue {
diff --git a/weinre.web/demo/weinre-demo.html b/weinre.web/demo/weinre-demo.html
index 9300102..ebdd2de 100644
--- a/weinre.web/demo/weinre-demo.html
+++ b/weinre.web/demo/weinre-demo.html
@@ -14,7 +14,6 @@
 
 <script type="text/javascript">
     require("weinre/common/Weinre").showNotImplemented()
-    require("weinre/target/Target").main()
 </script>
 
 <script src="weinre-demo.js"></script>
diff --git a/weinre.web/demo/weinre-demo.js b/weinre.web/demo/weinre-demo.js
index 5e3be85..6be2622 100644
--- a/weinre.web/demo/weinre-demo.js
+++ b/weinre.web/demo/weinre-demo.js
@@ -11,6 +11,7 @@
 var buttonStartStuff
 var buttonClearOutput
 var outputElement 
+var storageIndex = 0
 
 //------------------------------------------------------------------------------
 function onLoad() {
@@ -40,6 +41,11 @@
 var interval
 
 function startStuff() {
+    if (window.localStorage)   window.localStorage.clear()
+    if (window.sessionStorage) window.sessionStorage.clear()
+    
+    storageIndex = 0
+    
     interval = setInterval(intervalStuff, 1000)
 }
 
@@ -58,6 +64,18 @@
     // 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)
     
diff --git a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
index c121a96..bb5093e 100644
--- a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
+++ b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
@@ -23,7 +23,8 @@
 
 //-----------------------------------------------------------------------------
 method hiddenPanels
-    return "audits,profiles,resources,network"
+//    return "audits,profiles,resources,network"
+    return "audits,profiles,network"
 //    return "audits,profiles,resources,scripts,timeline,network"
 
 //-----------------------------------------------------------------------------
diff --git a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
index c58fbb0..8c35aeb 100644
--- a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
+++ b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
@@ -54,6 +54,7 @@
     
     WebInspector.panels.elements.reset()
     WebInspector.panels.timeline._clearPanel()
+    WebInspector.panels.resources.reset()
 
 //-----------------------------------------------------------------------------
 method connectionDestroyed(/*int*/ clientId, /*int*/ targetId)
diff --git a/weinre.web/modules/weinre/common/IDGenerator.scoop b/weinre.web/modules/weinre/common/IDGenerator.scoop
index 0b91770..4829267 100644
--- a/weinre.web/modules/weinre/common/IDGenerator.scoop
+++ b/weinre.web/modules/weinre/common/IDGenerator.scoop
@@ -12,9 +12,38 @@
 //-----------------------------------------------------------------------------
 init
     var nextId = 1
+    var idName = "__weinre__id"
 
 //-----------------------------------------------------------------------------
-static method next
+static method checkId(object)
+    return object[idName]
+    
+//-----------------------------------------------------------------------------
+static method getId(object, map)
+    var id = IDGenerator.checkId(object)
+    
+    if (!id) {
+        id = next()
+        
+        // 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
+    
+//-----------------------------------------------------------------------------
+function next
     var result = nextId
     nextId += 1
-    return result
\ No newline at end of file
+    return result
+
diff --git a/weinre.web/modules/weinre/common/Native.scoop b/weinre.web/modules/weinre/common/Native.scoop
index cc8ce08..705faa7 100644
--- a/weinre.web/modules/weinre/common/Native.scoop
+++ b/weinre.web/modules/weinre/common/Native.scoop
@@ -13,17 +13,28 @@
 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.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.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))}
-    
\ No newline at end of file
+    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))}
diff --git a/weinre.web/modules/weinre/target/CSSStore.scoop b/weinre.web/modules/weinre/target/CSSStore.scoop
index 3f1edf7..5d7575a 100644
--- a/weinre.web/modules/weinre/target/CSSStore.scoop
+++ b/weinre.web/modules/weinre/target/CSSStore.scoop
@@ -6,10 +6,15 @@
  * Copyright (c) 2010, 2011 IBM Corporation
  */
 
+requireClass ../common/IDGenerator
+requireClass ../common/Weinre
+
 //-----------------------------------------------------------------------------
 class CSSStore
-    this.__styleMap    = {}
-    this.__nextId      = 0
+    this.styleSheetMap = {}
+    this.styleRuleMap  = {}
+    this.styleDeclMap  = {}
+    this.testElement = document.createElement("div")
 
 //-----------------------------------------------------------------------------
 init
@@ -20,23 +25,8 @@
     Properties = properties
 
 //-----------------------------------------------------------------------------
-method getStyle(styleId)
-    return this.__styleMap[styleId]
-
-//-----------------------------------------------------------------------------
-method getStyleId(style)
-    if (style.__weinre_id) { 
-        return style.__weinre_id
-    }
-    
-    style.__weinre_id = this._nextId()
-    this.__styleMap[style.__weinre_id] = style
-    
-    return style.__weinre_id
-
-//-----------------------------------------------------------------------------
 method getInlineStyle(node)
-    var styleObject = this.buildObjectForStyle(node.style, true)
+    var styleObject = this._buildMirrorForStyle(node.style, true)
     for (var i=0; i<styleObject.cssProperties.length; i++) {
         styleObject.cssProperties[i].status = "style"
     }
@@ -47,7 +37,7 @@
     if (!node) return {}
     if (node.nodeType != Node.ELEMENT_NODE) return {}
     
-    var styleObject = this.buildObjectForStyle(window.getComputedStyle(node), false)
+    var styleObject = this._buildMirrorForStyle(window.getComputedStyle(node), false)
     return styleObject
 
 //-----------------------------------------------------------------------------
@@ -55,20 +45,20 @@
     var result = []
     
     for (var i=0; i<document.styleSheets.length; i++) {
-        var cssStyleSheet = document.styleSheets[i]
+        var styleSheet = document.styleSheets[i]
         
-        if (!cssStyleSheet.cssRules) continue
+        if (!styleSheet.cssRules) continue
         
-        for (var j=0; j<cssStyleSheet.cssRules.length; j++) {
-            var cssRule = cssStyleSheet.cssRules[j]
+        for (var j=0; j<styleSheet.cssRules.length; j++) {
+            var cssRule = styleSheet.cssRules[j]
             
-            if (!elementMatchesSelector(node, cssRule.selectorText)) continue
+            if (!_elementMatchesSelector(node, cssRule.selectorText)) continue
             
             var object = {}
             
-            object.ruleId = this.getStyleId(cssRule)
+            object.ruleId = this._getStyleRuleId(cssRule)
             object.selectorText = cssRule.selectorText
-            object.style = this.buildObjectForStyle(cssRule.style, true)
+            object.style = this._buildMirrorForStyle(cssRule.style, true)
             result.push(object)
         }
     }
@@ -88,86 +78,314 @@
     return result
 
 //-----------------------------------------------------------------------------
-method buildObjectForStyle(style, bind)
-    var result = {
-        width:         null,
-        height:        null,
-        properties:    [],
-        cssProperties: []
+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
     }
     
-    if (!style) return result
+    // 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])
+            }
+        }
     
-    if (bind) {
-        var styleId = this.getStyleId(style)
-        result.id = styleId
-
-        /*
-        CSSStyleSheet* parentStyleSheet = InspectorCSSStore::getParentStyleSheet(style);
-        if (parentStyleSheet)
-            result->setNumber("parentStyleSheetId", cssStore()->bindStyleSheet(parentStyleSheet));
-
-        DisabledStyleDeclaration* disabledStyle = cssStore()->disabledStyleForId(styleId, false);
-        if (disabledStyle)
-            result->setArray("disabled", buildArrayForDisabledStyleProperties(disabledStyle));
-        */
-    }
-    
-    result.width  = style.getPropertyValue("width")
-    result.height = style.getPropertyValue("height")
-
-    this.populateObjectWithStyleProperties(style, result)
-    
-    return result
-
-//-----------------------------------------------------------------------------
-method populateObjectWithStyleProperties(style, result)
-    var properties = []
-    var shorthandValues = {}
-
-    if (style) {
-        var foundShorthands = {}
-        for (var i=0; i < style.length; i++) {
-            var property = {}
-            var name = style.item(i)
-
-            property.name = name
-            property.priority = style.getPropertyPriority(name)
-            property.implicit = false // style.isPropertyImplicit(name)
-            property.shorthand = ""
-            property.status    = "active"
-            property.parsedOk  = true
-            property.value     = style.getPropertyValue(name)
-
-            properties.push(property);
+        for (var key in propertyMirror.shorthandValues) {
+            mirror.shorthandValues[key] = propertyMirror.shorthandValues[key]
         }
     }
     
-    result.cssProperties   = properties
-    result.shorthandValues = shorthandValues
+    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 _nextId
-    return "" + (++this.__nextId)
+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)
+function _mozMatchesSelector(element, selector)
     if (!element.mozMatchesSelector) return false
     return element.mozMatchesSelector(selector)
 
 //-----------------------------------------------------------------------------
-function webkitMatchesSelector(element, selector)
+function _webkitMatchesSelector(element, selector)
     if (!element.webkitMatchesSelector) return false
     return element.webkitMatchesSelector(selector)
 
 //-----------------------------------------------------------------------------
-function fallbackMatchesSelector(element, selector)
+function _fallbackMatchesSelector(element, selector)
     return false
     
 //-----------------------------------------------------------------------------
 init
-    var elementMatchesSelector
+    var _elementMatchesSelector
     
-    if      (Element.prototype.webkitMatchesSelector) elementMatchesSelector = webkitMatchesSelector 
-    else if (Element.prototype.mozMatchesSelector)    elementMatchesSelector = mozMatchesSelector
-    else                                              elementMatchesSelector = fallbackMatchesSelector
\ No newline at end of file
+    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.scoop b/weinre.web/modules/weinre/target/CheckForProblems.scoop
new file mode 100644
index 0000000..dc25ab8
--- /dev/null
+++ b/weinre.web/modules/weinre/target/CheckForProblems.scoop
@@ -0,0 +1,28 @@
+
+/*
+ * 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/NodeStore.scoop b/weinre.web/modules/weinre/target/NodeStore.scoop
index 3c77093..f8b288f 100644
--- a/weinre.web/modules/weinre/target/NodeStore.scoop
+++ b/weinre.web/modules/weinre/target/NodeStore.scoop
@@ -38,18 +38,16 @@
 
 //-----------------------------------------------------------------------------
 method checkNodeId(node)
-    return node.__weinre_id
+    return IDGenerator.checkId(node)
 
 //-----------------------------------------------------------------------------
 method getNodeId(node)
-    if (node.__weinre_id) { 
-        return node.__weinre_id
+    var id = this.checkNodeId(node)
+    if (id) {
+        return id
     }
-    
-    node.__weinre_id = this.nextNodeId()
-    this.__nodeMap[node.__weinre_id]     = node
-    
-    return node.__weinre_id
+
+    return IDGenerator.getId(node, this.__nodeMap)    
 
 //-----------------------------------------------------------------------------
 method getNodeData(nodeId, depth)
diff --git a/weinre.web/modules/weinre/target/Target.scoop b/weinre.web/modules/weinre/target/Target.scoop
index 73d8c00..d51ce41 100644
--- a/weinre.web/modules/weinre/target/Target.scoop
+++ b/weinre.web/modules/weinre/target/Target.scoop
@@ -14,6 +14,7 @@
 requireClass ../common/MessageDispatcher
 requireClass ../common/Weinre
 
+requireClass ./CheckForProblems
 requireClass ./NodeStore
 requireClass ./CSSStore
 requireClass ./ElementHighlighter
@@ -34,6 +35,8 @@
 
 //-----------------------------------------------------------------------------
 static method main
+    CheckForProblems.check()
+    
     Weinre.target = new Target()
     Weinre.target.initialize()
     
diff --git a/weinre.web/modules/weinre/target/Timeline.scoop b/weinre.web/modules/weinre/target/Timeline.scoop
index 4614dc6..4e9629b 100644
--- a/weinre.web/modules/weinre/target/Timeline.scoop
+++ b/weinre.web/modules/weinre/target/Timeline.scoop
@@ -263,7 +263,7 @@
 //-----------------------------------------------------------------------------
 function wrapped_XMLHttpRequest
     var xhr = new Native.XMLHttpRequest()
-    xhr.__weinre_id = IDGenerator.next()
+    IDGenerator.getId(xhr)
     xhr.addEventListener("readystatechange", getXhrEventHandler(xhr), false)
     return xhr
 
@@ -287,7 +287,7 @@
 //-----------------------------------------------------------------------------
 function getXhrEventHandler(xhr)
     return function(event) {
-        Timeline.addRecord_XHRReadyStateChange(xhr.__weinre_method, xhr.__weinre_url, xhr.__weinre_id, xhr)
+        Timeline.addRecord_XHRReadyStateChange(xhr.__weinre_method, xhr.__weinre_url, IDGenerator.getId(xhr), xhr)
     }
     
 //-----------------------------------------------------------------------------
diff --git a/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop b/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
index 2a00e04..9adf1e9 100644
--- a/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
+++ b/weinre.web/modules/weinre/target/WeinreTargetEventsImpl.scoop
@@ -25,6 +25,7 @@
 
     Weinre.target.setDocument()
     Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
+    Weinre.wi.DOMStorage.initialize()
 
 //-----------------------------------------------------------------------------
 method connectionDestroyed(/*string*/ clientId, /*string*/ targetId)
diff --git a/weinre.web/modules/weinre/target/WiCSSImpl.scoop b/weinre.web/modules/weinre/target/WiCSSImpl.scoop
index ad4cd05..29e572e 100644
--- a/weinre.web/modules/weinre/target/WiCSSImpl.scoop
+++ b/weinre.web/modules/weinre/target/WiCSSImpl.scoop
@@ -10,6 +10,7 @@
 
 //-----------------------------------------------------------------------------
 class WiCSSImpl
+    this.dummyComputedStyle = false
 
 //-----------------------------------------------------------------------------
 method getStylesForNode(/*int*/ nodeId, callback)
@@ -21,16 +22,30 @@
         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:   Weinre.cssStore.getComputedStyle(node),
+        computedStyle:   computedStyle,
         matchedCSSRules: Weinre.cssStore.getMatchedCSSRules(node),
         styleAttributes: Weinre.cssStore.getStyleAttributes(node),
         pseudoElements:  Weinre.cssStore.getPseudoElements(node),
         inherited:       []
     }
     
-    var parentNode   = node.parentNode
+    var parentNode = node.parentNode
     
     while (parentNode) {
         var parentStyle = {
@@ -102,14 +117,21 @@
 //-----------------------------------------------------------------------------
 method setPropertyText(/*any*/ styleId, /*int*/ propertyIndex, /*string*/ text, /*boolean*/ overwrite, callback)
     // callback: function(/*any*/ style)
-    Weinre.notImplemented(arguments.callee.signature)
-
-
+    
+    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)
-    // callback: function(/*any*/ style)
-    Weinre.notImplemented(arguments.callee.signature)
 
+    var result = Weinre.cssStore.toggleProperty(styleId, propertyIndex, disable)
+    
+    if (callback) {
+        Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
+    }
 
 //-----------------------------------------------------------------------------
 method setRuleSelector(/*any*/ ruleId, /*string*/ selector, callback)
diff --git a/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop b/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
index cd42790..b3d907f 100644
--- a/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
+++ b/weinre.web/modules/weinre/target/WiDOMStorageImpl.scoop
@@ -7,21 +7,164 @@
  */
 
 requireClass ../common/Weinre
+requireClass ../common/Native
 
 //-----------------------------------------------------------------------------
 class WiDOMStorageImpl
 
 //-----------------------------------------------------------------------------
 method getDOMStorageEntries(/*int*/ storageId, callback)
-    // callback: function(/*any[]*/ entries)
-    Weinre.notImplemented(arguments.callee.signature)
+    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)
-    // callback: function(/*boolean*/ success)
-    Weinre.notImplemented(arguments.callee.signature)
+    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)
-    // callback: function(/*boolean*/ success)
-    Weinre.notImplemented(arguments.callee.signature)
\ No newline at end of file
+    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)