Skip to content

Commit 77066c1

Browse files
author
Luke Leighton
committed
possibly the most devious horrible code ever added, allows styles to be added to richtext and importantly replaced again
1 parent a655d4a commit 77066c1

File tree

9 files changed

+554
-27
lines changed

9 files changed

+554
-27
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
Changes made to Pyjamas since 0.8pre1
55
---------------------------------
66

7+
* completed the port of RichTextArea, added an example RichTextToolbar,
8+
and added a port of gwt-selection which can be used to highlight
9+
links, add span styles around blocks etc. unsurprisingly an additional
10+
example named "richtext" has been added.
11+
712
* modified DisclosurePanel and Tree widgets to allow use of
813
alternative sets of images. Issue #715. (Phil Charlesworth)
914

examples/richtext/RichTextEditor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def getWindow(self, iFrame=None):
298298
if not iFrameWin.document:
299299
iFrameWin = iFrameWin.parentNode # FBJS version of parentNode
300300

301-
print "getWindow", iFrameWin, dir(iFrameWin)
301+
#print "getWindow", iFrameWin, dir(iFrameWin)
302302

303303
return iFrameWin
304304

examples/richtext/SelectionTest.py

Lines changed: 188 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,69 @@
99
from pyjamas.ui.TextArea import TextArea
1010
from pyjamas.ui import RootPanel
1111

12+
from pyjamas import DOM
13+
1214
from RichTextEditor import RichTextEditor
1315

1416
from pyjamas.selection.RangeEndPoint import RangeEndPoint
1517
from pyjamas.selection.Range import Range
1618
from pyjamas.selection.RangeUtil import getAdjacentTextElement
1719
from pyjamas.selection import Selection
1820

21+
import string
22+
23+
def print_tree(parent):
24+
if parent.nodeType == 1:
25+
print "parent", parent, parent.tagName, parent.innerHTML
26+
else:
27+
print "parent", parent, parent.nodeName
28+
child = parent.firstChild
29+
while child:
30+
print "child", child,
31+
if child.nodeType == 1:
32+
print child.tagName, repr(child.innerHTML)
33+
else:
34+
print repr(child.data)
35+
child = child.nextSibling
36+
37+
def remove_node(doc, element):
38+
""" removes a specific node, adding its children in its place
39+
"""
40+
fragment = doc.createDocumentFragment()
41+
while element.firstChild:
42+
fragment.appendChild(element.firstChild)
43+
44+
parent = element.parentNode
45+
parent.insertBefore(fragment, element)
46+
parent.removeChild(element)
47+
48+
print_tree(parent)
49+
#print "element", element, element.tagName, element.innerHTML
50+
51+
def remove_editor_styles(doc, tree):
52+
""" removes all other <span> nodes with an editor style
53+
"""
54+
55+
element = tree.lastChild
56+
while element:
57+
if element.nodeType != 1:
58+
element = element.previousSibling
59+
continue
60+
if string.lower(element.tagName) != 'span':
61+
element = element.previousSibling
62+
continue
63+
style = DOM.getAttribute(element, "className")
64+
print "span", style, element, element.innerHTML
65+
if not style or not style.startswith("editor-"):
66+
element = element.previousSibling
67+
continue
68+
prev_el = element
69+
remove_editor_styles(doc, prev_el)
70+
element = element.previousSibling
71+
remove_node(doc, prev_el)
72+
print "post-remove"
73+
print_tree(tree)
74+
1975
"""*
2076
* Entry point classes define <code>onModuleLoad()</code>.
2177
"""
@@ -40,7 +96,8 @@ def onModuleLoad(self):
4096
self.m_toSCursor.setTitle("Set the selection to be a cursor at the beginning of the current selection")
4197
self.m_toECursor = Button("To Cursor >", self)
4298
self.m_toECursor.setTitle("Set the selection to be a cursor at the end of the current selection")
43-
self.m_surround = Button("Surround", self)
99+
self.m_surround1 = Button("Surround1", self)
100+
self.m_surround2 = Button("Surround2", self)
44101

45102
grid = Grid(2, 2)
46103
self.m_startNode = self.createTextBox(1)
@@ -63,7 +120,8 @@ def onModuleLoad(self):
63120
buts.add(self.m_setHtml)
64121
buts.add(self.m_toSCursor)
65122
buts.add(self.m_toECursor)
66-
buts.add(self.m_surround)
123+
buts.add(self.m_surround1)
124+
buts.add(self.m_surround2)
67125
buts.add(grid)
68126
buts.add(self.m_select)
69127
buts.add(self.m_cursor)
@@ -146,13 +204,130 @@ def toCursor(self, start):
146204
Selection.setRange(rng)
147205
self.refresh()
148206

149-
def surround(self):
207+
def _surround(self, cls):
208+
""" this is possibly one of the most truly dreadful bits of code
209+
for manipulating DOM ever written. its purpose is to add only
210+
the editor class required, and no more. unfortunately, DOM gets
211+
chopped up by the range thing, and a bit more besides. so we
212+
have to:
213+
214+
* extract the range contents
215+
* clean up removing any blank text nodes that got created above
216+
* slap a span round it
217+
* clean up removing any blank text nodes that got created above
218+
* remove any prior editor styles on the range contents
219+
* go hunting through the entire document for stacked editor styles
220+
221+
this latter is funfunfun because only "spans with editor styles
222+
which themselves have no child elements but a single span with
223+
an editor style" must be removed. e.g. if an outer editor span
224+
has another editor span and also some text, the outer span must
225+
be left alone.
226+
"""
150227
rng = self.m_rte.getRange()
151-
if (rng is not None) and not rng.isCursor():
152-
rng.surroundContents()
153-
self.m_rte.getSelection()
154-
Selection.setRange(rng)
155-
self.refresh()
228+
if (rng is None) or rng.isCursor():
229+
return
230+
231+
rng.ensureRange()
232+
dfrag = rng.m_range.extractContents()
233+
print "doc pre remove"
234+
print_tree(rng.m_document)
235+
remove_editor_styles(rng.m_document, dfrag)
236+
print "dfrag post remove styles"
237+
print_tree(dfrag)
238+
print "doc after dfrag remove"
239+
print_tree(rng.m_document)
240+
print "extract", dfrag, dir(dfrag)
241+
element = rng.m_document.createElement("span")
242+
DOM.setAttribute(element, "className", cls)
243+
DOM.appendChild(element, dfrag)
244+
rng.m_range.insertNode(element)
245+
246+
it = DOM.IterWalkChildren(element, True)
247+
while True:
248+
try:
249+
node = it.next()
250+
except StopIteration:
251+
break
252+
print node, node.nodeType
253+
if node.nodeType == 3 and unicode(node.data) == u'':
254+
print "removing blank text"
255+
DOM.removeChild(node.parentNode, node)
256+
257+
rng.setRange(element)
258+
259+
it = DOM.IterWalkChildren(rng.m_document, True)
260+
while True:
261+
try:
262+
node = it.next()
263+
except StopIteration:
264+
break
265+
print node, node.nodeType
266+
if node.nodeType == 3 and unicode(node.data) == u'':
267+
print "removing blank text"
268+
DOM.removeChild(node.parentNode, node)
269+
270+
it = DOM.IterWalkChildren(rng.m_document)
271+
while True:
272+
try:
273+
node = it.next()
274+
except StopIteration:
275+
break
276+
style = DOM.getAttribute(node, "className")
277+
print "node", node.firstChild, node.tagName, style
278+
if node.firstChild:
279+
continue
280+
if str(string.lower(node.tagName)) != 'span':
281+
continue
282+
print "node", node.firstChild, node.tagName, style
283+
if not style or not style.startswith("editor-"):
284+
continue
285+
it.remove()
286+
287+
it = DOM.IterWalkChildren(rng.m_document, True)
288+
while True:
289+
try:
290+
node = it.next()
291+
except StopIteration:
292+
break
293+
if node.nodeType != 1:
294+
continue
295+
if node.firstChild is None:
296+
continue
297+
style = DOM.getAttribute(node, "className")
298+
print "double-node", node.tagName, style, node.firstChild.nextSibling
299+
print_tree(node)
300+
if node.firstChild.nextSibling:
301+
continue
302+
if node.firstChild.nodeType != 1:
303+
continue
304+
if str(string.lower(node.tagName)) != 'span':
305+
continue
306+
if str(string.lower(node.firstChild.tagName)) != 'span':
307+
continue
308+
if not style or not style.startswith("editor-"):
309+
continue
310+
style = DOM.getAttribute(node.firstChild, "className")
311+
if not style or not style.startswith("editor-"):
312+
continue
313+
# remove the *outer* one because the range was added to
314+
# the inner, and the inner one overrides anyway
315+
print "remove overlapping styles", node
316+
remove_node(rng.m_document, node)
317+
318+
doc = self.m_rte.getDocument()
319+
print "doc pre", doc, doc.body.innerHTML
320+
321+
self.m_rte.getSelection()
322+
Selection.setRange(rng)
323+
self.refresh()
324+
print "doc", doc, doc.body.innerHTML
325+
326+
def surround1(self):
327+
self._surround("editor-cls1")
328+
329+
def surround2(self):
330+
self._surround("editor-cls2")
156331

157332
def findNodeByNumber(self, num):
158333

@@ -208,8 +383,11 @@ def onClick(self, wid):
208383
elif wid == self.m_toSCursor:
209384
self.toCursor(True)
210385

211-
elif wid == self.m_surround:
212-
self.surround()
386+
elif wid == self.m_surround1:
387+
self.surround1()
388+
389+
elif wid == self.m_surround2:
390+
self.surround2()
213391

214392
elif wid == self.m_setHtml:
215393
self.toHtml()

0 commit comments

Comments
 (0)