UNPKG

13.8 kBJavaScriptView Raw
1export default {
2 getBootstrapVersion () {
3 let bootstrapVersion = 5
4
5 try {
6 const rawVersion = $.fn.dropdown.Constructor.VERSION
7
8 // Only try to parse VERSION if it is defined.
9 // It is undefined in older versions of Bootstrap (tested with 3.1.1).
10 if (rawVersion !== undefined) {
11 bootstrapVersion = parseInt(rawVersion, 10)
12 }
13 } catch (e) {
14 // ignore
15 }
16
17 try {
18 // eslint-disable-next-line no-undef
19 const rawVersion = bootstrap.Tooltip.VERSION
20
21 if (rawVersion !== undefined) {
22 bootstrapVersion = parseInt(rawVersion, 10)
23 }
24 } catch (e) {
25 // ignore
26 }
27
28 return bootstrapVersion
29 },
30
31 getIconsPrefix (theme) {
32 return {
33 bootstrap3: 'glyphicon',
34 bootstrap4: 'fa',
35 bootstrap5: 'bi',
36 'bootstrap-table': 'icon',
37 bulma: 'fa',
38 foundation: 'fa',
39 materialize: 'material-icons',
40 semantic: 'fa'
41 }[theme] || 'fa'
42 },
43
44 getIcons (prefix) {
45 return {
46 glyphicon: {
47 paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down',
48 paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up',
49 refresh: 'glyphicon-refresh icon-refresh',
50 toggleOff: 'glyphicon-list-alt icon-list-alt',
51 toggleOn: 'glyphicon-list-alt icon-list-alt',
52 columns: 'glyphicon-th icon-th',
53 detailOpen: 'glyphicon-plus icon-plus',
54 detailClose: 'glyphicon-minus icon-minus',
55 fullscreen: 'glyphicon-fullscreen',
56 search: 'glyphicon-search',
57 clearSearch: 'glyphicon-trash'
58 },
59 fa: {
60 paginationSwitchDown: 'fa-caret-square-down',
61 paginationSwitchUp: 'fa-caret-square-up',
62 refresh: 'fa-sync',
63 toggleOff: 'fa-toggle-off',
64 toggleOn: 'fa-toggle-on',
65 columns: 'fa-th-list',
66 detailOpen: 'fa-plus',
67 detailClose: 'fa-minus',
68 fullscreen: 'fa-arrows-alt',
69 search: 'fa-search',
70 clearSearch: 'fa-trash'
71 },
72 bi: {
73 paginationSwitchDown: 'bi-caret-down-square',
74 paginationSwitchUp: 'bi-caret-up-square',
75 refresh: 'bi-arrow-clockwise',
76 toggleOff: 'bi-toggle-off',
77 toggleOn: 'bi-toggle-on',
78 columns: 'bi-list-ul',
79 detailOpen: 'bi-plus',
80 detailClose: 'bi-dash',
81 fullscreen: 'bi-arrows-move',
82 search: 'bi-search',
83 clearSearch: 'bi-trash'
84 },
85 icon: {
86 paginationSwitchDown: 'icon-arrow-up-circle',
87 paginationSwitchUp: 'icon-arrow-down-circle',
88 refresh: 'icon-refresh-cw',
89 toggleOff: 'icon-toggle-right',
90 toggleOn: 'icon-toggle-right',
91 columns: 'icon-list',
92 detailOpen: 'icon-plus',
93 detailClose: 'icon-minus',
94 fullscreen: 'icon-maximize',
95 search: 'icon-search',
96 clearSearch: 'icon-trash-2'
97 },
98 'material-icons': {
99 paginationSwitchDown: 'grid_on',
100 paginationSwitchUp: 'grid_off',
101 refresh: 'refresh',
102 toggleOff: 'tablet',
103 toggleOn: 'tablet_android',
104 columns: 'view_list',
105 detailOpen: 'add',
106 detailClose: 'remove',
107 fullscreen: 'fullscreen',
108 sort: 'sort',
109 search: 'search',
110 clearSearch: 'delete'
111 }
112 }[prefix]
113 },
114
115 getSearchInput (that) {
116 if (typeof that.options.searchSelector === 'string') {
117 return $(that.options.searchSelector)
118 }
119 return that.$toolbar.find('.search input')
120 },
121
122 // it only does '%s', and return '' when arguments are undefined
123 sprintf (_str, ...args) {
124 let flag = true
125 let i = 0
126
127 const str = _str.replace(/%s/g, () => {
128 const arg = args[i++]
129
130 if (typeof arg === 'undefined') {
131 flag = false
132 return ''
133 }
134 return arg
135 })
136
137 return flag ? str : ''
138 },
139
140 isObject (val) {
141 return val instanceof Object && !Array.isArray(val)
142 },
143
144 isEmptyObject (obj = {}) {
145 return Object.entries(obj).length === 0 && obj.constructor === Object
146 },
147
148 isNumeric (n) {
149 return !isNaN(parseFloat(n)) && isFinite(n)
150 },
151
152 getFieldTitle (list, value) {
153 for (const item of list) {
154 if (item.field === value) {
155 return item.title
156 }
157 }
158 return ''
159 },
160
161 setFieldIndex (columns) {
162 let totalCol = 0
163 const flag = []
164
165 for (const column of columns[0]) {
166 totalCol += column.colspan || 1
167 }
168
169 for (let i = 0; i < columns.length; i++) {
170 flag[i] = []
171 for (let j = 0; j < totalCol; j++) {
172 flag[i][j] = false
173 }
174 }
175
176 for (let i = 0; i < columns.length; i++) {
177 for (const r of columns[i]) {
178 const rowspan = r.rowspan || 1
179 const colspan = r.colspan || 1
180 const index = flag[i].indexOf(false)
181
182 r.colspanIndex = index
183
184 if (colspan === 1) {
185 r.fieldIndex = index
186 // when field is undefined, use index instead
187 if (typeof r.field === 'undefined') {
188 r.field = index
189 }
190 } else {
191 r.colspanGroup = r.colspan
192 }
193
194 for (let j = 0; j < rowspan; j++) {
195 for (let k = 0; k < colspan; k++) {
196 flag[i + j][index + k] = true
197 }
198 }
199 }
200 }
201 },
202
203 normalizeAccent (value) {
204 if (typeof value !== 'string') {
205 return value
206 }
207 return value.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
208 },
209
210 updateFieldGroup (columns, fieldColumns) {
211 const allColumns = [].concat(...columns)
212
213 for (const c of columns) {
214 for (const r of c) {
215 if (r.colspanGroup > 1) {
216 let colspan = 0
217
218 for (let i = r.colspanIndex; i < r.colspanIndex + r.colspanGroup; i++) {
219 const column = allColumns.find(col => col.fieldIndex === i)
220
221 if (column.visible) {
222 colspan++
223 }
224 }
225 r.colspan = colspan
226 r.visible = colspan > 0
227 }
228 }
229 }
230
231 if (columns.length < 2) {
232 return
233 }
234
235 for (const column of fieldColumns) {
236 const sameColumns = allColumns.filter(col => col.fieldIndex === column.fieldIndex)
237
238 if (sameColumns.length > 1) {
239 for (const c of sameColumns) {
240 c.visible = column.visible
241 }
242 }
243 }
244 },
245
246 getScrollBarWidth () {
247 if (this.cachedWidth === undefined) {
248 const $inner = $('<div/>').addClass('fixed-table-scroll-inner')
249 const $outer = $('<div/>').addClass('fixed-table-scroll-outer')
250
251 $outer.append($inner)
252 $('body').append($outer)
253
254 const w1 = $inner[0].offsetWidth
255
256 $outer.css('overflow', 'scroll')
257 let w2 = $inner[0].offsetWidth
258
259 if (w1 === w2) {
260 w2 = $outer[0].clientWidth
261 }
262
263 $outer.remove()
264 this.cachedWidth = w1 - w2
265 }
266 return this.cachedWidth
267 },
268
269 calculateObjectValue (self, name, args, defaultValue) {
270 let func = name
271
272 if (typeof name === 'string') {
273 // support obj.func1.func2
274 const names = name.split('.')
275
276 if (names.length > 1) {
277 func = window
278 for (const f of names) {
279 func = func[f]
280 }
281 } else {
282 func = window[name]
283 }
284 }
285
286 if (func !== null && typeof func === 'object') {
287 return func
288 }
289
290 if (typeof func === 'function') {
291 return func.apply(self, args || [])
292 }
293
294 if (
295 !func &&
296 typeof name === 'string' &&
297 args &&
298 this.sprintf(name, ...args)
299 ) {
300 return this.sprintf(name, ...args)
301 }
302
303 return defaultValue
304 },
305
306 compareObjects (objectA, objectB, compareLength) {
307 const aKeys = Object.keys(objectA)
308 const bKeys = Object.keys(objectB)
309
310 if (compareLength && aKeys.length !== bKeys.length) {
311 return false
312 }
313
314 for (const key of aKeys) {
315 if (bKeys.includes(key) && objectA[key] !== objectB[key]) {
316 return false
317 }
318 }
319
320 return true
321 },
322
323 regexCompare (value, search) {
324 try {
325 const regexpParts = search.match(/^\/(.*?)\/([gim]*)$/)
326
327 if (value.toString().search(regexpParts ? new RegExp(regexpParts[1], regexpParts[2]) : new RegExp(search, 'gim')) !== -1) {
328 return true
329 }
330 } catch (e) {
331 return false
332 }
333 return false
334 },
335
336 escapeHTML (text) {
337 if (!text) {
338 return text
339 }
340 return text.toString()
341 .replace(/&/g, '&amp;')
342 .replace(/</g, '&lt;')
343 .replace(/>/g, '&gt;')
344 .replace(/"/g, '&quot;')
345 .replace(/'/g, '&#39;')
346 },
347
348 unescapeHTML (text) {
349 if (typeof text !== 'string' || !text) {
350 return text
351 }
352 return text.toString()
353 .replace(/&amp;/g, '&')
354 .replace(/&lt;/g, '<')
355 .replace(/&gt;/g, '>')
356 .replace(/&quot;/g, '"')
357 .replace(/&#39;/g, '\'')
358 },
359
360 removeHTML (text) {
361 if (!text) {
362 return text
363 }
364 return text.toString()
365 .replace(/(<([^>]+)>)/ig, '')
366 .replace(/&[#A-Za-z0-9]+;/gi, '')
367 .trim()
368 },
369
370 getRealDataAttr (dataAttr) {
371 for (const [attr, value] of Object.entries(dataAttr)) {
372 const auxAttr = attr.split(/(?=[A-Z])/).join('-').toLowerCase()
373
374 if (auxAttr !== attr) {
375 dataAttr[auxAttr] = value
376 delete dataAttr[attr]
377 }
378 }
379 return dataAttr
380 },
381
382 getItemField (item, field, escape, columnEscape = undefined) {
383 let value = item
384
385 // use column escape if it is defined
386 if (typeof columnEscape !== 'undefined') {
387 escape = columnEscape
388 }
389
390 if (typeof field !== 'string' || item.hasOwnProperty(field)) {
391 return escape ? this.escapeHTML(item[field]) : item[field]
392 }
393
394 const props = field.split('.')
395
396 for (const p of props) {
397 value = value && value[p]
398 }
399 return escape ? this.escapeHTML(value) : value
400 },
401
402 isIEBrowser () {
403 return navigator.userAgent.includes('MSIE ') ||
404 /Trident.*rv:11\./.test(navigator.userAgent)
405 },
406
407 findIndex (items, item) {
408 for (const it of items) {
409 if (JSON.stringify(it) === JSON.stringify(item)) {
410 return items.indexOf(it)
411 }
412 }
413 return -1
414 },
415
416 trToData (columns, $els) {
417 const data = []
418 const m = []
419
420 $els.each((y, el) => {
421 const $el = $(el)
422 const row = {}
423
424 // save tr's id, class and data-* attributes
425 row._id = $el.attr('id')
426 row._class = $el.attr('class')
427 row._data = this.getRealDataAttr($el.data())
428 row._style = $el.attr('style')
429
430 $el.find('>td,>th').each((_x, el) => {
431 const $el = $(el)
432 const cspan = +$el.attr('colspan') || 1
433 const rspan = +$el.attr('rowspan') || 1
434 let x = _x
435
436 // skip already occupied cells in current row
437 for (; m[y] && m[y][x]; x++) {
438 // ignore
439 }
440
441 // mark matrix elements occupied by current cell with true
442 for (let tx = x; tx < x + cspan; tx++) {
443 for (let ty = y; ty < y + rspan; ty++) {
444 if (!m[ty]) { // fill missing rows
445 m[ty] = []
446 }
447 m[ty][tx] = true
448 }
449 }
450
451 const field = columns[x].field
452
453 row[field] = $el.html().trim()
454 // save td's id, class and data-* attributes
455 row[`_${field}_id`] = $el.attr('id')
456 row[`_${field}_class`] = $el.attr('class')
457 row[`_${field}_rowspan`] = $el.attr('rowspan')
458 row[`_${field}_colspan`] = $el.attr('colspan')
459 row[`_${field}_title`] = $el.attr('title')
460 row[`_${field}_data`] = this.getRealDataAttr($el.data())
461 row[`_${field}_style`] = $el.attr('style')
462 })
463 data.push(row)
464 })
465 return data
466 },
467
468 sort (a, b, order, options, aPosition, bPosition) {
469 if (a === undefined || a === null) {
470 a = ''
471 }
472 if (b === undefined || b === null) {
473 b = ''
474 }
475
476 if (options.sortStable && a === b) {
477 a = aPosition
478 b = bPosition
479 }
480
481 // If both values are numeric, do a numeric comparison
482 if (this.isNumeric(a) && this.isNumeric(b)) {
483 // Convert numerical values form string to float.
484 a = parseFloat(a)
485 b = parseFloat(b)
486 if (a < b) {
487 return order * -1
488 }
489 if (a > b) {
490 return order
491 }
492 return 0
493 }
494
495 if (options.sortEmptyLast) {
496 if (a === '') {
497 return 1
498 }
499
500 if (b === '') {
501 return -1
502 }
503 }
504
505 if (a === b) {
506 return 0
507 }
508
509 // If value is not a string, convert to string
510 if (typeof a !== 'string') {
511 a = a.toString()
512 }
513
514 if (a.localeCompare(b) === -1) {
515 return order * -1
516 }
517
518 return order
519 },
520
521 getEventName (eventPrefix, id = '') {
522 id = id || `${+new Date()}${~~(Math.random() * 1000000)}`
523 return `${eventPrefix}-${id}`
524 },
525
526 hasDetailViewIcon (options) {
527 return options.detailView && options.detailViewIcon && !options.cardView
528 },
529
530 getDetailViewIndexOffset (options) {
531 return this.hasDetailViewIcon(options) && options.detailViewAlign !== 'right' ? 1 : 0
532 },
533
534 checkAutoMergeCells (data) {
535 for (const row of data) {
536 for (const key of Object.keys(row)) {
537 if (key.startsWith('_') && (key.endsWith('_rowspan') || key.endsWith('_colspan'))) {
538 return true
539 }
540 }
541 }
542 return false
543 },
544
545 deepCopy (arg) {
546 if (arg === undefined) {
547 return arg
548 }
549 return $.extend(true, Array.isArray(arg) ? [] : {}, arg)
550 },
551
552 debounce (func, wait, immediate) {
553 let timeout
554
555 return function executedFunction () {
556 const context = this
557 const args = arguments
558
559 const later = function () {
560 timeout = null
561 if (!immediate) func.apply(context, args)
562 }
563
564 const callNow = immediate && !timeout
565
566 clearTimeout(timeout)
567
568 timeout = setTimeout(later, wait)
569
570 if (callNow) func.apply(context, args)
571 }
572 }
573}