@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
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace qui.utils.object
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ArrayUtils from './array.js'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Use Object's `hasOwnProperty` and never rely on whatever `hasOwnProperty` incoming objects have.
|
|
10
|
+
* @private
|
|
11
|
+
* @param {Object} obj
|
|
12
|
+
* @param {String} prop
|
|
13
|
+
* @returns {Boolean}
|
|
14
|
+
*/
|
|
15
|
+
function hasOwnProperty(obj, prop) {
|
|
16
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tell if a value is a pure object.
|
|
21
|
+
* @param {*} value value to test
|
|
22
|
+
* @returns {Boolean}
|
|
23
|
+
*/
|
|
24
|
+
export function isObject(value) {
|
|
25
|
+
return (value != null) && (value.constructor === Object)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create an object from a list of entries. Each entry is an array of two elements: the key and its associated value.
|
|
30
|
+
* @alias qui.utils.object.fromEntries
|
|
31
|
+
* @param {Array[]} entries
|
|
32
|
+
* @returns {Object}
|
|
33
|
+
*/
|
|
34
|
+
export function fromEntries(entries) {
|
|
35
|
+
let obj = {}
|
|
36
|
+
entries.forEach(function (entry) {
|
|
37
|
+
obj[entry[0]] = entry[1]
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return obj
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Search an object for an entry that matches a condition and return the corresponding key.
|
|
45
|
+
* @alias qui.utils.object.findKey
|
|
46
|
+
* @param {Object} obj
|
|
47
|
+
* @param {Function} func function that implements the search condition; it is called with value and key as arguments
|
|
48
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
49
|
+
* @returns {String} the matched key, or `undefined` if no entry matched
|
|
50
|
+
*/
|
|
51
|
+
export function findKey(obj, func, thisArg = null) {
|
|
52
|
+
let entry = Object.entries(obj).find(function (entry) {
|
|
53
|
+
if (func.call(thisArg, entry[1], entry[0])) {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
if (entry) {
|
|
59
|
+
return entry[0]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Search an object for an entry that matches a condition and return the corresponding value.
|
|
67
|
+
* @alias qui.utils.object.findValue
|
|
68
|
+
* @param {Object} obj
|
|
69
|
+
* @param {Function} func function that implements the search condition; it is called with key and value as arguments
|
|
70
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
71
|
+
* @returns {String} the matched value, or `undefined` if no entry matched
|
|
72
|
+
*/
|
|
73
|
+
export function findValue(obj, func, thisArg = null) {
|
|
74
|
+
let entry = Object.entries(obj).find(function (entry) {
|
|
75
|
+
if (func.call(thisArg, entry[0], entry[1])) {
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (entry) {
|
|
81
|
+
return entry[1]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Parse an object, calling a function for each entry.
|
|
89
|
+
* @alias qui.utils.object.forEach
|
|
90
|
+
* @param {Object} obj
|
|
91
|
+
* @param {Function} func function to be called for each entry; it is called with the entry (array of key and value) as
|
|
92
|
+
* argument
|
|
93
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
94
|
+
* @param {Function} [sortKeyFunc] optional function used to extract key from each entry when sorting entries; no
|
|
95
|
+
* sorting is performed unless this function is supplied
|
|
96
|
+
*/
|
|
97
|
+
export function forEach(obj, func, thisArg = null, sortKeyFunc = null) {
|
|
98
|
+
let entries = Object.entries(obj)
|
|
99
|
+
if (sortKeyFunc) {
|
|
100
|
+
ArrayUtils.sortKey(entries, sortKeyFunc)
|
|
101
|
+
}
|
|
102
|
+
entries.forEach(function (entry) {
|
|
103
|
+
func.call(thisArg, entry[0], entry[1])
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Filter object entries. This function is similar to Array's `filter` method, but applied on objects.
|
|
109
|
+
* @alias qui.utils.object.filter
|
|
110
|
+
* @param {Object} obj
|
|
111
|
+
* @param {Function} func a function called with each key and value as arguments; only entries for which this function
|
|
112
|
+
* returns a true value will be kept
|
|
113
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
114
|
+
* @returns {Object} the filtered object
|
|
115
|
+
*/
|
|
116
|
+
export function filter(obj, func, thisArg = null) {
|
|
117
|
+
return fromEntries(Object.entries(obj).filter(entry => func.call(thisArg, entry[0], entry[1])))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Map object entries. This function is similar to Array's `map` method, but applied on objects.
|
|
122
|
+
* @alias qui.utils.object.map
|
|
123
|
+
* @param {Object} obj
|
|
124
|
+
* @param {Function} func a function called with each key and value as arguments; it is expected to return an array with
|
|
125
|
+
* two elements, the mapped key and value
|
|
126
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
127
|
+
* @returns {Object} the mapped object
|
|
128
|
+
*/
|
|
129
|
+
export function map(obj, func, thisArg = null) {
|
|
130
|
+
return fromEntries(Object.entries(obj).map(entry => func.call(thisArg, entry[0], entry[1])))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Map object keys.
|
|
135
|
+
* @alias qui.utils.object.mapKey
|
|
136
|
+
* @param {Object} obj
|
|
137
|
+
* @param {Function} func a function called with each key and value as arguments; it is expected to return the mapped
|
|
138
|
+
* key
|
|
139
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
140
|
+
* @returns {Object} the mapped object
|
|
141
|
+
*/
|
|
142
|
+
export function mapKey(obj, func, thisArg = null) {
|
|
143
|
+
return fromEntries(Object.entries(obj).map(entry => [func.call(thisArg, entry[0], entry[1]), entry[1]]))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Map object values.
|
|
148
|
+
* @alias qui.utils.object.mapValue
|
|
149
|
+
* @param {Object} obj
|
|
150
|
+
* @param {Function} func a function called with each value and key as arguments; it is expected to return the mapped
|
|
151
|
+
* value
|
|
152
|
+
* @param {*} [thisArg] optional `this` argument to be used when calling `func`
|
|
153
|
+
* @returns {Object} the mapped object
|
|
154
|
+
*/
|
|
155
|
+
export function mapValue(obj, func, thisArg = null) {
|
|
156
|
+
return fromEntries(Object.entries(obj).map(entry => [entry[0], func.call(thisArg, entry[1], entry[0])]))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Assign values to an object, *in place*, but only if corresponding keys are missing.
|
|
161
|
+
* @alias qui.utils.object.assignDefault
|
|
162
|
+
* @param {Object} dest
|
|
163
|
+
* @param {Object} src
|
|
164
|
+
* @returns {Object} the given `dest` object
|
|
165
|
+
*/
|
|
166
|
+
export function assignDefault(dest, src) {
|
|
167
|
+
Object.keys(src).forEach(function (key) {
|
|
168
|
+
if (key in dest) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
dest[key] = src[key]
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return dest
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Combine two or more objects into a single object by merging their keys and values.
|
|
180
|
+
* @alias qui.utils.object.combine
|
|
181
|
+
* @param {...Object} objs objects to combine
|
|
182
|
+
* @returns {Object} the combined object
|
|
183
|
+
*/
|
|
184
|
+
export function combine(...objs) {
|
|
185
|
+
let combined = {}
|
|
186
|
+
objs.forEach(function (obj) {
|
|
187
|
+
Object.keys(obj).forEach(function (key) {
|
|
188
|
+
combined[key] = obj[key]
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
return combined
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Clone an object by copying its entries into a new object. Optionally recurse into inner objects and arrays.
|
|
197
|
+
* @alias qui.utils.object.copy
|
|
198
|
+
* @param {Object} orig original object to copy
|
|
199
|
+
* @param {Boolean} [deep] set to `true` to perform a *deep* copy (defaults to `false`)
|
|
200
|
+
* @returns {Object}
|
|
201
|
+
*/
|
|
202
|
+
export function copy(orig, deep = false) {
|
|
203
|
+
if (deep) {
|
|
204
|
+
let type = typeof orig
|
|
205
|
+
if (orig === undefined || orig === null ||
|
|
206
|
+
type === 'number' || type === 'string' ||
|
|
207
|
+
type === 'boolean' || type === 'function') {
|
|
208
|
+
|
|
209
|
+
return orig
|
|
210
|
+
}
|
|
211
|
+
else if (Array.isArray(orig)) {
|
|
212
|
+
return orig.map(e => copy(e, /* deep = */ true))
|
|
213
|
+
}
|
|
214
|
+
else if (orig.constructor === Object) { /* Plain object */
|
|
215
|
+
return map(orig, (k, v) => [k, copy(v, /* deep = */ true)])
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
return orig
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
return fromEntries(Object.entries(orig))
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Remove a key from an object, returning it.
|
|
228
|
+
* @alias qui.utils.object.pop
|
|
229
|
+
* @param {Object} obj
|
|
230
|
+
* @param {String} key
|
|
231
|
+
* @param {*} [def] an optional default value to return if key is missing
|
|
232
|
+
* @returns {*}
|
|
233
|
+
*/
|
|
234
|
+
export function pop(obj, key, def = null) {
|
|
235
|
+
if (!(key in obj)) {
|
|
236
|
+
return def
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let value = obj[key]
|
|
240
|
+
delete obj[key]
|
|
241
|
+
|
|
242
|
+
return value
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Insert a value into an object, but only if it's not already present, and return it.
|
|
247
|
+
* @alias qui.utils.object.setDefault
|
|
248
|
+
* @param {Object} obj
|
|
249
|
+
* @param {String} key
|
|
250
|
+
* @param {*} value
|
|
251
|
+
* @returns {*}
|
|
252
|
+
*/
|
|
253
|
+
export function setDefault(obj, key, value) {
|
|
254
|
+
if (key in obj) {
|
|
255
|
+
return obj[key]
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return (obj[key] = value)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* A deep equals() operator that will recursively compare any non-primitive objects to validate the equality.
|
|
263
|
+
* @alias qui.utils.object.deepEquals
|
|
264
|
+
* @param {Object} obj1 the first object of the comparison
|
|
265
|
+
* @param {Object} obj2 the second object of the comparison
|
|
266
|
+
* @returns {Boolean} `true` if the two objects are deeply equal, `false`otherwise
|
|
267
|
+
*/
|
|
268
|
+
export function deepEquals(obj1, obj2) {
|
|
269
|
+
if (obj1 === obj2) {
|
|
270
|
+
return true
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (typeof obj1 !== typeof obj2) {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (Array.isArray(obj1)) {
|
|
278
|
+
if (!Array.isArray(obj2)) {
|
|
279
|
+
return false
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (obj1.length !== obj2.length) {
|
|
283
|
+
return false
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for (let i = 0; i < obj1.length; i++) {
|
|
287
|
+
if (!deepEquals(obj1[i], obj2[i])) {
|
|
288
|
+
return false
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
294
|
+
else if (obj1 instanceof Object) {
|
|
295
|
+
if (!(obj2 instanceof Object)) {
|
|
296
|
+
return false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
for (let key in obj1) {
|
|
300
|
+
if (hasOwnProperty(obj1, key)) {
|
|
301
|
+
if (!hasOwnProperty(obj2, key)) {
|
|
302
|
+
return false
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!deepEquals(obj1[key], obj2[key])) {
|
|
306
|
+
return false
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (let key in obj2) {
|
|
312
|
+
if (hasOwnProperty(obj2, key)) {
|
|
313
|
+
if (!hasOwnProperty(obj1, key)) {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return true
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
return false
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace qui.utils.promise
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {TimeoutError} from '$qui/base/errors.js'
|
|
6
|
+
import {asap as asapFunc} from '$qui/utils/misc.js'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Return a promise that resolves as soon as possible, in the next main loop iteration.
|
|
11
|
+
* @alias qui.utils.promise.asap
|
|
12
|
+
* @returns {Promise}
|
|
13
|
+
*/
|
|
14
|
+
export function asap() {
|
|
15
|
+
return new Promise(function (resolve) {
|
|
16
|
+
asapFunc(resolve)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return a promise that resolves later, after a specified number of seconds.
|
|
22
|
+
* @alias qui.utils.promise.later
|
|
23
|
+
* @param {Number} timeout the timeout, in milliseconds
|
|
24
|
+
* @param {*} [arg] an optional argument to pass along the promise chain
|
|
25
|
+
* @returns {Promise}
|
|
26
|
+
*/
|
|
27
|
+
export function later(timeout, arg) {
|
|
28
|
+
return new Promise(function (resolve) {
|
|
29
|
+
setTimeout(function () {
|
|
30
|
+
resolve(arg)
|
|
31
|
+
}, timeout)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run a promise with a timeout. If the promise is not fulfilled within the given timeout, the returned promise will be
|
|
37
|
+
* rejected with {@link qui.base.errors.TimeoutError}
|
|
38
|
+
* @alias qui.utils.promise.withTimeout
|
|
39
|
+
* @param {Promise} promise the promise to run
|
|
40
|
+
* @param {Number} timeout the timeout, in milliseconds
|
|
41
|
+
* @returns {Promise} the promise with timeout
|
|
42
|
+
*/
|
|
43
|
+
export function withTimeout(promise, timeout) {
|
|
44
|
+
let timeoutPromise = later(timeout).then(function () {
|
|
45
|
+
throw new TimeoutError(`Operation timed out after ${timeout} milliseconds`)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return Promise.race([promise, timeoutPromise])
|
|
49
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace qui.utils.string
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ObjectUtils from '$qui/utils/object.js'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const REGEX_ESCAPE_CHARS = '.\\+*?[^]$(){}=!<>|:-'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Replace all occurrences of `oldStr` in `s` with `newStr`.
|
|
13
|
+
* @alias qui.utils.string.replaceAll
|
|
14
|
+
* @param {String} s
|
|
15
|
+
* @param {String} oldStr
|
|
16
|
+
* @param {String} newStr
|
|
17
|
+
* @returns {String}
|
|
18
|
+
*/
|
|
19
|
+
export function replaceAll(s, oldStr, newStr) {
|
|
20
|
+
let p
|
|
21
|
+
while ((p = s.indexOf(oldStr)) >= 0) {
|
|
22
|
+
s = s.substring(0, p) + newStr + s.substring(p + oldStr.length, s.length)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return s.toString()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Replace percent-formatted placeholders in a string with given values.
|
|
30
|
+
*
|
|
31
|
+
* Placeholders can be given as `"%s"`, `"%d"` or `"%f"`, in which case `args` will be used, in order, to replace
|
|
32
|
+
* them.
|
|
33
|
+
*
|
|
34
|
+
* Placeholders can also be given as `"%(name)s"`, `"%(name)d"` or `"%(name)f"`, in which case the first argument in
|
|
35
|
+
* `args` will be used as an object that maps names to replacement values.
|
|
36
|
+
*
|
|
37
|
+
* @alias qui.utils.string.formatPercent
|
|
38
|
+
* @param {String} text
|
|
39
|
+
* @param {...*} args values used to replace the placeholders
|
|
40
|
+
* @returns {String}
|
|
41
|
+
*/
|
|
42
|
+
export function formatPercent(text, ...args) {
|
|
43
|
+
let rex = new RegExp('%[sdf]')
|
|
44
|
+
let match, i = 0
|
|
45
|
+
while ((match = text.match(rex))) {
|
|
46
|
+
text = text.substring(0, match.index) + args[i] + text.substring(match.index + 2)
|
|
47
|
+
i++
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (i) { /* %s format used */
|
|
51
|
+
text = text.replace(new RegExp('%%', 'g'), '%')
|
|
52
|
+
|
|
53
|
+
return text
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let keywords = args[0] || {}
|
|
57
|
+
|
|
58
|
+
ObjectUtils.forEach(keywords, function (key, value) {
|
|
59
|
+
text = text.replace(new RegExp(`%\\(${key}\\)s`, 'g'), value.toString())
|
|
60
|
+
text = text.replace(new RegExp(`%\\(${key}\\)d`, 'g'), value.toString())
|
|
61
|
+
text = text.replace(new RegExp(`%\\(${key}\\)f`, 'g'), value.toString())
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
text = text.replace('%%', '%')
|
|
65
|
+
|
|
66
|
+
return text
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Transform a regular text to its camel-case representation.
|
|
71
|
+
* @alias qui.utils.string.camelize
|
|
72
|
+
* @param {String} s
|
|
73
|
+
* @returns {String}
|
|
74
|
+
*/
|
|
75
|
+
export function camelize(s) {
|
|
76
|
+
s = s.replace(/(?:^|[-_])(\w)/g, function (_, c) {
|
|
77
|
+
return c ? c.toUpperCase() : ''
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return s.substr(0, 1).toLowerCase() + s.substr(1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Transform a camel-case string into its dash-separated representation.
|
|
85
|
+
* @alias qui.utils.string.uncamelize
|
|
86
|
+
* @param {String} s
|
|
87
|
+
* @param {String} [chr] separator character (defaults to dash `"-"`)
|
|
88
|
+
* @returns {String}
|
|
89
|
+
*/
|
|
90
|
+
export function uncamelize(s, chr = '-') {
|
|
91
|
+
return s.replace(/([A-Z])/g, function ($1) {
|
|
92
|
+
return chr + $1.toLowerCase()
|
|
93
|
+
}).replace(new RegExp(`^${chr}`), '')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Transform the starting letter of each word of a text to uppercase.
|
|
98
|
+
* @alias qui.utils.string.title
|
|
99
|
+
* @param {String} s
|
|
100
|
+
* @returns {String}
|
|
101
|
+
*/
|
|
102
|
+
export function title(s) {
|
|
103
|
+
s = s.replace(/\s(\w)/g, function (_, c) {
|
|
104
|
+
return c ? ` ${c.toUpperCase()}` : ' '
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return s.substr(0, 1).toUpperCase() + s.substr(1)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Encode a string to UTF8.
|
|
112
|
+
* @alias qui.utils.string.toUTF8
|
|
113
|
+
* @param {String} s
|
|
114
|
+
* @returns {String}
|
|
115
|
+
*/
|
|
116
|
+
export function toUTF8(s) {
|
|
117
|
+
s = s.replace(new RegExp('\\r\\n'), '\n')
|
|
118
|
+
let utf = ''
|
|
119
|
+
|
|
120
|
+
for (let n = 0; n < s.length; n++) {
|
|
121
|
+
let c = s.charCodeAt(n)
|
|
122
|
+
|
|
123
|
+
if (c < 128) {
|
|
124
|
+
utf += String.fromCharCode(c)
|
|
125
|
+
}
|
|
126
|
+
else if ((c > 127) && (c < 2048)) {
|
|
127
|
+
utf += String.fromCharCode((c >> 6) | 192)
|
|
128
|
+
utf += String.fromCharCode((c & 63) | 128)
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
utf += String.fromCharCode((c >> 12) | 224)
|
|
132
|
+
utf += String.fromCharCode(((c >> 6) & 63) | 128)
|
|
133
|
+
utf += String.fromCharCode((c & 63) | 128)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return utf
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Decode a string from UTF8.
|
|
142
|
+
* @alias qui.utils.string.fromUTF8
|
|
143
|
+
* @param {String} s
|
|
144
|
+
* @returns {String}
|
|
145
|
+
*/
|
|
146
|
+
export function fromUTF8(s) {
|
|
147
|
+
let result = ''
|
|
148
|
+
let i = 0
|
|
149
|
+
let c1 = 0
|
|
150
|
+
let c2 = 0
|
|
151
|
+
let c3 = 0
|
|
152
|
+
|
|
153
|
+
while (i < s.length) {
|
|
154
|
+
c1 = s.charCodeAt(i)
|
|
155
|
+
|
|
156
|
+
if (c1 < 128) {
|
|
157
|
+
result += String.fromCharCode(c1)
|
|
158
|
+
i++
|
|
159
|
+
}
|
|
160
|
+
else if ((c1 > 191) && (c1 < 224)) {
|
|
161
|
+
c2 = s.charCodeAt(i + 1)
|
|
162
|
+
result += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63))
|
|
163
|
+
i += 2
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
c2 = s.charCodeAt(i + 1)
|
|
167
|
+
c3 = s.charCodeAt(i + 2)
|
|
168
|
+
result += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63))
|
|
169
|
+
i += 3
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Intelligently search for an input sequence in a string. All characters in the input sequence must be present in the
|
|
178
|
+
* searched string, in the respective order.
|
|
179
|
+
* @alias qui.utils.string.intelliSearch
|
|
180
|
+
* @param {String} s string to search into
|
|
181
|
+
* @param {String} search string to search for
|
|
182
|
+
* @returns {?RegExpMatchArray}
|
|
183
|
+
*/
|
|
184
|
+
export function intelliSearch(s, search) {
|
|
185
|
+
let rexStr = Array.prototype.map.call(search, function (c) {
|
|
186
|
+
return `${REGEX_ESCAPE_CHARS.includes(c) ? '\\' : ''}${c}.*`
|
|
187
|
+
}).join('')
|
|
188
|
+
|
|
189
|
+
let rex = new RegExp(rexStr, 'i')
|
|
190
|
+
|
|
191
|
+
return s.match(rex)
|
|
192
|
+
}
|