Skip to content

Instantly share code, notes, and snippets.

@spiralx
Last active April 7, 2017 07:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spiralx/bd2a0d4a1afbb6449074 to your computer and use it in GitHub Desktop.
Save spiralx/bd2a0d4a1afbb6449074 to your computer and use it in GitHub Desktop.
Collected DevTool snippets
// 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")
})()
/* 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
})
})()
(() => {
'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)
})()
/* 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
}
})
})()
/**
* 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())
}
})()
/* 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()
})()
/* 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)
}
})
})
})()
(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)
}
}
})()
(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)
/* 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())
})()
'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>
(() => {
'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())
})()
(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)
/*
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())
})()
(() => {
'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
(() => {
'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)
}
})()
(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