@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
@@ -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
+ }