├── shells
├── .DS_Store
├── chrome
│ ├── 128.png
│ ├── devtool.html
│ ├── devtool.js
│ ├── manifest.json
│ ├── content.js
│ ├── panel.html
│ ├── panel.js
│ ├── background.js
│ └── json-formatter.js
└── firefox
│ ├── data
│ ├── 128.png
│ ├── panel.css
│ ├── content.js
│ ├── panel.html
│ ├── panel.js
│ └── json-formatter.js
│ ├── package.json
│ └── index.js
├── dist
└── firefox
│ ├── .DS_Store
│ ├── pux_devtool-1.0.2-an+fx.xpi
│ └── update.rdf
├── .gitignore
└── README.md
/shells/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexmingoia/pux-devtool/HEAD/shells/.DS_Store
--------------------------------------------------------------------------------
/dist/firefox/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexmingoia/pux-devtool/HEAD/dist/firefox/.DS_Store
--------------------------------------------------------------------------------
/shells/chrome/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexmingoia/pux-devtool/HEAD/shells/chrome/128.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **DS_Store
2 | dist/chrome
3 | bower_components/
4 | node_modules/
5 | npm-debug.log
6 | output/
7 |
--------------------------------------------------------------------------------
/shells/firefox/data/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexmingoia/pux-devtool/HEAD/shells/firefox/data/128.png
--------------------------------------------------------------------------------
/dist/firefox/pux_devtool-1.0.2-an+fx.xpi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexmingoia/pux-devtool/HEAD/dist/firefox/pux_devtool-1.0.2-an+fx.xpi
--------------------------------------------------------------------------------
/shells/chrome/devtool.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pux Devtool
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/shells/firefox/data/panel.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | background: #fcfcfc;
3 | padding: 12px 36px;
4 | color: #667380;
5 | font-family: "Source Sans Pro", sans-serif;
6 | font-size: 13px;
7 | }
8 |
9 | pre {
10 | color: #667380;
11 | font-size: 13px;
12 | }
13 |
14 | #states {
15 | display: inline-block;
16 | margin-left: .5em;
17 | }
18 |
--------------------------------------------------------------------------------
/shells/firefox/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Pux Devtool",
3 | "name": "pux-devtool",
4 | "version": "1.0.2",
5 | "description": "Pux devtool.",
6 | "main": "index.js",
7 | "author": "Alexander C. Mingoia",
8 | "engines": {
9 | "firefox": ">=38.0a1",
10 | "fennec": ">=38.0a1"
11 | },
12 | "multiprocess": true,
13 | "updateURL": "https://raw.githubusercontent.com/alexmingoia/pux-devtool/master/dist/firefox/update.rdf",
14 | "updateLink": "https://raw.githubusercontent.com/alexmingoia/pux-devtool/master/dist/firefox/pux-devtool.xpi",
15 | "license": "MIT",
16 | "keywords": [
17 | "jetpack"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/shells/chrome/devtool.js:
--------------------------------------------------------------------------------
1 | chrome.devtools.panels.create('Pux', '128.png', 'panel.html', function (panel) {
2 | const backgroundPort = chrome.runtime.connect({name: 'pux'});
3 |
4 | panel.onShown.addListener(function (panelWindow) {
5 | backgroundPort.postMessage({
6 | name: 'init',
7 | tabId: chrome.devtools.inspectedWindow.tabId
8 | });
9 |
10 | panelWindow.addEventListener('message', function (ev) {
11 | backgroundPort.postMessage({
12 | name: ev.data,
13 | tabId: chrome.devtools.inspectedWindow.tabId
14 | })
15 | })
16 |
17 | backgroundPort.onMessage.addListener(function(message) {
18 | panelWindow.postMessage(message, '*')
19 | })
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/shells/firefox/data/content.js:
--------------------------------------------------------------------------------
1 | self.port.on('init', function () {
2 | window.addEventListener('pux:state:change', function (ev) {
3 | self.port.emit('pux:state:change', ev.detail);
4 | });
5 |
6 | window.dispatchEvent(new CustomEvent('pux:devtool:init'));
7 | });
8 |
9 | self.port.on('first', function () {
10 | window.dispatchEvent(new CustomEvent('pux:state:first'))
11 | });
12 |
13 | self.port.on('prev', function () {
14 | window.dispatchEvent(new CustomEvent('pux:state:prev'))
15 | });
16 |
17 | self.port.on('next', function () {
18 | window.dispatchEvent(new CustomEvent('pux:state:next'))
19 | });
20 |
21 | self.port.on('last', function () {
22 | window.dispatchEvent(new CustomEvent('pux:state:last'))
23 | });
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pux-devtool
2 |
3 | > DevTools extension for
4 | > [Pux](https://github.com/alexmingoia/purescript-pux) applications.
5 |
6 | 
7 |
8 | Visualize app state and events.
9 |
10 | - Show current event
11 | - Inspect current state
12 | - Time-travel between application states
13 |
14 | ## Chrome Extension
15 |
16 | Install the Chrome extension from the [Chrome Web Store](https://chrome.google.com/webstore/detail/pux-devtool/ecolgfgmnimnllbgllbpfmnehejockmk).
17 |
18 | ## Firefox Extension
19 |
20 | Install the FireFox extension by going to Add-Ons -> Install Add-On From File...
21 | -> then choose [pux_devtool-1.0.2-an+fx.xpi](https://github.com/alexmingoia/pux-devtool/tree/master/dist/firefox).
22 |
--------------------------------------------------------------------------------
/shells/firefox/data/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Pux
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
Waiting to connect to Pux application...
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/shells/chrome/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Pux DevTool",
4 | "description": "Pux DevTool for visualizing app state and events.",
5 | "version": "1.1.0",
6 | "minimum_chrome_version": "43",
7 | "icons": {
8 | "128": "128.png"
9 | },
10 | "devtools_page": "devtool.html",
11 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
12 | "web_accessible_resources": [ "devtool.html", "panel.html"],
13 | "background": {
14 | "scripts": [
15 | "background.js"
16 | ],
17 | "persistent": false
18 | },
19 | "permissions": [
20 | "tabs",
21 | "contextMenus",
22 | ""
23 | ],
24 | "content_scripts": [
25 | {
26 | "matches": [""],
27 | "js": ["content.js"],
28 | "run_at": "document_end",
29 | "all_frames": true
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/shells/chrome/content.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('pux:state:change', function (ev) {
2 | chrome.runtime.sendMessage({
3 | name: 'pux:state:change',
4 | detail: ev.detail
5 | })
6 | });
7 |
8 | window.dispatchEvent(new CustomEvent('pux:devtool:init'));
9 |
10 | chrome.runtime.onMessage.addListener(function (message) {
11 | if (message === 'init') {
12 | window.dispatchEvent(new CustomEvent('pux:devtool:init'));
13 | }
14 |
15 | if (message === 'first') {
16 | window.dispatchEvent(new CustomEvent('pux:state:first'));
17 | }
18 |
19 | if (message === 'prev') {
20 | window.dispatchEvent(new CustomEvent('pux:state:prev'));
21 | }
22 |
23 | if (message === 'next') {
24 | window.dispatchEvent(new CustomEvent('pux:state:next'));
25 | }
26 |
27 | if (message === 'last') {
28 | window.dispatchEvent(new CustomEvent('pux:state:last'));
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/shells/chrome/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Pux
8 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Waiting to connect to Pux application...
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/shells/firefox/data/panel.js:
--------------------------------------------------------------------------------
1 | var eventEl = document.getElementById('event');
2 | var stateEl = document.getElementById('state');
3 | var statesEl = document.getElementById('states');
4 | var prevEl = document.getElementById('prev');
5 | var nextEl = document.getElementById('next');
6 | var firstEl = document.getElementById('first');
7 | var lastEl = document.getElementById('last');
8 |
9 | firstEl.addEventListener('click', function (ev) {
10 | window.port.postMessage('first');
11 | });
12 |
13 | prevEl.addEventListener('click', function (ev) {
14 | window.port.postMessage('prev');
15 | });
16 |
17 | nextEl.addEventListener('click', function (ev) {
18 | window.port.postMessage('next');
19 | });
20 |
21 | lastEl.addEventListener('click', function (ev) {
22 | window.port.postMessage('last');
23 | });
24 |
25 | window.addEventListener('message', function (ev) {
26 | if (ev.data === 'init') {
27 | window.port = ev.ports[0];
28 | window.port.start();
29 | } else {
30 | var data = JSON.parse(ev.data);
31 |
32 | if (data.event) {
33 | eventEl.textContent = data.event
34 | statesEl.textContent = (data.index + 1) + ' / ' + data.length;
35 | stateEl.innerHTML = '';
36 | stateEl.appendChild((new JSONFormatter(JSON.parse(data.state))).render());
37 | }
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/shells/chrome/panel.js:
--------------------------------------------------------------------------------
1 | var eventEl = document.getElementById("event");
2 | var stateEl = document.getElementById("state");
3 | var statesEl = document.getElementById("states");
4 | var prevEl = document.getElementById("prev");
5 | var nextEl = document.getElementById("next");
6 | var firstEl = document.getElementById("first");
7 | var lastEl = document.getElementById("last");
8 |
9 | if (!window.connected) {
10 | window.connected = true;
11 |
12 | firstEl.addEventListener("click", function (ev) {
13 | window.postMessage('first', '*');
14 | });
15 |
16 | prevEl.addEventListener("click", function (ev) {
17 | window.postMessage('prev', '*');
18 | });
19 |
20 | nextEl.addEventListener("click", function (ev) {
21 | window.postMessage('next', '*');
22 | });
23 |
24 | lastEl.addEventListener("click", function (ev) {
25 | window.postMessage('last', '*');
26 | });
27 |
28 | window.addEventListener('message', function (ev) {
29 | if (ev.data && ev.data.name) {
30 | if (ev.data.name === 'pux:state:change') {
31 | eventEl.textContent = ev.data.detail.event
32 | statesEl.textContent = (ev.data.detail.index + 1) + ' / ' + ev.data.detail.length;
33 | stateEl.innerHTML = '';
34 | stateEl.appendChild((new JSONFormatter(JSON.parse(ev.data.detail.state))).render());
35 | }
36 | }
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/shells/firefox/index.js:
--------------------------------------------------------------------------------
1 | const { Panel } = require('dev/panel');
2 | const { Tool } = require('dev/toolbox');
3 | const { Class } = require('sdk/core/heritage');
4 | const self = require('sdk/self');
5 | const tabs = require('sdk/tabs');
6 |
7 | const { MessageChannel } = require('sdk/messaging');
8 | const channel = new MessageChannel();
9 | const addonSide = channel.port1;
10 | const panelSide = channel.port2;
11 |
12 | const PuxPanel = Class({
13 | extends: Panel,
14 | label: 'Pux',
15 | tooltip: 'Pux debugger',
16 | icon: self.data.url('128.png'),
17 | invertIconForDarkTheme: true,
18 | url: self.data.url('panel.html'),
19 | onReady: function() {
20 | var worker;
21 | var _ = this;
22 |
23 | function makeWorker (tab) {
24 | worker = tab.attach({
25 | contentScriptFile: self.data.url('content.js')
26 | })
27 |
28 | worker.port.on('pux:state:change', function (ev) {
29 | _.postMessage(JSON.stringify(ev));
30 | });
31 |
32 | worker.port.emit('init');
33 | }
34 |
35 | tabs.on('pageshow', makeWorker);
36 |
37 | addonSide.onmessage = function (ev) {
38 | if (worker) {
39 | worker.port.emit(ev.data);
40 | }
41 | };
42 |
43 | _.postMessage('init', [panelSide]);
44 |
45 | makeWorker(tabs.activeTab);
46 | }
47 | });
48 |
49 | exports.PuxPanel = PuxPanel;
50 |
51 | const puxTool = new Tool({
52 | panels: { puxPanel: PuxPanel }
53 | });
54 |
--------------------------------------------------------------------------------
/dist/firefox/update.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 1.0.2
9 |
10 |
11 |
12 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
13 | 38.0a1
14 | *
15 | https://raw.githubusercontent.com/alexmingoia/pux-devtool/master/dist/firefox/pux-devtool.xpi
16 |
17 |
18 |
19 |
20 |
21 | {aa3c5121-dab2-40e2-81ca-7ea25febc110}
22 | 38.0a1
23 | *
24 | https://raw.githubusercontent.com/alexmingoia/pux-devtool/master/dist/firefox/pux-devtool.xpi
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/shells/chrome/background.js:
--------------------------------------------------------------------------------
1 | var connections = {};
2 |
3 | chrome.runtime.onConnect.addListener(function (port) {
4 | var extensionListener = function (message, sender, sendResponse) {
5 | // The original connection event doesn't include the tab ID of the
6 | // DevTools page, so we need to send it explicitly.
7 | if (message.name == "init") {
8 | connections[message.tabId] = port;
9 | chrome.tabs.sendMessage(message.tabId, 'init')
10 | return;
11 | }
12 |
13 | // Send message to content script
14 | chrome.tabs.sendMessage(message.tabId, message.name)
15 | }
16 |
17 | // Listen to messages sent from the DevTools page
18 | port.onMessage.addListener(extensionListener);
19 |
20 | port.onDisconnect.addListener(function(port) {
21 | port.onMessage.removeListener(extensionListener);
22 |
23 | var tabs = Object.keys(connections);
24 | for (var i=0, len=tabs.length; i < len; i++) {
25 | if (connections[tabs[i]] == port) {
26 | delete connections[tabs[i]]
27 | break;
28 | }
29 | }
30 | });
31 | });
32 |
33 | // Receive message from content script and relay to the devTools page for the
34 | // current tab
35 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
36 | // Messages from content scripts should have sender.tab set
37 | if (sender.tab) {
38 | var tabId = sender.tab.id;
39 | if (tabId in connections) {
40 | connections[tabId].postMessage(request);
41 | } else {
42 | console.log("Tab not found in connection list.");
43 | }
44 | } else {
45 | console.log("sender.tab not defined.");
46 | }
47 | return true;
48 | });
49 |
--------------------------------------------------------------------------------
/shells/chrome/json-formatter.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("JSONFormatter", [], factory);
6 | else if(typeof exports === 'object')
7 | exports["JSONFormatter"] = factory();
8 | else
9 | root["JSONFormatter"] = factory();
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 = "dist";
48 | /******/
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | module.exports = __webpack_require__(1);
58 |
59 |
60 | /***/ },
61 | /* 1 */
62 | /***/ function(module, exports, __webpack_require__) {
63 |
64 | "use strict";
65 | __webpack_require__(2);
66 | var helpers_ts_1 = __webpack_require__(6);
67 | var DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/;
68 | var PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/;
69 | var JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
70 | // When toggleing, don't animated removal or addition of more than a few items
71 | var MAX_ANIMATED_TOGGLE_ITEMS = 10;
72 | var requestAnimationFrame = window.requestAnimationFrame || function (cb) { cb(); return 0; };
73 | ;
74 | var _defaultConfig = {
75 | hoverPreviewEnabled: false,
76 | hoverPreviewArrayCount: 100,
77 | hoverPreviewFieldCount: 5,
78 | animateOpen: true,
79 | animateClose: true,
80 | theme: null
81 | };
82 | module.exports = (function () {
83 | /**
84 | * @param {object} json The JSON object you want to render. It has to be an
85 | * object or array. Do NOT pass raw JSON string.
86 | *
87 | * @param {number} [open=1] his number indicates up to how many levels the
88 | * rendered tree should expand. Set it to `0` to make the whole tree collapsed
89 | * or set it to `Infinity` to expand the tree deeply
90 | *
91 | * @param {object} [config=defaultConfig] -
92 | * defaultConfig = {
93 | * hoverPreviewEnabled: false,
94 | * hoverPreviewArrayCount: 100,
95 | * hoverPreviewFieldCount: 5
96 | * }
97 | *
98 | * Available configurations:
99 | * #####Hover Preview
100 | * * `hoverPreviewEnabled`: enable preview on hover
101 | * * `hoverPreviewArrayCount`: number of array items to show in preview Any
102 | * array larger than this number will be shown as `Array[XXX]` where `XXX`
103 | * is length of the array.
104 | * * `hoverPreviewFieldCount`: number of object properties to show for object
105 | * preview. Any object with more properties that thin number will be
106 | * truncated.
107 | *
108 | * @param {string} [key=undefined] The key that this object in it's parent
109 | * context
110 | */
111 | function JSONFormatter(json, open, config, key) {
112 | if (open === void 0) { open = 1; }
113 | if (config === void 0) { config = _defaultConfig; }
114 | this.json = json;
115 | this.open = open;
116 | this.config = config;
117 | this.key = key;
118 | // Hold the open state after the toggler is used
119 | this._isOpen = null;
120 | // Setting default values for config object
121 | if (this.config.hoverPreviewEnabled === undefined) {
122 | this.config.hoverPreviewEnabled = _defaultConfig.hoverPreviewEnabled;
123 | }
124 | if (this.config.hoverPreviewArrayCount === undefined) {
125 | this.config.hoverPreviewArrayCount = _defaultConfig.hoverPreviewArrayCount;
126 | }
127 | if (this.config.hoverPreviewFieldCount === undefined) {
128 | this.config.hoverPreviewFieldCount = _defaultConfig.hoverPreviewFieldCount;
129 | }
130 | }
131 | Object.defineProperty(JSONFormatter.prototype, "isOpen", {
132 | /*
133 | * is formatter open?
134 | */
135 | get: function () {
136 | if (this._isOpen !== null) {
137 | return this._isOpen;
138 | }
139 | else {
140 | return this.open > 0;
141 | }
142 | },
143 | /*
144 | * set open state (from toggler)
145 | */
146 | set: function (value) {
147 | this._isOpen = value;
148 | },
149 | enumerable: true,
150 | configurable: true
151 | });
152 | Object.defineProperty(JSONFormatter.prototype, "isDate", {
153 | /*
154 | * is this a date string?
155 | */
156 | get: function () {
157 | return (this.type === 'string') &&
158 | (DATE_STRING_REGEX.test(this.json) ||
159 | JSON_DATE_REGEX.test(this.json) ||
160 | PARTIAL_DATE_REGEX.test(this.json));
161 | },
162 | enumerable: true,
163 | configurable: true
164 | });
165 | Object.defineProperty(JSONFormatter.prototype, "isUrl", {
166 | /*
167 | * is this a URL string?
168 | */
169 | get: function () {
170 | return this.type === 'string' && (this.json.indexOf('http') === 0);
171 | },
172 | enumerable: true,
173 | configurable: true
174 | });
175 | Object.defineProperty(JSONFormatter.prototype, "isArray", {
176 | /*
177 | * is this an array?
178 | */
179 | get: function () {
180 | return Array.isArray(this.json);
181 | },
182 | enumerable: true,
183 | configurable: true
184 | });
185 | Object.defineProperty(JSONFormatter.prototype, "isObject", {
186 | /*
187 | * is this an object?
188 | * Note: In this context arrays are object as well
189 | */
190 | get: function () {
191 | return helpers_ts_1.isObject(this.json);
192 | },
193 | enumerable: true,
194 | configurable: true
195 | });
196 | Object.defineProperty(JSONFormatter.prototype, "isEmptyObject", {
197 | /*
198 | * is this an empty object with no properties?
199 | */
200 | get: function () {
201 | return !this.keys.length && !this.isArray;
202 | },
203 | enumerable: true,
204 | configurable: true
205 | });
206 | Object.defineProperty(JSONFormatter.prototype, "isEmpty", {
207 | /*
208 | * is this an empty object or array?
209 | */
210 | get: function () {
211 | return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
212 | },
213 | enumerable: true,
214 | configurable: true
215 | });
216 | Object.defineProperty(JSONFormatter.prototype, "hasKey", {
217 | /*
218 | * did we recieve a key argument?
219 | * This means that the formatter was called as a sub formatter of a parent formatter
220 | */
221 | get: function () {
222 | return typeof this.key !== 'undefined';
223 | },
224 | enumerable: true,
225 | configurable: true
226 | });
227 | Object.defineProperty(JSONFormatter.prototype, "constructorName", {
228 | /*
229 | * if this is an object, get constructor function name
230 | */
231 | get: function () {
232 | return helpers_ts_1.getObjectName(this.json);
233 | },
234 | enumerable: true,
235 | configurable: true
236 | });
237 | Object.defineProperty(JSONFormatter.prototype, "type", {
238 | /*
239 | * get type of this value
240 | * Possible values: all JavaScript primitive types plus "array" and "null"
241 | */
242 | get: function () {
243 | return helpers_ts_1.getType(this.json);
244 | },
245 | enumerable: true,
246 | configurable: true
247 | });
248 | Object.defineProperty(JSONFormatter.prototype, "keys", {
249 | /*
250 | * get object keys
251 | * If there is an empty key we pad it wit quotes to make it visible
252 | */
253 | get: function () {
254 | if (this.isObject) {
255 | return Object.keys(this.json).map(function (key) { return key ? key : '""'; });
256 | }
257 | else {
258 | return [];
259 | }
260 | },
261 | enumerable: true,
262 | configurable: true
263 | });
264 | /**
265 | * Toggles `isOpen` state
266 | *
267 | */
268 | JSONFormatter.prototype.toggleOpen = function () {
269 | this.isOpen = !this.isOpen;
270 | if (this.element) {
271 | if (this.isOpen) {
272 | this.appendChildren(this.config.animateOpen);
273 | }
274 | else {
275 | this.removeChildren(this.config.animateClose);
276 | }
277 | this.element.classList.toggle(helpers_ts_1.cssClass('open'));
278 | }
279 | };
280 | /**
281 | * Open all children up to a certain depth.
282 | * Allows actions such as expand all/collapse all
283 | *
284 | */
285 | JSONFormatter.prototype.openAtDepth = function (depth) {
286 | if (depth === void 0) { depth = 1; }
287 | if (depth < 0) {
288 | return;
289 | }
290 | this.open = depth;
291 | this.isOpen = (depth !== 0);
292 | if (this.element) {
293 | this.removeChildren(false);
294 | if (depth === 0) {
295 | this.element.classList.remove(helpers_ts_1.cssClass('open'));
296 | }
297 | else {
298 | this.appendChildren(this.config.animateOpen);
299 | this.element.classList.add(helpers_ts_1.cssClass('open'));
300 | }
301 | }
302 | };
303 | /**
304 | * Generates inline preview
305 | *
306 | * @returns {string}
307 | */
308 | JSONFormatter.prototype.getInlinepreview = function () {
309 | var _this = this;
310 | if (this.isArray) {
311 | // if array length is greater then 100 it shows "Array[101]"
312 | if (this.json.length > this.config.hoverPreviewArrayCount) {
313 | return "Array[" + this.json.length + "]";
314 | }
315 | else {
316 | return "[" + this.json.map(helpers_ts_1.getPreview).join(', ') + "]";
317 | }
318 | }
319 | else {
320 | var keys = this.keys;
321 | // the first five keys (like Chrome Developer Tool)
322 | var narrowKeys = keys.slice(0, this.config.hoverPreviewFieldCount);
323 | // json value schematic information
324 | var kvs = narrowKeys.map(function (key) { return (key + ":" + helpers_ts_1.getPreview(_this.json[key])); });
325 | // if keys count greater then 5 then show ellipsis
326 | var ellipsis = keys.length >= this.config.hoverPreviewFieldCount ? '…' : '';
327 | return "{" + kvs.join(', ') + ellipsis + "}";
328 | }
329 | };
330 | /**
331 | * Renders an HTML element and installs event listeners
332 | *
333 | * @returns {HTMLDivElement}
334 | */
335 | JSONFormatter.prototype.render = function () {
336 | // construct the root element and assign it to this.element
337 | this.element = helpers_ts_1.createElement('div', 'row');
338 | // construct the toggler link
339 | var togglerLink = helpers_ts_1.createElement('a', 'toggler-link');
340 | // if this is an object we need a wrapper span (toggler)
341 | if (this.isObject) {
342 | togglerLink.appendChild(helpers_ts_1.createElement('span', 'toggler'));
343 | }
344 | // if this is child of a parent formatter we need to append the key
345 | if (this.hasKey) {
346 | togglerLink.appendChild(helpers_ts_1.createElement('span', 'key', this.key + ":"));
347 | }
348 | // Value for objects and arrays
349 | if (this.isObject) {
350 | // construct the value holder element
351 | var value = helpers_ts_1.createElement('span', 'value');
352 | // we need a wrapper span for objects
353 | var objectWrapperSpan = helpers_ts_1.createElement('span');
354 | // get constructor name and append it to wrapper span
355 | var constructorName = helpers_ts_1.createElement('span', 'constructor-name', this.constructorName);
356 | objectWrapperSpan.appendChild(constructorName);
357 | // if it's an array append the array specific elements like brackets and length
358 | if (this.isArray) {
359 | var arrayWrapperSpan = helpers_ts_1.createElement('span');
360 | arrayWrapperSpan.appendChild(helpers_ts_1.createElement('span', 'bracket', '['));
361 | arrayWrapperSpan.appendChild(helpers_ts_1.createElement('span', 'number', (this.json.length)));
362 | arrayWrapperSpan.appendChild(helpers_ts_1.createElement('span', 'bracket', ']'));
363 | objectWrapperSpan.appendChild(arrayWrapperSpan);
364 | }
365 | // append object wrapper span to toggler link
366 | value.appendChild(objectWrapperSpan);
367 | togglerLink.appendChild(value);
368 | }
369 | else {
370 | // make a value holder element
371 | var value = this.isUrl ? helpers_ts_1.createElement('a') : helpers_ts_1.createElement('span');
372 | // add type and other type related CSS classes
373 | value.classList.add(helpers_ts_1.cssClass(this.type));
374 | if (this.isDate) {
375 | value.classList.add(helpers_ts_1.cssClass('date'));
376 | }
377 | if (this.isUrl) {
378 | value.classList.add(helpers_ts_1.cssClass('url'));
379 | value.setAttribute('href', this.json);
380 | }
381 | // Append value content to value element
382 | var valuePreview = helpers_ts_1.getValuePreview(this.json, this.json);
383 | value.appendChild(document.createTextNode(valuePreview));
384 | // append the value element to toggler link
385 | togglerLink.appendChild(value);
386 | }
387 | // if hover preview is enabled, append the inline preview element
388 | if (this.isObject && this.config.hoverPreviewEnabled) {
389 | var preview = helpers_ts_1.createElement('span', 'preview-text');
390 | preview.appendChild(document.createTextNode(this.getInlinepreview()));
391 | togglerLink.appendChild(preview);
392 | }
393 | // construct a children element
394 | var children = helpers_ts_1.createElement('div', 'children');
395 | // set CSS classes for children
396 | if (this.isObject) {
397 | children.classList.add(helpers_ts_1.cssClass('object'));
398 | }
399 | if (this.isArray) {
400 | children.classList.add(helpers_ts_1.cssClass('array'));
401 | }
402 | if (this.isEmpty) {
403 | children.classList.add(helpers_ts_1.cssClass('empty'));
404 | }
405 | // set CSS classes for root element
406 | if (this.config && this.config.theme) {
407 | this.element.classList.add(helpers_ts_1.cssClass(this.config.theme));
408 | }
409 | if (this.isOpen) {
410 | this.element.classList.add(helpers_ts_1.cssClass('open'));
411 | }
412 | // append toggler and children elements to root element
413 | this.element.appendChild(togglerLink);
414 | this.element.appendChild(children);
415 | // if formatter is set to be open call appendChildren
416 | if (this.isObject && this.isOpen) {
417 | this.appendChildren();
418 | }
419 | // add event listener for toggling
420 | if (this.isObject) {
421 | togglerLink.addEventListener('click', this.toggleOpen.bind(this));
422 | }
423 | return this.element;
424 | };
425 | /**
426 | * Appends all the children to children element
427 | * Animated option is used when user triggers this via a click
428 | */
429 | JSONFormatter.prototype.appendChildren = function (animated) {
430 | var _this = this;
431 | if (animated === void 0) { animated = false; }
432 | var children = this.element.querySelector("div." + helpers_ts_1.cssClass('children'));
433 | if (!children || this.isEmpty) {
434 | return;
435 | }
436 | if (animated) {
437 | var index_1 = 0;
438 | var addAChild_1 = function () {
439 | var key = _this.keys[index_1];
440 | var formatter = new JSONFormatter(_this.json[key], _this.open - 1, _this.config, key);
441 | children.appendChild(formatter.render());
442 | index_1 += 1;
443 | if (index_1 < _this.keys.length) {
444 | if (index_1 > MAX_ANIMATED_TOGGLE_ITEMS) {
445 | addAChild_1();
446 | }
447 | else {
448 | requestAnimationFrame(addAChild_1);
449 | }
450 | }
451 | };
452 | requestAnimationFrame(addAChild_1);
453 | }
454 | else {
455 | this.keys.forEach(function (key) {
456 | var formatter = new JSONFormatter(_this.json[key], _this.open - 1, _this.config, key);
457 | children.appendChild(formatter.render());
458 | });
459 | }
460 | };
461 | /**
462 | * Removes all the children from children element
463 | * Animated option is used when user triggers this via a click
464 | */
465 | JSONFormatter.prototype.removeChildren = function (animated) {
466 | if (animated === void 0) { animated = false; }
467 | var childrenElement = this.element.querySelector("div." + helpers_ts_1.cssClass('children'));
468 | if (animated) {
469 | var childrenRemoved_1 = 0;
470 | var removeAChild_1 = function () {
471 | if (childrenElement && childrenElement.children.length) {
472 | childrenElement.removeChild(childrenElement.children[0]);
473 | childrenRemoved_1 += 1;
474 | if (childrenRemoved_1 > MAX_ANIMATED_TOGGLE_ITEMS) {
475 | removeAChild_1();
476 | }
477 | else {
478 | requestAnimationFrame(removeAChild_1);
479 | }
480 | }
481 | };
482 | requestAnimationFrame(removeAChild_1);
483 | }
484 | else {
485 | if (childrenElement) {
486 | childrenElement.innerHTML = '';
487 | }
488 | }
489 | };
490 | return JSONFormatter;
491 | }());
492 |
493 |
494 | /***/ },
495 | /* 2 */
496 | /***/ function(module, exports, __webpack_require__) {
497 |
498 | // style-loader: Adds some css to the DOM by adding a