@qtoggle/qui 0.0.0

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 (202) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +492 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. package/.github/ISSUE_TEMPLATE/improvement_proposal.md +20 -0
  6. package/.github/workflows/main.yml +74 -0
  7. package/.pre-commit-config.yaml +8 -0
  8. package/LICENSE.txt +177 -0
  9. package/README.md +4 -0
  10. package/font/dejavusans-bold.woff +0 -0
  11. package/font/dejavusans-bolditalic.woff +0 -0
  12. package/font/dejavusans-italic.woff +0 -0
  13. package/font/dejavusans-regular.woff +0 -0
  14. package/img/qui-icons.svg +1937 -0
  15. package/js/base/base.js +47 -0
  16. package/js/base/condition-variable.js +92 -0
  17. package/js/base/errors.js +36 -0
  18. package/js/base/i18n.js +20 -0
  19. package/js/base/mixwith.js +135 -0
  20. package/js/base/require-js-compat.js +78 -0
  21. package/js/base/signal.js +91 -0
  22. package/js/base/singleton.js +66 -0
  23. package/js/base/timer.js +126 -0
  24. package/js/config.js +184 -0
  25. package/js/forms/common-fields/check-field.js +42 -0
  26. package/js/forms/common-fields/choice-buttons-field.js +30 -0
  27. package/js/forms/common-fields/color-combo-field.js +37 -0
  28. package/js/forms/common-fields/combo-field.js +108 -0
  29. package/js/forms/common-fields/common-fields.js +23 -0
  30. package/js/forms/common-fields/composite-field.js +132 -0
  31. package/js/forms/common-fields/custom-html-field.js +51 -0
  32. package/js/forms/common-fields/email-field.js +30 -0
  33. package/js/forms/common-fields/file-picker-field.js +46 -0
  34. package/js/forms/common-fields/jquery-ui-field.js +111 -0
  35. package/js/forms/common-fields/labels-field.js +69 -0
  36. package/js/forms/common-fields/numeric-field.js +39 -0
  37. package/js/forms/common-fields/password-field.js +28 -0
  38. package/js/forms/common-fields/phone-field.js +26 -0
  39. package/js/forms/common-fields/progress-disk-field.js +69 -0
  40. package/js/forms/common-fields/push-button-field.js +138 -0
  41. package/js/forms/common-fields/slider-field.js +51 -0
  42. package/js/forms/common-fields/text-area-field.js +34 -0
  43. package/js/forms/common-fields/text-field.js +89 -0
  44. package/js/forms/common-fields/up-down-field.js +85 -0
  45. package/js/forms/common-forms/common-forms.js +16 -0
  46. package/js/forms/common-forms/options-form.js +77 -0
  47. package/js/forms/common-forms/page-form.js +115 -0
  48. package/js/forms/form-button.js +202 -0
  49. package/js/forms/form-field.js +1183 -0
  50. package/js/forms/form.js +1181 -0
  51. package/js/forms/forms.js +68 -0
  52. package/js/global-glass.js +100 -0
  53. package/js/icons/default-stock.js +173 -0
  54. package/js/icons/icon.js +64 -0
  55. package/js/icons/icons.js +16 -0
  56. package/js/icons/multi-state-sprites-icon.js +362 -0
  57. package/js/icons/stock-icon.js +219 -0
  58. package/js/icons/stock.js +98 -0
  59. package/js/icons/stocks.js +57 -0
  60. package/js/index.js +232 -0
  61. package/js/lib/jquery.longpress.js +79 -0
  62. package/js/lib/jquery.module.js +4 -0
  63. package/js/lib/logger.module.js +4 -0
  64. package/js/lib/pep.module.js +4 -0
  65. package/js/lists/common-items/common-items.js +5 -0
  66. package/js/lists/common-items/icon-label-list-item.js +86 -0
  67. package/js/lists/common-lists/common-lists.js +5 -0
  68. package/js/lists/common-lists/page-list.js +53 -0
  69. package/js/lists/list-item.js +147 -0
  70. package/js/lists/list.js +636 -0
  71. package/js/lists/lists.js +26 -0
  72. package/js/main-ui/main-ui.js +64 -0
  73. package/js/main-ui/menu-bar.js +144 -0
  74. package/js/main-ui/options-bar.js +181 -0
  75. package/js/main-ui/status.js +185 -0
  76. package/js/main-ui/top-bar.js +59 -0
  77. package/js/messages/common-message-forms/common-message-forms.js +7 -0
  78. package/js/messages/common-message-forms/confirm-message-form.js +81 -0
  79. package/js/messages/common-message-forms/simple-message-form.js +67 -0
  80. package/js/messages/common-message-forms/sticky-simple-message-form.js +27 -0
  81. package/js/messages/message-form.js +107 -0
  82. package/js/messages/messages.js +21 -0
  83. package/js/messages/sticky-modal-page.js +98 -0
  84. package/js/messages/sticky-modal-progress-message.js +27 -0
  85. package/js/messages/toast.js +164 -0
  86. package/js/navigation.js +654 -0
  87. package/js/pages/breadcrumbs.js +124 -0
  88. package/js/pages/common-pages/common-pages.js +6 -0
  89. package/js/pages/common-pages/modal-progress-page.js +83 -0
  90. package/js/pages/common-pages/structured-page.js +46 -0
  91. package/js/pages/page.js +1018 -0
  92. package/js/pages/pages-context.js +154 -0
  93. package/js/pages/pages.js +252 -0
  94. package/js/pwa.js +337 -0
  95. package/js/sections/section.js +612 -0
  96. package/js/sections/sections.js +300 -0
  97. package/js/tables/common-cells/common-cells.js +7 -0
  98. package/js/tables/common-cells/icon-label-table-cell.js +68 -0
  99. package/js/tables/common-cells/push-button-table-cell.js +133 -0
  100. package/js/tables/common-cells/simple-table-cell.js +37 -0
  101. package/js/tables/common-tables/common-tables.js +5 -0
  102. package/js/tables/common-tables/page-table.js +55 -0
  103. package/js/tables/table-cell.js +198 -0
  104. package/js/tables/table-row.js +126 -0
  105. package/js/tables/table.js +492 -0
  106. package/js/tables/tables.js +36 -0
  107. package/js/theme.js +304 -0
  108. package/js/utils/ajax.js +126 -0
  109. package/js/utils/array.js +194 -0
  110. package/js/utils/colors.js +445 -0
  111. package/js/utils/cookies.js +65 -0
  112. package/js/utils/crypto.js +439 -0
  113. package/js/utils/css.js +234 -0
  114. package/js/utils/date.js +300 -0
  115. package/js/utils/files.js +27 -0
  116. package/js/utils/gestures.js +165 -0
  117. package/js/utils/html.js +76 -0
  118. package/js/utils/misc.js +81 -0
  119. package/js/utils/object.js +324 -0
  120. package/js/utils/promise.js +49 -0
  121. package/js/utils/string.js +192 -0
  122. package/js/utils/url.js +187 -0
  123. package/js/utils/utils.js +3 -0
  124. package/js/utils/visibility-manager.js +211 -0
  125. package/js/views/common-views/common-views.js +7 -0
  126. package/js/views/common-views/icon-label-view.js +210 -0
  127. package/js/views/common-views/progress-view.js +89 -0
  128. package/js/views/common-views/structured-view.js +368 -0
  129. package/js/views/view.js +467 -0
  130. package/js/views/views.js +3 -0
  131. package/js/widgets/base-widget.js +23 -0
  132. package/js/widgets/common-widgets/check-button.js +109 -0
  133. package/js/widgets/common-widgets/choice-buttons.js +322 -0
  134. package/js/widgets/common-widgets/color-combo.js +104 -0
  135. package/js/widgets/common-widgets/combo.js +645 -0
  136. package/js/widgets/common-widgets/common-widgets.js +17 -0
  137. package/js/widgets/common-widgets/email-input.js +7 -0
  138. package/js/widgets/common-widgets/file-picker.js +133 -0
  139. package/js/widgets/common-widgets/labels.js +132 -0
  140. package/js/widgets/common-widgets/numeric-input.js +49 -0
  141. package/js/widgets/common-widgets/password-input.js +91 -0
  142. package/js/widgets/common-widgets/phone-input.js +7 -0
  143. package/js/widgets/common-widgets/progress-disk.js +174 -0
  144. package/js/widgets/common-widgets/push-button.js +155 -0
  145. package/js/widgets/common-widgets/slider.js +455 -0
  146. package/js/widgets/common-widgets/text-area.js +52 -0
  147. package/js/widgets/common-widgets/text-input.js +174 -0
  148. package/js/widgets/common-widgets/up-down.js +351 -0
  149. package/js/widgets/widgets.js +57 -0
  150. package/js/window.js +557 -0
  151. package/jsdoc.conf.json +20 -0
  152. package/less/base.less +123 -0
  153. package/less/forms/common-fields.less +101 -0
  154. package/less/forms/common-forms.less +5 -0
  155. package/less/forms/form-button.less +21 -0
  156. package/less/forms/form-field.less +266 -0
  157. package/less/forms/form.less +131 -0
  158. package/less/global-glass.less +64 -0
  159. package/less/icon-label-view.less +82 -0
  160. package/less/icons.less +144 -0
  161. package/less/lists.less +105 -0
  162. package/less/main-ui.less +328 -0
  163. package/less/messages.less +189 -0
  164. package/less/no-effects.less +24 -0
  165. package/less/pages/breadcrumbs.less +98 -0
  166. package/less/pages/common-pages.less +36 -0
  167. package/less/pages/page.less +70 -0
  168. package/less/progress-view.less +51 -0
  169. package/less/stock-icons.less +43 -0
  170. package/less/structured-view.less +245 -0
  171. package/less/tables.less +84 -0
  172. package/less/theme-dark.less +133 -0
  173. package/less/theme-light.less +132 -0
  174. package/less/theme.less +419 -0
  175. package/less/visibility-manager.less +11 -0
  176. package/less/widgets/check-button.less +96 -0
  177. package/less/widgets/choice-buttons.less +160 -0
  178. package/less/widgets/color-combo.less +33 -0
  179. package/less/widgets/combo.less +230 -0
  180. package/less/widgets/common-buttons.less +120 -0
  181. package/less/widgets/common.less +24 -0
  182. package/less/widgets/input.less +258 -0
  183. package/less/widgets/labels.less +81 -0
  184. package/less/widgets/progress-disk.less +70 -0
  185. package/less/widgets/slider.less +199 -0
  186. package/less/widgets/updown.less +115 -0
  187. package/less/widgets/various.less +36 -0
  188. package/package.json +47 -0
  189. package/pyproject.toml +45 -0
  190. package/qui/__init__.py +110 -0
  191. package/qui/constants.py +1 -0
  192. package/qui/exceptions.py +2 -0
  193. package/qui/j2template.py +71 -0
  194. package/qui/settings.py +60 -0
  195. package/qui/templates/manifest.json +25 -0
  196. package/qui/templates/qui.html +126 -0
  197. package/qui/templates/service-worker.js +188 -0
  198. package/qui/web/__init__.py +0 -0
  199. package/qui/web/tornado.py +220 -0
  200. package/scripts/postinstall.sh +10 -0
  201. package/webpack/webpack-adjust-css-urls-loader.js +36 -0
  202. package/webpack/webpack-common.js +384 -0
package/js/window.js ADDED
@@ -0,0 +1,557 @@
1
+ /**
2
+ * @namespace qui.window
3
+ */
4
+
5
+ import $ from '$qui/lib/jquery.module.js'
6
+ import Logger from '$qui/lib/logger.module.js'
7
+
8
+ import Signal from '$qui/base/signal.js'
9
+ import Config from '$qui/config.js'
10
+ import * as AJAX from '$qui/utils/ajax.js'
11
+ import {asap} from '$qui/utils/misc.js'
12
+
13
+
14
+ const logger = Logger.get('qui.window')
15
+
16
+ let unloading = false
17
+ let reloading = false
18
+ let smallScreenThreshold = null
19
+ let scalingFactor = null
20
+ let appActive = false
21
+ let appFocused = false
22
+
23
+
24
+ /**
25
+ * jQuery-wrapped document object.
26
+ * @alias qui.window.$document
27
+ * @type {jQuery}
28
+ */
29
+ export let $document = null
30
+
31
+ /**
32
+ * jQuery-wrapped window object.
33
+ * @alias qui.window.$window
34
+ * @type {jQuery}
35
+ */
36
+ export let $window = null
37
+
38
+ /**
39
+ * jQuery-wrapped document body object.
40
+ * @alias qui.window.$body
41
+ * @type {jQuery}
42
+ */
43
+ export let $body = null
44
+
45
+ /**
46
+ * Emitted whenever the window is resized. Handlers are called with the following parameters:
47
+ * * `width: Number`, the new window width
48
+ * * `height: Number`, the new window height
49
+ * @alias qui.window.resizeSignal
50
+ */
51
+ export const resizeSignal = new Signal()
52
+
53
+ /**
54
+ * Emitted whenever the screen layout changes. Handlers are called with the following parameters:
55
+ * * `smallScreen: Boolean`, telling if the screen is small, as defined by {@link qui.window.isSmallScreen}
56
+ * * `landscape: Boolean`, telling if the screen orientation is landscape
57
+ * @alias qui.window.screenLayoutChangeSignal
58
+ */
59
+ export const screenLayoutChangeSignal = new Signal()
60
+
61
+ /**
62
+ * Emitted whenever the application enters or leaves full-screen mode. Handlers are called with the following
63
+ * parameters:
64
+ * * `fullScreen`, telling if the application is currently in full-screen mode, nor not: `Boolean`
65
+ * @alias qui.window.fullScreenChangeSignal
66
+ */
67
+ export const fullScreenChangeSignal = new Signal()
68
+
69
+ /**
70
+ * Emitted whenever the application window becomes focused or is no longer focused. Handlers are called with the
71
+ * following parameters:
72
+ * * `focused: Boolean`, telling if the application is focused or not
73
+ * @alias qui.window.focusChangeSignal
74
+ */
75
+ export const focusChangeSignal = new Signal()
76
+
77
+ /**
78
+ * Emitted whenever the application window becomes active or is no longer active. Handlers are called with the
79
+ * following parameters:
80
+ * * `active: Boolean`, telling if the application is active or not
81
+ * @alias qui.window.activeChangeSignal
82
+ */
83
+ export const activeChangeSignal = new Signal()
84
+
85
+ /**
86
+ * Emitted when the application window is about to be closed. Handlers are called with no parameters. If any of the
87
+ * handlers returns `false`, window closing will be prevented, if possible.
88
+ * @alias qui.window.closeSignal
89
+ */
90
+ export const closeSignal = new Signal()
91
+
92
+
93
+ /* Full screen */
94
+
95
+ function handleEnterFullScreen() {
96
+ $body.addClass('full-screen')
97
+ logger.debug('entering full screen')
98
+ }
99
+
100
+ function handleExitFullScreen() {
101
+ $body.removeClass('full-screen')
102
+ logger.debug('full screen exited')
103
+ }
104
+
105
+ /**
106
+ * Put the window full-screen mode.
107
+ * @alias qui.window.enterFullScreen
108
+ */
109
+ export function enterFullScreen() {
110
+ document.documentElement.requestFullscreen()
111
+ }
112
+
113
+ /**
114
+ * Exit from full-screen mode.
115
+ * @alias qui.window.exitFullScreen
116
+ */
117
+ export function exitFullScreen() {
118
+ if (document.exitFullscreen) {
119
+ if (appActive) {
120
+ document.exitFullscreen()
121
+ }
122
+ }
123
+ else {
124
+ logger.warn('exiting full-screen mode not supported by browser')
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Tell whether the browser window is in full-screen mode or not.
130
+ * @alias qui.window.isFullScreen
131
+ * @returns {Boolean}
132
+ */
133
+ export function isFullScreen() {
134
+ return document.fullscreenEnabled != null
135
+ }
136
+
137
+
138
+ /* Screen size & layout */
139
+
140
+ function handleSmallScreen() {
141
+ logger.debug('small screen mode')
142
+ }
143
+
144
+ function handleLargeScreen() {
145
+ logger.debug('large screen mode')
146
+ }
147
+
148
+ function handleLandscape() {
149
+ logger.debug('landscape mode')
150
+ }
151
+
152
+ function handlePortrait() {
153
+ logger.debug('portrait mode')
154
+ }
155
+
156
+ /**
157
+ * @private
158
+ * @returns {Boolean}
159
+ */
160
+ function evaluateScreenLayout() {
161
+ let smallScreen = isSmallScreen()
162
+ let landscape = isLandscape()
163
+ let changed = false
164
+
165
+ if (smallScreen && !$body.hasClass('small-screen')) {
166
+ $body.addClass('small-screen')
167
+ handleSmallScreen()
168
+ changed = true
169
+ }
170
+ else if (!smallScreen && $body.hasClass('small-screen')) {
171
+ $body.removeClass('small-screen')
172
+ handleLargeScreen()
173
+ changed = true
174
+ }
175
+
176
+ if (landscape && !$body.hasClass('landscape')) {
177
+ $body.addClass('landscape')
178
+ handleLandscape()
179
+ changed = true
180
+ }
181
+ else if (!landscape && $body.hasClass('landscape')) {
182
+ $body.removeClass('landscape')
183
+ handlePortrait()
184
+ changed = true
185
+ }
186
+
187
+ if (changed) {
188
+ screenLayoutChangeSignal.emit(smallScreen, landscape)
189
+ }
190
+
191
+ return changed
192
+ }
193
+
194
+ /**
195
+ * Tell whether the screen is small or not. A screen is considered small if its width is below a *small*
196
+ * threshold.
197
+ * @alias qui.window.isSmallScreen
198
+ * @returns {Boolean}
199
+ */
200
+ export function isSmallScreen() {
201
+ return document.documentElement.clientWidth <= smallScreenThreshold * scalingFactor
202
+ }
203
+
204
+ /**
205
+ * Tell the current small screen threshold.
206
+ * @alias qui.window.getSmallScreenThreshold
207
+ * @returns {Number}
208
+ */
209
+ export function getSmallScreenThreshold() {
210
+ return smallScreenThreshold
211
+ }
212
+
213
+ /**
214
+ * Set the small screen threshold. Defaults to `700` logical pixels.
215
+ *
216
+ * You can set the threshold to `0` to disable small screen mode; setting it to a large number (e.g. `1e6`) ensures that
217
+ * small screen mode is always active.
218
+ *
219
+ * This function may force-close current pages and re-navigate to the current path, if screen layout is changed.
220
+ *
221
+ * @alias qui.window.setSmallScreenThreshold
222
+ * @param {?Number} threshold small screen threshold, in logical pixels; passing `null` will reset to default
223
+ */
224
+ export function setSmallScreenThreshold(threshold) {
225
+ logger.debug(`setting small screen threshold to ${threshold} pixels`)
226
+ smallScreenThreshold = threshold
227
+ if (smallScreenThreshold == null) {
228
+ smallScreenThreshold = Config.defaultSmallScreenThreshold
229
+ }
230
+
231
+ if ($body != null) {
232
+ evaluateScreenLayout()
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Tell if the screen orientation is landscape (`true`) or portrait (`false`).
238
+ * @alias qui.window.isLandscape
239
+ * @returns {Boolean}
240
+ */
241
+ export function isLandscape() {
242
+ let width = document.documentElement.clientWidth
243
+ let height = document.documentElement.clientHeight
244
+
245
+ return width >= height
246
+ }
247
+
248
+ /**
249
+ * Tell the current scaling factor.
250
+ * @alias qui.window.getScalingFactor
251
+ * @returns {Number}
252
+ */
253
+ export function getScalingFactor() {
254
+ return scalingFactor
255
+ }
256
+
257
+ /**
258
+ * Set the root scaling factor. Use `1` to disable scaling.
259
+ *
260
+ * This function may force-close current pages and re-navigate to the current path, if screen layout is changed.
261
+ *
262
+ * @alias qui.window.setScalingFactor
263
+ * @param {Number} factor
264
+ */
265
+ export function setScalingFactor(factor) {
266
+ logger.debug(`setting scaling factor to ${factor}`)
267
+ scalingFactor = factor
268
+
269
+ if ($body != null) {
270
+ if (scalingFactor === 1) {
271
+ $body.css('zoom', '')
272
+ }
273
+ else {
274
+ $body.css('zoom', `${factor * 100}%`)
275
+ }
276
+
277
+ /* Changing scaling factor will effectively change the perceived size of the window */
278
+ $window.trigger('resize')
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Tell the current application window width.
284
+ * @alias qui.window.getWidth
285
+ * @returns {Number}
286
+ */
287
+ export function getWidth() {
288
+ return document.documentElement.clientWidth / scalingFactor
289
+ }
290
+
291
+ /**
292
+ * Tell the current application window height.
293
+ * @alias qui.window.getHeight
294
+ * @returns {Number}
295
+ */
296
+ export function getHeight() {
297
+ return document.documentElement.clientHeight / scalingFactor
298
+ }
299
+
300
+
301
+ /* Focus */
302
+
303
+ function handleBecomeFocused() {
304
+ if (appFocused) {
305
+ return
306
+ }
307
+
308
+ appFocused = true
309
+
310
+ $body.addClass('focused')
311
+ logger.info('application is focused')
312
+ focusChangeSignal.emit(true)
313
+ }
314
+
315
+ function handleBecomeUnfocused() {
316
+ if (!appFocused) {
317
+ return
318
+ }
319
+
320
+ appFocused = false
321
+
322
+ $body.removeClass('focused')
323
+ logger.info('application is unfocused')
324
+ focusChangeSignal.emit(false)
325
+ }
326
+
327
+ /**
328
+ * Tell whether the application is focused or not.
329
+ * @alias qui.window.isFocused
330
+ * @returns {Boolean}
331
+ */
332
+ export function isFocused() {
333
+ return appFocused
334
+ }
335
+
336
+
337
+ /* Active */
338
+
339
+ function handleBecomeActive() {
340
+ if (appActive) {
341
+ return
342
+ }
343
+
344
+ appActive = true
345
+
346
+ $body.addClass('active')
347
+ logger.info('application is active')
348
+ activeChangeSignal.emit(true)
349
+ }
350
+
351
+ function handleBecomeInactive() {
352
+ if (!appActive) {
353
+ return
354
+ }
355
+
356
+ /* An inactive app can't be focused */
357
+ if (appFocused) {
358
+ handleBecomeUnfocused()
359
+ }
360
+
361
+ appActive = false
362
+
363
+ $body.removeClass('active')
364
+ logger.info('application is inactive')
365
+ activeChangeSignal.emit(false)
366
+ }
367
+
368
+ /**
369
+ * Tell whether the application is active or not.
370
+ * @alias qui.window.isActive
371
+ * @returns {Boolean}
372
+ */
373
+ export function isActive() {
374
+ return appActive
375
+ }
376
+
377
+
378
+ /* Reloading & closing */
379
+
380
+ /**
381
+ * Reload the window.
382
+ * @alias qui.window.reload
383
+ * @param {String} [path] optional path to navigate
384
+ */
385
+ export function reload(path) {
386
+ reloading = true
387
+
388
+ logger.debug('reloading')
389
+
390
+ asap(function () {
391
+ if (path) {
392
+ if (window.location.href === path) {
393
+ path = ''
394
+ }
395
+
396
+ window.location.href = path
397
+ }
398
+ else {
399
+ window.location.reload()
400
+ }
401
+ })
402
+ }
403
+
404
+ /**
405
+ * Tell if the window is currently closing.
406
+ * @alias qui.window.isClosing
407
+ * @returns {Boolean}
408
+ */
409
+ export function isClosing() {
410
+ return unloading || reloading
411
+ }
412
+
413
+ export function init() {
414
+ /* Initialize some settings with default values from configuration, but only if they haven't been initialized yet */
415
+ if (smallScreenThreshold == null) {
416
+ smallScreenThreshold = Config.defaultSmallScreenThreshold
417
+ }
418
+ if (scalingFactor == null) {
419
+ scalingFactor = Config.defaultScalingFactor
420
+ }
421
+
422
+ /* Wrap main objects in jQuery */
423
+ $document = $(document)
424
+ $window = $(window)
425
+ $body = $(document.body)
426
+
427
+ if (scalingFactor !== 1) {
428
+ $body.css('zoom', `${scalingFactor * 100}%`)
429
+ }
430
+
431
+ /* Window resize handling */
432
+ $window.on('resize', function () {
433
+ let width = getWidth()
434
+ let height = getHeight()
435
+
436
+ resizeSignal.emit(width, height)
437
+ evaluateScreenLayout()
438
+ })
439
+
440
+ /* Window unload handling */
441
+ $window.on('beforeunload', function (e) {
442
+ unloading = true
443
+
444
+ logger.info('application unload requested')
445
+
446
+ /* These nested asap() calls allow us to detect if unloading has been cancelled */
447
+ asap(function () {
448
+ asap(function () {
449
+ logger.info('application unload cancelled')
450
+ })
451
+ })
452
+
453
+ let canUnload = true
454
+
455
+ /* Give a chance to any pending requests to complete */
456
+ let writePendingRequests = AJAX.getPendingRequests()
457
+ .filter(r => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(r.details.method))
458
+
459
+ if (writePendingRequests.length > 0) {
460
+ logger.warn(`application unload: there are ${writePendingRequests.length} write pending ajax requests`)
461
+ canUnload = false
462
+ }
463
+
464
+ let closeSignalResult = closeSignal.emit()
465
+ if (closeSignalResult === false) {
466
+ logger.warn('application unload prevented by close signal handler')
467
+ canUnload = false
468
+ }
469
+
470
+ if (!canUnload) {
471
+ e.preventDefault()
472
+ unloading = false
473
+ return false
474
+ }
475
+ })
476
+
477
+ /* Full screen handling */
478
+ $document.on('fullscreenchange', function () {
479
+ if (isFullScreen()) {
480
+ handleEnterFullScreen()
481
+ }
482
+ else {
483
+ handleExitFullScreen()
484
+ }
485
+
486
+ fullScreenChangeSignal.emit(isFullScreen())
487
+ })
488
+
489
+ /* Active handling */
490
+ $document.on('visibilitychange', function () {
491
+ if (this.visibilityState === 'visible') {
492
+ handleBecomeActive()
493
+ }
494
+ else {
495
+ handleBecomeInactive()
496
+ }
497
+ })
498
+
499
+ $window.on('pageshow', () => handleBecomeActive())
500
+ $window.on('pagehide', () => handleBecomeInactive())
501
+
502
+ $document.on('resume', () => handleBecomeActive())
503
+ $document.on('freeze', () => handleBecomeInactive())
504
+
505
+ /* Initial active state */
506
+ if (document.visibilityState === 'visible') {
507
+ handleBecomeActive()
508
+ }
509
+ else {
510
+ handleBecomeInactive()
511
+ }
512
+
513
+ /* Focus handling */
514
+ $window.on('focus', () => handleBecomeFocused())
515
+ $window.on('blur', () => handleBecomeUnfocused())
516
+
517
+ /* Initial focus */
518
+ if (document.hasFocus()) {
519
+ handleBecomeFocused()
520
+ }
521
+ else {
522
+ handleBecomeUnfocused()
523
+ }
524
+
525
+ /* Check every second to ensure the active and focus states are correctly set. This shouldn't normally be
526
+ * necessary, but PWAs running on Chrome sometimes don't fire some events wen waking up (unfreezing/resuming) */
527
+ setInterval(function () {
528
+
529
+ if (document.visibilityState === 'visible' && !appActive) {
530
+ logger.warn('surprised to detect application active')
531
+ handleBecomeActive()
532
+ }
533
+ else if (document.visibilityState !== 'visible' && appActive) {
534
+ logger.warn('surprised to detect application inactive')
535
+ handleBecomeInactive()
536
+ }
537
+
538
+ if (document.hasFocus() && !appFocused) {
539
+ logger.warn('surprised to detect application focused')
540
+ handleBecomeFocused()
541
+ }
542
+ else if (!document.hasFocus() && appFocused) {
543
+ logger.warn('surprised to detect application unfocused')
544
+ handleBecomeUnfocused()
545
+ }
546
+
547
+ }, 1000)
548
+
549
+ asap(function () {
550
+ /* Reset the scroll position of the body */
551
+ $body.scrollLeft(0)
552
+ $body.scrollTop(0)
553
+
554
+ /* Trigger an initial resize */
555
+ $window.resize()
556
+ })
557
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "plugins": [
3
+ "node_modules/jsdoc-export-default-interop/dist/index",
4
+ "node_modules/jsdoc-typeof-plugin",
5
+ "plugins/markdown"
6
+ ],
7
+ "source": {
8
+ "include": ["js"],
9
+ "excludePattern": "js/lib/"
10
+ },
11
+ "sourceType": "module",
12
+ "templates": {
13
+ "cleverLinks": true,
14
+ "applicationName": "QUI"
15
+ },
16
+ "opts": {
17
+ "destination": "docs/jsdoc",
18
+ "recurse": true
19
+ }
20
+ }
package/less/base.less ADDED
@@ -0,0 +1,123 @@
1
+
2
+ @import (reference) "theme";
3
+
4
+
5
+ /* Common stuff */
6
+
7
+ * {
8
+ padding: 0;
9
+ border: 0 solid black;
10
+ margin: 0;
11
+ outline: 0;
12
+ border-spacing: 0;
13
+ border-collapse: separate;
14
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
15
+ -webkit-overflow-scrolling: touch;
16
+ overflow-scrolling: touch;
17
+ }
18
+
19
+ ::selection {
20
+ /* fading is needed as a workaround for Chrome */
21
+ background: fade(@interactive-color, 99%);
22
+ color: @background-color;
23
+ }
24
+
25
+ ::-moz-selection {
26
+ background: @interactive-color;
27
+ color: @background-color;
28
+ }
29
+
30
+ *:FOCUS {
31
+ outline: 0;
32
+ }
33
+
34
+ ul {
35
+ list-style-position: inside;
36
+ }
37
+
38
+ .noselect {
39
+ -webkit-user-select: none;
40
+ -moz-user-select: none;
41
+ -ms-user-select: none;
42
+ user-select: none;
43
+ }
44
+
45
+ a {
46
+ color: @interactive-color;
47
+ transition: color ease @transition-duration;
48
+ text-decoration: none;
49
+ cursor: pointer;
50
+ }
51
+
52
+ a:ACTIVE {
53
+ color: @interactive-active-color;
54
+ }
55
+
56
+
57
+ /* Root elements */
58
+
59
+ html {
60
+ color: @foreground-color;
61
+ background: @background-root-color;
62
+ height: 100%;
63
+ font-family: @base-font-family, sans, arial, helvetica, sans-serif;
64
+ font-size: 16px;
65
+ overflow-x: hidden;
66
+ transition: background (@transition-duration * 2) linear;
67
+ }
68
+
69
+ body {
70
+ position: relative;
71
+ height: 100%;
72
+ overflow-x: hidden;
73
+ transition: opacity @transition-duration linear;
74
+ line-height: 1.15em;
75
+ }
76
+
77
+ body.disable-transitions * {
78
+ transition: none !important;
79
+ }
80
+
81
+
82
+ /* Fonts */
83
+
84
+ @font-face {
85
+ font-family: 'DejaVuSans';
86
+ src: url('@{qui_font_path}/dejavusans-regular.woff') format('woff');
87
+ font-weight: normal;
88
+ font-style: normal;
89
+ font-display: swap;
90
+ }
91
+
92
+ @font-face {
93
+ font-family: 'DejaVuSans';
94
+ src: url('@{qui_font_path}/dejavusans-bold.woff') format('woff');
95
+ font-weight: bold;
96
+ font-style: normal;
97
+ font-display: swap;
98
+ }
99
+
100
+ @font-face {
101
+ font-family: 'DejaVuSans';
102
+ src: url('@{qui_font_path}/dejavusans-italic.woff') format('woff');
103
+ font-weight: normal;
104
+ font-style: italic;
105
+ font-display: swap;
106
+ }
107
+
108
+ @font-face {
109
+ font-family: 'DejaVuSans';
110
+ src: url('@{qui_font_path}/dejavusans-bolditalic.woff') format('woff');
111
+ font-weight: bold;
112
+ font-style: italic;
113
+ font-display: swap;
114
+ }
115
+
116
+
117
+ /* Animations */
118
+
119
+ @keyframes spin {
120
+ 100% {
121
+ transform:rotate(360deg);
122
+ }
123
+ }