Skip to content

Instantly share code, notes, and snippets.

@d-akara
Last active January 25, 2016 13:52
Show Gist options
  • Save d-akara/19efeb0724406745b54a to your computer and use it in GitHub Desktop.
Save d-akara/19efeb0724406745b54a to your computer and use it in GitHub Desktop.
Chrome devtools script for debugging. Older browser version
(function () {
"use strict";
var moduleName = "debug";
var symbolOriginal = Symbol('original-implementation');
function interceptFunction(object, methodName, beforeFunction, conditionFunction) {
// Reset original method if it has been overridden
if (object[methodName][symbolOriginal]) {
object[methodName] = object[methodName][symbolOriginal];
delete object[methodName][symbolOriginal];
}
var originalMethod = object[methodName];
if (beforeFunction) {
object[methodName] = function _debugIntercept() {
if (!conditionFunction || conditionFunction(methodName, arguments, this)) {
if (beforeFunction) {
beforeFunction(methodName, arguments, this);
}
var result = originalMethod.apply(this, arguments);
return result;
} else {
return originalMethod.apply(this, arguments);
}
};
// store original method
object[methodName][symbolOriginal] = originalMethod;
}
}
function interceptProperty(object, propertyName, getFunction, setFunction, conditionFunction) {
var originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, propertyName);
var newPropertyDescriptor = originalPropertyDescriptor ? Object.create(originalPropertyDescriptor) : {} ;
newPropertyDescriptor.configurable = true;
function shouldIntercept(object) {
if (!conditionFunction || conditionFunction(propertyName, arguments, object)) {
return true;
}
return false;
}
if (getFunction) {
var conditionalGetFunction = function conditionalGetFunction() {
if (shouldIntercept(this)) {
var value = getOriginal.apply(this, arguments);
getFunction(propertyName, arguments, this);
return value;
} else return getOriginal.apply(this, arguments);
};
newPropertyDescriptor.get = conditionalGetFunction;
}
if (setFunction) {
var conditionalSetFunction = function conditionalSetFunction(value) {
if (shouldIntercept(this)) {
setFunction(propertyName, arguments, this);
return setOriginal.call(this, value);
} else setOriginal.call(this, value);
};
newPropertyDescriptor.set = conditionalSetFunction;
}
function getOriginal() {
Object.defineProperty(object, propertyName, originalPropertyDescriptor);
var value = this[propertyName];
Object.defineProperty(object, propertyName, newPropertyDescriptor);
return value;
}
function setOriginal(value) {
try {
Object.defineProperty(object, propertyName, originalPropertyDescriptor);
this[propertyName] = value;
Object.defineProperty(object, propertyName, newPropertyDescriptor);
} catch (e) {
console.log(e);
Object.defineProperty(object, propertyName, newPropertyDescriptor);
}
}
Object.defineProperty(object, propertyName, newPropertyDescriptor);
}
var recursionCount = 0;
function logElement(targetElement, parentElement, methodName) {
if ( recursionCount > 0 ) return;
recursionCount++;
console.groupCollapsed(moduleName + ": " + methodName, targetElement);
if (parentElement) console.log("parent: ", parentElement);
console.trace();
console.groupEnd();
recursionCount--;
}
function testContainsAttribute() {}
var inspectElementsConfig = {
appendChild: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
insertBefore: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
replaceChild: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
removeChild: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
appendData: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
deleteData: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
insertData: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
replaceData: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
substringData: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
innerHTML: { prototype: Element.prototype, fnInspectWhen: testContainsAttribute },
outerHTML: { prototype: Element.prototype, fnInspectWhen: testContainsAttribute },
textContent: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
innerText: {prototype: HTMLElement.prototype, fnInspectWhen: testContainsAttribute},
outerText: {prototype: HTMLElement.prototype, fnInspectWhen: testContainsAttribute},
data: { prototype: CharacterData.prototype, fnInspectWhen: testContainsAttribute },
nodeValue: { prototype: Node.prototype, fnInspectWhen: testContainsAttribute },
checked: { prototype: HTMLInputElement.prototype, fnInspectWhen: testContainsAttribute }
};
var inspectElements_Settings = {
inspectWhen: {
attributeName: null,
attributeValue: null
},
inspectOn: {
elementModifiers: {
appendChild: false,
insertBefore: false,
replaceChild: false,
removeChild: false
},
textModifiers: {
appendData: true,
deleteData: false,
insertData: true,
replaceData: true,
substringData: false
},
magicProperties: {
innerHTML: true,
outerHTML: true,
textContent: true,
innerText: true,
outerText: true,
data: true,
nodeValue: true,
checked: true
},
inspect_insertAdjacentHTML: true,
inspect_selectOptions: true
},
breakOnInspect: false
};
function inspectElements() {
var settings = inspectElements_Settings;
var conditionFunction = function _debugTestContainsAttribute(inspectOnSettings, methodPropertyName, childElement, parentElement) {
if (!inspectOnSettings[methodPropertyName]) {
return false;
}
if (typeof childElement.getAttribute != 'function') return false;
var attributeName = settings.inspectWhen.attributeName;
var attributeValue = settings.inspectWhen.attributeValue;
var elementAttributeValue = childElement.getAttribute(attributeName);
if (!attributeName || elementAttributeValue && (!attributeValue || elementAttributeValue.indexOf(attributeValue) > -1)) {
return true;
}
return false;
};
var conditionChildFunction = function _debugTestChildContainsAttribute(methodPropertyName, originalArguments, parentElement) {
return conditionFunction(inspectElements_Settings.inspectOn.elementModifiers, methodPropertyName, originalArguments[0], parentElement);
};
var conditionParentFunction = function _debugTestParentContainsAttribute(methodPropertyName, originalArguments, parentElement) {
return conditionFunction(inspectElements_Settings.inspectOn.textModifiers, methodPropertyName, parentElement, parentElement);
};
var conditionMagicPropertyFunction = function _debugTestParentContainsAttribute(methodPropertyName, originalArguments, parentElement) {
return conditionFunction(inspectElements_Settings.inspectOn.magicProperties, methodPropertyName, parentElement, parentElement);
};
var beforeFunction = function _debugBeforeFunction(methodName, originalArguments, object) {
logElement(originalArguments[0], object, methodName);
if (settings.breakOnInspect) debugger;
};
var setPropertyFunction = function _debugSetPropertyFunction(methodName, originalArguments, object) {
logElement(object, null, "set:" + methodName);
if (settings.breakOnInspect) debugger;
};
for (var methodName in inspectElements_Settings.inspectOn.elementModifiers) {
interceptFunction(inspectElementsConfig[methodName].prototype, methodName, beforeFunction, conditionChildFunction);
}
for (var methodName in inspectElements_Settings.inspectOn.textModifiers) {
interceptFunction(inspectElementsConfig[methodName].prototype, methodName, beforeFunction, conditionParentFunction);
}
for (var propertyName in inspectElements_Settings.inspectOn.magicProperties) {
interceptProperty(inspectElementsConfig[propertyName].prototype, propertyName, null, setPropertyFunction, conditionMagicPropertyFunction);
}
console.log("inspecting elements active. modify returned properties object to configure.");
return settings;
}
function logEvent(event) {
console.log(event);
}
function testEvents(eventType, enable) {
if (eventType === undefined) {
console.log("EXAMPLE: testEvents('keydown', true)");
return;
}
if (enable) {
document.addEventListener(eventType, logEvent, true);
console.log("logging all '" + eventType + "' events.");
} else {
document.removeEventListener(eventType, logEvent, true);
console.log("stoped logging all '" + eventType + "' events.");
}
}
function profileWaitForTrigger(startEventProperties, duration) {
if (startEventProperties === undefined) {
console.log("EXAMPLE: profileWaitForTrigger({type: 'click'}, 100)");
return;
}
function armProfileTrigger() {
setEventTrigger(startEventProperties.type, function () {
return profile(duration);
}, function (event) {
return containsProperties(startEventProperties, event);
}, true);
console.log("profile trigger set. profiler will start when event matches:", startEventProperties);
}
setKeyTrigger({
ctrlKey: true
}, armProfileTrigger);
console.log("profile trigger will be set when you press [ctrl]");
}
function profile(duration) {
console.profile(moduleName + " " + new Date().toTimeString());
if (duration) {
setTimeout(function () {
return console.profileEnd();
}, duration);
} else {
setTimeout(function () {
return console.profileEnd();
});
}
}
function breakOnTimeout(duration) {
setTimeout(function () {
debugger;
}, duration);
}
function setEventTrigger(eventType, invokeFunction, conditionFunction, proccessEvent) {
document.addEventListener(eventType, function listener(event) {
if (!conditionFunction || conditionFunction(event)) {
document.removeEventListener(eventType, listener, true);
if (!proccessEvent) {
event.preventDefault();
event.stopImmediatePropagation();
}
invokeFunction();
}
}, true);
}
function setKeyTrigger(keyEventProperties, invokeFunction) {
setEventTrigger('keydown', invokeFunction, function (event) {
return containsProperties(keyEventProperties, event);
});
}
function containsProperties(objectWithProperties, objectToCheck) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Object.getOwnPropertyNames(objectWithProperties)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var property = _step.value;
if (objectToCheck[property] !== objectWithProperties[property]) {
return false;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"]) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return true;
}
function breakOnKeypress(keyEvent) {
keyEvent = keyEvent || {
ctrlKey: true
};
setKeyTrigger(keyEvent, function () {
debugger;
});
console.log("trigger set for key event: ", keyEvent);
}
function listPrototypes(object) {
console.group("Prototypes %O", object);
var prototype = object.prototype || object.__proto__;
while (prototype) {
console.log(prototype.constructor.name, prototype);
prototype = prototype.__proto__;
}
console.groupEnd();
}
/* --------------------- Export public functions -------------------- */
var exports = {
profileWaitForTrigger: profileWaitForTrigger, breakOnKeypress: breakOnKeypress, testEvents: testEvents, inspectElements: inspectElements, profile: profile, breakOnTimeout: breakOnTimeout, listPrototypes: listPrototypes
};
window.d = exports;
console.log(moduleName + " module installed");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment