Skip to content

Instantly share code, notes, and snippets.

@ruzz311
Last active January 13, 2020 05:55
Show Gist options
  • Save ruzz311/b2878d068758edc47677 to your computer and use it in GitHub Desktop.
Save ruzz311/b2878d068758edc47677 to your computer and use it in GitHub Desktop.
Chrome Dev Tools snippets
// most snippets found at
// https://github.com/bahmutov/code-snippets for most snippets
// https://github.com/bgrins/devtools-snippets/
(function loadAllSnippets(window) {
var keyname = 'snippets',
templates = {
bgrinsImg:'https://rawgit.com/bgrins/devtools-snippets/master/snippets/$s/$s.png',
},
remoteSnippet = {
bahmutov: createRemote('bahmutov/code-snippets/master/$s.js'),
bgrins: createRemote('bgrins/devtools-snippets/master/snippets/$s/$s.js')
};
var root = window[keyname] = {};
function format (templ, replacer) {
return templ.replace(/\$s/g, replacer);
}
function createRemote(ghUrl) {
return function downloadAndRunCodeSnippet(filename, cb) {
// form rawGit proxy url
var rawUrl = 'https://rawgit.com/' + format(ghUrl,filename);
// download and run the script
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = rawUrl;
head.appendChild(script);
if (typeof cb === 'function') {
script.loaded(cb)
}
};
};
function createImgDoc(obj, url) {
var bValue = 38;
Object.defineProperty(obj, 'example', {
get: function () {
return console.image(url, 0.5);
},
enumerable: true,
configurable: false,
writeable: false
});
}
/**
* Display an image in the console. See https://github.com/adriancooney/console.image
* @param {string} url The url of the image.
* @param {int} scale Scale factor on the image
* @return {null}
*/
console.image = function (url, scale) {
scale = scale || 1;
var img = new Image();
img.onload = function () {
var dim = getBox(this.width * scale, this.height * scale);
console.log("%c" + dim.string, dim.style + "background: url(" + url + "); background-size: " + (this.width * scale) + "px " + (this.height * scale) + "px; color: transparent;");
};
img.src = url;
};
/**
* 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.
* See https://github.com/adriancooney/console.image
*
* @param {int} width The height of the box
* @param {int} height The width of the box
* @return {object} {string, css}
*/
function getBox(width, height) {
return {
string: "+",
style: "font-size: 1px; padding: " + Math.floor(height / 2) + "px " + Math.floor(width / 2) + "px; line-height: " + height + "px;"
}
}
////////////////////////////////////
////////////////////////////////////
root.runOnInterval = function runOnInterval(duration, fn) {
duration = duration || 5000;
function TimedFunction (duration, fn) {
this.timer = null;
this.count = 0;
this.fn = fn,
this.duration = duration;
this.permin = Math.floor(60000/duration);
console.log('Running loop every '+duration+'ms (~'+this.permin+' times a minute)');
}
TimedFunction.prototype.cancel = function(){
clearTimeout(this.timer);
}
TimedFunction.prototype.loop = function loop() {
var self = this;
this.count++;
console.log('loop iteration '+this.count+' running...');
this.fn();
this.timer = setTimeout(function(){self.loop()}, this.duration);
}
var newTimed = new TimedFunction(duration || 5000, fn);
root.runOnIntervalTimers.push(newTimed);
console.log('This timer can be accessed later at window.snippets.runOnIntervalTimers['+(root.runOnIntervalTimers.length-1)+']');
newTimed.loop();
return newTimed;
};
root.runOnInterval.description = 'Run a function on an interval.';
root.runOnIntervalTimers = [];
root.runOnIntervalTimers.description = 'Holds the timers created by runOnInterval()';
////////////////////////////////////
root.cssreload = function cssreload() {
remoteSnippet.bgrins('cssreload');
};
root.cssreload.description = 'Reloads all CSS files on the page.';
createImgDoc(root.cssreload, format(templates.bgrinsImg,'cssreload'));
////////////////////////////////////
root.querystringvalues = function querystringvalues() {
remoteSnippet.bgrins('querystringvalues');
};
root.querystringvalues.description = 'Print a table of query string (GET) values.';
createImgDoc(root.querystringvalues, format(templates.bgrinsImg,'querystringvalues'));
////////////////////////////////////
root.allcolors = function allColors() {
remoteSnippet.bgrins('allcolors');
};
root.allcolors.description = 'Print out CSS colors used in elements on the page.';
createImgDoc(root.allcolors, format(templates.bgrinsImg,'allcolors'));
////////////////////////////////////
root.ngFindScopeProperty = function ngFindScopeProperty(prop) {
remoteSnippet.bahmutov('ng-find-scope-property', function afterScriptLoad() {
findScopeProperty(prop);
});
};
root.ngFindScopeProperty.description = 'finds all properties with given name attached to the scopes';
////////////////////////////////////
root.timeFirstPaint = function timeFirstPaint() {
remoteSnippet.bahmutov('first-paint');
};
root.timeFirstPaint.description = 'time from page reload to first visible contents.';
////////////////////////////////////
root.showLocalStorageSize = function showLocalStorageSize() {
remoteSnippet.bahmutov('local-storage-size');
}
root.showLocalStorageSize.description = 'measures size of the strings in the localStorage.'
////////////////////////////////////
root.countAngularWatchers = function countAngularWatchers() {
remoteSnippet.bahmutov('ng-count-watchers');
}
root.countAngularWatchers.description = 'counts total watchers in the page. More watchers - slower digest cycle.';
////////////////////////////////////
root.ngRunDigestCycle = function ngRunDigestCycle() {
remoteSnippet.bahmutov('ng-count-watchers');
}
root.ngRunDigestCycle.description = 'runs application digest cycle starting from root scope.';
////////////////////////////////////
root.ngScopeSize = function ngScopeSize() {
remoteSnippet.bahmutov('ng-count-watchers');
}
root.ngScopeSize.description = 'Finds total size of objects attached to the scopes.';
////////////////////////////////////
(function (root, factory) {
root.showAngularStats = factory();
root.showAngularStats.description = "measures size of the strings in the localStorage."
}(window[keyname], function () {
'use strict';
if (!window.angular) {
return function () {
console.log('Angular is not available');
}
}
var autoloadKey = 'showAngularStats_autoload';
var current = null;
// define the timer function to use based upon whether or not 'performance is available'
var timerNow = window.performance ? function () {
return performance.now();
} : function () {
return Date.now();
};
var lastWatchCountRun = timerNow();
var watchCountTimeout = null;
var lastWatchCount = getWatcherCount() || 0;
var lastDigestLength = 0;
var bodyEl = angular.element(document.body);
var digestIsHijacked = false;
var listeners = {
digest: [],
watchCount: [],
digestLength: []
};
// Hijack $digest to time it and update data on every digest.
function hijackDigest() {
if (digestIsHijacked) {
return;
}
digestIsHijacked = true;
var $rootScope = bodyEl.injector().get('$rootScope');
var scopePrototype = Object.getPrototypeOf($rootScope);
var oldDigest = scopePrototype.$digest;
scopePrototype.$digest = function $digest() {
var start = timerNow();
oldDigest.apply(this, arguments);
var diff = (timerNow() - start);
updateData(getWatcherCount(), diff);
};
}
// check for autoload
var autoloadOptions = sessionStorage[autoloadKey];
if (autoloadOptions) {
autoload(JSON.parse(autoloadOptions));
}
function autoload(options) {
if (window.angular && angular.element(document.body).injector()) {
showAngularStats(options);
} else {
// wait for angular to load...
window.setTimeout(function () {
autoload(options);
}, 200);
}
}
function showAngularStats(opts) {
// delete the previous one
if (current) {
current.$el && current.$el.remove();
current.active = false;
current = null;
}
// do nothing if the argument is false
if (opts === false) {
sessionStorage.removeItem(autoloadKey);
return;
} else {
opts = angular.extend({
position: 'top-left',
digestTimeThreshold: 16,
autoload: false
}, opts || {});
}
hijackDigest();
// setup the state
var state = current = {active: true};
// auto-load on startup
if (opts.autoload) {
sessionStorage.setItem(autoloadKey, JSON.stringify(opts));
} else {
sessionStorage.removeItem(autoloadKey);
}
// general variables
var noDigestSteps = 0;
// add the DOM element
state.$el = angular.element('<div><canvas></canvas><div></div></div>').css({
position: 'fixed',
background: 'black',
borderBottom: '1px solid #666',
borderRight: '1px solid #666',
color: 'red',
fontFamily: 'Courier',
width: 130,
zIndex: 9999,
top: opts.position.indexOf('top') == -1 ? null : 0,
bottom: opts.position.indexOf('bottom') == -1 ? null : 0,
right: opts.position.indexOf('right') == -1 ? null : 0,
left: opts.position.indexOf('left') == -1 ? null : 0,
textAlign: 'right'
});
bodyEl.append(state.$el);
var $text = state.$el.find('div');
// initialize the canvas
var graphSz = {width: 130, height: 40};
var cvs = state.$el.find('canvas').attr(graphSz)[0];
// replace the digest
listeners.digestLength.push(function (digestLength) {
addDataToCanvas(null, digestLength);
});
listeners.watchCount.push(function (watchCount) {
addDataToCanvas(watchCount);
});
function addDataToCanvas(watchCount, digestLength) {
var averageDigest = digestLength || lastDigestLength;
var color = (averageDigest > opts.digestTimeThreshold) ? 'red' : 'green';
lastWatchCount = nullOrUndef(watchCount) ? lastWatchCount : watchCount;
lastDigestLength = nullOrUndef(digestLength) ? lastDigestLength : digestLength;
$text.text(lastWatchCount + ' | ' + lastDigestLength.toFixed(2)).css({color: color});
if (!digestLength) {
return;
}
// color the sliver if this is the first step
var ctx = cvs.getContext('2d');
if (noDigestSteps > 0) {
noDigestSteps = 0;
ctx.fillStyle = '#333';
ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
}
// mark the point on the graph
ctx.fillStyle = color;
ctx.fillRect(graphSz.width - 1, Math.max(0, graphSz.height - averageDigest), 2, 2);
}
//! Shift the canvas to the left.
function shiftLeft() {
if (state.active) {
window.setTimeout(shiftLeft, 250);
var ctx = cvs.getContext('2d');
var imageData = ctx.getImageData(1, 0, graphSz.width - 1, graphSz.height);
ctx.putImageData(imageData, 0, 0);
ctx.fillStyle = ((noDigestSteps++) > 2) ? 'black' : '#333';
ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
}
}
// start everything
shiftLeft();
var $rootScope = bodyEl.injector().get('$rootScope');
if (!$rootScope.$$phase) {
$rootScope.$digest();
}
}
return showAngularStats;
// UTILITY FUNCTIONS
// Uses timeouts to ensure that this is only run every 300ms (it's a perf bottleneck)
function getWatcherCount() {
window.clearTimeout(watchCountTimeout);
var now = timerNow();
if (now - lastWatchCountRun > 300) {
lastWatchCountRun = now;
lastWatchCount = getWatcherCountForElement(angular.element(document.documentElement));
} else {
watchCountTimeout = window.setTimeout(function () {
updateData(getWatcherCount());
}, 350);
}
return lastWatchCount;
}
function getWatcherCountForElement(element) {
var watcherCount = 0;
if (!element || !element.length) {
return watcherCount;
}
var isolateWatchers = getWatchersFromScope(element.data().$isolateScope);
var scopeWatchers = getWatchersFromScope(element.data().$scope);
var watchers = scopeWatchers.concat(isolateWatchers);
watcherCount += watchers.length;
angular.forEach(element.children(), function (childElement) {
watcherCount += getWatcherCountForElement(angular.element(childElement));
});
return watcherCount;
}
function getWatchersFromScope(scope) {
return scope && scope.$$watchers ? scope.$$watchers : [];
}
// iterate through listeners to call them with the watchCount and digestLength
function updateData(watchCount, digestLength) {
// update the listeners
if (!nullOrUndef(watchCount)) {
angular.forEach(listeners.watchCount, function (listener) {
listener(watchCount);
});
}
if (!nullOrUndef(digestLength)) {
angular.forEach(listeners.digestLength, function (listener) {
listener(digestLength);
});
}
}
function nullOrUndef(item) {
return item === null || item === undefined;
}
}));
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var textcolor = [
'#A3F01D', //green
'#FAFAFA', //white
'#696A56', //gray
'#F5006A' //pink
];
var bgcolor = ['transparent', '#21221C'];
var fonts = ['font-size:1em;'];
var all = 'display:block; width:100%; position:relative; z-index:1;';
var heading = 'font-size:1.6em; font-weight:bold;';
var styles = {
default: all + 'background: ' + bgcolor[1] + '; color:' + textcolor[3] + '; ' + fonts[0],
description: all + 'background: ' + bgcolor[1] + '; color:' + textcolor[2] + '; ' + fonts[0],
key1: all + 'background: ' + bgcolor[1] + '; color:' + textcolor[0] + '; ' + fonts[0] + ' font-weight:bold;',
key2: all + 'background: ' + bgcolor[1] + '; color:' + textcolor[1] + '; ' + fonts[0]
};
var str = '',
strStyle = [];
Object.keys(window[keyname]).sort().map(function (key) {
str += "%c " + keyname + "%c.%c" + key + "%c() %c//";
if (typeof window[keyname][key].description !== 'undefined') {
str += window[keyname][key].description
} else {
str += '<no description available>'
}
str += '\n';
strStyle = strStyle.concat(styles.key1, styles.default, styles.key2, styles.default, styles.description);
return {key: key, description: window[keyname][key].description};
});
console.log('%cTools have been loaded to %c window.%c' + keyname + ' ', heading, styles.default + '; ' + heading, styles.key1 + '; ' + heading);
console.log.apply(console, [].concat(str, strStyle));
return window[keyname];
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment