summaryrefslogtreecommitdiff
path: root/frontend/app/utils/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/app/utils/index.js')
-rw-r--r--frontend/app/utils/index.js628
1 files changed, 337 insertions, 291 deletions
diff --git a/frontend/app/utils/index.js b/frontend/app/utils/index.js
index fc12ee3..5ae979d 100644
--- a/frontend/app/utils/index.js
+++ b/frontend/app/utils/index.js
@@ -1,25 +1,30 @@
-import { api as api_type } from 'app/types'
+import { api as api_type } from "app/types";
// import { format, formatDistance } from 'date-fns'
-import format from 'date-fns/format'
-import formatDistance from 'date-fns/formatDistance'
+import format from "date-fns/format";
+import formatDistance from "date-fns/formatDistance";
-export const formatDateTime = dateStr => format(new Date(dateStr), 'd MMM yyyy H:mm')
-export const formatDate = dateStr => format(new Date(dateStr), 'd MMM yyyy')
-export const formatTime = dateStr => format(new Date(dateStr), 'H:mm')
-export const formatAge = dateStr => formatDistance(new Date(), new Date(dateStr)) + ' ago.'
-export const unslugify = fn => fn.replace(/-/g, ' ').replace(/_/g, ' ').replace('.mp3', '')
+export const formatDateTime = (dateStr) =>
+ format(new Date(dateStr), "d MMM yyyy H:mm");
+export const formatDate = (dateStr) => format(new Date(dateStr), "d MMM yyyy");
+export const formatTime = (dateStr) => format(new Date(dateStr), "H:mm");
+export const formatAge = (dateStr) =>
+ formatDistance(new Date(), new Date(dateStr)) + " ago.";
+export const unslugify = (fn) =>
+ fn.replace(/-/g, " ").replace(/_/g, " ").replace(".mp3", "");
/* Mobile check */
-export const isiPhone = !!((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)))
-export const isiPad = !!(navigator.userAgent.match(/iPad/i))
-export const isAndroid = !!(navigator.userAgent.match(/Android/i))
-export const isMobile = isiPhone || isiPad || isAndroid
-export const isDesktop = !isMobile
+export const isiPhone = !!(
+ navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i)
+);
+export const isiPad = !!navigator.userAgent.match(/iPad/i);
+export const isAndroid = !!navigator.userAgent.match(/Android/i);
+export const isMobile = isiPhone || isiPad || isAndroid;
+export const isDesktop = !isMobile;
-const htmlClassList = document.body.parentNode.classList
-htmlClassList.add(isDesktop ? 'desktop' : 'mobile')
+const htmlClassList = document.body.parentNode.classList;
+htmlClassList.add(isDesktop ? "desktop" : "mobile");
/* Default image dimensions */
@@ -28,399 +33,440 @@ export const widths = {
sm: 320,
md: 640,
lg: 1280,
-}
+};
/* Formatting functions */
-const acronyms = 'id url cc sa fp md5 sha256'.split(' ').map(s => '_' + s)
-const acronymsUpperCase = acronyms.map(s => s.toUpperCase())
+const acronyms = "id url cc sa fp md5 sha256".split(" ").map((s) => "_" + s);
+const acronymsUpperCase = acronyms.map((s) => s.toUpperCase());
-export const formatName = s => {
- acronyms.forEach((acronym, i) => s = s.replace(acronym, acronymsUpperCase[i]))
- return s.replace(/_/g, ' ')
-}
+export const formatName = (s) => {
+ acronyms.forEach(
+ (acronym, i) => (s = s.replace(acronym, acronymsUpperCase[i]))
+ );
+ return s.replace(/_/g, " ");
+};
// Use to pad frame numbers with zeroes
export const pad = (n, m) => {
- let s = String(n || 0)
+ let s = String(n || 0);
while (s.length < m) {
- s = '0' + s
+ s = "0" + s;
}
- return s
-}
+ return s;
+};
-export const courtesyS = (n, s) => n + ' ' + (n === 1 ? s : s + 's')
-export const capitalize = s => s.split(' ').map(capitalizeWord).join(' ')
-export const capitalizeWord = s => s.substr(0, 1).toUpperCase() + s.substr(1)
-export const padSeconds = n => n < 10 ? '0' + n : n
+export const courtesyS = (n, s) => n + " " + (n === 1 ? s : s + "s");
+export const capitalize = (s) => s.split(" ").map(capitalizeWord).join(" ");
+export const capitalizeWord = (s) => s.substr(0, 1).toUpperCase() + s.substr(1);
+export const padSeconds = (n) => (n < 10 ? "0" + n : n);
export const commatize = (n, radix) => {
- radix = radix || -1
- var nums = [], i, counter = 0, r = Math.floor
+ radix = radix || -1;
+ var nums = [],
+ i,
+ counter = 0,
+ r = Math.floor;
if (radix !== -1 && n > radix) {
- n /= radix
- nums.unshift(r((n * 10) % 10))
- nums.unshift(".")
+ n /= radix;
+ nums.unshift(r((n * 10) % 10));
+ nums.unshift(".");
}
do {
- i = r(n % 10)
- n = r(n / 10)
- counter += 1
- if (n && ! (counter % 3))
- { i = ',' + r(i) }
- nums.unshift(i)
- }
- while (n)
- return nums.join("")
-}
+ i = r(n % 10);
+ n = r(n / 10);
+ counter += 1;
+ if (n && !(counter % 3)) {
+ i = "," + r(i);
+ }
+ nums.unshift(i);
+ } while (n);
+ return nums.join("");
+};
export const timestamp = (n = 0, fps = 25) => {
- n /= fps
- let s = padSeconds(Math.round(n) % 60)
- n = Math.floor(n / 60)
+ n /= fps;
+ let s = padSeconds(Math.round(n) % 60);
+ n = Math.floor(n / 60);
if (n > 60) {
- return Math.floor(n / 60) + ':' + padSeconds(n % 60) + ':' + s
+ return Math.floor(n / 60) + ":" + padSeconds(n % 60) + ":" + s;
}
- return (n % 60) + ':' + s
-}
-export const timestampToSeconds = time_str => {
- const time_str_parts = (time_str || "").trim().split(":").map(s => parseFloat(s))
+ return (n % 60) + ":" + s;
+};
+export const timestampToSeconds = (time_str) => {
+ const time_str_parts = (time_str || "")
+ .trim()
+ .split(":")
+ .map((s) => parseFloat(s));
if (time_str_parts.length === 3) {
- return (time_str_parts[0] * 60 + time_str_parts[1]) * 60 + time_str_parts[2]
+ return (
+ (time_str_parts[0] * 60 + time_str_parts[1]) * 60 + time_str_parts[2]
+ );
}
if (time_str_parts.length === 2) {
- return time_str_parts[0] * 60 + time_str_parts[1]
+ return time_str_parts[0] * 60 + time_str_parts[1];
}
- return time_str_parts[0]
-}
+ return time_str_parts[0];
+};
-export const percent = n => (n * 100).toFixed(1) + '%'
+export const percent = (n) => (n * 100).toFixed(1) + "%";
-export const px = (n, w) => Math.round(n * w) + 'px'
+export const px = (n, w) => Math.round(n * w) + "px";
-export const clamp = (n, a=0, b=1) => n < a ? a : n < b ? n : b
-export const dist = (x1, y1, x2, y2) => Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))
-export const mod = (n, m) => n - (m * Math.floor(n / m))
-export const angle = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1)
+export const clamp = (n, a = 0, b = 1) => (n < a ? a : n < b ? n : b);
+export const dist = (x1, y1, x2, y2) =>
+ Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
+export const mod = (n, m) => n - m * Math.floor(n / m);
+export const angle = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
-export const randint = n => Math.floor(Math.random() * n)
+export const randint = (n) => Math.floor(Math.random() * n);
/* URLs */
-export const sha256_tree = (sha256, branch_size=2, tree_depth=2) => {
- const tree_size = tree_depth * branch_size
- let tree = ""
+export const sha256_tree = (sha256, branch_size = 2, tree_depth = 2) => {
+ const tree_size = tree_depth * branch_size;
+ let tree = "";
for (var i = 0; i < tree_size; i += branch_size) {
- tree += '/' + sha256.substr(i, branch_size)
+ tree += "/" + sha256.substr(i, branch_size);
}
- return tree
-}
+ return tree;
+};
-export const imageUrl = (sha256, frame, size = 'th') => [
- 'https://' + process.env.S3_HOST + '/v1/media/keyframes',
- sha256_tree(sha256),
- pad(frame, 6),
- size,
- 'index.jpg'
-].filter(s => !!s).join('/')
+export const imageUrl = (sha256, frame, size = "th") =>
+ [
+ "https://" + process.env.S3_HOST + "/v1/media/keyframes",
+ sha256_tree(sha256),
+ pad(frame, 6),
+ size,
+ "index.jpg",
+ ]
+ .filter((s) => !!s)
+ .join("/");
-export const uploadUri = ({ sha256, ext }) => '/static/data/uploads' + sha256_tree(sha256) + '/' + sha256 + ext
-export const metadataUri = (sha256, tag) => '/metadata/' + sha256 + '/' + tag + '/'
-export const keyframeUri = (sha256, frame) => '/metadata/' + sha256 + '/keyframe/' + pad(frame, 6) + '/'
+export const uploadUri = ({ sha256, ext }) =>
+ "/static/data/uploads" + sha256_tree(sha256) + "/" + sha256 + ext;
+export const metadataUri = (sha256, tag) =>
+ "/metadata/" + sha256 + "/" + tag + "/";
+export const keyframeUri = (sha256, frame) =>
+ "/metadata/" + sha256 + "/keyframe/" + pad(frame, 6) + "/";
-export const preloadImage = url => (
+export const preloadImage = (url) =>
new Promise((resolve, reject) => {
- const image = new Image()
- let loaded = false
+ const image = new Image();
+ let loaded = false;
image.onload = () => {
- if (loaded) return
- loaded = true
- image.onload = null
- image.onerror = null
- resolve(image)
- }
+ if (loaded) return;
+ loaded = true;
+ image.onload = null;
+ image.onerror = null;
+ console.log(image);
+ resolve(image);
+ };
image.onerror = () => {
- if (loaded) return
- image.onload = null
- image.onerror = null
- resolve(image)
- }
+ if (loaded) return;
+ image.onload = null;
+ image.onerror = null;
+ resolve(image);
+ };
// console.log(img.src)
// image.crossOrigin = 'anonymous'
- image.src = url
+ image.src = url;
if (image.complete) {
- image.onload()
+ image.onload();
}
- })
-)
+ });
-export const preloadVideo = (url, options = {}) => (
+export const preloadVideo = (url, options = {}) =>
new Promise((resolve, reject) => {
- const { canplaythrough } = options
- const video = document.createElement('video')
- let loaded = false
+ const { canplaythrough } = options;
+ const video = document.createElement("video");
+ let loaded = false;
const bind = () => {
- video.addEventListener(canplaythrough ? 'canplaythrough' : 'loadedmetadata', onload)
- video.addEventListener('error', onerror)
- }
+ video.addEventListener(
+ canplaythrough ? "canplaythrough" : "loadedmetadata",
+ onload
+ );
+ video.addEventListener("error", onerror);
+ };
const unbind = () => {
- video.removeEventListener(canplaythrough ? 'canplaythrough' : 'loadedmetadata', onload)
- video.removeEventListener('error', onerror)
- }
+ video.removeEventListener(
+ canplaythrough ? "canplaythrough" : "loadedmetadata",
+ onload
+ );
+ video.removeEventListener("error", onerror);
+ };
const onload = () => {
- if (loaded) return
- loaded = true
- unbind()
- resolve(video)
- }
+ if (loaded) return;
+ loaded = true;
+ unbind();
+ resolve(video);
+ };
const onerror = (error) => {
- if (loaded) return
- loaded = true
- unbind()
- reject(error)
- }
- bind()
- video.preload = 'auto'
- video.src = url
- })
-)
+ if (loaded) return;
+ loaded = true;
+ unbind();
+ reject(error);
+ };
+ bind();
+ video.preload = "auto";
+ video.src = url;
+ });
-export const preloadAudio = url => (
+export const preloadAudio = (url) =>
new Promise((resolve, reject) => {
- const audio = document.createElement('audio')
- let loaded = false
+ const audio = document.createElement("audio");
+ let loaded = false;
const bind = () => {
- audio.addEventListener('loadedmetadata', onload)
- audio.addEventListener('error', onerror)
- }
+ audio.addEventListener("loadedmetadata", onload);
+ audio.addEventListener("error", onerror);
+ };
const unbind = () => {
- audio.removeEventListener('loadedmetadata', onload)
- audio.removeEventListener('error', onerror)
- }
+ audio.removeEventListener("loadedmetadata", onload);
+ audio.removeEventListener("error", onerror);
+ };
const onload = () => {
- if (loaded) return
- loaded = true
- unbind()
- resolve(audio)
- }
+ if (loaded) return;
+ loaded = true;
+ unbind();
+ resolve(audio);
+ };
const onerror = (error) => {
- if (loaded) return
- loaded = true
- unbind()
- reject(error)
- }
- bind()
- audio.preload = 'auto'
- audio.src = url
- })
-)
+ if (loaded) return;
+ loaded = true;
+ unbind();
+ reject(error);
+ };
+ bind();
+ audio.preload = "auto";
+ audio.src = url;
+ });
export const cropImage = (url, crop) => {
return new Promise((resolve, reject) => {
- let { x, y, w, h } = crop
- const image = new Image()
- let loaded = false
- x = parseFloat(x)
- y = parseFloat(y)
- w = parseFloat(w)
- h = parseFloat(h)
+ let { x, y, w, h } = crop;
+ const image = new Image();
+ let loaded = false;
+ x = parseFloat(x);
+ y = parseFloat(y);
+ w = parseFloat(w);
+ h = parseFloat(h);
image.onload = () => {
- if (loaded) return
- loaded = true
- image.onload = null
- const canvas = document.createElement('canvas')
- const ctx = canvas.getContext('2d')
- const width = image.naturalWidth
- const height = image.naturalHeight
- canvas.width = w * width
- canvas.height = h * height
+ if (loaded) return;
+ loaded = true;
+ image.onload = null;
+ const canvas = document.createElement("canvas");
+ const ctx = canvas.getContext("2d");
+ const width = image.naturalWidth;
+ const height = image.naturalHeight;
+ canvas.width = w * width;
+ canvas.height = h * height;
ctx.drawImage(
image,
Math.round(x * width),
Math.round(y * height),
Math.round(w * width),
Math.round(h * height),
- 0, 0, canvas.width, canvas.height
- )
- resolve(canvas)
- }
+ 0,
+ 0,
+ canvas.width,
+ canvas.height
+ );
+ resolve(canvas);
+ };
image.onerror = () => {
- console.log('image error')
- reject()
- }
+ console.log("image error");
+ reject();
+ };
// console.log(img.src)
- image.crossOrigin = 'anonymous'
- image.src = url
+ image.crossOrigin = "anonymous";
+ image.src = url;
if (image.complete) {
- image.onload()
+ image.onload();
}
- })
-}
-export const urlSearchParamsToDict = search => {
- const params = new URLSearchParams(search)
- const dict = {}
- params.forEach((value, key) => { // ???
- dict[key] = value
- })
- return dict
-}
+ });
+};
+export const urlSearchParamsToDict = (search) => {
+ const params = new URLSearchParams(search);
+ const dict = {};
+ params.forEach((value, key) => {
+ // ???
+ dict[key] = value;
+ });
+ return dict;
+};
/* AJAX */
-let cachedAuth = null
-let token = ''
-let username = ''
+let cachedAuth = null;
+let token = "";
+let username = "";
-export const post = (dispatch, type=api_type, tag, url, data) => {
- let headers
+export const post = (dispatch, type = api_type, tag, url, data) => {
+ let headers;
if (data instanceof FormData) {
headers = {
- Accept: 'application/json',
- }
+ Accept: "application/json",
+ };
} else if (data) {
headers = {
- Accept: 'application/json',
- 'Content-Type': 'application/json; charset=utf-8',
- }
- data = JSON.stringify(data)
+ Accept: "application/json",
+ "Content-Type": "application/json; charset=utf-8",
+ };
+ data = JSON.stringify(data);
}
dispatch({
type: type.loading,
tag,
- })
+ });
return fetch(url, {
- method: 'POST',
+ method: "POST",
body: data,
headers,
})
- .then(res => res.json())
- .then(res => dispatch({
- type: type.loaded,
- tag,
- data: res,
- }))
- .catch(err => dispatch({
- type: type.error,
- tag,
- err,
- }))
-}
+ .then((res) => res.json())
+ .then((res) =>
+ dispatch({
+ type: type.loaded,
+ tag,
+ data: res,
+ })
+ )
+ .catch((err) =>
+ dispatch({
+ type: type.error,
+ tag,
+ err,
+ })
+ );
+};
-export const api = (dispatch, type=api_type, tag, url, data) => {
+export const api = (dispatch, type = api_type, tag, url, data) => {
dispatch({
type: type.loading,
tag,
- })
- if (url.indexOf('http') !== 0) {
- url = window.location.origin + url
+ });
+ if (url.indexOf("http") !== 0) {
+ url = window.location.origin + url;
}
- url = new URL(url)
+ url = new URL(url);
if (data) {
- url.search = new URLSearchParams(data).toString()
+ url.search = new URLSearchParams(data).toString();
}
return fetch(url, {
- method: 'GET',
+ method: "GET",
// mode: 'cors',
})
- .then(res => res.json())
- .then(res => dispatch({
- type: type.loaded,
- tag,
- data: res,
- }))
- .catch(err => dispatch({
- type: type.error,
- tag,
- err,
- }))
-}
+ .then((res) => res.json())
+ .then((res) =>
+ dispatch({
+ type: type.loaded,
+ tag,
+ data: res,
+ })
+ )
+ .catch((err) =>
+ dispatch({
+ type: type.error,
+ tag,
+ err,
+ })
+ );
+};
/* sorting */
export const numericSort = {
- asc: (a,b) => a[0] - b[0],
- desc: (a,b) => b[0] - a[0],
-}
+ asc: (a, b) => a[0] - b[0],
+ desc: (a, b) => b[0] - a[0],
+};
export const stringSort = {
- asc: (a,b) => a[0].localeCompare(b[0]),
- desc: (a,b) => b[0].localeCompare(a[0]),
-}
-export const orderByFn = (s='name asc') => {
- const [field='name', direction='asc'] = s.split(' ')
- let mapFn, sortFn
+ asc: (a, b) => a[0].localeCompare(b[0]),
+ desc: (a, b) => b[0].localeCompare(a[0]),
+};
+export const orderByFn = (s = "name asc") => {
+ const [field = "name", direction = "asc"] = s.split(" ");
+ let mapFn, sortFn;
switch (field) {
- case 'id':
- mapFn = a => [parseInt(a.id) || 0, a]
- sortFn = numericSort[direction]
- break
- case 'epoch':
- mapFn = a => [parseInt(a.epoch || a.epochs) || 0, a]
- sortFn = numericSort[direction]
- break
- case 'size':
- mapFn = a => [parseInt(a.size) || 0, a]
- sortFn = numericSort[direction]
- break
- case 'date':
- mapFn = a => [+new Date(a.date || a.created_at), a]
- sortFn = numericSort[direction]
- break
- case 'updated_at':
- mapFn = a => [+new Date(a.updated_at), a]
- sortFn = numericSort[direction]
- break
- case 'priority':
- mapFn = a => [parseInt(a.priority) || parseInt(a.id) || 1000, a]
- sortFn = numericSort[direction]
- case 'name':
+ case "id":
+ mapFn = (a) => [parseInt(a.id) || 0, a];
+ sortFn = numericSort[direction];
+ break;
+ case "epoch":
+ mapFn = (a) => [parseInt(a.epoch || a.epochs) || 0, a];
+ sortFn = numericSort[direction];
+ break;
+ case "size":
+ mapFn = (a) => [parseInt(a.size) || 0, a];
+ sortFn = numericSort[direction];
+ break;
+ case "date":
+ mapFn = (a) => [+new Date(a.date || a.created_at), a];
+ sortFn = numericSort[direction];
+ break;
+ case "updated_at":
+ mapFn = (a) => [+new Date(a.updated_at), a];
+ sortFn = numericSort[direction];
+ break;
+ case "priority":
+ mapFn = (a) => [parseInt(a.priority) || parseInt(a.id) || 1000, a];
+ sortFn = numericSort[direction];
+ case "name":
default:
- mapFn = a => [a.name || "", a]
- sortFn = stringSort[direction]
- break
+ mapFn = (a) => [a.name || "", a];
+ sortFn = stringSort[direction];
+ break;
}
- return { mapFn, sortFn }
-}
-export const getOrderedIds = (objects, sort, prepend=[]) => {
- const { mapFn, sortFn } = orderByFn(sort)
- return prepend.concat(objects.map(mapFn).sort(sortFn).map(a => a[1].id))
-}
+ return { mapFn, sortFn };
+};
+export const getOrderedIds = (objects, sort, prepend = []) => {
+ const { mapFn, sortFn } = orderByFn(sort);
+ return prepend.concat(
+ objects
+ .map(mapFn)
+ .sort(sortFn)
+ .map((a) => a[1].id)
+ );
+};
export const getOrderedIdsFromLookup = (lookup, sort) => {
- return getOrderedIds(Object.keys(lookup).map(key => lookup[key]), sort)
-}
+ return getOrderedIds(
+ Object.keys(lookup).map((key) => lookup[key]),
+ sort
+ );
+};
/* parallel promises */
export const allProgress = (promises, progress_cb) => {
- let d = 0
- progress_cb(0, 0, promises.length)
+ let d = 0;
+ progress_cb(0, 0, promises.length);
promises.forEach((p) => {
p.then((s) => {
- d += 1
- progress_cb(Math.floor((d * 100) / promises.length), d, promises.length)
- return s
- })
- })
- return Promise.all(promises)
-}
+ d += 1;
+ progress_cb(Math.floor((d * 100) / promises.length), d, promises.length);
+ return s;
+ });
+ });
+ return Promise.all(promises);
+};
/* Clipboard */
export function writeToClipboard(str) {
return new Promise((resolve, reject) => {
- let success = false
+ let success = false;
function listener(e) {
- e.clipboardData.setData("text/plain", str)
- e.preventDefault()
- success = true
+ e.clipboardData.setData("text/plain", str);
+ e.preventDefault();
+ success = true;
}
- document.addEventListener("copy", listener)
- document.execCommand("copy")
- document.removeEventListener("copy", listener)
+ document.addEventListener("copy", listener);
+ document.execCommand("copy");
+ document.removeEventListener("copy", listener);
if (success) {
- resolve()
+ resolve();
} else {
- reject()
+ reject();
}
- })
-} \ No newline at end of file
+ });
+}