annotate libervia/pages/_browser/editor.py @ 1415:8415d882b686

browser: new `editor` module: This module provide tools for dynamtic item edition. For now it implements forms autosave, and a tags editor.
author Goffi <goffi@goffi.org>
date Thu, 29 Apr 2021 16:45:39 +0200
parents
children 925a7c498cda
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1415
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 """text edition management"""
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
2
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
3 from browser import document, window, bind, timer
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 from browser.local_storage import storage
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
5 from browser.object_storage import ObjectStorage
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 from template import Template
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
7
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 object_storage = ObjectStorage(storage)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 profile = window.profile
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # how often we save forms, in seconds
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 AUTOSAVE_FREQUENCY = 20
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
13
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
14
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
15 def serialise_form(form_elt):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 ret = {}
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 for elt in form_elt.elements:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
18 if elt.tagName == "INPUT":
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
19 if elt.type in ("hidden", "submit"):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 elif elt.type == "text":
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 ret[elt.name] = elt.value
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 else:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 print(f"elt.type not managet yet: {elt.type}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 elif elt.tagName == "TEXTAREA":
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 ret[elt.name] = elt.value
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
28 elif elt.tagName in ("BUTTON",):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
29 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
30 else:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 print(f"tag not managet yet: {elt.tagName}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 return ret
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
34
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
35
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
36 def restore_form(form_elt, data):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 for elt in form_elt.elements:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 if elt.tagName not in ("INPUT", "TEXTAREA"):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 try:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 value = data[elt.name]
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 except KeyError:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 continue
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 else:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 elt.value = value
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
46
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
47
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 def set_form_autosave(form_id):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 """Save locally form data regularly and restore it until it's submitted
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
50
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 form is saved every AUTOSAVE_FREQUENCY seconds and when visibility is lost.
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 Saved data is restored when the method is called.
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
53 Saved data is cleared when the form is submitted.
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 """
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
55 if profile is None:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 print(f"No session started, won't save and restore form {form_id}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 return
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
58
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 form_elt = document[form_id]
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 submitted = False
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
61
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 key = {"profile": profile, "type": "form_autosave", "form": form_id}
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 try:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 form_saved_data = object_storage[key]
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 except KeyError:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 last_serialised = None
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 else:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
68 print(f"restoring content of form {form_id!r}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 last_serialised = form_saved_data
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 restore_form(form_elt, form_saved_data)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
71
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 def save_form():
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 if not submitted:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
74 nonlocal last_serialised
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 serialised = serialise_form(form_elt)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
76 if serialised != last_serialised:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 last_serialised = serialised
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
78 print(f"saving content of form {form_id!r}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 object_storage[key] = serialised
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
80
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 @bind(form_elt, "submit")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
82 def on_submit(evt):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 nonlocal submitted
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 submitted = True
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 print(f"clearing stored content of form {form_id!r}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 try:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 del object_storage[key]
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 except KeyError:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 print("key error")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 pass
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
91
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
92 @bind(document, "visibilitychange")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 def on_visibiliy_change(evt):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 print("visibility change")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 if document.visibilityState != "visible":
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 save_form()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
97
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
98 timer.set_interval(save_form, AUTOSAVE_FREQUENCY * 1000)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
99
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
100
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 class TagsEditor:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
102
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 def __init__(self, input_selector):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 print("installing Tags Editor")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 self.input_elt = document.select_one(input_selector)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 self.input_elt.style.display = "none"
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 tags_editor_tpl = Template('editor/tags_editor.html')
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 self.tag_tpl = Template('editor/tag.html')
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
109
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
110 editor_elt = tags_editor_tpl.get_elt()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
111 self.input_elt.parent <= editor_elt
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 self.tag_input_elt = editor_elt.select_one(".tag_input")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 self.tag_input_elt.bind("keydown", self.on_key_down)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
114 self._current_tags = None
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
115 self.tags_map = {}
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 for tag in self.current_tags:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 self.add_tag(tag, force=True)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
118
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 @property
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 def current_tags(self):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
121 if self._current_tags is None:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
122 self._current_tags = {
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
123 t.strip() for t in self.input_elt.value.split(',') if t.strip()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 }
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
125 return self._current_tags
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
126
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
127 @current_tags.setter
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 def current_tags(self, tags):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 self._current_tags = tags
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
130
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 def add_tag(self, tag, force=False):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 tag = tag.strip()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 if not force and (not tag or tag in self.current_tags):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
134 return
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
135 self.current_tags = self.current_tags | {tag}
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 self.input_elt.value = ','.join(self.current_tags)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 tag_elt = self.tag_tpl.get_elt({"label": tag})
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 self.tags_map[tag] = tag_elt
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 self.tag_input_elt.parent.insertBefore(tag_elt, self.tag_input_elt)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
140 tag_elt.select_one(".click_to_delete").bind(
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 "click", lambda evt: self.on_tag_click(evt, tag)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 )
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
143
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
144 def remove_tag(self, tag):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 try:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
146 tag_elt = self.tags_map[tag]
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
147 except KeyError:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
148 print(f"trying to remove an inexistant tag: {tag}")
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
149 else:
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
150 self.current_tags = self.current_tags - {tag}
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
151 self.input_elt.value = ','.join(self.current_tags)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
152 tag_elt.remove()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
153
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
154 def on_tag_click(self, evt, tag):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
155 evt.stopPropagation()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
156 self.remove_tag(tag)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
157
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
158 def on_key_down(self, evt):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
159 if evt.key in (",", "Enter"):
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
160 evt.stopPropagation()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
161 evt.preventDefault()
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
162 self.add_tag(self.tag_input_elt.value)
8415d882b686 browser: new `editor` module:
Goffi <goffi@goffi.org>
parents:
diff changeset
163 self.tag_input_elt.value = ""