changeset 347:f1ba38043d78

browser_side: status panel is based on a new class LightTextEditor which uses HTML5 "editablecontent" property
author souliane <souliane@mailoo.org>
date Fri, 07 Feb 2014 20:14:11 +0100
parents 82f9e92379b0
children 83454ba70a9c
files browser_side/base_panels.py browser_side/panels.py browser_side/tools.py public/libervia.css
diffstat 4 files changed, 95 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/browser_side/base_panels.py	Fri Feb 07 20:08:28 2014 +0100
+++ b/browser_side/base_panels.py	Fri Feb 07 20:14:11 2014 +0100
@@ -28,12 +28,14 @@
 from pyjamas.ui.StackPanel import StackPanel
 from pyjamas.ui.TextArea import TextArea
 from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT
+from pyjamas.ui.KeyboardListener import KEY_ENTER, KeyboardHandler
+from pyjamas.ui.FocusListener import FocusHandler
 from pyjamas import DOM
 
 from datetime import datetime
 from time import time
 
-from tools import html_sanitize, inlineRoot
+from tools import html_sanitize, html_clean, inlineRoot
 
 from sat_frontends.tools.strings import addURLToText
 from sat.core.i18n import _
@@ -285,3 +287,56 @@
 
     def setText(self, text):
         self.text_area.setText(text)
+
+
+class LightTextEditor(HTML, FocusHandler, KeyboardHandler):
+
+    def __init__(self, content='', single_line=False, callback=None):
+        HTML.__init__(self)
+        if single_line:
+            FocusHandler.__init__(self)
+            KeyboardHandler.__init__(self)
+        self.__single_line = single_line
+        self.__callback = callback
+        self.setContent(content)
+
+    def setContent(self, content=''):
+        self.__original_content = html_clean(content) if self.__single_line else content.strip()
+        self.setHTML('<div>%s</div>' % self.__original_content)
+
+    def getContent(self):
+        content = DOM.getInnerHTML(self.getElement().firstChild)
+        return html_clean(content) if self.__single_line else content.strip()
+
+    def modified(self, content=None):
+        if content is None:
+            content = self.getContent()
+        return content != self.__original_content
+
+    def edit(self, edit):
+        self.getElement().setAttribute('contenteditable', 'true' if edit else 'false')
+        if edit:
+            self.setFocus(True)
+            if self.__single_line:
+                self.addFocusListener(self)
+                self.addKeyboardListener(self)
+        else:
+            if self.__single_line:
+                self.removeFocusListener(self)
+                self.removeKeyboardListener(self)
+            content = self.getContent()
+            if self.modified(content) and self.__callback:
+                self.__callback(content)
+
+    def setFocus(self, focus):
+        if focus:
+            self.getElement().focus()
+        else:
+            self.getElement().blur()
+
+    def onKeyDown(self, sender, keycode, modifiers):
+        if keycode == KEY_ENTER:
+            self.setFocus(False)
+
+    def onLostFocus(self, sender):
+        self.edit(False)
--- a/browser_side/panels.py	Fri Feb 07 20:08:28 2014 +0100
+++ b/browser_side/panels.py	Fri Feb 07 20:14:11 2014 +0100
@@ -40,7 +40,7 @@
 from __pyjamas__ import doc
 
 from tools import html_sanitize, setPresenceStyle
-from base_panels import ChatText, OccupantsList, PopupMenuPanel
+from base_panels import ChatText, OccupantsList, PopupMenuPanel, LightTextEditor
 from datetime import datetime
 from time import time
 from card_game import CardPanel
@@ -897,52 +897,27 @@
         return False
 
 
-class StatusPanel(HTMLPanel, ClickHandler, FocusHandler, KeyboardHandler):
+class StatusPanel(LightTextEditor, ClickHandler):
 
     EMPTY_STATUS = '<click to set a status>'
 
     def __init__(self, host, status=''):
         self.host = host
         self.status = status
-        HTMLPanel.__init__(self, self.__getContent())
+        LightTextEditor.__init__(self, self.__getStatus(), True, lambda status: self.host.bridge.call('setStatus', None, self.host.status_panel.presence, status))
         self.setStyleName('statusPanel')
         ClickHandler.__init__(self)
-        FocusHandler.__init__(self)
-        KeyboardHandler.__init__(self)
         self.addClickListener(self)
 
-    def __getContent(self):
-        return "<span class='status'>%(status)s</span>" % {'status': html_sanitize(self.status or self.EMPTY_STATUS)}
+    def __getStatus(self):
+        return html_sanitize(self.status or self.EMPTY_STATUS)
 
     def changeStatus(self, new_status):
         self.status = new_status
-        self.setHTML(self.__getContent())
+        self.setContent(self.__getStatus())
 
     def onClick(self, sender):
-        self.textarea = TextArea()
-        self.textarea.setStyleName('status-edit')
-        self.textarea.setText(self.status)
-        self.textarea.addKeyboardListener(self)
-        self.setVisible(False)
-        self.parent.insert(self.textarea, self.parent.getWidgetIndex(self))
-        self.textarea.setFocus(True)
-        self.textarea.setSelectionRange(0, len(self.status))
-        self.textarea.addFocusListener(self)
-        self.textarea.addKeyboardListener(self)
-
-    def onKeyPress(self, sender, keycode, modifiers):
-        text = self.textarea.getText()
-
-        if keycode == KEY_ENTER:
-            if text != self.status:
-                self.host.bridge.call('setStatus', None, self.host.status_panel.presence, text)
-            self.parent.remove(self.textarea)
-            self.setVisible(True)
-
-    def onLostFocus(self, sender):
-        FocusHandler.onLostFocus(self, sender)
-        if sender == self.textarea:
-            self.onKeyPress(self, KEY_ENTER, None)
+        self.edit(True)
 
 
 class PresenceStatusPanel(HorizontalPanel, ClickHandler):
--- a/browser_side/tools.py	Fri Feb 07 20:08:28 2014 +0100
+++ b/browser_side/tools.py	Fri Feb 07 20:14:11 2014 +0100
@@ -22,6 +22,7 @@
 from pyjamas import Window
 from nativedom import NativeDOM
 from sat_frontends.tools import xmltools
+import re
 
 dom = NativeDOM()
 
@@ -31,6 +32,30 @@
     return html.replace('<', '&lt;').replace('>', '&gt;')
 
 
+def html_clean(html):
+    """
+    Remove HTML markup from the given string.
+    Copied from nltk.clean_html (http://www.nltk.org/)
+
+    @param html: the HTML string to be cleaned
+    @type html: C{string}
+    @rtype: C{string}
+    """
+
+    # First we remove inline JavaScript/CSS:
+    cleaned = re.sub(r"(?is)<(script|style).*?>.*?(</\1>)", "", html.strip())
+    # Then we remove html comments. This has to be done before removing regular
+    # tags since comments can contain '>' characters.
+    cleaned = re.sub(r"(?s)<!--(.*?)-->[\n]?", "", cleaned)
+    # Next we can remove the remaining tags:
+    cleaned = re.sub(r"(?s)<.*?>", " ", cleaned)
+    # Finally, we deal with whitespace
+    cleaned = re.sub(r"&nbsp;", " ", cleaned)
+    cleaned = re.sub(r"  ", " ", cleaned)
+    cleaned = re.sub(r"  ", " ", cleaned)
+    return cleaned.strip()
+
+
 def inlineRoot(xhtml):
     """ make root element inline """
     doc = dom.parseString(xhtml)
--- a/public/libervia.css	Fri Feb 07 20:08:28 2014 +0100
+++ b/public/libervia.css	Fri Feb 07 20:14:11 2014 +0100
@@ -581,6 +581,10 @@
     text-shadow: 0 -1px 1px rgba(255,255,255,0.25);
     font-size: 1.2em;
     background-color: #eee;
+    font-style: italic;
+    font-weight: bold;
+    color: #666;
+    cursor: pointer;
 }
 
 .presence-button {
@@ -589,20 +593,6 @@
 	cursor: pointer;
 }
 
-.status {
-    font-style: italic;
-    font-weight: bold;
-    color: #666;
-    cursor: pointer;
-}
-
-.status-edit {
-    font-style: italic;
-    font-weight: bold;
-    color: #666;
-    height: 25px;
-}
-
 /* RegisterBox */
 
 .registerPanel_main button {
@@ -1497,3 +1487,6 @@
 .gwt-RadioButton {
 	white-space: nowrap;
 }
+
+[contenteditable="true"] {
+}