summaryrefslogtreecommitdiff
path: root/client/lib/util.js
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2018-08-22 17:35:49 +0200
committerJules Laplace <julescarbon@gmail.com>2018-08-22 17:35:49 +0200
commitacf03a89ea16bfa49db8c1175be8f0cf1e021ab3 (patch)
treeb0ece3cd3f67e5db7cbeed6d58600835f75e5d59 /client/lib/util.js
civic violence simulator
Diffstat (limited to 'client/lib/util.js')
-rw-r--r--client/lib/util.js198
1 files changed, 198 insertions, 0 deletions
diff --git a/client/lib/util.js b/client/lib/util.js
new file mode 100644
index 0000000..35a537d
--- /dev/null
+++ b/client/lib/util.js
@@ -0,0 +1,198 @@
+import Nexus from 'nexusui'
+// import Tone from 'tone'
+// import StartAudioContext from './startAudioContext'
+
+const isIphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))
+const isIpad = (navigator.userAgent.match(/iPad/i))
+const isAndroid = (navigator.userAgent.match(/Android/i))
+const isMobile = isIphone || isIpad || isAndroid
+const isDesktop = ! isMobile
+
+document.body.classList.add(isMobile ? 'mobile' : 'desktop')
+
+const browser = { isIphone, isIpad, isMobile, isDesktop }
+
+function choice (a){ return a[ Math.floor(Math.random() * a.length) ] }
+function randint (n) { return Math.floor(Math.random() * n) }
+function mod(n,m){ return n-(m * Math.floor(n/m)) }
+function norm(n, min, max){ return (n - min) / (max - min) }
+function shuffle(a){
+ a = a.slice(0)
+ for (var i = a.length; i > 0; i--){
+ var r = randint(i)
+ var swap = a[i-1]
+ a[i-1] = a[r]
+ a[r] = swap
+ }
+ return a
+}
+function shuffleInPlace(a){
+ for (var i = a.length; i > 0; i--){
+ var r = randint(i)
+ var swap = a[i-1]
+ a[i-1] = a[r]
+ a[r] = swap
+ }
+ return a
+}
+function requestAudioContext (fn) {
+ if (isMobile) {
+ const container = document.createElement('div')
+ const button = document.createElement('div')
+ button.innerHTML = 'Tap to start - please unmute your phone'
+ Object.assign(container.style, {
+ position: 'absolute',
+ width: '100%',
+ height: '100%',
+ zIndex: '10000',
+ top: '0px',
+ left: '0px',
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
+ })
+ Object.assign(button.style, {
+ position: 'absolute',
+ left: '50%',
+ top: '50%',
+ padding: '20px',
+ backgroundColor: '#7F33ED',
+ color: 'white',
+ fontFamily: 'monospace',
+ borderRadius: '3px',
+ transform: 'translate3D(-50%,-50%,0)',
+ textAlign: 'center',
+ lineHeight: '1.5',
+ })
+ container.appendChild(button)
+ document.body.appendChild(container)
+ StartAudioContext.setContext(Tone.context)
+ StartAudioContext.on(button)
+ StartAudioContext.onStarted(_ => {
+ container.remove()
+ fn()
+ })
+ } else {
+ fn()
+ }
+}
+
+function dataURItoBlob(dataURI) {
+ // convert base64 to raw binary data held in a string
+ // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
+ var byteString = atob(dataURI.split(',')[1]);
+
+ // separate out the mime component
+ var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
+
+ // write the bytes of the string to an ArrayBuffer
+ var ab = new ArrayBuffer(byteString.length);
+
+ // create a view into the buffer
+ var ia = new Uint8Array(ab);
+
+ // set the bytes of the buffer to the correct values
+ for (var i = 0; i < byteString.length; i++) {
+ ia[i] = byteString.charCodeAt(i);
+ }
+
+ // write the ArrayBuffer to a blob, and you're done
+ var blob = new Blob([ab], {type: mimeString});
+ return blob;
+
+}
+function ftom(f) {
+ // return (Math.log(f) - Math.log(261.626)) / Math.log(2) + 4.0
+ return 69 + 12 * Math.log2(f / 440)
+}
+function mtof(m) {
+ return 440 * Math.pow(2, (m - 69) / 12)
+}
+function tap (fn) {
+ return (e) => {
+ if (browser.isMobile) fn()
+ else if (e.press) fn()
+ }
+}
+
+function update_value_on_change(el, id, is_int, fn) {
+ const label = document.querySelector(id + ' + .val')
+ const update = v => {
+ label.innerHTML = is_int ? parseInt(v) : v.toFixed(2)
+ fn && fn(v)
+ }
+ el.on('change', update)
+ label.innerHTML = is_int ? parseInt(el.value) : el.value.toFixed(2)
+ el.update = update
+}
+function update_radio_value_on_change(el, id, values, fn) {
+ let old_v = el.active
+ const label = document.querySelector(id + ' + .val')
+ const update = v => {
+ if (v === -1) {
+ v = el.active = old_v
+ } else {
+ old_v = v
+ }
+ label.innerHTML = values[v][1]
+ fn && fn(v)
+ }
+ el.on('change', update)
+ update(el.active)
+ el.update = update
+}
+function build_options(el, lists, fn) {
+ Object.keys(lists).forEach(key => {
+ const list = lists[key]
+ const option = document.createElement('option')
+ option.innerHTML = list.name
+ option.value = key
+ el.appendChild(option)
+ })
+ el.addEventListener('input', function(e){
+ fn(e.target.value)
+ })
+}
+function Slider(parent, tag, title, options, is_int, fn){
+ const block = document.createElement('div')
+ block.classList.add('block')
+ const el = document.createElement('div')
+ el.setAttribute('id', tag)
+ const val = document.createElement('span')
+ val.classList.add('val')
+ const label = document.createElement('label')
+ label.innerHTML = title
+ block.appendChild(label)
+ block.appendChild(el)
+ block.appendChild(val)
+ parent.appendChild(block)
+ options.size = [200, 24]
+ const nx = new Nexus.Slider('#' + tag, options)
+ update_value_on_change(nx, '#' + tag, is_int, fn)
+ return nx
+}
+function Statistic(parent, label, fn){
+ const block = document.createElement('div')
+ block.classList.add('stat')
+ const key = document.createElement('div')
+ key.classList.add('key')
+ const val = document.createElement('div')
+ val.classList.add('val')
+ block.appendChild(key)
+ block.appendChild(val)
+ parent.appendChild(block)
+ key.innerHTML = label
+ val.innerHTML = fn()
+ let old_v
+ return () => {
+ const v = fn()
+ if (old_v === v) return
+ val.innerText = old_v = v
+ }
+}
+
+export {
+ mod, norm, choice, shuffle, randint,
+ browser, requestAudioContext, ftom, mtof, tap, dataURItoBlob,
+ update_value_on_change, update_radio_value_on_change, build_options,
+ Slider, Statistic,
+ }
+