datafolder 1.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/README.md +169 -0
- package/index.js +3 -0
- package/package.json +27 -0
- package/sample.js +37 -0
- package/scripts/airbnb.js +73 -0
- package/scripts/airbnb2.js +74 -0
- package/scripts/airbnb2_queries.js +231 -0
- package/scripts/airbnb_queries.js +264 -0
- package/scripts/downloadFile.js +42 -0
- package/scripts/express.js +46 -0
- package/scripts/fastify.js +79 -0
- package/scripts/flights.js +75 -0
- package/scripts/flights2.js +81 -0
- package/scripts/flights2_queries.js +453 -0
- package/scripts/flights_queries.js +403 -0
- package/src/QyAcl.js +252 -0
- package/src/QyAclSaver.js +14 -0
- package/src/QyAclSaver_Worker.js +29 -0
- package/src/QyAclWriter.js +19 -0
- package/src/QyBatch.js +501 -0
- package/src/QyBinWriter.js +140 -0
- package/src/QyCD.js +61 -0
- package/src/QyCache.js +212 -0
- package/src/QyCompressor.js +60 -0
- package/src/QyCompressor_Worker.js +13 -0
- package/src/QyDB.js +122 -0
- package/src/QyDefaultOptions.js +59 -0
- package/src/QyDir.js +192 -0
- package/src/QyFetch.js +38 -0
- package/src/QyFile.js +124 -0
- package/src/QyFileContentUtils.js +175 -0
- package/src/QyFileLogger.js +58 -0
- package/src/QyIndex.js +237 -0
- package/src/QyIndexPropTree.js +92 -0
- package/src/QyIntersectNode.js +48 -0
- package/src/QyItem.js +105 -0
- package/src/QyJsonSaver.js +20 -0
- package/src/QyKVData.js +289 -0
- package/src/QyKVDataCleaner.js +17 -0
- package/src/QyKVDataCleaner_Worker.js +94 -0
- package/src/QyKVDataLoader.js +65 -0
- package/src/QyKVDataSaver.js +17 -0
- package/src/QyKVDataSaver_Worker.js +81 -0
- package/src/QyLogFileSaver.js +14 -0
- package/src/QyLogFileSaver_Worker.js +26 -0
- package/src/QyLogger.js +76 -0
- package/src/QyMessageWorker.js +41 -0
- package/src/QyMessager.js +84 -0
- package/src/QyMessager_Run.js +13 -0
- package/src/QyMod.js +15 -0
- package/src/QyOpRunner.js +73 -0
- package/src/QyQueue.js +60 -0
- package/src/QySaver.js +83 -0
- package/src/QySnapshotCompactor.js +17 -0
- package/src/QySnapshotCompactor_Worker.js +151 -0
- package/src/QySnapshotSaver.js +17 -0
- package/src/QySnapshotSaver_Worker.js +164 -0
- package/src/QySnapshots.js +354 -0
- package/src/QyStructure.js +50 -0
- package/src/QyTrigger.js +133 -0
- package/src/QyUnionNode.js +105 -0
- package/src/QyUtils.js +250 -0
- package/src/QyWorker.js +33 -0
- package/src/hackernews.js +93 -0
- package/src/rpc/evalCode.js +9 -0
- package/src/rpc/getFileContent.js +16 -0
- package/src/rpc/getSubItems.js +32 -0
- package/src/rpc/queryFiles.js +33 -0
- package/src/rpc/reloadRpc.js +1 -0
- package/src/simulate.js +114 -0
- package/src/simulate2.js +110 -0
- package/src/soc_pokec.js +43 -0
- package/static/bootstrap-5.3.8-dist/css/bootstrap.min.css +6 -0
- package/static/bootstrap-5.3.8-dist/js/bootstrap.bundle.js +6312 -0
- package/static/datafolderui/QyStyledElement.js +30 -0
- package/static/datafolderui/QyUIFolderTree.js +203 -0
- package/static/datafolderui/QyUIUtils.js +42 -0
- package/static/datafolderui/display-tree.js +108 -0
- package/static/datafolderui/explore-tree.js +122 -0
- package/static/datafolderui/folder-tree.js +5 -0
- package/static/datafolderui/hscrpt.js +25 -0
- package/static/datafolderui/index.css +6 -0
- package/static/datafolderui/index.html +20 -0
- package/static/datafolderui/json-view.js +74 -0
- package/static/datafolderui/kv-input.js +66 -0
- package/static/datafolderui/list-input.js +52 -0
- package/static/datafolderui/main-page.js +98 -0
- package/static/datafolderui/num-input.js +27 -0
- package/static/datafolderui/query-editor.js +229 -0
- package/static/datafolderui/querySelectorShadowRoot.js +24 -0
- package/static/datafolderui/resizable-input.js +67 -0
- package/static/favicon.ico +0 -0
- package/static/icons-1.13.1/font/bootstrap-icons.min.css +5 -0
- package/static/icons-1.13.1/font/fonts/bootstrap-icons.woff +0 -0
- package/static/icons-1.13.1/font/fonts/bootstrap-icons.woff2 +0 -0
- package/static/json-view/dist/jsonview.js +433 -0
- package/static/tree.js/tree.css +106 -0
- package/static/tree.js/tree.js +374 -0
- package/static/w2ui/dist/w2ui.min.css +2 -0
- package/static/w2ui/src/less/icons/icons.json +1 -0
- package/static/w2ui/src/less/icons/preview.html +33 -0
- package/static/w2ui/src/less/icons/readme.md +15 -0
- package/static/w2ui/src/less/icons/svg/box.svg +3 -0
- package/static/w2ui/src/less/icons/svg/check.svg +3 -0
- package/static/w2ui/src/less/icons/svg/colors.svg +3 -0
- package/static/w2ui/src/less/icons/svg/columns.svg +5 -0
- package/static/w2ui/src/less/icons/svg/cross.svg +4 -0
- package/static/w2ui/src/less/icons/svg/drop.svg +6 -0
- package/static/w2ui/src/less/icons/svg/empty.svg +3 -0
- package/static/w2ui/src/less/icons/svg/eye-dropper.svg +4 -0
- package/static/w2ui/src/less/icons/svg/info.svg +8 -0
- package/static/w2ui/src/less/icons/svg/paste.svg +4 -0
- package/static/w2ui/src/less/icons/svg/pencil.svg +5 -0
- package/static/w2ui/src/less/icons/svg/plus.svg +3 -0
- package/static/w2ui/src/less/icons/svg/reload.svg +5 -0
- package/static/w2ui/src/less/icons/svg/search.svg +3 -0
- package/static/w2ui/src/less/icons/svg/settings.svg +8 -0
- package/static/w2ui/src/less/icons/w2ui-font.css +36 -0
- package/static/w2ui/src/less/icons/w2ui-font.woff +0 -0
- package/static/w2ui/src/less/src/buttons.less +184 -0
- package/static/w2ui/src/less/src/common.less +541 -0
- package/static/w2ui/src/less/src/fields.less +302 -0
- package/static/w2ui/src/less/src/form.less +319 -0
- package/static/w2ui/src/less/src/grid.less +1057 -0
- package/static/w2ui/src/less/src/icons.less +37 -0
- package/static/w2ui/src/less/src/layout.less +86 -0
- package/static/w2ui/src/less/src/mixins.less +87 -0
- package/static/w2ui/src/less/src/popup.less +277 -0
- package/static/w2ui/src/less/src/sidebar.less +343 -0
- package/static/w2ui/src/less/src/tabs.less +145 -0
- package/static/w2ui/src/less/src/toolbar.less +219 -0
- package/static/w2ui/src/less/src/tooltip.less +948 -0
- package/static/w2ui/src/less/w2ui.less +196 -0
- package/static/w2ui/src/locale/az-az.json +114 -0
- package/static/w2ui/src/locale/ba-ba.json +114 -0
- package/static/w2ui/src/locale/bg-bg.json +114 -0
- package/static/w2ui/src/locale/ca-es.json +114 -0
- package/static/w2ui/src/locale/da-dk.json +114 -0
- package/static/w2ui/src/locale/de-de.json +114 -0
- package/static/w2ui/src/locale/en-gb.json +17 -0
- package/static/w2ui/src/locale/en-us.json +17 -0
- package/static/w2ui/src/locale/es-ar.json +115 -0
- package/static/w2ui/src/locale/es-cr.json +119 -0
- package/static/w2ui/src/locale/es-es.json +114 -0
- package/static/w2ui/src/locale/es-mx.json +114 -0
- package/static/w2ui/src/locale/fr-fr.json +114 -0
- package/static/w2ui/src/locale/gl-es.json +114 -0
- package/static/w2ui/src/locale/hr-hr.json +114 -0
- package/static/w2ui/src/locale/hu-hu.json +114 -0
- package/static/w2ui/src/locale/id-id.json +114 -0
- package/static/w2ui/src/locale/it-it.json +114 -0
- package/static/w2ui/src/locale/ja-jp.json +114 -0
- package/static/w2ui/src/locale/kk-kz.json +116 -0
- package/static/w2ui/src/locale/ko-kr.json +114 -0
- package/static/w2ui/src/locale/lt-lt.json +114 -0
- package/static/w2ui/src/locale/nl-nl.json +114 -0
- package/static/w2ui/src/locale/no-no.json +114 -0
- package/static/w2ui/src/locale/pl-pl.json +114 -0
- package/static/w2ui/src/locale/pt-br.json +114 -0
- package/static/w2ui/src/locale/ru-ru.json +116 -0
- package/static/w2ui/src/locale/sk-sk.json +114 -0
- package/static/w2ui/src/locale/sl-si.json +116 -0
- package/static/w2ui/src/locale/sr-rs.json +114 -0
- package/static/w2ui/src/locale/tr-tr.json +114 -0
- package/static/w2ui/src/locale/uk-ua.json +114 -0
- package/static/w2ui/src/locale/zh-cn.json +114 -0
- package/static/w2ui/src/locale/zh-tw.json +114 -0
- package/static/w2ui/src/query.js +583 -0
- package/static/w2ui/src/readme.md +11 -0
- package/static/w2ui/src/w2base.js +311 -0
- package/static/w2ui/src/w2compat.js +207 -0
- package/static/w2ui/src/w2field.js +1854 -0
- package/static/w2ui/src/w2form.js +2411 -0
- package/static/w2ui/src/w2grid.js +8916 -0
- package/static/w2ui/src/w2layout.js +1194 -0
- package/static/w2ui/src/w2locale.js +129 -0
- package/static/w2ui/src/w2popup.js +909 -0
- package/static/w2ui/src/w2sidebar.js +1769 -0
- package/static/w2ui/src/w2tabs.js +669 -0
- package/static/w2ui/src/w2toolbar.js +1180 -0
- package/static/w2ui/src/w2tooltip.js +3134 -0
- package/static/w2ui/src/w2utils.js +2457 -0
|
@@ -0,0 +1,1854 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Part of w2ui 2.0 library
|
|
3
|
+
* - Dependencies: mQuery, w2utils, w2base, w2tooltip, w2color, w2menu, w2date
|
|
4
|
+
*
|
|
5
|
+
* == TODO ==
|
|
6
|
+
* - upload (regular files)
|
|
7
|
+
* - BUG with prefix/postfix and arrows (test in different contexts)
|
|
8
|
+
* - multiple date selection
|
|
9
|
+
* - month selection, year selections
|
|
10
|
+
* - MultiSelect - Allow Copy/Paste for single and multi values
|
|
11
|
+
* - add routeData to list/enum
|
|
12
|
+
* - ENUM, LIST: should have same as grid (limit, offset, search, sort)
|
|
13
|
+
* - ENUM, LIST: should support wild chars
|
|
14
|
+
* - add selection of predefined times (used for appointments)
|
|
15
|
+
* - options.items - can be an array
|
|
16
|
+
* - options.msgNoItems - can be a function
|
|
17
|
+
* - REMOTE fields
|
|
18
|
+
*
|
|
19
|
+
* == 2.0 changes
|
|
20
|
+
* - removed jQuery dependency
|
|
21
|
+
* - enum options.autoAdd
|
|
22
|
+
* - [numeric, date] - options.autoCorrect to enforce range and validity
|
|
23
|
+
* - remote source response items => records or just an array
|
|
24
|
+
* - deprecated "success" field for remote source response
|
|
25
|
+
* - CSP - fixed inline events
|
|
26
|
+
* - remove clear, use reset instead
|
|
27
|
+
* - options.msgSearch
|
|
28
|
+
* - options.msgNoItems
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import query from './query.js'
|
|
32
|
+
import { w2base } from './w2base.js'
|
|
33
|
+
import { w2utils } from './w2utils.js'
|
|
34
|
+
import { w2tooltip, w2color, w2menu, w2date } from './w2tooltip.js'
|
|
35
|
+
|
|
36
|
+
class w2field extends w2base {
|
|
37
|
+
constructor(type, options) {
|
|
38
|
+
super()
|
|
39
|
+
// sanitization
|
|
40
|
+
if (typeof type == 'string' && options == null) {
|
|
41
|
+
options = { type: type }
|
|
42
|
+
}
|
|
43
|
+
if (typeof type == 'object' && options == null) {
|
|
44
|
+
options = w2utils.clone(type)
|
|
45
|
+
}
|
|
46
|
+
if (typeof type == 'string' && typeof options == 'object') {
|
|
47
|
+
options.type = type
|
|
48
|
+
}
|
|
49
|
+
options.type = String(options.type).toLowerCase()
|
|
50
|
+
this.el = options.el ?? null
|
|
51
|
+
this.selected = null
|
|
52
|
+
this.helpers = {} // object or helper elements
|
|
53
|
+
this.type = options.type ?? 'text'
|
|
54
|
+
this.options = w2utils.clone(options)
|
|
55
|
+
this.onClick = options.onClick ?? null
|
|
56
|
+
this.onAdd = options.onAdd ?? null
|
|
57
|
+
this.onNew = options.onNew ?? null
|
|
58
|
+
this.onRemove = options.onRemove ?? null
|
|
59
|
+
this.onMouseEnter= options.onMouseEnter ?? null
|
|
60
|
+
this.onMouseLeave= options.onMouseLeave ?? null
|
|
61
|
+
this.onScroll = options.onScroll ?? null
|
|
62
|
+
this.tmp = {} // temp object
|
|
63
|
+
// clean up some options
|
|
64
|
+
delete this.options.type
|
|
65
|
+
delete this.options.onClick
|
|
66
|
+
delete this.options.onMouseEnter
|
|
67
|
+
delete this.options.onMouseLeave
|
|
68
|
+
delete this.options.onScroll
|
|
69
|
+
|
|
70
|
+
if (this.el) {
|
|
71
|
+
this.render(this.el)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
render(el) {
|
|
76
|
+
if (!(el instanceof HTMLElement)) {
|
|
77
|
+
console.log('ERROR: Cannot init w2field on empty subject')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
el._w2field?.reset?.() // will remove all previous events
|
|
81
|
+
el._w2field = this
|
|
82
|
+
this.el = el
|
|
83
|
+
this.init()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
init() {
|
|
87
|
+
let options = this.options
|
|
88
|
+
let defaults
|
|
89
|
+
|
|
90
|
+
// only for INPUT or TEXTAREA
|
|
91
|
+
if (!['INPUT', 'TEXTAREA'].includes(this.el.tagName.toUpperCase())) {
|
|
92
|
+
console.log('ERROR: w2field could only be applied to INPUT or TEXTAREA.', this.el)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
switch (this.type) {
|
|
97
|
+
case 'text':
|
|
98
|
+
case 'int':
|
|
99
|
+
case 'float':
|
|
100
|
+
case 'money':
|
|
101
|
+
case 'currency':
|
|
102
|
+
case 'percent':
|
|
103
|
+
case 'alphanumeric':
|
|
104
|
+
case 'bin':
|
|
105
|
+
case 'hex': {
|
|
106
|
+
defaults = {
|
|
107
|
+
min: null,
|
|
108
|
+
max: null,
|
|
109
|
+
step: 1,
|
|
110
|
+
autoFormat: true,
|
|
111
|
+
autoCorrect: true,
|
|
112
|
+
currency: {
|
|
113
|
+
prefix: w2utils.settings.currencyPrefix,
|
|
114
|
+
suffix: w2utils.settings.currencySuffix,
|
|
115
|
+
precision: w2utils.settings.currencyPrecision
|
|
116
|
+
},
|
|
117
|
+
decimalSymbol: w2utils.settings.decimalSymbol,
|
|
118
|
+
groupSymbol: w2utils.settings.groupSymbol,
|
|
119
|
+
arrows: false,
|
|
120
|
+
keyboard: true,
|
|
121
|
+
precision: null,
|
|
122
|
+
prefix: '',
|
|
123
|
+
suffix: ''
|
|
124
|
+
}
|
|
125
|
+
this.options = w2utils.extend({}, defaults, options)
|
|
126
|
+
options = this.options // since object is re-created, need to re-assign
|
|
127
|
+
options.numberRE = new RegExp('['+ options.groupSymbol + ']', 'g')
|
|
128
|
+
options.moneyRE = new RegExp('['+ options.currency.prefix + options.currency.suffix + options.groupSymbol +']', 'g')
|
|
129
|
+
options.percentRE = new RegExp('['+ options.groupSymbol + '%]', 'g')
|
|
130
|
+
// no keyboard support needed
|
|
131
|
+
if (['text', 'alphanumeric', 'hex', 'bin'].includes(this.type)) {
|
|
132
|
+
options.arrows = false
|
|
133
|
+
options.keyboard = false
|
|
134
|
+
}
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
case 'color': {
|
|
138
|
+
let size = parseInt(getComputedStyle(this.el)['font-size']) || 12
|
|
139
|
+
defaults = {
|
|
140
|
+
prefix : '#',
|
|
141
|
+
suffix : `<div style="width: ${size}px; height: ${size}px; margin-top: -2px;
|
|
142
|
+
position: relative; top: 50%; transform: translateY(-50%);"> </div>`,
|
|
143
|
+
arrows : false,
|
|
144
|
+
advanced : null, // open advanced by default
|
|
145
|
+
transparent : true
|
|
146
|
+
}
|
|
147
|
+
this.options = w2utils.extend({}, defaults, options)
|
|
148
|
+
options = this.options // since object is re-created, need to re-assign
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
case 'date': {
|
|
152
|
+
defaults = {
|
|
153
|
+
format : w2utils.settings.dateFormat, // date format
|
|
154
|
+
keyboard : true, // if true, allows to select date with format
|
|
155
|
+
autoCorrect : true, // correc date or shows the error
|
|
156
|
+
start : null, // first date allowed to select
|
|
157
|
+
end : null, // last date allowed to select
|
|
158
|
+
blockDates : [], // array of blocked dates
|
|
159
|
+
blockWeekdays : [], // blocked weekdays 0 - sunday, 1 - monday, etc
|
|
160
|
+
colored : {}, // ex: { '3/13/2022': 'bg-color|text-color' }
|
|
161
|
+
btnNow : true // if true, displays Now button
|
|
162
|
+
}
|
|
163
|
+
this.options = w2utils.extend({ type: 'date' }, defaults, options)
|
|
164
|
+
options = this.options // since object is re-created, need to re-assign
|
|
165
|
+
if (query(this.el).attr('placeholder') == null) {
|
|
166
|
+
query(this.el).attr('placeholder', options.format)
|
|
167
|
+
}
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
case 'time': {
|
|
171
|
+
defaults = {
|
|
172
|
+
format : w2utils.settings.timeFormat,
|
|
173
|
+
keyboard : true,
|
|
174
|
+
autoCorrect : true,
|
|
175
|
+
start : null,
|
|
176
|
+
end : null,
|
|
177
|
+
btnNow : true,
|
|
178
|
+
noMinutes : false
|
|
179
|
+
}
|
|
180
|
+
this.options = w2utils.extend({ type: 'time' }, defaults, options)
|
|
181
|
+
options = this.options // since object is re-created, need to re-assign
|
|
182
|
+
if (query(this.el).attr('placeholder') == null) {
|
|
183
|
+
query(this.el).attr('placeholder', options.format)
|
|
184
|
+
}
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
case 'datetime': {
|
|
188
|
+
defaults = {
|
|
189
|
+
format : w2utils.settings.dateFormat + '|' + w2utils.settings.timeFormat,
|
|
190
|
+
keyboard : true,
|
|
191
|
+
autoCorrect : true,
|
|
192
|
+
start : null,
|
|
193
|
+
end : null,
|
|
194
|
+
startTime : null,
|
|
195
|
+
endTime : null,
|
|
196
|
+
blockDates : [], // array of blocked dates
|
|
197
|
+
blockWeekdays : [], // blocked weekdays 0 - sunday, 1 - monday, etc
|
|
198
|
+
colored : {}, // ex: { '3/13/2022': 'bg-color|text-color' }
|
|
199
|
+
btnNow : true,
|
|
200
|
+
noMinutes : false
|
|
201
|
+
}
|
|
202
|
+
this.options = w2utils.extend({ type: 'datetime' }, defaults, options)
|
|
203
|
+
options = this.options // since object is re-created, need to re-assign
|
|
204
|
+
if (query(this.el).attr('placeholder') == null) {
|
|
205
|
+
query(this.el).attr('placeholder', options.placeholder || options.format)
|
|
206
|
+
}
|
|
207
|
+
break
|
|
208
|
+
}
|
|
209
|
+
case 'list':
|
|
210
|
+
case 'combo': {
|
|
211
|
+
defaults = {
|
|
212
|
+
items : [], // array of items, can be a function
|
|
213
|
+
selected : {}, // selected item
|
|
214
|
+
itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item
|
|
215
|
+
match : 'begins', // ['contains', 'is', 'begins', 'ends']
|
|
216
|
+
filter : true, // weather to filter at all
|
|
217
|
+
compare : null, // compare function for filtering
|
|
218
|
+
prefix : '', // prefix for input
|
|
219
|
+
suffix : '', // sufix for input
|
|
220
|
+
icon : null, // icon class for selected item
|
|
221
|
+
iconStyle : '', // icon style for selected item
|
|
222
|
+
// -- remote items --
|
|
223
|
+
url : null, // remove data source for items
|
|
224
|
+
method : null, // default comes from w2utils.settings.dataType
|
|
225
|
+
postData : {}, // additional data to submit to URL
|
|
226
|
+
recId : null, // map retrieved data from url to id, can be string or function
|
|
227
|
+
recText : null, // map retrieved data from url to text, can be string or function
|
|
228
|
+
debounce : 250, // number of ms to wait before sending server call on search
|
|
229
|
+
minLength : 1, // min number of chars when trigger search
|
|
230
|
+
cacheMax : 250,
|
|
231
|
+
// -- drop items --
|
|
232
|
+
renderDrop : null, // render function for drop down item
|
|
233
|
+
maxDropHeight : 350, // max height for drop down menu
|
|
234
|
+
maxDropWidth : null, // if null then auto set
|
|
235
|
+
minDropWidth : null, // if null then auto set
|
|
236
|
+
// -- misc --
|
|
237
|
+
markSearch : false, // if true, highlights search phrase
|
|
238
|
+
align : 'both', // align with the input ['left', 'right', 'both', 'none']
|
|
239
|
+
altRows : true, // alternate row color for drop itesm
|
|
240
|
+
openOnFocus : false, // if true, shows drop items on focus
|
|
241
|
+
hideSelected : false, // hide selected item from drop down
|
|
242
|
+
msgNoItems : 'No matches',
|
|
243
|
+
msgSearch : 'Type to search...',
|
|
244
|
+
// -- events --
|
|
245
|
+
onSearch : null, // when search needs to be performed
|
|
246
|
+
onRequest : null, // when request is submitted
|
|
247
|
+
onLoad : null, // when data is received
|
|
248
|
+
onError : null, // when data fails to load due to server error
|
|
249
|
+
}
|
|
250
|
+
if (typeof options.items == 'function') {
|
|
251
|
+
options._items_fun = options.items
|
|
252
|
+
}
|
|
253
|
+
// need to be first
|
|
254
|
+
options.items = w2utils.normMenu.call(this, options.items, options)
|
|
255
|
+
if (this.type === 'list') {
|
|
256
|
+
// defaults.search = (options.items && options.items.length >= 10 ? true : false);
|
|
257
|
+
query(this.el).addClass('w2ui-select')
|
|
258
|
+
// if simple value - look it up
|
|
259
|
+
if (!w2utils.isPlainObject(options.selected) && Array.isArray(options.items)) {
|
|
260
|
+
options.items.forEach(item => {
|
|
261
|
+
if (item && item.id === options.selected) {
|
|
262
|
+
options.selected = w2utils.clone(item)
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
options = w2utils.extend({}, defaults, options)
|
|
268
|
+
// validate match
|
|
269
|
+
let valid = ['is', 'begins', 'contains', 'ends']
|
|
270
|
+
if (!valid.includes(options.match)) {
|
|
271
|
+
console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`)
|
|
272
|
+
}
|
|
273
|
+
this.options = options
|
|
274
|
+
if (!w2utils.isPlainObject(options.selected)) options.selected = {}
|
|
275
|
+
this.selected = options.selected
|
|
276
|
+
query(this.el)
|
|
277
|
+
.attr('autocapitalize', 'off')
|
|
278
|
+
.attr('autocomplete', 'off')
|
|
279
|
+
.attr('autocorrect', 'off')
|
|
280
|
+
.attr('spellcheck', 'false')
|
|
281
|
+
if (options.selected.text != null) {
|
|
282
|
+
query(this.el).val(options.selected.text)
|
|
283
|
+
}
|
|
284
|
+
break
|
|
285
|
+
}
|
|
286
|
+
case 'enum': {
|
|
287
|
+
defaults = {
|
|
288
|
+
items : [], // id, text, tooltip, icon
|
|
289
|
+
selected : [],
|
|
290
|
+
itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item
|
|
291
|
+
max : 0, // max number of selected items, 0 - unlimited
|
|
292
|
+
match : 'begins', // ['contains', 'is', 'begins', 'ends']
|
|
293
|
+
filter : true, // if true, will apply filtering
|
|
294
|
+
compare : null, // compare function for filtering
|
|
295
|
+
// -- remote items --
|
|
296
|
+
url : null, // remove source for items
|
|
297
|
+
method : null, // default httpMethod
|
|
298
|
+
postData : {},
|
|
299
|
+
recId : null, // map retrieved data from url to id, can be string or function
|
|
300
|
+
recText : null, // map retrieved data from url to text, can be string or function
|
|
301
|
+
debounce : 250, // number of ms to wait before sending server call on search
|
|
302
|
+
minLength : 1, // min number of chars when trigger search
|
|
303
|
+
cacheMax : 250,
|
|
304
|
+
// -- item and drop items --
|
|
305
|
+
maxItemWidth : 250, // max width for a single item
|
|
306
|
+
maxDropHeight : 350, // max height for drop down menu
|
|
307
|
+
maxDropWidth : null, // if null then auto set
|
|
308
|
+
renderItem : null, // render selected item
|
|
309
|
+
renderDrop : null, // render function for drop down item
|
|
310
|
+
// -- misc --
|
|
311
|
+
style : '', // style for container div
|
|
312
|
+
openOnFocus : false, // if true, opens drop down on focus
|
|
313
|
+
markSearch : false, // if true, highlights search phrase
|
|
314
|
+
align : 'both',// align with the input ['left', 'right', 'both', 'none']
|
|
315
|
+
altRows : true, // if ture, will use alternate row colors
|
|
316
|
+
hideSelected : true, // hide selected items from drop down
|
|
317
|
+
msgNoItems : 'No matches',
|
|
318
|
+
msgSearch : 'Type to search...',
|
|
319
|
+
// -- events --
|
|
320
|
+
onAdd : null, // when item is selected from drop down
|
|
321
|
+
onNew : null, // when new item should be added
|
|
322
|
+
onRemove : null, // when item is removed
|
|
323
|
+
onSearch : null, // when search is triggered
|
|
324
|
+
onClick : null, // when item is clicked
|
|
325
|
+
onRequest : null, // when data is requested
|
|
326
|
+
onLoad : null, // when data is received
|
|
327
|
+
onError : null, // when data fails to load due to server error
|
|
328
|
+
onScroll : null, // when div with selected items is scrolled
|
|
329
|
+
onMouseEnter : null, // when mouse enters item
|
|
330
|
+
onMouseLeave : null, // when mouse leaves item
|
|
331
|
+
}
|
|
332
|
+
options = w2utils.extend({}, defaults, options, { suffix: '' })
|
|
333
|
+
if (typeof options.items == 'function') {
|
|
334
|
+
options._items_fun = options.items
|
|
335
|
+
}
|
|
336
|
+
// validate match
|
|
337
|
+
let valid = ['is', 'begins', 'contains', 'ends']
|
|
338
|
+
if (!valid.includes(options.match)) {
|
|
339
|
+
console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`)
|
|
340
|
+
}
|
|
341
|
+
options.items = w2utils.normMenu.call(this, options.items, options)
|
|
342
|
+
options.selected = w2utils.normMenu.call(this, options.selected, options)
|
|
343
|
+
this.options = options
|
|
344
|
+
if (!Array.isArray(options.selected)) options.selected = []
|
|
345
|
+
this.selected = options.selected
|
|
346
|
+
break
|
|
347
|
+
}
|
|
348
|
+
case 'file': {
|
|
349
|
+
defaults = {
|
|
350
|
+
selected : [], // array of selected files
|
|
351
|
+
max : 0, // max number of selected files, 0 - unlim
|
|
352
|
+
maxSize : 0, // max size of all files, 0 - unlimited
|
|
353
|
+
maxFileSize : 0, // max size of a single file, 0 -unlimited
|
|
354
|
+
renderItem : null, // render function fo the selected item
|
|
355
|
+
// -- misc --
|
|
356
|
+
maxItemWidth : 250, // max width for a single item
|
|
357
|
+
maxDropHeight : 350, // max height for drop down menu
|
|
358
|
+
maxDropWidth : null, // if null then auto set
|
|
359
|
+
readContent : true, // if true, it will readAsDataURL content of the file
|
|
360
|
+
showErrors : true, // if not true, will show errors
|
|
361
|
+
align : 'both', // align with the input ['left', 'right', 'both', 'none']
|
|
362
|
+
altRows : true, // alternate row color for drop itesm
|
|
363
|
+
style : '', // style for container div
|
|
364
|
+
// -- events --
|
|
365
|
+
onClick : null, // when item is clicked
|
|
366
|
+
onAdd : null, // when item is added
|
|
367
|
+
onRemove : null, // when item is removed
|
|
368
|
+
onMouseEnter : null, // when item is mouse over
|
|
369
|
+
onMouseLeave : null // when item is mouse out
|
|
370
|
+
}
|
|
371
|
+
options = w2utils.extend({}, defaults, options)
|
|
372
|
+
this.options = options
|
|
373
|
+
if (!Array.isArray(options.selected)) options.selected = []
|
|
374
|
+
this.selected = options.selected
|
|
375
|
+
if (query(this.el).attr('placeholder') == null) {
|
|
376
|
+
query(this.el).attr('placeholder', w2utils.lang('Attach files by dragging and dropping or Click to Select'))
|
|
377
|
+
}
|
|
378
|
+
break
|
|
379
|
+
}
|
|
380
|
+
default: {
|
|
381
|
+
console.log(`ERROR: field type "${this.type}" is not supported.`)
|
|
382
|
+
break
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// attach events
|
|
386
|
+
query(this.el)
|
|
387
|
+
.css('box-sizing', 'border-box')
|
|
388
|
+
.addClass('w2field w2ui-input')
|
|
389
|
+
.off('.w2field')
|
|
390
|
+
.on('change.w2field', (event) => { this.change(event) })
|
|
391
|
+
.on('click.w2field', (event) => { this.click(event) })
|
|
392
|
+
.on('focus.w2field', (event) => { this.focus(event) })
|
|
393
|
+
.on('blur.w2field', (event) => { if (this.type !== 'list') this.blur(event) })
|
|
394
|
+
.on('keydown.w2field', (event) => { this.keyDown(event) })
|
|
395
|
+
.on('keyup.w2field', (event) => { this.keyUp(event) })
|
|
396
|
+
// suffix and prefix need to be after styles
|
|
397
|
+
this.addPrefix() // only will add if needed
|
|
398
|
+
this.addSuffix() // only will add if needed
|
|
399
|
+
this.addSearch()
|
|
400
|
+
this.addMultiSearch()
|
|
401
|
+
// this.refresh() // do not call refresh, on change will trigger refresh (for list at list)
|
|
402
|
+
// format initial value
|
|
403
|
+
this.change(new Event('change'))
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
get() {
|
|
407
|
+
let ret
|
|
408
|
+
if (['list', 'enum', 'file'].indexOf(this.type) !== -1) {
|
|
409
|
+
ret = this.selected
|
|
410
|
+
} else {
|
|
411
|
+
ret = query(this.el).val()
|
|
412
|
+
}
|
|
413
|
+
return ret
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
set(val, append) {
|
|
417
|
+
if (['list', 'enum', 'file'].indexOf(this.type) !== -1) {
|
|
418
|
+
let overlay = w2menu.get(this.el.id + '_menu')
|
|
419
|
+
overlay?.hide()
|
|
420
|
+
if (this.type !== 'list' && append) {
|
|
421
|
+
if (!Array.isArray(this.selected)) this.selected = []
|
|
422
|
+
this.selected.push(val)
|
|
423
|
+
// update selected array in overlay
|
|
424
|
+
if (overlay) overlay.options.selected = this.selected
|
|
425
|
+
query(this.el).trigger('input').trigger('change')
|
|
426
|
+
} else {
|
|
427
|
+
if (val == null) val = []
|
|
428
|
+
let it = (this.type === 'enum' && !Array.isArray(val) ? [val] : val)
|
|
429
|
+
this.selected = it
|
|
430
|
+
query(this.el).trigger('input').trigger('change')
|
|
431
|
+
}
|
|
432
|
+
this.refresh()
|
|
433
|
+
} else {
|
|
434
|
+
query(this.el).val(val)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
setIndex(ind, append) {
|
|
439
|
+
if (['list', 'enum'].indexOf(this.type) !== -1) {
|
|
440
|
+
let overlay = w2menu.get(this.el.id + '_menu')
|
|
441
|
+
overlay?.hide()
|
|
442
|
+
let items = this.options.items
|
|
443
|
+
if (items && items[ind]) {
|
|
444
|
+
if (this.type == 'list') {
|
|
445
|
+
this.selected = items[ind]
|
|
446
|
+
}
|
|
447
|
+
if (this.type == 'enum') {
|
|
448
|
+
if (!append) this.selected = []
|
|
449
|
+
this.selected.push(items[ind])
|
|
450
|
+
}
|
|
451
|
+
if (overlay) overlay.options.selected = this.selected
|
|
452
|
+
query(this.el).trigger('input').trigger('change')
|
|
453
|
+
this.refresh()
|
|
454
|
+
return true
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return false
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
refresh() {
|
|
461
|
+
let options = this.options
|
|
462
|
+
let time = Date.now()
|
|
463
|
+
let styles = getComputedStyle(this.el)
|
|
464
|
+
// update color
|
|
465
|
+
if (this.type == 'color') {
|
|
466
|
+
let color = this.el.value
|
|
467
|
+
if (color.substr(0, 1) != '#' && color.substr(0, 3) != 'rgb') {
|
|
468
|
+
color = '#' + color
|
|
469
|
+
}
|
|
470
|
+
query(this.helpers.suffix).find(':scope > div').css('background-color', color)
|
|
471
|
+
}
|
|
472
|
+
// enum
|
|
473
|
+
if (this.type == 'list') {
|
|
474
|
+
// next line will not work in a form with span: -1
|
|
475
|
+
// query(this.el).parent().css('white-space', 'nowrap') // needs this for arrow always to appear on the right side
|
|
476
|
+
// hide focus and show text
|
|
477
|
+
if (this.helpers.prefix) this.helpers.prefix.hide()
|
|
478
|
+
if (!this.helpers.search) return
|
|
479
|
+
// if empty show no icon
|
|
480
|
+
if (this.selected == null && options.icon) {
|
|
481
|
+
options.prefix = `
|
|
482
|
+
<span class="w2ui-icon ${options.icon} "style="cursor: pointer; font-size: 14px;
|
|
483
|
+
display: inline-block; margin-top: -1px; color: #7F98AD; ${options.iconStyle}">
|
|
484
|
+
</span>`
|
|
485
|
+
this.addPrefix()
|
|
486
|
+
} else {
|
|
487
|
+
options.prefix = ''
|
|
488
|
+
this.addPrefix()
|
|
489
|
+
}
|
|
490
|
+
// focus helper
|
|
491
|
+
let focus = query(this.helpers.search_focus)
|
|
492
|
+
let icon = query(focus[0].previousElementSibling)
|
|
493
|
+
focus.css({ outline: 'none' })
|
|
494
|
+
if (focus.val() === '') {
|
|
495
|
+
focus.css('opacity', 0)
|
|
496
|
+
icon.css('opacity', 0)
|
|
497
|
+
if (this.selected?.id != null) { // id could be "", then it is valid
|
|
498
|
+
let text = this.selected.text
|
|
499
|
+
let ind = this.findItemIndex(options.items, this.selected.id)
|
|
500
|
+
if (text != null) {
|
|
501
|
+
query(this.el)
|
|
502
|
+
.val(w2utils.lang(text))
|
|
503
|
+
.data({
|
|
504
|
+
selected: text,
|
|
505
|
+
selectedIndex: ind[0]
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
this.el.value = ''
|
|
510
|
+
query(this.el).removeData('selected selectedIndex')
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
focus.css('opacity', 1)
|
|
514
|
+
icon.css('opacity', 1)
|
|
515
|
+
query(this.el).val('')
|
|
516
|
+
setTimeout(() => {
|
|
517
|
+
if (this.helpers.prefix) this.helpers.prefix.hide()
|
|
518
|
+
if (options.icon) {
|
|
519
|
+
focus.css('margin-left', '17px')
|
|
520
|
+
query(this.helpers.search).find('.w2ui-icon-search')
|
|
521
|
+
.addClass('show-search')
|
|
522
|
+
} else {
|
|
523
|
+
focus.css('margin-left', '0px')
|
|
524
|
+
query(this.helpers.search).find('.w2ui-icon-search')
|
|
525
|
+
.removeClass('show-search')
|
|
526
|
+
}
|
|
527
|
+
}, 1)
|
|
528
|
+
}
|
|
529
|
+
// if readonly or disabled
|
|
530
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) {
|
|
531
|
+
setTimeout(() => {
|
|
532
|
+
if (this.helpers.prefix) query(this.helpers.prefix).css('opacity', '0.6')
|
|
533
|
+
if (this.helpers.suffix) query(this.helpers.suffix).css('opacity', '0.6')
|
|
534
|
+
}, 1)
|
|
535
|
+
} else {
|
|
536
|
+
setTimeout(() => {
|
|
537
|
+
if (this.helpers.prefix) query(this.helpers.prefix).css('opacity', '1')
|
|
538
|
+
if (this.helpers.suffix) query(this.helpers.suffix).css('opacity', '1')
|
|
539
|
+
}, 1)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// multi select control
|
|
543
|
+
let div = this.helpers.multi
|
|
544
|
+
if (['enum', 'file'].includes(this.type) && div) {
|
|
545
|
+
let html = ''
|
|
546
|
+
if (Array.isArray(this.selected)) {
|
|
547
|
+
this.selected.forEach((it, ind) => {
|
|
548
|
+
if (it == null) return
|
|
549
|
+
html += `
|
|
550
|
+
<div class="li-item" index="${ind}" style="max-width: ${parseInt(options.maxItemWidth)}px; ${it.style ? it.style : ''}">
|
|
551
|
+
${
|
|
552
|
+
typeof options.renderItem === 'function'
|
|
553
|
+
? options.renderItem(it, ind, `<div class="w2ui-list-remove" index="${ind}">  </div>`)
|
|
554
|
+
: `
|
|
555
|
+
${it.icon ? `<span class="w2ui-icon ${it.icon}"></span>` : ''}
|
|
556
|
+
<div class="w2ui-list-remove" index="${ind}">  </div>
|
|
557
|
+
${(this.type === 'enum' ? it.text : it.name) ?? it.id ?? it }
|
|
558
|
+
${it.size ? `<span class="file-size"> - ${w2utils.formatSize(it.size)}</span>` : ''}
|
|
559
|
+
`
|
|
560
|
+
}
|
|
561
|
+
</div>`
|
|
562
|
+
})
|
|
563
|
+
}
|
|
564
|
+
let ul = div.find('.w2ui-multi-items')
|
|
565
|
+
if (options.style) {
|
|
566
|
+
div.attr('style', div.attr('style') + ';' + options.style)
|
|
567
|
+
}
|
|
568
|
+
query(this.el).css('z-index', '-1')
|
|
569
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) {
|
|
570
|
+
setTimeout(() => {
|
|
571
|
+
div[0].scrollTop = 0 // scroll to the top
|
|
572
|
+
div.addClass('w2ui-readonly')
|
|
573
|
+
.find('.li-item').css('opacity', '0.9')
|
|
574
|
+
.parent().find('.li-search').hide()
|
|
575
|
+
.find('input').prop('readOnly', true)
|
|
576
|
+
.closest('.w2ui-multi-items')
|
|
577
|
+
.find('.w2ui-list-remove').hide()
|
|
578
|
+
}, 1)
|
|
579
|
+
} else {
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
div.removeClass('w2ui-readonly')
|
|
582
|
+
.find('.li-item').css('opacity', '1')
|
|
583
|
+
.parent().find('.li-search').show()
|
|
584
|
+
.find('input').prop('readOnly', false)
|
|
585
|
+
.closest('.w2ui-multi-items')
|
|
586
|
+
.find('.w2ui-list-remove').show()
|
|
587
|
+
}, 1)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// clean
|
|
591
|
+
if (this.selected?.length > 0) {
|
|
592
|
+
query(this.el).attr('placeholder', '')
|
|
593
|
+
}
|
|
594
|
+
div.find('.w2ui-enum-placeholder').remove()
|
|
595
|
+
ul.find('.li-item').remove()
|
|
596
|
+
|
|
597
|
+
// add new list
|
|
598
|
+
if (html !== '') {
|
|
599
|
+
ul.prepend(html)
|
|
600
|
+
} else if (query(this.el).attr('placeholder') != null && div.find('input').val() === '') {
|
|
601
|
+
let style = w2utils.stripSpaces(`
|
|
602
|
+
padding-top: ${styles['padding-top']};
|
|
603
|
+
padding-left: ${styles['padding-left']};
|
|
604
|
+
box-sizing: ${styles['box-sizing']};
|
|
605
|
+
line-height: ${styles['line-height']};
|
|
606
|
+
font-size: ${styles['font-size']};
|
|
607
|
+
font-family: ${styles['font-family']};
|
|
608
|
+
`)
|
|
609
|
+
div.prepend(`<div class="w2ui-enum-placeholder" style="${style}">${query(this.el).attr('placeholder')}</div>`)
|
|
610
|
+
}
|
|
611
|
+
// ITEMS events
|
|
612
|
+
div.off('.w2item')
|
|
613
|
+
.on('scroll.w2item', (event) => {
|
|
614
|
+
let edata = this.trigger('scroll', { target: this.el, originalEvent: event })
|
|
615
|
+
if (edata.isCancelled === true) return
|
|
616
|
+
// hide tooltip if any
|
|
617
|
+
w2tooltip.hide(this.el.id + '_preview')
|
|
618
|
+
// event after
|
|
619
|
+
edata.finish()
|
|
620
|
+
})
|
|
621
|
+
.find('.li-item')
|
|
622
|
+
.on('click.w2item', (event) => {
|
|
623
|
+
let target = query(event.target).closest('.li-item')
|
|
624
|
+
let index = target.attr('index')
|
|
625
|
+
let item = this.selected[index]
|
|
626
|
+
if (query(target).hasClass('li-search')) return
|
|
627
|
+
event.stopPropagation()
|
|
628
|
+
let edata
|
|
629
|
+
// default behavior
|
|
630
|
+
if (query(event.target).hasClass('w2ui-list-remove')) {
|
|
631
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
632
|
+
// trigger event
|
|
633
|
+
edata = this.trigger('remove', { target: this.el, originalEvent: event, item })
|
|
634
|
+
if (edata.isCancelled === true) return
|
|
635
|
+
// remove file from input element
|
|
636
|
+
let transfer = new DataTransfer()
|
|
637
|
+
let input = query(event.target).closest('.w2ui-list').find('input.file-input').get(0)
|
|
638
|
+
if (input) {
|
|
639
|
+
Array.from(input.files)
|
|
640
|
+
.filter(f => f.name != item.name)
|
|
641
|
+
.forEach(f => transfer.items.add(f))
|
|
642
|
+
input.files = transfer.files
|
|
643
|
+
}
|
|
644
|
+
// remove placeholder in the field
|
|
645
|
+
this.selected.splice(index, 1)
|
|
646
|
+
query(this.el).trigger('input').trigger('change')
|
|
647
|
+
query(event.target).remove()
|
|
648
|
+
} else {
|
|
649
|
+
// trigger event
|
|
650
|
+
edata = this.trigger('click', { target: this.el, originalEvent: event.originalEvent, item })
|
|
651
|
+
if (edata.isCancelled === true) return
|
|
652
|
+
// if file - show image preview
|
|
653
|
+
let preview = item.tooltip
|
|
654
|
+
if (this.type === 'file') {
|
|
655
|
+
if ((/image/i).test(item.type)) { // image
|
|
656
|
+
preview = `
|
|
657
|
+
<div class="w2ui-file-preview">
|
|
658
|
+
<img src="${(item.content ? 'data:'+ item.type +';base64,'+ item.content : '')}"
|
|
659
|
+
style="max-width: 300px">
|
|
660
|
+
</div>`
|
|
661
|
+
}
|
|
662
|
+
preview += `
|
|
663
|
+
<div class="w2ui-file-info">
|
|
664
|
+
<div class="file-caption">${w2utils.lang('Name')}:</div>
|
|
665
|
+
<div class="file-value">${item.name}</div>
|
|
666
|
+
<div class="file-caption">${w2utils.lang('Size')}:</div>
|
|
667
|
+
<div class="file-value">${w2utils.formatSize(item.size)}</div>
|
|
668
|
+
<div class="file-caption">${w2utils.lang('Type')}:</div>
|
|
669
|
+
<div class="file-value file-type">${item.type}</div>
|
|
670
|
+
<div class="file-caption">${w2utils.lang('Modified')}:</div>
|
|
671
|
+
<div class="file-value">${w2utils.date(item.modified)}</div>
|
|
672
|
+
</div>`
|
|
673
|
+
}
|
|
674
|
+
if (preview) {
|
|
675
|
+
let name = this.el.id + '_preview'
|
|
676
|
+
w2tooltip.show({
|
|
677
|
+
name,
|
|
678
|
+
anchor: target.get(0),
|
|
679
|
+
html: preview,
|
|
680
|
+
hideOn: ['doc-click'],
|
|
681
|
+
class: ''
|
|
682
|
+
})
|
|
683
|
+
.show((event) => {
|
|
684
|
+
let $img = query(`#w2overlay-${name} img`)
|
|
685
|
+
$img.on('load', function (event) {
|
|
686
|
+
let w = this.clientWidth
|
|
687
|
+
let h = this.clientHeight
|
|
688
|
+
if (w < 300 & h < 300) return
|
|
689
|
+
if (w >= h && w > 300) query(this).css('width', '300px')
|
|
690
|
+
if (w < h && h > 300) query(this).css('height', '300px')
|
|
691
|
+
})
|
|
692
|
+
.on('error', function (event) {
|
|
693
|
+
this.style.display = 'none'
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
})
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
edata.finish()
|
|
700
|
+
})
|
|
701
|
+
.on('mouseenter.w2item', (event) => {
|
|
702
|
+
let target = query(event.target).closest('.li-item')
|
|
703
|
+
if (query(target).hasClass('li-search')) return
|
|
704
|
+
let item = this.selected[query(event.target).attr('index')]
|
|
705
|
+
// trigger event
|
|
706
|
+
let edata = this.trigger('mouseEnter', { target: this.el, originalEvent: event, item })
|
|
707
|
+
if (edata.isCancelled === true) return
|
|
708
|
+
// event after
|
|
709
|
+
edata.finish()
|
|
710
|
+
})
|
|
711
|
+
.on('mouseleave.w2item', (event) => {
|
|
712
|
+
let target = query(event.target).closest('.li-item')
|
|
713
|
+
if (query(target).hasClass('li-search')) return
|
|
714
|
+
let item = this.selected[query(event.target).attr('index')]
|
|
715
|
+
// trigger event
|
|
716
|
+
let edata = this.trigger('mouseLeave', { target: this.el, originalEvent: event, item })
|
|
717
|
+
if (edata.isCancelled === true) return
|
|
718
|
+
// event after
|
|
719
|
+
edata.finish()
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
// update size for enum, hide for file
|
|
723
|
+
if (this.type === 'enum') {
|
|
724
|
+
let search = this.helpers.multi.find('input')
|
|
725
|
+
search.css({ width: '15px' })
|
|
726
|
+
} else {
|
|
727
|
+
this.helpers.multi.find('.li-search').hide()
|
|
728
|
+
}
|
|
729
|
+
this.resize()
|
|
730
|
+
}
|
|
731
|
+
return Date.now() - time
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// resizing width of list, enum, file controls
|
|
735
|
+
resize() {
|
|
736
|
+
let width = this.el.clientWidth
|
|
737
|
+
// let height = this.el.clientHeight
|
|
738
|
+
// if (this.tmp.current_width == width && height > 0) return
|
|
739
|
+
let styles = getComputedStyle(this.el)
|
|
740
|
+
|
|
741
|
+
let focus = this.helpers.search
|
|
742
|
+
let multi = this.helpers.multi
|
|
743
|
+
let suffix = this.helpers.suffix
|
|
744
|
+
let prefix = this.helpers.prefix
|
|
745
|
+
|
|
746
|
+
// resize helpers
|
|
747
|
+
if (focus) {
|
|
748
|
+
query(focus).css('width', width)
|
|
749
|
+
}
|
|
750
|
+
if (multi) {
|
|
751
|
+
query(multi).css('width', width - parseInt(styles['margin-left'], 10) - parseInt(styles['margin-right'], 10))
|
|
752
|
+
}
|
|
753
|
+
if (suffix) {
|
|
754
|
+
this.addSuffix()
|
|
755
|
+
}
|
|
756
|
+
if (prefix) {
|
|
757
|
+
this.addPrefix()
|
|
758
|
+
}
|
|
759
|
+
// enum or file
|
|
760
|
+
let div = this.helpers.multi
|
|
761
|
+
if (['enum', 'file'].includes(this.type) && div) {
|
|
762
|
+
// adjust height
|
|
763
|
+
query(this.el).css('height', '')
|
|
764
|
+
let cntHeight = query(div).find(':scope div.w2ui-multi-items').get(0).clientHeight + 5
|
|
765
|
+
if (cntHeight < 20) cntHeight = 20
|
|
766
|
+
// max height
|
|
767
|
+
if (cntHeight > this.tmp['max-height']) {
|
|
768
|
+
cntHeight = this.tmp['max-height']
|
|
769
|
+
}
|
|
770
|
+
// min height
|
|
771
|
+
if (cntHeight < this.tmp['min-height']) {
|
|
772
|
+
cntHeight = this.tmp['min-height']
|
|
773
|
+
}
|
|
774
|
+
let inpHeight = w2utils.getSize(this.el, 'height') - 2
|
|
775
|
+
if (inpHeight > cntHeight) cntHeight = inpHeight
|
|
776
|
+
query(div).css({
|
|
777
|
+
'height': cntHeight + 'px',
|
|
778
|
+
overflow: (cntHeight == this.tmp['max-height'] ? 'auto' : 'hidden')
|
|
779
|
+
})
|
|
780
|
+
query(div).css('height', cntHeight + 'px')
|
|
781
|
+
query(this.el).css({ 'height': cntHeight + 'px' })
|
|
782
|
+
}
|
|
783
|
+
// remember width
|
|
784
|
+
this.tmp.current_width = width
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
reset() {
|
|
788
|
+
// restore paddings
|
|
789
|
+
if (this.tmp != null) {
|
|
790
|
+
query(this.el).css('height', '')
|
|
791
|
+
Array('padding-left', 'padding-right', 'background-color', 'border-color').forEach(prop => {
|
|
792
|
+
if (this.tmp && this.tmp['old-'+ prop] != null) {
|
|
793
|
+
query(this.el).css(prop, this.tmp['old-' + prop])
|
|
794
|
+
delete this.tmp['old-' + prop]
|
|
795
|
+
}
|
|
796
|
+
})
|
|
797
|
+
// remove resize watcher
|
|
798
|
+
clearInterval(this.tmp.sizeTimer)
|
|
799
|
+
}
|
|
800
|
+
// remove events and (data)
|
|
801
|
+
query(this.el)
|
|
802
|
+
.val(this.clean(query(this.el).val()))
|
|
803
|
+
.removeClass('w2field w2ui-input')
|
|
804
|
+
.removeData('selected selectedIndex')
|
|
805
|
+
.off('.w2field') // remove only events added by w2field
|
|
806
|
+
// remove helpers
|
|
807
|
+
Object.keys(this.helpers).forEach(key => {
|
|
808
|
+
query(this.helpers[key]).remove()
|
|
809
|
+
})
|
|
810
|
+
this.helpers = {}
|
|
811
|
+
delete this.el._w2field
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
clean(val) {
|
|
815
|
+
// issue #499
|
|
816
|
+
if (typeof val === 'number'){
|
|
817
|
+
return val
|
|
818
|
+
}
|
|
819
|
+
let options = this.options
|
|
820
|
+
val = String(val).trim()
|
|
821
|
+
// clean
|
|
822
|
+
if (['int', 'float', 'money', 'currency', 'percent'].includes(this.type)) {
|
|
823
|
+
if (typeof val === 'string') {
|
|
824
|
+
if (options.autoFormat) {
|
|
825
|
+
if (['money', 'currency'].includes(this.type)) {
|
|
826
|
+
val = String(val).replace(options.moneyRE, '')
|
|
827
|
+
}
|
|
828
|
+
if (this.type === 'percent') {
|
|
829
|
+
val = String(val).replace(options.percentRE, '')
|
|
830
|
+
}
|
|
831
|
+
if (['int', 'float'].includes(this.type)) {
|
|
832
|
+
val = String(val).replace(options.numberRE, '')
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// escape group symbol for regex as it could be a ., which is wild card in regex
|
|
836
|
+
let esc_gsroupSymbol = options.groupSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
837
|
+
let esc_decimalSymbol = options.decimalSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
838
|
+
val = val.replace(/\s+/g, '')
|
|
839
|
+
.replace(new RegExp(esc_gsroupSymbol, 'g'), '')
|
|
840
|
+
.replace(new RegExp(esc_decimalSymbol, 'g'), '.')
|
|
841
|
+
}
|
|
842
|
+
if (val !== '' && w2utils.isFloat(val)) val = Number(val); else val = ''
|
|
843
|
+
}
|
|
844
|
+
return val
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
format(val) {
|
|
848
|
+
let options = this.options
|
|
849
|
+
// auto format numbers or money
|
|
850
|
+
if (options.autoFormat && val !== '') {
|
|
851
|
+
switch (this.type) {
|
|
852
|
+
case 'money':
|
|
853
|
+
case 'currency':
|
|
854
|
+
val = w2utils.formatNumber(val, options.currency.precision, true)
|
|
855
|
+
if (val !== '') val = options.currency.prefix + val + options.currency.suffix
|
|
856
|
+
break
|
|
857
|
+
case 'percent':
|
|
858
|
+
val = w2utils.formatNumber(val, options.precision, true)
|
|
859
|
+
if (val !== '') val += '%'
|
|
860
|
+
break
|
|
861
|
+
case 'float':
|
|
862
|
+
val = w2utils.formatNumber(val, options.precision, true)
|
|
863
|
+
break
|
|
864
|
+
case 'int':
|
|
865
|
+
val = w2utils.formatNumber(val, 0, true)
|
|
866
|
+
break
|
|
867
|
+
}
|
|
868
|
+
// if default group symbol does not match - replase it
|
|
869
|
+
let group = parseInt(1000).toLocaleString(w2utils.settings.locale, { useGrouping: true }).slice(1, 2)
|
|
870
|
+
if (group !== this.options.groupSymbol) {
|
|
871
|
+
val = val.replaceAll(group, this.options.groupSymbol)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return val
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
change(event) {
|
|
878
|
+
// numeric
|
|
879
|
+
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(this.type) !== -1) {
|
|
880
|
+
// check max/min
|
|
881
|
+
let val = query(this.el).val()
|
|
882
|
+
let new_val = this.format(this.clean(query(this.el).val()))
|
|
883
|
+
// if was modified
|
|
884
|
+
if (val !== '' && val != new_val) {
|
|
885
|
+
query(this.el).val(new_val)
|
|
886
|
+
// cancel event
|
|
887
|
+
event.stopPropagation()
|
|
888
|
+
event.preventDefault()
|
|
889
|
+
return false
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
// color
|
|
893
|
+
if (this.type === 'color') {
|
|
894
|
+
let color = query(this.el).val()
|
|
895
|
+
if (color.substr(0, 3).toLowerCase() !== 'rgb') {
|
|
896
|
+
color = '#' + color
|
|
897
|
+
let len = query(this.el).val().length
|
|
898
|
+
if (len !== 8 && len !== 6 && len !== 3) color = ''
|
|
899
|
+
}
|
|
900
|
+
let next = query(this.el).get(0).nextElementSibling
|
|
901
|
+
query(next).find('div').css('background-color', color)
|
|
902
|
+
if (query(this.el).hasClass('has-focus')) {
|
|
903
|
+
this.updateOverlay()
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
// list, enum
|
|
907
|
+
if (['list', 'enum', 'file'].indexOf(this.type) !== -1) {
|
|
908
|
+
this.refresh()
|
|
909
|
+
}
|
|
910
|
+
// date, time
|
|
911
|
+
if (['date', 'time', 'datetime'].indexOf(this.type) !== -1) {
|
|
912
|
+
// convert linux timestamps
|
|
913
|
+
let tmp = parseInt(this.el.value)
|
|
914
|
+
if (w2utils.isInt(this.el.value) && tmp > 3000) {
|
|
915
|
+
if (this.type === 'time') tmp = w2utils.formatTime(new Date(tmp), this.options.format)
|
|
916
|
+
if (this.type === 'date') tmp = w2utils.formatDate(new Date(tmp), this.options.format)
|
|
917
|
+
if (this.type === 'datetime') tmp = w2utils.formatDateTime(new Date(tmp), this.options.format)
|
|
918
|
+
query(this.el).val(tmp).trigger('input').trigger('change')
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
click(event) {
|
|
924
|
+
// lists
|
|
925
|
+
if (['list', 'combo', 'enum'].includes(this.type)) {
|
|
926
|
+
if (!query(this.el).hasClass('has-focus')) {
|
|
927
|
+
this.focus(event)
|
|
928
|
+
}
|
|
929
|
+
if (this.type == 'list' || this.type == 'combo') {
|
|
930
|
+
// if overlay is already open (and not just opened on focus event) then hide it
|
|
931
|
+
if (!this.tmp.openedOnFocus) {
|
|
932
|
+
let name = this.el.id + '_menu'
|
|
933
|
+
let overlay = w2menu.get(name)
|
|
934
|
+
if (overlay?.displayed) {
|
|
935
|
+
w2menu.hide(name)
|
|
936
|
+
} else {
|
|
937
|
+
this.updateOverlay()
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
delete this.tmp.openedOnFocus
|
|
941
|
+
if (this.type == 'list') {
|
|
942
|
+
// since list has separate search input, in order to keep the overlay open, need to stop
|
|
943
|
+
event.stopPropagation()
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (this.type == 'enum') {
|
|
947
|
+
this.updateOverlay()
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
// other fields with drops
|
|
951
|
+
if (['date', 'time', 'datetime', 'color'].includes(this.type)) {
|
|
952
|
+
this.updateOverlay()
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
focus(event) {
|
|
957
|
+
if (this.type == 'list' && document.activeElement == this.el) {
|
|
958
|
+
this.helpers.search_focus.focus()
|
|
959
|
+
// update overlay if needed
|
|
960
|
+
if (event.showMenu !== false && (this.options.openOnFocus !== false && query(this.el).hasClass('has-focus'))
|
|
961
|
+
&& !this.tmp.overlay?.overlay?.displayed) {
|
|
962
|
+
setTimeout(() => {
|
|
963
|
+
this.tmp.openedOnFocus = true
|
|
964
|
+
this.updateOverlay()
|
|
965
|
+
}, 0) // execute at the end of event loop
|
|
966
|
+
}
|
|
967
|
+
return
|
|
968
|
+
}
|
|
969
|
+
// color, date, time
|
|
970
|
+
if (['color', 'date', 'time', 'datetime'].indexOf(this.type) !== -1) {
|
|
971
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
972
|
+
this.updateOverlay()
|
|
973
|
+
}
|
|
974
|
+
// menu
|
|
975
|
+
if (['list', 'combo', 'enum'].indexOf(this.type) !== -1) {
|
|
976
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) {
|
|
977
|
+
// still add focus
|
|
978
|
+
query(this.el).addClass('has-focus')
|
|
979
|
+
return
|
|
980
|
+
}
|
|
981
|
+
// regenerate items
|
|
982
|
+
if (typeof this.options._items_fun == 'function') {
|
|
983
|
+
this.options.items = w2utils.normMenu.call(this, this.options._items_fun, this.options)
|
|
984
|
+
}
|
|
985
|
+
if (this.helpers.search) {
|
|
986
|
+
let search = this.helpers.search_focus
|
|
987
|
+
search.value = ''
|
|
988
|
+
search.select()
|
|
989
|
+
}
|
|
990
|
+
if (this.type == 'enum') {
|
|
991
|
+
// file control in particular need to receive focus after file select
|
|
992
|
+
let search = query(this.el.previousElementSibling).find('.li-search input').get(0)
|
|
993
|
+
if (document.activeElement !== search) {
|
|
994
|
+
search.focus()
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
this.resize()
|
|
998
|
+
// update overlay if needed
|
|
999
|
+
if (event.showMenu !== false && (this.options.openOnFocus !== false && query(this.el).hasClass('has-focus'))
|
|
1000
|
+
&& !this.tmp.overlay?.overlay?.displayed) {
|
|
1001
|
+
setTimeout(() => {
|
|
1002
|
+
this.tmp.openedOnFocus = true
|
|
1003
|
+
this.updateOverlay()
|
|
1004
|
+
}, 0) // execute at the end of event loop
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (this.type == 'file') {
|
|
1008
|
+
let prev = query(this.el).get(0).previousElementSibling
|
|
1009
|
+
query(prev).addClass('has-focus')
|
|
1010
|
+
}
|
|
1011
|
+
query(this.el).addClass('has-focus')
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
blur(event) {
|
|
1015
|
+
let val = query(this.el).val().trim()
|
|
1016
|
+
query(this.el).removeClass('has-focus')
|
|
1017
|
+
|
|
1018
|
+
if (['int', 'float', 'money', 'currency', 'percent'].includes(this.type)) {
|
|
1019
|
+
if (val !== '') {
|
|
1020
|
+
let newVal = val
|
|
1021
|
+
let error = ''
|
|
1022
|
+
if (!this.isStrValid(val)) { // validity is also checked in blur
|
|
1023
|
+
newVal = ''
|
|
1024
|
+
} else {
|
|
1025
|
+
let rVal = this.clean(val)
|
|
1026
|
+
if (this.options.min != null && rVal < this.options.min) {
|
|
1027
|
+
newVal = this.options.min
|
|
1028
|
+
error = `Should be >= ${this.options.min}`
|
|
1029
|
+
}
|
|
1030
|
+
if (this.options.max != null && rVal > this.options.max) {
|
|
1031
|
+
newVal = this.options.max
|
|
1032
|
+
error = `Should be <= ${this.options.max}`
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (this.options.autoCorrect) {
|
|
1036
|
+
query(this.el).val(newVal).trigger('input').trigger('change')
|
|
1037
|
+
if (error) {
|
|
1038
|
+
w2tooltip.show({
|
|
1039
|
+
name: this.el.id + '_error',
|
|
1040
|
+
anchor: this.el,
|
|
1041
|
+
html: error
|
|
1042
|
+
})
|
|
1043
|
+
setTimeout(() => { w2tooltip.hide(this.el.id + '_error') }, 3000)
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
// date or time
|
|
1049
|
+
if (['date', 'time', 'datetime'].includes(this.type) && this.options.autoCorrect) {
|
|
1050
|
+
if (val !== '') {
|
|
1051
|
+
let check = this.type == 'date' ? w2utils.isDate :
|
|
1052
|
+
(this.type == 'time' ? w2utils.isTime : w2utils.isDateTime)
|
|
1053
|
+
if (!w2date.inRange(this.el.value, this.options)
|
|
1054
|
+
|| !check.bind(w2utils)(this.el.value, this.options.format)) {
|
|
1055
|
+
// if not in range or wrong value - clear it
|
|
1056
|
+
query(this.el).val('').trigger('input').trigger('change')
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
// clear search input
|
|
1061
|
+
if (this.type === 'enum') {
|
|
1062
|
+
query(this.helpers.multi).find('input').val('').css('width', '15px')
|
|
1063
|
+
// don't hide menu on blur, it should be hidden on tab key up instead, or it will not alow select with click
|
|
1064
|
+
// w2menu.hide(this.el.id + '_menu')
|
|
1065
|
+
}
|
|
1066
|
+
if (this.type == 'file') {
|
|
1067
|
+
let prev = this.el.previousElementSibling
|
|
1068
|
+
query(prev).removeClass('has-focus')
|
|
1069
|
+
}
|
|
1070
|
+
if (this.type === 'list') {
|
|
1071
|
+
this.el.value = this.selected?.text ?? ''
|
|
1072
|
+
// don't hide menu on blur, it should be hidden on tab key up instead, or it will not alow select with click
|
|
1073
|
+
// w2menu.hide(this.el.id + '_menu')
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
keyDown(event, extra) {
|
|
1078
|
+
let options = this.options
|
|
1079
|
+
let key = event.keyCode || (extra && extra.keyCode)
|
|
1080
|
+
let cancel = false
|
|
1081
|
+
let val, inc, daymil, dt, newValue, newDT
|
|
1082
|
+
// ignore wrong pressed key
|
|
1083
|
+
if (['int', 'float', 'money', 'currency', 'percent', 'hex', 'bin', 'color', 'alphanumeric'].includes(this.type)) {
|
|
1084
|
+
if (!event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
1085
|
+
if (!this.isStrValid(event.key ?? '1', true) && // valid & is not arrows, dot, comma, etc keys
|
|
1086
|
+
![9, 8, 13, 27, 37, 38, 39, 40, 46].includes(event.keyCode)) {
|
|
1087
|
+
event.preventDefault()
|
|
1088
|
+
if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true
|
|
1089
|
+
return false
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
// numeric
|
|
1094
|
+
if (['int', 'float', 'money', 'currency', 'percent'].includes(this.type)) {
|
|
1095
|
+
if (!options.keyboard || query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1096
|
+
val = parseFloat(query(this.el).val().replace(options.moneyRE, '')) || 0
|
|
1097
|
+
inc = options.step
|
|
1098
|
+
if (event.ctrlKey || event.metaKey) inc = options.step * 10
|
|
1099
|
+
switch (key) {
|
|
1100
|
+
case 38: // up
|
|
1101
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1102
|
+
newValue = (val + inc <= options.max || options.max == null ? Number((val + inc).toFixed(12)) : options.max)
|
|
1103
|
+
query(this.el).val(newValue).trigger('input').trigger('change')
|
|
1104
|
+
cancel = true
|
|
1105
|
+
break
|
|
1106
|
+
case 40: // down
|
|
1107
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1108
|
+
newValue = (val - inc >= options.min || options.min == null ? Number((val - inc).toFixed(12)) : options.min)
|
|
1109
|
+
query(this.el).val(newValue).trigger('input').trigger('change')
|
|
1110
|
+
cancel = true
|
|
1111
|
+
break
|
|
1112
|
+
}
|
|
1113
|
+
if (cancel) {
|
|
1114
|
+
event.preventDefault()
|
|
1115
|
+
this.moveCaret2end()
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
// date/datetime
|
|
1119
|
+
if (['date', 'datetime'].includes(this.type)) {
|
|
1120
|
+
if (!options.keyboard || query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1121
|
+
let is = (this.type == 'date' ? w2utils.isDate : w2utils.isDateTime).bind(w2utils)
|
|
1122
|
+
let format = (this.type == 'date' ? w2utils.formatDate : w2utils.formatDateTime).bind(w2utils)
|
|
1123
|
+
|
|
1124
|
+
daymil = 24*60*60*1000
|
|
1125
|
+
inc = 1
|
|
1126
|
+
if (event.ctrlKey || event.metaKey) inc = 10 // by month
|
|
1127
|
+
dt = is(query(this.el).val(), options.format, true)
|
|
1128
|
+
if (!dt) { dt = new Date(); daymil = 0 }
|
|
1129
|
+
switch (key) {
|
|
1130
|
+
case 38: // up
|
|
1131
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1132
|
+
if (inc == 10) {
|
|
1133
|
+
dt.setMonth(dt.getMonth() + 1)
|
|
1134
|
+
} else {
|
|
1135
|
+
dt.setTime(dt.getTime() + daymil)
|
|
1136
|
+
}
|
|
1137
|
+
newDT = format(dt.getTime(), options.format)
|
|
1138
|
+
query(this.el).val(newDT).trigger('input').trigger('change')
|
|
1139
|
+
cancel = true
|
|
1140
|
+
break
|
|
1141
|
+
case 40: // down
|
|
1142
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1143
|
+
if (inc == 10) {
|
|
1144
|
+
dt.setMonth(dt.getMonth() - 1)
|
|
1145
|
+
} else {
|
|
1146
|
+
dt.setTime(dt.getTime() - daymil)
|
|
1147
|
+
}
|
|
1148
|
+
newDT = format(dt.getTime(), options.format)
|
|
1149
|
+
query(this.el).val(newDT).trigger('input').trigger('change')
|
|
1150
|
+
cancel = true
|
|
1151
|
+
break
|
|
1152
|
+
}
|
|
1153
|
+
if (cancel) {
|
|
1154
|
+
event.preventDefault()
|
|
1155
|
+
this.moveCaret2end()
|
|
1156
|
+
this.updateOverlay()
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
// time
|
|
1160
|
+
if (this.type === 'time') {
|
|
1161
|
+
if (!options.keyboard || query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1162
|
+
inc = (event.ctrlKey || event.metaKey ? 60 : 1)
|
|
1163
|
+
val = query(this.el).val()
|
|
1164
|
+
let time = w2date.str2min(val) || w2date.str2min((new Date()).getHours() + ':' + ((new Date()).getMinutes() - 1))
|
|
1165
|
+
switch (key) {
|
|
1166
|
+
case 38: // up
|
|
1167
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1168
|
+
time += inc
|
|
1169
|
+
cancel = true
|
|
1170
|
+
break
|
|
1171
|
+
case 40: // down
|
|
1172
|
+
if (event.shiftKey) break // no action if shift key is pressed
|
|
1173
|
+
time -= inc
|
|
1174
|
+
cancel = true
|
|
1175
|
+
break
|
|
1176
|
+
}
|
|
1177
|
+
if (cancel) {
|
|
1178
|
+
event.preventDefault()
|
|
1179
|
+
query(this.el).val(w2date.min2str(time)).trigger('input').trigger('change')
|
|
1180
|
+
this.moveCaret2end()
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
// list/enum
|
|
1184
|
+
if (['list', 'enum'].includes(this.type)) {
|
|
1185
|
+
switch (key) {
|
|
1186
|
+
case 8: // delete
|
|
1187
|
+
case 46: // backspace
|
|
1188
|
+
if (this.type == 'list') {
|
|
1189
|
+
let search = query(this.helpers.search_focus)
|
|
1190
|
+
if (search.val() == '') {
|
|
1191
|
+
let edata = this.trigger('remove', { target: this.el, originalEvent: event, item: this.selected })
|
|
1192
|
+
if (edata.isCancelled === true) return
|
|
1193
|
+
this.selected = null
|
|
1194
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1195
|
+
query(this.el).val('').trigger('input').trigger('change')
|
|
1196
|
+
edata.finish()
|
|
1197
|
+
}
|
|
1198
|
+
} else {
|
|
1199
|
+
let search = query(this.helpers.multi).find('input')
|
|
1200
|
+
if (search.val() == '') {
|
|
1201
|
+
let edata = this.trigger('remove', { target: this.el, originalEvent: event, item: this.selected[this.selected.length - 1] })
|
|
1202
|
+
if (edata.isCancelled === true) return
|
|
1203
|
+
|
|
1204
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1205
|
+
this.selected.pop()
|
|
1206
|
+
// update selected array in overlay
|
|
1207
|
+
let overlay = w2menu.get(this.el.id + '_menu')
|
|
1208
|
+
if (overlay) overlay.options.selected = this.selected
|
|
1209
|
+
this.refresh()
|
|
1210
|
+
edata.finish()
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
break
|
|
1214
|
+
case 9: // tab key
|
|
1215
|
+
case 16: // shift key (when shift+tab)
|
|
1216
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1217
|
+
break
|
|
1218
|
+
case 27: // escape
|
|
1219
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1220
|
+
this.refresh()
|
|
1221
|
+
break
|
|
1222
|
+
default: {
|
|
1223
|
+
// intentionally blank
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
keyUp(event) {
|
|
1230
|
+
if (this.type == 'list') {
|
|
1231
|
+
let search = query(this.helpers.search_focus)
|
|
1232
|
+
if (search.val() !== '') {
|
|
1233
|
+
query(this.el).attr('placeholder', '')
|
|
1234
|
+
} else {
|
|
1235
|
+
query(this.el).attr('placeholder', this.tmp.pholder)
|
|
1236
|
+
}
|
|
1237
|
+
if (event.keyCode == 13) {
|
|
1238
|
+
setTimeout(() => {
|
|
1239
|
+
search.val('')
|
|
1240
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1241
|
+
this.refresh()
|
|
1242
|
+
}, 1)
|
|
1243
|
+
}
|
|
1244
|
+
// if arrows are clicked, it will show overlay
|
|
1245
|
+
if ([38, 40].includes(event.keyCode) && !this.tmp.overlay?.overlay?.displayed) {
|
|
1246
|
+
this.updateOverlay()
|
|
1247
|
+
}
|
|
1248
|
+
this.refresh()
|
|
1249
|
+
}
|
|
1250
|
+
if (this.type == 'combo') {
|
|
1251
|
+
if (![9, 16, 27].includes(event.keyCode) && this.options.openOnFocus !== true) {
|
|
1252
|
+
// do not show when receives focus on tab or shift + tab or on esc
|
|
1253
|
+
this.updateOverlay()
|
|
1254
|
+
}
|
|
1255
|
+
// if arrows are clicked, it will show overlay
|
|
1256
|
+
if ([38, 40].includes(event.keyCode) && !this.tmp.overlay?.overlay?.displayed) {
|
|
1257
|
+
this.updateOverlay()
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (this.type == 'enum') {
|
|
1261
|
+
let search = this.helpers.multi.find('input')
|
|
1262
|
+
let styles = getComputedStyle(search.get(0))
|
|
1263
|
+
let width = w2utils.getStrWidth(search.val(),
|
|
1264
|
+
`font-family: ${styles['font-family']}; font-size: ${styles['font-size']};`)
|
|
1265
|
+
search.css({ width: (width + 15) + 'px' })
|
|
1266
|
+
this.resize()
|
|
1267
|
+
// if delete, backspace, tab, shift, escape - hide menu
|
|
1268
|
+
if ([8, 46, 9, 16, 27].includes(event.keyCode)) {
|
|
1269
|
+
if (this.tmp.overlay?.overlay?.displayed) {
|
|
1270
|
+
w2menu.hide(this.el.id + '_menu')
|
|
1271
|
+
}
|
|
1272
|
+
} else {
|
|
1273
|
+
this.updateOverlay()
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
findItemIndex(items, id, parents) {
|
|
1279
|
+
let inds = []
|
|
1280
|
+
if (!parents) parents = []
|
|
1281
|
+
if (['list', 'combo', 'enum'].includes(this.type) && this.options.url) {
|
|
1282
|
+
// remove source, so get it from overlay
|
|
1283
|
+
let overlay = w2menu.get(this.el.id + '_menu')
|
|
1284
|
+
if (overlay) {
|
|
1285
|
+
items = overlay.options.items
|
|
1286
|
+
this.options.items = items
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
items.forEach((item, ind) => {
|
|
1290
|
+
if (item.id === id) {
|
|
1291
|
+
inds = parents.concat([ind])
|
|
1292
|
+
this.options.index = [ind]
|
|
1293
|
+
}
|
|
1294
|
+
if (inds.length == 0 && item.items && item.items.length > 0) {
|
|
1295
|
+
parents.push(ind)
|
|
1296
|
+
inds = this.findItemIndex(item.items, id, parents)
|
|
1297
|
+
parents.pop()
|
|
1298
|
+
}
|
|
1299
|
+
})
|
|
1300
|
+
return inds
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
updateOverlay(indexOnly) {
|
|
1304
|
+
let options = this.options
|
|
1305
|
+
let params
|
|
1306
|
+
// color
|
|
1307
|
+
if (this.type === 'color') {
|
|
1308
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1309
|
+
w2color.show(w2utils.extend({
|
|
1310
|
+
name: this.el.id + '_color',
|
|
1311
|
+
anchor: this.el,
|
|
1312
|
+
transparent: options.transparent,
|
|
1313
|
+
advanced: options.advanced,
|
|
1314
|
+
color: this.el.value,
|
|
1315
|
+
liveUpdate: true
|
|
1316
|
+
}, this.options))
|
|
1317
|
+
.select(event => {
|
|
1318
|
+
let color = event.detail.color
|
|
1319
|
+
query(this.el).val(color).trigger('input').trigger('change')
|
|
1320
|
+
})
|
|
1321
|
+
.liveUpdate(event => {
|
|
1322
|
+
let color = event.detail.color
|
|
1323
|
+
query(this.helpers.suffix).find(':scope > div').css('background-color', '#' + color)
|
|
1324
|
+
})
|
|
1325
|
+
}
|
|
1326
|
+
// list
|
|
1327
|
+
if (['list', 'combo', 'enum'].includes(this.type)) {
|
|
1328
|
+
let el = this.el
|
|
1329
|
+
let input = this.el
|
|
1330
|
+
if (this.type === 'enum') {
|
|
1331
|
+
el = this.helpers.multi.get(0)
|
|
1332
|
+
input = query(el).find('input').get(0)
|
|
1333
|
+
}
|
|
1334
|
+
if (this.type === 'list') {
|
|
1335
|
+
let sel = this.selected
|
|
1336
|
+
if (w2utils.isPlainObject(sel) && Object.keys(sel).length > 0) {
|
|
1337
|
+
let ind = this.findItemIndex(options.items, sel.id)
|
|
1338
|
+
if (ind.length > 0) {
|
|
1339
|
+
options.index = ind
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
input = this.helpers.search_focus
|
|
1343
|
+
}
|
|
1344
|
+
if (query(this.el).hasClass('has-focus') && !this.el.readOnly && !this.el.disabled) {
|
|
1345
|
+
params = w2utils.extend({}, options, {
|
|
1346
|
+
name: this.el.id + '_menu',
|
|
1347
|
+
anchor: input,
|
|
1348
|
+
selected: this.selected,
|
|
1349
|
+
search: false,
|
|
1350
|
+
render: options.renderDrop,
|
|
1351
|
+
anchorClass: '',
|
|
1352
|
+
offsetY: 5,
|
|
1353
|
+
maxHeight: options.maxDropHeight, // TODO: check
|
|
1354
|
+
maxWidth: options.maxDropWidth, // TODO: check
|
|
1355
|
+
minWidth: options.minDropWidth // TODO: check
|
|
1356
|
+
})
|
|
1357
|
+
this.tmp.overlay = w2menu.show(params)
|
|
1358
|
+
.select(event => {
|
|
1359
|
+
if (['list', 'combo'].includes(this.type)) {
|
|
1360
|
+
this.selected = event.detail.item
|
|
1361
|
+
query(input).val('')
|
|
1362
|
+
query(this.el).val(this.selected.text).trigger('input').trigger('change')
|
|
1363
|
+
this.focus({ showMenu: false })
|
|
1364
|
+
} else {
|
|
1365
|
+
let selected = this.selected
|
|
1366
|
+
let newItem = event.detail?.item
|
|
1367
|
+
if (newItem) {
|
|
1368
|
+
// trigger event
|
|
1369
|
+
let edata = this.trigger('add', { target: this.el, item: newItem, originalEvent: event })
|
|
1370
|
+
if (edata.isCancelled === true) return
|
|
1371
|
+
// default behavior
|
|
1372
|
+
if (selected.length >= options.max && options.max > 0) selected.pop()
|
|
1373
|
+
delete newItem.hidden
|
|
1374
|
+
selected.push(newItem)
|
|
1375
|
+
query(this.el).trigger('input').trigger('change')
|
|
1376
|
+
query(this.helpers.multi).find('input').val('')
|
|
1377
|
+
// updaet selected array in overlays
|
|
1378
|
+
let overlay = w2menu.get(this.el.id + '_menu')
|
|
1379
|
+
if (overlay) overlay.options.selected = this.selected
|
|
1380
|
+
// event after
|
|
1381
|
+
edata.finish()
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
})
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
// date
|
|
1388
|
+
if (['date', 'time', 'datetime'].includes(this.type)) {
|
|
1389
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1390
|
+
w2date.show(w2utils.extend({
|
|
1391
|
+
name: this.el.id + '_date',
|
|
1392
|
+
anchor: this.el,
|
|
1393
|
+
value: this.el.value,
|
|
1394
|
+
}, this.options))
|
|
1395
|
+
.select(event => {
|
|
1396
|
+
let date = event.detail.date
|
|
1397
|
+
if (date != null) {
|
|
1398
|
+
query(this.el).val(date).trigger('input').trigger('change')
|
|
1399
|
+
}
|
|
1400
|
+
})
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/*
|
|
1405
|
+
* INTERNAL FUNCTIONS
|
|
1406
|
+
*/
|
|
1407
|
+
|
|
1408
|
+
isStrValid(ch, loose) {
|
|
1409
|
+
let isValid = true
|
|
1410
|
+
switch (this.type) {
|
|
1411
|
+
case 'int':
|
|
1412
|
+
if (loose && ['-', this.options.groupSymbol].includes(ch)) {
|
|
1413
|
+
isValid = true
|
|
1414
|
+
} else {
|
|
1415
|
+
isValid = w2utils.isInt(ch.replace(this.options.numberRE, ''))
|
|
1416
|
+
}
|
|
1417
|
+
break
|
|
1418
|
+
case 'percent':
|
|
1419
|
+
ch = ch.replace(/%/g, '')
|
|
1420
|
+
case 'float':
|
|
1421
|
+
if (loose && ['-', '', this.options.decimalSymbol, this.options.groupSymbol].includes(ch)) {
|
|
1422
|
+
isValid = true
|
|
1423
|
+
} else {
|
|
1424
|
+
isValid = w2utils.isFloat(ch.replace(this.options.numberRE, ''))
|
|
1425
|
+
}
|
|
1426
|
+
break
|
|
1427
|
+
case 'money':
|
|
1428
|
+
case 'currency':
|
|
1429
|
+
if (loose && ['-', this.options.decimalSymbol, this.options.groupSymbol, this.options.currency.prefix,
|
|
1430
|
+
this.options.currency.suffix].includes(ch)) {
|
|
1431
|
+
isValid = true
|
|
1432
|
+
} else {
|
|
1433
|
+
isValid = w2utils.isFloat(ch.replace(this.options.moneyRE, ''))
|
|
1434
|
+
}
|
|
1435
|
+
break
|
|
1436
|
+
case 'bin':
|
|
1437
|
+
isValid = w2utils.isBin(ch)
|
|
1438
|
+
break
|
|
1439
|
+
case 'color':
|
|
1440
|
+
case 'hex':
|
|
1441
|
+
isValid = w2utils.isHex(ch)
|
|
1442
|
+
break
|
|
1443
|
+
case 'alphanumeric':
|
|
1444
|
+
isValid = w2utils.isAlphaNumeric(ch)
|
|
1445
|
+
break
|
|
1446
|
+
}
|
|
1447
|
+
return isValid
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
addPrefix() {
|
|
1451
|
+
if (!this.options.prefix) {
|
|
1452
|
+
return
|
|
1453
|
+
}
|
|
1454
|
+
let helper
|
|
1455
|
+
let styles = getComputedStyle(this.el)
|
|
1456
|
+
if (this.tmp['old-padding-left'] == null) {
|
|
1457
|
+
this.tmp['old-padding-left'] = styles['padding-left']
|
|
1458
|
+
}
|
|
1459
|
+
// remove if already displayed
|
|
1460
|
+
if (this.helpers.prefix) query(this.helpers.prefix).remove()
|
|
1461
|
+
query(this.el).before(`<div class="w2ui-field-helper">${this.options.prefix}</div>`)
|
|
1462
|
+
helper = query(this.el).get(0).previousElementSibling
|
|
1463
|
+
query(helper)
|
|
1464
|
+
.css({
|
|
1465
|
+
'color' : styles.color,
|
|
1466
|
+
'font-family' : styles['font-family'],
|
|
1467
|
+
'font-size' : styles['font-size'],
|
|
1468
|
+
'height' : this.el.clientHeight + 'px',
|
|
1469
|
+
'padding-top' : parseInt(styles['padding-top'], 10) + 1 + 'px',
|
|
1470
|
+
'padding-bottom' : parseInt(styles['padding-bottom'], 10) - 1 + 'px',
|
|
1471
|
+
'padding-left' : this.tmp['old-padding-left'],
|
|
1472
|
+
'padding-right' : 0,
|
|
1473
|
+
'margin-top' : (parseInt(styles['margin-top'], 10)) + 'px',
|
|
1474
|
+
'margin-bottom' : (parseInt(styles['margin-bottom'], 10)) + 'px',
|
|
1475
|
+
'margin-left' : styles['margin-left'],
|
|
1476
|
+
'margin-right' : 0,
|
|
1477
|
+
'z-index' : 1,
|
|
1478
|
+
'display' : 'inline-flex',
|
|
1479
|
+
'align-items' : 'center'
|
|
1480
|
+
})
|
|
1481
|
+
// only if visible
|
|
1482
|
+
query(this.el).css('padding-left', helper.clientWidth + 'px !important')
|
|
1483
|
+
// remember helper
|
|
1484
|
+
this.helpers.prefix = helper
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
addSuffix() {
|
|
1488
|
+
if (!this.options.suffix && !this.options.arrows) {
|
|
1489
|
+
return
|
|
1490
|
+
}
|
|
1491
|
+
let helper
|
|
1492
|
+
let self = this
|
|
1493
|
+
let styles = getComputedStyle(this.el)
|
|
1494
|
+
if (this.tmp['old-padding-right'] == null) {
|
|
1495
|
+
this.tmp['old-padding-right'] = styles['padding-right']
|
|
1496
|
+
}
|
|
1497
|
+
let pr = parseInt(styles['padding-right'] || 0)
|
|
1498
|
+
if (this.options.arrows) {
|
|
1499
|
+
// remove if already displayed
|
|
1500
|
+
if (this.helpers.arrows) query(this.helpers.arrows).remove()
|
|
1501
|
+
// add fresh
|
|
1502
|
+
query(this.el).after(
|
|
1503
|
+
'<div class="w2ui-field-helper" style="border: 1px solid transparent"> '+
|
|
1504
|
+
' <div class="w2ui-field-up" type="up">'+
|
|
1505
|
+
' <div class="arrow-up" type="up"></div>'+
|
|
1506
|
+
' </div>'+
|
|
1507
|
+
' <div class="w2ui-field-down" type="down">'+
|
|
1508
|
+
' <div class="arrow-down" type="down"></div>'+
|
|
1509
|
+
' </div>'+
|
|
1510
|
+
'</div>')
|
|
1511
|
+
helper = query(this.el).get(0).nextElementSibling
|
|
1512
|
+
query(helper).css({
|
|
1513
|
+
'color' : styles.color,
|
|
1514
|
+
'font-family' : styles['font-family'],
|
|
1515
|
+
'font-size' : styles['font-size'],
|
|
1516
|
+
'height' : this.el.clientHeight + 'px',
|
|
1517
|
+
'padding' : 0,
|
|
1518
|
+
'margin-top' : (parseInt(styles['margin-top'], 10) + 1) + 'px',
|
|
1519
|
+
'margin-bottom' : 0,
|
|
1520
|
+
'border-left' : '1px solid silver',
|
|
1521
|
+
'width' : '16px',
|
|
1522
|
+
'transform' : 'translateX(-100%)'
|
|
1523
|
+
})
|
|
1524
|
+
.on('mousedown', function(event) {
|
|
1525
|
+
if (query(event.target).hasClass('arrow-up')) {
|
|
1526
|
+
self.keyDown(event, { keyCode: 38 })
|
|
1527
|
+
}
|
|
1528
|
+
if (query(event.target).hasClass('arrow-down')) {
|
|
1529
|
+
self.keyDown(event, { keyCode: 40 })
|
|
1530
|
+
}
|
|
1531
|
+
})
|
|
1532
|
+
pr += helper.clientWidth // width of the control
|
|
1533
|
+
query(this.el).css('padding-right', pr + 'px !important')
|
|
1534
|
+
this.helpers.arrows = helper
|
|
1535
|
+
}
|
|
1536
|
+
if (this.options.suffix !== '') {
|
|
1537
|
+
// remove if already displayed
|
|
1538
|
+
if (this.helpers.suffix) query(this.helpers.suffix).remove()
|
|
1539
|
+
// add fresh
|
|
1540
|
+
query(this.el).after(`<div class="w2ui-field-helper">${this.options.suffix}</div>`)
|
|
1541
|
+
helper = query(this.el).get(0).nextElementSibling
|
|
1542
|
+
query(helper)
|
|
1543
|
+
.css({
|
|
1544
|
+
'color' : styles.color,
|
|
1545
|
+
'font-family' : styles['font-family'],
|
|
1546
|
+
'font-size' : styles['font-size'],
|
|
1547
|
+
'height' : this.el.clientHeight + 'px',
|
|
1548
|
+
'padding-top' : styles['padding-top'],
|
|
1549
|
+
'padding-bottom' : styles['padding-bottom'],
|
|
1550
|
+
'padding-left' : 0,
|
|
1551
|
+
'padding-right' : styles['padding-right'],
|
|
1552
|
+
'margin-top' : (parseInt(styles['margin-top'], 10) + 2) + 'px',
|
|
1553
|
+
'margin-bottom' : (parseInt(styles['margin-bottom'], 10) + 1) + 'px',
|
|
1554
|
+
'transform' : 'translateX(-100%)'
|
|
1555
|
+
})
|
|
1556
|
+
|
|
1557
|
+
query(this.el).css('padding-right', helper.clientWidth + 'px !important')
|
|
1558
|
+
this.helpers.suffix = helper
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Only used for list
|
|
1563
|
+
addSearch() {
|
|
1564
|
+
if (this.type !== 'list') return
|
|
1565
|
+
// clean up & init
|
|
1566
|
+
if (this.helpers.search) query(this.helpers.search).remove()
|
|
1567
|
+
// remember original tabindex
|
|
1568
|
+
let tabIndex = parseInt(query(this.el).attr('tabIndex'))
|
|
1569
|
+
if (!isNaN(tabIndex) && tabIndex !== -1) this.tmp['old-tabIndex'] = tabIndex
|
|
1570
|
+
if (this.tmp['old-tabIndex']) tabIndex = this.tmp['old-tabIndex']
|
|
1571
|
+
if (tabIndex == null || isNaN(tabIndex)) tabIndex = 0
|
|
1572
|
+
// if there is id, add to search with "_search"
|
|
1573
|
+
let searchId = ''
|
|
1574
|
+
if (query(this.el).attr('id') != null) {
|
|
1575
|
+
searchId = 'id="' + query(this.el).attr('id') + '_search"'
|
|
1576
|
+
}
|
|
1577
|
+
// build helper
|
|
1578
|
+
let html = `
|
|
1579
|
+
<div class="w2ui-field-helper">
|
|
1580
|
+
<span class="w2ui-icon w2ui-icon-search"></span>
|
|
1581
|
+
<input ${searchId} type="text" tabIndex="${tabIndex}" ${query(this.el).prop('readOnly') ? 'readonly' : ''}
|
|
1582
|
+
autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"/>
|
|
1583
|
+
</div>`
|
|
1584
|
+
query(this.el).attr('tabindex', -1).before(html)
|
|
1585
|
+
let helper = query(this.el).get(0).previousElementSibling
|
|
1586
|
+
this.helpers.search = helper
|
|
1587
|
+
this.helpers.search_focus = query(helper).find('input').get(0)
|
|
1588
|
+
let styles = getComputedStyle(this.el)
|
|
1589
|
+
query(helper).css({
|
|
1590
|
+
width : this.el.clientWidth + 'px',
|
|
1591
|
+
'margin-top' : styles['margin-top'],
|
|
1592
|
+
'margin-left' : styles['margin-left'],
|
|
1593
|
+
'margin-bottom' : styles['margin-bottom'],
|
|
1594
|
+
'margin-right' : styles['margin-right']
|
|
1595
|
+
})
|
|
1596
|
+
.find('input')
|
|
1597
|
+
.css({
|
|
1598
|
+
cursor : 'default',
|
|
1599
|
+
width : '100%',
|
|
1600
|
+
opacity : 1,
|
|
1601
|
+
padding : styles.padding,
|
|
1602
|
+
margin : styles.margin,
|
|
1603
|
+
border : '1px solid transparent',
|
|
1604
|
+
'background-color' : 'transparent'
|
|
1605
|
+
})
|
|
1606
|
+
// INPUT events
|
|
1607
|
+
query(helper).find('input')
|
|
1608
|
+
.off('.w2ui-helper')
|
|
1609
|
+
.on('focus.w2ui-helper', event => {
|
|
1610
|
+
query(event.target).val('')
|
|
1611
|
+
this.tmp.pholder = query(this.el).attr('placeholder') ?? ''
|
|
1612
|
+
this.focus(event)
|
|
1613
|
+
event.stopPropagation()
|
|
1614
|
+
})
|
|
1615
|
+
.on('blur.w2ui-helper', event => {
|
|
1616
|
+
query(event.target).val('')
|
|
1617
|
+
if (this.tmp.pholder != null) query(this.el).attr('placeholder', this.tmp.pholder)
|
|
1618
|
+
this.blur(event)
|
|
1619
|
+
event.stopPropagation()
|
|
1620
|
+
})
|
|
1621
|
+
.on('keydown.w2ui-helper', event => { this.keyDown(event) })
|
|
1622
|
+
.on('keyup.w2ui-helper', event => { this.keyUp(event) })
|
|
1623
|
+
// MAIN div
|
|
1624
|
+
query(helper)
|
|
1625
|
+
.off('.w2ui-helper')
|
|
1626
|
+
.on('click.w2ui-helper', event => {
|
|
1627
|
+
query(helper).find('input').get(0).focus()
|
|
1628
|
+
})
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// Used in enum/file
|
|
1632
|
+
addMultiSearch() {
|
|
1633
|
+
if (!['enum', 'file'].includes(this.type)) {
|
|
1634
|
+
return
|
|
1635
|
+
}
|
|
1636
|
+
// clean up & init
|
|
1637
|
+
query(this.helpers.multi).remove()
|
|
1638
|
+
// build helper
|
|
1639
|
+
let html = ''
|
|
1640
|
+
let styles = getComputedStyle(this.el)
|
|
1641
|
+
let margin = w2utils.stripSpaces(`
|
|
1642
|
+
margin-top: 0px;
|
|
1643
|
+
margin-bottom: 0px;
|
|
1644
|
+
margin-left: ${styles['margin-left']};
|
|
1645
|
+
margin-right: ${styles['margin-right']};
|
|
1646
|
+
width: ${(w2utils.getSize(this.el, 'width') - parseInt(styles['margin-left'], 10)
|
|
1647
|
+
- parseInt(styles['margin-right'], 10))}px;
|
|
1648
|
+
`)
|
|
1649
|
+
if (this.tmp['min-height'] == null) {
|
|
1650
|
+
let min = this.tmp['min-height'] = parseInt((styles['min-height'] != 'none' ? styles['min-height'] : 0) || 0)
|
|
1651
|
+
let current = parseInt(styles.height)
|
|
1652
|
+
this.tmp['min-height'] = Math.max(min, current)
|
|
1653
|
+
}
|
|
1654
|
+
if (this.tmp['max-height'] == null && styles['max-height'] != 'none') {
|
|
1655
|
+
this.tmp['max-height'] = parseInt(styles['max-height'])
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// if there is id, add to search with "_search"
|
|
1659
|
+
let searchId = ''
|
|
1660
|
+
if (query(this.el).attr('id') != null) {
|
|
1661
|
+
searchId = `id="${query(this.el).attr('id')}_search"`
|
|
1662
|
+
}
|
|
1663
|
+
// remember original tabindex
|
|
1664
|
+
let tabIndex = parseInt(query(this.el).attr('tabIndex'))
|
|
1665
|
+
if (!isNaN(tabIndex) && tabIndex !== -1) this.tmp['old-tabIndex'] = tabIndex
|
|
1666
|
+
if (this.tmp['old-tabIndex']) tabIndex = this.tmp['old-tabIndex']
|
|
1667
|
+
if (tabIndex == null || isNaN(tabIndex)) tabIndex = 0
|
|
1668
|
+
|
|
1669
|
+
if (this.type === 'enum') {
|
|
1670
|
+
html = `
|
|
1671
|
+
<div class="w2ui-field-helper w2ui-list" style="${margin}">
|
|
1672
|
+
<div class="w2ui-multi-items">
|
|
1673
|
+
<div class="li-search">
|
|
1674
|
+
<input ${searchId} type="text" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
|
|
1675
|
+
tabindex="${tabIndex}"
|
|
1676
|
+
${query(this.el).prop('readOnly') ? 'readonly': '' }
|
|
1677
|
+
${query(this.el).prop('disabled') ? 'disabled': '' }>
|
|
1678
|
+
</div>
|
|
1679
|
+
</div>
|
|
1680
|
+
</div>`
|
|
1681
|
+
}
|
|
1682
|
+
if (this.type === 'file') {
|
|
1683
|
+
html = `
|
|
1684
|
+
<div class="w2ui-field-helper w2ui-list" style="${margin}">
|
|
1685
|
+
<div class="w2ui-multi-file">
|
|
1686
|
+
<input name="attachment" class="file-input" type="file" tabindex="-1"'
|
|
1687
|
+
style="width: 100%; height: 100%; opacity: 0" title=""
|
|
1688
|
+
${this.options.max !== 1 ? 'multiple' : ''}
|
|
1689
|
+
${query(this.el).prop('readOnly') || query(this.el).prop('disabled') ? 'disabled': ''}
|
|
1690
|
+
${query(this.el).attr('accept') ? ' accept="'+ query(this.el).attr('accept') +'"': ''}>
|
|
1691
|
+
</div>
|
|
1692
|
+
<div class="w2ui-multi-items">
|
|
1693
|
+
<div class="li-search" style="display: none">
|
|
1694
|
+
<input ${searchId} type="text" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
|
|
1695
|
+
tabindex="${tabIndex}"
|
|
1696
|
+
${query(this.el).prop('readOnly') ? 'readonly': '' }
|
|
1697
|
+
${query(this.el).prop('disabled') ? 'disabled': '' }>
|
|
1698
|
+
</div>
|
|
1699
|
+
</div>
|
|
1700
|
+
</div>`
|
|
1701
|
+
}
|
|
1702
|
+
// old bg and border
|
|
1703
|
+
this.tmp['old-background-color'] = styles['background-color']
|
|
1704
|
+
this.tmp['old-border-color'] = styles['border-color']
|
|
1705
|
+
|
|
1706
|
+
query(this.el)
|
|
1707
|
+
.before(html)
|
|
1708
|
+
.css({
|
|
1709
|
+
'border-color': 'transparent',
|
|
1710
|
+
'background-color': 'transparent'
|
|
1711
|
+
})
|
|
1712
|
+
|
|
1713
|
+
let div = query(this.el.previousElementSibling)
|
|
1714
|
+
this.helpers.multi = div
|
|
1715
|
+
query(this.el).attr('tabindex', -1)
|
|
1716
|
+
// click anywhere on the field
|
|
1717
|
+
div.on('mousedown', event => { query(event.target).addClass('has-focus') }) // this is needed so that visually focus is there
|
|
1718
|
+
.on('mouseup', event => { query(event.target).removeClass('has-focus') })
|
|
1719
|
+
.on('click', event => { this.focus(event); this.updateOverlay() })
|
|
1720
|
+
|
|
1721
|
+
// search field (small and growing one)
|
|
1722
|
+
div.find('input:not(.file-input)')
|
|
1723
|
+
.on('click', event => { this.click(event) })
|
|
1724
|
+
.on('focus', event => { this.focus(event) })
|
|
1725
|
+
.on('blur', event => { this.blur(event) })
|
|
1726
|
+
.on('keydown', event => { this.keyDown(event) })
|
|
1727
|
+
.on('keyup', event => { this.keyUp(event) })
|
|
1728
|
+
|
|
1729
|
+
// file input
|
|
1730
|
+
if (this.type === 'file') {
|
|
1731
|
+
div.find('input.file-input')
|
|
1732
|
+
.off('.drag')
|
|
1733
|
+
.on('click.drag', (event) => {
|
|
1734
|
+
event.stopPropagation()
|
|
1735
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1736
|
+
this.focus(event)
|
|
1737
|
+
})
|
|
1738
|
+
.on('dragenter.drag', (event) => {
|
|
1739
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1740
|
+
div.addClass('w2ui-file-dragover')
|
|
1741
|
+
})
|
|
1742
|
+
.on('dragleave.drag', (event) => {
|
|
1743
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1744
|
+
div.removeClass('w2ui-file-dragover')
|
|
1745
|
+
})
|
|
1746
|
+
.on('drop.drag', (event) => {
|
|
1747
|
+
if (query(this.el).prop('readOnly') || query(this.el).prop('disabled')) return
|
|
1748
|
+
div.removeClass('w2ui-file-dragover')
|
|
1749
|
+
let files = Array.from(event.dataTransfer.files)
|
|
1750
|
+
files.forEach(file => { this.addFile(file) })
|
|
1751
|
+
this.focus(event)
|
|
1752
|
+
// cancel to stop browser behaviour
|
|
1753
|
+
event.preventDefault()
|
|
1754
|
+
event.stopPropagation()
|
|
1755
|
+
})
|
|
1756
|
+
.on('dragover.drag', (event) => {
|
|
1757
|
+
// cancel to stop browser behaviour
|
|
1758
|
+
event.preventDefault()
|
|
1759
|
+
event.stopPropagation()
|
|
1760
|
+
})
|
|
1761
|
+
.on('change.drag', (event) => {
|
|
1762
|
+
if (typeof event.target.files !== 'undefined') {
|
|
1763
|
+
Array.from(event.target.files).forEach(file => { this.addFile(file) })
|
|
1764
|
+
}
|
|
1765
|
+
this.focus(event)
|
|
1766
|
+
})
|
|
1767
|
+
}
|
|
1768
|
+
this.refresh()
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
addFile(file) {
|
|
1772
|
+
let options = this.options
|
|
1773
|
+
let selected = this.selected
|
|
1774
|
+
let newItem = {
|
|
1775
|
+
name : file.name,
|
|
1776
|
+
type : file.type,
|
|
1777
|
+
modified : file.lastModifiedDate,
|
|
1778
|
+
size : file.size,
|
|
1779
|
+
content : null,
|
|
1780
|
+
file : file
|
|
1781
|
+
}
|
|
1782
|
+
let size = 0
|
|
1783
|
+
let cnt = 0
|
|
1784
|
+
let errors = []
|
|
1785
|
+
if (Array.isArray(selected)) {
|
|
1786
|
+
selected.forEach(item => {
|
|
1787
|
+
if (item.name == file.name && item.size == file.size) {
|
|
1788
|
+
errors.push(w2utils.lang('The file "${name}" (${size}) is already added.', {
|
|
1789
|
+
name: file.name, size: w2utils.formatSize(file.size) }))
|
|
1790
|
+
}
|
|
1791
|
+
size += item.size
|
|
1792
|
+
cnt++
|
|
1793
|
+
})
|
|
1794
|
+
}
|
|
1795
|
+
if (options.maxFileSize !== 0 && newItem.size > options.maxFileSize) {
|
|
1796
|
+
errors.push(w2utils.lang('Maximum file size is ${size}', { size: w2utils.formatSize(options.maxFileSize) }))
|
|
1797
|
+
}
|
|
1798
|
+
if (options.maxSize !== 0 && size + newItem.size > options.maxSize) {
|
|
1799
|
+
errors.push(w2utils.lang('Maximum total size is ${size}', { size: w2utils.formatSize(options.maxSize) }))
|
|
1800
|
+
}
|
|
1801
|
+
if (options.max !== 0 && cnt >= options.max) {
|
|
1802
|
+
errors.push(w2utils.lang('Maximum number of files is ${count}', { count: options.max }))
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// trigger event
|
|
1806
|
+
let edata = this.trigger('add', { target: this.el, file: newItem, total: cnt, totalSize: size, errors })
|
|
1807
|
+
if (edata.isCancelled === true) return
|
|
1808
|
+
// if errors
|
|
1809
|
+
if (errors.length > 0) {
|
|
1810
|
+
if (options.showErrors) {
|
|
1811
|
+
w2tooltip.show({
|
|
1812
|
+
anchor: this.el,
|
|
1813
|
+
html: 'Errors: ' + errors.join('<br>'),
|
|
1814
|
+
hideOn: ['input', 'doc-click']
|
|
1815
|
+
})
|
|
1816
|
+
}
|
|
1817
|
+
console.log('ERRORS (while adding files): ', errors)
|
|
1818
|
+
return
|
|
1819
|
+
}
|
|
1820
|
+
// check params
|
|
1821
|
+
selected.push(newItem)
|
|
1822
|
+
// read file as base64
|
|
1823
|
+
if (typeof FileReader !== 'undefined' && options.readContent === true) {
|
|
1824
|
+
let reader = new FileReader()
|
|
1825
|
+
let self = this
|
|
1826
|
+
// need a closure
|
|
1827
|
+
reader.onload = (function onload() {
|
|
1828
|
+
return function closure(event) {
|
|
1829
|
+
let fl = event.target.result
|
|
1830
|
+
let ind = fl.indexOf(',')
|
|
1831
|
+
newItem.content = fl.substr(ind + 1)
|
|
1832
|
+
self.refresh()
|
|
1833
|
+
query(self.el).trigger('input').trigger('change')
|
|
1834
|
+
// event after
|
|
1835
|
+
edata.finish()
|
|
1836
|
+
}
|
|
1837
|
+
})()
|
|
1838
|
+
reader.readAsDataURL(file)
|
|
1839
|
+
} else {
|
|
1840
|
+
this.refresh()
|
|
1841
|
+
query(this.el).trigger('input').trigger('change')
|
|
1842
|
+
edata.finish()
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// move cursror to end
|
|
1847
|
+
moveCaret2end() {
|
|
1848
|
+
setTimeout(() => {
|
|
1849
|
+
this.el.setSelectionRange(this.el.value.length, this.el.value.length)
|
|
1850
|
+
}, 0)
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
export { w2field }
|