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