├── .babelrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── README.md
├── app
├── ActiveBreakpointsList.js
├── AppView.js
├── AvailableBreakpointsList.js
├── BreakpointTypeSelector.js
└── app.js
├── breakpoints
├── breakpointCombinations.js
├── consoleInterface.js
├── debugObj.js
├── getCallbackFromUserFriendlyCallbackArgument.js
└── predefinedBreakpoints.js
├── console-api.md
├── dev
├── CONTRIBUTING.md
├── copy-live-demo-code-to-gh-pages.sh
├── make-webstore-package.sh
├── update-bookmarklet.js
└── update-snippet.js
├── devtools-panel.js
├── dist
└── javascript-breakpoint-collection.js
├── extension
├── background.js
├── content-script.js
├── devtools.html
├── icon.png
├── manifest.json
├── panel.css
├── panel.html
└── show-panel.js
├── gh-pages
├── bookmarklet.html
├── chrome-web-store.png
├── expand-stack-trace-button.png
├── expanded-stack-trace.png
├── expanded-trace-message.png
├── index.html
├── live-demo-copied-code
│ ├── devtools-panel.js
│ ├── javascript-breakpoint-collection.js
│ └── panel.css
└── live-demo.html
├── injected-script.js
├── karma.conf.js
├── node-test.js
├── package-lock.json
├── package.json
├── tests
├── app-spec.js
├── breakpointCombinations-spec.js
├── debugObj-spec.js
├── injected-integration-spec.js
└── predefinedBreakpoints-spec.js
├── webpack-test.config.js
├── webpack.config.js
└── webstore-assets
├── demo-with-page.png
├── extension-icon.png
├── icon-large.png
├── screenshot-old.png
├── screenshot.png
└── scroll-trace-demo.png
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | presets: ['es2015', 'react'],
3 | plugins: ["transform-object-rest-spread"]
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | extension/build/*
3 | dist/node.js
4 | dist/node.js.map
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | app/*
2 | breakpoints/*
3 | gh-pages/*
4 | tests/*
5 | webstore-assets/*
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.1"
4 | sudo: required
5 | dist: trusty
6 | before_install:
7 | - export CHROME_BIN=google-chrome-beta
8 | - export DISPLAY=:99.0
9 | - sh -e /etc/init.d/xvfb start
10 | - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
11 | - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
12 | - sudo apt-get update
13 | - sudo apt-get install google-chrome-beta
14 | script:
15 | - npm run build
16 | - npm run ci-test
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript Breakpoint Collection [](https://travis-ci.org/mattzeunert/javascript-breakpoint-collection)
2 |
3 | Find out what part of your code is causing a behavior in the browser. For example, you can pause when the window scroll position is updated or when cookie data is written.
4 |
5 | [Live Demo](http://www.mattzeunert.com/javascript-breakpoint-collection/live-demo.html)
6 |
7 | Either use the UI or add breakpoints from the console:
8 |
9 | breakpoints.debugScroll()
10 | breakpoints.debugPropertySet(obj, "propertyName", "trace") // trace instead of pausing
11 | breakpoints.debugCookieWrites(function(){ /* whatever */ })
12 | breakpoints.resetLastBreakpoint()
13 |
14 | Learn more about the [Console API](https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/console-api.md).
15 |
16 | ## Chrome Extension
17 |
18 | [Install from Chrome Web Store](https://chrome.google.com/webstore/detail/javascript-breakpoint-col/kgpjjblahlmjlfljfpcneapmeblichbp)
19 |
20 | 
21 |
22 | Example trace message:
23 |
24 | 
25 |
26 | ## Bookmarklet
27 |
28 | Get the bookmarklet
29 |
30 | ## Snippet
31 |
32 | Just paste the contents of [this file](https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/dist/javascript-breakpoint-collection.js) in the console.
33 |
34 | ## NPM
35 |
36 | Download the module from NPM:
37 |
38 | npm install javascript-breakpoint-collection
39 |
40 | Then load the module:
41 |
42 | var breakpoints = require("javascript-breakpoint-collection")
43 |
44 | ## Development
45 |
46 | See [Contributing.md](https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/dev/CONTRIBUTING.md).
47 |
--------------------------------------------------------------------------------
/app/ActiveBreakpointsList.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import {deactivateBreakpoint, updateBreakpointType} from "./app"
3 | import BreakpointTypeSelector from "./BreakpointTypeSelector"
4 |
5 | class ActiveBreakpointsListItem extends React.Component {
6 | render(){
7 | var title = this.props.breakpoint.details.title;
8 | return
11 | {title}
12 |
deactivateBreakpoint(this.props.breakpoint)}>
16 | ×
17 |
18 |
19 | updateBreakpointType(this.props.breakpoint, breakpointType)} />
22 |
23 |
24 | }
25 | }
26 |
27 | export default class ActiveBreakpointsList extends React.Component {
28 | render(){
29 | if (this.props.breakpoints.length === 0) {
30 | return
31 | Click on a breakpoint on the left to activate it.
32 |
33 | }
34 | return
35 | {this.props.breakpoints.map((bp, i) =>
36 |
37 | )}
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/AppView.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import {appState, registerAppView} from "./app"
3 | import AvailableBreakpointsList from "./AvailableBreakpointsList"
4 | import ActiveBreakpointsList from "./ActiveBreakpointsList"
5 |
6 | export default class AppView extends React.Component {
7 | constructor(props){
8 | super(props);
9 | this.state = {
10 | availableBreakpointsListSearchString: ""
11 | }
12 | }
13 | componentDidMount(){
14 | registerAppView(this);
15 | }
16 | render(){
17 | return
18 |
19 |
20 | JavaScript Breakpoint Collection
21 |
22 |
23 |
24 | Click on a breakpoint on the right to add it.
25 |
26 |
27 | To debug property access and function calls on any object, use the code below in the console.
28 |
29 |
30 |
31 | breakpoints.debugPropertySet(object, "propertyName");
32 |
33 |
34 | breakpoints.debugPropertyGet(document, "cookie", "trace");
35 |
36 |
37 | breakpoints.debugPropertyCall(localStorage, "setItem");
38 |
39 |
40 |
41 | breakpoints.debugScroll(function(details){"{"}
42 | console.log('JS changed scroll position', details)
43 | {"}"});
44 |
45 |
46 |
47 | Learn more
48 |
49 |
50 |
51 |
52 | Report an issue or request a feature
53 |
54 |
55 |
56 |
57 |
58 |
this.setState({
63 | availableBreakpointsListSearchString: e.target.value
64 | })} />
65 |
Add Breakpoint
66 |
71 |
72 |
73 |
Active Breakpoints
74 |
77 |
78 |
79 | }
80 | update(){
81 | this.setState({sth: Math.random()})
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/AvailableBreakpointsList.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import {activateBreakpoint, setTypeOfMostRecentBreakpointToDebugger} from "./app"
3 |
4 | class AvailableBreakpointsListItem extends React.Component {
5 | render(){
6 | var convertToDebuggerTypeButton = null;
7 | var title = this.props.breakpoint.title;
8 | var plusButton = +
;
9 | if (this.props.recentlyActivated) {
10 | convertToDebuggerTypeButton =
20 | Click again to change trace to debugger
21 |
22 | plusButton = null;
23 | title = String.fromCharCode(160); //nbsp
24 | }
25 |
26 | return this.props.onClick()}
28 | onMouseLeave={() => this.props.onMouseLeave()}
29 | className="unactivated-breakpoint-list-item"
30 | data-test-marker-available-bp-title={title}>
31 | {title}
32 | {convertToDebuggerTypeButton}
33 | {plusButton}
34 |
35 | }
36 | }
37 |
38 | export default class AvailableBreakpointsList extends React.Component {
39 | constructor(props){
40 | super(props);
41 | this.state = {
42 | recentlyActivatedBreakpoint: null
43 | }
44 | }
45 | render(){
46 | return
47 | {this.getBreakpointsToShow().map(
48 | (bp) => {
49 | var recentlyActivated = bp===this.state.recentlyActivatedBreakpoint;
50 | return
{
54 | if (this.state.recentlyActivatedBreakpoint !== null) {
55 | this.setState({recentlyActivatedBreakpoint: null})
56 | }
57 | }}
58 | onClick={() => {
59 | if (recentlyActivated) {
60 | setTypeOfMostRecentBreakpointToDebugger();
61 | this.setState({recentlyActivatedBreakpoint: null})
62 | } else {
63 | activateBreakpoint(bp);
64 | this.setState({recentlyActivatedBreakpoint: bp})
65 | }
66 | }}
67 | breakpoint={bp} />
68 | }
69 | )}
70 |
71 | }
72 | getBreakpointsToShow(){
73 | return this.props.breakpoints.filter((breakpoint) => {
74 | return breakpoint.title.toLowerCase().indexOf(this.props.search.toLowerCase()) > -1;
75 | });
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/BreakpointTypeSelector.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | function getStyle(isSelected){
4 | var style = {};
5 | if (isSelected) {
6 | style.color = "white";
7 | style.backgroundColor = "#698CFE";
8 | style.border = "1px solid #698CFE";
9 | } else {
10 | style.color = "black";
11 | style.backgroundColor = "white";
12 | }
13 |
14 | return style;
15 | }
16 |
17 | export default class BreakpointTypeSelector extends React.Component {
18 | render(){
19 | var options = ["trace", "debugger"];
20 | if (this.props.value === "custom") {
21 | options.push("custom")
22 | }
23 |
24 | return
25 | {options.map((option, i) => {
26 | var isSelected = this.props.value === option;
27 | return
this.props.onChange(option)}>
32 | {option}
33 |
34 | })}
35 |
36 | }
37 | }
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import predefinedBreakpoints from "../breakpoints/predefinedBreakpoints"
2 |
3 | var appState = {
4 | registeredBreakpoints: [],
5 | predefinedBreakpoints
6 | }
7 |
8 | var appViews = [];
9 |
10 | export function activateBreakpoint(breakpoint, options){
11 | var code = "window.breakpoints." + breakpoint.title + "('trace')"
12 | evalInInspectedWindow(code);
13 | }
14 |
15 | export function deactivateBreakpoint(breakpoint) {
16 | var code = "window.breakpoints.__internal.disableBreakpoint(" + breakpoint.id + ");"
17 | evalInInspectedWindow(code);
18 | }
19 |
20 | export function updateBreakpointType(breakpoint, traceOrDebugger){
21 | var id = breakpoint.id;
22 | var code = "window.breakpoints.__internal.updateBreakpointType('"+ id + "', '" + traceOrDebugger + "');"
23 | evalInInspectedWindow(code)
24 | }
25 |
26 | export function setTypeOfMostRecentBreakpointToDebugger(){
27 | evalInInspectedWindow("breakpoints.__internal.setTypeOfMostRecentBreakpointToDebugger()")
28 | }
29 |
30 | function checkIfBreakpointsInstalledOnPage(callback) {
31 | evalInInspectedWindow("window.breakpoints !== undefined", function(result){
32 | callback(result);
33 | })
34 | }
35 |
36 | function isRunningInDevToolsPanel(){
37 | return typeof chrome !== "undefined" && chrome.devtools && chrome.devtools.inspectedWindow;
38 | }
39 |
40 | function evalInInspectedWindow(code, callback){
41 | if (isRunningInDevToolsPanel()) {
42 | chrome.devtools.inspectedWindow.eval(code, afterEval);
43 | } else {
44 | try {
45 | var returnValue = eval(code);
46 | afterEval(returnValue)
47 | } catch (err) {
48 | afterEval(null, {value: err, isException: true});
49 | }
50 | }
51 |
52 | function afterEval(result, err){
53 | if (err && err.isException) {
54 | console.log("Exception occured in eval'd code", err.value)
55 | console.log("Code that was run: ", code)
56 | }
57 | else {
58 | if (callback) {
59 | callback(result);
60 | }
61 | }
62 | }
63 | }
64 |
65 | function readBreakpointsFromPage(){
66 | evalInInspectedWindow("breakpoints.__internal.getRegisteredBreakpoints();", function(regBp){
67 | appState.registeredBreakpoints = regBp;
68 | updateApp();
69 | });
70 | }
71 |
72 | function installBreakpointsOnPage(callback){
73 | var src;
74 | if (isRunningInDevToolsPanel()){
75 | src = chrome.extension.getURL('build/javascript-breakpoint-collection.js');
76 | } else {
77 | src = "extension/build/javascript-breakpoint-collection.js"
78 | }
79 | var code = `
80 | var s = document.createElement('script');
81 | s.src = '${src}'
82 | s.onload = function() {
83 | this.parentNode.removeChild(this);
84 | };
85 | (document.head || document.documentElement).appendChild(s);
86 | `;
87 | evalInInspectedWindow(code, function(){
88 | callCallbackIfHasBeenInstalled();
89 |
90 | function callCallbackIfHasBeenInstalled(){
91 | checkIfBreakpointsInstalledOnPage(function(isInstalled){
92 | if (isInstalled) {
93 | callback()
94 | } else {
95 | setTimeout(function(){
96 | callCallbackIfHasBeenInstalled();
97 | }, 50)
98 | }
99 | })
100 | }
101 | });
102 | }
103 |
104 | export function registerAppView(appView){
105 | appViews.push(appView)
106 | }
107 |
108 | function updateApp(){
109 | appViews.forEach(function(appView){
110 | appView.update()
111 | })
112 | }
113 |
114 | checkIfBreakpointsInstalledOnPage(function(isInstalled){
115 | if (isInstalled) {
116 | readBreakpointsFromPage();
117 | } else {
118 | installBreakpointsOnPage(function(){
119 | readBreakpointsFromPage();
120 | })
121 | }
122 | })
123 |
124 | if (isRunningInDevToolsPanel()) {
125 | var backgroundPageConnection = chrome.runtime.connect({
126 | name: "devtools-page"
127 | });
128 |
129 | backgroundPageConnection.onMessage.addListener(function (message) {
130 | // console.log("readBreakpointsFromPage b/c bg page said so")
131 | readBreakpointsFromPage();
132 | });
133 | } else {
134 | window.addEventListener("RebroadcastExtensionMessage", function(){
135 | readBreakpointsFromPage();
136 | });
137 | }
138 |
139 | export {appState}
140 |
--------------------------------------------------------------------------------
/breakpoints/breakpointCombinations.js:
--------------------------------------------------------------------------------
1 | import debugObj, {resetDebug, updateDebugIdCallback, disableBreakpointsDuringAllFunctionCalls} from "./debugObj"
2 | import getCallbackFromUserFriendlyCallbackArgument from "./getCallbackFromUserFriendlyCallbackArgument"
3 | var registeredBreakpoints = [];
4 |
5 | function debugPropertyCall(object, prop, callback){
6 | return debugObj(object, prop, {
7 | propertyCallBefore: {
8 | fn: callback
9 | }
10 | })
11 | }
12 |
13 | var debugPropertyGet = function(object, propertyName, callback){
14 | return debugObj(object, propertyName, {
15 | propertyGetBefore: {
16 | fn: callback
17 | }
18 | })
19 | }
20 | var debugPropertySet = function(object, propertyName, callback) {
21 | return debugObj(object, propertyName, {
22 | propertySetBefore: {
23 | fn: callback
24 | }
25 | })
26 | }
27 |
28 | var breakpointCombinations = {
29 | register(fn, bpDetails, predefinedBreakpoint) {
30 | var debugIds = [];
31 | var _debugPropertyGet = function(object, propertyName, callback){
32 | debugIds.push(debugPropertyGet(object, propertyName, callback));
33 | }
34 | var _debugPropertySet = function(object, propertyName, callback){
35 | debugIds.push(debugPropertySet(object, propertyName, callback));
36 | }
37 | var _debugPropertyCall = function(object, propertyName, callback){
38 | debugIds.push(debugPropertyCall(object, propertyName, callback));
39 | }
40 | fn(_debugPropertyGet, _debugPropertySet, _debugPropertyCall);
41 |
42 | var id = Math.floor(Math.random() * 1000000000)
43 | var bp = {
44 | id: id,
45 | debugIds,
46 | details: bpDetails,
47 | createdAt: new Date(),
48 | predefinedBreakpoint
49 | }
50 | registeredBreakpoints.push(bp);
51 |
52 | return id;
53 | },
54 | disable(id){
55 | var bp = registeredBreakpoints.filter(function(bp){
56 | return bp.id == id;
57 | })[0];
58 | if (bp === undefined) {
59 | console.log("Couldn't find breakpoint with id", id)
60 | return;
61 | }
62 | bp.debugIds.forEach(function(debugId){
63 | resetDebug(debugId);
64 | });
65 | registeredBreakpoints = registeredBreakpoints.filter(function(bp){
66 | return bp.id != id;
67 | })
68 | },
69 | updateType(id, newType){
70 | if (newType !== "debugger" && newType !== "trace") {
71 | throw new Error("Invalid breakpoint type")
72 | }
73 |
74 | var bp = registeredBreakpoints.filter(function(bp){
75 | return bp.id == id;
76 | })[0];
77 |
78 | var callback = getCallbackFromUserFriendlyCallbackArgument(newType, bp.predefinedBreakpoint);
79 | bp.debugIds.forEach(function(debugId){
80 | updateDebugIdCallback(debugId, callback)
81 | });
82 |
83 | bp.details.type = newType;
84 | },
85 | resetAll(){
86 | registeredBreakpoints.forEach(function(breakpoint){
87 | breakpointCombinations.disable(breakpoint.id);
88 | });
89 | },
90 | getRegisteredBreakpoints(){
91 | return registeredBreakpoints;
92 | },
93 | resetLastBreakpoint(){
94 | var breakpointToReset = registeredBreakpoints[registeredBreakpoints.length - 1];
95 | breakpointCombinations.disable(breakpointToReset.id);
96 | },
97 | setTypeOfMostRecentBreakpointToDebugger(){
98 | var mostRecentBreakpoint = registeredBreakpoints[registeredBreakpoints.length - 1];
99 | breakpointCombinations.updateType(mostRecentBreakpoint.id, "debugger")
100 | }
101 | }
102 |
103 | disableBreakpointsDuringAllFunctionCalls(breakpointCombinations);
104 |
105 | export default breakpointCombinations;
106 |
--------------------------------------------------------------------------------
/breakpoints/consoleInterface.js:
--------------------------------------------------------------------------------
1 | import debugObj, {debugObjBreakpointRegistry, objectsAndPropsByDebugId, disableBreakpointsDuringAllFunctionCalls} from "./debugObj"
2 | import predefinedBreakpoints from "./predefinedBreakpoints"
3 | import getCallbackFromUserFriendlyCallbackArgument from "./getCallbackFromUserFriendlyCallbackArgument"
4 | import breakpointCombinations from "./breakpointCombinations"
5 |
6 | function pushRegisteredBreakpointsToExtension() {
7 | if (typeof CustomEvent === "undefined"
8 | || typeof window === "undefined"
9 | || !window.dispatchEvent
10 | ) {
11 | return; // probably in a Node environment
12 | }
13 | var event = new CustomEvent("RebroadcastExtensionMessage", {
14 | type: "updateRegisteredBreakpoints",
15 | registeredBreakpoints: breakpointCombinations.getRegisteredBreakpoints()
16 | });
17 | window.dispatchEvent(event);
18 | }
19 |
20 | var __internal = {
21 | updateBreakpointType: function(id, newType){
22 | breakpointCombinations.updateType(id, newType)
23 | pushRegisteredBreakpointsToExtension();
24 | },
25 | disableBreakpoint: function(id){
26 | breakpointCombinations.disable(id);
27 | pushRegisteredBreakpointsToExtension();
28 | },
29 | registerBreakpoint: function(){
30 | var id = breakpointCombinations.register.apply(null, arguments);
31 | pushRegisteredBreakpointsToExtension();
32 | return id;
33 | },
34 | isBreakpointCollectionExtension: true,
35 | debug: {
36 | debugObj,
37 | debugObjBreakpointRegistry,
38 | objectsAndPropsByDebugId
39 | },
40 | getRegisteredBreakpoints: function(){
41 | return breakpointCombinations.getRegisteredBreakpoints();
42 | },
43 | setTypeOfMostRecentBreakpointToDebugger: function(){
44 | breakpointCombinations.setTypeOfMostRecentBreakpointToDebugger();
45 | pushRegisteredBreakpointsToExtension();
46 | }
47 | }
48 |
49 | disableBreakpointsDuringAllFunctionCalls(__internal)
50 |
51 | function publicDebugPropertyAccess(obj, prop, callback, accessType) {
52 | var functionName = {
53 | "get": "debugPropertyGet",
54 | "set": "debugPropertySet",
55 | "call": "debugPropertyCall"
56 | }[accessType];
57 |
58 | callback = getCallbackFromUserFriendlyCallbackArgument(callback);
59 | __internal.registerBreakpoint(function(
60 | debugPropertyGet, debugPropertySet, debugPropertyCall
61 | ){
62 | var debugFunctions = {
63 | debugPropertyGet,
64 | debugPropertySet,
65 | debugPropertyCall
66 | }
67 | debugFunctions[functionName](obj, prop, callback);
68 | }, {
69 | title: functionName + " (" + prop + ")",
70 | type: callback.callbackType
71 | });
72 | }
73 |
74 | var breakpoints = {
75 | debugPropertyGet: function(obj, prop, callback){
76 | return publicDebugPropertyAccess(obj, prop, callback, "get")
77 | },
78 | debugPropertySet: function(obj, prop, callback){
79 | return publicDebugPropertyAccess(obj, prop, callback, "set")
80 | },
81 | debugPropertyCall: function(obj, prop, callback){
82 | return publicDebugPropertyAccess(obj, prop, callback, "call")
83 | },
84 | resetAllBreakpoints: function(){
85 | breakpointCombinations.resetAll()
86 | pushRegisteredBreakpointsToExtension();
87 | },
88 | resetLastBreakpoint: function(){
89 | if (breakpointCombinations.getRegisteredBreakpoints().length === 0) {
90 | console.log("No breakpoints are currently registered")
91 | return;
92 | }
93 | breakpointCombinations.resetLastBreakpoint();
94 | pushRegisteredBreakpointsToExtension();
95 | },
96 | __internal
97 | }
98 |
99 | predefinedBreakpoints.forEach(function(breakpoint){
100 | breakpoints[breakpoint.title] = function(callback){
101 | callback = getCallbackFromUserFriendlyCallbackArgument(callback, breakpoint);
102 |
103 | var details = {
104 | title: breakpoint.title,
105 | type: callback.callbackType
106 | }
107 |
108 | var fn = function(debugPropertyGet, debugPropertySet, debugPropertyCall){
109 | if (breakpoint.debugPropertyGets) {
110 | breakpoint.debugPropertyGets.forEach(function(property){
111 | debugPropertyGet(eval(property.obj), property.prop, callback)
112 | })
113 | }
114 | if (breakpoint.debugPropertySets) {
115 | breakpoint.debugPropertySets.forEach(function(property){
116 | debugPropertySet(eval(property.obj), property.prop, callback)
117 | })
118 | }
119 | if (breakpoint.debugCalls) {
120 | breakpoint.debugCalls.forEach(function(property){
121 | debugPropertyCall(eval(property.obj), property.prop, callback)
122 | })
123 | }
124 | }
125 |
126 | __internal.registerBreakpoint(fn, details, breakpoint);
127 | pushRegisteredBreakpointsToExtension();
128 | }
129 | });
130 |
131 | disableBreakpointsDuringAllFunctionCalls(breakpoints);
132 |
133 | export default breakpoints
134 | export { pushRegisteredBreakpointsToExtension }
135 |
--------------------------------------------------------------------------------
/breakpoints/debugObj.js:
--------------------------------------------------------------------------------
1 | var registry = new Map();
2 | var objectsAndPropsByDebugId = {}
3 |
4 | var hookNames = [
5 | "propertyGetBefore",
6 | "propertyGetAfter",
7 | "propertySetBefore",
8 | "propertySetAfter",
9 | "propertyCallBefore",
10 | "propertyCallAfter"
11 | ];
12 |
13 | function getPropertyDescriptor(object, propertyName){
14 | try {
15 | var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
16 | } catch (err){
17 | var newError = Error ("Are you sure the property \"" + propertyName + "\" exists?");
18 | newError.originalError = err;
19 | throw newError;
20 | }
21 | if (!object){
22 | throw new Error("Descriptor " + propertyName + " not found");
23 | }
24 | if (!descriptor) {
25 | return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
26 | }
27 | return descriptor;
28 | }
29 |
30 | export { registry as debugObjBreakpointRegistry, objectsAndPropsByDebugId }
31 |
32 | // counter instead of boolean to allow nested calls of runWithBreakpointsDisabled
33 | var timesBreakpointsWereDisabled = 0;
34 | export function runWithBreakpointsDisabled(fn){
35 | timesBreakpointsWereDisabled++;
36 | var retVal = fn();
37 | timesBreakpointsWereDisabled--;
38 | return retVal;
39 | }
40 |
41 | export function disableBreakpointsDuringAllFunctionCalls(object){
42 | for (var functionName in object){
43 | let fn = object[functionName];
44 | if (typeof fn !== "function") {
45 | continue;
46 | }
47 |
48 | object[functionName] = function(){
49 | var thisArg = this;
50 | var args = arguments;
51 | return runWithBreakpointsDisabled(function(){
52 | return fn.apply(thisArg, args);
53 | })
54 | }
55 | }
56 | }
57 |
58 | function areBreakpointsDisabled(){
59 | return timesBreakpointsWereDisabled > 0;
60 | }
61 |
62 | export default function debugObj(obj, prop, hooks) {
63 | var debugId = Math.floor(Math.random() * 100000000000).toString()
64 | objectsAndPropsByDebugId[debugId] = {
65 | obj,
66 | prop
67 | }
68 |
69 | if (registry.get(obj) === undefined) {
70 | registry.set(obj, {});
71 | }
72 |
73 | if (registry.get(obj)[prop] === undefined) {
74 | registry.get(obj)[prop] = {hooks: {}};
75 |
76 | var originalProp = getPropertyDescriptor(obj, prop);
77 | var isSimpleValue = "value" in originalProp; // rather than getter + setter
78 |
79 | Object.defineProperty(obj, prop, {
80 | get: function(){
81 | var retVal;
82 |
83 | triggerHook("propertyGetBefore", {
84 | thisArgument: this
85 | });
86 |
87 | if (isSimpleValue) {
88 | retVal = originalProp.value;
89 | } else {
90 | retVal = originalProp.get.apply(this, arguments);
91 | }
92 |
93 | triggerHook("propertyGetAfter", {
94 | thisArgument: this
95 | });
96 |
97 | if (typeof retVal === "function") {
98 | return function(){
99 | var args = Array.prototype.slice.call(arguments);
100 |
101 | triggerHook("propertyCallBefore", {
102 | callArguments: args,
103 | thisArgument: this
104 | });
105 |
106 | var fnRetVal = retVal.apply(this, arguments);
107 |
108 | triggerHook("propertyCallAfter", {
109 | callArguments: args,
110 | thisArgument: this
111 | });
112 |
113 | return fnRetVal;
114 | }
115 | }
116 |
117 | return retVal;
118 | },
119 | set: function(newValue){
120 | var retVal;
121 |
122 | triggerHook("propertySetBefore", {
123 | newPropertyValue: newValue,
124 | thisArgument: this
125 | });
126 |
127 | if (isSimpleValue) {
128 | retVal = originalProp.value = newValue;
129 | } else {
130 | retVal = originalProp.set.apply(this, arguments);
131 | }
132 |
133 | triggerHook("propertySetAfter", {
134 | newPropertyValue: newValue,
135 | thisArgument: this
136 | });
137 |
138 | return retVal;
139 | }
140 | });
141 | }
142 |
143 |
144 | hookNames.forEach(function(hookName){
145 | if (hooks[hookName] !== undefined) {
146 | if (registry.get(obj)[prop].hooks[hookName] === undefined) {
147 | registry.get(obj)[prop].hooks[hookName] = [];
148 | }
149 | var hook = hooks[hookName];
150 | registry.get(obj)[prop].hooks[hookName].push({
151 | id: debugId,
152 | fn: hook.fn,
153 | data: hook.data
154 | })
155 | }
156 | });
157 |
158 | return debugId;
159 |
160 | function triggerHook(hookName, additionalHookInfo) {
161 | if (areBreakpointsDisabled()) {
162 | return;
163 | }
164 | var hooks = registry.get(obj)[prop].hooks;
165 | var hooksWithName = hooks[hookName];
166 |
167 | var infoForHook = {
168 | object: obj,
169 | propertyName: prop,
170 | ...additionalHookInfo
171 | }
172 |
173 | if (hooksWithName !== undefined && hooksWithName.length > 0) {
174 | hooksWithName.forEach(function(hook){
175 | hook.fn({
176 | ...infoForHook,
177 | accessType: getAccessTypeFromHookName(hookName),
178 | data: hook.data
179 | });
180 | })
181 | }
182 | }
183 | }
184 |
185 | function getAccessTypeFromHookName(hookName){
186 | var accessType = "";
187 | if (hookName === "propertyGetBefore" || hookName === "propertyGetAfter") {
188 | accessType = "get";
189 | }
190 | if (hookName === "propertySetBefore" || hookName === "propertySetAfter") {
191 | accessType = "set";
192 | }
193 | if (hookName === "propertyCallBefore" || hookName === "propertyCallAfter") {
194 | accessType = "call";
195 | }
196 | return accessType;
197 | }
198 |
199 | function updateEachHook(obj, prop, cb){
200 | var hooks = registry.get(obj)[prop].hooks;
201 | hookNames.forEach(function(hookName){
202 | var hooksWithName = hooks[hookName];
203 | if (hooksWithName !== undefined) {
204 | hooks[hookName] = hooksWithName.map(function(hook){
205 | return cb(hook)
206 | })
207 | }
208 | })
209 | }
210 |
211 | export function updateDebugIdCallback(debugId, callback){
212 | var objAndProp = objectsAndPropsByDebugId[debugId];
213 | updateEachHook(objAndProp.obj, objAndProp.prop, function(hook){
214 | if (hook.id === debugId) {
215 | return {
216 | id: debugId,
217 | fn: callback,
218 | data: hook.data
219 | }
220 | } else {
221 | return hook;
222 | }
223 | });
224 | }
225 |
226 | export function resetDebug(id){
227 | var objAndProp = objectsAndPropsByDebugId[id];
228 | var hooks = registry.get(objAndProp.obj)[objAndProp.prop].hooks;
229 | for (var hookName in hooks) {
230 | var hooksWithName = hooks[hookName];
231 | hooks[hookName] = hooksWithName.filter(function(hook){
232 | return hook.id != id;
233 | })
234 | }
235 |
236 | delete objectsAndPropsByDebugId[id];
237 | }
238 |
--------------------------------------------------------------------------------
/breakpoints/getCallbackFromUserFriendlyCallbackArgument.js:
--------------------------------------------------------------------------------
1 | import {runWithBreakpointsDisabled} from "./debugObj"
2 |
3 | function getDebuggerFunction() {
4 | var debuggerFunc = function() {
5 | debugger;
6 | }
7 | debuggerFunc.callbackType = "debugger";
8 | return debuggerFunc;
9 | }
10 |
11 | export default function getCallbackFromUserFriendlyCallbackArgument(callback, predefinedBreakpoint){
12 | if (typeof callback === "function") {
13 | callback.callbackType = "custom"
14 | return callback;
15 | } else if (typeof callback === "string") {
16 | if (callback === "debugger") {
17 | return getDebuggerFunction();
18 |
19 | } else if (callback === "trace") {
20 | return getTraceFunction(predefinedBreakpoint);
21 | } else {
22 | throw new Error("Invalid string callback")
23 | }
24 | } else if(typeof callback=== "undefined") {
25 | return getDebuggerFunction();
26 | } else {
27 | throw new Error("Invalid callback type: " + typeof callback)
28 | }
29 | }
30 |
31 | function getTraceFunction(predefinedBreakpoint) {
32 | var traceFn;
33 | if (predefinedBreakpoint) {
34 | if (predefinedBreakpoint.getTraceInfo) {
35 | traceFn = function(){
36 | try {
37 | var traceArgs = predefinedBreakpoint.getTraceInfo.apply(null, arguments);
38 |
39 | runWithBreakpointsDisabled(function(){
40 | console.trace.apply(console, traceArgs);
41 | });
42 | } catch (err) {
43 | console.error("Generating trace message failed", err);
44 | }
45 | }
46 | }
47 | else if (predefinedBreakpoint.traceMessage) {
48 | traceFn = function(){
49 | runWithBreakpointsDisabled(function(){
50 | console.trace(predefinedBreakpoint.traceMessage)
51 | })
52 | }
53 | }
54 | }
55 | else {
56 | traceFn = function(debugInfo){
57 | runWithBreakpointsDisabled(function() {
58 | showTraceMessageForCustomBreakpoints(debugInfo);
59 | });
60 | }
61 | }
62 |
63 | traceFn.callbackType = "trace";
64 | return traceFn;
65 | }
66 |
67 | function showTraceMessageForCustomBreakpoints(debugInfo) {
68 | var truncate = function(str, isArray) {
69 | const MAX_LENGTH = 25;
70 |
71 | if (str.length > MAX_LENGTH) {
72 | return str.substring(0, MAX_LENGTH) + "..." + (isArray ? "]" : "");
73 | }
74 | return str;
75 | };
76 |
77 | try {
78 | var message = "About to " + debugInfo.accessType + " property '" + debugInfo.propertyName + "' ";
79 |
80 | if (debugInfo.accessType == "set") {
81 | var newPropertyValue = debugInfo.newPropertyValue;
82 | var newPropertyType = typeof newPropertyValue;
83 |
84 | var isArray = (newPropertyValue !== undefined && newPropertyValue != null &&
85 | newPropertyValue.constructor === Array);
86 |
87 | if (newPropertyType === "string") {
88 | newPropertyValue = truncate(newPropertyValue, false);
89 | } else if (isArray) {
90 | try {
91 | newPropertyValue = JSON.stringify(newPropertyValue);
92 | newPropertyValue = truncate(newPropertyValue, true);
93 | } catch(e) {
94 | newPropertyValue = newPropertyValue.toString(); // fallback to a shallow version
95 | newPropertyValue = "[" + truncate(newPropertyValue, false) + "]";
96 | }
97 | }
98 |
99 | if (isArray) {
100 | console.trace(message + "to " + newPropertyValue + " on this object: ", debugInfo.object);
101 | } else {
102 | console.trace(message + "to %o ", newPropertyValue," on this object: ", debugInfo.object);
103 | }
104 | }
105 | else {
106 | console.trace(message + "on this object: ", debugInfo.object);
107 | }
108 | } catch (err) {
109 | // in case something else breaks the trace message, we don't want to break the whole app
110 | console.error("Generating trace message failed", err);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/breakpoints/predefinedBreakpoints.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | title: "debugScroll",
4 | debugCalls: [{
5 | obj: "window",
6 | prop: "scrollTo"
7 | }, {
8 | obj: "window",
9 | prop: "scrollBy"
10 | }],
11 | debugPropertySets: [{
12 | obj: "document.body",
13 | prop: "scrollTop"
14 | }, {
15 | obj: "document.body",
16 | prop: "scrollLeft"
17 | }, {
18 | obj: "Element.prototype",
19 | prop: "scrollTop"
20 | }, {
21 | obj: "Element.prototype",
22 | prop: "scrollLeft"
23 | }],
24 | getTraceInfo: function(details){
25 | if (details.propertyName == "scrollTo"
26 | || details.propertyName == "scrollBy") {
27 | return [
28 | "The scroll position of the window was changed by a window." +
29 | details.propertyName
30 | + " call with", details.callArguments];
31 | }
32 | return [
33 | "The scroll position of",
34 | details.thisArgument,
35 | "was changed by setting the " + details.propertyName + " property to " + details.newPropertyValue
36 | ];
37 | }
38 | },
39 | {
40 | title: "debugCookieReads",
41 | debugPropertyGets: [{
42 | obj: "document",
43 | prop: "cookie"
44 | }],
45 | traceMessage: "Reading cookie contents"
46 | },
47 | {
48 | title: "debugCookieWrites",
49 | debugPropertySets: [{
50 | obj: "document",
51 | prop: "cookie"
52 | }],
53 | traceMessage: "Updating cookie contents"
54 | },
55 | {
56 | title: "debugAlertCalls",
57 | debugCalls: [{
58 | obj: "window",
59 | prop: "alert"
60 | }],
61 | traceMessage: "Showing alert box"
62 | }, {
63 | title: "debugElementSelection",
64 | debugCalls: [{
65 | obj: "document",
66 | prop: "getElementById"
67 | }, {
68 | obj: "document",
69 | prop: "getElementsByClassName"
70 | }, {
71 | obj: "document",
72 | prop: "getElementsByName"
73 | }, {
74 | obj: "document",
75 | prop: "getElementsByTagName"
76 | }, {
77 | obj: "document",
78 | prop: "getElementsByTagNameNS"
79 | }, {
80 | obj: "document",
81 | prop: "getElementsByClassName"
82 | }, {
83 | obj: "document",
84 | prop: "querySelector"
85 | }, {
86 | obj: "document",
87 | prop: "querySelectorAll"
88 | }, {
89 | obj: "document",
90 | prop: "evaluate" // xpath
91 | }],
92 | getTraceInfo: function(details){
93 | return ["Selecting DOM elements \"" + details.callArguments[0] + "\" using " + details.propertyName];
94 | }
95 | },
96 | {
97 | title: "debugConsoleErrorCalls",
98 | debugCalls: [{
99 | obj: "window.console",
100 | prop: "error"
101 | }],
102 | traceMessage: "Calling console.error"
103 | },
104 | {
105 | title: "debugConsoleLogCalls",
106 | debugCalls: [{
107 | obj: "window.console",
108 | prop: "log"
109 | }],
110 | traceMessage: "Calling console.log"
111 | },
112 | {
113 | title: "debugConsoleTraceCalls",
114 | debugCalls: [{
115 | obj: "window.console",
116 | prop: "trace"
117 | }],
118 | traceMessage: "Calling console.trace"
119 | },
120 | {
121 | title: "debugMathRandom",
122 | debugCalls: [
123 | {
124 | obj: "window.Math",
125 | prop: "random"
126 | }
127 | ],
128 | getTraceInfo: function(){
129 | return ["Calling Math.random"]
130 | }
131 | },
132 | {
133 | title: "debugTimerCreation",
134 | debugCalls: [
135 | {
136 | obj: "window",
137 | prop: "setTimeout"
138 | },
139 | {
140 | obj: "window",
141 | prop: "setInterval"
142 | }
143 | ],
144 | getTraceInfo: function(details){
145 | return ["Creating timer using " + details.propertyName]
146 | }
147 | },
148 | {
149 | title: "debugLocalStorageReads",
150 | debugCalls: [{
151 | obj: "window.localStorage",
152 | prop: "getItem"
153 | }],
154 | getTraceInfo: function(details){
155 | return ["Reading localStorage data for key \"" + details.callArguments[0] + "\""];
156 | }
157 | },
158 | {
159 | title: "debugLocalStorageWrites",
160 | debugCalls: [{
161 | obj: "window.localStorage",
162 | prop: "setItem"
163 | }, {
164 | obj: "window.localStorage",
165 | prop: "clear"
166 | }],
167 | getTraceInfo: function(details){
168 | if (details.propertyName == "clear") {
169 | return ["Clearing all localStorage data"];
170 | }
171 | return ["Writing localStorage data for key \"" + details.callArguments[0] + "\""];
172 | }
173 | }
174 | ];
175 |
--------------------------------------------------------------------------------
/console-api.md:
--------------------------------------------------------------------------------
1 | # Console API
2 |
3 | ## Adding breakpoints from the console
4 |
5 | To add a pre-defined breakpoint just call its function on the `breakpoints` object:
6 |
7 | breakpoints.debugScroll()
8 |
9 | If you want to debug a custom object you have to pass in the object and property you
10 | want to debug.
11 |
12 | var obj = {test: "value"}
13 | breakpoints.debugPropertySet(obj, "test")
14 | obj.test = "hi" // Pauses execution
15 |
16 | You can use the following three functions:
17 |
18 | - **debugPropertyGet** to pause when a value is read, e.g. just running `obj.test`
19 | - **debugPropertySet** to pause when a value is set, e.g. `obj.test = "hi"`
20 | - **debugPropertyCall** to pause when a value is called, g.. `obj.sayHello()`
21 |
22 | The pre-defined breakpoints are just combinations of these calls. For example:
23 |
24 | breakpoints.debugLocalStorageWrites()
25 |
26 | Is equivalent to:
27 |
28 | breakpoints.debugPropertyCall(localStorage, "setItem")
29 | breakpoints.debugPropertyCall(localStorage, "clear")
30 |
31 | ## Tracepoints
32 |
33 | If you don't pass any additional parameters into the functions above they will pause
34 | execution when the breakpoint is hit.
35 |
36 | However, you can also simply show a trace message without interrupting execution, by
37 | passing in "trace" as a third argument:
38 |
39 | breakpoints.debugPropertySet(obj, "test", "trace")
40 |
41 | 
42 |
43 | Or, for pre-defined breakpoints, passing in "trace" as the first parameter:
44 |
45 | breakpoints.debugLocalStorageWrites("trace")
46 |
47 | ## Custom callbacks
48 |
49 | Instead of "trace" you can also pass in a custom function that should be called when
50 | the breakpoint is hit
51 |
52 | breakpoints.debugScroll(function(details){
53 | // do whatever here
54 | console.log("In debugScroll callback with details:", details)
55 | })
56 |
57 | The details will vary depending on the breakpoint, but generally will have this information:
58 |
59 | - **object** a reference to the object being debugged
60 | - **propertyName** the name of the property being debugged
61 | - **callArguments** when using debugPropertyCalls this will contain the arguments the function
62 | is being called with
63 | - **thisArgument** when using debugPropertyCalls this will be the execution context the function is called with.
64 |
65 | ## Resetting breakpoints
66 |
67 | You can either use `breakpoints.resetLastBreakpoint()` to reset the most recently
68 | created breakpoint, or use `breakpoints.resetAllBreakpoints()` to reset all breakpoints
69 | added using JS Breakpoint Collection.
70 |
71 | ## Links
72 |
73 | [Bookmarklet](http://www.mattzeunert.com/javascript-breakpoint-collection/bookmarklet.html)
74 | [Chrome Extension](https://chrome.google.com/webstore/detail/javascript-breakpoint-col/kgpjjblahlmjlfljfpcneapmeblichbp)
75 | [Snippet](https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/dist/javascript-breakpoint-collection.js)
76 |
--------------------------------------------------------------------------------
/dev/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | Install the dependencies:
4 |
5 | npm install webpack -g
6 | npm install
7 |
8 | To build the project:
9 |
10 | webpack --watch
11 |
12 | And run the tests:
13 |
14 | npm run test
15 |
16 | ### Loading the extension in Chrome
17 |
18 | 1. Go to [chrome://extensions/](chrome://extensions/)
19 | 2. Enter Developer Mode
20 | 3. Load Unpacked Extension
21 | 4. Select "extension" directory in this repo
22 |
23 | ### Updating the bookmarklet
24 |
25 | Run `node ./dev/update-bookmarklet.js`.
26 |
27 | ## Update the snippet
28 |
29 | Run `node ./dev/update-snippet.js`.
30 |
31 | ### Update website
32 |
33 | `git subtree push --prefix gh-pages origin gh-pages`
34 |
35 | ### Upload to Chrome Web Store
36 |
37 | Run `./dev/make-webstore-package.sh` and then upload dist.zip.
38 |
39 | ## Releases to update
40 |
41 | - Chrome extension
42 | - Bookmarklet
43 | - NPM module
44 | - Live demo
45 | - Snippet
46 |
--------------------------------------------------------------------------------
/dev/copy-live-demo-code-to-gh-pages.sh:
--------------------------------------------------------------------------------
1 | OUT=gh-pages/live-demo-copied-code
2 | cp extension/panel.css $OUT/panel.css
3 | cp extension/build/devtools-panel.js $OUT/devtools-panel.js
4 | cp extension/build/javascript-breakpoint-collection.js $OUT/javascript-breakpoint-collection.js
5 |
--------------------------------------------------------------------------------
/dev/make-webstore-package.sh:
--------------------------------------------------------------------------------
1 | cp -r extension/ webstore
2 | rm webstore/build/*.map
3 | zip -r webstore.zip webstore
4 | rm -r webstore
5 |
--------------------------------------------------------------------------------
/dev/update-bookmarklet.js:
--------------------------------------------------------------------------------
1 | var UglifyJS = require("uglify-js");
2 |
3 | var result = UglifyJS.minify("./extension/build/javascript-breakpoint-collection.js", {
4 | compress: false
5 | });
6 | var bookmarkletContent = "javascript:window.__BP_SHOW_CONSOLE_API_MESSAGE = true;eval(decodeURIComponent('" + encodeURIComponent(result.code).replace(/'/g, "\\'") + "'))";
7 |
8 | var fs = require("fs");
9 | var htmlContent = fs.readFileSync("./gh-pages/bookmarklet.html").toString()
10 | htmlContent = htmlContent.replace(
11 | /data\-bookmarklet\-href([\s\S]*)data\-bookmarklet\-href\-end/i,
12 | "data-bookmarklet-href href=\"" + bookmarkletContent + "\" data-bookmarklet-href-end"
13 | );
14 | fs.writeFileSync("./gh-pages/bookmarklet.html", htmlContent)
15 |
--------------------------------------------------------------------------------
/dev/update-snippet.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs");
2 | var code = fs.readFileSync("extension/build/javascript-breakpoint-collection.js")
3 | code = "window.__BP_SHOW_CONSOLE_API_MESSAGE = true;\n" + code;
4 | fs.writeFileSync("dist/javascript-breakpoint-collection.js", code)
5 |
--------------------------------------------------------------------------------
/devtools-panel.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import ReactDOM from "react-dom"
3 | import AppView from "./app/AppView.js"
4 |
5 | ReactDOM.render( , document.getElementById("app-content"));
6 |
--------------------------------------------------------------------------------
/dist/javascript-breakpoint-collection.js:
--------------------------------------------------------------------------------
1 | window.__BP_SHOW_CONSOLE_API_MESSAGE = true;
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 | /******/
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 | /******/
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId])
11 | /******/ return installedModules[moduleId].exports;
12 | /******/
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ exports: {},
16 | /******/ id: moduleId,
17 | /******/ loaded: false
18 | /******/ };
19 | /******/
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 | /******/
23 | /******/ // Flag the module as loaded
24 | /******/ module.loaded = true;
25 | /******/
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 | /******/
30 | /******/
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 | /******/
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 | /******/
37 | /******/ // __webpack_public_path__
38 | /******/ __webpack_require__.p = "";
39 | /******/
40 | /******/ // Load entry module and return exports
41 | /******/ return __webpack_require__(0);
42 | /******/ })
43 | /************************************************************************/
44 | /******/ ({
45 |
46 | /***/ 0:
47 | /***/ function(module, exports, __webpack_require__) {
48 |
49 | "use strict";
50 |
51 | var _consoleInterface = __webpack_require__(165);
52 |
53 | var _consoleInterface2 = _interopRequireDefault(_consoleInterface);
54 |
55 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
56 |
57 | module.exports = _consoleInterface2.default;
58 | var isInBrowser = typeof window !== "undefined";
59 | if (isInBrowser) {
60 | if (window.breakpoints !== undefined) {
61 | if (!window.breakpoints.__internal || !window.breakpoints.__internal.isBreakpointCollectionExtension) {
62 | console.log("Breakpoints extension can't load, global `breakpoints` variable is already defined");
63 | }
64 | } else {
65 | window.breakpoints = _consoleInterface2.default;
66 |
67 | (0, _consoleInterface.pushRegisteredBreakpointsToExtension)();
68 |
69 | if (window.__BP_SHOW_CONSOLE_API_MESSAGE) {
70 | delete window.__BP_SHOW_CONSOLE_API_MESSAGE;
71 | console.log("Breakpoints Collection API docs: https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/console-api.md");
72 | }
73 | }
74 | }
75 |
76 | /***/ },
77 |
78 | /***/ 161:
79 | /***/ function(module, exports) {
80 |
81 | "use strict";
82 |
83 | Object.defineProperty(exports, "__esModule", {
84 | value: true
85 | });
86 | exports.default = [{
87 | title: "debugScroll",
88 | debugCalls: [{
89 | obj: "window",
90 | prop: "scrollTo"
91 | }, {
92 | obj: "window",
93 | prop: "scrollBy"
94 | }],
95 | debugPropertySets: [{
96 | obj: "document.body",
97 | prop: "scrollTop"
98 | }, {
99 | obj: "document.body",
100 | prop: "scrollLeft"
101 | }, {
102 | obj: "Element.prototype",
103 | prop: "scrollTop"
104 | }, {
105 | obj: "Element.prototype",
106 | prop: "scrollLeft"
107 | }],
108 | getTraceInfo: function getTraceInfo(details) {
109 | if (details.propertyName == "scrollTo" || details.propertyName == "scrollBy") {
110 | return ["The scroll position of the window was changed by a window." + details.propertyName + " call with", details.callArguments];
111 | }
112 | return ["The scroll position of", details.thisArgument, "was changed by setting the " + details.propertyName + " property to " + details.newPropertyValue];
113 | }
114 | }, {
115 | title: "debugCookieReads",
116 | debugPropertyGets: [{
117 | obj: "document",
118 | prop: "cookie"
119 | }],
120 | traceMessage: "Reading cookie contents"
121 | }, {
122 | title: "debugCookieWrites",
123 | debugPropertySets: [{
124 | obj: "document",
125 | prop: "cookie"
126 | }],
127 | traceMessage: "Updating cookie contents"
128 | }, {
129 | title: "debugAlertCalls",
130 | debugCalls: [{
131 | obj: "window",
132 | prop: "alert"
133 | }],
134 | traceMessage: "Showing alert box"
135 | }, {
136 | title: "debugElementSelection",
137 | debugCalls: [{
138 | obj: "document",
139 | prop: "getElementById"
140 | }, {
141 | obj: "document",
142 | prop: "getElementsByClassName"
143 | }, {
144 | obj: "document",
145 | prop: "getElementsByName"
146 | }, {
147 | obj: "document",
148 | prop: "getElementsByTagName"
149 | }, {
150 | obj: "document",
151 | prop: "getElementsByTagNameNS"
152 | }, {
153 | obj: "document",
154 | prop: "getElementsByClassName"
155 | }, {
156 | obj: "document",
157 | prop: "querySelector"
158 | }, {
159 | obj: "document",
160 | prop: "querySelectorAll"
161 | }, {
162 | obj: "document",
163 | prop: "evaluate" // xpath
164 | }],
165 | getTraceInfo: function getTraceInfo(details) {
166 | return ["Selecting DOM elements \"" + details.callArguments[0] + "\" using " + details.propertyName];
167 | }
168 | }, {
169 | title: "debugConsoleErrorCalls",
170 | debugCalls: [{
171 | obj: "window.console",
172 | prop: "error"
173 | }],
174 | traceMessage: "Calling console.error"
175 | }, {
176 | title: "debugConsoleLogCalls",
177 | debugCalls: [{
178 | obj: "window.console",
179 | prop: "log"
180 | }],
181 | traceMessage: "Calling console.log"
182 | }, {
183 | title: "debugConsoleTraceCalls",
184 | debugCalls: [{
185 | obj: "window.console",
186 | prop: "trace"
187 | }],
188 | traceMessage: "Calling console.trace"
189 | }, {
190 | title: "debugMathRandom",
191 | debugCalls: [{
192 | obj: "window.Math",
193 | prop: "random"
194 | }],
195 | getTraceInfo: function getTraceInfo() {
196 | return ["Calling Math.random"];
197 | }
198 | }, {
199 | title: "debugTimerCreation",
200 | debugCalls: [{
201 | obj: "window",
202 | prop: "setTimeout"
203 | }, {
204 | obj: "window",
205 | prop: "setInterval"
206 | }],
207 | getTraceInfo: function getTraceInfo(details) {
208 | return ["Creating timer using " + details.propertyName];
209 | }
210 | }, {
211 | title: "debugLocalStorageReads",
212 | debugCalls: [{
213 | obj: "window.localStorage",
214 | prop: "getItem"
215 | }],
216 | getTraceInfo: function getTraceInfo(details) {
217 | return ["Reading localStorage data for key \"" + details.callArguments[0] + "\""];
218 | }
219 | }, {
220 | title: "debugLocalStorageWrites",
221 | debugCalls: [{
222 | obj: "window.localStorage",
223 | prop: "setItem"
224 | }, {
225 | obj: "window.localStorage",
226 | prop: "clear"
227 | }],
228 | getTraceInfo: function getTraceInfo(details) {
229 | if (details.propertyName == "clear") {
230 | return ["Clearing all localStorage data"];
231 | }
232 | return ["Writing localStorage data for key \"" + details.callArguments[0] + "\""];
233 | }
234 | }];
235 |
236 | /***/ },
237 |
238 | /***/ 165:
239 | /***/ function(module, exports, __webpack_require__) {
240 |
241 | "use strict";
242 |
243 | Object.defineProperty(exports, "__esModule", {
244 | value: true
245 | });
246 | exports.pushRegisteredBreakpointsToExtension = undefined;
247 |
248 | var _debugObj = __webpack_require__(166);
249 |
250 | var _debugObj2 = _interopRequireDefault(_debugObj);
251 |
252 | var _predefinedBreakpoints = __webpack_require__(161);
253 |
254 | var _predefinedBreakpoints2 = _interopRequireDefault(_predefinedBreakpoints);
255 |
256 | var _getCallbackFromUserFriendlyCallbackArgument = __webpack_require__(167);
257 |
258 | var _getCallbackFromUserFriendlyCallbackArgument2 = _interopRequireDefault(_getCallbackFromUserFriendlyCallbackArgument);
259 |
260 | var _breakpointCombinations = __webpack_require__(168);
261 |
262 | var _breakpointCombinations2 = _interopRequireDefault(_breakpointCombinations);
263 |
264 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
265 |
266 | function pushRegisteredBreakpointsToExtension() {
267 | if (typeof CustomEvent === "undefined" || typeof window === "undefined" || !window.dispatchEvent) {
268 | return; // probably in a Node environment
269 | }
270 | var event = new CustomEvent("RebroadcastExtensionMessage", {
271 | type: "updateRegisteredBreakpoints",
272 | registeredBreakpoints: _breakpointCombinations2.default.getRegisteredBreakpoints()
273 | });
274 | window.dispatchEvent(event);
275 | }
276 |
277 | var __internal = {
278 | updateBreakpointType: function updateBreakpointType(id, newType) {
279 | _breakpointCombinations2.default.updateType(id, newType);
280 | pushRegisteredBreakpointsToExtension();
281 | },
282 | disableBreakpoint: function disableBreakpoint(id) {
283 | _breakpointCombinations2.default.disable(id);
284 | pushRegisteredBreakpointsToExtension();
285 | },
286 | registerBreakpoint: function registerBreakpoint() {
287 | var id = _breakpointCombinations2.default.register.apply(null, arguments);
288 | pushRegisteredBreakpointsToExtension();
289 | return id;
290 | },
291 | isBreakpointCollectionExtension: true,
292 | debug: {
293 | debugObj: _debugObj2.default,
294 | debugObjBreakpointRegistry: _debugObj.debugObjBreakpointRegistry,
295 | objectsAndPropsByDebugId: _debugObj.objectsAndPropsByDebugId
296 | },
297 | getRegisteredBreakpoints: function getRegisteredBreakpoints() {
298 | return _breakpointCombinations2.default.getRegisteredBreakpoints();
299 | },
300 | setTypeOfMostRecentBreakpointToDebugger: function setTypeOfMostRecentBreakpointToDebugger() {
301 | _breakpointCombinations2.default.setTypeOfMostRecentBreakpointToDebugger();
302 | pushRegisteredBreakpointsToExtension();
303 | }
304 | };
305 |
306 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(__internal);
307 |
308 | function publicDebugPropertyAccess(obj, prop, callback, accessType) {
309 | var functionName = {
310 | "get": "debugPropertyGet",
311 | "set": "debugPropertySet",
312 | "call": "debugPropertyCall"
313 | }[accessType];
314 |
315 | callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(callback);
316 | __internal.registerBreakpoint(function (debugPropertyGet, debugPropertySet, debugPropertyCall) {
317 | var debugFunctions = {
318 | debugPropertyGet: debugPropertyGet,
319 | debugPropertySet: debugPropertySet,
320 | debugPropertyCall: debugPropertyCall
321 | };
322 | debugFunctions[functionName](obj, prop, callback);
323 | }, {
324 | title: functionName + " (" + prop + ")",
325 | type: callback.callbackType
326 | });
327 | }
328 |
329 | var breakpoints = {
330 | debugPropertyGet: function debugPropertyGet(obj, prop, callback) {
331 | return publicDebugPropertyAccess(obj, prop, callback, "get");
332 | },
333 | debugPropertySet: function debugPropertySet(obj, prop, callback) {
334 | return publicDebugPropertyAccess(obj, prop, callback, "set");
335 | },
336 | debugPropertyCall: function debugPropertyCall(obj, prop, callback) {
337 | return publicDebugPropertyAccess(obj, prop, callback, "call");
338 | },
339 | resetAllBreakpoints: function resetAllBreakpoints() {
340 | _breakpointCombinations2.default.resetAll();
341 | pushRegisteredBreakpointsToExtension();
342 | },
343 | resetLastBreakpoint: function resetLastBreakpoint() {
344 | if (_breakpointCombinations2.default.getRegisteredBreakpoints().length === 0) {
345 | console.log("No breakpoints are currently registered");
346 | return;
347 | }
348 | _breakpointCombinations2.default.resetLastBreakpoint();
349 | pushRegisteredBreakpointsToExtension();
350 | },
351 | __internal: __internal
352 | };
353 |
354 | _predefinedBreakpoints2.default.forEach(function (breakpoint) {
355 | breakpoints[breakpoint.title] = function (callback) {
356 | callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(callback, breakpoint);
357 |
358 | var details = {
359 | title: breakpoint.title,
360 | type: callback.callbackType
361 | };
362 |
363 | var fn = function fn(debugPropertyGet, debugPropertySet, debugPropertyCall) {
364 | if (breakpoint.debugPropertyGets) {
365 | breakpoint.debugPropertyGets.forEach(function (property) {
366 | debugPropertyGet(eval(property.obj), property.prop, callback);
367 | });
368 | }
369 | if (breakpoint.debugPropertySets) {
370 | breakpoint.debugPropertySets.forEach(function (property) {
371 | debugPropertySet(eval(property.obj), property.prop, callback);
372 | });
373 | }
374 | if (breakpoint.debugCalls) {
375 | breakpoint.debugCalls.forEach(function (property) {
376 | debugPropertyCall(eval(property.obj), property.prop, callback);
377 | });
378 | }
379 | };
380 |
381 | __internal.registerBreakpoint(fn, details, breakpoint);
382 | pushRegisteredBreakpointsToExtension();
383 | };
384 | });
385 |
386 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(breakpoints);
387 |
388 | exports.default = breakpoints;
389 | exports.pushRegisteredBreakpointsToExtension = pushRegisteredBreakpointsToExtension;
390 |
391 | /***/ },
392 |
393 | /***/ 166:
394 | /***/ function(module, exports) {
395 |
396 | "use strict";
397 |
398 | Object.defineProperty(exports, "__esModule", {
399 | value: true
400 | });
401 |
402 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
403 |
404 | exports.runWithBreakpointsDisabled = runWithBreakpointsDisabled;
405 | exports.disableBreakpointsDuringAllFunctionCalls = disableBreakpointsDuringAllFunctionCalls;
406 | exports.default = debugObj;
407 | exports.updateDebugIdCallback = updateDebugIdCallback;
408 | exports.resetDebug = resetDebug;
409 | var registry = new Map();
410 | var objectsAndPropsByDebugId = {};
411 |
412 | var hookNames = ["propertyGetBefore", "propertyGetAfter", "propertySetBefore", "propertySetAfter", "propertyCallBefore", "propertyCallAfter"];
413 |
414 | function getPropertyDescriptor(object, propertyName) {
415 | try {
416 | var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
417 | } catch (err) {
418 | var newError = Error("Are you sure the property \"" + propertyName + "\" exists?");
419 | newError.originalError = err;
420 | throw newError;
421 | }
422 | if (!object) {
423 | throw new Error("Descriptor " + propertyName + " not found");
424 | }
425 | if (!descriptor) {
426 | return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
427 | }
428 | return descriptor;
429 | }
430 |
431 | exports.debugObjBreakpointRegistry = registry;
432 | exports.objectsAndPropsByDebugId = objectsAndPropsByDebugId;
433 |
434 | // counter instead of boolean to allow nested calls of runWithBreakpointsDisabled
435 |
436 | var timesBreakpointsWereDisabled = 0;
437 | function runWithBreakpointsDisabled(fn) {
438 | timesBreakpointsWereDisabled++;
439 | var retVal = fn();
440 | timesBreakpointsWereDisabled--;
441 | return retVal;
442 | }
443 |
444 | function disableBreakpointsDuringAllFunctionCalls(object) {
445 | var _loop = function _loop() {
446 | var fn = object[functionName];
447 | if (typeof fn !== "function") {
448 | return "continue";
449 | }
450 |
451 | object[functionName] = function () {
452 | var thisArg = this;
453 | var args = arguments;
454 | return runWithBreakpointsDisabled(function () {
455 | return fn.apply(thisArg, args);
456 | });
457 | };
458 | };
459 |
460 | for (var functionName in object) {
461 | var _ret = _loop();
462 |
463 | if (_ret === "continue") continue;
464 | }
465 | }
466 |
467 | function areBreakpointsDisabled() {
468 | return timesBreakpointsWereDisabled > 0;
469 | }
470 |
471 | function debugObj(obj, prop, hooks) {
472 | var debugId = Math.floor(Math.random() * 100000000000).toString();
473 | objectsAndPropsByDebugId[debugId] = {
474 | obj: obj,
475 | prop: prop
476 | };
477 |
478 | if (registry.get(obj) === undefined) {
479 | registry.set(obj, {});
480 | }
481 |
482 | if (registry.get(obj)[prop] === undefined) {
483 | registry.get(obj)[prop] = { hooks: {} };
484 |
485 | var originalProp = getPropertyDescriptor(obj, prop);
486 | var isSimpleValue = "value" in originalProp; // rather than getter + setter
487 |
488 | Object.defineProperty(obj, prop, {
489 | get: function get() {
490 | var retVal;
491 |
492 | triggerHook("propertyGetBefore", {
493 | thisArgument: this
494 | });
495 |
496 | if (isSimpleValue) {
497 | retVal = originalProp.value;
498 | } else {
499 | retVal = originalProp.get.apply(this, arguments);
500 | }
501 |
502 | triggerHook("propertyGetAfter", {
503 | thisArgument: this
504 | });
505 |
506 | if (typeof retVal === "function") {
507 | return function () {
508 | var args = Array.prototype.slice.call(arguments);
509 |
510 | triggerHook("propertyCallBefore", {
511 | callArguments: args,
512 | thisArgument: this
513 | });
514 |
515 | var fnRetVal = retVal.apply(this, arguments);
516 |
517 | triggerHook("propertyCallAfter", {
518 | callArguments: args,
519 | thisArgument: this
520 | });
521 |
522 | return fnRetVal;
523 | };
524 | }
525 |
526 | return retVal;
527 | },
528 | set: function set(newValue) {
529 | var retVal;
530 |
531 | triggerHook("propertySetBefore", {
532 | newPropertyValue: newValue,
533 | thisArgument: this
534 | });
535 |
536 | if (isSimpleValue) {
537 | retVal = originalProp.value = newValue;
538 | } else {
539 | retVal = originalProp.set.apply(this, arguments);
540 | }
541 |
542 | triggerHook("propertySetAfter", {
543 | newPropertyValue: newValue,
544 | thisArgument: this
545 | });
546 |
547 | return retVal;
548 | }
549 | });
550 | }
551 |
552 | hookNames.forEach(function (hookName) {
553 | if (hooks[hookName] !== undefined) {
554 | if (registry.get(obj)[prop].hooks[hookName] === undefined) {
555 | registry.get(obj)[prop].hooks[hookName] = [];
556 | }
557 | var hook = hooks[hookName];
558 | registry.get(obj)[prop].hooks[hookName].push({
559 | id: debugId,
560 | fn: hook.fn,
561 | data: hook.data
562 | });
563 | }
564 | });
565 |
566 | return debugId;
567 |
568 | function triggerHook(hookName, additionalHookInfo) {
569 | if (areBreakpointsDisabled()) {
570 | return;
571 | }
572 | var hooks = registry.get(obj)[prop].hooks;
573 | var hooksWithName = hooks[hookName];
574 |
575 | var infoForHook = _extends({
576 | object: obj,
577 | propertyName: prop
578 | }, additionalHookInfo);
579 |
580 | if (hooksWithName !== undefined && hooksWithName.length > 0) {
581 | hooksWithName.forEach(function (hook) {
582 | hook.fn(_extends({}, infoForHook, {
583 | accessType: getAccessTypeFromHookName(hookName),
584 | data: hook.data
585 | }));
586 | });
587 | }
588 | }
589 | }
590 |
591 | function getAccessTypeFromHookName(hookName) {
592 | var accessType = "";
593 | if (hookName === "propertyGetBefore" || hookName === "propertyGetAfter") {
594 | accessType = "get";
595 | }
596 | if (hookName === "propertySetBefore" || hookName === "propertySetAfter") {
597 | accessType = "set";
598 | }
599 | if (hookName === "propertyCallBefore" || hookName === "propertyCallAfter") {
600 | accessType = "call";
601 | }
602 | return accessType;
603 | }
604 |
605 | function updateEachHook(obj, prop, cb) {
606 | var hooks = registry.get(obj)[prop].hooks;
607 | hookNames.forEach(function (hookName) {
608 | var hooksWithName = hooks[hookName];
609 | if (hooksWithName !== undefined) {
610 | hooks[hookName] = hooksWithName.map(function (hook) {
611 | return cb(hook);
612 | });
613 | }
614 | });
615 | }
616 |
617 | function updateDebugIdCallback(debugId, callback) {
618 | var objAndProp = objectsAndPropsByDebugId[debugId];
619 | updateEachHook(objAndProp.obj, objAndProp.prop, function (hook) {
620 | if (hook.id === debugId) {
621 | return {
622 | id: debugId,
623 | fn: callback,
624 | data: hook.data
625 | };
626 | } else {
627 | return hook;
628 | }
629 | });
630 | }
631 |
632 | function resetDebug(id) {
633 | var objAndProp = objectsAndPropsByDebugId[id];
634 | var hooks = registry.get(objAndProp.obj)[objAndProp.prop].hooks;
635 | for (var hookName in hooks) {
636 | var hooksWithName = hooks[hookName];
637 | hooks[hookName] = hooksWithName.filter(function (hook) {
638 | return hook.id != id;
639 | });
640 | }
641 |
642 | delete objectsAndPropsByDebugId[id];
643 | }
644 |
645 | /***/ },
646 |
647 | /***/ 167:
648 | /***/ function(module, exports, __webpack_require__) {
649 |
650 | "use strict";
651 |
652 | Object.defineProperty(exports, "__esModule", {
653 | value: true
654 | });
655 |
656 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
657 |
658 | exports.default = getCallbackFromUserFriendlyCallbackArgument;
659 |
660 | var _debugObj = __webpack_require__(166);
661 |
662 | function getDebuggerFunction() {
663 | var debuggerFunc = function debuggerFunc() {
664 | debugger;
665 | };
666 | debuggerFunc.callbackType = "debugger";
667 | return debuggerFunc;
668 | }
669 |
670 | function getCallbackFromUserFriendlyCallbackArgument(callback, predefinedBreakpoint) {
671 | if (typeof callback === "function") {
672 | callback.callbackType = "custom";
673 | return callback;
674 | } else if (typeof callback === "string") {
675 | if (callback === "debugger") {
676 | return getDebuggerFunction();
677 | } else if (callback === "trace") {
678 | return getTraceFunction(predefinedBreakpoint);
679 | } else {
680 | throw new Error("Invalid string callback");
681 | }
682 | } else if (typeof callback === "undefined") {
683 | return getDebuggerFunction();
684 | } else {
685 | throw new Error("Invalid callback type: " + (typeof callback === "undefined" ? "undefined" : _typeof(callback)));
686 | }
687 | }
688 |
689 | function getTraceFunction(predefinedBreakpoint) {
690 | var traceFn;
691 | if (predefinedBreakpoint) {
692 | if (predefinedBreakpoint.getTraceInfo) {
693 | traceFn = function traceFn() {
694 | var traceArgs = predefinedBreakpoint.getTraceInfo.apply(null, arguments);
695 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
696 | console.trace.apply(console, traceArgs);
697 | });
698 | };
699 | } else if (predefinedBreakpoint.traceMessage) {
700 | traceFn = function traceFn() {
701 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
702 | console.trace(predefinedBreakpoint.traceMessage);
703 | });
704 | };
705 | }
706 | } else {
707 | traceFn = function traceFn(debugInfo) {
708 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
709 | console.trace("About to " + debugInfo.accessType + " property '" + debugInfo.propertyName + "' on this object: ", debugInfo.object);
710 | });
711 | };
712 | }
713 |
714 | traceFn.callbackType = "trace";
715 | return traceFn;
716 | }
717 |
718 | /***/ },
719 |
720 | /***/ 168:
721 | /***/ function(module, exports, __webpack_require__) {
722 |
723 | "use strict";
724 |
725 | Object.defineProperty(exports, "__esModule", {
726 | value: true
727 | });
728 |
729 | var _debugObj = __webpack_require__(166);
730 |
731 | var _debugObj2 = _interopRequireDefault(_debugObj);
732 |
733 | var _getCallbackFromUserFriendlyCallbackArgument = __webpack_require__(167);
734 |
735 | var _getCallbackFromUserFriendlyCallbackArgument2 = _interopRequireDefault(_getCallbackFromUserFriendlyCallbackArgument);
736 |
737 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
738 |
739 | var registeredBreakpoints = [];
740 |
741 | function debugPropertyCall(object, prop, callback) {
742 | return (0, _debugObj2.default)(object, prop, {
743 | propertyCallBefore: {
744 | fn: callback
745 | }
746 | });
747 | }
748 |
749 | var debugPropertyGet = function debugPropertyGet(object, propertyName, callback) {
750 | return (0, _debugObj2.default)(object, propertyName, {
751 | propertyGetBefore: {
752 | fn: callback
753 | }
754 | });
755 | };
756 | var debugPropertySet = function debugPropertySet(object, propertyName, callback) {
757 | return (0, _debugObj2.default)(object, propertyName, {
758 | propertySetBefore: {
759 | fn: callback
760 | }
761 | });
762 | };
763 |
764 | var breakpointCombinations = {
765 | register: function register(fn, bpDetails, predefinedBreakpoint) {
766 | var debugIds = [];
767 | var _debugPropertyGet = function _debugPropertyGet(object, propertyName, callback) {
768 | debugIds.push(debugPropertyGet(object, propertyName, callback));
769 | };
770 | var _debugPropertySet = function _debugPropertySet(object, propertyName, callback) {
771 | debugIds.push(debugPropertySet(object, propertyName, callback));
772 | };
773 | var _debugPropertyCall = function _debugPropertyCall(object, propertyName, callback) {
774 | debugIds.push(debugPropertyCall(object, propertyName, callback));
775 | };
776 | fn(_debugPropertyGet, _debugPropertySet, _debugPropertyCall);
777 |
778 | var id = Math.floor(Math.random() * 1000000000);
779 | var bp = {
780 | id: id,
781 | debugIds: debugIds,
782 | details: bpDetails,
783 | createdAt: new Date(),
784 | predefinedBreakpoint: predefinedBreakpoint
785 | };
786 | registeredBreakpoints.push(bp);
787 |
788 | return id;
789 | },
790 | disable: function disable(id) {
791 | var bp = registeredBreakpoints.filter(function (bp) {
792 | return bp.id == id;
793 | })[0];
794 | if (bp === undefined) {
795 | console.log("Couldn't find breakpoint with id", id);
796 | return;
797 | }
798 | bp.debugIds.forEach(function (debugId) {
799 | (0, _debugObj.resetDebug)(debugId);
800 | });
801 | registeredBreakpoints = registeredBreakpoints.filter(function (bp) {
802 | return bp.id != id;
803 | });
804 | },
805 | updateType: function updateType(id, newType) {
806 | if (newType !== "debugger" && newType !== "trace") {
807 | throw new Error("Invalid breakpoint type");
808 | }
809 |
810 | var bp = registeredBreakpoints.filter(function (bp) {
811 | return bp.id == id;
812 | })[0];
813 |
814 | var callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(newType, bp.predefinedBreakpoint);
815 | bp.debugIds.forEach(function (debugId) {
816 | (0, _debugObj.updateDebugIdCallback)(debugId, callback);
817 | });
818 |
819 | bp.details.type = newType;
820 | },
821 | resetAll: function resetAll() {
822 | registeredBreakpoints.forEach(function (breakpoint) {
823 | breakpointCombinations.disable(breakpoint.id);
824 | });
825 | },
826 | getRegisteredBreakpoints: function getRegisteredBreakpoints() {
827 | return registeredBreakpoints;
828 | },
829 | resetLastBreakpoint: function resetLastBreakpoint() {
830 | var breakpointToReset = registeredBreakpoints[registeredBreakpoints.length - 1];
831 | breakpointCombinations.disable(breakpointToReset.id);
832 | },
833 | setTypeOfMostRecentBreakpointToDebugger: function setTypeOfMostRecentBreakpointToDebugger() {
834 | var mostRecentBreakpoint = registeredBreakpoints[registeredBreakpoints.length - 1];
835 | breakpointCombinations.updateType(mostRecentBreakpoint.id, "debugger");
836 | }
837 | };
838 |
839 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(breakpointCombinations);
840 |
841 | exports.default = breakpointCombinations;
842 |
843 | /***/ }
844 |
845 | /******/ });
846 | //# sourceMappingURL=javascript-breakpoint-collection.js.map
--------------------------------------------------------------------------------
/extension/background.js:
--------------------------------------------------------------------------------
1 | chrome.runtime.onConnect.addListener(function(devToolsConnection) {
2 | // assign the listener function to a variable so we can remove it later
3 | var devToolsListener = function(message, sender, sendResponse) {
4 | // Inject a content script into the identified tab
5 | chrome.tabs.executeScript(message.tabId,
6 | { file: message.scriptToInject });
7 | }
8 | // add the listener
9 | devToolsConnection.onMessage.addListener(devToolsListener);
10 |
11 |
12 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
13 | devToolsConnection.postMessage(request)
14 | });
15 |
16 | devToolsConnection.onDisconnect.addListener(function() {
17 | devToolsConnection.onMessage.removeListener(devToolsListener);
18 | });
19 | })
--------------------------------------------------------------------------------
/extension/content-script.js:
--------------------------------------------------------------------------------
1 | var s = document.createElement('script');
2 | s.src = chrome.extension.getURL('build/javascript-breakpoint-collection.js');
3 | s.onload = function() {
4 | this.parentNode.removeChild(this);
5 | };
6 | (document.head || document.documentElement).appendChild(s);
7 |
8 | window.addEventListener("RebroadcastExtensionMessage", function(evt) {
9 | try {
10 | chrome.runtime.sendMessage(evt)
11 | } catch (err) {
12 | // `Cannot read property 'name' of undefined` can be caused by background page refresh (e.g. alt+r)
13 | console.log("Breakpoints: Sending info to DevTools tab failed:", err)
14 | console.log("Breakpoints: This can occur after reloading the extension. Close and re-open the current tab to fix it.")
15 | }
16 | }, false);
--------------------------------------------------------------------------------
/extension/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/extension/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/extension/icon.png
--------------------------------------------------------------------------------
/extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JavaScript Breakpoint Collection",
3 | "manifest_version": 2,
4 | "version": "1.7.1",
5 | "description": "Find what code is responsible for page behavior.",
6 | "content_scripts": [
7 | {
8 | "matches": ["http://*/*", "https://*/*", "file://*/*"],
9 |
10 | "js": ["content-script.js"]
11 | }
12 | ],
13 | "background": {
14 | "scripts": ["background.js"]
15 | },
16 | "icons": {
17 | "128": "icon.png"
18 | },
19 | "permissions": ["http://*/*", "https://*/*", "file://*/*"],
20 | "minimum_chrome_version": "10.0",
21 | "devtools_page": "devtools.html",
22 | "web_accessible_resources": ["build/javascript-breakpoint-collection.js"]
23 | }
24 |
--------------------------------------------------------------------------------
/extension/panel.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 | h2 {
5 | padding: 10px;
6 | font-weight: normal;
7 | padding-top: 7px;
8 | padding-bottom: 6px;
9 | margin: 0;
10 | background: #fafafa;
11 | border-bottom: 1px solid #eee;
12 | }
13 | body {
14 | margin: 0;
15 | font-family: "Helvetica Neue", "Lucida Grande", sans-serif;
16 | font-size: 75%;
17 | }
18 | a {
19 | color: #698cfe;
20 | }
21 | .col-parent > div {
22 | width: 33.3333%;
23 | float: left;
24 | border-right: 1px solid #eee;
25 | height: 100%;
26 | background: white;
27 | }
28 | .col-parent > div:first-child {
29 | background: #fafafa;
30 | }
31 | .col-parent > div:last-child {
32 | border-right: none;
33 | }
34 | .col-parent .col-content {
35 | padding: 10px;
36 | padding-top: 0px;
37 | height: calc(100% - 36px);
38 | overflow: auto;
39 | }
40 | .unactivated-breakpoint-list-item {
41 | border-bottom: 1px solid #eee;
42 | padding: 10px;
43 | cursor: pointer;
44 | }
45 | .unactivated-breakpoint-list-item:hover {
46 | background: #fafafa;
47 | }
48 | .unactivated-breakpoint-list-item .plus {
49 | display: none;
50 |
51 | float: right;
52 | font-size: 24px;
53 | color: red;
54 | width: 20px;
55 | height: 20px;
56 | text-align: center;
57 | margin-top: -3px;
58 | line-height: 16px;
59 | }
60 | .unactivated-breakpoint-list-item:hover .plus {
61 | display: inline-block;
62 | }
63 |
64 | .activated-breakpiont-list-item {
65 | border-bottom: 1px solid #eee;
66 | padding: 10px;
67 | }
68 | .activated-breakpiont-list-item .delete {
69 | float: right;
70 | color: #777;
71 | font-size: 24px;
72 | line-height: 30px;
73 | text-align: center;
74 | width: 35px;
75 | height: 35px;
76 | padding: 0;
77 | background: none;
78 | border: none;
79 | border-radius: 550px;
80 | cursor: pointer;
81 | }
82 | .activated-breakpiont-list-item:active {
83 | outline: none;
84 | }
85 | .activated-breakpiont-list-item .delete:hover {
86 | color: red;
87 | }
88 |
89 | .breakpoint-type-selector__option {
90 | display: inline-block;
91 | margin-right: 4px;
92 | padding: 4px;
93 | padding-top: 2px;
94 | padding-bottom: 2px;
95 | border: 1px solid #ccc;
96 | cursor: pointer;
97 | border-radius: 4px;
98 | }
99 | .breakpoint-type-selector__option:hover {
100 | background: red !important;
101 | border-color: red !important;
102 | color: white !important;
103 | }
104 |
105 | .left-column-header {
106 | background: #698cfe;
107 | color: white;
108 | padding: 10px;
109 | font-weight: bold;
110 | border-bottom: 1px solid #eee;
111 | }
112 |
113 | .code-section {
114 | max-width: 100%;
115 | overflow: auto;
116 | padding: 10px;
117 | background: #eee;
118 | }
119 | .code-section pre {
120 | font-family: Arial;
121 | padding-bottom: 2px;
122 | margin: 0;
123 | }
124 |
125 | .available-breakpoints-search-input {
126 | float: right;
127 | margin-top: 8px;
128 | margin-right: 10px;
129 | background: #eee;
130 | border: none;
131 | padding: 3px;
132 | }
133 |
--------------------------------------------------------------------------------
/extension/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/extension/show-panel.js:
--------------------------------------------------------------------------------
1 | chrome.devtools.panels.create("Breakpoints",
2 | null,
3 | "panel.html",
4 | function(panel) {
5 |
6 | }
7 | );
8 |
9 | window.addEventListener("RebroadcastExtensionMessage", function(evt) {
10 | chrome.runtime.sendMessage(evt.detail);
11 | }, false);
12 |
--------------------------------------------------------------------------------
/gh-pages/bookmarklet.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | JS Breakpoint Collection - Bookmarklet
5 |
6 |
7 |
24 | JS Breakpoint Collection — Bookmarklet
25 | Add the link below to your bookmarks.
26 | After clicking on it you'll have access to the breakpoints
object in the console.
27 |
28 |
29 |
30 | JS Breakpoint Collection
33 |
34 |
35 |
36 |
37 | API documentation
38 |
39 |
40 | Return to project page on Github
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/gh-pages/chrome-web-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/gh-pages/chrome-web-store.png
--------------------------------------------------------------------------------
/gh-pages/expand-stack-trace-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/gh-pages/expand-stack-trace-button.png
--------------------------------------------------------------------------------
/gh-pages/expanded-stack-trace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/gh-pages/expanded-stack-trace.png
--------------------------------------------------------------------------------
/gh-pages/expanded-trace-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/gh-pages/expanded-trace-message.png
--------------------------------------------------------------------------------
/gh-pages/index.html:
--------------------------------------------------------------------------------
1 | jbc test
2 | aa
3 |
--------------------------------------------------------------------------------
/gh-pages/live-demo-copied-code/javascript-breakpoint-collection.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory();
4 | else if(typeof define === 'function' && define.amd)
5 | define([], factory);
6 | else {
7 | var a = factory();
8 | for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
9 | }
10 | })(this, function() {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 | /******/
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 | /******/
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ({
54 |
55 | /***/ 0:
56 | /***/ function(module, exports, __webpack_require__) {
57 |
58 | "use strict";
59 |
60 | var _consoleInterface = __webpack_require__(165);
61 |
62 | var _consoleInterface2 = _interopRequireDefault(_consoleInterface);
63 |
64 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
65 |
66 | module.exports = _consoleInterface2.default;
67 | var isInBrowser = typeof window !== "undefined";
68 | if (isInBrowser) {
69 | if (window.breakpoints !== undefined) {
70 | if (!window.breakpoints.__internal || !window.breakpoints.__internal.isBreakpointCollectionExtension) {
71 | console.log("Breakpoints extension can't load, global `breakpoints` variable is already defined");
72 | }
73 | } else {
74 | window.breakpoints = _consoleInterface2.default;
75 |
76 | (0, _consoleInterface.pushRegisteredBreakpointsToExtension)();
77 | }
78 | }
79 |
80 | /***/ },
81 |
82 | /***/ 161:
83 | /***/ function(module, exports) {
84 |
85 | "use strict";
86 |
87 | Object.defineProperty(exports, "__esModule", {
88 | value: true
89 | });
90 | exports.default = [{
91 | title: "debugScroll",
92 | debugCalls: [{
93 | obj: "window",
94 | prop: "scrollTo"
95 | }, {
96 | obj: "window",
97 | prop: "scrollBy"
98 | }],
99 | debugPropertySets: [{
100 | obj: "document.body",
101 | prop: "scrollTop"
102 | }, {
103 | obj: "document.body",
104 | prop: "scrollLeft"
105 | }, {
106 | obj: "Element.prototype",
107 | prop: "scrollTop"
108 | }, {
109 | obj: "Element.prototype",
110 | prop: "scrollLeft"
111 | }],
112 | getTraceInfo: function getTraceInfo(details) {
113 | if (details.propertyName == "scrollTo" || details.propertyName == "scrollBy") {
114 | return ["The scroll position of the window was changed by a window." + details.propertyName + " call with", details.callArguments];
115 | }
116 | return ["The scroll position of", details.thisArgument, "was changed by setting the " + details.propertyName + " property to " + details.newPropertyValue];
117 | }
118 | }, {
119 | title: "debugCookieReads",
120 | debugPropertyGets: [{
121 | obj: "document",
122 | prop: "cookie"
123 | }],
124 | traceMessage: "Reading cookie contents"
125 | }, {
126 | title: "debugCookieWrites",
127 | debugPropertySets: [{
128 | obj: "document",
129 | prop: "cookie"
130 | }],
131 | traceMessage: "Updating cookie contents"
132 | }, {
133 | title: "debugAlertCalls",
134 | debugCalls: [{
135 | obj: "window",
136 | prop: "alert"
137 | }],
138 | traceMessage: "Showing alert box"
139 | }, {
140 | title: "debugElementSelection",
141 | debugCalls: [{
142 | obj: "document",
143 | prop: "getElementById"
144 | }, {
145 | obj: "document",
146 | prop: "getElementsByClassName"
147 | }, {
148 | obj: "document",
149 | prop: "getElementsByName"
150 | }, {
151 | obj: "document",
152 | prop: "getElementsByTagName"
153 | }, {
154 | obj: "document",
155 | prop: "getElementsByTagNameNS"
156 | }, {
157 | obj: "document",
158 | prop: "getElementsByClassName"
159 | }, {
160 | obj: "document",
161 | prop: "querySelector"
162 | }, {
163 | obj: "document",
164 | prop: "querySelectorAll"
165 | }, {
166 | obj: "document",
167 | prop: "evaluate" // xpath
168 | }],
169 | getTraceInfo: function getTraceInfo(details) {
170 | return ["Selecting DOM elements \"" + details.callArguments[0] + "\" using " + details.propertyName];
171 | }
172 | }, {
173 | title: "debugConsoleErrorCalls",
174 | debugCalls: [{
175 | obj: "window.console",
176 | prop: "error"
177 | }],
178 | traceMessage: "Calling console.error"
179 | }, {
180 | title: "debugConsoleLogCalls",
181 | debugCalls: [{
182 | obj: "window.console",
183 | prop: "log"
184 | }],
185 | traceMessage: "Calling console.log"
186 | }, {
187 | title: "debugConsoleTraceCalls",
188 | debugCalls: [{
189 | obj: "window.console",
190 | prop: "trace"
191 | }],
192 | traceMessage: "Calling console.trace"
193 | }, {
194 | title: "debugMathRandom",
195 | debugCalls: [{
196 | obj: "window.Math",
197 | prop: "random"
198 | }],
199 | getTraceInfo: function getTraceInfo() {
200 | return ["Calling Math.random"];
201 | }
202 | }, {
203 | title: "debugTimerCreation",
204 | debugCalls: [{
205 | obj: "window",
206 | prop: "setTimeout"
207 | }, {
208 | obj: "window",
209 | prop: "setInterval"
210 | }],
211 | getTraceInfo: function getTraceInfo(details) {
212 | return ["Creating timer using " + details.propertyName];
213 | }
214 | }, {
215 | title: "debugLocalStorageReads",
216 | debugCalls: [{
217 | obj: "window.localStorage",
218 | prop: "getItem"
219 | }],
220 | getTraceInfo: function getTraceInfo(details) {
221 | return ["Reading localStorage data for key \"" + details.callArguments[0] + "\""];
222 | }
223 | }, {
224 | title: "debugLocalStorageWrites",
225 | debugCalls: [{
226 | obj: "window.localStorage",
227 | prop: "setItem"
228 | }, {
229 | obj: "window.localStorage",
230 | prop: "clear"
231 | }],
232 | getTraceInfo: function getTraceInfo(details) {
233 | if (details.propertyName == "clear") {
234 | return ["Clearing all localStorage data"];
235 | }
236 | return ["Writing localStorage data for key \"" + details.callArguments[0] + "\""];
237 | }
238 | }];
239 |
240 | /***/ },
241 |
242 | /***/ 165:
243 | /***/ function(module, exports, __webpack_require__) {
244 |
245 | "use strict";
246 |
247 | Object.defineProperty(exports, "__esModule", {
248 | value: true
249 | });
250 | exports.pushRegisteredBreakpointsToExtension = undefined;
251 |
252 | var _debugObj = __webpack_require__(166);
253 |
254 | var _debugObj2 = _interopRequireDefault(_debugObj);
255 |
256 | var _predefinedBreakpoints = __webpack_require__(161);
257 |
258 | var _predefinedBreakpoints2 = _interopRequireDefault(_predefinedBreakpoints);
259 |
260 | var _getCallbackFromUserFriendlyCallbackArgument = __webpack_require__(167);
261 |
262 | var _getCallbackFromUserFriendlyCallbackArgument2 = _interopRequireDefault(_getCallbackFromUserFriendlyCallbackArgument);
263 |
264 | var _breakpointCombinations = __webpack_require__(168);
265 |
266 | var _breakpointCombinations2 = _interopRequireDefault(_breakpointCombinations);
267 |
268 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
269 |
270 | function pushRegisteredBreakpointsToExtension() {
271 | if (typeof CustomEvent === "undefined" || typeof window === "undefined" || !window.dispatchEvent) {
272 | return; // probably in a Node environment
273 | }
274 | var event = new CustomEvent("RebroadcastExtensionMessage", {
275 | type: "updateRegisteredBreakpoints",
276 | registeredBreakpoints: _breakpointCombinations2.default.getRegisteredBreakpoints()
277 | });
278 | window.dispatchEvent(event);
279 | }
280 |
281 | var __internal = {
282 | updateBreakpointType: function updateBreakpointType(id, newType) {
283 | _breakpointCombinations2.default.updateType(id, newType);
284 | pushRegisteredBreakpointsToExtension();
285 | },
286 | disableBreakpoint: function disableBreakpoint(id) {
287 | _breakpointCombinations2.default.disable(id);
288 | pushRegisteredBreakpointsToExtension();
289 | },
290 | registerBreakpoint: function registerBreakpoint() {
291 | var id = _breakpointCombinations2.default.register.apply(null, arguments);
292 | pushRegisteredBreakpointsToExtension();
293 | return id;
294 | },
295 | isBreakpointCollectionExtension: true,
296 | debug: {
297 | debugObj: _debugObj2.default,
298 | debugObjBreakpointRegistry: _debugObj.debugObjBreakpointRegistry,
299 | objectsAndPropsByDebugId: _debugObj.objectsAndPropsByDebugId
300 | },
301 | getRegisteredBreakpoints: function getRegisteredBreakpoints() {
302 | return _breakpointCombinations2.default.getRegisteredBreakpoints();
303 | },
304 | setTypeOfMostRecentBreakpointToDebugger: function setTypeOfMostRecentBreakpointToDebugger() {
305 | _breakpointCombinations2.default.setTypeOfMostRecentBreakpointToDebugger();
306 | pushRegisteredBreakpointsToExtension();
307 | }
308 | };
309 |
310 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(__internal);
311 |
312 | function publicDebugPropertyAccess(obj, prop, callback, accessType) {
313 | var functionName = {
314 | "get": "debugPropertyGet",
315 | "set": "debugPropertySet",
316 | "call": "debugPropertyCall"
317 | }[accessType];
318 |
319 | callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(callback);
320 | __internal.registerBreakpoint(function (debugPropertyGet, debugPropertySet, debugPropertyCall) {
321 | var debugFunctions = {
322 | debugPropertyGet: debugPropertyGet,
323 | debugPropertySet: debugPropertySet,
324 | debugPropertyCall: debugPropertyCall
325 | };
326 | debugFunctions[functionName](obj, prop, callback);
327 | }, {
328 | title: functionName + " (" + prop + ")",
329 | type: callback.callbackType
330 | });
331 | }
332 |
333 | var breakpoints = {
334 | debugPropertyGet: function debugPropertyGet(obj, prop, callback) {
335 | return publicDebugPropertyAccess(obj, prop, callback, "get");
336 | },
337 | debugPropertySet: function debugPropertySet(obj, prop, callback) {
338 | return publicDebugPropertyAccess(obj, prop, callback, "set");
339 | },
340 | debugPropertyCall: function debugPropertyCall(obj, prop, callback) {
341 | return publicDebugPropertyAccess(obj, prop, callback, "call");
342 | },
343 | resetAllBreakpoints: function resetAllBreakpoints() {
344 | _breakpointCombinations2.default.resetAll();
345 | pushRegisteredBreakpointsToExtension();
346 | },
347 | resetLastBreakpoint: function resetLastBreakpoint() {
348 | if (_breakpointCombinations2.default.getRegisteredBreakpoints().length === 0) {
349 | console.log("No breakpoints are currently registered");
350 | return;
351 | }
352 | _breakpointCombinations2.default.resetLastBreakpoint();
353 | pushRegisteredBreakpointsToExtension();
354 | },
355 | __internal: __internal
356 | };
357 |
358 | _predefinedBreakpoints2.default.forEach(function (breakpoint) {
359 | breakpoints[breakpoint.title] = function (callback) {
360 | callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(callback, breakpoint);
361 |
362 | var details = {
363 | title: breakpoint.title,
364 | type: callback.callbackType
365 | };
366 |
367 | var fn = function fn(debugPropertyGet, debugPropertySet, debugPropertyCall) {
368 | if (breakpoint.debugPropertyGets) {
369 | breakpoint.debugPropertyGets.forEach(function (property) {
370 | debugPropertyGet(eval(property.obj), property.prop, callback);
371 | });
372 | }
373 | if (breakpoint.debugPropertySets) {
374 | breakpoint.debugPropertySets.forEach(function (property) {
375 | debugPropertySet(eval(property.obj), property.prop, callback);
376 | });
377 | }
378 | if (breakpoint.debugCalls) {
379 | breakpoint.debugCalls.forEach(function (property) {
380 | debugPropertyCall(eval(property.obj), property.prop, callback);
381 | });
382 | }
383 | };
384 |
385 | __internal.registerBreakpoint(fn, details, breakpoint);
386 | pushRegisteredBreakpointsToExtension();
387 | };
388 | });
389 |
390 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(breakpoints);
391 |
392 | exports.default = breakpoints;
393 | exports.pushRegisteredBreakpointsToExtension = pushRegisteredBreakpointsToExtension;
394 |
395 | /***/ },
396 |
397 | /***/ 166:
398 | /***/ function(module, exports) {
399 |
400 | "use strict";
401 |
402 | Object.defineProperty(exports, "__esModule", {
403 | value: true
404 | });
405 |
406 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
407 |
408 | exports.runWithBreakpointsDisabled = runWithBreakpointsDisabled;
409 | exports.disableBreakpointsDuringAllFunctionCalls = disableBreakpointsDuringAllFunctionCalls;
410 | exports.default = debugObj;
411 | exports.updateDebugIdCallback = updateDebugIdCallback;
412 | exports.resetDebug = resetDebug;
413 | var registry = new Map();
414 | var objectsAndPropsByDebugId = {};
415 |
416 | var hookNames = ["propertyGetBefore", "propertyGetAfter", "propertySetBefore", "propertySetAfter", "propertyCallBefore", "propertyCallAfter"];
417 |
418 | function getPropertyDescriptor(object, propertyName) {
419 | try {
420 | var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
421 | } catch (err) {
422 | var newError = Error("Are you sure the property \"" + propertyName + "\" exists?");
423 | newError.originalError = err;
424 | throw newError;
425 | }
426 | if (!object) {
427 | throw new Error("Descriptor " + propertyName + " not found");
428 | }
429 | if (!descriptor) {
430 | return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
431 | }
432 | return descriptor;
433 | }
434 |
435 | exports.debugObjBreakpointRegistry = registry;
436 | exports.objectsAndPropsByDebugId = objectsAndPropsByDebugId;
437 |
438 | // counter instead of boolean to allow nested calls of runWithBreakpointsDisabled
439 |
440 | var timesBreakpointsWereDisabled = 0;
441 | function runWithBreakpointsDisabled(fn) {
442 | timesBreakpointsWereDisabled++;
443 | var retVal = fn();
444 | timesBreakpointsWereDisabled--;
445 | return retVal;
446 | }
447 |
448 | function disableBreakpointsDuringAllFunctionCalls(object) {
449 | var _loop = function _loop() {
450 | var fn = object[functionName];
451 | if (typeof fn !== "function") {
452 | return "continue";
453 | }
454 |
455 | object[functionName] = function () {
456 | var thisArg = this;
457 | var args = arguments;
458 | return runWithBreakpointsDisabled(function () {
459 | return fn.apply(thisArg, args);
460 | });
461 | };
462 | };
463 |
464 | for (var functionName in object) {
465 | var _ret = _loop();
466 |
467 | if (_ret === "continue") continue;
468 | }
469 | }
470 |
471 | function areBreakpointsDisabled() {
472 | return timesBreakpointsWereDisabled > 0;
473 | }
474 |
475 | function debugObj(obj, prop, hooks) {
476 | var debugId = Math.floor(Math.random() * 100000000000).toString();
477 | objectsAndPropsByDebugId[debugId] = {
478 | obj: obj,
479 | prop: prop
480 | };
481 |
482 | if (registry.get(obj) === undefined) {
483 | registry.set(obj, {});
484 | }
485 |
486 | if (registry.get(obj)[prop] === undefined) {
487 | registry.get(obj)[prop] = { hooks: {} };
488 |
489 | var originalProp = getPropertyDescriptor(obj, prop);
490 | var isSimpleValue = "value" in originalProp; // rather than getter + setter
491 |
492 | Object.defineProperty(obj, prop, {
493 | get: function get() {
494 | var retVal;
495 |
496 | triggerHook("propertyGetBefore", {
497 | thisArgument: this
498 | });
499 |
500 | if (isSimpleValue) {
501 | retVal = originalProp.value;
502 | } else {
503 | retVal = originalProp.get.apply(this, arguments);
504 | }
505 |
506 | triggerHook("propertyGetAfter", {
507 | thisArgument: this
508 | });
509 |
510 | if (typeof retVal === "function") {
511 | return function () {
512 | var args = Array.prototype.slice.call(arguments);
513 |
514 | triggerHook("propertyCallBefore", {
515 | callArguments: args,
516 | thisArgument: this
517 | });
518 |
519 | var fnRetVal = retVal.apply(this, arguments);
520 |
521 | triggerHook("propertyCallAfter", {
522 | callArguments: args,
523 | thisArgument: this
524 | });
525 |
526 | return fnRetVal;
527 | };
528 | }
529 |
530 | return retVal;
531 | },
532 | set: function set(newValue) {
533 | var retVal;
534 |
535 | triggerHook("propertySetBefore", {
536 | newPropertyValue: newValue,
537 | thisArgument: this
538 | });
539 |
540 | if (isSimpleValue) {
541 | retVal = originalProp.value = newValue;
542 | } else {
543 | retVal = originalProp.set.apply(this, arguments);
544 | }
545 |
546 | triggerHook("propertySetAfter", {
547 | newPropertyValue: newValue,
548 | thisArgument: this
549 | });
550 |
551 | return retVal;
552 | }
553 | });
554 | }
555 |
556 | hookNames.forEach(function (hookName) {
557 | if (hooks[hookName] !== undefined) {
558 | if (registry.get(obj)[prop].hooks[hookName] === undefined) {
559 | registry.get(obj)[prop].hooks[hookName] = [];
560 | }
561 | var hook = hooks[hookName];
562 | registry.get(obj)[prop].hooks[hookName].push({
563 | id: debugId,
564 | fn: hook.fn,
565 | data: hook.data
566 | });
567 | }
568 | });
569 |
570 | return debugId;
571 |
572 | function triggerHook(hookName, additionalHookInfo) {
573 | if (areBreakpointsDisabled()) {
574 | return;
575 | }
576 | var hooks = registry.get(obj)[prop].hooks;
577 | var hooksWithName = hooks[hookName];
578 |
579 | var infoForHook = _extends({
580 | object: obj,
581 | propertyName: prop
582 | }, additionalHookInfo);
583 |
584 | if (hooksWithName !== undefined && hooksWithName.length > 0) {
585 | hooksWithName.forEach(function (hook) {
586 | hook.fn(_extends({}, infoForHook, {
587 | accessType: getAccessTypeFromHookName(hookName),
588 | data: hook.data
589 | }));
590 | });
591 | }
592 | }
593 | }
594 |
595 | function getAccessTypeFromHookName(hookName) {
596 | var accessType = "";
597 | if (hookName === "propertyGetBefore" || hookName === "propertyGetAfter") {
598 | accessType = "get";
599 | }
600 | if (hookName === "propertySetBefore" || hookName === "propertySetAfter") {
601 | accessType = "set";
602 | }
603 | if (hookName === "propertyCallBefore" || hookName === "propertyCallAfter") {
604 | accessType = "call";
605 | }
606 | return accessType;
607 | }
608 |
609 | function updateEachHook(obj, prop, cb) {
610 | var hooks = registry.get(obj)[prop].hooks;
611 | hookNames.forEach(function (hookName) {
612 | var hooksWithName = hooks[hookName];
613 | if (hooksWithName !== undefined) {
614 | hooks[hookName] = hooksWithName.map(function (hook) {
615 | return cb(hook);
616 | });
617 | }
618 | });
619 | }
620 |
621 | function updateDebugIdCallback(debugId, callback) {
622 | var objAndProp = objectsAndPropsByDebugId[debugId];
623 | updateEachHook(objAndProp.obj, objAndProp.prop, function (hook) {
624 | if (hook.id === debugId) {
625 | return {
626 | id: debugId,
627 | fn: callback,
628 | data: hook.data
629 | };
630 | } else {
631 | return hook;
632 | }
633 | });
634 | }
635 |
636 | function resetDebug(id) {
637 | var objAndProp = objectsAndPropsByDebugId[id];
638 | var hooks = registry.get(objAndProp.obj)[objAndProp.prop].hooks;
639 | for (var hookName in hooks) {
640 | var hooksWithName = hooks[hookName];
641 | hooks[hookName] = hooksWithName.filter(function (hook) {
642 | return hook.id != id;
643 | });
644 | }
645 |
646 | delete objectsAndPropsByDebugId[id];
647 | }
648 |
649 | /***/ },
650 |
651 | /***/ 167:
652 | /***/ function(module, exports, __webpack_require__) {
653 |
654 | "use strict";
655 |
656 | Object.defineProperty(exports, "__esModule", {
657 | value: true
658 | });
659 |
660 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
661 |
662 | exports.default = getCallbackFromUserFriendlyCallbackArgument;
663 |
664 | var _debugObj = __webpack_require__(166);
665 |
666 | function getDebuggerFunction() {
667 | var debuggerFunc = function debuggerFunc() {
668 | debugger;
669 | };
670 | debuggerFunc.callbackType = "debugger";
671 | return debuggerFunc;
672 | }
673 |
674 | function getCallbackFromUserFriendlyCallbackArgument(callback, predefinedBreakpoint) {
675 | if (typeof callback === "function") {
676 | callback.callbackType = "custom";
677 | return callback;
678 | } else if (typeof callback === "string") {
679 | if (callback === "debugger") {
680 | return getDebuggerFunction();
681 | } else if (callback === "trace") {
682 | return getTraceFunction(predefinedBreakpoint);
683 | } else {
684 | throw new Error("Invalid string callback");
685 | }
686 | } else if (typeof callback === "undefined") {
687 | return getDebuggerFunction();
688 | } else {
689 | throw new Error("Invalid callback type: " + (typeof callback === "undefined" ? "undefined" : _typeof(callback)));
690 | }
691 | }
692 |
693 | function getTraceFunction(predefinedBreakpoint) {
694 | var traceFn;
695 | if (predefinedBreakpoint) {
696 | if (predefinedBreakpoint.getTraceInfo) {
697 | traceFn = function traceFn() {
698 | var traceArgs = predefinedBreakpoint.getTraceInfo.apply(null, arguments);
699 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
700 | console.trace.apply(console, traceArgs);
701 | });
702 | };
703 | } else if (predefinedBreakpoint.traceMessage) {
704 | traceFn = function traceFn() {
705 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
706 | console.trace(predefinedBreakpoint.traceMessage);
707 | });
708 | };
709 | }
710 | } else {
711 | traceFn = function traceFn(debugInfo) {
712 | (0, _debugObj.runWithBreakpointsDisabled)(function () {
713 | console.trace("About to " + debugInfo.accessType + " property '" + debugInfo.propertyName + "' on this object: ", debugInfo.object);
714 | });
715 | };
716 | }
717 |
718 | traceFn.callbackType = "trace";
719 | return traceFn;
720 | }
721 |
722 | /***/ },
723 |
724 | /***/ 168:
725 | /***/ function(module, exports, __webpack_require__) {
726 |
727 | "use strict";
728 |
729 | Object.defineProperty(exports, "__esModule", {
730 | value: true
731 | });
732 |
733 | var _debugObj = __webpack_require__(166);
734 |
735 | var _debugObj2 = _interopRequireDefault(_debugObj);
736 |
737 | var _getCallbackFromUserFriendlyCallbackArgument = __webpack_require__(167);
738 |
739 | var _getCallbackFromUserFriendlyCallbackArgument2 = _interopRequireDefault(_getCallbackFromUserFriendlyCallbackArgument);
740 |
741 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
742 |
743 | var registeredBreakpoints = [];
744 |
745 | function debugPropertyCall(object, prop, callback) {
746 | return (0, _debugObj2.default)(object, prop, {
747 | propertyCallBefore: {
748 | fn: callback
749 | }
750 | });
751 | }
752 |
753 | var debugPropertyGet = function debugPropertyGet(object, propertyName, callback) {
754 | return (0, _debugObj2.default)(object, propertyName, {
755 | propertyGetBefore: {
756 | fn: callback
757 | }
758 | });
759 | };
760 | var debugPropertySet = function debugPropertySet(object, propertyName, callback) {
761 | return (0, _debugObj2.default)(object, propertyName, {
762 | propertySetBefore: {
763 | fn: callback
764 | }
765 | });
766 | };
767 |
768 | var breakpointCombinations = {
769 | register: function register(fn, bpDetails, predefinedBreakpoint) {
770 | var debugIds = [];
771 | var _debugPropertyGet = function _debugPropertyGet(object, propertyName, callback) {
772 | debugIds.push(debugPropertyGet(object, propertyName, callback));
773 | };
774 | var _debugPropertySet = function _debugPropertySet(object, propertyName, callback) {
775 | debugIds.push(debugPropertySet(object, propertyName, callback));
776 | };
777 | var _debugPropertyCall = function _debugPropertyCall(object, propertyName, callback) {
778 | debugIds.push(debugPropertyCall(object, propertyName, callback));
779 | };
780 | fn(_debugPropertyGet, _debugPropertySet, _debugPropertyCall);
781 |
782 | var id = Math.floor(Math.random() * 1000000000);
783 | var bp = {
784 | id: id,
785 | debugIds: debugIds,
786 | details: bpDetails,
787 | createdAt: new Date(),
788 | predefinedBreakpoint: predefinedBreakpoint
789 | };
790 | registeredBreakpoints.push(bp);
791 |
792 | return id;
793 | },
794 | disable: function disable(id) {
795 | var bp = registeredBreakpoints.filter(function (bp) {
796 | return bp.id == id;
797 | })[0];
798 | if (bp === undefined) {
799 | console.log("Couldn't find breakpoint with id", id);
800 | return;
801 | }
802 | bp.debugIds.forEach(function (debugId) {
803 | (0, _debugObj.resetDebug)(debugId);
804 | });
805 | registeredBreakpoints = registeredBreakpoints.filter(function (bp) {
806 | return bp.id != id;
807 | });
808 | },
809 | updateType: function updateType(id, newType) {
810 | if (newType !== "debugger" && newType !== "trace") {
811 | throw new Error("Invalid breakpoint type");
812 | }
813 |
814 | var bp = registeredBreakpoints.filter(function (bp) {
815 | return bp.id == id;
816 | })[0];
817 |
818 | var callback = (0, _getCallbackFromUserFriendlyCallbackArgument2.default)(newType, bp.predefinedBreakpoint);
819 | bp.debugIds.forEach(function (debugId) {
820 | (0, _debugObj.updateDebugIdCallback)(debugId, callback);
821 | });
822 |
823 | bp.details.type = newType;
824 | },
825 | resetAll: function resetAll() {
826 | registeredBreakpoints.forEach(function (breakpoint) {
827 | breakpointCombinations.disable(breakpoint.id);
828 | });
829 | },
830 | getRegisteredBreakpoints: function getRegisteredBreakpoints() {
831 | return registeredBreakpoints;
832 | },
833 | resetLastBreakpoint: function resetLastBreakpoint() {
834 | var breakpointToReset = registeredBreakpoints[registeredBreakpoints.length - 1];
835 | breakpointCombinations.disable(breakpointToReset.id);
836 | },
837 | setTypeOfMostRecentBreakpointToDebugger: function setTypeOfMostRecentBreakpointToDebugger() {
838 | var mostRecentBreakpoint = registeredBreakpoints[registeredBreakpoints.length - 1];
839 | breakpointCombinations.updateType(mostRecentBreakpoint.id, "debugger");
840 | }
841 | };
842 |
843 | (0, _debugObj.disableBreakpointsDuringAllFunctionCalls)(breakpointCombinations);
844 |
845 | exports.default = breakpointCombinations;
846 |
847 | /***/ }
848 |
849 | /******/ })
850 | });
851 | ;
852 | //# sourceMappingURL=javascript-breakpoint-collection.js.map
--------------------------------------------------------------------------------
/gh-pages/live-demo-copied-code/panel.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 | h2 {
5 | padding: 10px;
6 | font-weight: normal;
7 | padding-top: 7px;
8 | padding-bottom: 6px;
9 | margin: 0;
10 | background: #fafafa;
11 | border-bottom: 1px solid #eee
12 | }
13 | body {
14 | margin: 0;
15 | font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif;
16 | font-size: 75%;
17 | }
18 | a {
19 | color: #698CFE;
20 | }
21 | .col-parent > div {
22 | width: 33.3333%;
23 | float: left;
24 | border-right: 1px solid #eee;
25 | height: 100%;
26 | }
27 | .col-parent > div:first-child {
28 | background: #fafafa;
29 | }
30 | .col-parent > div:last-child {
31 | border-right: none;
32 | }
33 | .col-parent .col-content {
34 | padding: 10px;
35 | padding-top: 0px;
36 | height: calc(100% - 36px);
37 | overflow: auto;
38 | }
39 | .unactivated-breakpoint-list-item {
40 | border-bottom: 1px solid #eee;
41 | padding: 10px;
42 | cursor: pointer;
43 | }
44 | .unactivated-breakpoint-list-item:hover {
45 | background: #fafafa;
46 | }
47 | .unactivated-breakpoint-list-item .plus {
48 | display: none;
49 |
50 | float: right;
51 | font-size: 24px;
52 | color: red;
53 | width: 20px;
54 | height: 20px;
55 | text-align: center;
56 | margin-top: -3px;
57 | line-height: 16px;
58 | }
59 | .unactivated-breakpoint-list-item:hover .plus{
60 | display: inline-block;
61 | }
62 |
63 | .activated-breakpiont-list-item {
64 | border-bottom: 1px solid #eee;
65 | padding: 10px;
66 | }
67 | .activated-breakpiont-list-item .delete {
68 | float: right;
69 | color: #777;
70 | font-size: 24px;
71 | line-height: 30px;
72 | text-align: center;
73 | width: 35px;
74 | height: 35px;
75 | padding: 0;
76 | background: none;
77 | border: none;
78 | border-radius: 550px;
79 | cursor: pointer;
80 | }
81 | .activated-breakpiont-list-item:active {
82 | outline: none;
83 | }
84 | .activated-breakpiont-list-item .delete:hover {
85 | color: red;
86 | }
87 |
88 | .breakpoint-type-selector__option {
89 | display: inline-block;
90 | margin-right: 4px;
91 | padding: 4px;
92 | padding-top: 2px;
93 | padding-bottom: 2px;
94 | border: 1px solid #ccc;
95 | cursor: pointer;
96 | border-radius: 4px;
97 | }
98 | .breakpoint-type-selector__option:hover {
99 | background: red !important;
100 | border-color: red !important;
101 | color: white !important;
102 | }
103 |
104 | .left-column-header {
105 | background: #698CFE;
106 | color: white;
107 | padding: 10px;
108 | font-weight: bold;
109 | border-bottom: 1px solid #eee;
110 | }
111 |
112 | .code-section {
113 | max-width: 100%;
114 | overflow: auto;
115 | padding: 10px;
116 | background: #eee;
117 | }
118 | .code-section pre {
119 | font-family: Arial;
120 | padding-bottom: 2px;
121 | margin: 0;
122 | }
123 |
124 | .available-breakpoints-search-input {
125 | float: right;
126 | margin-top: 8px;
127 | margin-right: 10px;
128 | background: #eee;
129 | border: none;
130 | padding: 3px;
131 | }
132 |
--------------------------------------------------------------------------------
/gh-pages/live-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | JS Breakpoint Collection - Live Demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
28 | Find the code that's causing the scroll change in the box on the right
29 |
30 |
31 | Open Chrome's DevTools so you can see the console
32 | Click on debugScroll
in the list of breakpoints below
33 | A trace message will appear every time the scroll position is updated
34 | Expand the trace message to see the stack trace
35 | ( )
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus fermentum justo sit amet ullamcorper porta. In viverra mattis diam, non accumsan tellus accumsan a. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis egestas odio vitae urna fringilla lobortis. Sed ut gravida mauris. Sed laoreet leo id enim ornare imperdiet. Ut venenatis sem sit amet odio posuere finibus. Maecenas elementum est eget libero auctor, eget eleifend libero hendrerit. Integer non risus imperdiet ipsum convallis dapibus. Quisque finibus orci a maximus viverra. Aliquam ante lectus, ornare non purus eget, finibus ultrices ante. Etiam eu elit vulputate, rhoncus elit et, hendrerit ex.
45 |
46 |
62 |
63 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/injected-script.js:
--------------------------------------------------------------------------------
1 | import consoleInterface , {pushRegisteredBreakpointsToExtension} from "./breakpoints/consoleInterface"
2 |
3 | module.exports = consoleInterface;
4 | var isInBrowser = typeof window !== "undefined";
5 | if (isInBrowser) {
6 | if (window.breakpoints !== undefined) {
7 | if (!window.breakpoints.__internal || !window.breakpoints.__internal.isBreakpointCollectionExtension) {
8 | console.log("Breakpoints extension can't load, global `breakpoints` variable is already defined")
9 | }
10 | } else {
11 | window.breakpoints = consoleInterface;
12 |
13 | pushRegisteredBreakpointsToExtension();
14 |
15 | if (window.__BP_SHOW_CONSOLE_API_MESSAGE){
16 | delete window.__BP_SHOW_CONSOLE_API_MESSAGE;
17 | console.log("Breakpoints Collection API docs: https://github.com/mattzeunert/javascript-breakpoint-collection/blob/master/console-api.md")
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | module.exports = function (config) {
4 | var configuration = {
5 | browsers: ['Chrome'],
6 | singleRun: false,
7 | frameworks: ['jasmine'],
8 | files: ['webpack-test.config.js',
9 | { pattern: 'extension/build/javascript-breakpoint-collection.js', included: true, watched: true }
10 | ],
11 | proxies: {
12 | '/extension/': '/base/extension/'
13 | },
14 | preprocessors: {
15 | 'webpack-test.config.js': ['webpack', 'sourcemap']
16 | },
17 | customLaunchers: {
18 | Chrome_travis_ci: {
19 | base: 'Chrome',
20 | flags: ['--no-sandbox']
21 | }
22 | },
23 | reporters: ['dots'],
24 | webpack: {
25 | module: {
26 | loaders: [
27 | {
28 | test: /\.jsx?$/,
29 | exclude: /node_modules/,
30 | loader: 'babel-loader'
31 | }]
32 | },
33 | watch: true,
34 | resolve: {
35 | extensions: ["", ".js", ".jsx", ".js.jsx"]
36 | },
37 | devtool: 'inline-source-map',
38 | },
39 | webpackServer: {
40 | noInfo: true
41 | }
42 | }
43 | if (process.env.TRAVIS) {
44 | configuration.browsers = ['Chrome_travis_ci'];
45 | }
46 |
47 | config.set(configuration);
48 | };
49 |
--------------------------------------------------------------------------------
/node-test.js:
--------------------------------------------------------------------------------
1 |
2 | console.log("start")
3 | var breakpoints = require("./dist/node.js")
4 | var obj = {"hi": "ho"}
5 | breakpoints.debugPropertyGet(obj, "hi", "trace")
6 | obj.hi
7 | console.log("end")
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-breakpoint-collection",
3 | "version": "1.7.0",
4 | "description": "",
5 | "main": "dist/node.js",
6 | "scripts": {
7 | "test": "karma start",
8 | "build": "webpack",
9 | "ci-test": "karma start --single-run"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/mattzeunert/javascript-breakpoint-collection.git"
14 | },
15 | "author": "Matt Zeunert",
16 | "license": "MIT",
17 | "bugs": {
18 | "url": "https://github.com/mattzeunert/javascript-breakpoint-collection/issues"
19 | },
20 | "homepage": "https://github.com/mattzeunert/javascript-breakpoint-collection#readme",
21 | "dependencies": {},
22 | "devDependencies": {
23 | "babel-core": "^6.7.4",
24 | "babel-loader": "^6.2.4",
25 | "babel-plugin-transform-object-rest-spread": "^6.6.5",
26 | "babel-preset-es2015": "^6.6.0",
27 | "babel-preset-react": "^6.5.0",
28 | "jasmine-core": "^2.4.1",
29 | "karma": "^0.13.22",
30 | "karma-chrome-launcher": "^0.2.3",
31 | "karma-jasmine": "^0.3.8",
32 | "karma-sourcemap-loader": "^0.3.7",
33 | "karma-webpack": "^1.7.0",
34 | "react-addons-test-utils": "^0.14.8",
35 | "uglify-js": "^2.6.2",
36 | "webpack": "^1.12.14",
37 | "react": "^0.14.8",
38 | "react-dom": "^0.14.8"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/app-spec.js:
--------------------------------------------------------------------------------
1 | import AppView from "../app/AppView"
2 | import TestUtils from "react-addons-test-utils"
3 | import React from "react"
4 | import ReactDOM from "react-dom"
5 | import AvailableBreakpointsList from "../app/AvailableBreakpointsList"
6 | import predefinedBreakpoints from "../breakpoints/predefinedBreakpoints"
7 |
8 | describe("Breakpoints UI/App", function(){
9 | var component = TestUtils.renderIntoDocument( );
10 | var node = ReactDOM.findDOMNode(component);
11 |
12 | it("Shows a list of predefined breakpoints", function(){
13 | var availableBreakpointsListComponent = TestUtils.findRenderedComponentWithType(
14 | component,
15 | AvailableBreakpointsList
16 | );
17 | var listNode = ReactDOM.findDOMNode(availableBreakpointsListComponent);
18 | expect(listNode.children.length).toBe(predefinedBreakpoints.length)
19 | })
20 |
21 | it("After adding a breakpoint it appears in the list of activated breakpoints", function(){
22 | var addTraceToCookieReadsButton = node.querySelector("[data-test-marker-available-bp-title='debugCookieReads']")
23 | TestUtils.Simulate.click(addTraceToCookieReadsButton);
24 |
25 | var activatedBreakpoint = node.querySelector("[data-test-marker-activated-bp-title='debugCookieReads']")
26 | expect(activatedBreakpoint).not.toBe(null)
27 | })
28 |
29 | it("Shows a trace message when reading the cookie", function(){
30 | spyOn(console, "trace");
31 | document.cookie;
32 | expect(console.trace).toHaveBeenCalled();
33 | })
34 |
35 | it("Removes the breakpoint when clicking on the delete button", function(){
36 | var deleteButton = node.querySelector("[data-test-marker-delete-bp]");
37 | TestUtils.Simulate.click(deleteButton);
38 | spyOn(console, "trace")
39 | document.cookie;
40 | expect(console.trace).not.toHaveBeenCalled();
41 | })
42 |
43 | it("Doesn't show the breakpoint in the list of activated breakpoint after removing the breakpoint", function(){
44 | var activatedBreakpointElement = node.querySelector("[data-test-marker-activated-bp-title='debugCookieReads']");
45 | expect(activatedBreakpointElement).toBe(null);
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/tests/breakpointCombinations-spec.js:
--------------------------------------------------------------------------------
1 | import breakpointCombinations from "../breakpoints/breakpointCombinations"
2 |
3 | describe("breakpointCombinations", function(){
4 | it("Can register a breakpiontCombination that breaks in multiple situations", function(){
5 | var obj = {hello: "world"}
6 | var propertyGetCallback = jasmine.createSpy();
7 | var propertySetCallback = jasmine.createSpy();
8 | var fn = function(debugPropertyGet, debugPropertySet, debugPropertyCall){
9 | debugPropertyGet(obj, "hello", propertyGetCallback);
10 | debugPropertySet(obj, "hello", propertySetCallback);
11 | }
12 | breakpointCombinations.register(fn, {});
13 |
14 | obj.hello;
15 | expect(propertyGetCallback).toHaveBeenCalled();
16 | expect(propertySetCallback).not.toHaveBeenCalled();
17 |
18 | obj.hello = "hi";
19 | expect(propertySetCallback).toHaveBeenCalled();
20 | })
21 |
22 | it("Can disable a registerered breakpoint", function(){
23 | var obj = {hello: "world"};
24 | var callback = jasmine.createSpy();
25 | var fn = function(debugPropertyGet) {
26 | debugPropertyGet(obj, "hello", callback);
27 | }
28 | var id = breakpointCombinations.register(fn, {});
29 | breakpointCombinations.disable(id);
30 | obj.hello;
31 | expect(callback).not.toHaveBeenCalled();
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/tests/debugObj-spec.js:
--------------------------------------------------------------------------------
1 | import "../extension/build/javascript-breakpoint-collection"
2 | import debugObj, {updateDebugIdCallback} from "../breakpoints/debugObj"
3 |
4 | describe("debugObj", function(){
5 | it("When debugging a function it passes the call arguments and value of `this` into the hook function", function(){
6 | var obj = {
7 | sayHi: function(){}
8 | };
9 | var fn = jasmine.createSpy();
10 | debugObj(obj, "sayHi", {
11 | propertyCallBefore: {
12 | fn: fn
13 | }
14 | })
15 | obj.sayHi(1,2,3)
16 | expect(fn.calls.mostRecent().args[0].callArguments).toEqual([1,2,3])
17 | expect(fn.calls.mostRecent().args[0].thisArgument).toBe(obj)
18 |
19 | });
20 | it("Lets you provide additional data to be passed into the hook function", function(){
21 | var obj = {
22 | sayHi: function(){}
23 | }
24 | var fn = jasmine.createSpy();
25 | debugObj(obj, "sayHi", {
26 | propertyCallBefore: {
27 | fn: fn,
28 | data: {
29 | message: "test"
30 | }
31 | }
32 | })
33 |
34 | obj.sayHi();
35 | expect(fn.calls.mostRecent().args[0].data.message).toEqual("test");
36 | })
37 |
38 | it("Throws an exception if the property that should be debugged doesn't exist", function(){
39 | var obj = {};
40 | expect(function(){
41 | debugObj(obj, "hi", {});
42 | }).toThrowError("Are you sure the property \"hi\" exists?");
43 | })
44 | });
45 |
46 | describe("updateDebugIdCallback", function(){
47 | it("Preserves data after updating a callback", function(){
48 | var obj = {hello: "world"}
49 | var fn = jasmine.createSpy();
50 | var id = debugObj(obj, "hello", {
51 | propertyGetBefore: {fn: fn, data: "test" }
52 | });
53 |
54 | var fn2 = jasmine.createSpy();
55 | updateDebugIdCallback(id, fn2)
56 |
57 | obj.hello
58 | expect(fn).not.toHaveBeenCalled();
59 | expect(fn2).toHaveBeenCalled();
60 | expect(fn2.calls.mostRecent().args[0].data).toBe("test");
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/tests/injected-integration-spec.js:
--------------------------------------------------------------------------------
1 | describe("Injected script integration test", function(){
2 | it("Can update the type of a breakpoint", function(){
3 | var obj = {hello: "world"}
4 | spyOn(console, "trace")
5 | breakpoints.debugPropertyGet(obj, "hello");
6 | var id = breakpoints.__internal.getRegisteredBreakpoints()[0].id;
7 | breakpoints.__internal.updateBreakpointType(id, "trace");
8 | var hi = obj.hello;
9 |
10 | expect(console.trace.calls.mostRecent().args[0]).toBe("About to get property 'hello' on this object: ")
11 | })
12 | });
--------------------------------------------------------------------------------
/tests/predefinedBreakpoints-spec.js:
--------------------------------------------------------------------------------
1 | import "../extension/build/javascript-breakpoint-collection"
2 |
3 | describe("Combining multiple breakpoints on the same object", function(){
4 | it("Can combine setters and getters", function(){
5 | var obj = {
6 | hi: "there"
7 | };
8 | var fn = jasmine.createSpy();
9 | breakpoints.debugPropertyGet(obj, "hi", fn);
10 | breakpoints.debugPropertyGet(obj, "hi", fn);
11 | breakpoints.debugPropertySet(obj, "hi", fn);
12 |
13 | var sth = obj.hi;
14 | expect(fn.calls.count()).toBe(2);
15 |
16 | obj.hi = "hey";
17 | expect(fn.calls.count()).toBe(3);
18 | })
19 | it("Can debug multiple properties of an object", function(){
20 | var obj = {
21 | hi: "there",
22 | hello: "world"
23 | }
24 | var fn = jasmine.createSpy();
25 | breakpoints.debugPropertyGet(obj, "hi", fn);
26 | breakpoints.debugPropertyGet(obj, "hello", fn);
27 |
28 | var sth = obj.hi;
29 | expect(fn.calls.count()).toBe(1);
30 |
31 | sth = obj.hello;
32 | expect(fn.calls.count()).toBe(2);
33 | })
34 | });
35 |
36 | describe("breakpoints.resetAllBreakpoints", function(){
37 | it("Disables all breakpoints", function(){
38 | var obj = {hi: "there"}
39 | var fn1 = jasmine.createSpy();
40 | breakpoints.debugPropertySet(obj, "hi", fn1);
41 |
42 | var fn2 = jasmine.createSpy();
43 | breakpoints.debugCookieReads(fn2);
44 |
45 | breakpoints.resetAllBreakpoints();
46 | var cookie = document.cookie;
47 | obj.hi = "hey";
48 |
49 | expect(fn1).not.toHaveBeenCalled();
50 | expect(fn2).not.toHaveBeenCalled();
51 | });
52 | });
53 |
54 | describe("breakpoints.resetLastBreakpoint", function(){
55 | it("Disables the last breakpoint that was added", function(){
56 | var fn = jasmine.createSpy();
57 | breakpoints.debugCookieReads(fn);
58 | breakpoints.resetLastBreakpoint();
59 | var cookie = document.cookie;
60 | expect(fn).not.toHaveBeenCalled();
61 | })
62 | it("Disables the breakpoints in reverse order", function(){
63 | var fn1 = jasmine.createSpy();
64 | breakpoints.debugCookieReads(fn1);
65 | var fn2 = jasmine.createSpy();
66 | breakpoints.debugCookieWrites(fn2);
67 | breakpoints.resetLastBreakpoint();
68 | document.cookie = "test";
69 | var cookie = document.cookie;
70 | expect(fn2).not.toHaveBeenCalled();
71 | expect(fn1).toHaveBeenCalled();
72 | breakpoints.resetLastBreakpoint();
73 | cookie = document.cookie;
74 | expect(fn1.calls.count()).toBe(1);
75 | })
76 | })
77 |
78 | describe("debugPropertyGet", function(){
79 | it("Calls a callback before a property is read", function(){
80 | var obj = {hi: "there"};
81 | var fn = jasmine.createSpy();
82 | breakpoints.debugPropertyGet(obj, "hi", fn)
83 | var hi = obj.hi;
84 | expect(fn).toHaveBeenCalled();
85 | })
86 |
87 | it("Can show a trace message when a property is read", function(){
88 | var obj = {hi: "there"};
89 | var trace = spyOn(console, "trace")
90 | breakpoints.debugPropertyGet(obj, "hi", "trace");
91 | var hi = obj.hi;
92 | expect(trace).toHaveBeenCalled();
93 | expect(trace.calls.mostRecent().args[0]).toEqual("About to get property 'hi' on this object: ");
94 | });
95 | })
96 |
97 | describe("debugPropertySet", function(){
98 | it("Calls a callback when an object property is set", function(){
99 | var obj = {hi: "there"};
100 | var fn = jasmine.createSpy();
101 | breakpoints.debugPropertySet(obj, "hi", fn);
102 | obj.hi = "hello";
103 | expect(fn).toHaveBeenCalled();
104 | })
105 | it("Can show a trace message when a string property is written", function(){
106 | var obj = {hi: "there"};
107 | var trace = spyOn(console, "trace")
108 | breakpoints.debugPropertySet(obj, "hi", "trace");
109 | obj.hi = "Bob";
110 | expect(trace).toHaveBeenCalled();
111 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'hi' to %o ");
112 | expect(trace.calls.mostRecent().args[1]).toEqual("Bob");
113 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
114 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
115 | });
116 | it("Can show a trace message when a long string property is written", function(){
117 | var obj = {hi: "there"};
118 | var trace = spyOn(console, "trace")
119 | breakpoints.debugPropertySet(obj, "hi", "trace");
120 | obj.hi = "Hubert Blaine Wolfeschlegelsteinhausenbergerdorff";
121 | expect(trace).toHaveBeenCalled();
122 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'hi' to %o ");
123 | expect(trace.calls.mostRecent().args[1]).toEqual("Hubert Blaine Wolfeschleg...");
124 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
125 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
126 | });
127 | it("Can show a trace message when a numeric property is written", function(){
128 | var obj = {myNum: 0};
129 | var trace = spyOn(console, "trace")
130 | breakpoints.debugPropertySet(obj, "myNum", "trace");
131 | obj.myNum = 12345;
132 | expect(trace).toHaveBeenCalled();
133 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'myNum' to %o ");
134 | expect(trace.calls.mostRecent().args[1]).toEqual(12345);
135 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
136 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
137 | });
138 | it("Can show a trace message when a long numeric property is written", function(){
139 | var obj = {myNum: 0};
140 | var trace = spyOn(console, "trace")
141 | breakpoints.debugPropertySet(obj, "myNum", "trace");
142 | obj.myNum = 432423495353284928492304982223432423423;
143 | expect(trace).toHaveBeenCalled();
144 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'myNum' to %o ");
145 | expect(trace.calls.mostRecent().args[1]).toEqual(4.3242349535328495e+38);
146 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
147 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
148 | });
149 | it("Can show a trace message when a boolean property is written", function(){
150 | var obj = {available: false};
151 | var trace = spyOn(console, "trace")
152 | breakpoints.debugPropertySet(obj, "available", "trace");
153 | obj.available = true;
154 | expect(trace).toHaveBeenCalled();
155 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'available' to %o ");
156 | expect(trace.calls.mostRecent().args[1]).toEqual(true);
157 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
158 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
159 | });
160 | it("Can show a trace message when an object property is written", function(){
161 | var obj = {complexObj: {someArray: [{ name: "Bob", age: 45 }, { name: "Sophie", age: 23 }]}};
162 | var trace = spyOn(console, "trace")
163 | breakpoints.debugPropertySet(obj, "complexObj", "trace");
164 | obj.complexObj = {someArray: [{ name: "Bob", age: 45 }, { name: "Sophie", age: 23 }, { name: "Lewis", age:15 }]};
165 | expect(trace).toHaveBeenCalled();
166 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'complexObj' to %o ");
167 | expect(trace.calls.mostRecent().args[1]).toEqual(Object({ someArray: [ Object({ name: 'Bob', age: 45 }), Object({ name: 'Sophie', age: 23 }), Object({ name: 'Lewis', age: 15 }) ] }));
168 | expect(trace.calls.mostRecent().args[2]).toEqual(" on this object: ");
169 | expect(trace.calls.mostRecent().args[3]).toEqual(obj);
170 | });
171 | it("Can show a trace message when an array property is written", function(){
172 | var obj = {myArray: [1,2,3]};
173 | var trace = spyOn(console, "trace")
174 | breakpoints.debugPropertySet(obj, "myArray", "trace");
175 | obj.myArray = [1,2,3,4,5];
176 | expect(trace).toHaveBeenCalled();
177 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'myArray' to [1,2,3,4,5] on this object: ");
178 | expect(trace.calls.mostRecent().args[1]).toEqual(obj);
179 | });
180 | it("Can show a trace message when a long array property is written", function(){
181 | var obj = {myArray: [1,2,3,4,5]};
182 | var trace = spyOn(console, "trace")
183 | breakpoints.debugPropertySet(obj, "myArray", "trace");
184 | obj.myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
185 | expect(trace).toHaveBeenCalled();
186 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'myArray' to [1,2,3,4,5,6,7,8,9,10,11,...] on this object: ");
187 | expect(trace.calls.mostRecent().args[1]).toEqual(obj);
188 | });
189 | it("Can show a trace message when a complex array property is written", function(){
190 | var obj = {myArray: []};
191 | var trace = spyOn(console, "trace")
192 | breakpoints.debugPropertySet(obj, "myArray", "trace");
193 | obj.myArray = [window, undefined, null];
194 | expect(trace).toHaveBeenCalled();
195 | expect(trace.calls.mostRecent().args[0]).toEqual("About to set property 'myArray' to [[object Window],,] on this object: ");
196 | expect(trace.calls.mostRecent().args[1]).toEqual(obj);
197 | });
198 | });
199 |
200 | describe("debugPropertyCall", function(){
201 | it("Calls a callback when an object property containing a function is called", function(){
202 | var obj = {
203 | sayHello: function(){
204 | var hi = "hello";
205 | }
206 | }
207 | var fn = jasmine.createSpy();
208 | breakpoints.debugPropertyCall(obj, "sayHello", fn);
209 | obj.sayHello();
210 | expect(fn).toHaveBeenCalled();
211 | })
212 | })
213 |
214 | describe("debugCookieReads", function(){
215 | it("Detects when the cookie is being read", function(){
216 | var fn = jasmine.createSpy();
217 | breakpoints.debugCookieReads(fn);
218 | var cookie = document.cookie;
219 | expect(fn).toHaveBeenCalled();
220 | })
221 | it("Doesn't call the callback when the cookie property is being set", function(){
222 | var fn = jasmine.createSpy();
223 | breakpoints.debugCookieReads(fn);
224 | document.cookie = "test";
225 | expect(fn).not.toHaveBeenCalled();
226 | });
227 | it("Shows a predefined trace message", function(){
228 | var fn = spyOn(console, "trace");
229 | breakpoints.debugCookieReads("trace");
230 | var cookie = document.cookie;
231 | expect(console.trace).toHaveBeenCalled();
232 | expect(console.trace.calls.mostRecent().args[0]).toBe("Reading cookie contents");
233 | });
234 | });
235 |
236 | describe("debugCookieWrites", function(){
237 | it("Detects when the cookie is being set", function(){
238 | var fn = jasmine.createSpy();
239 | breakpoints.debugCookieWrites(fn);
240 | document.cookie = "hello";
241 | expect(fn).toHaveBeenCalled();
242 | })
243 | })
244 |
245 | describe("debugAlertCalls", function(){
246 | it("Detects when an alert box is shown", function(){
247 | var alertSpy = spyOn(window, "alert");
248 | var fn = jasmine.createSpy();
249 | breakpoints.debugAlertCalls(fn)
250 | window.alert("test");
251 | expect(fn).toHaveBeenCalled();
252 | })
253 | })
254 |
255 | describe("debugConsoleErrorCalls", function(){
256 | it("Detects when console.error is called", function(){
257 | var consoleErrorSpy = spyOn(console, "error");
258 | var fn = jasmine.createSpy();
259 | breakpoints.debugConsoleErrorCalls(fn);
260 | console.error("error")
261 | expect(fn).toHaveBeenCalled();
262 | });
263 | });
264 |
265 | describe("debugConsoleLogCalls", function(){
266 | it("Detects when console.log is called", function(){
267 | var consoleErrorSpy = spyOn(console, "log");
268 | var fn = jasmine.createSpy();
269 | breakpoints.debugConsoleLogCalls(fn);
270 | console.log("log")
271 | expect(fn).toHaveBeenCalled();
272 | });
273 | });
274 |
275 | describe("debugConsoleTraceCalls", function(){
276 | it("Detects when console.trace is called", function(){
277 | var consoleBackup = window.console;
278 | window.console = {trace: function(){}};
279 | var fn = jasmine.createSpy();
280 | breakpoints.debugConsoleTraceCalls(fn);
281 | console.trace("test");
282 | expect(fn).toHaveBeenCalled();
283 | window.console = consoleBackup;
284 | })
285 | it("Works when showing trace messages when breakpoint is hit", function(){
286 | var consoleBackup = window.console;
287 | window.console = {trace: function(){}};
288 | spyOn(console, "trace")
289 | // The spy is replaced by the debugConsoleTraceCalls call,
290 | // so keep a reference to it
291 | var spy = window.console.trace;
292 | breakpoints.debugConsoleTraceCalls("trace");
293 | console.trace("test");
294 | expect(spy).toHaveBeenCalled();
295 | window.console = consoleBackup;
296 | })
297 | });
298 |
299 | describe("debugMathRandom", function(){
300 | it("Hits when Math.random is called and shows a trace message", function(){
301 | spyOn(console, "trace");
302 | breakpoints.debugMathRandom("trace");
303 | Math.random();
304 | expect(console.trace).toHaveBeenCalled();
305 | expect(console.trace.calls.count()).toBe(1);
306 | })
307 | })
308 |
309 | describe("debugTimerCreation", function(){
310 | it("Shows a trace message when setTimeout is called", function(){
311 | spyOn(console, "trace");
312 | breakpoints.debugTimerCreation("trace");
313 | setTimeout(function(){}, 0);
314 | expect(console.trace).toHaveBeenCalled();
315 | expect(console.trace.calls.mostRecent().args[0]).toBe("Creating timer using setTimeout")
316 | });
317 | })
318 |
319 | describe("debugScroll", function(){
320 | it("Hits when window.scrollTo is called", function(){
321 | spyOn(window, "scrollTo")
322 | var fn = jasmine.createSpy();
323 | breakpoints.debugScroll(fn);
324 | window.scrollTo(50, 50);
325 | expect(fn).toHaveBeenCalled()
326 | });
327 | it("Hits when window.scrollBy is called", function(){
328 | spyOn(window, "scrollBy")
329 | var fn = jasmine.createSpy();
330 | breakpoints.debugScroll(fn);
331 | window.scrollBy(50, 50);
332 | expect(fn).toHaveBeenCalled()
333 | });
334 | it("Hits when scrollTop is set on the body", function(){
335 | var fn = jasmine.createSpy();
336 | breakpoints.debugScroll(fn);
337 | document.body.scrollTop = 50;
338 | expect(fn).toHaveBeenCalled();
339 | })
340 | it("Hits when scrollLeft is set on the body", function(){
341 | var fn = jasmine.createSpy();
342 | breakpoints.debugScroll(fn);
343 | document.body.scrollLeft = 50;
344 | expect(fn).toHaveBeenCalled();
345 | })
346 | it("Hits when scrollTop is set on an element", function(){
347 | var div = document.createElement("div");
348 | var fn = jasmine.createSpy();
349 | breakpoints.debugScroll(fn);
350 | div.scrollTop = 40;
351 | expect(fn).toHaveBeenCalled();
352 | });
353 | it("Hits when scrollLeft is set on an element", function(){
354 | var div = document.createElement("div");
355 | var fn = jasmine.createSpy();
356 | breakpoints.debugScroll(fn);
357 | div.scrollLeft = 40;
358 | expect(fn).toHaveBeenCalled();
359 | });
360 | it("Shows the scroll position message", function(){
361 | spyOn(console, "trace");
362 | breakpoints.debugScroll("trace");
363 | var myParagraph = document.createElement("p");
364 | myParagraph.id = "myParagraph";
365 | var div = document.createElement("div");
366 | div.appendChild(myParagraph);
367 | document.body.appendChild(div);
368 | document.getElementById("myParagraph").scrollTop = 10;
369 | expect(console.trace).toHaveBeenCalled();
370 | var mostRecentCallArgs = console.trace.calls.mostRecent().args;
371 | expect(mostRecentCallArgs[0]).toBe("The scroll position of");
372 | expect(mostRecentCallArgs[1].outerHTML).toBe("
");
373 | expect(mostRecentCallArgs[2]).toBe("was changed by setting the scrollTop property to 10");
374 | })
375 | it("Shows the scroll position message", function(){
376 | spyOn(console, "trace");
377 | breakpoints.debugScroll("trace");
378 | window.scrollTo(10, 10);
379 | expect(console.trace).toHaveBeenCalled();
380 | var mostRecentCallArgs = console.trace.calls.mostRecent().args;
381 | expect(mostRecentCallArgs[0]).toBe("The scroll position of the window was changed by a window.scrollTo call with");
382 | expect(mostRecentCallArgs[1]).toEqual([10, 10]);
383 | })
384 | })
385 |
386 | describe("debugLocalStorageReads", function(){
387 | it("Hits when localStorage.getItem is called", function(){
388 | var fn = jasmine.createSpy();
389 | breakpoints.debugLocalStorageReads(fn);
390 | localStorage.getItem("hello");
391 | expect(fn).toHaveBeenCalled();
392 | })
393 | it("Shows the data key in the trace message", function(){
394 | spyOn(console, "trace");
395 | breakpoints.debugLocalStorageReads("trace");
396 | localStorage.getItem("hello");
397 | expect(console.trace).toHaveBeenCalled();
398 | expect(console.trace.calls.mostRecent().args[0]).toBe("Reading localStorage data for key \"hello\"");
399 | })
400 | })
401 |
402 | describe("debugLocalStorageWrites", function(){
403 | it("Shows the data key in the trace message", function(){
404 | spyOn(console, "trace");
405 | breakpoints.debugLocalStorageWrites("trace");
406 | localStorage.setItem("hi", "there");
407 | expect(console.trace).toHaveBeenCalled();
408 | expect(console.trace.calls.mostRecent().args[0]).toBe("Writing localStorage data for key \"hi\"");
409 | })
410 | it("Hits when localStorage.setItem is called", function(){
411 | var fn = jasmine.createSpy();
412 | breakpoints.debugLocalStorageWrites(fn);
413 | localStorage.setItem("hi", "there");
414 | expect(fn).toHaveBeenCalled();
415 | })
416 | it("Hits when localStorage.clear is called", function(){
417 | var fn = jasmine.createSpy();
418 | breakpoints.debugLocalStorageWrites(fn);
419 | localStorage.clear();
420 | expect(fn).toHaveBeenCalled();
421 | })
422 | it("Shows the localStorage.clear trace message", function(){
423 | spyOn(console, "trace");
424 | breakpoints.debugLocalStorageWrites("trace");
425 | localStorage.clear();
426 | expect(console.trace).toHaveBeenCalled();
427 | expect(console.trace.calls.mostRecent().args[0]).toBe("Clearing all localStorage data");
428 | })
429 | })
430 |
431 | describe("debugElementSelection", function(){
432 | it("Hits when document.getElementById is called", function(){
433 | var fn = jasmine.createSpy();
434 | breakpoints.debugElementSelection(fn);
435 | document.getElementById("test")
436 | expect(fn).toHaveBeenCalled();
437 | });
438 | it("Shows the function that was called in the trace message", function(){
439 | spyOn(console, "trace");
440 | breakpoints.debugElementSelection("trace");
441 | document.getElementsByClassName("hello");
442 | expect(console.trace).toHaveBeenCalled();
443 | expect(console.trace.calls.mostRecent().args[0]).toBe("Selecting DOM elements \"hello\" using getElementsByClassName");
444 | })
445 | //it's all just a list of calls... no point in duplicating them all here
446 | })
447 |
--------------------------------------------------------------------------------
/webpack-test.config.js:
--------------------------------------------------------------------------------
1 | var context = require.context('./tests', true, /-spec\.jsx?$/);
2 | context.keys().forEach(context);
3 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | var toBuild = [
5 | {
6 | entry: ["devtools-panel", "./devtools-panel.js"],
7 | path: __dirname + "/extension/build",
8 | libraryTarget: "var"
9 | },
10 | {
11 | entry: ["javascript-breakpoint-collection", "./injected-script.js"],
12 | path: __dirname + "/extension/build",
13 | libraryTarget: "var"
14 | },
15 | {
16 | entry: ["node", "./injected-script"],
17 | //needs separate build becasue otherwise the script fails
18 | // on pages with a global `define` function
19 | libraryTarget: "umd",
20 | path: __dirname + "/dist/"
21 | }
22 | ]
23 |
24 | function makeConfig(item){
25 | var entry = {};
26 | entry[item.entry[0]] = item.entry[1];
27 |
28 | return {
29 | entry: entry,
30 | devtool: "source-map",
31 | output: {
32 | path: item.path,
33 | filename: '[name].js',
34 | libraryTarget: item.libraryTarget
35 | },
36 | module: {
37 | loaders: [
38 | {
39 | test: /.jsx?$/,
40 | loader: 'babel-loader',
41 | exclude: /node_modules/,
42 | presets: ['es2015', 'react']
43 | }
44 | ]
45 | }
46 | };
47 | }
48 |
49 | var configs = toBuild.map(makeConfig);
50 |
51 | module.exports = configs;
52 |
--------------------------------------------------------------------------------
/webstore-assets/demo-with-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/demo-with-page.png
--------------------------------------------------------------------------------
/webstore-assets/extension-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/extension-icon.png
--------------------------------------------------------------------------------
/webstore-assets/icon-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/icon-large.png
--------------------------------------------------------------------------------
/webstore-assets/screenshot-old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/screenshot-old.png
--------------------------------------------------------------------------------
/webstore-assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/screenshot.png
--------------------------------------------------------------------------------
/webstore-assets/scroll-trace-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzeunert/javascript-breakpoint-collection/2b6124c4e7bee442a12fdc3441710e0b9ba4e22e/webstore-assets/scroll-trace-demo.png
--------------------------------------------------------------------------------