Last active
April 7, 2017 07:58
-
-
Save spiralx/bd2a0d4a1afbb6449074 to your computer and use it in GitHub Desktop.
Collected DevTool snippets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// allcolors.js | |
// https://github.com/bgrins/devtools-snippets | |
// Print out CSS colors used in elements on the page. | |
(function() { | |
'use strict' | |
const BOLD = 'font-weight: bold;' | |
const LINK = 'text-decoration: underline; color: #05f' | |
const NORMAL = 'font-weight: normal; text-decoration: none; color: black; background-color: white; display: inline' | |
// -------------------------------------------------------------------------- | |
// Should include colors from elements that have a border color but have a zero width? | |
const INCLUDE_ZERO_WIDTH_COLOURS = true | |
const dircols = prefix => 'top right bottom left'.split(' ').map(d => `${prefix}-${d}-color`) | |
const COLOUR_PROPERTIES = [ | |
'color', | |
'background-color', | |
'-moz-column-rule-color', | |
'-webkit-column-rule-color', | |
'column-rule-color', | |
'outline-color', | |
'text-decoration-color', | |
'-webkit-text-emphasis-color', | |
'text-emphasis-color' | |
] | |
.concat(dircols('border')) | |
.concat(dircols('border-block')) | |
.concat(dircols('border-inline')) | |
const skipColors = new Set([ | |
"rgb(0, 0, 0)", | |
"rgba(0, 0, 0, 0)", | |
"rgb(255, 255, 255)" | |
]) | |
// -------------------------------------------------------------------------- | |
console.clear() | |
let allColors = new Map(); | |
[ ...document.querySelectorAll('*') ].forEach(node => { | |
const nodeColors = new Set() | |
const computedStyle = window.getComputedStyle(node, null) | |
// console.groupCollapsed(node) | |
// console.info(node, computedStyle) | |
COLOUR_PROPERTIES.forEach(prop => { | |
const color = computedStyle.getPropertyValue(prop) | |
// console.log(prop, color) | |
if (!color || skipColors.has(color)) { | |
return | |
} | |
const notBorderZero = prop.includes('border') | |
? computedStyle.getPropertyValue(prop.replace("color", "width")) !== "0px" | |
: true | |
const colorConditionsMet = INCLUDE_ZERO_WIDTH_COLOURS || notBorderZero | |
if (colorConditionsMet && !nodeColors.has(color)) { | |
if (!allColors.has(color)) { | |
allColors.set(color, { | |
count: 0, | |
nodes: [] | |
}) | |
} | |
const info = allColors.get(color) | |
info.count++ | |
info.nodes.push(node) | |
nodeColors.add(color) | |
} | |
}) | |
}) | |
// console.dir(allColors) | |
// -------------------------------------------------------------------------- | |
function rgbTextToRgbArray(rgbText) { | |
return rgbText | |
.replace(/\s/g, '') | |
.match(/(\d+),(\d+),(\d+)(?:,(\d+))/) | |
.slice(1) | |
.map(num => parseInt(num, 10)) | |
} | |
const decToHex = d => (d < 16 ? '0' : '') + (Math.round(d)).toString(16) | |
function rgbArrayToHex(rgbArray) { | |
const hex = '#' + rgbArray.map(decToHex).join('') | |
const m = rgbArray.length === 4 | |
? hex.match(/(#)([a-f\d])\2([a-f\d])\3([a-f\d])\4([a-f\d])\5/) | |
: hex.match(/(#)([a-f\d])\2([a-f\d])\3([a-f\d])\4/) | |
// console.log(rgbArray, hex) | |
return m ? m.slice(1).join('') : hex | |
} | |
function rgbToHex(rgb) { | |
if (rgb.startsWith('rgba')) { | |
const m = rgb.replace(/\s/g, '') | |
.match(/(\d+),(\d+),(\d+),(\d+\.\d+)/) | |
.slice(1) | |
.map(v => parseFloat(v)) | |
m[3] = m[3] * 255 | |
return rgbArrayToHex(m) | |
} else { | |
return rgbArrayToHex(rgb | |
.replace(/\s/g, '') | |
.match(/(\d+),(\d+),(\d+)/) | |
.slice(1) | |
.map(v => parseInt(v))) | |
} | |
} | |
// -------------------------------------------------------------------------- | |
let allColorsSorted = [] | |
for (const color of allColors.keys()) { | |
// const rgbArray = rgbTextToRgbArray(color) | |
const hexValue = rgbToHex(color) | |
// console.info(color, hexValue) | |
allColorsSorted.push(Object.assign({ | |
color, | |
hexValue | |
}, allColors.get(color))) | |
} | |
allColorsSorted = allColorsSorted.sort((a, b) => b.count - a.count || a.hexValue.localeCompare(b.hexValue)) | |
// -------------------------------------------------------------------------- | |
const lpad = (s, w) => (s.length < w ? ' '.repeat(w - s.length) : '') + s | |
const rpad = (s, w) => s + (s.length < w ? ' '.repeat(w - s.length) : '') | |
function colorStyle(color) { | |
return `background: ${color}; color: ${color}; border: 1px solid #333;` | |
} | |
console.clear() | |
console.group(`${allColorsSorted.length} colors are used in elements on the page: %c${location.href}%c`, LINK, NORMAL) | |
const nw = Math.trunc(Math.log10(allColorsSorted[0].count)) + 2 | |
allColorsSorted.forEach(c => { | |
console.groupCollapsed(`%c %c %c${lpad('×' + c.count, nw)}%c ${rpad(c.hexValue, 11)} ${c.color}`, | |
colorStyle(c.color), NORMAL, BOLD, NORMAL) | |
c.nodes.forEach(n => console.log(n)) | |
console.groupEnd() | |
}) | |
console.groupEnd("All colors used in elements on the page") | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
const rootElement = angular.element(document) | |
const mockApp = angular.module('mockApp', []).provider({ | |
$rootElement: function() { | |
this.$get = function() { | |
return rootElement | |
} | |
} | |
}) | |
function getInjector (...modules) { | |
return angular.injector([ 'ng', 'mockApp', ...modules ]) | |
} | |
const rootInjector = getInjector('seedlegals') | |
console.dir(rootInjector) | |
Object.assign(window, { | |
rootInjector, | |
getInjector | |
}) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
'use strict' | |
/** | |
* 2017-02-17 Added floating close box, fixed cursor | |
*/ | |
// -------------------------------------------------------------------------- | |
const ACTIVE_CSS = ` | |
body { | |
cursor: crosshair !important; | |
} | |
.__close { | |
display: inline-block; | |
position: fixed; | |
top: 4em; | |
right: 4em; | |
z-index: 10001; | |
background: black; | |
color: #aaa; | |
font-size: 16pt; | |
padding: 0.3em 0.6em; | |
border-radius: 0.3em; | |
box-shadow: 0.1em 0.2em 0.3em rgba(0, 0, 0, 0.54); | |
} | |
.__close:hover { | |
color: yellow; | |
cursor: pointer; | |
} | |
` | |
// -------------------------------------------------------------------------- | |
// Given a scope, return an array consisting of itself and all its parent scopes | |
function getScopeChain (scope) { | |
if (!(scope && scope.$id && scope.$parent)) { | |
throw new Error(`Invalid scope!`, scope) | |
} | |
const res = [ scope ] | |
while (scope = scope.$parent) { | |
res.push(scope) | |
} | |
return res | |
} | |
// -------------------------------------------------------------------------- | |
// Return scope data without internal Angular variables | |
function getScopeData (scope) { | |
return Object.keys(scope) | |
.filter(k => !k.match(/^\$(parent|root|id|emit|broadcast|resolve|\$.+)/)) | |
.reduce((out, k) => Object.assign(out, { [k]: scope[k] }), {}) | |
} | |
// -------------------------------------------------------------------------- | |
// Merge one or more scopes into a single object | |
function mergeScopes(...scopes) { | |
return scopes.reduce((out, scope) => Object.assign(out, getScopeData(scope), {})) | |
} | |
// -------------------------------------------------------------------------- | |
function watchFormat (w) { | |
if (typeof w === 'string') { | |
return w | |
} | |
return w.toString() | |
// .replace(/^.+(\([^)]+\))/, '$1 =>') | |
.split(/\n/g) | |
.map(l => l.replace(/^\s+(?=\}| [^\s\}])/, '')) | |
.join('\n') | |
} | |
// -------------------------------------------------------------------------- | |
function getWatchers (scope) { | |
const kf = s => [ | |
s.startsWith('function') ? 1 : 0, | |
s.replace(/^(?:function\s*|\$|!)/, '') | |
] | |
const watch_set = getScopeChain($scope) | |
.reduce((out, scope) => new Set([...out, ...(scope.$$watchers || []).map(w => watchFormat(w.exp))]), new Set()) | |
return [...watch_set] | |
.sort((a,b) => { | |
const ak = kf(a), bk = kf(b) | |
return bk[0] - ak[0] || ak[1].localeCompare(bk[1]) | |
}) | |
} | |
// -------------------------------------------------------------------------- | |
function deRestangularize (scope) { | |
const new_scope = {} | |
for (let k in scope) { | |
new_scope[k] = scope[k] && scope[k].restangularized | |
? scope[k].plain() | |
: scope[k] | |
} | |
return new_scope | |
} | |
// -------------------------------------------------------------------------- | |
function gd(name, obj) { | |
console.group(name) | |
console.dir(obj) | |
console.groupEnd() | |
} | |
function writeInfo(elem) { | |
const $el = angular.element(elem) | |
const $ctrl = $el.controller() | |
const $scope = $el.scope() | |
const $scopes = getScopeChain($scope).map(getScopeData) | |
const $all = $scopes | |
.map(deRestangularize) | |
.reduce((out, scope) => Object.assign(out, scope), {}) | |
Object.assign(window, { | |
$el, | |
$scope, | |
$scopes, | |
$all, | |
$ctrl | |
}) | |
// console.clear() | |
console.group(elem) | |
console.info($ctrl) | |
gd('$scope', $scope) | |
gd('$scopes', $scopes) | |
gd('$all', $all) | |
gd('watches', getWatchers($scope)) | |
gd('state', $all.state.current) | |
console.groupEnd() | |
} | |
// -------------------------------------------------------------------------- | |
function mouseOver (event) { | |
event.target.classList.add('__outlined') | |
} | |
function mouseOut (event) { | |
event.target.classList.remove('__outlined') | |
} | |
// -------------------------------------------------------------------------- | |
function cleanUp () { | |
document.removeEventListener('click', documentClickHandler, true) | |
closeElem.removeEventListener('click', closeClickHandler, true) | |
document.body.removeChild(styleElem) | |
document.body.removeChild(closeElem) | |
} | |
// -------------------------------------------------------------------------- | |
function documentClickHandler (event) { | |
event.stopPropagation() | |
event.preventDefault() | |
writeInfo(event.target) | |
} | |
// -------------------------------------------------------------------------- | |
function closeClickHandler (event) { | |
event.stopPropagation() | |
event.preventDefault() | |
cleanUp() | |
} | |
// -------------------------------------------------------------------------- | |
function el(tag, attrs, content) { | |
const elem = Object.assign(document.createElement(tag), attrs) | |
elem.textContent = content | |
document.body.appendChild(elem) | |
return elem | |
} | |
// -------------------------------------------------------------------------- | |
const styleElem = el('style', { type: 'text/css' }, ACTIVE_CSS) | |
const closeElem = el('div', { className: '__close'}, `Close ×`) | |
closeElem.addEventListener('click', closeClickHandler, true) | |
document.addEventListener('click', documentClickHandler) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
Object.assign(window, { | |
// Get a list of all Angular-wrapped elements that have a scope defined. | |
getScopeElements() { | |
return [...document.querySelectorAll('.ng-scope')].map(angular.element) | |
}, | |
// Return a list of objects that contain a scope, the element | |
// it's defined on, and whether it is an isolated scope or not. | |
getScopes() { | |
return getScopeElements().map($elem => { | |
return { | |
$elem, | |
scope: $elem.scope() || $elem.isolateScope(), | |
isIsolated: !!$elem.isolateScope() | |
} | |
}) | |
}, | |
// Given a numeric id, try and fine the scope with sam internal $id. | |
getScopeById(id) { | |
return getScopes().find(item => item.scope.$id === id) | |
}, | |
// Given a scope object or ID, return an array of that scope and | |
// all of its parent scopes up to the root scope. | |
getScopeChain(scope) { | |
if (typeof scope === 'string') { | |
scope = getScopeById(scope).scope | |
if (!scope) { | |
throw new Error(`No scope found!`) | |
} | |
} | |
const res = [ scope ] | |
while (scope = scope.$parent) { | |
res.push(scope) | |
} | |
return res | |
} | |
}) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Based on console.image created by Adrian Cooney: | |
* | |
* http://dunxrion.github.io | |
*/ | |
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
/** | |
* Since the console.log doesn't respond to the `display` style, | |
* setting a width and height has no effect. In fact, the only styles | |
* I've found it responds to is font-size, background-image and color. | |
* To combat the image repeating, we have to get a create a font bounding | |
* box so to speak with the unicode box characters. EDIT: See Readme.md | |
* | |
* @param {int} width The height of the box | |
* @param {int} height The width of the box | |
* @return {object} {string, css} | |
*/ | |
function getBox (width, height) { | |
const pv = Math.floor(height / 2) | |
const ph = Math.floor(width / 2) | |
return { | |
string: "+", | |
style: `font-size: 1px; padding: ${pv}px ${ph}px; line-height: ${height}px;` | |
} | |
} | |
// ---------------------------------------------------------- | |
/** | |
* Display an image in the console. | |
* | |
* @param {string} url The url of the image. | |
* @param {int} scale Scale factor on the image | |
*/ | |
console.image = function (src, scale = 1) { | |
const img = Object.assign(new Image(), { | |
onload() { | |
const w = this.width * scale | |
const h = this.height * scale | |
const { string, style } = getBox(w, h) | |
console.log(`%c` + string, style + `background: url(${src}); background-size: ${w}px ${h}px; color: transparent;`) | |
}, | |
// crossOrigin: 'anonymous', | |
src | |
}) | |
} | |
// ---------------------------------------------------------- | |
console.draw = function (canvas) { | |
console.image(canvas.toDataURL()) | |
} | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
const BOLD = 'font-weight: bold;' | |
const LINK = 'text-decoration: underline; color: #05f' | |
const NAME = 'color: #8a2be2;' | |
const VALUE = 'color: #536872;' | |
const NORMAL = 'font-weight: normal; text-decoration: none; color: black; background-color: white; display: inline' | |
// -------------------------------------------------------------------------- | |
class ConsoleItem { | |
constructor(...args) { | |
this.args = args | |
} | |
display() { | |
console.log(...args) | |
} | |
} | |
class ConsoleGroup { | |
constructor(title, ...items) { | |
this.title = Array.isArray(title) ? title : [ title ] | |
this.items = items | |
} | |
add(...args) { | |
if (args.length === 1 && args[0] instanceof ConsoleGroup) { | |
this.items.push(args[0]) | |
} else { | |
this.items.push(args) | |
} | |
} | |
_start() { | |
console.group(...this.title) | |
} | |
display() { | |
this._start() | |
for (const i of this.items) { | |
if (i instanceof ConsoleGroup) { | |
i.display() | |
} else if (Array.isArray(i)) { | |
console.log(...i) | |
} else { | |
console.log(i) | |
} | |
} | |
console.groupEnd() | |
} | |
} | |
class ConsoleGroupCollapsed extends ConsoleGroup { | |
_start() { | |
console.groupCollapsed(...this.title) | |
} | |
} | |
// -------------------------------------------------------------------------- | |
const g = new ConsoleGroup( | |
[`%cThing%c (blah)`, BOLD, NORMAL], | |
[`Moo = %cCOW%c`, VALUE, NORMAL], | |
new ConsoleGroupCollapsed('Extras', 'An item', [`%cgoogle.com%c`, LINK, NORMAL]), | |
'End' | |
) | |
g.display() | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
const BOLD = 'font-weight: bold;', | |
LINK = 'text-decoration: underline; color: #03d', | |
RESET = 'font-weight: normal; text-decoration: none; color: black; background-color: white; display: inline' | |
// -------------------------------------------------------------------------- | |
const findElement = (tag, attr = {}) => !!document.querySelector(tag + Object.keys(attr).map(k => `[${k}="${attr[k]}"]`).join('')) | |
// ---------------------------------------------------------- | |
const EL = (tag, attr = {}) => new Promise((onload, onerror) => { | |
const e = Object.assign(document.createElement(tag), attr, { onload, onerror }) | |
document.body.appendChild(e) | |
}) | |
// ---------------------------------------------------------- | |
console.inject = (library, force) => new Promise((resolve, reject) => { | |
if (!force || findElement('script', { 'data-query': library })) { | |
console.info(`Library %c"${library}"%c already injected`, BOLD, RESET) | |
resolve(library) | |
} | |
// https://api.cdnjs.com/libraries?search=url&fields=name,description,version.,author,homepage,license,repository,keywords,autoupdate,latest,assets | |
const url = `https://api.cdnjs.com/libraries?search=${library}` | |
fetch(url) | |
.then(response => response.ok && response.json()) | |
.then(json => { | |
const { total, results } = json | |
const lib_re = RegExp(`^${library}(\\.js)?$`, 'i') | |
const m = results.find(r => lib_re.test(r.name)) | |
if (m) { | |
const src = m.latest.replace(/^http:/, 'https:') | |
EL('script', { | |
src, | |
'data-query': library | |
}) | |
.then( | |
() => { | |
console.info(`Library %c"${library}"%c injected from %c${src}%c`, BOLD, RESET, LINK, RESET) | |
resolve(library) | |
}, | |
() => { | |
console.warn(`Library %c"${library}"%c from %c${src}%c failed on load!`, BOLD, RESET, LINK, RESET) | |
reject(library) | |
} | |
) | |
} else { | |
console.warn(`Library %c"${library}"%c not found!`, BOLD, RESET) | |
reject(library) | |
} | |
}) | |
}) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict' | |
/** | |
* Bookmarklet version! | |
* | |
* javascript:((D, v, $i) => { D.body.appendChild($i = Object.assign(D.createElement('input'), { type: 'text', style: 'position: fixed; bottom -80px;', value: v })); $i.select(); $i.focus(); try { D.execCommand('copy') } catch (e) { console.warn(e) } finally { D.body.removeChild($i) } })(document, localStorage.getItem('id_token')) | |
* | |
*/ | |
// -------------------------------------------------------------------------- | |
window.copyToClipboard = function copyToClipboard (value='') { | |
const $input = Object.assign(document.createElement('textarea'), { | |
// type: 'text', | |
style: 'position: fixed; bottom: -80px; width: 80%; height: 800px;', | |
value | |
}) | |
document.body.appendChild($input) | |
$input.select() | |
$input.focus() | |
try { | |
document.execCommand('copy') | |
$input.blur() | |
console.info(`${value.length} characters copied to the clipboard!`) | |
} catch (ex) { | |
console.warn(ex) | |
} finally { | |
document.body.removeChild($input) | |
} | |
} | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
'use strict'; | |
const BOLD = 'font-weight: bold;', | |
BLUE = 'color: #88f', | |
NORMAL = 'font-weight: normal; color: black;' | |
/* | |
See: | |
* https://gist.github.com/NV/5376464 | |
* http://jsfiddle.net/4RGfa/ | |
* https://gist.github.com/johan/5436827 | |
*/ | |
// ---------------------------------------------------------- | |
Object.assign(global, { | |
breakBefore(object, name) { | |
if (typeof object === 'string') { | |
name = object | |
object = typeof window[name] === 'function' ? window : typeof document[name] === 'function' ? document : null | |
} | |
let originalMethod = object[name] | |
object[name] = function(...args) { | |
debugger | |
return originalMethod.apply(this, args) | |
} | |
object[name].removeBreak = () => { | |
object[name] = originalMethod | |
} | |
}, | |
breakAfter(object, name) { | |
if (typeof object === 'string') { | |
name = object | |
object = typeof window[name] === 'function' ? window : typeof document[name] === 'function' ? document : null | |
} | |
let originalMethod = object[name] | |
object[name] = function(...args) { | |
let result = originalMethod.apply(this, args) | |
debugger | |
return result | |
} | |
object[name].removeBreak = () => { | |
object[name] = originalMethod | |
} | |
}, | |
/* | |
See https://gist.github.com/dmethvin/1676346 | |
*/ | |
debugAccess(obj, prop, debugGet) { | |
let originalValue = obj[prop] | |
Object.defineProperty(obj, prop, { | |
get() { | |
if (debugGet) { | |
debugger | |
} | |
return originalValue | |
}, | |
set(val) { | |
debugger | |
return originalValue = val | |
} | |
}) | |
}, | |
traceMethodCalls(obj) { | |
return new Proxy(obj, { | |
get(target, propKey, receiver) { | |
const origMethod = target[propKey] | |
return function(...args) { | |
const result = origMethod.apply(this, args) | |
console.log(`%c${propKey}%c${JSON.stringify(args)} -> ${JSON.stringify(result)}`, BLUE, NORMAL) | |
return result | |
} | |
} | |
}) | |
}, | |
tracePropertyAccess(obj, propKeys) { | |
const propKeySet = new Set(propKeys) | |
return new Proxy(obj, { | |
get(target, propKey, receiver) { | |
if (propKeySet.has(propKey)) { | |
console.log(`%cGET%c ${propKey}`, BOLD, NORMAL) | |
} | |
return Reflect.get(target, propKey, receiver) | |
}, | |
set(target, propKey, value, receiver) { | |
if (propKeySet.has(propKey)) { | |
console.log(`%cSET%c ${propKey} = %c${value}%c`, BOLD, NORMAL, BLUE, NORMAL) | |
} | |
return Reflect.set(target, propKey, value, receiver) | |
}, | |
}) | |
}, | |
}) | |
})(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint asi: true, esnext: true */ | |
(() => { | |
'use strict' | |
// -------------------------------------------------------------------------- | |
class DefaultMap extends Map { | |
get(key) { | |
return super.has(key) | |
? super.get(key) | |
: this.getDefault(key) | |
} | |
getDefault(key) {} | |
} | |
// -------------------------------------------------------------------------- | |
class Counter extends DefaultMap { | |
getDefault(key) { | |
return 0 | |
} | |
sortedItems() { | |
return Array.from(this.entries()) | |
.sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0])) | |
} | |
} | |
// -------------------------------------------------------------------------- | |
class Collector extends DefaultMap { | |
getDefault(key) { | |
return [] | |
} | |
count(key) { | |
return this.get(key).length | |
} | |
} | |
// -------------------------------------------------------------------------- | |
const c = new Counter() | |
Array.from(document.querySelectorAll('*')).forEach(elem => { | |
[...elem.classList].forEach(cls => { | |
c.set(cls, c.get(cls) + 1) | |
}) | |
}) | |
console.table(c.sortedItems()) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// ---------------------------------------------------------------------------- | |
// UMD wrapper to support CommonJS and AMD as well as the browser | |
(function(root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
define(factory) | |
} | |
else if (typeof exports === 'object') { | |
module.exports = factory() | |
} | |
else { | |
root.EL = factory() | |
} | |
})(this, function factory() { | |
/** | |
* Function to generated nested HTML elements. | |
* | |
* @param {string} defn - TAG_NAME[#ID]?[.CLASS_NAME]*[#ID]? - create element with id/classes | |
* @param {string|Object} [attributes] - if a string, set textContent, otherwise apply all attributes | |
* @param {Array<Array|HTMLElement>} [children] - add children as nodes or the result of another EL | |
* @return {HTMLElement} - constructed element | |
*/ | |
function EL(defn, attributes, ...children) { | |
const m = defn.split(/\b(?=[\.#])/g), | |
element = document.createElement(m.shift()) | |
m.forEach(v => { | |
if (v[0] === '.') { | |
element.classList.add(v.substr(1)) | |
} | |
else if (v[0] === '#') { | |
element.id = v.substr(1) | |
} | |
}) | |
if (Array.isArray(attributes) || attributes instanceof HTMLElement) { | |
children.unshift(attributes) | |
attributes = {} | |
} | |
else if (typeof attributes === 'string') { | |
attributes = { textContent: attributes } | |
} | |
Object.assign(element, attributes || {}) | |
for (const child of children) { | |
element.appendChild(child instanceof HTMLElement ? child : EL(...child)) | |
} | |
return element | |
} | |
return EL | |
}) | |
// ---------------------------------------------------------------------------- | |
// Examples | |
// let panel = EL('div.panel', ['div.panel-header', ['a', { href: '/' }, ['h1#title', 'TITLE']]]) | |
// console.info(panel.outerHTML) | |
// <div class="panel"><div class="panel-header"><a href="/"><h1 id="title">TITLE</h1></a></div></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
'use strict' | |
const BOLD = 'font-weight: bold;' | |
const LINK = 'text-decoration: underline; color: #05f' | |
const NAME = 'color: #8a2be2;' | |
const VALUE = 'color: #536872;' | |
const NORMAL = 'font-weight: normal; text-decoration: none; color: black; background-color: white; display: inline' | |
// -------------------------------------------------------------------------- | |
function getStyle(rule) { | |
const props = {} | |
if (rule.style) { | |
for (const propName of rule.style) { | |
props[propName] = rule.style[propName] | |
} | |
} else { | |
console.warn(rule) | |
} | |
return props | |
} | |
// -------------------------------------------------------------------------- | |
function getAllRules() { | |
const allRules = new Map() | |
for (const styleSheet of document.styleSheets) { | |
const rules = styleSheet.cssRules || styleSheet.rules | |
if (!rules) { | |
// Happens for included stylesheets | |
continue | |
} | |
for (const rule of styleSheet.cssRules) { | |
if (rule instanceof CSSMediaRule) { | |
} else if (rule instanceof CSSKeyframesRule) { | |
} else if (rule instanceof CSSSupportsRule) { | |
} else { | |
const selector = rule.selectorText || '' | |
let style = getStyle(rule) | |
if (allRules.has(selector)) { | |
style = Object.assign(allRules.get(selector), style) | |
} | |
allRules.set(selector, style) | |
} | |
} | |
} | |
return allRules | |
} | |
// -------------------------------------------------------------------------- | |
function displayRules(rules) { | |
console.clear() | |
const sorted = [...rules.entries()].sort((a, b) => a[0].localeCompare(b[0])) | |
for (const [ selector, style ] of sorted) { | |
//const style = allRules.get(selector) | |
const propertyNames = Object.keys(style).sort() | |
console.groupCollapsed(`%c${selector}%c (${propertyNames.length} properties)`, BOLD, NORMAL) | |
for (const name of propertyNames) { | |
console.log(`%c${name}%c: %c${style[name]}%c`, NAME, NORMAL, VALUE, NORMAL) | |
} | |
console.groupEnd() | |
} | |
} | |
// -------------------------------------------------------------------------- | |
displayRules(getAllRules()) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
'use strict' | |
Object.assign(global, { | |
isValue: v => v != null, | |
getType(obj) { | |
if (!isValue(obj)) { | |
return String(obj) | |
} | |
return Object.prototype.toString.call(obj).match(/\[object (\w+)\]/)[1].toLowerCase() | |
}, | |
isObject: v => getType(v) === 'object', | |
isFunction: v => getType(v) === 'function', | |
getMatcher(options, root) { | |
if (isFunction(options)) { | |
return options | |
} | |
let selector = '.', | |
depth = false | |
if (isObject(options)) { | |
selector = options.selector || selector | |
depth = options.depth || depth | |
} | |
else { | |
selector = options | |
} | |
root = root || options.root || document.documentElement | |
if (selector === '.') { | |
return elem => elem === root | |
} | |
// console.info(`selector: ${selector}, depth: ${depth}, root:`, root) | |
return elem => { | |
if (!elem.matches(selector)) { | |
return false | |
} | |
return depth === false || distance(elem, root) <= depth | |
} | |
}, | |
resolveObject(source, path) { | |
if (typeof source === 'string') { | |
path = source | |
source = global | |
} | |
path.split('.').reduce((curobj, name) => curobj && curobj[name], source) | |
}, | |
parents(elem, selector) { | |
let cur = elem, | |
match = getType(selector) === 'string' ? e => e.matches(selector) : e => e === selector, | |
res = [] | |
while (cur = cur && cur.parentElement) { | |
res.push(cur) | |
if (selector && match(cur)) { | |
break | |
} | |
} | |
return res | |
}, | |
distance(elem, selector) { | |
return parents(elem, selector).length | |
}, | |
sorted(items, key, reverse) { | |
const key_func = typeof key === 'string' ? o => o[key] : key, | |
revfac = reverse ? -1 : 1 | |
return Array.from(items).sort((a, b) => { | |
const ak = key_func(a), | |
bk = key_func(b) | |
return revfac * (typeof ak === 'string' ? ak.localeCompare(bk) : ak - bk) | |
}) | |
} | |
}) | |
})(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
log-globals | |
Based on code by Sindre Sorhus (https://github.com/sindresorhus/log-globals) | |
MIT License | |
*/ | |
(() => { | |
'use strict' | |
function getIframe() { | |
const el = document.createElement('iframe') | |
el.style.display = 'none' | |
document.body.appendChild(el) | |
const win = el.contentWindow | |
document.body.removeChild(el) | |
return win | |
} | |
// -------------------------------------------------------------------------- | |
function detectGlobals() { | |
const iframe = getIframe() | |
const ret = Object.create(null) | |
for (const prop in window) { | |
if (!(prop in iframe)) { | |
ret[prop] = window[prop] | |
} | |
} | |
return ret | |
} | |
// -------------------------------------------------------------------------- | |
console.dir(detectGlobals()) | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
'use strict' | |
Object.assign(RegExp.prototype, { | |
findAll(s) { | |
const rr = new RegExp(this, 'g') | |
const result = [] | |
let m | |
while (m = rr.exec(s)) { | |
result.push(m.length === 1 ? m[0] : m.slice(1)) | |
} | |
return result | |
} | |
}) | |
})() | |
on |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
'use strict' | |
console.save = function save (data, filename, data_type) { | |
if (!data) { | |
console.error('Console.save: No data') | |
return | |
} | |
if (data instanceof Document) { | |
data = data.documentElement.outerHTML | |
data_type = 'text/html' | |
} else if (data instanceof Element) { | |
data = data.outerHTML | |
data_type = 'text/html' | |
} else if (typeof data === "object") { | |
data = JSON.stringify(data, undefined, 2) | |
data_type = data_type || 'text/json' | |
} else { | |
data = String(data) | |
} | |
filename = filename || 'console.json' | |
data_type = data_type || 'text/plain' | |
const blob = new Blob([ data ], { type: data_type }) | |
const href = window.URL.createObjectURL(blob) | |
const event = document.createEvent('MouseEvents') | |
const $a = Object.assign(document.createElement('a'), { | |
download: filename, | |
href | |
}) | |
// $a.dataset.downloadurl = [ data_type, filename, href ].join(':') | |
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null) | |
$a.dispatchEvent(event) | |
window.URL.revokeObjectURL(href) | |
} | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict' | |
const BOLD = 'font-weight: bold;' | |
const LINK = 'text-decoration: underline; color: #05f' | |
const NORMAL = 'font-weight: normal; text-decoration: none; color: black; background-color: white; display: inline' | |
// -------------------------------------------------------------------------- | |
function getSortFunction(criteria) { | |
console.warn(criteria) | |
if (typeof criteria === 'string') { | |
criteria = criteria.split(' ') | |
} | |
criteria = criteria.map(c => { | |
if (typeof c === 'string' && c[0] === '-') { | |
return obj => { | |
console.info(obj) | |
return typeof obj[c] === 'number' ? -obj[c] : obj[c] | |
} | |
} | |
return c | |
}) | |
console.info(criteria) | |
return items => _.sortBy(items, criteria) | |
} | |
function getKeyFunction(keyfunc) { | |
if (typeof keyfunc === 'string') { | |
return _.property(keyfunc) | |
} | |
return keyfunc | |
} | |
// -------------------------------------------------------------------------- | |
class Collection { | |
constructor(keyfunc = 'id', idfunc = 'id') { | |
this._sortfunc = getSortFunction(keyfunc) | |
this._idfunc = getKeyFunction(idfunc) | |
this._data = new Map() | |
} | |
*[Symbol.iterator]() { | |
for (const i of this.items) { | |
yield i | |
} | |
} | |
get length() { | |
return this._data.size | |
} | |
clear() { | |
this._data.clear() | |
} | |
oid(obj) { | |
return this._idfunc(obj) | |
} | |
get(id) { | |
return this._data.get(id) | |
} | |
getmany(ids) { | |
return ids.map(id => this.get(id)).filter(i => !!i) | |
} | |
get items() { | |
return this._sortfunc([...this._data.values()]) | |
} | |
sortedBy(...criteria) { | |
return getSortFunction(criteria)([...this._data.values()]) | |
} | |
has(obj) { | |
return this._data.has(this.oid(obj)) | |
} | |
set(obj) { | |
this._data.set(this.oid(obj), obj) | |
return this | |
} | |
_add(obj) { | |
if (this.has(obj)) { | |
console.error(obj, this.oid(obj)) | |
throw new TypeError(`Object already in collection`) | |
} | |
return this.set(obj) | |
} | |
add(...objs) { | |
for (const o of objs) { | |
this._add(o) | |
} | |
return this | |
} | |
replace(obj) { | |
if (!this.has(obj)) { | |
console.error(obj, this.oid(obj)) | |
throw new TypeError(`Object not in collection`) | |
} | |
return this.set(obj) | |
} | |
remove(obj) { | |
if (!this.has(obj)) { | |
console.error(obj, this.oid(obj)) | |
throw new TypeError(`Object not in collection!`) | |
} | |
this._data.delete(this.oid(obj)) | |
return this | |
} | |
dump() { | |
console.group(`Collection`) | |
console.info(this._idfunc.toString()) | |
console.info(this._sortfunc.toString()) | |
console.group(`${this.length} items`) | |
for (const i of this.items) { | |
console.log(i.toString()) | |
} | |
console.groupEnd() | |
console.groupEnd() | |
} | |
} | |
// -------------------------------------------------------------------------- | |
class Person { | |
constructor(id, firstName, lastName, seniority) { | |
this.id = id | |
this.firstName = firstName | |
this.lastName = lastName | |
this.seniority = seniority | |
} | |
toString() { | |
return `${this.firstName} ${this.lastName}(id: ${this.id}, seniority: ${this.seniority})` | |
} | |
} | |
// -------------------------------------------------------------------------- | |
const c = new Collection('firstName lastName') | |
const bob = new Person(1, 'Bob', 'Smith', 1) | |
const keith = new Person(2, 'Keith', 'Boulder', 2) | |
const aaron = new Person(3, 'Aaron', 'Clark', 1) | |
const mike = new Person(1, 'Mike', 'Hunt', 5) | |
const will = new Person(4, 'Will', 'Beaver', 1) | |
const people = window.people = [bob, keith, aaron, will] | |
function tester(coll) { | |
return (msg, fn, ...args) => { | |
console.info(msg) | |
try { | |
const res = coll[fn].apply(coll, args) | |
if (typeof res !== 'undefined') { | |
console.info(`result: %o`, res) | |
} | |
coll.dump() | |
} catch (ex) { | |
console.warn(`Error calling ${fn.name}() on %o with args %o`, coll, args) | |
console.log(ex) | |
} | |
} | |
} | |
console.clear() | |
// const tc = tester(c) | |
// tc('Adding Bob', 'add', bob) | |
// tc('Adding Keith', 'add', keith) | |
// tc('Adding Aaron', 'add', aaron) | |
// tc('Adding Aaron again', 'add', aaron) | |
// tc('Adding Mike with bad ID', 'add', mike) | |
// tc('Replacing Bob with Mike', 'replace', mike) | |
// tc('Removing Aaron', 'remove', aaron) | |
const c2 = window.c2 = new Collection('-seniority firstName lastName') | |
c2.add(mike, keith, aaron, will) | |
c2.dump() | |
const li = coll => { | |
console.info(coll.map(p => p.toString()).join('\n')) | |
} | |
console.group('Sorted by last name - array of args') | |
li(c2.sortedBy('lastName', 'firstName')) | |
console.groupEnd() | |
console.group('Sorted by last name - spaced string argument') | |
li(c2.sortedBy('lastName firstName')) | |
console.groupEnd() | |
console.group('Sorted by seniority and last name') | |
//li(c2.sortedBy('seniority lastName firstName')) | |
li(c2.sortedBy('seniority')) | |
console.groupEnd() | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment