Skip to content

Instantly share code, notes, and snippets.

@jodyheavener
Last active October 8, 2017 03:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jodyheavener/0e6e453c00a99d3575b10f843daccc7a to your computer and use it in GitHub Desktop.
Save jodyheavener/0e6e453c00a99d3575b10f843daccc7a to your computer and use it in GitHub Desktop.
Various Javascript utilities
// Use this to access process env vars in a React setup
export function env(key, fallback) {
return process.env[`REACT_APP_${key}`] || fallback;
}
/**
* request - a single function to perform fetch-based requests with custom data, headers, and optional caching
*
* Requires storage and env (see below)
*
* @param {string} path - required - path to the resource you're attempting to access, to be used as storage key
* @param {object} options - {} - object containing all options for the request
* > @param {object} data - null - key value pairs to pass to your request
* > @param {string} method - "GET" - the verb to use for the request, defaults
* > @param {object} headers - {} - any headers to pass to perform the request with
* > @param {boolean} external - false - should the request use the base path, or is the path itself external?
* > @param {boolean} authed - false - should the request add the Authorization header with bearer value to the headers?
* > @param {boolean} cache - false - should the request be cached? If set to true, future calls will first check if the response exists in the cache
* > @param {integer} expiration - how many days should the cached response be valid?
* @return {Promise} a valid request will resolve with an object containing the data and response information
*/
export const request = (path, { data, method="GET", headers={}, external=false, authed=false, cache=false, expiration }={}) => {
return new Promise((resolve, reject) => {
const basePath = path;
path = external ? path : `${env('API_BASE', '/api')}/${path}`;
if (expiration != null) cache = true;
if (cache) {
var cachedRequest = storage(basePath);
// Does the cached item exist and has it expired?
if (cachedRequest && !cachedRequest.expired) {
return resolve({
data: cachedRequest.data,
response: cachedRequest.response
});
};
};
let params = { method: method };
let setHeaders = Object.assign({
'Content-Type': 'application/json',
'Accept': 'application/json'
}, headers);
if (authed) {
setHeaders['Authorization'] = `Bearer ${auth.getToken()}`;
};
if (data) {
if (method === 'GET') {
path = `${path}?${objectToURLParams(data)}`;
} else {
params.body = JSON.stringify(data);
}
};
params.headers = new Headers(setHeaders);
fetch(path, params).then((response) => {
response.json().then((data) => {
if (cache) {
storage(basePath, {
expiration: expiration,
value: {
data: data,
response: {
status: response.status
}
}
})
};
resolve({
data: data,
response: response
});
}).catch((error) => {
// Response returned no data, so
// send a blank object
resolve({
data: {},
response: response
});
})
}).catch((error) => {
reject({
data: {},
error: error
});
});
});
}
// This is pretty straighforward. Scroll an element (or the page) to a given position, at a certain speed, and with a certain easing.
// When scrolling the body, for best results use (document.scrollingElement || document.documentElement)
const easingEquations = {
easeOutSine: function (pos) {
return Math.sin(pos * (Math.PI / 2))
},
easeInOutSine: function (pos) {
return (-0.5 * (Math.cos(Math.PI * pos) - 1))
},
easeInOutQuint: function (pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 5)
}
return 0.5 * (Math.pow((pos - 2), 5) + 2)
}
}
export function scrollX(el, targetX, speed=10, easing='easeOutSine') {
let scrollX = el.scrollLeft
let currentTime = 0
const time = Math.max(.1, Math.min(Math.abs(scrollX - targetX) / speed, .8))
function tick() {
currentTime += 1 / 60
var p = currentTime / time
var t = easingEquations[easing](p)
if (p < 1) {
window.requestAnimationFrame(tick)
el.scrollLeft = scrollX + ((targetX - scrollX) * t)
} else {
el.scrollLeft = targetX
}
}
tick()
}
export function scrollY(el, targetY, speed=2000, easing='easeOutSine') {
let scrollY = el.scrollTop
let currentTime = 0
const time = Math.max(.1, Math.min(Math.abs(scrollY - targetY) / speed, .8))
function tick() {
currentTime += 1 / 60
var p = currentTime / time
var t = easingEquations[easing](p)
if (p < 1) {
window.requestAnimationFrame(tick)
el.scrollTop = scrollY + ((targetY - scrollY) * t)
} else {
el.scrollTop = targetY
}
}
tick()
}
/**
* storage - basically a wrapper around localStorage to allow storage of objects, and expiring data
*
* @param {string} key - required - the key used to retrieve or store data from storage
* @param {object} options - {} - object containing all options for the action
* > @param {any} value - null - the value of the item you would like to store
* > @param {integer} expiration - 7 - the expiration of the data, in number of days
* > @param {integer} fallback - null - the fallback value, in case the lookup fails
* > @param {integer} remove - false - set to true to remove the storage item
* @return {any}
*/
export const storage = (key, { value, expiration=7, fallback=null, remove=false } = {}) => {
const cacheName = `${config.cacheVersion}Cache`;
const nowSeconds = new Date().getTime() / 1000;
let cacheStore = {};
if (window.localStorage[cacheName]) {
cacheStore = JSON.parse(window.localStorage[cacheName]);
};
if (remove) {
delete cacheStore[key];
window.localStorage[cacheName] = JSON.stringify(cacheStore);
return true;
};
if (value) {
const cacheExpiration = expiration * 86400;
const assignedValue = typeof value === 'string' ? { data: value } : value;
cacheStore[key] = Object.assign({
expires: nowSeconds + cacheExpiration
}, assignedValue);
window.localStorage[cacheName] = JSON.stringify(cacheStore);
return cacheStore[key];
};
return cacheStore[key]
? Object.assign(cacheStore[key], { expired: nowSeconds > cacheStore[key].expires })
: fallback;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment