-
-
Save codenameyau/9f0a26a0f1be35a6784a to your computer and use it in GitHub Desktop.
/************************************************************************ | |
* CLIENT ONE-LINERS | |
*************************************************************************/ | |
// Set top level domain cookie. | |
document.cookie = "token=12345; Max-Age=120; Secure; Domain=mozilla.org; Path=/;" | |
// Set a subdomain cookie. | |
document.cookie = "name=hello world; Max-Age=120; Secure; Domain=developer.mozilla.org; Path=/;" | |
// You can have cookies with the same name but with different paths and domains. | |
document.cookie = "name=1; Max-Age=60; Secure; Domain=mozilla.org;" | |
document.cookie = "name=2; Max-Age=60; Secure; Domain=mozilla.org; Path=/" | |
document.cookie = "name=3; Max-Age=60; Secure; Domain=developer.mozilla.org; Path=/;" | |
// Copies variable. | |
copy(temp1) | |
copy(JSON.stringify(temp1)) | |
// Capture regex groups with variable | |
const string = 'ayyyy lmao' | |
const rgx = /(?<firstVowel>[aeiou])/ | |
string.match(rgx).groups.firstVowel | |
// Copies current selection to clipboard. | |
// https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand | |
// https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f | |
document.execCommand('copy') | |
// Copies text content of click to clipboard. | |
navigator.clipboard && navigator.clipboard.writeText(event.target.textContent); | |
// Makes page text editable. | |
document.designMode = 'on' | |
// Access third party frames on window. | |
window.frames | |
// Create bookmark and set this to URL to skip ads. | |
javascript:void(document.querySelector('video').currentTime = document.querySelector('video').duration) | |
// See all event listeners. Remember to remove listener when unmounting. | |
getEventListeners(document) | |
// See Slack Emoji authors. | |
Array.from(document.querySelectorAll('.author_cell > a')).map((a) => a.text.trim()).sort() | |
// Use this to click all links. | |
Array.from(document.querySelectorAll('a.block')).forEach(a => a.click()) | |
// Grab all the images from a page (namely instagram) | |
Array.from(document.images).forEach(img => console.log(img.getAttribute('src'))) | |
// Like all facebook posts. | |
Array.from(document.body.querySelectorAll('.UFILikeLink')).forEach((a) => a.click()) | |
// Selecting wildcard classes. | |
Array.from(document.querySelectorAll('div[class^="ManageSection__column-left"] .dp-checkbox')).forEach((a) => a.click()) | |
/************************************************************************ | |
* SERVER ONE-LINERS | |
*************************************************************************/ | |
// Use Now to deploying static app serving a single page app. | |
ns ./build --cmd 'list ./content -s' |
// Checks if nested property exists. | |
db.drafts.find({"collection_type": "web_advertising", "email": "jorge@email.com", "rows.222": { $exists: true } }); | |
// Grabs the count. | |
db.drafts.find({"email": "jorge@email.com"}).count(); |
/******************************************************************** | |
* UTILS | |
*********************************************************************/ | |
// https://stackoverflow.com/q/779379 | |
setTimeout(function (callback) { | |
// Offload Heavy computation. | |
}, 0); | |
/******************************************************************** | |
* JQUERY serializeArray | |
*********************************************************************/ | |
// Converts form into Array of Objects. | |
// https://plainjs.com/javascript/ajax/serialize-form-data-into-an-array-46/ | |
function serializeArray(form) { | |
var field, l, s = []; | |
if (typeof form == 'object' && form.nodeName == "FORM") { | |
var len = form.elements.length; | |
for (var i = 0; i < len; i++) { | |
field = form.elements[i]; | |
if (field.name && !field.disabled && field.type != 'file' && field.type != 'reset' && field.type != 'submit' && field.type != 'button') { | |
if (field.type == 'select-multiple') { | |
l = form.elements[i].options.length; | |
for (j = 0; j < l; j++) { | |
if (field.options[j].selected) | |
s[s.length] = { | |
name: field.name, | |
value: field.options[j].value | |
}; | |
} | |
} else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) { | |
s[s.length] = { | |
name: field.name, | |
value: field.value | |
}; | |
} | |
} | |
} | |
} | |
return s; | |
} | |
// Nodejs check if module is called directly | |
const CALLED_DIRECTLY = require.main === module; | |
function splitTextarea(text) { | |
return text.trim().replace(/\s+/, '\n').replace(/\n{2,}/, '').split('\n'); | |
} | |
// Download using fetch API.. | |
import download from 'downloadjs'; | |
const handleBufferDownload = (filename) => { | |
res.arrayBuffer() | |
.then((buffer) => { | |
download(buffer, filename, contentType); | |
stopLoading(); | |
}); | |
}; | |
// http://stackoverflow.com/a/23842171 | |
encodeURIComparison = function() { | |
var arr = []; | |
for(var i=0; i<256; i++) { | |
var char=String.fromCharCode(i); | |
if(encodeURI(char) !== encodeURIComponent(char)) { | |
arr.push({ | |
character: char, | |
encodeURI: encodeURI(char), | |
encodeURIComponent: encodeURIComponent(char) | |
}); | |
} | |
} | |
console.table(arr); | |
}; | |
function encodeQueryParams (url, queryParams) { | |
return Object.keys(queryParams).reduce((a, key) => { | |
a.push(key + '=' + encodeURIComponent(queryParams[key])); | |
return a; | |
}, []).join('&'); | |
} | |
let queryString = encodeQueryParams(queryParams); | |
url += url.indexOf('?') > -1 ? '&' : '?' + queryString; | |
/******************************************************************** | |
* JQUERY COLOR CONTRAST | |
*********************************************************************/ | |
jQuery.Color.fn.contrastColor = function() { | |
var r = this._rgba[0], g = this._rgba[1], b = this._rgba[2]; | |
return (((r*299)+(g*587)+(b*144))/1000) >= 131.5 ? "black" : "white"; | |
}; | |
// usage examples: | |
jQuery.Color("#bada55").contrastColor(); // "black" | |
element.css( "color", jQuery.Color( element, "backgroundColor" ).contrastColor() ); | |
Console scripting
- Bookmarklets: https://en.wikipedia.org/wiki/Bookmarklet
- Load external scripts: https://stackoverflow.com/questions/106425/load-external-js-from-bookmarklet
- Load external script snippet: https://gist.github.com/cou929/587995
- Content Security Policy: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- Copy as Curl: https://developers.google.com/web/updates/2015/05/replay-a-network-request-in-curl
- https://stackoverflow.com/questions/2934787/view-list-of-all-javascript-variables-in-google-chrome-console
- https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging
// Create bookmark and set this to URL to skip ads.
javascript:void(document.querySelector('video').currentTime = document.querySelector('video').duration)
// See global variables.
window
keys(window)
dir(window)
// Access React component.
window.FindReact = function(domElement) {
for (var key in domElement) {
if (key.startsWith("__reactInternalInstance$")) {
var compInternals = domElement[key]._currentElement;
var compWrapper = compInternals._owner;
var comp = compWrapper._instance;
return comp;
}
}
return null;
};
// Access component state from render element.
$0.__reactInternalInstance$8d28m1ddneb._currentElement
$0.__reactInternalInstance$8d28m1ddneb._currentElement._owner._instance
Knex and Bookshel ORM
Raw queries with knex and bookshelf.
const knex = require('knex');
const bookshelf = require('bookshelf');
const chalk = require('chalk');
// Setup pooling and connections to Postgres DB
const postgres = exports.client = knex({
client: 'pg',
connection: {
host: process.env.POSTGRES_DB_HOST,
user: process.env.POSTGRES_DB_USER,
password: process.env.POSTGRES_DB_PASSWORD,
database: process.env.POSTGRES_DB_NAME,
port: process.env.POSTGRES_DB_PORT,
ssl: true
},
pool: {
min: 0,
max: 10
}
});
// Used for applying query params to get requests.
const filterParams = exports.filterParams = (params, paramsList) => {
const filteredParams = {};
paramsList.forEach((param) => {
if (params[param]) {
filteredParams[param] = params[param];
}
});
return filteredParams;
};
// Enable console output of SQL queries.
const headerMessage = chalk.dim.italic;
const infoMessage = chalk.yellow;
postgres.on('query', (query) => {
console.log(
headerMessage('\nQuery: '),
infoMessage(`"${query.sql}"`),
headerMessage('\nParams: '),
infoMessage(`[${query.bindings}]\n`)
);
});
const errorMessage = chalk.red;
const dbQueryError = exports.dbQueryError = (res, error) => {
console.error(errorMessage(error));
res.sendStatus(400);
};
// Setup ORM
const orm = exports.orm = bookshelf(postgres);
// Log connection details
const hostName = (process.env.POSTGRES_DB_HOST || '')
console.log(`[+] Connected to Postgres DB: ${hostName}`);
Knex raw queries
// controller.js
app.get('/api/search/datapoints/', (req, res, next) => {
const query = `
select dp.id, dp.name, dp.datasource_id, ds.name as datasource_name
from ref.datapoint dp
join ref.datasource ds on ds.id = dp.datasource_id
where lower(dp.name) ilike (?)
`;
const params = [`%${req.query.q}%`];
orm.knex.raw(query, params).then((result) => {
res.json(result.rows);
}).catch(requestError(next));
});
Knex transactions and many-to-many delete and insert with transaction
, detach
, attach
.
const section_id = parseInt(req.params.sectionId, 10);
const timestamp = (new Date()).toJSON();
// Bulk delete all datapoints in the section and then add new datapoints.
orm.knex.transaction((transaction) => {
return Section
.forge({ id: section_id })
.fetch()
.then((section) => {
return section.datapoints().detach(null, { transacting: transaction })
.then(() => {
return section;
});
})
.then((section) => {
const newDatapoints = req.body.map((datapoint) => {
return {
datapoint_id: datapoint.id,
section_id: section_id,
created_by: req.user.email,
created_date: timestamp,
ordinal_position: datapoint.ordinal_position
};
});
return section.datapoints().attach(newDatapoints, { transacting: transaction });
})
.then((datapointToSectionList) => {
return res.json(datapointToSectionList);
});
});
Uploading and downloading excel spreadsheets via AJAX and multer.
const memory_storage = multer.memoryStorage();
const buffer_upload = multer({ storage: memory_storage });
const BATCH_CHUNK_SIZE = 20;
app.post('/api/datapoint/reservation', (req, res, next) => {
const createSpreadsheet = (ids) => {
let fields = ['ID', 'Name', 'Description'];
let data = [fields];
ids.forEach((datapointID) => {
data.push([datapointID]); // Add more cols to this row.
});
return xlsx.build([{ name: 'custom datapoints', data: data }]);
};
const {datasource_id, count} = req.body;
let batchRows = [];
for (var i = 0; i < count; i++) {
batchRows.push({
datasource_id: datasource_id,
reserved_by: req.user.email
});
}
// You can also use postgres.batchInsert
orm.knex.batchInsert('ref.datapoint', batchRows, BATCH_CHUNK_SIZE)
.returning('id')
.then((ids) => {
const xlsxBuffer = createSpreadsheet(ids);
res.attachment('dp-template.xlsx');
res.set('Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.send(xlsxBuffer);
})
.catch(requestError(next));
});
app.post('/api/datapoint/upload', buffer_upload.single('spreadsheet'), (req, res, next) => {
const timestamp = (new Date()).toJSON();
const spreadsheet = xlsx.parse(req.file.buffer);
const templateWorksheet = spreadsheet[0];
const templateRows = templateWorksheet.data;
const dataRows = templateRows.slice(1);
let updateRows = dataRows.map((row) => {
return {
id: row[0],
name: row[1],
description: row[2],
data_type: row[3].toLowerCase(),
display_type: row[4],
created_by: req.user.email,
created_date: timestamp,
is_active: true,
is_terminated: false
};
});
async.each(updateRows, (row, callback) => {
Datapoint.forge({ id: row.id })
.save({ ...row })
.then((datapoint) => {
callback(null);
});
}, (error) => {
if (error) { return res.status(500).send(error); }
return res.sendStatus(200);
});
});
Image Tool to resize canvas: https://gist.github.com/dcollien/312bce1270a5f511bf4a
var ImageTool = {};
var hasToBlobSupport = typeof HTMLCanvasElement !== "undefined"
? HTMLCanvasElement.prototype.toBlob : false;
ImageTool.resize = function(file, maxDimensions, callback) {
if (typeof maxDimensions === 'function') {
callback = maxDimensions;
maxDimensions = {
width: 600,
height: 600
};
}
var maxWidth = maxDimensions.width;
var maxHeight = maxDimensions.height;
var image = document.createElement('img');
image.onload = function (imgEvt) {
var width = image.width;
var height = image.height;
if (width > height && width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
} else if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
}
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
if (hasToBlobSupport) {
canvas.toBlob(function (blob) {
return callback(blob, true);
}, file.type);
}
else {
var blob = ImageTool._toBlob(canvas, file.type);
return callback(blob, true);
}
};
ImageTool._loadImage(image, file);
return true;
};
ImageTool._toBlob = function(canvas, type) {
var dataURI = canvas.toDataURL(type);
var dataURIParts = dataURI.split(',');
var byteString;
if (dataURIParts[0].indexOf('base64') >= 0) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataURIParts[1]);
} else {
// Convert base64/URLEncoded data component to raw binary data:
byteString = decodeURIComponent(dataURIParts[1]);
}
var arrayBuffer = new ArrayBuffer(byteString.length);
var intArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
var mimeString = dataURIParts[0].split(':')[1].split(';')[0];
var blob = null;
blob = new Blob([intArray], { type: mimeString });
return blob;
};
ImageTool._loadImage = function(image, file, callback) {
if (typeof URL === 'undefined') {
var reader = new FileReader();
reader.onload = function (evt) {
image.src = evt.target.result;
if (callback) {
return callback();
}
};
reader.readAsDataURL(file);
}
else {
image.src = URL.createObjectURL(file);
if (callback) {
return callback();
}
}
};
return ImageTool;
Old Image Module and upload with AJAX. Broken.
/********************************************************************
* PREVIEW, RESIZE, AND UPLOAD IMAGE VIA AJAX
*********************************************************************/
// https://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/
function previewImageUpload ($e) {
var files = $e.target.files;
if (files) {
var imgContainerSelector = $e.target.getAttribute('data-img-preview-container');
var imgPreviewObj = URL.createObjectURL(files[0]);
var $imgContainer = $(imgContainerSelector);
$imgContainer.html('<img class="img-upload-preview">');
$imgContainer.find('img').attr('src', imgPreviewObj);
}
}
// http://stackoverflow.com/a/5100158
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = window.unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
function resizeWidth(file, max_width, callback) {
max_width = max_width || 600;
// Read image from file system and resize.
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement('img');
img.src = e.target.result;
// Specify new width and height.
var width = img.width;
var height = img.height;
if (width > max_width) {
height = parseInt(height * (max_width / width), 10);
width = max_width;
}
// Create canvas which performs the re-sizing.
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.style.visibility = 'hidden';
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var resizedFile = dataURItoBlob(canvas.toDataURL(file.type));
callback(resizedFile);
};
reader.readAsDataURL(file);
}
function formatFilename(filename) {
var extIndex = filename.lastIndexOf('.');
var basename = filename.slice(0, extIndex).replace(/\s+/g, '-');
var extension = filename.slice(extIndex);
return basename + '-' + Date.now() + extension;
}
// Callback to ajax. It needs a special handler to upload images.
var $inputImage = $form.find('.input-image-upload');
var imageFile = $inputImage[0].files[0];
var formattedFilename = (imageFile === undefined) ?
self.model.get('logo_filename') : this.formatFilename(imageFile.name);
resizeWidth(imageFile, 115, function(resizedFile) {
var formData = new FormData();
formData.append('input-image-upload', resizedFile, formattedFilename);
$.ajax({
url: '/api/brand-logo/',
type: 'POST',
data: formData,
cache: false,
processData: false,
contentType: false,
success: function(data, status) {
console.log(data);
}
});
});
/********************************************************************
* RAW MANY TO MANY FOR API
*********************************************************************/
var fake_data_points = [
{ id: 1, name: 'Can its beauty be refurbished?' },
{ id: 2, name: 'Offers online car cleaning?' },
{ id: 3, name: 'Has shopping cart?' }
];
var fake_projects = [
{ id: 1, name: 'Auto' },
{ id: 2, name: 'Beauty' }
];
var fake_data_points_to_project = [
{ id: 1, data_point_id: 1, project_id: 1 },
{ id: 2, data_point_id: 1, project_id: 2 },
{ id: 4, data_point_id: 2, project_id: 1 },
{ id: 5, data_point_id: 2, project_id: 2 }
];
exports.get_many_to_many = function(params) {
var many_to_many_data = _.where(params.many_to_many, params.lookup);
var related_table_index = _.indexBy(params.related_table, 'id');
return _.map(many_to_many_data, function(object) {
return related_table_index[object[params.related_id]];
});
};
get_many_to_many({
many_to_many: fake_data_points_to_project,
table: fake_data_points,
related_table: fake_data_points,
lookup: {'data_point_id': 1},
related_id: 'project_id'
});
Assignments and reference
// Reference
a = { a1: {a2: 1} }
// Assign by reference.
b = a
// Assign property.
c = a.a1
// Delete pointer.
delete a.a1
console.log(a);
console.log(b);
console.log(c);
Redux React
- Structure app and absolute imports: https://daveceddia.com/react-project-structure/
- React Ideal directory structure: https://marmelab.com/blog/2015/12/17/react-directory-structure.html
React CSS
There are three main ways to use css with React. Alternatives:
- Styled components: https://github.com/styled-components/styled-components
Styled components
import styled, { css } from 'styled-components';
const CheckValidation = styled.div`
background: ${({ isValid }) =>
isValid ? `url(${CheckComplete})` : `url(${CheckIncomplete})`}
0 center no-repeat;
`;
const CheckValidation = styled.div`
background: url(${CheckIncomplete}) 0 center no-repeat;
${({ isValid }) => isValid && css`
background-image: url(${CheckComplete});
`}
`;
Define styles directly inside component
import React from 'react';
const Red = {
color: 'white';
backgroundColor: 'red';
};
export class Component extends React.Component {
render() {
return (
<div style={Red}>This is white text on red background</div>
);
}
}
export default Component;
Use css loader and import styles
import React from 'react';
import '../styles/styles.css';
export class Component extends React.Component {
render() {
return (
<div className="red">Hello World</div>
);
}
}
Use css modules
https://medium.com/nulogy/how-to-use-css-modules-with-create-react-app-9e44bec2b5c2
import React from 'react';
import styles from '../styles/styles.css';
export class Component extends React.Component {
render() {
return (
<div className="styles.red">Hello World</div>
);
}
}
Redux Best Practices
- Redux styleguide: https://redux.js.org/style-guide/style-guide
- Reducers Structure: http://redux.js.org/docs/recipes/reducers/BasicReducerStructure.html
You should avoid nesting in store. But here is how you update nested state.
/********************************************************************
* NESTED REDUCERS
*********************************************************************/
var x = {
name: 'Sabbir',
same: 'name',
skillz: { awesomeness: false, dbStuffs: false }
};
var y = {
name: 'Sabbeez',
skillz: { awesomeness: true }
};
// First extend flat props, then extended the nested skillz.
var z = {...x, ...y, skillz: {...x.skillz, ...y.skillz}};
console.log(z);
String replace with components (ex. highlight string): facebook/react#3386
_renderHighlightedName () {
const { datapoint, highlight } = this.props;
let datapointName = datapoint.name;
let highlightI = highlight ? highlight.toLowerCase() : highlight;
if (highlight && datapointName.toLowerCase().includes(highlightI)) {
var regex = new RegExp(`(${highlightI})`, 'gi');
var parts = datapointName.split(regex);
for (var i = 1; i < parts.length; i += 2) {
parts[i] = (
<span key={`highlight-${i}`} className={classes['dp-name-highlight']}>
{parts[i]}
</span>
);
}
datapointName = parts;
}
return <span className={classes['dp-item-name']}>{datapointName}</span>;
}
Tree rendering in React
const DatapointTree = (datapoints) => {
// Extend each datapoint with these properties: ancestors, children
// ancestors is array of ids to avoid circular object recursion.
datapoints.forEach((datapoint) => {
datapoint.tree = {
ancestors: datapoint.parent_path.split('.').slice(1),
children: {},
children_list: []
};
});
// Create a sorted array based the length of parents to build the tree.
let sortedDatapoints = datapoints.sort((dpA, dpB) => {
return dpA.tree.ancestors.length < dpB.tree.ancestors.length ? -1
: dpA.tree.ancestors.length > dpB.tree.ancestors.length ? 1 : 0;
});
// Iterate through the sorted parents path to build tree.
let tree = { children: {}, children_list: [] };
sortedDatapoints.forEach((datapoint) => {
let ancestors = datapoint.tree.ancestors;
// This node has no ancestors so add it underneath the root node.
if (!ancestors.length) {
tree.children[datapoint.id] = datapoint;
tree.children_list.push(datapoint);
}
// Traverse the tree to get the parent subtree, and add the current
// datapoint as a new node under the parent subtree.
else {
let subtree = tree;
ancestors.forEach((ancestorId) => {
if (subtree.children[ancestorId]) {
subtree = subtree.children[ancestorId].tree;
}
});
subtree.children[datapoint.id] = datapoint;
subtree.children_list.push(datapoint);
}
});
return tree;
};
export default DatapointTree;
touchstart event
class PopoverContainer extends React.Component {
constructor(props) {
super(props);
this.dropdownRef = this.dropdownRef.bind(this);
this.handleClose = this.handleClose.bind(this);
this.listeners = ['click', 'touchstart'];
}
componentDidMount() {
this.listeners.forEach((eventType) => {
document.addEventListener(eventType, this.handleClose, false);
});
}
componentWillUnmount() {
this.listeners.forEach((eventType) => {
document.removeEventListener(eventType, this.handleClose, false);
});
}
handleClose(e) {
if (this.props.settingsDropdown && this.dropdown && !this.dropdown.contains(e.target)) {
this.props.close();
}
}
dropdownRef(ref) {
this.dropdown = ref;
}
render() {
return <Popover dropdownRef = { this.dropdownRef } { ...this.props } />
}
}
Event Tracking
We use Google Tag Manager to manage our third-party integrations and trackers. To get started you should create a new Google Tag Manager account and ask for access to "Stash Invest" and "app.stashinvest.com".
Events are fired in our redux middleware in middleware/events.js
. Make sure to publish your tag!!!
Triggers
In the Tag Manager UI, you can create a trigger to listen for events which you can fire with this code snippet.
const eventName = 'Transaction';
const eventProperties = {
origin: window.location.pathname
};
window.dataLayer.push(eventName, eventProperties);
Tags
After you create a trigger, you can create a Tag. Choose Custom HTML and insert a code snippet.
Mixpanel Example
<script>
window.mixpanel && window.mixpanel.track('Transaction', {{DataLayer properties}})
</script>
Marketing Pixel Example
<script>
(function() {
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
var uuid = uuidv4();
var iframe = document.createElement("iframe");
iframe.style.visibility = 'hidden';
iframe.style.position = 'absolute';
iframe.style.left = '-9000px';
iframe.src = "https://example.com" + uuid;
iframe.width = "1";
iframe.height = "1";
document.body.appendChild(iframe);
})()
</script>
Google Pixel
<script>
/* <![CDATA[ */
var google_conversion_id = 0;
var google_conversion_label = "";
var google_remarketing_only = false;
/* ]]> */
(function() {
var properties = {{DataLayer properties}};
if (properties && properties.Action === 'On') {
var s = document.createElement('script');
s.async = true;
s.src = '//www.googleadservices.com/pagead/conversion.js';
var h = document.getElementsByTagName('head')[0];
h.appendChild(s);
}
})();
</script>
Dynamic Script Example
<script>
(function() {
var s = document.createElement('script');
s.async = true;
s.src = '//cdn.nanigans.com/NaN_tracker.js';
var h = document.getElementsByTagName('head')[0];
h.appendChild(s);
})();
</script>
Publish
Click Publish and test your code locally. GTM allows you to push your changes to two "environments":
- Live will push changes to production (app.stashinvest.com)
- Latest will push changes to all other environments (Local, Edge and Staging)
Tips
⚠️ Watch out for quotes. Make sure to use actual quotes instead of MS Word quotes.
Utils
const isMobileThunk = () => {
let cachedValue;
return () => {
if (cachedValue === undefined) {
let check = false;
(function(a) {
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0, 4))) check = true;
})(navigator.userAgent || navigator.vendor || window.opera);
cachedValue = check;
}
return cachedValue;
};
}
export const isMobile = isMobileThunk();
export const isMacintosh = () => {
return navigator.platform.indexOf('Mac') > -1
}
export const isWindows = () => {
return navigator.platform.indexOf('Win') > -1
}
export const serialize = (queryParams) => {
const params = [];
for (let param in queryParams) {
if (queryParams.hasOwnProperty(param)) {
params.push(encodeURIComponent(param) + "=" + encodeURIComponent(queryParams[param]));
}
}
return params.join("&");
}
export const pathOr = (defaultVal, paths, obj) => {
let val = obj;
let index = 0;
while (index < paths.length) {
if (val == null) {
return defaultVal;
}
val = val[paths[index]]
index++;
}
return val == null ? defaultVal : val;
}
Code highlights
const GLTR_ID = tickers['GLTR'] && tickers['GLTR'].card_id;
const VNQ_ID = tickers['VNQ'] && tickers['VNQ'].card_id;
const hasInvestmentInAnyAccount = (cardIds) => {
return Object.values(accounts).some((account) => {
return account.cards && cardIds.some((cardId) => {
return account.cards[cardId];
});
})
}
hasInvestmentInAnyAccount([GLTR_ID, VNQ_ID])
Fetch redux thunk helper
middleware/request.js
export const encodeQuery = (query) => {
return Object.keys(query).reduce((a, key) => {
a.push(key + '=' + encodeURIComponent(query[key]));
return a;
}, []).join('&');
}
export const httpOptions = {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
export const request = async (dispatch, getState, config) => {
const {actions, path, query, options} = config;
// Parse query string options and add to url.
const url = query ? '?' + encodeQuery(query) : path;
// Stringify request body.
options.body = options.body && JSON.stringify(options.body);
const fetchOptions = {
...httpOptions,
...options,
}
actions.request && dispatch({
type: actions.request
});
const res = await fetch(url, fetchOptions);
if (res.status >= 400) {
const json = await res.json();
actions.error && dispatch({
type: actions.error,
status: res.status,
body: json,
})
}
const contentType = res.headers.get('Content-Type');
if (contentType.indexOf('application/json') > -1) {
const json = await res.json();
actions.success && dispatch({
type: actions.success,
status: res.status,
body: json
});
}
if (contentType.indexOf('text/html') > -1) {
await res.text();
actions.success && dispatch({
type: actions.success,
status: res.status,
});
}
};
export default request;
middleware/index.js
/**
* Read more about middleware here:
* http://redux.js.org/docs/advanced/Middleware.html
*/
import { applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from './thunk';
import request from './request';
const middleware = [
thunk.withExtraArgument(request),
];
if (process.env.NODE_ENV === 'development') {
const { logger } = require('redux-logger');
middleware.push(logger);
}
export default composeWithDevTools(applyMiddleware(...middleware));
export const PATCH_EXPERIMENT_REQUEST = 'PATCH_EXPERIMENT_REQUEST';
export const PATCH_EXPERIMENT_SUCCESS = 'PATCH_EXPERIMENT_SUCCESS';
export const PATCH_EXPERIMENT_ERROR = 'PATCH_EXPERIMENT_ERROR';
export const patchExperiment = ({ version, active, experiment_urls }) => {
return (dispatch, getState, request) => {
return request(dispatch, getState, {
actions: {
request: PATCH_EXPERIMENT_REQUEST,
success: PATCH_EXPERIMENT_SUCCESS,
error: PATCH_EXPERIMENT_ERROR
},
path: `/api/experiment/${version}`,
options: {
method: 'PATCH',
body: {
active,
experiment_urls
}
}
});
}
}
Tooling and Build
Compiling ES6 with Babel CLI
# Babel CLI should be installed locally rather than globally.
npm install --save-dev babel-cli
# Compile or watch a particular file.
babel public/scripts/snake.js -o public/scripts/snake-es5.js
babel public/scripts/snake.js -o public/scripts/snake-es5.js --watch
White-transparent gradient
const Wrapper = styled.div`
width: 100%;
position: relative;
@media (max-width: 768px) {
&::before {
content: '';
background: linear-gradient(to bottom, white, transparent);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50px;
z-index: 1;
margin-top: -25px;
}
}
`;
VSCode debugger with CRA tests
Sell bots
https://botbroker.io/bots
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug CRA Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
"args": ["test", "${relativeFile}", "--no-cache", "--modulePaths=src"],
"env": { "CI": "true" },
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
Mocking modules
jest.mock('stash/utils/experiments.js', function() {
return {
startExperiment: jest.fn(res => {
return 0;
}),
};
});
import * as utils from 'stash/utils';
utils.isMobile = jest.fn().mockReturnValue(true);
Opening chrome with CORS disabled
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
Mongo DB data migration
Binary
# Import database
mongorestore -h ds141168.mlab.com:41168 -d heroku_nl79h610 -u <user> -p <password> <input db directory>
# Export database
mongodump -h ds141168.mlab.com:41168 -d heroku_nl79h610 -u <user> -p <password> -o <output directory>
# Import collection
mongorestore -h ds141168.mlab.com:41168 -d heroku_nl79h610 -u <user> -p <password> <input .bson file>
# Export collection
mongodump -h ds141168.mlab.com:41168 -d heroku_nl79h610 -c <collection> -u <user> -p <password> -o <output directory>
JSON
# Import collection
mongoimport -h ds141168.mlab.com:41168 -d heroku_nl79h610 -c <collection> -u <user> -p <password> --file <input file>
# Export collection
mongoexport -h ds141168.mlab.com:41168 -d heroku_nl79h610 -c <collection> -u <user> -p <password> -o <output file>
CSV
# Import collection
mongoimport -h ds141168.mlab.com:41168 -d heroku_nl79h610 -c <collection> -u <user> -p <password> --file <input .csv file> --type csv --headerline
# Export collection
mongoexport -h ds141168.mlab.com:41168 -d heroku_nl79h610 -c <collection> -u <user> -p <password> -o <output .csv file> --csv -f <comma-separated list of field names>
Links and Resources
Introduction to File API and Drag and Drop
https://www.html5rocks.com/en/tutorials/file/dndfiles/
JavaScript setTimeout 0 for offloading heavy computation
https://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
JavaScript fetch API with await, streams
https://jakearchibald.com/2015/thats-so-fetch/
Div that looks different on every browser
https://codepen.io/MartijnCuppens/pen/MXojmw
Fix htst certificate security warning by adding to Delete domain security policies
chrome://net-internals/#hsts