@es-labs/jslib 0.0.1
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/CHANGELOG.md +4 -0
- package/README.md +42 -0
- package/__test__/services.test.js +32 -0
- package/auth/index.js +226 -0
- package/auth/keyv.js +23 -0
- package/auth/knex.js +29 -0
- package/auth/redis.js +23 -0
- package/comms/email.js +123 -0
- package/comms/nexmo.js +44 -0
- package/comms/telegram.js +43 -0
- package/comms/telegram2/inbound.js +314 -0
- package/comms/telegram2/outbound.js +574 -0
- package/comms/webpush.js +60 -0
- package/config.js +37 -0
- package/express/controller/auth/oauth.js +39 -0
- package/express/controller/auth/oidc.js +87 -0
- package/express/controller/auth/own.js +100 -0
- package/express/controller/auth/saml.js +74 -0
- package/express/upload.js +48 -0
- package/index.js +1 -0
- package/iso/README.md +4 -0
- package/iso/__tests__/csv-utils.spec.js +128 -0
- package/iso/__tests__/datetime.spec.js +101 -0
- package/iso/__tests__/fetch.spec.js +270 -0
- package/iso/csv-utils.js +206 -0
- package/iso/datetime.js +103 -0
- package/iso/fetch.js +129 -0
- package/iso/fetch2.js +180 -0
- package/iso/log-filter.js +17 -0
- package/iso/sleep.js +6 -0
- package/iso/ws.js +63 -0
- package/node/oss-files/oss-uploader-client-fetch.js +258 -0
- package/node/oss-files/oss-uploader-client-fetch.md +31 -0
- package/node/oss-files/oss-uploader-client.js +219 -0
- package/node/oss-files/oss-uploader-server.js +199 -0
- package/node/oss-files/oss-uploader-usage.js +121 -0
- package/node/oss-files/oss-uploader-usage.md +34 -0
- package/node/oss-files/s3-uploader-client.js +217 -0
- package/node/oss-files/s3-uploader-server.js +123 -0
- package/node/oss-files/s3-uploader-usage.js +77 -0
- package/node/oss-files/s3-uploader-usage.md +34 -0
- package/package.json +53 -0
- package/packageInfo.js +9 -0
- package/services/ali.js +279 -0
- package/services/aws.js +194 -0
- package/services/db/__tests__/keyv.spec.js +31 -0
- package/services/db/keyv.js +14 -0
- package/services/db/knex.js +67 -0
- package/services/db/redis.js +51 -0
- package/services/index.js +57 -0
- package/services/mq/README.md +8 -0
- package/services/websocket.js +139 -0
- package/t4t/README.md +1 -0
- package/traps.js +20 -0
- package/utils/__tests__/aes.spec.js +52 -0
- package/utils/aes.js +23 -0
- package/web/UI.md +71 -0
- package/web/bwc-autocomplete.js +211 -0
- package/web/bwc-combobox.js +343 -0
- package/web/bwc-fileupload.js +87 -0
- package/web/bwc-loading-overlay.js +54 -0
- package/web/bwc-t4t-form.js +511 -0
- package/web/bwc-table.js +756 -0
- package/web/fetch.js +129 -0
- package/web/i18n.js +24 -0
- package/web/idle.js +49 -0
- package/web/parse-jwt.js +15 -0
- package/web/pwa.js +84 -0
- package/web/sign-pad.js +164 -0
- package/web/t4t-fe.js +164 -0
- package/web/util.js +126 -0
- package/web/web-cam.js +182 -0
package/web/util.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads data as a file
|
|
3
|
+
* @param {*} content - file content to be downloaded
|
|
4
|
+
* @param {string} filename - name of file to be downloaded
|
|
5
|
+
* @param {string} type - MIME type, default is 'text/csv;charset=utf-8;'
|
|
6
|
+
*/
|
|
7
|
+
// improved download function
|
|
8
|
+
const downloadData = (content, filename, type = 'text/csv;charset=utf-8;') => {
|
|
9
|
+
const blob = new Blob([content], { type })
|
|
10
|
+
// IE11 & Edge
|
|
11
|
+
if (navigator.msSaveBlob) {
|
|
12
|
+
// IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
|
|
13
|
+
navigator.msSaveBlob(blob, filename)
|
|
14
|
+
} else {
|
|
15
|
+
// In FF link must be added to DOM to be clicked
|
|
16
|
+
const link = document.createElement('a')
|
|
17
|
+
link.href = window.URL.createObjectURL(blob)
|
|
18
|
+
// link.setAttribute('href', 'data;text/csv;charset=utf-8,' + encodeURIComponent(SOME_CSV_DATA)) // previous way of doing
|
|
19
|
+
link.setAttribute('download', filename)
|
|
20
|
+
document.body.appendChild(link)
|
|
21
|
+
link.click() // IE: "Access is denied"; see: https://connect.microsoft.com/IE/feedback/details/797361/ie-10-treats-blob-url-as-cross-origin-and-denies-access
|
|
22
|
+
document.body.removeChild(link)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* call function only after a delay ms has passed
|
|
28
|
+
* @param {function} fn - function to debounce
|
|
29
|
+
* @param {Number} delay - milliseconds to delay
|
|
30
|
+
* @returns {function} - return debounced function
|
|
31
|
+
*
|
|
32
|
+
* Sample Usage
|
|
33
|
+
* searchIdDom.addEventListener('input', debounce(makeApiFetch, 1000))
|
|
34
|
+
* Application - Debouncing an input type event handler. (like our search input example), a scroll event handler.
|
|
35
|
+
*/
|
|
36
|
+
const debounce = (fn, delay) => {
|
|
37
|
+
let timeoutTimerId = null // Declare a variable called 'timeoutTimerId' to store the timer ID
|
|
38
|
+
return (...args) => { // Return an anonymous function that takes in any number of arguments
|
|
39
|
+
clearTimeout(timeoutTimerId) // Clear the previous timer to prevent the execution of 'fn'
|
|
40
|
+
timeoutTimerId = setTimeout(() => fn(...args), delay) // Set a new timer that will execute 'fn' after the specified delay
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* call function only X times in Y ms
|
|
46
|
+
* @param {function} fn - function to throttle
|
|
47
|
+
* @param {Number} wait - time to measure the number of calls
|
|
48
|
+
* @returns {function} - return throttled function/s
|
|
49
|
+
*
|
|
50
|
+
* Sample Usage
|
|
51
|
+
* const myHandler = (event) => {...do some stuf...}
|
|
52
|
+
* myMouseDomElement.addEventListener("mousemove", throttle(myHandler, 1000));
|
|
53
|
+
* Application - Throttling an API call, button click so we can’t spam click, touch/move mouse event handler.
|
|
54
|
+
*/
|
|
55
|
+
const throttle = (fn, wait) => {
|
|
56
|
+
let time = Date.now()
|
|
57
|
+
return function () {
|
|
58
|
+
if (time + wait - Date.now() < 0) {
|
|
59
|
+
fn()
|
|
60
|
+
time = Date.now()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// https://www.samanthaming.com/tidbits/94-how-to-check-if-object-is-empty/
|
|
66
|
+
/**
|
|
67
|
+
* check if object is empty, also false if not object
|
|
68
|
+
* @param {any} value - time to measure the number of calls
|
|
69
|
+
* @returns {boolean} - true if empty object, false otherwise
|
|
70
|
+
*/
|
|
71
|
+
const emptyObject = (value) => value && Object.keys(value).length === 0 && value.constructor === Object
|
|
72
|
+
|
|
73
|
+
const isEmail = (email) => {
|
|
74
|
+
// return /[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/.test(email)
|
|
75
|
+
return /[\w\d-]+@[\w\d-]+\.[\w\d-]+/.test(email)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// universal end-of-line splitter
|
|
79
|
+
// .split(/\r?\n/)
|
|
80
|
+
|
|
81
|
+
// https://stackoverflow.com/questions/8847766/how-to-convert-json-to-csv-format-and-store-in-a-variable
|
|
82
|
+
const jsonToCsv = (items) => {
|
|
83
|
+
const replacer = (key, value) => value === null ? '' : value // specify how you want to handle null values here
|
|
84
|
+
const header = Object.keys(items[0])
|
|
85
|
+
return [
|
|
86
|
+
header.join(','), // header row first
|
|
87
|
+
...items.map(row => header.map(fieldName => JSON.stringify(row[fieldName], replacer)).join(','))
|
|
88
|
+
].join('\r\n')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* convert object to query string
|
|
93
|
+
* @param {object} obj
|
|
94
|
+
* @returns
|
|
95
|
+
*/
|
|
96
|
+
const objectToQueryString = (obj) =>
|
|
97
|
+
Object.keys(obj)
|
|
98
|
+
.map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
|
|
99
|
+
.join('&')
|
|
100
|
+
|
|
101
|
+
const rebaseString = (str, from, to) => Buffer.from(str, from).toString(to) // base64, utf8
|
|
102
|
+
|
|
103
|
+
// NOSONAR
|
|
104
|
+
// Description: traverse a JSON object and transform the nodes. object will be mutated
|
|
105
|
+
//
|
|
106
|
+
// Usage:
|
|
107
|
+
// let testObj = [
|
|
108
|
+
// 1, 2, [3, { 'a': 5, 'b': [6, 7, { 'c': 8}] }], { 'd': 9}
|
|
109
|
+
// ]
|
|
110
|
+
// const testFn = (x) => typeof x === 'number' ? x * 2 : x
|
|
111
|
+
// traverseJson(testObj, testFn)
|
|
112
|
+
// console.log(testObj)
|
|
113
|
+
//
|
|
114
|
+
const traverseJson = (json, fn, parent = null, key = null) => {
|
|
115
|
+
if (typeof json === 'object') {
|
|
116
|
+
if (Array.isArray(json)) {
|
|
117
|
+
json.forEach((item, index) => traverseJson(item, fn, json, index))
|
|
118
|
+
} else if (json.constructor === Object) {
|
|
119
|
+
for (const k in json) traverseJson(json[k], fn, json, k)
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
if (typeof parent === 'object' && key !== null) parent[key] = fn(json)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { debounce, downloadData, emptyObject, isEmail, jsonToCsv, objectToQueryString, rebaseString, throttle, traverseJson }
|
package/web/web-cam.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// FRONTEND ONLY
|
|
2
|
+
const template = document.createElement('template')
|
|
3
|
+
template.innerHTML = `
|
|
4
|
+
<style>
|
|
5
|
+
body {
|
|
6
|
+
background-color: #F0F0F0;
|
|
7
|
+
}
|
|
8
|
+
.container{
|
|
9
|
+
position: relative;
|
|
10
|
+
width: 320px;
|
|
11
|
+
height: 240px;
|
|
12
|
+
}
|
|
13
|
+
.container>.button2 {
|
|
14
|
+
background: url('http://cdn1.iconfinder.com/data/icons/iconslandplayer/PNG/64x64/CircleBlue/Play1Pressed.png') center center no-repeat;
|
|
15
|
+
height: 64px;
|
|
16
|
+
left: 50%;
|
|
17
|
+
margin: -32px 0 0 -32px;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 50%;
|
|
20
|
+
width: 64px;
|
|
21
|
+
z-index: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.container>slot>.button {
|
|
25
|
+
top: 5%;
|
|
26
|
+
right: 5%;
|
|
27
|
+
position: absolute;
|
|
28
|
+
z-index: 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
::slotted(button) {
|
|
32
|
+
top: var(--vcxwc-web-cam-top, 25%);
|
|
33
|
+
right: var(--vcxwc-web-cam-top, 25%);
|
|
34
|
+
position: absolute;
|
|
35
|
+
z-index: 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#video {
|
|
39
|
+
background-color: #000000;
|
|
40
|
+
}
|
|
41
|
+
#snap {
|
|
42
|
+
display: block;
|
|
43
|
+
}
|
|
44
|
+
#unsnap {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
</style>
|
|
49
|
+
<div class="container">
|
|
50
|
+
<video id="video" width="320" height="240" autoplay></video>
|
|
51
|
+
<slot name="button-snap">
|
|
52
|
+
<button class="button" id="snap">Take Photo</button>
|
|
53
|
+
</slot>
|
|
54
|
+
<slot name="button-unsnap">
|
|
55
|
+
<button class="button" id="unsnap">Start Camera</button>
|
|
56
|
+
</slot>
|
|
57
|
+
</div>
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
class WebCam extends HTMLElement {
|
|
61
|
+
constructor() {
|
|
62
|
+
super()
|
|
63
|
+
const shadowRoot = this.attachShadow({ mode: 'open' })
|
|
64
|
+
shadowRoot.appendChild(template.content.cloneNode(true))
|
|
65
|
+
|
|
66
|
+
this.capture = this.capture.bind(this) // bind callback function
|
|
67
|
+
// captures: []
|
|
68
|
+
|
|
69
|
+
this.slotNode = {
|
|
70
|
+
['button-snap']: this.shadowRoot.querySelector('#snap'),
|
|
71
|
+
['button-unsnap']: this.shadowRoot.querySelector('#unsnap')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const slots = this.shadowRoot.querySelectorAll('slot')
|
|
75
|
+
// console.log(slots)
|
|
76
|
+
const slotMap = {}
|
|
77
|
+
slots.forEach((slot) => {
|
|
78
|
+
slotMap[slot.name] = slot
|
|
79
|
+
slot.addEventListener('slotchange', (e) => {
|
|
80
|
+
if (this.slotNode[slot.name]) {
|
|
81
|
+
const nodes = slot.assignedNodes()
|
|
82
|
+
const btnNode = nodes[0]
|
|
83
|
+
this.slotNode[slot.name].removeEventListener('click', this.capture)
|
|
84
|
+
this.slotNode[slot.name] = btnNode
|
|
85
|
+
if (slot.name === 'button-snap') this.slotNode[slot.name].style.display = 'block'
|
|
86
|
+
if (slot.name === 'button-unsnap') this.slotNode[slot.name].style.display = 'none'
|
|
87
|
+
this.slotNode[slot.name].addEventListener('click', this.capture)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
console.log(slotMap)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static get observedAttributes() {
|
|
95
|
+
return ['show']
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// getter and setter for property - show
|
|
99
|
+
get show() {
|
|
100
|
+
return this.hasAttribute('show')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
set show(value) {
|
|
104
|
+
value ? this.setAttribute('show', '') : this.removeAttribute('show')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// added to the DOM
|
|
108
|
+
connectedCallback() {
|
|
109
|
+
console.log('connected')
|
|
110
|
+
|
|
111
|
+
console.log('width', this.getAttribute('width'))
|
|
112
|
+
console.log('height', this.getAttribute('height'))
|
|
113
|
+
this.width = this.getAttribute('width') || 320
|
|
114
|
+
this.height = this.getAttribute('height') || 240
|
|
115
|
+
|
|
116
|
+
this.slotNode['button-snap'].addEventListener('click', this.capture)
|
|
117
|
+
this.slotNode['button-unsnap'].addEventListener('click', this.capture)
|
|
118
|
+
|
|
119
|
+
const containerEl = this.shadowRoot.querySelector('.container')
|
|
120
|
+
containerEl.style.width = this.width + 'px'
|
|
121
|
+
containerEl.style.height = this.height + 'px'
|
|
122
|
+
// console.log('containerEl', containerEl)
|
|
123
|
+
|
|
124
|
+
const videoEl = this.shadowRoot.querySelector('#video')
|
|
125
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
126
|
+
// console.log('xxxx', window.URL.createObjectURL)
|
|
127
|
+
navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
|
|
128
|
+
try {
|
|
129
|
+
videoEl.srcObject = stream
|
|
130
|
+
} catch (e) {
|
|
131
|
+
videoEl.src = window.URL.createObjectURL(stream)
|
|
132
|
+
}
|
|
133
|
+
videoEl.play()
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
videoEl.setAttribute('width', this.width)
|
|
137
|
+
videoEl.setAttribute('height', this.height)
|
|
138
|
+
|
|
139
|
+
this.captureMode = true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// attributeChangedCallback(name, oldVal, newVal) {} // attribute changed
|
|
143
|
+
// adoptedCallback() {} // moved into a new document
|
|
144
|
+
|
|
145
|
+
// removed from the DOM
|
|
146
|
+
disconnectedCallback() {
|
|
147
|
+
this.slotNode['button-snap'].removeEventListener('click', this.capture)
|
|
148
|
+
this.slotNode['button-unsnap'].removeEventListener('click', this.capture)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
capture() {
|
|
152
|
+
console.log('capture event')
|
|
153
|
+
const videoEl = this.shadowRoot.querySelector('#video')
|
|
154
|
+
if (this.captureMode) {
|
|
155
|
+
const scale = 1
|
|
156
|
+
const canvas = document.createElement('canvas')
|
|
157
|
+
canvas.width = videoEl.clientWidth * scale
|
|
158
|
+
canvas.height = videoEl.clientHeight * scale
|
|
159
|
+
canvas.getContext('2d').drawImage(videoEl, 0, 0, canvas.width, canvas.height)
|
|
160
|
+
videoEl.pause()
|
|
161
|
+
|
|
162
|
+
// console.log('videoEl', canvas.toDataURL('image/png'))
|
|
163
|
+
this.slotNode['button-snap'].style.display = 'none'
|
|
164
|
+
this.slotNode['button-unsnap'].style.display = 'block'
|
|
165
|
+
|
|
166
|
+
const event = new CustomEvent('snap', {
|
|
167
|
+
detail: canvas.toDataURL('image/png')
|
|
168
|
+
})
|
|
169
|
+
this.dispatchEvent(event)
|
|
170
|
+
|
|
171
|
+
this.captureMode = false
|
|
172
|
+
} else {
|
|
173
|
+
videoEl.play()
|
|
174
|
+
this.captureMode = true
|
|
175
|
+
this.slotNode['button-snap'].style.display = 'block'
|
|
176
|
+
this.slotNode['button-unsnap'].style.display = 'none'
|
|
177
|
+
// set image data back to empty
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
customElements.define('vcxwc-web-cam', WebCam)
|