libreqda 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. CHANGELOG.md +481 -0
  2. libreqda-1.2.0.dist-info/LICENSE.txt +28 -0
  3. libreqda-1.2.0.dist-info/METADATA +131 -0
  4. libreqda-1.2.0.dist-info/RECORD +255 -0
  5. libreqda-1.2.0.dist-info/WHEEL +4 -0
  6. libreqda-1.2.0.dist-info/entry_points.txt +4 -0
  7. scripts/babelrc +8 -0
  8. scripts/dist/LICENSE.txt +33 -0
  9. scripts/dist/RELEASE.txt +23 -0
  10. scripts/dist/macos/libreqda.icns +0 -0
  11. scripts/dist/macos/macos.spec +43 -0
  12. scripts/dist/macos/plist.patch +10 -0
  13. scripts/dist/macos/taguette.icns +0 -0
  14. scripts/dist/macos/taguette_console_wrapper +4 -0
  15. scripts/dist/macos.sh +32 -0
  16. scripts/dist/pyinstaller_entrypoint.py +19 -0
  17. scripts/dist/windows/windows-installer.iss +51 -0
  18. scripts/dist/windows/windows.spec +39 -0
  19. scripts/edugain/worker_update_edugain_idps.py +89 -0
  20. scripts/edugain/worker_update_edugain_idps.sh +9 -0
  21. scripts/make_password_reset_link.py +56 -0
  22. scripts/make_theme.py +99 -0
  23. scripts/new_db_revision.sh +8 -0
  24. scripts/register_user.py +76 -0
  25. scripts/update_pot.sh +33 -0
  26. scripts/update_translations.sh +17 -0
  27. scripts/validate.py +140 -0
  28. scripts/wipe_and_disable_user.py +90 -0
  29. taguette/__init__.py +13 -0
  30. taguette/access_control.py +145 -0
  31. taguette/convert.py +623 -0
  32. taguette/database/__init__.py +177 -0
  33. taguette/database/base.py +9 -0
  34. taguette/database/copy.py +433 -0
  35. taguette/database/models.py +920 -0
  36. taguette/demodata.py +688 -0
  37. taguette/export.py +500 -0
  38. taguette/extract.py +287 -0
  39. taguette/import_codebook.py +110 -0
  40. taguette/l10n/de_DE/LC_MESSAGES/taguette_javascript.mo +0 -0
  41. taguette/l10n/de_DE/LC_MESSAGES/taguette_main.mo +0 -0
  42. taguette/l10n/el_EL/LC_MESSAGES/taguette_javascript.mo +0 -0
  43. taguette/l10n/el_GR/LC_MESSAGES/taguette_javascript.mo +0 -0
  44. taguette/l10n/el_GR/LC_MESSAGES/taguette_main.mo +0 -0
  45. taguette/l10n/en_US/LC_MESSAGES/taguette_javascript.mo +0 -0
  46. taguette/l10n/en_US/LC_MESSAGES/taguette_main.mo +0 -0
  47. taguette/l10n/es_ES/LC_MESSAGES/taguette_javascript.mo +0 -0
  48. taguette/l10n/es_ES/LC_MESSAGES/taguette_main.mo +0 -0
  49. taguette/l10n/fr_FR/LC_MESSAGES/taguette_javascript.mo +0 -0
  50. taguette/l10n/fr_FR/LC_MESSAGES/taguette_main.mo +0 -0
  51. taguette/l10n/id_ID/LC_MESSAGES/taguette_javascript.mo +0 -0
  52. taguette/l10n/id_ID/LC_MESSAGES/taguette_main.mo +0 -0
  53. taguette/l10n/it_IT/LC_MESSAGES/taguette_javascript.mo +0 -0
  54. taguette/l10n/it_IT/LC_MESSAGES/taguette_main.mo +0 -0
  55. taguette/l10n/nl_NL/LC_MESSAGES/taguette_javascript.mo +0 -0
  56. taguette/l10n/nl_NL/LC_MESSAGES/taguette_main.mo +0 -0
  57. taguette/l10n/pt_PT/LC_MESSAGES/taguette_javascript.mo +0 -0
  58. taguette/l10n/pt_PT/LC_MESSAGES/taguette_main.mo +0 -0
  59. taguette/l10n/sv_SE/LC_MESSAGES/taguette_javascript.mo +0 -0
  60. taguette/l10n/sv_SE/LC_MESSAGES/taguette_main.mo +0 -0
  61. taguette/loki_handler.py +107 -0
  62. taguette/main.py +649 -0
  63. taguette/migrations/env.py +72 -0
  64. taguette/migrations/script.py.mako +24 -0
  65. taguette/migrations/versions/09c662cd9483_make_documents_filename_not_null.py +39 -0
  66. taguette/migrations/versions/1b29e68900c1_add_user_roles.py +55 -0
  67. taguette/migrations/versions/1b96a606551d_update_user_to_have_a_display_name.py +34 -0
  68. taguette/migrations/versions/2467bab602b7_add_color_column_to_tags_table.py +33 -0
  69. taguette/migrations/versions/382db36f20f4_add_user_disabled.py +66 -0
  70. taguette/migrations/versions/43d6c240309d_remove_document_groups.py +81 -0
  71. taguette/migrations/versions/447d636f72c5_cascades.py +40 -0
  72. taguette/migrations/versions/491de2dc7cd7_add_indexes.py +51 -0
  73. taguette/migrations/versions/4c55b3b3ff30_user_login_length.py +58 -0
  74. taguette/migrations/versions/61a4abcc8769_user_with_parent_for_permissions.py +40 -0
  75. taguette/migrations/versions/6489b5f9cfb5_increase_password_hash_size.py +34 -0
  76. taguette/migrations/versions/679f625e6e6a_fix_unique_tags.py +59 -0
  77. taguette/migrations/versions/77c4543a5b9b_add_parent_id_column_to_collection_table.py +41 -0
  78. taguette/migrations/versions/7c94b7a31e69_add_is_archived_column_to_projets.py +39 -0
  79. taguette/migrations/versions/7f56de97e3c8_change_edugain_displayname_length.py +42 -0
  80. taguette/migrations/versions/807c0cc0ecf6_remove_invalid_hltags.py +43 -0
  81. taguette/migrations/versions/80b1cc9d4c22_add_user_email.py +35 -0
  82. taguette/migrations/versions/91ade71ccf4d_add_user_language.py +28 -0
  83. taguette/migrations/versions/955db2e523d1_add_collections_tables_and_associations_.py +60 -0
  84. taguette/migrations/versions/a1b2c3d4e5f6_add_user_hide_code_counts.py +28 -0
  85. taguette/migrations/versions/b23f3b7a638e_add_user_password_change_date.py +36 -0
  86. taguette/migrations/versions/b7c1e2d3f4a5_add_project_compact_layout.py +28 -0
  87. taguette/migrations/versions/bc8e0e0677e9_rename_command_fields.py +126 -0
  88. taguette/migrations/versions/bce44849c2f2_lowercase_logins.py +96 -0
  89. taguette/migrations/versions/ca076140922f_update_user_for_external_logins.py +32 -0
  90. taguette/migrations/versions/d86c45d7aa8a_add_document_direction.py +127 -0
  91. taguette/migrations/versions/db5e31a0233d_remove_blank_highlights.py +88 -0
  92. taguette/migrations/versions/dd37d9027a24_unique_tags.py +68 -0
  93. taguette/migrations/versions/de678cef2a94_add_project_id_to_collections_and_.py +41 -0
  94. taguette/migrations/versions/e459991d960e_add_table_edugainidps.py +39 -0
  95. taguette/migrations/versions/e4cf92942271_add_index.py +29 -0
  96. taguette/migrations/versions/e4e090a5b511_remove_commands_document_id_fk.py +38 -0
  97. taguette/migrations/versions/e9a9f1c77971_add_favorite_column_to_document.py +32 -0
  98. taguette/migrations/versions/ecb4065de575_don_t_use_server_default_now_timezone_.py +44 -0
  99. taguette/migrations/versions/fbdc29566f62_edugain_fix_field_type.py +42 -0
  100. taguette/migrations/versions/fdc6eb54b034_add_parent_id_column_to_tags_table.py +31 -0
  101. taguette/static/README.md +34 -0
  102. taguette/static/alt_templates/.gitkeep +0 -0
  103. taguette/static/assets/test/logos/monsters-university-logo.png +0 -0
  104. taguette/static/css/custom.css +1696 -0
  105. taguette/static/css/fa-all.min.css +5 -0
  106. taguette/static/css/themed.css +3 -0
  107. taguette/static/css/themed.css.map +25 -0
  108. taguette/static/css/vendor/bootstrap-treeview.min.css +1 -0
  109. taguette/static/css/vendor/bootstrap.min.css +6 -0
  110. taguette/static/css/vendor/font/context-menu-icons.eot +0 -0
  111. taguette/static/css/vendor/font/context-menu-icons.ttf +0 -0
  112. taguette/static/css/vendor/font/context-menu-icons.woff +0 -0
  113. taguette/static/css/vendor/font/context-menu-icons.woff2 +0 -0
  114. taguette/static/css/vendor/hierarchy-select.min.css +1 -0
  115. taguette/static/css/vendor/introjs.min.css +2 -0
  116. taguette/static/css/vendor/jquery.contextMenu.min.css +16 -0
  117. taguette/static/favicon.ico +0 -0
  118. taguette/static/js/codebook-viz.js +428 -0
  119. taguette/static/js/collection/context-menu.js +180 -0
  120. taguette/static/js/collection/hierarchy-select.js +71 -0
  121. taguette/static/js/collection/manager.js +217 -0
  122. taguette/static/js/document/context-menu.js +92 -0
  123. taguette/static/js/document/custom-select.js +82 -0
  124. taguette/static/js/document/manager.js +943 -0
  125. taguette/static/js/document/treeview.js +593 -0
  126. taguette/static/js/highlight/README.md +18 -0
  127. taguette/static/js/highlight/crud.js +119 -0
  128. taguette/static/js/highlight/extracts.js +497 -0
  129. taguette/static/js/highlight/loader.js +109 -0
  130. taguette/static/js/highlight/renderer.js +405 -0
  131. taguette/static/js/login.js +15 -0
  132. taguette/static/js/permissions.js +153 -0
  133. taguette/static/js/polling-event.js +145 -0
  134. taguette/static/js/project/intro.js +524 -0
  135. taguette/static/js/project/manager.js +0 -0
  136. taguette/static/js/project/search.js +124 -0
  137. taguette/static/js/tag/context-menu.js +133 -0
  138. taguette/static/js/tag/explore-codes.js +400 -0
  139. taguette/static/js/tag/hierarchy-select.js +69 -0
  140. taguette/static/js/tag/manager.js +545 -0
  141. taguette/static/js/tag/treeview.js +613 -0
  142. taguette/static/js/taguette.js +1790 -0
  143. taguette/static/js/utilities.js +714 -0
  144. taguette/static/js/vendor/bootstrap-treeview-custum.js +421 -0
  145. taguette/static/js/vendor/bootstrap-treeview-custum.min.js +1 -0
  146. taguette/static/js/vendor/bootstrap-treeview-default.min.js +1 -0
  147. taguette/static/js/vendor/bootstrap.bundle.min.js +7 -0
  148. taguette/static/js/vendor/bootstrap.bundle.min.js.map +1 -0
  149. taguette/static/js/vendor/hierarchy-select.min.js +1 -0
  150. taguette/static/js/vendor/introjs.min.js +17 -0
  151. taguette/static/js/vendor/jquery-3.7.1.js +2 -0
  152. taguette/static/js/vendor/jquery.contextMenu.min.js +2 -0
  153. taguette/static/libreqda.ico +0 -0
  154. taguette/static/logo-h.png +0 -0
  155. taguette/static/logo.png +0 -0
  156. taguette/static/logo_blanc.png +0 -0
  157. taguette/static/missing.png +0 -0
  158. taguette/static/robots.txt +2 -0
  159. taguette/static/scss/components/_badge.scss +42 -0
  160. taguette/static/scss/components/_button.scss +169 -0
  161. taguette/static/scss/components/_checkbox.scss +40 -0
  162. taguette/static/scss/components/_context_menu.scss +28 -0
  163. taguette/static/scss/components/_custom_select.scss +150 -0
  164. taguette/static/scss/components/_dropdown.scss +43 -0
  165. taguette/static/scss/components/_form.scss +54 -0
  166. taguette/static/scss/components/_modal.scss +57 -0
  167. taguette/static/scss/components/_project_card.scss +163 -0
  168. taguette/static/scss/components/_tag.scss +308 -0
  169. taguette/static/scss/components/_treeview.scss +76 -0
  170. taguette/static/scss/custom.scss +809 -0
  171. taguette/static/scss/layout/_credits.scss +62 -0
  172. taguette/static/scss/layout/_footer.scss +6 -0
  173. taguette/static/scss/layout/_navigation.scss +165 -0
  174. taguette/static/scss/layout/_sidebar.scss +37 -0
  175. taguette/static/scss/themes/default/_variables.scss +103 -0
  176. taguette/static/scss/themes/default/theme.css +3 -0
  177. taguette/static/scss/themes/default/theme.css.map +26 -0
  178. taguette/static/scss/themes/default/theme.scss +4 -0
  179. taguette/static/webfonts/BadScript-OFL.txt +94 -0
  180. taguette/static/webfonts/BadScript.ttf +0 -0
  181. taguette/static/webfonts/Montserrat-Light.ttf +0 -0
  182. taguette/static/webfonts/Montserrat-OFL.txt +93 -0
  183. taguette/static/webfonts/fa-brands-400.eot +0 -0
  184. taguette/static/webfonts/fa-brands-400.svg +1184 -0
  185. taguette/static/webfonts/fa-brands-400.ttf +0 -0
  186. taguette/static/webfonts/fa-brands-400.woff +0 -0
  187. taguette/static/webfonts/fa-brands-400.woff2 +0 -0
  188. taguette/static/webfonts/fa-regular-400.eot +0 -0
  189. taguette/static/webfonts/fa-regular-400.svg +467 -0
  190. taguette/static/webfonts/fa-regular-400.ttf +0 -0
  191. taguette/static/webfonts/fa-regular-400.woff +0 -0
  192. taguette/static/webfonts/fa-regular-400.woff2 +0 -0
  193. taguette/static/webfonts/fa-solid-900.eot +0 -0
  194. taguette/static/webfonts/fa-solid-900.svg +2618 -0
  195. taguette/static/webfonts/fa-solid-900.ttf +0 -0
  196. taguette/static/webfonts/fa-solid-900.woff +0 -0
  197. taguette/static/webfonts/fa-solid-900.woff2 +0 -0
  198. taguette/templates/alt_templates/.gitkeep +0 -0
  199. taguette/templates/archived_projects.html +50 -0
  200. taguette/templates/auth/account.html +74 -0
  201. taguette/templates/auth/cilogon.login.html +89 -0
  202. taguette/templates/auth/email_reset_password.html +5 -0
  203. taguette/templates/auth/external_account.html +60 -0
  204. taguette/templates/auth/login.html +144 -0
  205. taguette/templates/auth/reset_password.html +27 -0
  206. taguette/templates/auth/token_needed.html +7 -0
  207. taguette/templates/base.html +209 -0
  208. taguette/templates/bug_report.html +158 -0
  209. taguette/templates/cookies_prompt.html +15 -0
  210. taguette/templates/downloads.html +127 -0
  211. taguette/templates/email_bug_report.txt +9 -0
  212. taguette/templates/email_reset_password.txt +5 -0
  213. taguette/templates/error.html +8 -0
  214. taguette/templates/export_codebook.html +20 -0
  215. taguette/templates/export_document.html +20 -0
  216. taguette/templates/export_highlights.html +47 -0
  217. taguette/templates/index.html +347 -0
  218. taguette/templates/maintenance.html +8 -0
  219. taguette/templates/messages.js +74 -0
  220. taguette/templates/modal/change_document.html +68 -0
  221. taguette/templates/modal/create_collection.html +100 -0
  222. taguette/templates/modal/create_document.html +84 -0
  223. taguette/templates/modal/create_highlight.html +38 -0
  224. taguette/templates/modal/create_tag.html +73 -0
  225. taguette/templates/modal/explore_codes.html +58 -0
  226. taguette/templates/modal/manage_collaborators.html +44 -0
  227. taguette/templates/modal/merge_tag.html +49 -0
  228. taguette/templates/new_password.html +32 -0
  229. taguette/templates/partials/alert.html +16 -0
  230. taguette/templates/partials/codebook_viz_modal.html +34 -0
  231. taguette/templates/partials/confirm.html +17 -0
  232. taguette/templates/partials/document_toolbar.html +37 -0
  233. taguette/templates/partials/hlinfo.html +13 -0
  234. taguette/templates/partials/project_card.html +44 -0
  235. taguette/templates/partials/project_card_archived.html +41 -0
  236. taguette/templates/partials/project_sidebar.html +99 -0
  237. taguette/templates/partials/spinner.html +11 -0
  238. taguette/templates/permissions.html +54 -0
  239. taguette/templates/project.html +288 -0
  240. taguette/templates/project_delete.html +17 -0
  241. taguette/templates/project_import.html +122 -0
  242. taguette/templates/project_import_codebook.html +34 -0
  243. taguette/templates/project_import_codebook_confirm.html +49 -0
  244. taguette/templates/project_new.html +31 -0
  245. taguette/templates/tos.html +8 -0
  246. taguette/templates/trans.js +7 -0
  247. taguette/templates/welcome.html +15 -0
  248. taguette/utils.py +177 -0
  249. taguette/validate.py +141 -0
  250. taguette/version_check.py +102 -0
  251. taguette/web/__init__.py +252 -0
  252. taguette/web/api.py +1835 -0
  253. taguette/web/base.py +864 -0
  254. taguette/web/export.py +338 -0
  255. taguette/web/views.py +1618 -0
@@ -0,0 +1,145 @@
1
+ /*
2
+ * Long polling
3
+ */
4
+
5
+ let lastPoll = null;
6
+ let polling = true;
7
+ let windowLastActive = new Date();
8
+
9
+ if (document.hasFocus()) {
10
+ windowLastActive = null; // Focused now
11
+ }
12
+
13
+ window.addEventListener('focus', function () {
14
+ // We are active as long as we have the focus
15
+ windowLastActive = null;
16
+ maybeResumePolling();
17
+ });
18
+ window.addEventListener('mousemove', function () {
19
+ if (windowLastActive !== null) {
20
+ // If the mouse moved over the window, and we're not focused, refresh timer
21
+ windowLastActive = new Date();
22
+ maybeResumePolling();
23
+ }
24
+ });
25
+ window.addEventListener('blur', function () {
26
+ // We lost focus, start timer
27
+ windowLastActive = new Date();
28
+ });
29
+
30
+ function maybeResumePolling() {
31
+ if (!polling) {
32
+ longPollForEvents();
33
+ }
34
+ }
35
+
36
+ function longPollForEvents() {
37
+ // If we've been inactive for 10min, pause polling for now
38
+ if (windowLastActive !== null && (new Date() - windowLastActive > 600000)) {
39
+ log("Browser window inactive, stop polling");
40
+ polling = false;
41
+ return;
42
+ }
43
+
44
+ polling = true;
45
+ lastPoll = Date.now();
46
+ getJSON(
47
+ `/api/project/${project_id}/events`,
48
+ {version: version, from: last_event}
49
+ )
50
+ .then(function (result) {
51
+ if (result.reload) {
52
+ log("Server sent signal to reload");
53
+ window.location.reload();
54
+ return;
55
+ }
56
+ for (let i = 0; i < result.events.length; ++i) {
57
+ const event = result.events[i];
58
+ if (event.type === 'project_meta') {
59
+ setProjectMetadata({
60
+ project_name: event.project_name,
61
+ description: event.description
62
+ });
63
+ } else if (event.type === 'document_add') {
64
+ addDocument({
65
+ id: event.document_id,
66
+ name: event.document_name,
67
+ description: event.description,
68
+ favorite: event.favorite,
69
+ text_direction: event.text_direction,
70
+ collections: event.collections
71
+ });
72
+ } else if (event.type === 'document_delete') {
73
+ deleteDocument(event.document_id);
74
+ } else if (event.type === 'highlight_add') {
75
+ addHighlight({
76
+ id: event.highlight_id,
77
+ start_offset: event.start_offset,
78
+ end_offset: event.end_offset,
79
+ snippet: event.snippet,
80
+ tags: event.tags
81
+ }, event.document_id);
82
+ } else if (event.type === 'highlight_delete') {
83
+ removeHighlight(event.highlight_id, event.document_id);
84
+ } else if (event.type === 'tag_add') {
85
+ addTag({
86
+ id: event.tag_id,
87
+ path: event.tag_path,
88
+ description: event.description,
89
+ color: event.tag_color,
90
+ parent: event.tag_parent
91
+ });
92
+ } else if (event.type === 'tag_delete') {
93
+ removeTag(event.tag_id);
94
+ } else if (event.type === 'tag_merge') {
95
+ mergeTags(event.src_tag_id, event.dest_tag_id);
96
+ } else if (event.type === 'member_add') {
97
+ addMember(event.member, event.privileges);
98
+ } else if (event.type === 'member_remove') {
99
+ removeMember(event.member);
100
+ } else if (event.type === 'collection_add') {
101
+ addCollection({
102
+ id: event.collection_id,
103
+ name: event.collection_name,
104
+ description: event.description,
105
+ parent_id: event.parent_id,
106
+ document_ids: event.document_ids
107
+ });
108
+ } else if (event.type === 'collection_delete') {
109
+ removeCollection(event.collection_id);
110
+ } else if (event.type === 'collection_update') {
111
+ updateCollection({
112
+ id: event.collection_id,
113
+ name: event.collection_name,
114
+ description: event.description,
115
+ parent_id: event.parent_id,
116
+ document_ids: event.document_ids
117
+ });
118
+ }
119
+
120
+ if ('tag_count_changes' in event) {
121
+ updateTagCount(event.tag_count_changes)
122
+ }
123
+ last_event = event.id;
124
+ }
125
+
126
+ // Re-open connection
127
+ setTimeout(longPollForEvents, 1);
128
+ }, function (error) {
129
+ log("Polling failed", 'error', {error})
130
+ if (error instanceof ApiError && error.status === 403) {
131
+ showAlert(gettext("It appears that you have been logged out."));
132
+ window.location = '/';
133
+ } else if (error instanceof ApiError && error.status === 404) {
134
+ showAlert(gettext("You can no longer access this project."));
135
+ window.location = '/';
136
+ } else {
137
+ setTimeout(longPollForEvents, Math.max(1, 5000 + lastPoll - Date.now()));
138
+ }
139
+ })
140
+ .catch(function (error) {
141
+ log("Polling function error", 'error', {error})
142
+ });
143
+ }
144
+
145
+ longPollForEvents();
@@ -0,0 +1,524 @@
1
+ if((demo_highlights_location.length < 3)) {
2
+ demo_highlights_location = [[],[],[]]; //Default empty value
3
+ }
4
+
5
+ $(document).ready(function() {
6
+ let demoElement = null,
7
+ introjs_selection = null,
8
+ current_step = 0,
9
+ home_intro = introJs().setOptions({
10
+ exitOnOverlayClick: true,
11
+ exitOnEsc: true,
12
+ steps: [
13
+ {
14
+ title: gettext('Welcome to LibreQDA, a qualitative data analysis software (QDA) developed by the Plateforme en humanités numériques (PHuN) at Université de Sherbrooke.'),
15
+ intro: gettext('<p>Visit our <a href="https://gitlab.com/bin-cirst/libreqda/libre-qda" target="_blank">Gitlab repository</a></p>'),
16
+ },
17
+ {
18
+ element: document.querySelector('#intro-documentation'),
19
+ intro: gettext('<p>To consult the documentation</p>'),
20
+ position: 'bottom'
21
+ },
22
+ {
23
+ element: $('#demo-project-container .col:first')[0],
24
+ intro: gettext('<p>A demo project has been added to your home page.</p>'),
25
+ position: 'top',
26
+ doAction: 'create-demo-project'
27
+ },
28
+ ]
29
+ }).
30
+ setOption('disableInteraction', true).
31
+ setOption("nextLabel", gettext('Next')).
32
+ setOption("prevLabel", gettext('Back')).
33
+ setOption("doneLabel", gettext('Continue')).
34
+ onbeforechange(function (targetElement) {
35
+ if(this._introItems[this._currentStep].doAction) {
36
+ switch (this._introItems[this._currentStep].doAction) {
37
+ case 'create-demo-project':
38
+ if(demoElement === null) {
39
+ createDemoProject();
40
+ }
41
+ break;
42
+ }
43
+ }
44
+ }).
45
+ onafterchange(function(targetElement) {
46
+ if(this._introItems[this._currentStep].doAction) {
47
+ switch (this._introItems[this._currentStep].doAction) {
48
+ case 'create-demo-project':
49
+ setTimeout(function(){
50
+ repositionIntroJS('right');
51
+ }, 500);
52
+ break;
53
+ }
54
+ }
55
+ }).
56
+ oncomplete(function () {
57
+ if(demoElement !== null) {
58
+ demoElement.trigger('click');
59
+ }
60
+ }),
61
+ project_intro = introJs().setOptions({
62
+ showProgress: true,
63
+ exitOnOverlayClick: true,
64
+ exitOnEsc: true,
65
+ hidePrev: true,
66
+ keyboardNavigation: false,
67
+ showBullets: false,
68
+ steps: [
69
+ {
70
+ element: document.querySelector("#right-panel"),
71
+ title: gettext('Your documents'),
72
+ intro: gettext('<p>Use this tab to load files and go through them.</p>'),
73
+ position: 'right',
74
+ },
75
+ { // #1
76
+ element: document.querySelector("#left-panel"),
77
+ title: gettext('Codebook'),
78
+ intro: gettext('<p>This tab lists all the codes that have been defined. Add new codes with the button dedicated to that purpose.</p><br><p>The total number of uses is indicated by the number on the right, and the number of documents on the left.</p>'),
79
+ position: 'right'
80
+ },
81
+ { // #2
82
+ element: document.querySelector("#treeview-tags"),
83
+ title: gettext('Parent codes'),
84
+ intro: gettext('<p>You can define codes that include child codes under them.</p>'),
85
+ position: 'right'
86
+ },
87
+ { // #3
88
+ element: $("[data-nodeid='1']")[0],
89
+ title: gettext('Child codes'),
90
+ intro: gettext('<p>These can be expanded or collapsed.</p>'),
91
+ position: 'right',
92
+ doAction: 'expand-tag-childrens'
93
+ },
94
+ { // #4
95
+ element: $(".document-link:first")[0],
96
+ title: gettext('Consulting documents'),
97
+ intro: gettext('<p>Click on a document to view its contents. Use the star to save your bookmarks.</p>'),
98
+ position: 'left',
99
+ doAction: 'expand-first-collection'
100
+ },
101
+ { // #5
102
+ element: document.querySelector("#document-highlight"), // This element doesn't exist
103
+ title: gettext('Coding'),
104
+ intro: gettext('<p>Select a portion of text with your cursor. Two buttons will appear to assign a code to the selection.</p>'),
105
+ position: 'bottom',
106
+ highlight: (typeof(demo_highlights_location[0][0]) !== 'undefined') ? demo_highlights_location[0][0] : [0,0],
107
+ nodes: [null, null],
108
+ highlight_context: [0, 0],
109
+ doAction: 'document-highlight' // Action to create wrapper for introjs
110
+ },
111
+ { // #6
112
+ element: document.querySelector("#highlight-add-form"),
113
+ title: gettext("Selecting a code"),
114
+ intro: gettext('<p>From the menu, select one or more codes that correspond to the content of the selection and save the changes. In this case, the selection refers to reinforcing social relationships.</p>'),
115
+ position: 'left',
116
+ tagPosition: [3],
117
+ doAction: 'document-selecttag' // Action to create wrapper for introjs
118
+ },
119
+ { //#14
120
+ element: document.querySelector("#left-panel"),
121
+ title: gettext('Exploring the codes'),
122
+ intro: gettext('<p>You can explore the text selections tagged with each code at any time during or after the coding process.</p><br><p> Show all of the selections tagged with a code from the codebook by clicking on it.</p>'),
123
+ position: 'right',
124
+ },
125
+ { //#15
126
+ element: document.querySelector("#document-view"),
127
+ title: gettext('Narrowing your search'),
128
+ intro: gettext('<p>If there are a large number of selections shown, you can refine the results with an advanced search.</p>'),
129
+ position: 'left',
130
+ doAction: 'exploration-mode'
131
+ },
132
+ { //#16
133
+ element: document.querySelector(".context-menu-root"),
134
+ title: gettext('Conditional selections'),
135
+ intro: gettext('<p>By right-clicking, you can specify conditions to either add or exclude additional codes.</p>'),
136
+ position: 'left',
137
+ which: 2,
138
+ doAction: 'select-condition'
139
+ },
140
+ { //#17
141
+ element: document.querySelector("#document-view"),
142
+ title: gettext('Exploring the list'),
143
+ intro: gettext('<p>The labels shown with this query indicate that the selections must necessarily include the codes <i>barriers</i> and <i>skills development</i>.</p>'),
144
+ position: 'left',
145
+ doAction: 'apply-condition'
146
+ },
147
+ { //#18
148
+ element: $('#document-contents .highlight-entry:first .badge')[0],
149
+ title: gettext('Navigating to documents'),
150
+ intro: gettext('<p>By clicking on the name of the document, you can return to the context.</p>'),
151
+ position: 'top',
152
+ doAction: 'select-badge'
153
+ },
154
+ { //#19
155
+ element: document.querySelector('#tag-selection-return'),
156
+ title: gettext('Returning to the selections'),
157
+ intro: gettext('<p>After consulting the context, you can return to the list of selections previously identified using the advanced search.</p>'),
158
+ position: 'right',
159
+ doAction: 'reposition-after-scroll'
160
+ },
161
+ { //#20
162
+ element: document.querySelector('#document-dropdown-export'),
163
+ title: gettext('Exporting'),
164
+ intro: gettext('<p>You can choose to download these selections to your computer, as needed.</p>'),
165
+ position: 'left',
166
+ },
167
+ { //#22
168
+ title: gettext('Time to code!'),
169
+ intro: gettext('<p>Use LibreQDA however you wish, knowing that your data is always saved to your personal account. Feel free to further familiarize yourself with these functions by continuing to code and explore these fictional interviews about board games.</p>'),
170
+ position: 'left',
171
+ doAction: 'last'
172
+ },
173
+ ]
174
+ }).
175
+ setOption('disableInteraction', true).
176
+ setOption('scrollTo', 'tooltip').
177
+ setOption("nextLabel", gettext('Next')).
178
+ setOption("prevLabel", gettext('Back')).
179
+ setOption("doneLabel", gettext('Done')).
180
+ onbeforechange(function(targetElement) {
181
+ if(this._introItems[this._currentStep] &&
182
+ this._introItems[this._currentStep].doAction === 'document-highlight') {
183
+ let dh_start = this._introItems[this._currentStep].highlight[0],
184
+ dh_end = this._introItems[this._currentStep].highlight[1],
185
+ hlContainer = document.getElementById('highlight-content');
186
+
187
+ if (hlContainer && typeof collectTextNodes === 'function') {
188
+ try {
189
+ const textNodes = collectTextNodes(hlContainer);
190
+ const { fullText, charMap } = buildCharMap(textNodes);
191
+ const startChar = getCharIndexFromUTF8Index(fullText, dh_start);
192
+ const endChar = getCharIndexFromUTF8Index(fullText, dh_end);
193
+ const startEntry = charMap[Math.max(0, startChar)];
194
+ const endEntry = charMap[Math.min(charMap.length - 1, Math.max(0, endChar - 1))];
195
+
196
+ if (startEntry && endEntry) {
197
+ this._introItems[this._currentStep].nodes = [startEntry.node, endEntry.node];
198
+ this._introItems[this._currentStep].highlight_context = [startEntry.offset, endEntry.offset + 1];
199
+ const startBlock = startEntry.node.parentElement.closest('p, li, h1, h2, h3, h4, h5, h6, .document-chunk') || hlContainer;
200
+ $(startBlock).addClass('intro-js-highlight');
201
+ }
202
+ } catch(e) {
203
+ log('document-highlight action failed:', 'warn', e);
204
+ }
205
+ }
206
+
207
+ $(".intro-js-highlight").wrapAll("<div id='document-highlight' />");
208
+ this._introItems[this._currentStep].element = $("#document-highlight")[0] ||
209
+ hlContainer ||
210
+ document.getElementById('document-contents');
211
+ targetElement = this._introItems[this._currentStep].element;
212
+ }
213
+
214
+ if(targetElement) {
215
+ // Always submit previous step before continuing
216
+ if($("#highlight-add-modal").hasClass('show')) {
217
+ $("#highlight-add-form").submit();
218
+ introjs_selection = null;
219
+ }
220
+ if($("#tag-add-modal").hasClass('show')) {
221
+ $("#tag-add-form button[type='submit']").trigger('click');
222
+ }
223
+
224
+ switch(targetElement.id) {
225
+ case 'tag-selection-return':
226
+ $('#document-contents .highlight-entry:first .badge')[0].click();
227
+ this._introItems[this._currentStep].element = $("#tag-selection-return")[0];
228
+ break;
229
+
230
+ case 'document-dropdown-export':
231
+ window.scrollTo({
232
+ top: 0,
233
+ behavior: 'instant'
234
+ });
235
+ $("#tag-selection-return a").click();
236
+ break;
237
+ }
238
+ if(this._introItems[this._currentStep].doAction) {
239
+ switch(this._introItems[this._currentStep].doAction) {
240
+ case 'expand-tag-childrens':
241
+ $("[data-nodeid='1'] .expand-icon").trigger('click');
242
+ for(let i=1; i<=3; i++) {
243
+ $("[data-nodeid='" + i + "']").addClass('intro-js-tags-child')
244
+ }
245
+ $( ".intro-js-tags-child" ).wrapAll( "<div id='intro-js-wrapped' />");
246
+ // Fix duplicate elements
247
+ $("#intro-js-wrapped > li:eq(3)").remove();
248
+ // Force refresh dynamic element
249
+ this._introItems[this._currentStep].element = $("#intro-js-wrapped")[0];
250
+ break;
251
+ case 'expand-first-collection':
252
+ // Expand the first collection node to reveal documents
253
+ if(typeof treeview_documents !== 'undefined' && treeview_documents.length) {
254
+ try {
255
+ // Get the first node (should be a collection) and expand it
256
+ const allNodes = treeview_documents.treeview('getEnabled');
257
+ if(allNodes && allNodes.length > 0) {
258
+ const firstNode = allNodes[0];
259
+ if(firstNode && !firstNode.state.expanded) {
260
+ treeview_documents.treeview('expandNode', [firstNode.nodeId, {silent: false}]);
261
+ }
262
+ }
263
+ } catch(e) {
264
+ // Fallback to clicking expand icon
265
+ $("#treeview-documents .expand-icon:first").trigger('click');
266
+ }
267
+ }
268
+ // Force refresh dynamic element after expansion
269
+ this._introItems[this._currentStep].element = $(".document-link:first")[0];
270
+ const firstDocLink = $(".document-link:first");
271
+ if (firstDocLink.length) {
272
+ const introDocId = parseInt(firstDocLink.attr('data-document-id'));
273
+ if (introDocId && typeof loadDocument === 'function') {
274
+ loadDocument(introDocId);
275
+ }
276
+ }
277
+ break;
278
+
279
+ case 'document-selecttag':
280
+ let tag_position = this._introItems[this._currentStep].tagPosition;
281
+ if(typeof tag_position === "undefined") {
282
+ tag_position = [0];
283
+ }
284
+ $(window).trigger('click');
285
+ $('#highlight-add-modal').removeClass('fade');
286
+
287
+ if (!introjs_selection) {
288
+ const hlStep = this._introItems[this._currentStep - 1];
289
+ if (hlStep && hlStep.nodes && hlStep.nodes[0]) {
290
+ selectRange(hlStep.nodes, hlStep.highlight_context);
291
+ }
292
+ }
293
+ if (introjs_selection) {
294
+ createHighlight(introjs_selection);
295
+ for(let i in tag_position) {
296
+ $("#highlight-add-form li[data-nodeid='" + tag_position[i] + "']").trigger('click');
297
+ }
298
+ }
299
+
300
+ $(window).unbind('click');
301
+ break;
302
+ case 'exploration-mode':
303
+ $("[data-nodeid='0']:not(.node-selected)").click();
304
+ // Reposition
305
+ setTimeout(function() {
306
+ window.scrollTo({
307
+ top: 0,
308
+ behavior: 'instant'
309
+ });
310
+ $('.introjs-tooltipReferenceLayer').css('height', $(targetElement).outerHeight())
311
+ $('.introjs-helperLayer').css('height', $(targetElement).outerHeight())
312
+ $('.introjs-disableInteraction').css('height', $(targetElement).outerHeight())
313
+ }, 500);
314
+ break;
315
+ case 'select-condition':
316
+ let i = this._introItems[this._currentStep].which;
317
+ if(isNaN(i)) {
318
+ i = 0;
319
+ }
320
+ window.scrollTo({
321
+ top: 0,
322
+ behavior: 'instant'
323
+ });
324
+ if(!$("[data-nodeid='2']").is(':visible')) {
325
+ $("[data-nodeid='1'] .expand-icon").trigger('click');
326
+ }
327
+ $("[data-nodeid='2']").trigger( "contextmenu" );
328
+ $(".context-menu-root li:nth(" + i + ")").addClass('context-menu-hover context-menu-visible');
329
+ $(".context-menu-root li:nth(" + i + ") > ul").css('left', $(".context-menu-root").width() + 'px');
330
+ $(".context-menu-root li:nth(" + i + ") > ul > li:first").addClass('context-menu-hover context-menu-visible');
331
+ this._introItems[this._currentStep].element = $(".context-menu-root")[0];
332
+ break;
333
+ case 'apply-condition':
334
+ $(".context-menu-root li.context-menu-visible > ul > li.context-menu-hover").trigger('mouseup');
335
+ break;
336
+ case 'select-badge':
337
+ this._introItems[this._currentStep].element = $('#document-contents .highlight-entry:first .badge')[0];
338
+ break;
339
+ case 'reposition-after-scroll':
340
+ setTimeout(function() {
341
+ //Force scroll to bottom of page
342
+ window.scrollTo({
343
+ top: document.body.scrollHeight,
344
+ behavior: 'instant'
345
+ });
346
+
347
+ let elems = ['.introjs-helperLayer', '.introjs-tooltipReferenceLayer', '.introjs-disableInteraction'],
348
+ offset = $('#tag-selection-return').offset().top - $('.introjs-tooltipReferenceLayer').offset().top;
349
+
350
+ for (let i in elems) {
351
+ $(elems[i]).css('top', (parseFloat($(elems[i]).css('top')) + offset) + 'px');
352
+ }
353
+ }, 500);
354
+ break;
355
+ case 'last':
356
+ $('#project-sidebar').css('width', 0);
357
+ break;
358
+ }
359
+ }
360
+ //Scroll to top if new document to prevent position problem with introjs
361
+ if(targetElement.classList.contains('document-link')) {
362
+ window.scrollTo({
363
+ top: 0,
364
+ behavior: 'instant'
365
+ });
366
+ }
367
+ }
368
+ }).
369
+ onchange(function (targetElement) {
370
+ current_step = this._currentStep;
371
+ }).
372
+ onafterchange(function(targetElement) {
373
+ switch(targetElement.id) {
374
+ case 'document-highlight':
375
+ $(".intro-js-highlight").unwrap().removeClass('intro-js-highlight');
376
+ let node_start = this._introItems[this._currentStep].nodes[0],
377
+ node_end = this._introItems[this._currentStep].nodes[1],
378
+ text_start = this._introItems[this._currentStep].highlight_context[0],
379
+ text_end = this._introItems[this._currentStep].highlight_context[1];
380
+ console.log([[node_start, node_end], [text_start, text_end]])
381
+ setTimeout(function(){
382
+ selectRange([node_start, node_end], [text_start, text_end]);
383
+ repositionIntroJS('top');
384
+ }, 500);
385
+ //Force reselection of text is unselected
386
+ $(window).bind('click', function(e) {
387
+ selectRange([node_start, node_end], [text_start, text_end]);
388
+ });
389
+ break;
390
+
391
+ case 'document-dropdown-export':
392
+ setTimeout(function(){
393
+ $("#document-dropdown-export").click()
394
+ $('.introjs-helperLayer').css('width', $('#document-export-dropdown-menu').outerWidth() + 10)
395
+ $('.introjs-helperLayer').css('height', $('#document-export-dropdown-menu').outerHeight() + $('.introjs-helperLayer').outerHeight())
396
+ $('.introjs-disableInteraction').css('width', $('#document-export-dropdown-menu').outerWidth() + 10)
397
+ $('.introjs-disableInteraction').css('height', $('#document-export-dropdown-menu').outerHeight() + $('.introjs-helperLayer').outerHeight())
398
+ }, 500);
399
+ break;
400
+ }
401
+ if(this._introItems[this._currentStep].doAction) {
402
+ switch(this._introItems[this._currentStep].doAction) {
403
+ case 'expand-first-collection':
404
+ setTimeout(function(){
405
+ repositionIntroJS('left');
406
+ }, 300);
407
+ break;
408
+ case 'select-condition':
409
+ setTimeout(function(){
410
+ repositionIntroJS('right', 275);
411
+ }, 500);
412
+ break;
413
+ case 'select-badge':
414
+ setTimeout(function(){
415
+ repositionIntroJS('right', 15);
416
+ }, 500);
417
+ break;
418
+ }
419
+ }
420
+ }).
421
+ oncomplete(function (){
422
+ $("#start-tutorial").remove();
423
+ }).
424
+ onexit(function () {
425
+ is_demo = true;
426
+ //Make sure everything is back as it was
427
+ $( ".intro-js-tags-child" ).unwrap();
428
+ $( ".intro-js-highlight" ).unwrap();
429
+ $( ".document-highlight" ).unwrap();
430
+ $(window).unbind('click');
431
+ });
432
+
433
+ function createDemoProject() {
434
+ log('Create demo project');
435
+ let postData = {'_xsrf': getCookie('_xsrf')};
436
+ postJSON('/api/create_demo_project', postData)
437
+ .then(function (result) {
438
+ if (result.html) {
439
+ $('#demo-project-container').prepend(result.html);
440
+ demoElement = $('#demo-project-container .project:first .project-card');
441
+ $('#demo-project-container .project.empty').remove();
442
+ }
443
+ })
444
+ }
445
+
446
+ let repositionIntroJS = (where='top', offset=0) => {
447
+ //Remove position classes from tooltip
448
+ let cls = $('.introjs-arrow').attr("class").split(/\s+/);
449
+ for(let c in cls) {
450
+ if(cls[c] !== 'introjs-arrow') {
451
+ $('.introjs-arrow').removeClass(cls[c]);
452
+ }
453
+ }
454
+ cls = $('.introjs-tooltip').attr("class").split(/\s+/);
455
+ for(let c in cls) {
456
+ if(cls[c] !== 'introjs-tooltip') {
457
+ $('.introjs-tooltip').removeClass(cls[c]);
458
+ }
459
+ }
460
+
461
+ //set new position
462
+ switch(where) {
463
+ case 'top':
464
+ let newtop = -$('.introjs-tooltip').outerHeight() + offset;
465
+ $('.introjs-tooltip')
466
+ .addClass('introjs-top-middle-aligned')
467
+ .css('top', newtop + 'px')
468
+ .css('margin-top', '-10px');
469
+ $('.introjs-arrow').css('display', 'inherit').addClass('bottom-middle');
470
+ break;
471
+ case 'left':
472
+ $('.introjs-tooltip')
473
+ .addClass('introjs-left-middle-aligned')
474
+ .css('left', -$('.introjs-tooltip').outerWidth() + 'px')
475
+ .css('margin-right', -offset + 'px');
476
+ $('.introjs-arrow').css('display', 'inherit').addClass('right');
477
+ break;
478
+ case 'right':
479
+ $('.introjs-tooltip')
480
+ .addClass('introjs-right-middle-aligned')
481
+ .css('left', $('.introjs-helperLayer').outerWidth() + 'px')
482
+ .css('margin-left', offset + 'px');
483
+ $('.introjs-arrow').css('display', 'inherit').addClass('left');
484
+ $('.introjs-disableInteraction').css('width', ($('.introjs-tooltip').offset().left - $('.introjs-disableInteraction').offset().left) + 'px');
485
+ break;
486
+ }
487
+ },
488
+ showHomeIntro = () => {
489
+ home_intro.start();
490
+ },
491
+ selectRange = (nodes, positions) => {
492
+ let range=document.createRange(),
493
+ selection = window.getSelection();
494
+ range.setStart(nodes[0], positions[0]);
495
+ range.setEnd(nodes[1], positions[1]);
496
+ selection.removeAllRanges();
497
+ selection.addRange(range);
498
+
499
+ introjs_selection = describeSelection();
500
+ },
501
+ showProjectIntro = (step=1) => {
502
+ project_intro.start();
503
+ if(step && !isNaN(step) && step > 0) {
504
+ log(`IntroJS : Go to step: ${step}`)
505
+ project_intro.goToStepNumber(step);
506
+ }
507
+ }
508
+
509
+ $("#a-start-demo").click(function(){
510
+ showHomeIntro();
511
+ });
512
+
513
+ $("#start-tutorial").click(function(){
514
+ showProjectIntro(current_step+1);
515
+ });
516
+
517
+ if($("#a-start-demo").length && is_demo) {
518
+ // In index.html template
519
+ showHomeIntro();
520
+ } else if(is_demo) {
521
+ // In project.html template
522
+ showProjectIntro();
523
+ }
524
+ });
File without changes