BNPChrome.controller("PanelController", function PanelController($scope, toolbar, parse, $timeout) { const LOCAL_STORAGE = window.localStorage; const MAX_BODY_SIZE = 20000; const HOST = "https://leviolson.com"; // "http://localhost:3000" const CHANGELOG = { "What's New": { "v1.0.2:": { "Added Settings": HOST + "/posts/bnp-changelog#added-settings", "Auto URL Decoding": "" }, "v1.0.1:": { "Panel Settings": HOST + "/posts/bnp-changelog#panel-settings", "Bugs": "squashed" }, "v1.0.0:": { "Improved Search": HOST + "/posts/bnp-changelog#improved-search", "JSON Editor BUILT IN": HOST + "/posts/bnp-changelog#json-editor-built-in", "Vertical Chrome Panel": HOST + "/posts/bnp-changelog#vertical-chrome-panel", "Download JSON": HOST + "/posts/bnp-changelog#download-json" } } }; const STORAGE_MAP = { 'autoJSONParseDepthRes': 3, 'autoJSONParseDepthReq': 6, 'clearOnRefresh': false, 'scrollToNew': true, 'andfilter': false, 'searchTerms': [], 'oldSearchTerms': [], }; $scope.autoJSONParseDepthRes = STORAGE_MAP.autoJSONParseDepthRes; $scope.autoJSONParseDepthReq = STORAGE_MAP.autoJSONParseDepthReq; $scope.clearOnRefresh = STORAGE_MAP.clearOnRefresh; $scope.scrollToNew = STORAGE_MAP.scrollToNew; $scope.andFilter = STORAGE_MAP.andfilter; $scope.searchTerms = STORAGE_MAP.searchTerms; $scope.oldSearchTerms = STORAGE_MAP.oldSearchTerms; $scope.search = ""; $scope.uniqueId = 100000; $scope.activeId = null; $scope.requests = {}; $scope.masterRequests = []; $scope.filteredRequests = []; $scope.showAll = true; $scope.limitNetworkRequests = false; $scope.showOriginal = false; $scope.currentDetailTab = "tab-response"; $scope.filter = ""; $scope.editor = null; $scope.activeCookies = []; $scope.activeHeaders = []; $scope.activePostData = []; $scope.activeRequest = []; $scope.activeResponseData = []; $scope.activeResponseCookies = []; $scope.activeResponseHeaders = []; $scope.activeCode = null; $scope.init = function () { $("#tabs").tabs(); _initChrome(); _createToolbar(); const options = { mode: 'view', modes: ['code', 'view', 'preview'], onEditable: function (node) { if (!node.path) { // In modes code and text, node is empty: no path, field, or value // returning false makes the text area read-only return false; } return true; } }; const response = document.getElementById('response-jsoneditor'); const request = document.getElementById('request-jsoneditor'); $scope.responseJsonEditor = new JSONEditor(response, options); $scope.requestJsonEditor = new JSONEditor(request, options); $timeout(() => { $scope.responseJsonEditor.set(CHANGELOG); $scope.responseJsonEditor.expandAll(); }); }; const _initChrome = function () { for (const property in STORAGE_MAP) { try { let item = LOCAL_STORAGE.getItem(`bnp-${property}`); if (item) { let retrieved = JSON.parse(item); switch (typeof STORAGE_MAP[property]) { case 'boolean': $scope[property] = retrieved; break; default: $scope[property] = retrieved || STORAGE_MAP[property]; break; } } else { $scope[property] = STORAGE_MAP[property]; } } catch (e) { $scope[property] = STORAGE_MAP[property]; } $scope.$watch(property, function(n, o) { if (n !== o) { _setLocalStorage(); } }); console.debug(`Retrieving ${property}`, $scope[property]); } chrome.devtools.network.onRequestFinished.addListener(function (request) { // do not show requests to chrome extension resources if (request.request.url.startsWith("chrome-extension://")) { return; } $scope.handleRequest(request); }); chrome.devtools.network.onNavigated.addListener(function (event) { // display a line break in the network logs to show page reloaded if ($scope.clearOnRefresh) { $scope.clear(); return; } $scope.masterRequests.push({ id: $scope.uniqueId, separator: true, event: event }); $scope.uniqueId++; $scope.cleanRequests(); }); }; const _filterRequests = function () { if (!$scope.searchTerms || $scope.searchTerms.length === 0) { $scope.filteredRequests = $scope.masterRequests; return; } // console.log("Filtering for: ", $scope.searchTerms); let negTerms = []; let posTerms = []; for (let term of $scope.searchTerms) { term = term.toLowerCase(); if (term && term[0] === '-') negTerms.push(term.substring(1)); else posTerms.push(term); } $scope.filteredRequests = $scope.masterRequests.filter(function (x) { if (x.separator) return true; for (let term of negTerms) { // if neg if (x && x.searchIndex && x.searchIndex.includes(term)) return false; } if ($scope.andFilter) { // AND condition for (let term of posTerms) { // if pos if (x && x.searchIndex && !x.searchIndex.includes(term)) { return false; } } return true; } else { // OR condition for (let term of posTerms) { // if pos if (x && x.searchIndex && x.searchIndex.includes(term)) { return true; } } return false; } }); }; $scope.toggleSearchType = function() { $scope.andFilter = !$scope.andFilter; _setLocalStorage(); _filterRequests(); }; $scope.customSearch = function() { if (!$scope.searchTerms.includes($scope.search)) { $scope.searchTerms.push($scope.search); $scope.search = ""; _setLocalStorage(); _filterRequests(); } }; const _setLocalStorage = function() { // do some sort of comparison to searchTerms and oldSearchTerms to make sure there is only one. // although, now that I think about it... this comparison shouldn't be necessary... /shrug for (const property in STORAGE_MAP) { LOCAL_STORAGE.setItem(`bnp-${property}`, JSON.stringify($scope[property])); console.debug(`Saving ${property}`, $scope[property]); } }; $scope.addSearchTerm = function(index) { $scope.searchTerms.push($scope.oldSearchTerms.splice(index, 1)[0]); _setLocalStorage(); _filterRequests(); }; $scope.removeSearchTerm = function(index) { $scope.oldSearchTerms.push($scope.searchTerms.splice(index, 1)[0]); _setLocalStorage(); _filterRequests(); }; $scope.deleteSearchTerm = function(index) { $scope.oldSearchTerms.splice(index, 1); _setLocalStorage(); }; $scope.handleRequest = function (har_entry) { $scope.addRequest(har_entry, har_entry.request.method, har_entry.request.url, har_entry.response.status, null); }; const _createToolbar = function () { toolbar.createToggleButton( "embed", "Toggle JSON Parsing (See Panel Settings)", false, function () { // ga('send', 'event', 'button', 'click', 'Toggle JSON Parsing'); $scope.$apply(function () { $scope.showOriginal = !$scope.showOriginal; $scope.selectDetailTab($scope.currentDetailTab); // $scope.displayCode(); }); }, true ); toolbar.createButton("download3", "Download", false, function () { // ga('send', 'event', 'button', 'click', 'Download'); $scope.$apply(function () { const panel = $scope.currentDetailTab; let blob; if (panel === "tab-response") { blob = new Blob([JSON.parse(JSON.stringify($scope.activeCode, null, 4))], { type: "application/json;charset=utf-8" }); saveAs(blob, "export_response.json"); } else { try { blob = new Blob([JSON.stringify($scope.activePostData)], { type: "application/json;charset=utf-8" }); saveAs(blob, "export_request.json"); } catch (e) { console.log(e); } } }); }); toolbar.createButton("blocked", "Clear", false, function () { // ga('send', 'event', 'button', 'click', 'Clear'); $scope.$apply(function () { $scope.clear(); }); }); $(".toolbar").replaceWith(toolbar.render()); }; $scope.addRequest = function (data, request_method, request_url, response_status) { $scope.$apply(function () { const requestId = data.id || $scope.uniqueId; $scope.uniqueId++; if (data.request != null) { data["request_data"] = $scope.createKeypairs(data.request); if (data.request.cookies != null) { data.cookies = $scope.createKeypairsDeep(data.request.cookies); } if (data.request.headers != null) { data.headers = $scope.createKeypairsDeep(data.request.headers); } if (data.request.postData != null) { data.postData = $scope.createKeypairs(data.request.postData); } } if (data.response != null) { data["response_data"] = $scope.createKeypairs(data.response); data.response_data.response_body = "Loading " + requestId; if (data.response.cookies != null) { data["response_cookies"] = $scope.createKeypairsDeep(data.response.cookies); } if (data.response.headers != null) { data["response_headers"] = $scope.createKeypairsDeep(data.response.headers); } } data["request_method"] = request_method; if (request_url.includes("apexremote")) { try { let text = data && data.request && data.request.postData && data.request.postData.text ? JSON.parse(data.request.postData.text) : ""; data["request_apex_type"] = text.data && typeof text.data[1] === "string" ? text.data[1] : JSON.stringify(text.data); data["request_apex_method"] = text.method || ""; } catch (e) { console.debug("Error", e); } } data.request_url = request_url; data.response_status = response_status; data['id'] = requestId; let ctObj = data.response_headers.find(x => x.name == "Content-Type"); data.content_type = ctObj && ctObj.value || null; $scope.requests[requestId] = data; // master data.searchIndex = JSON.stringify(data.request).toLowerCase(); $scope.masterRequests.push(data); data.getContent(function (content, encoding) { $scope.requests[requestId].response_data.response_body = content; }); $scope.cleanRequests(); }); }; $scope.cleanRequests = function () { if ($scope.limitNetworkRequests === true) { if ($scope.masterRequests.length >= 500) $scope.masterRequests.shift(); const keys = Object.keys($scope.requests).reverse().slice(500); keys.forEach(function (key) { if ($scope.requests[key]) { delete $scope.requests[key]; } }); } _filterRequests(); }; $scope.clear = function () { $scope.requests = {}; $scope.activeId = null; $scope.masterRequests = []; $scope.filteredRequests = []; $scope.activeCookies = []; $scope.activeHeaders = []; $scope.activePostData = []; $scope.activeRequest = []; $scope.activeResponseData = []; $scope.activeResponseDataPreview = ""; $scope.activeResponseCookies = []; $scope.activeResponseHeaders = []; $scope.activeCode = null; }; $scope.setActive = function (requestId) { if (!$scope.requests[requestId]) { return; } $scope.activeId = requestId; $scope.activeCookies = $scope.requests[requestId].cookies; $scope.activeHeaders = $scope.requests[requestId].headers; $scope.activePostData = $scope.requests[requestId].postData; $scope.activeRequest = $scope.requests[requestId].request_data; $scope.activeResponseData = $scope.requests[requestId].response_data; $scope.activeResponseDataPreview = $scope.requests[requestId].response_data.response_body; $scope.activeResponseCookies = $scope.requests[requestId].response_cookies; $scope.activeResponseHeaders = $scope.requests[requestId].response_headers; $scope.activeCode = $scope.requests[requestId].response_data.response_body; }; $scope.getClass = function (requestId, separator) { if (separator) return "separator"; if (requestId === $scope.activeId) { return "selected"; } else { return ""; } }; $scope.titleIfSeparator = function(separator) { if (separator) return "Page reloaded here"; return ""; }; $scope.createKeypairs = function (data) { let keypairs = []; if (!(data instanceof Object)) { return keypairs; } $.each(data, function (key, value) { if (!(value instanceof Object)) { keypairs.push({ name: key, value: value }); } }); return keypairs; }; $scope.createKeypairsDeep = function (data) { let keypairs = []; if (!(data instanceof Object)) { return keypairs; } $.each(data, function (key, value) { keypairs.push({ name: value.name, value: value.value }); }); return keypairs; }; $scope.$watch("activeCode", function (newVal, oldVal) { if (newVal === null) { $scope.responseJsonEditor.set(null); $scope.requestJsonEditor.set(null); } $scope.displayCode("responseJsonEditor", $scope.activeCode, $scope.autoJSONParseDepthRes); $scope.displayCode("requestJsonEditor", $scope.activePostData, $scope.autoJSONParseDepthReq); }); $scope.selectDetailTab = function (tabId, external) { $scope.currentDetailTab = tabId; if (external) { $("#tabs a[href='#" + tabId + "']").trigger("click"); } if (tabId === "tab-response") { $scope.displayCode("responseJsonEditor", $scope.activeCode, $scope.autoJSONParseDepthRes); } if (tabId === "tab-request") { $scope.displayCode("requestJsonEditor", $scope.activePostData, $scope.autoJSONParseDepthReq); } }; $scope.displayCode = function (elementId, input, depth) { if (input) { let content; if ($scope.showOriginal) { content = input; } else { content = parse(input, 0, depth); } if (typeof input === 'object' || Array.isArray(input)) { // JSON $scope[elementId].setMode("view"); $scope[elementId].set(content); } else { // Something else try { let json = JSON.parse(input); $scope[elementId].setMode("view"); $scope[elementId].set(content); } catch (e) { $scope[elementId].setMode("code"); $scope[elementId].set(content); } } let bodySize; if (elementId === "responseJsonEditor") { bodySize = $scope.activeResponseData.find(x => x.name === "bodySize"); if (bodySize && bodySize.value < MAX_BODY_SIZE) { // an arbitrary number that I picked so there is HUGE lag if ($scope[elementId].getMode() === 'tree' || $scope[elementId].getMode() === 'view') $scope[elementId].expandAll(); } } else if (elementId === "requestJsonEditor") { bodySize = $scope.activeRequest.find(x => x.name === "bodySize"); if (bodySize && bodySize.value < MAX_BODY_SIZE) { if ($scope[elementId].getMode() === 'tree' || $scope[elementId].getMode() === 'view') $scope[elementId].expandAll(); } } } else { $scope[elementId].set(null); $scope[elementId].expandAll(); } }; });