Editor and base API changes

Project: http://git-wip-us.apache.org/repos/asf/incubator-wave/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-wave/commit/a768997c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-wave/tree/a768997c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-wave/diff/a768997c

Branch: refs/heads/swellrt
Commit: a768997c3943f25ed4c1b761ca6c5508d0eb1d2b
Parents: 8ebaa26
Author: Pablo Ojanguren <pablo...@gmail.com>
Authored: Wed Aug 31 00:27:34 2016 +0200
Committer: Pablo Ojanguren <pablo...@gmail.com>
Committed: Wed Aug 31 00:27:34 2016 +0200

----------------------------------------------------------------------
 .../java/org/swellrt/api/BrowserSession.java    |  10 +
 .../java/org/swellrt/api/ServiceCallback.java   |   5 +
 wave/src/main/java/org/swellrt/api/SwellRT.java | 271 +++++++-
 .../main/java/org/swellrt/api/SwellRTUtils.java |  15 +
 .../main/java/org/swellrt/api/WaveClient.java   |  39 +-
 .../java/org/swellrt/api/js/WaveClientJS.java   |  25 +
 .../org/swellrt/api/js/editor/TextEditorJS.java |  73 +-
 .../api/js/editor/TextEditorJSListener.java     |   4 +-
 .../org/swellrt/client/editor/TextEditor.java   | 664 +++++++++++--------
 .../client/editor/TextEditorListener.java       |   4 +-
 .../doodad/annotation/AnnotationHandler.java    |  39 +-
 .../annotation/jso/JsoAnnotationController.java |  24 +-
 .../client/doodad/annotation/jso/JsoRange.java  |  88 ++-
 .../wave/client/doodad/widget/WidgetDoodad.java |   9 +-
 .../doodad/widget/jso/JsoWidgetController.java  |  23 +-
 .../editor/content/misc/AnnotationPaint.java    |   2 +
 .../content/misc/AnnotationSpreadRenderer.java  |  28 +-
 .../editor/content/paragraph/Paragraph.java     |   2 +
 .../content/paragraph/ParagraphRenderer.java    |  23 +-
 .../editor/harness/DefaultTestHarness.java      |  11 +-
 20 files changed, 947 insertions(+), 412 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/BrowserSession.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/BrowserSession.java 
b/wave/src/main/java/org/swellrt/api/BrowserSession.java
index 1b492b9..9d215db 100644
--- a/wave/src/main/java/org/swellrt/api/BrowserSession.java
+++ b/wave/src/main/java/org/swellrt/api/BrowserSession.java
@@ -81,6 +81,10 @@ public class BrowserSession {
            return token;
        
        }-*/;
+
+       public static String getWindowSessionId() {
+         return getToken();
+       }
        
        
        public static native String getSessionId() /*-{
@@ -100,6 +104,12 @@ public class BrowserSession {
                return null;
        }-*/;
        
+       public static native String getUserAddress() /*-{
+         if ($wnd.__session && $wnd.__session['address'] != null)   
+      return $wnd.__session['address'];
+      
+    return null;
+       }-*/;
        
        /**
         * Define session data in a window object.

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/ServiceCallback.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/ServiceCallback.java 
b/wave/src/main/java/org/swellrt/api/ServiceCallback.java
index 5e08297..0bceb27 100644
--- a/wave/src/main/java/org/swellrt/api/ServiceCallback.java
+++ b/wave/src/main/java/org/swellrt/api/ServiceCallback.java
@@ -1,5 +1,7 @@
 package org.swellrt.api;
 
+import org.swellrt.api.js.generic.ModelJS;
+
 import com.google.gwt.core.client.JavaScriptObject;
 
 public class ServiceCallback extends JavaScriptObject {
@@ -23,6 +25,9 @@ public class ServiceCallback extends JavaScriptObject {
       return r;
     }-*/;
 
+    public static native JavaScriptResponse success(ModelJS cObject) /*-{
+      return cObject;
+    }-*/;
 
     public static native JavaScriptResponse error(String json) /*-{
       var r;

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/SwellRT.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/SwellRT.java 
b/wave/src/main/java/org/swellrt/api/SwellRT.java
index f7e9263..7d3a83e 100644
--- a/wave/src/main/java/org/swellrt/api/SwellRT.java
+++ b/wave/src/main/java/org/swellrt/api/SwellRT.java
@@ -7,6 +7,7 @@ import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.JsArrayString;
 import com.google.gwt.core.client.JsonUtils;
 import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.http.client.Request;
 import com.google.gwt.http.client.RequestBuilder;
 import com.google.gwt.http.client.RequestCallback;
@@ -17,7 +18,9 @@ import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.ui.RootPanel;
 
 import org.swellrt.api.ServiceCallback.JavaScriptResponse;
+import org.swellrt.api.js.generic.ModelJS;
 import org.swellrt.client.WaveLoader;
+import org.swellrt.client.editor.TextEditor;
 import org.swellrt.model.generic.Model;
 import org.swellrt.model.generic.TypeIdGenerator;
 import org.waveprotocol.box.stat.Timing;
@@ -25,6 +28,9 @@ import 
org.waveprotocol.box.webclient.client.ClientIdGenerator;
 import org.waveprotocol.box.webclient.client.RemoteViewServiceMultiplexer;
 import 
org.waveprotocol.box.webclient.client.WaveSocket.WaveSocketStartCallback;
 import org.waveprotocol.box.webclient.client.WaveWebSocketClient;
+import org.waveprotocol.wave.client.common.util.JsoView;
+import 
org.waveprotocol.wave.client.doodad.annotation.jso.JsoAnnotationController;
+import org.waveprotocol.wave.client.doodad.widget.jso.JsoWidgetController;
 import org.waveprotocol.wave.client.events.ClientEvents;
 import org.waveprotocol.wave.client.events.NetworkStatusEvent;
 import org.waveprotocol.wave.client.events.NetworkStatusEventHandler;
@@ -37,12 +43,14 @@ import org.waveprotocol.wave.model.schema.SchemaProvider;
 import org.waveprotocol.wave.model.schema.conversation.ConversationSchemas;
 import org.waveprotocol.wave.model.util.CollectionUtils;
 import org.waveprotocol.wave.model.util.Preconditions;
+import org.waveprotocol.wave.model.util.StringMap;
 import org.waveprotocol.wave.model.wave.ParticipantId;
 import org.waveprotocol.wave.model.waveref.WaveRef;
 
 import java.util.Collections;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -133,12 +141,20 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
 
 
   /** List of living waves for the active session. */
-  private Map<WaveId, WaveLoader> waveWrappers = CollectionUtils.newHashMap();;
+  private Map<WaveId, WaveLoader> waveRegistry = CollectionUtils.newHashMap();
+  
+  /** List of living collab objects with waves as substrate  */
+  private Map<WaveId, ModelJS> objectRegistry = CollectionUtils.newHashMap();
+  
+  /** List of editors created in the app */
+  private Map<Element, TextEditor> editorRegistry = 
CollectionUtils.newHashMap();
 
   /** A listener to global data/network/runtime events */
   private SwellRT.Listener listener = null;
 
   private boolean useWebSocket = true;
+  
+  private boolean shouldOpenWebsocket = true;
 
 
   /**
@@ -166,10 +182,10 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
 
   protected void cleanChannelData() {
     // Destroy all waves
-    for (Entry<WaveId, WaveLoader> entry : waveWrappers.entrySet())
+    for (Entry<WaveId, WaveLoader> entry : waveRegistry.entrySet())
       entry.getValue().destroy();
 
-    waveWrappers.clear();
+    waveRegistry.clear();
     websocket.disconnect(true);
     channel = null;
   }
@@ -192,8 +208,10 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
         else {
 
           // Clean everything before setting new session
-          cleanChannelData();
           cleanSessionData();
+          shouldOpenWebsocket = true;
+          if (websocket != null)
+            websocket.disconnect(true);
 
           JavaScriptResponse responseData =
               ServiceCallback.JavaScriptResponse.success(response.getText());
@@ -241,8 +259,9 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
         else {
 
           // Clean everything before setting new session
-          cleanChannelData();
-          cleanSessionData();
+          shouldOpenWebsocket = true;
+          if (websocket != null)
+            websocket.disconnect(true);
 
           JavaScriptResponse responseData =
               ServiceCallback.JavaScriptResponse.success(response.getText());
@@ -276,10 +295,34 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
 
   public void logout(final ServiceCallback callback) throws RequestException {
 
-    // Clean up session data
-    cleanChannelData();
+    //
+    // Clean session, websocket ,objects and registries
+    //
     cleanSessionData();
-
+   
+    shouldOpenWebsocket = true;
+    
+    for (ModelJS co: objectRegistry.values())
+       SwellRTUtils.deleteJsObject(co);
+    
+    objectRegistry.clear();
+    
+    for (WaveLoader wave: waveRegistry.values())
+      wave.destroy();
+    
+    waveRegistry.clear();
+    websocket.disconnect(false);
+    
+    for (TextEditor editor: editorRegistry.values())
+      editor.cleanUp();
+    
+    editorRegistry.clear();
+    
+    
+    //
+    // Call server to close remote session
+    // 
+    
     String url = baseServerUrl + "/swell/auth";
     url = BrowserSession.addSessionToUrl(url);
 
@@ -306,6 +349,183 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
     });
 
   }
+  
+  protected void openWebsocket(final Callback<Void, Void> callback) {
+    Preconditions.checkArgument(loggedInUser != null, "User not logged in. 
Can't open websocket.");
+    
+    // this is needed to atmosphere to work
+    setWebsocketAddress(baseServerUrl);
+
+    // Use Model.MODEL_VERSION to get the client version
+    websocket = new 
WaveWebSocketClient(SwellRTUtils.toWebsocketAddress(baseServerUrl), "1.0");
+    websocket.connect(new WaveSocketStartCallback() {
+
+      @Override
+      public void onSuccess() {
+        channel = new RemoteViewServiceMultiplexer(websocket, 
loggedInUser.getAddress());
+        shouldOpenWebsocket = false;
+        callback.onSuccess((Void) null);
+      }
+
+      @Override
+      public void onFailure() {
+        callback.onFailure((Void) null);
+
+      }
+    });    
+    
+  }
+  
+  private void openProc(WaveId waveId, final Callback<WaveLoader, String> 
callback) {
+    
+     final WaveLoader wave =
+          new WaveLoader(WaveRef.of(waveId), channel, TypeIdGenerator.get()
+              .getUnderlyingGenerator(), waveDomain, 
Collections.<ParticipantId> emptySet(),
+              loggedInUser, this);
+
+    if (wave.isLoaded()) {
+      callback.onSuccess(wave);
+    } else {
+      
+      try {
+              
+        wave.load(new Command() {
+          @Override
+          public void execute() {
+            callback.onSuccess(wave);
+          }
+        });
+  
+        } catch(RuntimeException e) {
+            callback.onFailure(e.getMessage());
+        }
+    }
+
+    
+  }
+  
+  /**
+   * Open or create a collaborative object. 
+   * The underlying websocket will be openend if it is necessary.
+   * 
+   * @param parameters field "id" for collab object id or void to create a new 
one 
+   * @param callback
+   * @throws RequestException
+   */
+  public void open(JavaScriptObject parameters, final ServiceCallback 
callback) throws RequestException {
+    
+    Preconditions.checkArgument(loggedInUser != null, "Login is not present");
+    
+    JsoView p = JsoView.as(parameters);
+   
+    WaveId id = null;
+    if (p.getString("id") != null) {
+      id = WaveId.deserialise(p.getString("id"));
+    } else {
+      id = TypeIdGenerator.get().newWaveId();
+    }
+    
+    final WaveId waveId = id;
+   
+    final Callback<WaveLoader, String> openProcCallback = new  
Callback<WaveLoader, String>() {
+
+      @Override
+      public void onFailure(String reason) {
+        
callback.onComplete(ServiceCallback.JavaScriptResponse.error("SERVICE_EXCEPTION",
 reason));
+      }
+
+      @Override
+      public void onSuccess(WaveLoader wave) {
+        
+        waveRegistry.put(waveId, wave);
+        
+        ModelJS cobJsFacade = null;
+
+        Model cob =
+          Model.create(wave.getWave().getWave(), wave.getLocalDomain(),
+                  wave.getLoggedInUser(),
+                wave.isNewWave(), wave.getIdGenerator());
+
+          cobJsFacade = ModelJS.create(cob);
+          cob.addListener(cobJsFacade);
+          
+          objectRegistry.put(waveId, cobJsFacade);
+          
+          
callback.onComplete(ServiceCallback.JavaScriptResponse.success(cobJsFacade));
+      }
+      
+    };
+    
+    
+    if (shouldOpenWebsocket) {
+      openWebsocket(new Callback<Void, Void>() {
+        
+        
+        @Override
+        public void onFailure(Void reason) {
+          
callback.onComplete(ServiceCallback.JavaScriptResponse.error("WEBSOCKET_ERROR", 
"Websocket can't be open"));
+        }
+
+        @Override
+        public void onSuccess(Void result) {
+          openProc(waveId, openProcCallback);          
+        }
+      });
+    } else {
+      openProc(waveId, openProcCallback);
+    }
+    
+  }
+  
+  private native String extractWaveIdParameter(JavaScriptObject parameters) 
/*-{
+  
+    if (parameters == null || parameters === undefined)
+      return null;
+  
+    if (typeof parameters == "string")
+      return parameters;
+      
+    if (parameters.id && typeof parameters.id == "function")
+       return parameters.id();
+    
+    if (parameters.id && typeof parameters.id == "string")
+       return parameters.id;
+       
+    return null;
+  }-*/;
+
+  
+  public void close(JavaScriptObject parameters, final ServiceCallback 
callback) throws RequestException {
+    String id = extractWaveIdParameter(parameters);
+    Preconditions.checkArgument(id != null, "Missing object or id");
+    WaveId waveId = WaveId.deserialise(id);
+    Preconditions.checkArgument(waveRegistry.containsKey(waveId), "Object is 
not opened");    
+    
+    for (TextEditor e: editorRegistry.values())
+      if (e.getWaveId().equals(waveId))
+        e.cleanUp();
+    
+    waveRegistry.remove(waveId).destroy();
+    ModelJS co = objectRegistry.remove(waveId);
+    SwellRTUtils.deleteJsObject(co);
+   
+  }
+
+  
+  public TextEditor createTextEditor(Element parent, 
StringMap<JsoWidgetController> widgetControllers, 
StringMap<JsoAnnotationController> annotationControllers) {
+            
+    TextEditor textEditor = TextEditor.create(parent,
+        widgetControllers,
+        annotationControllers);
+   
+    editorRegistry.put(parent, textEditor);
+    
+    return textEditor;
+  }
+  
+  //
+  // 
*******************************************************************************
+  //
 
   /**
    * Performs a login against Wave's /auth servlet. This method doesn't start a
@@ -424,7 +644,7 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
 
   }
 
-  public void stopComms() {
+  private void stopComms() {
     websocket.disconnect(true);
     channel = null;
   }
@@ -630,10 +850,10 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
     if (loggedInUser == null) throw new SessionNotStartedException();
 
     // Destroy all waves
-    for (Entry<WaveId, WaveLoader> entry : waveWrappers.entrySet())
+    for (Entry<WaveId, WaveLoader> entry : waveRegistry.entrySet())
       entry.getValue().destroy();
 
-    waveWrappers.clear();
+    waveRegistry.clear();
 
     // Disconnect from Wave's websocket
     stopComms();
@@ -646,15 +866,15 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
 
   protected WaveLoader getWaveWrapper(WaveId waveId, boolean isNew) {
 
-    if (!waveWrappers.containsKey(waveId) || 
waveWrappers.get(waveId).isClosed()) {
+    if (!waveRegistry.containsKey(waveId) || 
waveRegistry.get(waveId).isClosed()) {
       WaveLoader ww =
           new WaveLoader(WaveRef.of(waveId), channel, TypeIdGenerator.get()
               .getUnderlyingGenerator(), waveDomain, 
Collections.<ParticipantId> emptySet(),
               loggedInUser, this);
-      waveWrappers.put(waveId, ww);
+      waveRegistry.put(waveId, ww);
     }
 
-    return waveWrappers.get(waveId);
+    return waveRegistry.get(waveId);
   }
 
 
@@ -748,21 +968,23 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
       throw new InvalidIdException();
     }
 
-    WaveLoader waveWrapper = waveWrappers.get(waveId);
+    WaveLoader waveWrapper = waveRegistry.get(waveId);
 
     if (waveWrapper == null) throw new InvalidIdException();
 
     waveWrapper.destroy();
-    waveWrappers.remove(waveId);
+    waveRegistry.remove(waveId);
 
   }
 
-  protected WaveDocuments<? extends InteractiveDocument> 
getDocumentRegistry(Model model) {
-    Preconditions.checkArgument(model != null,
-        "Unable to get document registry from a null data model");
-    Preconditions.checkArgument(waveWrappers.containsKey(model.getWaveId()),
-        "Wave wrapper is not avaiable for the model");
-    return waveWrappers.get(model.getWaveId()).getDocumentRegistry();
+
+  protected WaveDocuments<? extends InteractiveDocument> 
getDocumentRegistry(WaveId waveId) {
+    Preconditions.checkArgument(waveId != null,
+        "Can't get document registry from null object id");
+    Preconditions.checkArgument(waveRegistry.containsKey(waveId),
+        "No object registered for the id. Can't get document registry.");
+
+    return waveRegistry.get(waveId).getDocumentRegistry();
   }
 
 
@@ -808,7 +1030,8 @@ public class SwellRT implements EntryPoint, 
UnsavedDataListener {
       @Override
       public void onNetworkStatus(NetworkStatusEvent event) {
 
-        if (listener == null) return;
+        // Don't fire events if user is not logged in
+        if (listener == null || loggedInUser == null) return;
 
         switch (event.getStatus()) {
           case CONNECTED:

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/SwellRTUtils.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/SwellRTUtils.java 
b/wave/src/main/java/org/swellrt/api/SwellRTUtils.java
index 11916f3..79d7654 100644
--- a/wave/src/main/java/org/swellrt/api/SwellRTUtils.java
+++ b/wave/src/main/java/org/swellrt/api/SwellRTUtils.java
@@ -224,4 +224,19 @@ public class SwellRTUtils {
     return encodeWaveRefUri(model.getWaveRef());
   }
 
+  public static String toWebsocketAddress(String httpAddress) {
+   
+    String websocketAddress = httpAddress + "/";
+
+    if (websocketAddress.startsWith("http://";))
+      websocketAddress = websocketAddress.replace("http://";, "ws://");
+    else if (websocketAddress.startsWith("https://";))
+      websocketAddress = websocketAddress.replace("https://";, "wss://");
+    
+    return websocketAddress;
+  }
+  
+  public static native void deleteJsObject(JavaScriptObject o) /*-{
+    delete o;
+  }-*/;
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/WaveClient.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/WaveClient.java 
b/wave/src/main/java/org/swellrt/api/WaveClient.java
index 504a708..eaeaf4e 100644
--- a/wave/src/main/java/org/swellrt/api/WaveClient.java
+++ b/wave/src/main/java/org/swellrt/api/WaveClient.java
@@ -8,6 +8,7 @@ import com.google.gwt.core.client.JsArray;
 import com.google.gwt.core.client.JsArrayString;
 import com.google.gwt.core.client.JsonUtils;
 import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.shared.UmbrellaException;
 import com.google.gwt.http.client.RequestException;
 import com.google.gwt.logging.impl.StackTracePrintStream;
@@ -26,6 +27,7 @@ import 
org.waveprotocol.wave.client.doodad.widget.jso.JsoWidgetController;
 import org.waveprotocol.wave.client.wave.InteractiveDocument;
 import org.waveprotocol.wave.client.wave.WaveDocuments;
 import 
org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener.UnsavedDataInfo;
+import org.waveprotocol.wave.model.id.WaveId;
 
 /**
  * SwellRT client API entrypoint
@@ -138,12 +140,12 @@ public class WaveClient implements SwellRT.Listener {
     coreClient.logout(onComplete);
   }
 
-  public void openChannel() {
-
+  public void open(JavaScriptObject parameters, ServiceCallback onComplete) 
throws RequestException {
+    coreClient.open(parameters, onComplete);
   }
 
-  public void closeChannel() {
-
+  public void close(JavaScriptObject parameters, ServiceCallback onComplete) 
throws RequestException {
+    coreClient.close(parameters, onComplete);
   }
 
 
@@ -386,26 +388,23 @@ public class WaveClient implements SwellRT.Listener {
 
 
   public TextEditorJS getTextEditor(String elementId, JavaScriptObject 
widgets, JavaScriptObject annotations) {
-    Preconditions.checkArgument(Document.get().getElementById(elementId) != 
null,
-        "Element id is not provided");
+    Element parent = Document.get().getElementById(elementId);
+    Preconditions.checkArgument(parent != null, "Can't hook editor in a null 
element");
+    
+    TextEditor textEditor = coreClient.createTextEditor(parent, 
JsoWidgetController.fromJso(widgets),
+        JsoAnnotationController.fromJso(annotations));
     
-    TextEditor textEditor = TextEditor.create(elementId,
-               JsoWidgetController.fromJso(widgets),
-               JsoAnnotationController.fromJso(annotations));
-    return TextEditorJS.create(textEditor, this);
+    TextEditorJS textEditorJS = TextEditorJS.create(textEditor, this);
+    textEditor.initialize(textEditorJS);
+    
+    return textEditorJS;
   }
 
   /**
-   * Set TextEditor dependencies from current wave/model. In particular, set 
the
-   * document registry associated with TextType's Model before editing.
-   *
-   * @param text
+   * Get the document registry of a object's sustrate wave
    */
-  public void configureTextEditor(TextEditor editor, TextType text) {
-    WaveDocuments<? extends InteractiveDocument> documentRegistry =
-        coreClient.getDocumentRegistry(text.getModel());
-
-    editor.setDocumentRegistry(documentRegistry);
+  public WaveDocuments<? extends InteractiveDocument> 
getDocumentRegistry(WaveId waveId) {
+    return coreClient.getDocumentRegistry(waveId);
   }
 
   /**
@@ -622,7 +621,7 @@ public class WaveClient implements SwellRT.Listener {
         GWT.log("Exception: " +  ((Throwable) e).getMessage());
     }
 
-    String exceptionCode = "UNWRAPPED_EXCEPTION";
+    String exceptionCode = "EXCEPTION";
 
     if (e instanceof InvalidIdException)
       exceptionCode = "INVALID_ID_EXCEPTION";

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/js/WaveClientJS.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/js/WaveClientJS.java 
b/wave/src/main/java/org/swellrt/api/js/WaveClientJS.java
index 6f5de51..c338901 100644
--- a/wave/src/main/java/org/swellrt/api/js/WaveClientJS.java
+++ b/wave/src/main/java/org/swellrt/api/js/WaveClientJS.java
@@ -238,6 +238,31 @@ public class WaveClientJS extends JavaScriptObject {
             }
 
          },
+         
+         //
+         // Manage Collaborative Objects
+         // 
+         
+         open: function(parameters, onComplete) {
+
+            try {
+              return 
delega...@org.swellrt.api.WaveClient::open(Lcom/google/gwt/core/client/JavaScriptObject;Lorg/swellrt/api/ServiceCallback;)(parameters,
 onComplete)
+            } catch (e) {
+              throw 
@org.swellrt.api.WaveClient::wrapJavaException(Ljava/lang/Object;)(e);
+            }
+           
+         },
+         
+         close: function(parameters, onComplete) {
+         
+            try {
+              return 
delega...@org.swellrt.api.WaveClient::close(Lcom/google/gwt/core/client/JavaScriptObject;Lorg/swellrt/api/ServiceCallback;)(parameters,
 onComplete)
+            } catch (e) {
+              throw 
@org.swellrt.api.WaveClient::wrapJavaException(Ljava/lang/Object;)(e);
+            }         
+            
+         },
+          
 
          startSession: function(url, user, password, onSuccess, onFailure) {
 

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJS.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJS.java 
b/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJS.java
index 318b5de..b4bafe9 100644
--- a/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJS.java
+++ b/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJS.java
@@ -4,6 +4,11 @@ import com.google.gwt.core.client.JavaScriptObject;
 
 import org.swellrt.api.WaveClient;
 import org.swellrt.client.editor.TextEditor;
+import org.swellrt.client.editor.TextEditor.Configurator;
+import org.swellrt.model.generic.TextType;
+import org.waveprotocol.wave.client.wave.InteractiveDocument;
+import org.waveprotocol.wave.client.wave.WaveDocuments;
+import org.waveprotocol.wave.model.id.WaveId;
 
 
 public class TextEditorJS extends JavaScriptObject {
@@ -18,12 +23,9 @@ public class TextEditorJS extends JavaScriptObject {
       },
 
       edit: function(text) {
-
         // TODO check for cleanUp();
-
         var _text = text.getDelegate();
-        
clie...@org.swellrt.api.WaveClient::configureTextEditor(Lorg/swellrt/client/editor/TextEditor;Lorg/swellrt/model/generic/TextType;)(delegate,
 _text);
-        
delegate.@org.swellrt.client.editor.TextEditor::edit(Lorg/swellrt/model/generic/TextType;)(_text);
+        
@org.swellrt.api.js.editor.TextEditorJS::edit(Lorg/swellrt/client/editor/TextEditor;Lorg/swellrt/model/generic/TextType;Lorg/swellrt/api/WaveClient;)(delegate,
 _text, client);
       },
 
       cleanUp: function() {
@@ -48,42 +50,45 @@ public class TextEditorJS extends JavaScriptObject {
        return 
delegate.@org.swellrt.client.editor.TextEditor::getWidget(Lcom/google/gwt/dom/client/Element;)(element);
       },
 
-      setAnnotation: function(name, value) {
-         
delegate.@org.swellrt.client.editor.TextEditor::setAnnotation(Ljava/lang/String;Ljava/lang/String;)(name,
 value);
+      setAnnotation: function(key, value) {
+         return 
delegate.@org.swellrt.client.editor.TextEditor::setAnnotation(Ljava/lang/String;Ljava/lang/String;)(key,
 value);
+      },
+      
+      setAnnotationInRange: function(range, key, value) {
+         return 
delegate.@org.swellrt.client.editor.TextEditor::setAnnotationInRange(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;Ljava/lang/String;Ljava/lang/String;)(range,
 key, value);
       },
 
       getSelection: function() {
         return 
delegate.@org.swellrt.client.editor.TextEditor::getSelection()();
       },
       
-      getAnnotationSet: function(name) {
-       return 
delegate.@org.swellrt.client.editor.TextEditor::getAnnotationSet(Ljava/lang/String;)(name);
+      getAnnotationSet: function(key) {
+       return 
delegate.@org.swellrt.client.editor.TextEditor::getAnnotationSet(Ljava/lang/String;)(key);
       },
       
-      getAnnotation: function(range, key) {
-       return 
delegate.@org.swellrt.client.editor.TextEditor::getAnnotation(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoEditorRange;Ljava/lang/String;)(range,
 key);      
+      getAnnotationInRange: function(range, key) {
+       return 
delegate.@org.swellrt.client.editor.TextEditor::getAnnotationInRange(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;Ljava/lang/String;)(range,
 key);      
       },
-
-      clearAnnotation: function(a, b) {
       
-        if (a && b && typeof a == 'string' && typeof b == 'object') {          
          
-                 
-                       // a -> annotation key
-                   // b -> editor range
-                   
delegate.@org.swellrt.client.editor.TextEditor::clearAnnotation(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoEditorRange;Ljava/lang/String;)(b,
 a);                         
-
-        } else if (a && typeof a == 'string') {
-
-                       // a -> annotation key
-                       
delegate.@org.swellrt.client.editor.TextEditor::clearAnnotation(Ljava/lang/String;)(a);
-                                               
-         } else if (a && typeof a == 'object') {
-               
-               // a -> editor range
-                       
delegate.@org.swellrt.client.editor.TextEditor::clearAnnotation(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoEditorRange;)(a);
                
-        }
+      clearAnnotation: function(keyPrefix) {
+        
delegate.@org.swellrt.client.editor.TextEditor::clearAnnotation(Ljava/lang/String;)(keyPrefix);
+      },
       
+      clearAnnotationInRange: function(range, keyPrefix) {
+        
delegate.@org.swellrt.client.editor.TextEditor::clearAnnotationInRange(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;Ljava/lang/String;)(range,
 keyPrefix);   
       },
+      
+      setText: function(range, text) {
+        return 
delegate.@org.swellrt.client.editor.TextEditor::setText(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;Ljava/lang/String;)(range,
 text); 
+      },
+      
+      getText: function(range) {
+        return 
delegate.@org.swellrt.client.editor.TextEditor::getText(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;)(range);
 
+      },
+      
+      deleteText: function(range) {
+         
delegate.@org.swellrt.client.editor.TextEditor::deleteText(Lorg/waveprotocol/wave/client/doodad/annotation/jso/JsoRange;)(range);
 
+      }
 
     }; 
 
@@ -95,5 +100,17 @@ public class TextEditorJS extends JavaScriptObject {
   protected TextEditorJS() {
 
   }
+  
+  private static void edit(TextEditor editor, TextType text, WaveClient 
client) {
+    final WaveId waveId = text.getModel().getWaveId();
+    editor.edit(text, new Configurator() {
+
+      @Override
+      public WaveDocuments<? extends InteractiveDocument> 
getDocumentRegistry() {
+         return client.getDocumentRegistry(waveId);
+      }
+      
+    });
+  }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJSListener.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJSListener.java 
b/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJSListener.java
index abd05b1..45f8d15 100644
--- a/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJSListener.java
+++ b/wave/src/main/java/org/swellrt/api/js/editor/TextEditorJSListener.java
@@ -3,7 +3,7 @@ package org.swellrt.api.js.editor;
 import com.google.gwt.core.client.JavaScriptObject;
 
 import org.swellrt.client.editor.TextEditorListener;
-import org.waveprotocol.wave.client.doodad.annotation.jso.JsoEditorRange;
+import org.waveprotocol.wave.client.doodad.annotation.jso.JsoRange;
 import org.waveprotocol.wave.client.gadget.StateMap;
 
 public class TextEditorJSListener extends JavaScriptObject implements 
TextEditorListener {
@@ -17,7 +17,7 @@ public class TextEditorJSListener extends JavaScriptObject 
implements TextEditor
   }-*/;
 
        @Override
-       public final native void onSelectionChange(JsoEditorRange editorRange)  
/*-{
+       public final native void onSelectionChange(JsoRange editorRange)  /*-{
           if (this.executor !== undefined)
                this.executor(editorRange);
        }-*/;

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/client/editor/TextEditor.java
----------------------------------------------------------------------
diff --git a/wave/src/main/java/org/swellrt/client/editor/TextEditor.java 
b/wave/src/main/java/org/swellrt/client/editor/TextEditor.java
index 017318f..1943eeb 100644
--- a/wave/src/main/java/org/swellrt/client/editor/TextEditor.java
+++ b/wave/src/main/java/org/swellrt/client/editor/TextEditor.java
@@ -6,22 +6,25 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.swellrt.api.BrowserSession;
 import org.swellrt.client.editor.TextEditorDefinitions.ParagraphAnnotation;
 import org.swellrt.model.generic.TextType;
 import org.swellrt.model.shared.ModelUtils;
+import org.waveprotocol.wave.client.account.impl.ProfileManagerImpl;
 import org.waveprotocol.wave.client.common.util.JsoStringMap;
 import org.waveprotocol.wave.client.common.util.JsoStringSet;
-import org.waveprotocol.wave.client.common.util.JsoView;
 import org.waveprotocol.wave.client.common.util.LogicalPanel;
 import org.waveprotocol.wave.client.doodad.annotation.AnnotationHandler;
 import org.waveprotocol.wave.client.doodad.annotation.jso.JsoAnnotation;
 import 
org.waveprotocol.wave.client.doodad.annotation.jso.JsoAnnotationController;
-import org.waveprotocol.wave.client.doodad.annotation.jso.JsoEditorRange;
+import org.waveprotocol.wave.client.doodad.annotation.jso.JsoRange;
 import 
org.waveprotocol.wave.client.doodad.annotation.jso.JsoParagraphAnnotation;
 import org.waveprotocol.wave.client.doodad.diff.DiffAnnotationHandler;
 import org.waveprotocol.wave.client.doodad.diff.DiffDeleteRenderer;
 import org.waveprotocol.wave.client.doodad.link.LinkAnnotationHandler;
 import 
org.waveprotocol.wave.client.doodad.link.LinkAnnotationHandler.LinkAttributeAugmenter;
+import 
org.waveprotocol.wave.client.doodad.selection.SelectionAnnotationHandler;
+import org.waveprotocol.wave.client.doodad.selection.SelectionExtractor;
 import org.waveprotocol.wave.client.doodad.widget.WidgetDoodad;
 import org.waveprotocol.wave.client.doodad.widget.jso.JsoWidget;
 import org.waveprotocol.wave.client.doodad.widget.jso.JsoWidgetController;
@@ -40,13 +43,14 @@ import 
org.waveprotocol.wave.client.editor.content.misc.AnnotationPaint;
 import 
org.waveprotocol.wave.client.editor.content.misc.AnnotationPaint.EventHandler;
 import 
org.waveprotocol.wave.client.editor.content.misc.AnnotationPaint.MutationHandler;
 import org.waveprotocol.wave.client.editor.content.misc.StyleAnnotationHandler;
-import org.waveprotocol.wave.client.editor.content.paragraph.Line;
 import org.waveprotocol.wave.client.editor.content.paragraph.LineRendering;
 import org.waveprotocol.wave.client.editor.content.paragraph.Paragraph;
 import 
org.waveprotocol.wave.client.editor.content.paragraph.Paragraph.LineStyle;
 import 
org.waveprotocol.wave.client.editor.content.paragraph.ParagraphBehaviour;
 import org.waveprotocol.wave.client.editor.keys.KeyBindingRegistry;
 import org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil;
+import org.waveprotocol.wave.client.scheduler.SchedulerInstance;
+import org.waveprotocol.wave.client.scheduler.TimerService;
 import org.waveprotocol.wave.client.wave.InteractiveDocument;
 import org.waveprotocol.wave.client.wave.RegistriesHolder;
 import org.waveprotocol.wave.client.wave.WaveDocuments;
@@ -62,15 +66,17 @@ import org.waveprotocol.wave.model.document.util.DocHelper;
 import org.waveprotocol.wave.model.document.util.LineContainers;
 import org.waveprotocol.wave.model.document.util.Point;
 import org.waveprotocol.wave.model.document.util.Range;
+import org.waveprotocol.wave.model.id.WaveId;
+import org.waveprotocol.wave.model.util.CollectionUtils;
 import org.waveprotocol.wave.model.util.Preconditions;
 import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
 import org.waveprotocol.wave.model.util.ReadableStringSet.Proc;
 import org.waveprotocol.wave.model.util.StringMap;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Node;
 import com.google.gwt.user.client.Event;
 
 /**
@@ -82,6 +88,15 @@ import com.google.gwt.user.client.Event;
  */
 public class TextEditor implements EditorUpdateListener {
 
+  public interface Configurator {
+    
+    /**
+     * The gateway to get UI-versions of Blips. Registry is GWT related, so 
must
+     * be injected in the Editor by the JS API. Model's classes have to ignore 
it.
+     */
+    public WaveDocuments<? extends InteractiveDocument> getDocumentRegistry();
+  
+  }
 
   public static class JSLogSink extends LogSink {
 
@@ -104,7 +119,7 @@ public class TextEditor implements EditorUpdateListener {
 
   }
 
-
+  
   public static class CustomLogger extends AbstractLogger {
 
     public CustomLogger(LogSink sink) {
@@ -151,17 +166,18 @@ public class TextEditor implements EditorUpdateListener {
   private LogicalPanel.Impl docPanel;
   private ContentDocument doc;
 
-
-  /**
-   * The gateway to get UI-versions of Blips. Registry is GWT related, so must
-   * be injected in the Editor by the JS API. Model's classes have to ignore 
it.
-   */
-  private WaveDocuments<? extends InteractiveDocument> documentRegistry;
-
+  private SelectionExtractor selectionExtractor;
+      
+  /** The actual implementation of the editor */
   private Editor editor;
 
+  /** Listener for editor events */
   private TextEditorListener listener;
+  
+  private WaveId containerWaveId;
 
+  private boolean shouldFireEvents = false;
+  
   /**
    * Registry of JavaScript controllers for widgets
    */
@@ -183,14 +199,12 @@ public class TextEditor implements EditorUpdateListener {
 
   }
 
-  public static TextEditor create(String containerElementId, 
StringMap<JsoWidgetController> widgetControllers, 
StringMap<JsoAnnotationController> annotationControllers) {
-    Element e = Document.get().getElementById(containerElementId);
-    Preconditions.checkNotNull(e, "Editor's parent element doesn't exist");
-    
-    TextEditor editor = new TextEditor(e, widgetControllers, 
annotationControllers);
-    editor.registerDoodads();
+  public static TextEditor create(Element parent, 
StringMap<JsoWidgetController> widgetControllers, 
StringMap<JsoAnnotationController> annotationControllers) {
+    Preconditions.checkNotNull(parent, "Element to hook editor canvas doesn't 
exist");    
+    TextEditor editor = new TextEditor(parent, widgetControllers, 
annotationControllers);
     return editor;
   }
+  
 
   protected TextEditor(final Element containerElement, 
StringMap<JsoWidgetController> widgetControllers, 
StringMap<JsoAnnotationController> annotationControllers) {
     this.editorPanel = new LogicalPanel.Impl() {
@@ -202,26 +216,223 @@ public class TextEditor implements EditorUpdateListener {
     this.annotationRegistry = annotationControllers;
   }
 
-
-
   /**
-   * Inject document registry which manages UI versions of blips. Registry must
-   * be only injected by the JS API.
-   *
-   * @param documentRegistry
+   * This is a nasty method to pass a reference of editor's js facade 
+   * to widget and annotation controllers.
+   * 
+   * So there is a circular reference between TextEditor and TextEditorJS.
+   * 
+   * @param editorJsFacade editor's pure JavaScript facade
    */
-  public void setDocumentRegistry(WaveDocuments<? extends InteractiveDocument> 
documentRegistry) {
-    this.documentRegistry = documentRegistry;
+  public void initialize(JavaScriptObject editorJsFacade) {
+      registerDoodads(editorJsFacade);
+      registerAnnotations(editorJsFacade);
+  }
+
+  
+  protected void registerDoodads(final JavaScriptObject editorJsFacade) {
+
+
+    // TOPLEVEL_CONTAINER_TAGNAME
+    LineRendering.registerContainer(TOPLEVEL_CONTAINER_TAGNAME,
+        registries.getElementHandlerRegistry());
+
+    StyleAnnotationHandler.register(registries);
+
+    // Listen for Diff annotations to paint new content or to insert a
+    // delete-content tag
+    // to be rendered by the DiffDeleteRendere
+    DiffAnnotationHandler.register(registries.getAnnotationHandlerRegistry(),
+        registries.getPaintRegistry());
+    DiffDeleteRenderer.register(registries.getElementHandlerRegistry());
+        
+    //
+    // Reuse existing link annotation handler, but also support external
+    // controller to get notified on mutation or input events
+    //
+    LinkAnnotationHandler.register(registries, new LinkAttributeAugmenter() {
+      @Override
+      public Map<String, String> augment(Map<String, Object> annotations, 
boolean isEditing,
+          Map<String, String> current) {
+        return current;
+      }
+    });
+    
+    // Set reference to js editor's facade to controllers.
+    widgetRegistry.each(new ProcV<JsoWidgetController>() {
+
+      @Override
+      public void apply(String key, JsoWidgetController value) {
+        value.setEditorJsFacade(editorJsFacade);
+      }
+      
+    });
+
+    WidgetDoodad.register(registries.getElementHandlerRegistry(), 
widgetRegistry);
+
+    
   }
 
+ 
+  protected void registerAnnotations(final JavaScriptObject editorJsFacade) {
+
+    // Configure annotations.
+    // For shake of encapsulation, we don't expose native JS type
+    // JsoAnntationController to inner editor classes.
+   
+    annotationRegistry.each(new ProcV<JsoAnnotationController>() {
+
+      @Override
+      public void apply(String key, JsoAnnotationController controller) {
+
+        // sets the circular reference to the editor's js facade
+        controller.setEditorJsFacade(editorJsFacade);
+        
+        if (key.contains(AnnotationConstants.LINK_PREFIX)) {
+          
+          // Link annotations are actually registered in registerDoodads() 
method
+          // here only handlers are registered 
+
+          
AnnotationPaint.registerEventHandler(AnnotationConstants.LINK_PREFIX, new 
EventHandler() {
+
+            @Override
+            public void onEvent(ContentElement node, Event event) {
+              if (controller != null && shouldFireEvents)
+                
controller.onEvent(JsoRange.Builder.create(node.getMutableDoc()).range(node)
+                    .annotation(AnnotationConstants.LINK_PREFIX, 
null).build(), event);
+            }
+          });
+
+          AnnotationPaint.setMutationHandler(AnnotationConstants.LINK_PREFIX,
+              new MutationHandler() {
+
+                @Override
+                public void onMutation(ContentElement node) {
+                  if (controller != null  && shouldFireEvents)
+                    
controller.onChange(JsoRange.Builder.create(node.getMutableDoc())
+                        
.range(node).annotation(AnnotationConstants.LINK_PREFIX, null).build());
+                }
+                
+                @Override
+                public void onAdded(ContentElement node) {
+                  if (controller != null  && shouldFireEvents)
+                    
controller.onAdd(JsoRange.Builder.create(node.getMutableDoc())
+                        
.range(node).annotation(AnnotationConstants.LINK_PREFIX, null).build());        
+                }
+
+                @Override
+                public void onRemoved(ContentElement node) {
+                  if (controller != null  && shouldFireEvents)
+                    
controller.onRemove(JsoRange.Builder.create(node.getMutableDoc())
+                        
.range(node).annotation(AnnotationConstants.LINK_PREFIX, null).build());        
+                }
+                
+              });
+
+        } else if (TextEditorDefinitions.isParagraphAnnotation(key)) {
+
+          if (key.equals(ParagraphAnnotation.HEADER.toString())) {
+
+            Paragraph.registerEventHandler(ParagraphBehaviour.HEADING,
+                new Paragraph.EventHandler() {
+
+                  @Override
+                  public void onEvent(ContentElement node, Event event) {
+                    if (controller != null  && shouldFireEvents)
+                      controller.onEvent(
+                          
JsoRange.Builder.create(node.getMutableDoc()).range(node)
+                              .annotation(key, 
node.getAttribute(Paragraph.SUBTYPE_ATTR)).build(),
+                          event);
+                  }
+                });
+
+            Paragraph.registerMutationHandler(ParagraphBehaviour.HEADING,
+                new Paragraph.MutationHandler() {
+
+                  @Override
+                  public void onMutation(ContentElement node) {
+                    if (controller != null && shouldFireEvents)
+                      controller
+                          
.onChange(JsoRange.Builder.create(node.getMutableDoc()).range(node)
+                              .annotation(key, 
node.getAttribute(Paragraph.SUBTYPE_ATTR)).build());
+                  }
+
+                  @Override
+                  public void onAdded(ContentElement node) {
+                    if (controller != null && shouldFireEvents)
+                      controller
+                          
.onAdd(JsoRange.Builder.create(node.getMutableDoc()).range(node)
+                              .annotation(key, 
node.getAttribute(Paragraph.SUBTYPE_ATTR)).build());                   
+                  }
+
+                  @Override
+                  public void onRemoved(ContentElement node) {
+                    if (controller != null && shouldFireEvents)
+                      controller
+                          
.onRemove(JsoRange.Builder.create(node.getMutableDoc()).range(node)
+                              .annotation(key, 
node.getAttribute(Paragraph.SUBTYPE_ATTR)).build());     
+                    
+                  }
+                  
+                  
+                  
+                  
+                });
+
+          }
+
+        } else if (TextEditorDefinitions.isStyleAnnotation(key)) {
 
-  public void edit(TextType text) {
-    Preconditions.checkNotNull(text, "Text object is null");
-    Preconditions.checkNotNull(documentRegistry, "Document registry hasn't 
been initialized");
+          // Nothing to do with style annotations
+
+        } else {
+
+          // Register custom annotations
+          AnnotationHandler.register(registries, key, controller, new 
AnnotationHandler.Activator() {            
+            @Override
+            public boolean shouldFireEvent() {
+              return shouldFireEvents;
+            }
+          });
+
+        }
+
+      }
+
+    });
+
+  }
+  
+  
+  public void setListener(TextEditorListener listener) {
+    this.listener = listener;
+  }
+  
+  public WaveId getWaveId() {
+    return containerWaveId;
+  }
+  
+
+  /**
+   * Start an editing session.
+   * 
+   * @param text the TextType instance to edit
+   * @param configurator editor dependencies not related with text
+   */
+  public void edit(TextType text, Configurator configurator) {
+    Preconditions.checkNotNull(text, "Text object can't be null");
+    Preconditions.checkNotNull(configurator, "Editor configurator can't be 
null");
+    
+    shouldFireEvents = false;
 
     if (!isClean()) cleanUp();
 
-    doc = getContentDocument(text);
+    // Place here selection extractor to ensure session id and user id are 
refreshed
+    SelectionAnnotationHandler.register(registries, 
BrowserSession.getWindowSessionId(), new ProfileManagerImpl());
+    TimerService clock = SchedulerInstance.getLowPriorityTimer();
+    selectionExtractor = new SelectionExtractor(clock, 
BrowserSession.getUserAddress(), BrowserSession.getWindowSessionId());
+    
+    doc = getContentDocument(text, configurator.getDocumentRegistry());
     Preconditions.checkArgument(doc != null, "Can't edit an unattached 
TextType");
 
     doc.setRegistries(registries);
@@ -257,22 +468,22 @@ public class TextEditor implements EditorUpdateListener {
 
     editor.setEditing(true);
     editor.focus(true);
-
+    
+    selectionExtractor.start(editor);
+    
+    containerWaveId = text.getModel().getWaveId();
+    
+    shouldFireEvents = true;
   }
 
 
 
-  /* ---------------------------------------------------------- */
-
-
-
-  private ContentDocument getContentDocument(TextType text) {
+  private ContentDocument getContentDocument(TextType text, WaveDocuments<? 
extends InteractiveDocument> documentRegistry) {
     Preconditions.checkArgument(text != null,
         "Unable to get ContentDocument from null TextType");
     Preconditions.checkArgument(documentRegistry != null,
         "Unable to get ContentDocument from null DocumentRegistry");
 
-
     return 
documentRegistry.getBlipDocument(ModelUtils.serialize(text.getModel().getWaveletId()),
         text.getDocumentId()).getDocument();
   }
@@ -286,10 +497,13 @@ public class TextEditor implements EditorUpdateListener {
 
   public void cleanUp() {
     if (editor != null) {
+      if (selectionExtractor != null)
+        selectionExtractor.start(editor);
       editor.removeUpdateListener(this);
       editor.removeContentAndUnrender();
       editor.reset();
       doc = null;
+      containerWaveId = null;
     }
   }
 
@@ -319,125 +533,16 @@ public class TextEditor implements EditorUpdateListener {
     return JsoWidget.create(w.getImplNodelet(), w);
   }
 
-  
-  
   /**
    * Get the widget associated with the DOM element
    * 
    * @param domElement the widget element or a descendant
    */
   public JsoWidget getWidget(Element domElement) {
-        return WidgetDoodad.getWidget(editor.getDocument(), domElement);       
 
-  }
-
-  protected void registerDoodads() {
-
-
-    // TOPLEVEL_CONTAINER_TAGNAME
-    LineRendering.registerContainer(TOPLEVEL_CONTAINER_TAGNAME,
-        registries.getElementHandlerRegistry());
-
-    StyleAnnotationHandler.register(registries);
-
-    // Listen for Diff annotations to paint new content or to insert a
-    // delete-content tag
-    // to be rendered by the DiffDeleteRendere
-    DiffAnnotationHandler.register(registries.getAnnotationHandlerRegistry(),
-        registries.getPaintRegistry());
-    DiffDeleteRenderer.register(registries.getElementHandlerRegistry());
-
-    //
-    // Reuse existing link annotation handler, but also support external
-    // controller to
-    // get notified on mutation or input events
-    //
-    LinkAnnotationHandler.register(registries, new LinkAttributeAugmenter() {
-      @Override
-      public Map<String, String> augment(Map<String, Object> annotations, 
boolean isEditing,
-          Map<String, String> current) {
-        return current;
-      }
-    });
-
-    WidgetDoodad.register(registries.getElementHandlerRegistry(), 
widgetRegistry);
-
-    
-    // Configure annotations. 
-    // For shake of encapsulation, we don't expose native JS type 
JsoAnntationController to inner editor classes.
-    
-   annotationRegistry.each(new ProcV<JsoAnnotationController>() {
-
-    @Override
-    public void apply(String key, JsoAnnotationController controller) {
-      
-      
-      if (key.contains(AnnotationConstants.LINK_PREFIX)) {
-        
-        AnnotationPaint.registerEventHandler(AnnotationConstants.LINK_PREFIX, 
new EventHandler() {
-
-          @Override
-          public void onEvent(ContentElement node, Event event) {
-            if (controller != null)
-              
controller.onEvent(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(AnnotationConstants.LINK_PREFIX,
 null).build(), event);
-          }
-        });
-
-        AnnotationPaint.setMutationHandler(AnnotationConstants.LINK_PREFIX, 
new MutationHandler() {
-
-          @Override
-          public void onMutation(ContentElement node) {
-            if (controller != null)
-              
controller.onChange(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(AnnotationConstants.LINK_PREFIX,
 null).build());
-          }
-        });
-        
-      } else if (TextEditorDefinitions.isParagraphAnnotation(key)) {
-      
-          if (key.equals(ParagraphAnnotation.HEADER.toString())) {
-            
-            Paragraph.registerEventHandler(ParagraphBehaviour.HEADING, new 
Paragraph.EventHandler() {
-              
-              @Override
-              public void onEvent(ContentElement node, Event event) {
-                if (controller != null)
-                  
controller.onEvent(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(Paragraph.SUBTYPE_ATTR)).build(), event);
-              }
-            });
-            
-            Paragraph.registerMutationHandler(ParagraphBehaviour.HEADING, new 
Paragraph.MutationHandler() {
-
-              @Override
-              public void onMutation(ContentElement node) {
-                if (controller != null)                  
-                  
controller.onChange(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(Paragraph.SUBTYPE_ATTR)).build());                
-              }              
-            });
-            
-          }
-        
-      } else if (TextEditorDefinitions.isStyleAnnotation(key)) {
-        
-        // Nothing to do with style annotations
-        
-      } else {
-            
-        // Register custom annotations        
-        AnnotationHandler.register(registries, key, controller);  
-        
-      }
-      
-    }
-     
-   });
-    
-    
+   return WidgetDoodad.getWidget(editor.getDocument(), domElement);  
   }
-
-  
   
-  public void setListener(TextEditorListener listener) {
-    this.listener = listener;
-  }
+
 
   protected boolean isValidAnnotationKey(String key) {
                return (TextEditorDefinitions.isParagraphAnnotation(key) || 
@@ -454,7 +559,7 @@ public class TextEditor implements EditorUpdateListener {
    *
    * @return a native JS object having a property for each annotation.
    */
-  protected JsoStringMap<String> getAnnotationsOverRange(final Range range) {
+  protected JsoStringMap<String> getAllAnnotationsInRange(final Range range) {
          
          
          // Map to contain the current state of each annotation
@@ -611,50 +716,37 @@ public class TextEditor implements EditorUpdateListener {
                return ranges;
   }
   
-  
-  @Override
-  public void onUpdate(final EditorUpdateEvent event) {
-    if (event.selectionLocationChanged()) {
-        Range range = editor.getSelectionHelper().getOrderedSelectionRange();
-        JsoEditorRange.Builder editorRangeBuilder = 
JsoEditorRange.Builder.create(editor.getDocument());
-        if (range != null) {
-               editorRangeBuilder.range(range)
-                                       
.annotations(getAnnotationsOverRange(range)).build();
-        } else {
-               editorRangeBuilder.annotations(getAnnotationsByDefault());
+    
+  public JsoAnnotation getAnnotationInRange(JsoRange editorRange, String key) {
+    Preconditions.checkNotNull(editorRange, "Range can't be null");
+    Preconditions.checkArgument(isValidAnnotationKey(key), "Invalid annotation 
key");
+    
+    if (TextEditorDefinitions.isParagraphAnnotation(key)) {
+      
+      for(Entry<String, LineStyle> ls: 
ParagraphAnnotation.fromString(key).getLineStyles().entrySet()) {
+      
+        if (Paragraph.appliesEntirely(editor.getDocument(), 
editorRange.start(), editorRange.end(), ls.getValue())) {
+        Point<ContentNode> point = 
editor.getDocument().locate(editorRange.start());
+          ContentNode lineNode = 
LineContainers.getRelatedLineElement(editor.getDocument(), point);              
    
+          return JsoParagraphAnnotation.create(editor.getDocument(), 
editorRange.start(), editorRange.end(), key, ls.getKey(), 
lineNode.asElement().getImplNodelet());
         }
         
-        
-               if (listener != null)
-                       listener.onSelectionChange(editorRangeBuilder.build());
-        
+      }
+      
+    } else {
+      
+      Range range = 
EditorAnnotationUtil.getEncompassingAnnotationRange(editor.getDocument(), key, 
editorRange.start());  
+      if (range == null)
+        return null;
+      
+      return JsoAnnotation.create(editor, range, key);
     }
-  }
+    
+  return null;
+    
+    
+  }  
   
-  protected void getParagraphAnnotations(int start, int end, 
JsoStringMap<String> annotations) {
-      
-         TextEditorDefinitions.PARAGRAPH_ANNOTATIONS.each(new Proc() {
-
-          @Override
-          public void apply(String annotationName) {
-
-            Collection<Entry<String, LineStyle>> styles =
-                
TextEditorDefinitions.ParagraphAnnotation.fromString(annotationName).values
-                    .entrySet();
-
-            String annotationValue = null;
-            for (Entry<String, LineStyle> s : styles) {
-              if (Paragraph.appliesEntirely(editor.getDocument(), start, end,
-                  s.getValue())) {
-                annotationValue = s.getKey();
-                break;
-              }
-            }
-            annotations.put(annotationName, annotationValue); 
-          }
-        });
-         
-  }
   
   
   /**
@@ -689,19 +781,20 @@ public class TextEditor implements EditorUpdateListener {
    * @param key
    * @param value
    */
-       public void setAnnotation(String key, String value) {
+       public JsoAnnotation setAnnotation(String key, String value) {
        
                Preconditions.checkArgument(isValidAnnotationKey(key), "Unknown 
annotation key");
-       
+               final Range range = 
editor.getSelectionHelper().getOrderedSelectionRange();
+               if (range == null) return null;
+               
                if (TextEditorDefinitions.isParagraphAnnotation(key)) {
-                       final Range range = 
editor.getSelectionHelper().getOrderedSelectionRange();
-                       if (range != null) {
-                               setParagraphAnnotation(key, value, 
range.getStart(), range.getEnd());
-                       }
+                       setParagraphAnnotation(key, value, range.getStart(), 
range.getEnd());
                } else {
                        EditorAnnotationUtil.setAnnotationOverSelection(editor, 
key, value);
                }
-       
+               
+               
+               return JsoAnnotation.create(editor, range, key);
        }
        
        /**
@@ -711,7 +804,7 @@ public class TextEditor implements EditorUpdateListener {
         * @param key
         * @param value
         */
-       public void setAnnotationOverRange(JsoEditorRange range, String key, 
String value) {    
+       public JsoAnnotation setAnnotationInRange(JsoRange range, String key, 
String value) {   
                Preconditions.checkArgument(isValidAnnotationKey(key), "Unknown 
annotation key");
                Preconditions.checkArgument(range != null && range.start() <= 
range.end(), "Invalid range object");
                
@@ -721,110 +814,141 @@ public class TextEditor implements EditorUpdateListener 
{
                        
EditorAnnotationUtil.setAnnotationOverRange(editor.getDocument(), 
editor.getCaretAnnotations(), key, value, range.start(), range.end());
                }               
                
+               return JsoAnnotation.create(editor, new Range(range.start(), 
range.end()), key);
        }
   
   /**
    * Clear the annotation in the current selection or caret position.
    * 
-   * @param annotationName
+   * @param keyPrefix
    */
-  public void clearAnnotation(String annotationName) {
-         Preconditions.checkNotNull(annotationName, "Annotation key can't be 
null");
-         
-         if (TextEditorDefinitions.isParagraphAnnotation(annotationName)) {
-                 final Range range = 
editor.getSelectionHelper().getOrderedSelectionRange();
-         setParagraphAnnotation(annotationName, null, range.getStart(), 
range.getEnd());
-      } else {
-         EditorAnnotationUtil.clearAnnotationsOverSelection(editor, 
annotationName);
-      }
+  public void clearAnnotation(String keyPrefix) {      
+    Range r = editor.getSelectionHelper().getOrderedSelectionRange();
+    if (r == null) {
+      return;
+    }   
+    
clearAnnotationInRange(JsoRange.Builder.create(editor.getDocument()).range(r).build(),
 keyPrefix);
   }  
   
+  
   /**
-   * Clear an annotation in the caret or document range. 
+   * Clear all annotations in the range starting with the prefix
    * 
-   * @param editorRange the range in the doc
-   * @param key annotation key
+   * @param range the range in the doc
    */
-  public void clearAnnotation(JsoEditorRange editorRange, String key) {
-         Preconditions.checkNotNull(editorRange, "Range can't be null");
-         Preconditions.checkArgument(isValidAnnotationKey(key), "Invalid 
annotation key");
+  public void clearAnnotationInRange(JsoRange range, String keyPrefix) {
+    Preconditions.checkNotNull(keyPrefix, "Annotation key or prefix can't be 
null");    
+         Preconditions.checkNotNull(range, "Range can't be null");
+               
+         StringMap<String> annotations = range.getAnnotations();
+         if (annotations.isEmpty()) {
+           annotations = getAllAnnotationsInRange(new Range(range.start(), 
range.end()));
+         }
+                 
+         List<String> textAnnotations = new ArrayList<String>();
          
-         if (TextEditorDefinitions.isParagraphAnnotation(key)) {               
  
-         setParagraphAnnotation(key, null, editorRange.start(), 
editorRange.end());
-      } else {
-         EditorAnnotationUtil.clearAnnotationsOverRange(editor.getDocument(), 
editor.getCaretAnnotations(), new String[] { key }, editorRange.start(), 
editorRange.end());
+         annotations.each(new ProcV<String>() {
+
+      @Override
+      public void apply(String key, String value) {
+        if (key.startsWith(keyPrefix)) {
+          if (TextEditorDefinitions.isParagraphAnnotation(key))
+            setParagraphAnnotation(key, null, range.start(), range.end());
+          else
+            textAnnotations.add(key);
+        }
       }
-         
+           
+         });
+
+                 
+         if (!textAnnotations.isEmpty()) {
+           String[] anotArray = (String[]) textAnnotations.toArray(new 
String[]{});
+                 
EditorAnnotationUtil.clearAnnotationsOverRange(editor.getDocument(), 
editor.getCaretAnnotations(), anotArray, range.start(), range.end());
+         }
+  }
+
+  /**
+   * Insert o replace text over range in the document
+   *  
+   * @param range
+   * @param text
+   */
+  public JsoRange setText(JsoRange range, String text) {
+    Preconditions.checkNotNull(range, "Range can't be null");
+    CMutableDocument doc = editor.getDocument();
+    if (range.start() == range.end()) {
+      doc.insertText(range.start(), text);
+    } else if (range.start() > 0 && range.start() < range.end() && range.end() 
< editor.getDocument().size()) {
+      doc.beginMutationGroup();
+      doc.deleteRange(range.start(), range.end());
+      doc.insertText(range.start(), text);
+      doc.endMutationGroup();
+    } else {
+      Preconditions.checkArgument(false, "Range is not correct");
+    }
+    
+    
+    return JsoRange.Builder.create(doc).range(range.start(), 
range.start()+text.length(), text.length()).build();
+    
   }
   
   /**
-   * Clear all annotations in the range
+   * Delete text in a range
    * 
-   * @param editorRange the range in the doc
+   * @param range
+   * @return
    */
-  public void clearAnnotation(JsoEditorRange editorRange) {
-         Preconditions.checkNotNull(editorRange, "Range can't be null");
-         
-         // Separate paragraph annotations
-         List<String> textAnnotations = new ArrayList<String>();
-         for (String s: editorRange.getAnnotationKeys()) {
-                 if (TextEditorDefinitions.isParagraphAnnotation(s))
-                         setParagraphAnnotation(s, null, editorRange.start(), 
editorRange.end());
-                 else
-                         textAnnotations.add(s);
-         }
-                 
-         if (!textAnnotations.isEmpty())
-                 
EditorAnnotationUtil.clearAnnotationsOverRange(editor.getDocument(), 
editor.getCaretAnnotations(), (String[]) textAnnotations.toArray(), 
editorRange.start(), editorRange.end());
+  public void deleteText(JsoRange range) {
+    Preconditions.checkNotNull(range, "Range can't be null");
+    CMutableDocument doc = editor.getDocument();
+    doc.deleteRange(range.start(), range.end());
   }
-
   
-  public JsoAnnotation getAnnotation(JsoEditorRange editorRange, String key) {
-         Preconditions.checkNotNull(editorRange, "Range can't be null");
-         Preconditions.checkArgument(isValidAnnotationKey(key), "Invalid 
annotation key");
-         
-         if (TextEditorDefinitions.isParagraphAnnotation(key)) {
-                 
-                 for(Entry<String, LineStyle> ls: 
ParagraphAnnotation.fromString(key).getLineStyles().entrySet()) {
-                 
-                         if (Paragraph.appliesEntirely(editor.getDocument(), 
editorRange.start(), editorRange.end(), ls.getValue())) {
-                               Point<ContentNode> point = 
editor.getDocument().locate(editorRange.start());
-                           ContentNode lineNode = 
LineContainers.getRelatedLineElement(editor.getDocument(), point);              
                                
-                           return 
JsoParagraphAnnotation.create(editor.getDocument(), editorRange.start(), 
editorRange.end(), key, ls.getKey(), lineNode.asElement().getImplNodelet());
-                         }
-                         
-                 }
-                 
-         } else {
-                 
-                 Range range = 
EditorAnnotationUtil.getEncompassingAnnotationRange(editor.getDocument(), key, 
editorRange.start());    
-                 if (range == null)
-                         return null;
-                 
-                 return JsoAnnotation.create(editor, range, key);
-         }
-         
-       return null;
-         
-         
-  }
   
+  public String getText(JsoRange range) {
+    Preconditions.checkNotNull(range, "Range can't be null");
+    CMutableDocument doc = editor.getDocument();
+    Preconditions.checkArgument(range.start() >= 0 && range.start() <= 
range.end() && range.end() < doc.size(), "Range is not correct");
+    return DocHelper.getText(doc, range.start(), range.end());
+  }
+ 
   /**
-   * Gets the current selection. See {@link JsoEditorRange} for methods to
+   * Gets the current selection. See {@link JsoRange} for methods to
    * update the document's selection.
    * 
    *  Includes annotations.
    * 
    * @return
    */
-  public JsoEditorRange getSelection() {
+  public JsoRange getSelection() {
     Range r = editor.getSelectionHelper().getOrderedSelectionRange();
     if (r == null) {
       return null;
-    }
-    return JsoEditorRange.Builder.create(editor.getDocument()).range(r)
-        .annotations(getAnnotationsOverRange(r)).build();
-
+    }   
+    return JsoRange.Builder.create(editor.getDocument()).range(r)
+        .annotations(getAllAnnotationsInRange(r)).build();
   }
+  
+  
 
+  @Override
+  public void onUpdate(final EditorUpdateEvent event) {
+    if (event.selectionLocationChanged()) {
+        Range range = editor.getSelectionHelper().getOrderedSelectionRange();
+        JsoRange.Builder editorRangeBuilder = 
JsoRange.Builder.create(editor.getDocument());
+        if (range != null) {
+          editorRangeBuilder.range(range)
+          .annotations(getAllAnnotationsInRange(range)).build();
+        } else {
+          editorRangeBuilder.annotations(getAnnotationsByDefault());
+        }
+        
+        
+    if (listener != null)
+      listener.onSelectionChange(editorRangeBuilder.build());
+        
+    }
+  }
+  
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/swellrt/client/editor/TextEditorListener.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/swellrt/client/editor/TextEditorListener.java 
b/wave/src/main/java/org/swellrt/client/editor/TextEditorListener.java
index 7835434..a3e7b9d 100644
--- a/wave/src/main/java/org/swellrt/client/editor/TextEditorListener.java
+++ b/wave/src/main/java/org/swellrt/client/editor/TextEditorListener.java
@@ -1,11 +1,11 @@
 package org.swellrt.client.editor;
 
-import org.waveprotocol.wave.client.doodad.annotation.jso.JsoEditorRange;
+import org.waveprotocol.wave.client.doodad.annotation.jso.JsoRange;
 
 
 public interface TextEditorListener {
 
-  public void onSelectionChange(JsoEditorRange annotationsInRage);
+  public void onSelectionChange(JsoRange annotationsInRage);
 
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/AnnotationHandler.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/AnnotationHandler.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/AnnotationHandler.java
index fa13de4..f5c3bef 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/AnnotationHandler.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/AnnotationHandler.java
@@ -3,10 +3,9 @@ package org.waveprotocol.wave.client.doodad.annotation;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.waveprotocol.wave.client.doodad.annotation.jso.JsoEditorRange;
-import org.waveprotocol.wave.client.common.util.JsoStringSet;
 import org.waveprotocol.wave.client.common.util.JsoView;
 import 
org.waveprotocol.wave.client.doodad.annotation.jso.JsoAnnotationController;
+import org.waveprotocol.wave.client.doodad.annotation.jso.JsoRange;
 import org.waveprotocol.wave.client.editor.content.AnnotationPainter;
 import 
org.waveprotocol.wave.client.editor.content.AnnotationPainter.PaintFunction;
 import org.waveprotocol.wave.client.editor.content.ContentElement;
@@ -22,11 +21,7 @@ import 
org.waveprotocol.wave.model.document.AnnotationMutationHandler;
 import org.waveprotocol.wave.model.document.util.AnnotationRegistry;
 import org.waveprotocol.wave.model.document.util.DocumentContext;
 import org.waveprotocol.wave.model.util.CollectionUtils;
-import org.waveprotocol.wave.model.util.Preconditions;
 import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
-import org.waveprotocol.wave.model.util.ReadableStringSet.Proc;
-import org.waveprotocol.wave.model.util.StringMap;
-import org.waveprotocol.wave.model.util.StringSet;
 
 import com.google.gwt.user.client.Event;
 
@@ -39,6 +34,10 @@ import com.google.gwt.user.client.Event;
  */
 public class AnnotationHandler implements AnnotationMutationHandler {
 
+  public interface Activator {
+    public boolean shouldFireEvent();
+  }
+  
   private static AnnotationHandler handlerInstance = null;
   
   private final AnnotationPainter painter;
@@ -128,9 +127,7 @@ public class AnnotationHandler implements 
AnnotationMutationHandler {
     annotationRegistry.registerBehaviour(key, new 
DefaultAnnotationBehaviour(AnnotationFamily.CONTENT));
                
     // Register painter to update attributes of the local view
-    JsoStringSet keySet = JsoStringSet.create();
-    keySet.add(key);
-    painterRegistry.registerPaintFunction(keySet, new RenderFunc(key, 
styleClass, stylesInline));
+    painterRegistry.registerPaintFunction(CollectionUtils.newStringSet(key), 
new RenderFunc(key, styleClass, stylesInline));
     
     if (eventHandler != null) 
       AnnotationPaint.registerEventHandler(key, eventHandler);
@@ -144,7 +141,7 @@ public class AnnotationHandler implements 
AnnotationMutationHandler {
        }
 
        
-       public static void register(Registries registries, String key, 
JsoAnnotationController controller) {
+       public static void register(Registries registries, String key, 
JsoAnnotationController controller, Activator activator) {
          
     AnnotationHandler.register(registries, key, 
         new AnnotationPaint.MutationHandler() {
@@ -153,8 +150,22 @@ public class AnnotationHandler implements 
AnnotationMutationHandler {
           @Override
           public void onMutation(ContentElement node) {
             String valueAttr = AnnotationPaint.VALUE_ATTR_PREFIX + 
AnnotationHandler.getSafeKey(key);
-            if (controller != null)
-              
controller.onChange(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build());
+            if (controller != null && activator.shouldFireEvent())
+              
controller.onChange(JsoRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build());
+          }
+
+          @Override
+          public void onAdded(ContentElement node) {
+            String valueAttr = AnnotationPaint.VALUE_ATTR_PREFIX + 
AnnotationHandler.getSafeKey(key);
+            if (controller != null && activator.shouldFireEvent())
+              
controller.onAdd(JsoRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build());            
+          }
+
+          @Override
+          public void onRemoved(ContentElement node) {
+            String valueAttr = AnnotationPaint.VALUE_ATTR_PREFIX + 
AnnotationHandler.getSafeKey(key);
+            if (controller != null && activator.shouldFireEvent())
+              
controller.onRemove(JsoRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build());            
           }
         }, 
         
@@ -163,8 +174,8 @@ public class AnnotationHandler implements 
AnnotationMutationHandler {
           @Override
           public void onEvent(ContentElement node, Event event) {
             String valueAttr = AnnotationPaint.VALUE_ATTR_PREFIX + 
AnnotationHandler.getSafeKey(key);
-            if (controller != null)
-              
controller.onEvent(JsoEditorRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build(), event);
+            if (controller != null && activator.shouldFireEvent())
+              
controller.onEvent(JsoRange.Builder.create(node.getMutableDoc()).range(node).annotation(key,
 node.getAttribute(valueAttr)).build(), event);
           }
         }, 
         controller != null ? controller.getStyleClass() : null, 

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoAnnotationController.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoAnnotationController.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoAnnotationController.java
index 288a601..59be2ac 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoAnnotationController.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoAnnotationController.java
@@ -97,15 +97,35 @@ public class JsoAnnotationController extends 
JavaScriptObject {
        /**
         * Handle events raised in the rendered annotation element. 
         */
-       public native final void onEvent(JsoEditorRange annotationContent, 
Event event) /*-{
+       public native final void onEvent(JsoRange annotationContent, Event 
event) /*-{
                if (this.onEvent) 
                        this.onEvent(annotationContent, event);
        }-*/;
        
-       public native final void onChange(JsoEditorRange annotationContent) /*-{
+       public native final void onChange(JsoRange annotationContent) /*-{
                if (this.onChange)
                        this.onChange(annotationContent);
        }-*/;
        
+       
+  public native final void onAdd(JsoRange annotationContent) /*-{
+    if (this.onAdd)
+      this.onAdd(annotationContent);
+  }-*/;
+
+  public native final void onRemove(JsoRange annotationContent) /*-{
+    if (this.onRemove)
+      this.onRemove(annotationContent);
+  }-*/;        
+  
+       /**
+        * Sets the reference to the editor's pure javascript facade.
+        * Allows handlers to interact with the editor.
+        * 
+        * @param jsEditorFacade
+        */
+       public final native void setEditorJsFacade(JavaScriptObject 
jsEditorFacade) /*-{
+         this.editor = jsEditorFacade;
+       }-*/;
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoRange.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoRange.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoRange.java
index 889cb09..0d4e4ee 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoRange.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/annotation/jso/JsoRange.java
@@ -4,12 +4,17 @@ import org.waveprotocol.wave.client.common.util.JsoStringMap;
 import org.waveprotocol.wave.client.common.util.JsoView;
 import org.waveprotocol.wave.client.editor.content.CMutableDocument;
 import org.waveprotocol.wave.client.editor.content.ContentElement;
+import org.waveprotocol.wave.client.editor.content.ContentNode;
 import org.waveprotocol.wave.model.document.AnnotationInterval;
 import org.waveprotocol.wave.model.document.util.DocHelper;
+import org.waveprotocol.wave.model.document.util.Point;
 import org.waveprotocol.wave.model.document.util.Range;
+import org.waveprotocol.wave.model.util.CollectionUtils;
 import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
+import org.waveprotocol.wave.model.util.StringMap;
 
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Node;
 
 /**
  * A single native Js class to handle different document and editor ranged 
stuff.
@@ -17,13 +22,20 @@ import com.google.gwt.core.client.JavaScriptObject;
  * @author pablo...@gmail.com (Pablo Ojanguren)
  *
  */
-public class JsoEditorRange extends JavaScriptObject {
+public class JsoRange extends JavaScriptObject {
        
        
        public static class Builder {
                
                JsoView jso;
-               CMutableDocument doc;
+               JsoView annotations;
+               
+
+    CMutableDocument doc;
+               int start;
+               int end;
+               int length;
+               
                
                public static Builder create(CMutableDocument doc) {
                        return new Builder(doc);
@@ -35,9 +47,9 @@ public class JsoEditorRange extends JavaScriptObject {
                }
                
                public Builder range(int start, int end, int length) {
-                       jso.setNumber("start", start);
-                       jso.setNumber("end", end);
-                       jso.setNumber("lenght", length);
+                       this.start = start;
+                       this.end = end;
+                       this.length = length;
                        return this;
                }
                
@@ -46,28 +58,26 @@ public class JsoEditorRange extends JavaScriptObject {
                }
                
                public Builder range(ContentElement contentElement) {
-                       int start = 
contentElement.getContext().locationMapper().getLocation(contentElement);
-                       int end = 
contentElement.getContext().locationMapper().getLocation(contentElement.getNextSibling());
-                       int lenght = DocHelper.getItemSize(doc, contentElement);
-                       return range(start, end, lenght);                       
+                       start = 
contentElement.getContext().locationMapper().getLocation(contentElement);
+                       end = 
contentElement.getContext().locationMapper().getLocation(contentElement.getNextSibling());
+                       length = DocHelper.getItemSize(doc, contentElement);
+                       return this;                    
                }
                
 
                public Builder annotation(String key, String value) {
                        
-                       JsoView annotations;
-                       
-                       if (jso.getJso("annotations") == null) {
-                               jso.setJso("annotations", JsoView.create());
+                       if (annotations == null) {
+                         annotations = JsoView.create();
                        }
-                       annotations = jso.getJsoView("annotations");
+       
                        annotations.setString(key, value);
                        return this;
                }
                
 
                public Builder annotations(JsoStringMap<String> map) {
-                       jso.setJso("annotations", map.backend);                 
+                 annotations = map.backend;    
                        return this;
                }
                
@@ -86,14 +96,30 @@ public class JsoEditorRange extends JavaScriptObject {
                }
                
                
-               public JsoEditorRange build() {                 
+               public JsoRange build() {                       
+                 
+      jso.setNumber("start", start);
+      jso.setNumber("end", end);
+      jso.setNumber("lenght", length);
+                 
+                 if (annotations != null)
+                   jso.setJso("annotations", annotations);
+                 
+                 Node node = null;
+                 Point<ContentNode> point = doc.locate(start);
+                 if (point != null)
+                   node = point.getCanonicalNode().getImplNodeletRightwards(); 
    
+                   
+                 jso.setJso("node", node);
+                   
+      
                        functionize(jso, doc);
                        return jso.cast();
                }
                
                private native void functionize(JavaScriptObject jso, 
CMutableDocument doc) /*-{
                        if (jso != null && typeof jso.start == "number" && 
typeof jso.end == "number")
-                               jso.text = 
@org.waveprotocol.wave.client.doodad.annotation.jso.JsoEditorRange::getRangeText(IILorg/waveprotocol/wave/client/editor/content/CMutableDocument;)(jso.start,
 jso.end, doc);
+                               jso.text = 
@org.waveprotocol.wave.client.doodad.annotation.jso.JsoRange::getRangeText(IILorg/waveprotocol/wave/client/editor/content/CMutableDocument;)(jso.start,
 jso.end, doc);
                        else
                                jso = "";                       
                }-*/;
@@ -112,18 +138,20 @@ public class JsoEditorRange extends JavaScriptObject {
                return this.annotations;
        }-*/;
        
-       public final String[] getAnnotationKeys() {
-               JsoView jsoKeys = getAnnotationsJsoView();
-               String[] keys = new String[jsoKeys.countEntries()];
-               jsoKeys.each(new ProcV<Object>() {
-                       int c = 0;
-                       @Override
-                       public void apply(String key, Object value) {
-                               keys[c++] = key;                        
-                       }
-                       
-               });
-               return keys;
+       public final StringMap<String> getAnnotations() {
+         StringMap<String> map = CollectionUtils.<String>createStringMap();
+         JsoView annotations = getAnnotationsJsoView();
+               
+         annotations.each(new ProcV<String>(){
+
+      @Override
+      public void apply(String key, String value) {
+        map.put(key, value);       
+      }
+           
+         });
+         
+               return map;
        }
         
        private static String getRangeText(int start, int end, CMutableDocument 
doc) {
@@ -134,7 +162,7 @@ public class JsoEditorRange extends JavaScriptObject {
                doc.deleteRange(start, end);
        }
                        
-       protected JsoEditorRange() {
+       protected JsoRange() {
                
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/WidgetDoodad.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/WidgetDoodad.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/WidgetDoodad.java
index 9a1a680..75eb377 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/WidgetDoodad.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/WidgetDoodad.java
@@ -93,7 +93,14 @@ public class WidgetDoodad {
         }
       }
     }
+    
+
+    @Override
+    public void onRemovedFromParent(ContentElement element, ContentElement 
newParent) {
+    }    
+    
   }
+  
 
 
   static class WidgetEventHandler extends NodeEventHandlerImpl {
@@ -188,7 +195,7 @@ public class WidgetDoodad {
   public static void register(ElementHandlerRegistry registry,
       StringMap<JsoWidgetController> controllers) {
 
-       widgetControllers = controllers;  
+    widgetControllers = controllers;  
          
     WidgetRendererHandler renderer = new WidgetRendererHandler(controllers);
     WidgetEventHandler eventHandler = new WidgetEventHandler(controllers);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/jso/JsoWidgetController.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/jso/JsoWidgetController.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/jso/JsoWidgetController.java
index eae09ed..f08a577 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/jso/JsoWidgetController.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/doodad/widget/jso/JsoWidgetController.java
@@ -73,13 +73,13 @@ public class JsoWidgetController extends JavaScriptObject {
         element.innerHTML="<span style='background: 
#FFD677;'>"+after+"</span>";
       },
 
-         onActivated: function(element) {
-               // attach event handlers
-         },
-         
-         onDeactivated: function(element) {
-               // deattach event handlers
-         }
+         onActivated: function(element) {
+               // attach event handlers
+         },
+         
+         onDeactivated: function(element) {
+               // deattach event handlers
+         }
 
     };
 
@@ -113,5 +113,14 @@ public class JsoWidgetController extends JavaScriptObject {
        this.onDeactivated(parent);  
   }-*/;
 
+  /**
+   * Sets the reference to the editor's pure javascript facade.
+   * Allows handlers to interact with the editor.
+   * 
+   * @param jsEditorFacade
+   */
+  public final native void setEditorJsFacade(JavaScriptObject jsEditorFacade) 
/*-{
+    this.editor = jsEditorFacade;
+  }-*/;
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationPaint.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationPaint.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationPaint.java
index 6fda1f9..c4df1d5 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationPaint.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationPaint.java
@@ -91,7 +91,9 @@ public class AnnotationPaint {
    * Handlers may register callback for mutation events over painted regions.
    */
   public interface MutationHandler {
+    void onAdded(ContentElement node);
     void onMutation(ContentElement node);
+    void onRemoved(ContentElement node);
   }
 
   static final Map<String, MutationHandler> mutationHandlerRegistry =

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationSpreadRenderer.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationSpreadRenderer.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationSpreadRenderer.java
index daaa71c..8d6d8c8 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationSpreadRenderer.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/misc/AnnotationSpreadRenderer.java
@@ -78,17 +78,17 @@ class AnnotationSpreadRenderer extends 
RenderingMutationHandler {
   private static Set<MutationHandler> getMutationHandlers(ContentElement 
element) {
        
        Set<MutationHandler> handlers = new HashSet<MutationHandler>();  
-         
+       
+       
        element.getAttributes().each(new ProcV<String>() {
                @Override
                public void apply(String key, String value) {
                        
-                       if 
(key.startsWith(AnnotationPaint.MUTATION_LISTENER_ATTR)) {
-                               MutationHandler h = 
AnnotationPaint.mutationHandlerRegistry.get(key);
-                               if (h != null)
-                                       handlers.add(h);
-                       }
-                       
+                 if 
(key.startsWith(AnnotationPaint.MUTATION_LISTENER_ATTR_PREFIX)) {              
+                   MutationHandler h = 
AnnotationPaint.mutationHandlerRegistry.get(value);
+        if (h != null)
+          handlers.add(h);                 
+                 }             
                }               
        });
        
@@ -223,12 +223,26 @@ class AnnotationSpreadRenderer extends 
RenderingMutationHandler {
       element.setBothNodelets(newNodelet);
     }
   }
+    
+  @Override
+  public void onAddedToParent(ContentElement element, ContentElement 
oldParent) {
+    Set<MutationHandler> handlers = getMutationHandlers(element);
+    for (MutationHandler h : handlers) {
+      h.onAdded(element);
+    }
+  }  
 
   @Override
   public void onRemovedFromParent(ContentElement element, ContentElement 
newParent) {
     if (newParent != null) {
       return;
     }
+    
+    Set<MutationHandler> handlers = getMutationHandlers(element);
+    for (MutationHandler h: handlers) {
+      h.onRemoved(element);
+    }
+    
     removeListener(DomHelper.castToOld(element.getImplNodelet()));
     super.onRemovedFromParent(element, newParent);
   }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/a768997c/wave/src/main/java/org/waveprotocol/wave/client/editor/content/paragraph/Paragraph.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/paragraph/Paragraph.java
 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/paragraph/Paragraph.java
index cbc7210..94b0849 100644
--- 
a/wave/src/main/java/org/waveprotocol/wave/client/editor/content/paragraph/Paragraph.java
+++ 
b/wave/src/main/java/org/waveprotocol/wave/client/editor/content/paragraph/Paragraph.java
@@ -54,7 +54,9 @@ public class Paragraph {
    * Callback for mutation events within a paragraph.
    */
   public interface MutationHandler {
+    void onAdded(ContentElement node);
     void onMutation(ContentElement node);
+    void onRemoved(ContentElement node);
   }
 
   /** A registry of event handlers by paragraph type */

Reply via email to