@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.
- package/.eslintignore +2 -0
- package/.eslintrc.json +492 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/ISSUE_TEMPLATE/improvement_proposal.md +20 -0
- package/.github/workflows/main.yml +74 -0
- package/.pre-commit-config.yaml +8 -0
- package/LICENSE.txt +177 -0
- package/README.md +4 -0
- package/font/dejavusans-bold.woff +0 -0
- package/font/dejavusans-bolditalic.woff +0 -0
- package/font/dejavusans-italic.woff +0 -0
- package/font/dejavusans-regular.woff +0 -0
- package/img/qui-icons.svg +1937 -0
- package/js/base/base.js +47 -0
- package/js/base/condition-variable.js +92 -0
- package/js/base/errors.js +36 -0
- package/js/base/i18n.js +20 -0
- package/js/base/mixwith.js +135 -0
- package/js/base/require-js-compat.js +78 -0
- package/js/base/signal.js +91 -0
- package/js/base/singleton.js +66 -0
- package/js/base/timer.js +126 -0
- package/js/config.js +184 -0
- package/js/forms/common-fields/check-field.js +42 -0
- package/js/forms/common-fields/choice-buttons-field.js +30 -0
- package/js/forms/common-fields/color-combo-field.js +37 -0
- package/js/forms/common-fields/combo-field.js +108 -0
- package/js/forms/common-fields/common-fields.js +23 -0
- package/js/forms/common-fields/composite-field.js +132 -0
- package/js/forms/common-fields/custom-html-field.js +51 -0
- package/js/forms/common-fields/email-field.js +30 -0
- package/js/forms/common-fields/file-picker-field.js +46 -0
- package/js/forms/common-fields/jquery-ui-field.js +111 -0
- package/js/forms/common-fields/labels-field.js +69 -0
- package/js/forms/common-fields/numeric-field.js +39 -0
- package/js/forms/common-fields/password-field.js +28 -0
- package/js/forms/common-fields/phone-field.js +26 -0
- package/js/forms/common-fields/progress-disk-field.js +69 -0
- package/js/forms/common-fields/push-button-field.js +138 -0
- package/js/forms/common-fields/slider-field.js +51 -0
- package/js/forms/common-fields/text-area-field.js +34 -0
- package/js/forms/common-fields/text-field.js +89 -0
- package/js/forms/common-fields/up-down-field.js +85 -0
- package/js/forms/common-forms/common-forms.js +16 -0
- package/js/forms/common-forms/options-form.js +77 -0
- package/js/forms/common-forms/page-form.js +115 -0
- package/js/forms/form-button.js +202 -0
- package/js/forms/form-field.js +1183 -0
- package/js/forms/form.js +1181 -0
- package/js/forms/forms.js +68 -0
- package/js/global-glass.js +100 -0
- package/js/icons/default-stock.js +173 -0
- package/js/icons/icon.js +64 -0
- package/js/icons/icons.js +16 -0
- package/js/icons/multi-state-sprites-icon.js +362 -0
- package/js/icons/stock-icon.js +219 -0
- package/js/icons/stock.js +98 -0
- package/js/icons/stocks.js +57 -0
- package/js/index.js +232 -0
- package/js/lib/jquery.longpress.js +79 -0
- package/js/lib/jquery.module.js +4 -0
- package/js/lib/logger.module.js +4 -0
- package/js/lib/pep.module.js +4 -0
- package/js/lists/common-items/common-items.js +5 -0
- package/js/lists/common-items/icon-label-list-item.js +86 -0
- package/js/lists/common-lists/common-lists.js +5 -0
- package/js/lists/common-lists/page-list.js +53 -0
- package/js/lists/list-item.js +147 -0
- package/js/lists/list.js +636 -0
- package/js/lists/lists.js +26 -0
- package/js/main-ui/main-ui.js +64 -0
- package/js/main-ui/menu-bar.js +144 -0
- package/js/main-ui/options-bar.js +181 -0
- package/js/main-ui/status.js +185 -0
- package/js/main-ui/top-bar.js +59 -0
- package/js/messages/common-message-forms/common-message-forms.js +7 -0
- package/js/messages/common-message-forms/confirm-message-form.js +81 -0
- package/js/messages/common-message-forms/simple-message-form.js +67 -0
- package/js/messages/common-message-forms/sticky-simple-message-form.js +27 -0
- package/js/messages/message-form.js +107 -0
- package/js/messages/messages.js +21 -0
- package/js/messages/sticky-modal-page.js +98 -0
- package/js/messages/sticky-modal-progress-message.js +27 -0
- package/js/messages/toast.js +164 -0
- package/js/navigation.js +654 -0
- package/js/pages/breadcrumbs.js +124 -0
- package/js/pages/common-pages/common-pages.js +6 -0
- package/js/pages/common-pages/modal-progress-page.js +83 -0
- package/js/pages/common-pages/structured-page.js +46 -0
- package/js/pages/page.js +1018 -0
- package/js/pages/pages-context.js +154 -0
- package/js/pages/pages.js +252 -0
- package/js/pwa.js +337 -0
- package/js/sections/section.js +612 -0
- package/js/sections/sections.js +300 -0
- package/js/tables/common-cells/common-cells.js +7 -0
- package/js/tables/common-cells/icon-label-table-cell.js +68 -0
- package/js/tables/common-cells/push-button-table-cell.js +133 -0
- package/js/tables/common-cells/simple-table-cell.js +37 -0
- package/js/tables/common-tables/common-tables.js +5 -0
- package/js/tables/common-tables/page-table.js +55 -0
- package/js/tables/table-cell.js +198 -0
- package/js/tables/table-row.js +126 -0
- package/js/tables/table.js +492 -0
- package/js/tables/tables.js +36 -0
- package/js/theme.js +304 -0
- package/js/utils/ajax.js +126 -0
- package/js/utils/array.js +194 -0
- package/js/utils/colors.js +445 -0
- package/js/utils/cookies.js +65 -0
- package/js/utils/crypto.js +439 -0
- package/js/utils/css.js +234 -0
- package/js/utils/date.js +300 -0
- package/js/utils/files.js +27 -0
- package/js/utils/gestures.js +165 -0
- package/js/utils/html.js +76 -0
- package/js/utils/misc.js +81 -0
- package/js/utils/object.js +324 -0
- package/js/utils/promise.js +49 -0
- package/js/utils/string.js +192 -0
- package/js/utils/url.js +187 -0
- package/js/utils/utils.js +3 -0
- package/js/utils/visibility-manager.js +211 -0
- package/js/views/common-views/common-views.js +7 -0
- package/js/views/common-views/icon-label-view.js +210 -0
- package/js/views/common-views/progress-view.js +89 -0
- package/js/views/common-views/structured-view.js +368 -0
- package/js/views/view.js +467 -0
- package/js/views/views.js +3 -0
- package/js/widgets/base-widget.js +23 -0
- package/js/widgets/common-widgets/check-button.js +109 -0
- package/js/widgets/common-widgets/choice-buttons.js +322 -0
- package/js/widgets/common-widgets/color-combo.js +104 -0
- package/js/widgets/common-widgets/combo.js +645 -0
- package/js/widgets/common-widgets/common-widgets.js +17 -0
- package/js/widgets/common-widgets/email-input.js +7 -0
- package/js/widgets/common-widgets/file-picker.js +133 -0
- package/js/widgets/common-widgets/labels.js +132 -0
- package/js/widgets/common-widgets/numeric-input.js +49 -0
- package/js/widgets/common-widgets/password-input.js +91 -0
- package/js/widgets/common-widgets/phone-input.js +7 -0
- package/js/widgets/common-widgets/progress-disk.js +174 -0
- package/js/widgets/common-widgets/push-button.js +155 -0
- package/js/widgets/common-widgets/slider.js +455 -0
- package/js/widgets/common-widgets/text-area.js +52 -0
- package/js/widgets/common-widgets/text-input.js +174 -0
- package/js/widgets/common-widgets/up-down.js +351 -0
- package/js/widgets/widgets.js +57 -0
- package/js/window.js +557 -0
- package/jsdoc.conf.json +20 -0
- package/less/base.less +123 -0
- package/less/forms/common-fields.less +101 -0
- package/less/forms/common-forms.less +5 -0
- package/less/forms/form-button.less +21 -0
- package/less/forms/form-field.less +266 -0
- package/less/forms/form.less +131 -0
- package/less/global-glass.less +64 -0
- package/less/icon-label-view.less +82 -0
- package/less/icons.less +144 -0
- package/less/lists.less +105 -0
- package/less/main-ui.less +328 -0
- package/less/messages.less +189 -0
- package/less/no-effects.less +24 -0
- package/less/pages/breadcrumbs.less +98 -0
- package/less/pages/common-pages.less +36 -0
- package/less/pages/page.less +70 -0
- package/less/progress-view.less +51 -0
- package/less/stock-icons.less +43 -0
- package/less/structured-view.less +245 -0
- package/less/tables.less +84 -0
- package/less/theme-dark.less +133 -0
- package/less/theme-light.less +132 -0
- package/less/theme.less +419 -0
- package/less/visibility-manager.less +11 -0
- package/less/widgets/check-button.less +96 -0
- package/less/widgets/choice-buttons.less +160 -0
- package/less/widgets/color-combo.less +33 -0
- package/less/widgets/combo.less +230 -0
- package/less/widgets/common-buttons.less +120 -0
- package/less/widgets/common.less +24 -0
- package/less/widgets/input.less +258 -0
- package/less/widgets/labels.less +81 -0
- package/less/widgets/progress-disk.less +70 -0
- package/less/widgets/slider.less +199 -0
- package/less/widgets/updown.less +115 -0
- package/less/widgets/various.less +36 -0
- package/package.json +47 -0
- package/pyproject.toml +45 -0
- package/qui/__init__.py +110 -0
- package/qui/constants.py +1 -0
- package/qui/exceptions.py +2 -0
- package/qui/j2template.py +71 -0
- package/qui/settings.py +60 -0
- package/qui/templates/manifest.json +25 -0
- package/qui/templates/qui.html +126 -0
- package/qui/templates/service-worker.js +188 -0
- package/qui/web/__init__.py +0 -0
- package/qui/web/tornado.py +220 -0
- package/scripts/postinstall.sh +10 -0
- package/webpack/webpack-adjust-css-urls-loader.js +36 -0
- 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
|
+
}
|
package/jsdoc.conf.json
ADDED
|
@@ -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
|
+
}
|