devtron
Version: 
Electron DevTools Extension
1,849 lines (1,535 loc) • 965 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict'
const Chrome = require('./chrome-helpers')
const Eval = require('./eval')
const View = require('./view')
const metadata = require('../package')
class AboutView extends View {
  constructor () {
    super('about-view')
    this.handleEvents()
    this.apis = Chrome.getChromeAPIs()
    this.render()
  }
  render () {
    this.versionLabel.textContent = metadata.version
    this.tabID.textContent = window.chrome.devtools.inspectedWindow.tabId
    if (window.chrome.runtime) {
      this.runtimeID.textContent = window.chrome.runtime.id
    }
    this.chromeAPIs.textContent = this.apis.sort().join('\n')
  }
  handleEvents () {
    this.issueButton.addEventListener('click', () => this.reportIssue())
  }
  reportIssue () {
    Eval.openExternal('https://github.com/electron/devtron/issues')
  }
}
module.exports = AboutView
},{"../package":189,"./chrome-helpers":7,"./eval":9,"./view":28}],2:[function(require,module,exports){
'use strict'
const SelectableView = require('./selectable-view')
const Eval = require('./eval')
class AccessibilityElementView extends SelectableView {
  constructor (element, parent) {
    super('element-row')
    this.path = element.selector
    this.pathId = element.id
    parent.appendChild(this.element)
    this.render()
    this.handleEvents(parent)
  }
  handleEvents (parent) {
    this.listenForSelection(parent)
    this.listenForSelectionKeys(parent.parentElement)
  }
  render () {
    this.selectorPath.textContent = this.path
    // Add a click-handler that will select the element.
    // Uses the `accessibilityAuditMap` defined in accessibility.js
    this.selectorPath.onclick = (evt) => {
      evt.stopPropagation()
      Eval.execute(`inspect(window.__devtron.accessibilityAuditMap.get(${this.pathId}))`)
    }
  }
  filter (searchText) {
    let matches = this.path.toLowerCase().includes(searchText)
    matches ? this.show() : this.hide()
    return matches
  }
}
module.exports = AccessibilityElementView
},{"./eval":9,"./selectable-view":26}],3:[function(require,module,exports){
'use strict'
const ExpandableView = require('./expandable-view')
const AccessibilityElementView = require('./accessibility-element-view')
class AccessibilityRuleView extends ExpandableView {
  constructor (rule, table) {
    super('rule-row')
    this.rule = rule
    this.count = rule.elements.length
    table.appendChild(this.element)
    this.handleEvents(table)
    this.render()
    this.children = rule.elements.map((element) => {
      return new AccessibilityElementView(element, table)
    })
    this.collapse()
  }
  handleEvents (table) {
    this.listenForSelection(table)
    this.listenForSelectionKeys(table.parentElement)
    this.listenForExpanderKeys(table.parentElement)
  }
  render () {
    this.status.textContent = this.rule.status
    this.severity.textContent = this.rule.severity
    this.ruleName.textContent = this.rule.title
    this.elementCount.textContent = `(${this.count})`
    if (this.count === 0) {
      this.disclosure.style.visibility = 'hidden'
    }
  }
  filter (searchText) {
    this.collapse()
    let matches = this.rule.title.toLowerCase().includes(searchText) || this.rule.status.toLowerCase().includes(searchText)
    this.children.forEach((child) => {
      if (child.filter(searchText)) matches = true
    })
    if (matches) {
      this.markCollapsed()
      this.show()
      this.markExpanded()
    } else {
      this.hide()
    }
    return matches
  }
}
module.exports = AccessibilityRuleView
},{"./accessibility-element-view":2,"./expandable-view":14}],4:[function(require,module,exports){
'use strict'
const Eval = require('./eval')
const AccessibilityRuleView = require('./accessibility-rule-view')
const View = require('./view')
const accessibility = require('./accessibility')
class AccessibilityView extends View {
  constructor () {
    super('accessibility-view')
    this.handleEvents()
  }
  handleEvents () {
    this.debounceInput(this.searchBox, () => this.filterAudits())
    this.docsButton.addEventListener('click', () => this.openDocs())
    this.accessibilityButton.addEventListener('click', () => this.audit())
  }
  audit () {
    accessibility.audit().then((results) => {
      return this.render(results)
    })
  }
  render (results) {
    this.tableDescription.classList.add('hidden')
    this.accessibilityResultsTable.innerHTML = ''
    this.children = results.map((result) => {
      return new AccessibilityRuleView(result, this.accessibilityResultsTable)
    })
    this.children[0].select()
  }
  openDocs () {
    Eval.openExternal('https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules')
  }
  filterAudits () {
    const searchText = this.getFilterText()
    if (searchText) {
      this.children.forEach((child) => child.filter(searchText))
    } else {
      this.children.forEach((child) => {
        child.show()
        child.collapse()
      })
    }
  }
  getFilterText () {
    return this.searchBox.value.toLowerCase()
  }
}
module.exports = AccessibilityView
},{"./accessibility":5,"./accessibility-rule-view":3,"./eval":9,"./view":28}],5:[function(require,module,exports){
const Eval = require('./eval')
exports.audit = () => {
  return Eval.execute(function () {
    const {axs} = window.__devtron // defined in browser-globals.js
    const config = new axs.AuditConfiguration({showUnsupportedRulesWarning: false})
    const results = axs.Audit.run(config)
    // Create a lookup map so users can click on an element to inspect it
    let idCounter = 0
    window.__devtron.accessibilityAuditMap = new Map()
    results.forEach(function (result) {
      const elements = result.elements || []
      elements.forEach(function (element) {
        const id = idCounter++
        element.__accessibilityAuditId = id
        window.__devtron.accessibilityAuditMap.set(id, element)
      })
    })
    return results.map(function (result) {
      const elements = result.elements || []
      let status = 'N/A'
      if (result.result === 'PASS') {
        status = 'Pass'
      } else if (result.result === 'FAIL') {
        status = 'Fail'
      }
      return {
        code: result.rule.code,
        severity: result.rule.severity,
        status: status,
        title: result.rule.heading,
        url: result.rule.url,
        elements: elements.map(function (element) {
          let selector = element.tagName.toLowerCase()
          if (element.className) {
            selector += '.' + element.className.split(' ').join('.')
          }
          return {
            selector: selector,
            id: element.__accessibilityAuditId
          }
        })
      }
    }).sort(function (resultA, resultB) {
      const statusA = resultA.status
      const statusB = resultB.status
      const severityA = resultA.severity
      const severityB = resultB.severity
      if (statusA === statusB) {
        if (severityA === severityB) {
          return resultB.elements.length - resultA.elements.length
        }
        if (severityA === 'Severe') return -1
        if (severityB === 'Severe') return 1
        if (severityA === 'Warning') return -1
        if (severityB === 'Warning') return 1
      } else {
        if (statusA === 'Fail') return -1
        if (statusB === 'Fail') return 1
        if (statusA === 'Pass') return -1
        if (statusB === 'Pass') return 1
      }
    })
  })
}
},{"./eval":9}],6:[function(require,module,exports){
// This defines globals that will be used in the browser context
// (via the content_scripts definition in manifest.json)
//
// It is generated via `npm run-script prepublish`
const axs = require('accessibility-developer-tools')
window.__devtron = window.__devtron || {}
window.__devtron.axs = axs
},{"accessibility-developer-tools":30}],7:[function(require,module,exports){
'use strict'
const objectPrototype = Object.getPrototypeOf({})
const isCustomClass = (object, prototype) => {
  if (typeof object !== 'object') return false
  if (Array.isArray(object)) return false
  return prototype && prototype !== objectPrototype
}
const checkAPI = (apis, parent, name, value) => {
  const api = parent + '.' + name
  if (typeof value === 'object') {
    findChromeAPIs(apis, api, value)
  } else {
    apis[api] = true
  }
}
const findChromeAPIs = (apis, parent, object) => {
  for (const name in object) {
    checkAPI(apis, parent, name, object[name])
  }
  const prototype = Object.getPrototypeOf(object)
  if (isCustomClass(object, prototype)) {
    Object.getOwnPropertyNames(prototype).filter((name) => {
      return name !== 'constructor'
    }).forEach((name) => {
      checkAPI(apis, parent, name, object[name])
    })
  }
  return Object.keys(apis)
}
exports.getChromeAPIs = () => findChromeAPIs({}, 'chrome', window.chrome)
},{}],8:[function(require,module,exports){
'use strict'
const EventView = require('./event-view')
const ExpandableView = require('./expandable-view')
class EmitterView extends ExpandableView {
  constructor (name, listeners, table) {
    super('emitter-row')
    this.name = name
    this.count = Object.keys(listeners).reduce((count, name) => {
      return count + listeners[name].length
    }, 0)
    table.appendChild(this.element)
    this.render()
    this.handleEvents(table)
    this.children = Object.keys(listeners).map((name) => {
      return new EventView(name, listeners[name], this, table)
    })
    this.collapse()
  }
  handleEvents (table) {
    this.listenForSelection(table)
    this.listenForSelectionKeys(table.parentElement)
    this.listenForExpanderKeys(table.parentElement)
  }
  render () {
    this.emitterName.textContent = this.name
    this.listenerCount.textContent = `(${this.count})`
  }
  filter (searchText) {
    this.collapse()
    let matches = this.name.includes(searchText)
    this.children.forEach((child) => {
      if (child.filter(searchText)) matches = true
    })
    if (matches) {
      this.markCollapsed()
      this.show()
      this.markExpanded()
    } else {
      this.hide()
    }
    return matches
  }
}
module.exports = EmitterView
},{"./event-view":12,"./expandable-view":14}],9:[function(require,module,exports){
(function (process){
'use strict'
class Eval {
  static execute (expression) {
    if (typeof expression === 'function') {
      expression = `(${expression})`
      if (arguments.length > 1) {
        let expressionArgs = JSON.stringify(Array.prototype.slice.call(arguments, 1))
        expression += `.apply(this, ${expressionArgs})`
      } else {
        expression += '()'
      }
    }
    expression = `
      (function () {
        window.__devtron = window.__devtron || {}
        window.__devtron.evaling = true
        var require = window.__devtron.require || window.require
        var process = window.__devtron.process || window.process
        try {
          return ${expression}
        } finally {
          window.__devtron.evaling = false
        }
      })()
    `
    return new Promise((resolve, reject) => {
      window.chrome.devtools.inspectedWindow.eval(expression, (result, error) => {
        if (error) {
          if (error.isException && error.value) {
            let stack = error.value
            error = new Error(stack.split('\n')[0])
            error.stack = stack
          }
          reject(error)
        } else {
          resolve(result)
        }
      })
    })
  }
  static getFileSize (path) {
    return Eval.execute((path) => {
      try {
        return require('fs').statSync(path).size
      } catch (error) {
        return -1
      }
    }, path)
  }
  static openExternal (urlToOpen) {
    return Eval.execute((urlToOpen) => {
      return require('electron').shell.openExternal(urlToOpen)
    }, urlToOpen)
  }
  static getFileVersion (filePath) {
    return Eval.execute((filePath) => {
      if (/\/atom\.asar\/(browser|common|renderer)\//.test(filePath)) return process.versions.electron
      const fs = require('fs')
      const path = require('path')
      const appVersion = require('electron').remote.app.getVersion()
      let directory = path.dirname(filePath)
      while (path.basename(directory) !== 'node_modules') {
        try {
          let metadataPath = path.join(directory, 'package.json')
          let version = JSON.parse(fs.readFileSync(metadataPath)).version
          if (version) return version
        } catch (error) {
          // Ignore and continue
        }
        let nextDirectory = path.dirname(directory)
        if (nextDirectory === directory) break
        directory = nextDirectory
      }
      return appVersion
    }, filePath)
  }
  static isDebugMode () {
    return Eval.execute(() => {
      return process && !!process.env.DEVTRON_DEBUG_PATH
    })
  }
  static isApiAvailable () {
    return Eval.execute(() => {
      return typeof process === 'object' && typeof require === 'function'
    })
  }
  // Start a local http server in the currently running app that will
  // listen to requests sent by a browser
  static startServer () {
    return Eval.execute(() => {
      const path = require('path')
      const serverPath = path.join(process.env.DEVTRON_DEBUG_PATH, 'test', 'server.js')
      require(serverPath)
    })
  }
  // Implement the window.chrome.devtools.inspectedWindow.eval API via
  // window.fetch talking to a local http server running in an opened
  // Electron app
  static proxyToServer () {
    window.chrome.devtools = {
      inspectedWindow: {
        eval: function (expression, callback) {
          window.fetch('http://localhost:3948', {
            body: JSON.stringify({expression: expression}),
            headers: {
              'Accept': 'application/json',
              'Content-Type': 'application/json'
            },
            method: 'POST'
          }).then((response) => {
            return response.json()
          }).then((json) => {
            callback(json.result)
          }).catch((error) => {
            callback(null, error)
          })
        }
      }
    }
  }
}
module.exports = Eval
}).call(this,require('_process'))
},{"_process":188,"electron":undefined,"fs":31,"path":187}],10:[function(require,module,exports){
(function (process){
'use strict'
const Eval = require('./eval')
exports.getEvents = () => {
  return Eval.execute(() => {
    const formatCode = (listener) => {
      let lines = listener.split(/\r?\n/)
      if (lines.length === 1) return listener
      let lastLine = lines[lines.length - 1]
      let lastLineMatch = /^(\s+)}/.exec(lastLine)
      if (!lastLineMatch) return listener
      let whitespaceRegex = new RegExp('^' + lastLineMatch[1])
      return lines.map((line) => {
        return line.replace(whitespaceRegex, '')
      }).join('\n')
    }
    const getEvents = (emitter) => {
      const events = {}
      Object.keys(emitter._events).sort().forEach((name) => {
        let listeners = emitter.listeners(name)
        if (listeners.length > 0) {
          events[name] = listeners.map((listener) => {
            return formatCode(listener.toString())
          })
        }
      })
      return events
    }
    const electron = require('electron')
    const remote = electron.remote
    return {
      'electron.remote.getCurrentWindow()': getEvents(remote.getCurrentWindow()),
      'electron.remote.getCurrentWebContents()': getEvents(remote.getCurrentWebContents()),
      'electron.remote.app': getEvents(remote.app),
      'electron.remote.ipcMain': getEvents(remote.ipcMain),
      'electron.ipcRenderer': getEvents(electron.ipcRenderer),
      'electron.remote.process': getEvents(remote.process),
      'global.process': getEvents(process)
    }
  })
}
}).call(this,require('_process'))
},{"./eval":9,"_process":188,"electron":undefined}],11:[function(require,module,exports){
'use strict'
const highlight = require('highlight.js')
const SelectableView = require('./selectable-view')
class EventListenerView extends SelectableView {
  constructor (listener, parent) {
    super('listener-code-row')
    this.listener = listener
    parent.appendChild(this.element)
    this.render()
    this.handleEvents(parent)
  }
  handleEvents (parent) {
    this.listenForSelection(parent)
    this.listenForSelectionKeys(parent.parentElement)
  }
  render () {
    this.listenerValue.textContent = this.listener
    highlight.highlightBlock(this.listenerValue)
  }
  filter (searchText) {
    let matches = this.listener.toLowerCase().includes(searchText)
    matches ? this.show() : this.hide()
    return matches
  }
}
module.exports = EventListenerView
},{"./selectable-view":26,"highlight.js":33}],12:[function(require,module,exports){
'use strict'
const ExpandableView = require('./expandable-view')
const EventListenerView = require('./event-listener-view')
class EventView extends ExpandableView {
  constructor (name, listeners, parent, table) {
    super('event-type-row')
    this.name = name
    this.count = listeners.length
    this.parent = parent
    table.appendChild(this.element)
    this.handleEvents(table)
    this.render()
    this.children = listeners.map((listener) => {
      return new EventListenerView(listener, table)
    })
    this.collapse()
  }
  handleEvents (table) {
    this.listenForSelection(table)
    this.listenForSelectionKeys(table.parentElement)
    this.listenForExpanderKeys(table.parentElement)
  }
  render () {
    this.eventName.textContent = this.name
    this.listenerCount.textContent = `(${this.count})`
  }
  filter (searchText) {
    this.collapse()
    let matches = this.name.includes(searchText)
    this.children.forEach((child) => {
      if (child.filter(searchText)) matches = true
    })
    if (matches) {
      this.markCollapsed()
      this.show()
      this.markExpanded()
    } else {
      this.hide()
    }
    return matches
  }
}
module.exports = EventView
},{"./event-listener-view":11,"./expandable-view":14}],13:[function(require,module,exports){
'use strict'
const events = require('./event-helpers')
const EmitterView = require('./emitter-view')
const View = require('./view')
class EventsView extends View {
  constructor () {
    super('events-view')
    this.children = []
    this.handleEvents()
  }
  reload () {
    this.loadEvents()
  }
  focus () {
    this.listenersTable.focus()
  }
  handleEvents () {
    this.loadButton.addEventListener('click', () => this.loadEvents())
    this.debounceInput(this.searchBox, () => this.filterEvents())
  }
  filterEvents () {
    const searchText = this.searchBox.value.toLowerCase()
    if (searchText) {
      this.children.forEach((child) => {
        child.filter(searchText)
      })
    } else {
      this.children.forEach((child) => {
        child.show()
        child.collapse()
      })
    }
  }
  loadEvents () {
    events.getEvents().then((events) => {
      this.tableDescription.classList.add('hidden')
      this.listenersTable.innerHTML = ''
      this.destroyChildren()
      this.children = Object.keys(events).map((name) => {
        return new EmitterView(name, events[name], this.listenersTable)
      })
      this.children[0].select()
    }).catch((error) => {
      console.error('Getting event listeners failed')
      console.error(error.stack || error)
    })
  }
}
module.exports = EventsView
},{"./emitter-view":8,"./event-helpers":10,"./view":28}],14:[function(require,module,exports){
'use strict'
const SelectableView = require('./selectable-view')
class ExpandableView extends SelectableView {
  constructor (viewId) {
    super(viewId)
    this.listenForArrowClicks()
  }
  toggleExpansion () {
    if (this.expanded) {
      this.collapse()
    } else {
      this.expand()
    }
  }
  markExpanded () {
    this.expanded = true
    this.disclosure.classList.add('disclosure-arrow-expanded')
  }
  expand () {
    this.markExpanded()
    this.children.forEach((child) => child.show())
  }
  markCollapsed () {
    this.expanded = false
    this.disclosure.classList.remove('disclosure-arrow-expanded')
  }
  collapse () {
    this.markCollapsed()
    this.children.forEach((child) => child.hide())
  }
  collapseAll () {
    this.collapse()
    this.children.forEach((child) => child.collapse())
  }
  hide () {
    super.hide()
    this.children.forEach((child) => child.hide())
  }
  show () {
    super.show()
    if (this.expanded) this.children.forEach((child) => child.show())
  }
  listenForArrowClicks () {
    this.disclosure.addEventListener('click', () => this.toggleExpansion())
  }
  listenForExpanderKeys (emitter) {
    this.bindListener(emitter, 'keydown', (event) => {
      if (!this.selected) return
      if (event.altKey || event.metaKey || event.ctrlKey) return
      switch (event.code) {
        case 'ArrowLeft':
          if (this.expanded) {
            this.collapse()
          } else if (this.parent && this.parent.expanded) {
            this.deselect()
            this.parent.collapse()
            this.parent.select()
          }
          event.stopImmediatePropagation()
          event.preventDefault()
          break
        case 'ArrowRight':
          this.expand()
          event.stopImmediatePropagation()
          event.preventDefault()
          break
      }
    })
  }
}
module.exports = ExpandableView
},{"./selectable-view":26}],15:[function(require,module,exports){
'use strict'
const AboutView = require('./about-view')
const Eval = require('./eval')
const EventsView = require('./events-view')
const IpcView = require('./ipc-view')
const LintView = require('./lint-view')
const AccessibilityView = require('./accessibility-view')
const ModulesView = require('./modules-view')
const NodeIntegrationView = require('./node-integration-view')
const SidebarView = require('./sidebar-view')
document.addEventListener('DOMContentLoaded', () => {
  Eval.isApiAvailable().then(function (apiAvailable) {
    const sidebarView = new SidebarView()
    if (apiAvailable) {
      sidebarView.addPane(new ModulesView())
      sidebarView.addPane(new EventsView())
      sidebarView.addPane(new IpcView())
      sidebarView.addPane(new LintView())
      sidebarView.addPane(new AccessibilityView())
      sidebarView.addPane(new AboutView())
      listenForLinkClicks()
    } else {
      sidebarView.addPane(new NodeIntegrationView())
    }
  })
})
if (!window.chrome.devtools) {
  Eval.proxyToServer()
} else {
  Eval.isDebugMode().then(function (debugMode) {
    if (debugMode) Eval.startServer()
  })
}
const listenForLinkClicks = () => {
  document.body.addEventListener('click', (event) => {
    const href = event.target.href
    if (href) {
      Eval.openExternal(href)
      event.stopImmediatePropagation()
      event.preventDefault()
    }
  })
}
},{"./about-view":1,"./accessibility-view":4,"./eval":9,"./events-view":13,"./ipc-view":18,"./lint-view":20,"./modules-view":24,"./node-integration-view":25,"./sidebar-view":27}],16:[function(require,module,exports){
'use strict'
const highlight = require('highlight.js')
const SelectableView = require('./selectable-view')
class IpcEventView extends SelectableView {
  constructor (event, table) {
    super('ipc-table-row')
    this.event = event
    this.internalEvent = event.channel.startsWith('ELECTRON_') || event.channel.startsWith('ATOM_')
    table.appendChild(this.element)
    this.listenForSelection(table)
    this.listenForSelectionKeys(table.parentElement)
    this.render()
  }
  render () {
    this.eventName.textContent = this.event.channel
    this.eventName.title = this.event.channel
    if (this.event.sent) {
      this.eventIcon.classList.add('ipc-icon-sent')
      this.eventIcon.title = 'Outgoing'
    } else {
      this.eventIcon.classList.add('ipc-icon-received')
      this.eventIcon.title = 'Incoming'
    }
    if (!this.event.sync) {
      this.syncIcon.style.display = 'none'
    }
    if (this.event.listenerCount > 0) {
      this.eventListenerCount.textContent = this.event.listenerCount
    }
    this.eventData.textContent = this.event.data
    highlight.highlightBlock(this.eventData)
  }
  filter (searchText) {
    let matches = this.event.channel.toLowerCase().includes(searchText)
    matches = matches || this.event.data.toLowerCase().includes(searchText)
    matches ? this.show() : this.hide()
  }
}
module.exports = IpcEventView
},{"./selectable-view":26,"highlight.js":33}],17:[function(require,module,exports){
'use strict'
const Eval = require('./eval')
exports.listenForEvents = () => {
  return Eval.execute(() => {
    // Return if events are already being listened to to prevent duplicates
    // when reloading the extension
    if (window.__devtron.events != null) {
      window.__devtron.events = []
      return
    }
    window.__devtron.events = []
    const ipcRenderer = require('electron').ipcRenderer
    const ignoredEvents = {
      'ATOM_BROWSER_DEREFERENCE': true,
      'ELECTRON_BROWSER_DEREFERENCE': true
    }
    const trackEvent = (channel, args, sent, sync) => {
      if (window.__devtron.evaling) return
      if (ignoredEvents.hasOwnProperty(channel)) return
      let data
      try {
        data = JSON.stringify(args)
      } catch (error) {
        data = `Failed to serialize args to JSON: ${error.message || error}`
      }
      window.__devtron.events.push({
        channel: channel,
        data: data,
        listenerCount: ipcRenderer.listenerCount(channel),
        sent: !!sent,
        sync: !!sync
      })
    }
    const originalEmit = ipcRenderer.emit
    ipcRenderer.emit = function (channel, event) {
      const args = Array.prototype.slice.call(arguments, 2)
      trackEvent(channel, args)
      return originalEmit.apply(ipcRenderer, arguments)
    }
    const originalSend = ipcRenderer.send
    ipcRenderer.send = function (channel) {
      const args = Array.prototype.slice.call(arguments, 1)
      trackEvent(channel, args, true)
      return originalSend.apply(ipcRenderer, arguments)
    }
    const originalSendSync = ipcRenderer.sendSync
    ipcRenderer.sendSync = function (channel) {
      const args = Array.prototype.slice.call(arguments, 1)
      trackEvent(channel, args, true, true)
      const returnValue = originalSendSync.apply(ipcRenderer, arguments)
      trackEvent(channel, [returnValue], false, true)
      return returnValue
    }
  })
}
exports.getEvents = () => {
  return Eval.execute(() => {
    const events = window.__devtron.events
    if (events) window.__devtron.events = []
    return events
  }).then((events) => {
    if (events) return events
    // Start listening for events if array is missing meaning
    // the window was reloaded
    return exports.listenForEvents().then(() => [])
  })
}
},{"./eval":9,"electron":undefined}],18:[function(require,module,exports){
'use strict'
const Eval = require('./eval')
const ipc = require('./ipc-helpers')
const IpcEventView = require('./ipc-event-view')
const View = require('./view')
class IpcView extends View {
  constructor () {
    super('ipc-view')
    this.children = []
    this.recording = false
    this.handleEvents()
  }
  handleEvents () {
    this.debounceInput(this.searchBox, () => this.filterEvents())
    this.clearButton.addEventListener('click', () => this.clear())
    this.recordButton.addEventListener('click', () => this.toggleRecording())
    this.docsButton.addEventListener('click', () => this.openDocs())
    this.hideInternalButton.addEventListener('click', () => this.toggleHideInternal())
  }
  toggleHideInternal () {
    if (this.hideInternal) {
      this.hideInternalButton.classList.remove('active')
      this.hideInternal = false
      this.children.forEach((child) => {
        if (child.internalEvent) child.show()
      })
    } else {
      this.hideInternalButton.classList.add('active')
      this.hideInternal = true
      this.children.forEach((child) => {
        if (child.internalEvent) child.hide()
      })
    }
  }
  toggleRecording () {
    if (this.recording) {
      this.stopRecording()
      this.recordButton.classList.remove('active')
    } else {
      this.startRecording()
      this.recordButton.classList.add('active')
    }
  }
  startRecording () {
    ipc.listenForEvents().then(() => {
      this.recording = true
      this.addNewEvents()
    }).catch((error) => {
      console.error('Listening for IPC events failed')
      console.error(error.stack || error)
    })
  }
  stopRecording () {
    clearTimeout(this.timeoutId)
    this.recording = false
  }
  openDocs () {
    Eval.openExternal('http://electron.atom.io/docs/latest/api/ipc-main')
  }
  clear () {
    this.ipcTable.innerHTML = ''
    this.destroyChildren()
  }
  addNewEvents () {
    ipc.getEvents().then((events) => {
      if (!this.recording) return
      events.forEach((event) => this.addEvent(event))
      this.timeoutId = setTimeout(() => this.addNewEvents(), 333)
    }).catch((error) => {
      console.error('Getting IPC events failed')
      console.error(error.stack || error)
    })
  }
  addEvent (event) {
    this.tableDescription.classList.add('hidden')
    const eventView = new IpcEventView(event, this.ipcTable)
    this.children.push(eventView)
    this.filterIncomingEvent(eventView)
  }
  filterIncomingEvent (view) {
    if (this.hideInternal && view.internalEvent) {
      view.hide()
    } else {
      const searchText = this.getFilterText()
      if (searchText) view.filter(searchText)
    }
  }
  filterEvents () {
    const searchText = this.getFilterText()
    if (searchText) {
      this.children.forEach((child) => child.filter(searchText))
    } else {
      this.children.forEach((child) => child.show())
    }
  }
  getFilterText () {
    return this.searchBox.value.toLowerCase()
  }
}
module.exports = IpcView
},{"./eval":9,"./ipc-event-view":16,"./ipc-helpers":17,"./view":28}],19:[function(require,module,exports){
(function (process){
'use strict'
const Eval = require('./eval')
exports.isUsingAsar = () => {
  return Eval.execute(() => {
    const mainPath = require('electron').remote.process.mainModule.filename
    return /[\\/]app\.asar[\\/]/.test(mainPath)
  })
}
exports.isListeningForCrashEvents = () => {
  return Eval.execute(() => {
    const webContents = require('electron').remote.getCurrentWebContents()
    // For versions less than 1.x.y
    // Electron has an crashed listener, so look for more than 1
    const crashedForwarding = /^0/.test(process.versions.electron)
    const minCount = crashedForwarding ? 1 : 0
    return webContents.listenerCount('crashed') > minCount
  })
}
exports.isListeningForUnresponsiveEvents = () => {
  return Eval.execute(() => {
    const browserWindow = require('electron').remote.getCurrentWindow()
    return browserWindow.listenerCount('unresponsive') > 0
  })
}
exports.isListeningForUncaughtExceptionEvents = () => {
  return Eval.execute(() => {
    const mainProcess = require('electron').remote.process
    // Electron has an uncaughtException listener, so look for more than 1
    return mainProcess.listenerCount('uncaughtException') > 1
  })
}
exports.getCurrentElectronVersion = () => {
  return Eval.execute(() => {
    return process.versions.electron
  })
}
exports.getLatestElectronVersion = () => {
  return Eval.execute(() => {
    return window.__devtron.latestElectronVersion
  })
}
exports.fetchLatestVersion = () => {
  return Eval.execute(() => {
    window.fetch('https://atom.io/download/atom-shell/index.json')
      .then((response) => {
        return response.json()
      }).then((versions) => {
        window.__devtron.latestElectronVersion = versions[0].version
      }).catch(() => {
        window.__devtron.latestElectronVersion = 'unknown'
      })
  })
}
}).call(this,require('_process'))
},{"./eval":9,"_process":188,"electron":undefined}],20:[function(require,module,exports){
'use strict'
const highlight = require('highlight.js')
const Lint = require('./lint-helpers')
const View = require('./view')
class LintView extends View {
  constructor () {
    super('lint-view')
    this.handleEvents()
    this.highlightBlocks()
  }
  reload () {
    this.lint()
  }
  highlightBlocks () {
    highlight.highlightBlock(this.crashedExample)
    highlight.highlightBlock(this.unresponsiveExample)
    highlight.highlightBlock(this.uncaughtExample)
  }
  handleEvents () {
    this.lintButton.addEventListener('click', () => this.lint())
  }
  updateAlert (alertElement, descriptionElement, passing) {
    if (passing) {
      alertElement.classList.add('alert-lint-pass')
      descriptionElement.classList.add('hidden')
    } else {
      alertElement.classList.add('alert-lint-fail')
      descriptionElement.classList.remove('hidden')
    }
    alertElement.classList.remove('hidden')
    this.tableDescription.classList.add('hidden')
  }
  lint () {
    Lint.isUsingAsar().then((usingAsar) => {
      this.updateAlert(this.usingAsar, this.asarDescription, usingAsar)
    })
    Lint.isListeningForCrashEvents().then((listening) => {
      this.updateAlert(this.crashListener, this.crashDescription, listening)
    })
    Lint.isListeningForUnresponsiveEvents().then((listening) => {
      this.updateAlert(this.unresponsiveListener, this.unresponsiveDescription, listening)
    })
    Lint.isListeningForUncaughtExceptionEvents().then((listening) => {
      this.updateAlert(this.uncaughtListener, this.uncaughtDescription, listening)
    })
    this.checkVersion()
  }
  checkVersion () {
    Lint.getCurrentElectronVersion().then((version) => {
      this.currentVersion = version
      this.updateVersion()
    })
    Lint.fetchLatestVersion()
    this.checkLatestVersion()
  }
  checkLatestVersion () {
    Lint.getLatestElectronVersion().then((version) => {
      if (version) {
        this.latestVersion = version
        this.updateVersion()
      } else {
        setTimeout(() => this.checkLatestVersion(), 250)
      }
    })
  }
  updateVersion () {
    if (!this.latestVersion || !this.currentVersion) return
    const upToDate = this.latestVersion === this.currentVersion
    this.updateAlert(this.outdated, this.outdatedDescription, upToDate)
    this.latestLabel.textContent = this.latestVersion
    this.versionLabel.textContent = this.currentVersion
  }
}
module.exports = LintView
},{"./lint-helpers":19,"./view":28,"highlight.js":33}],21:[function(require,module,exports){
'use strict'
const Eval = require('./eval')
const Module = require('./module')
const loadSizes = (mainModule) => {
  let totalSize = 0
  return Promise.all(mainModule.toArray().map((module) => {
    return Eval.getFileSize(module.path).then((size) => {
      totalSize += size
      return module.setSize(size)
    })
  })).then(() => {
    mainModule.totalSize = totalSize
    return mainModule
  })
}
const loadVersions = (mainModule) => {
  return Promise.all(mainModule.toArray().map((module) => {
    return Eval.getFileVersion(module.path).then((version) => module.setVersion(version))
  })).then(() => mainModule)
}
const createModules = (mainModule) => {
  const resourcesPath = mainModule.resourcesPath
  const appName = mainModule.appName
  const processModule = (node) => {
    const module = new Module(node.path, resourcesPath, appName)
    node.children.forEach((childNode) => {
      module.addChild(processModule(childNode))
    })
    return module
  }
  const convertedMainModule = processModule(mainModule)
  convertedMainModule.count = mainModule.count
  return convertedMainModule
}
const getRenderRequireGraph = () => {
  return Eval.execute(() => {
    let count = 0
    const walkModule = (module) => {
      count++
      let modulePath = module.filename || module.id
      if (process.platform === 'win32') {
        modulePath = modulePath.replace(/\\/g, '/')
      }
      return {
        path: modulePath,
        children: module.children.map(walkModule)
      }
    }
    const mainModule = walkModule(process.mainModule)
    mainModule.resourcesPath = process.resourcesPath
    mainModule.appName = require('electron').remote.app.getName()
    mainModule.count = count
    return mainModule
  })
}
const getMainRequireGraph = () => {
  return Eval.execute(() => {
    let process = require('electron').remote.process
    let count = 0
    const walkModule = (module) => {
      count++
      let modulePath = module.filename || module.id
      if (process.platform === 'win32') {
        modulePath = modulePath.replace(/\\/g, '/')
      }
      return {
        path: modulePath,
        children: module.children.map(walkModule)
      }
    }
    const mainModule = walkModule(process.mainModule)
    mainModule.resourcesPath = process.resourcesPath
    mainModule.appName = require('electron').remote.app.getName()
    mainModule.count = count
    return mainModule
  })
}
exports.getRenderModules = () => {
  return getRenderRequireGraph().then(createModules).then(loadSizes).then(loadVersions)
}
exports.getMainModules = () => {
  return getMainRequireGraph().then(createModules).then(loadSizes).then(loadVersions)
}
},{"./eval":9,"./module":23,"electron":undefined}],22:[function(require,module,exports){
'use strict'
const ExpandableView = require('./expandable-view')
const Humanize = require('humanize-plus')
class ModuleView extends ExpandableView {
  constructor (module, table, parent) {
    super('requires-table-row')
    this.parent = parent
    this.module = module
    this.table = table
    table.appendChild(this.element)
    this.render()
    this.children = this.module.children.map((child) => {
      return new ModuleView(child, table, this)
    })
    this.module.getDepth() === 1 ? this.expand() : this.collapse()
    if (!this.module.hasChildren()) this.disclosure.style.display = 'none'
    this.handleEvents()
  }
  handleEvents () {
    this.listenForSelection(this.table)
    this.listenForSelectionKeys(this.table.parentElement)
    this.listenForExpanderKeys(this.table.parentElement)
  }
  getHumanizedSize () {
    const size = this.module.getSize()
    return Humanize.fileSize(size).replace('bytes', 'B')
  }
  render () {
    this.moduleName.textContent = this.module.getLibrary()
    this.moduleName.title = this.module.getLibrary()
    this.moduleVersion.textContent = this.module.getVersion()
    this.fileSize.textContent = this.getHumanizedSize()
    this.fileName.textContent = this.module.getName()
    this.fileName.title = this.module.path
    this.moduleDirectory.textContent = this.module.getDirectory()
    this.moduleDirectory.title = this.module.path
    this.pathSection.style['padding-left'] = `${(this.module.getDepth()) * 15}px`
  }
  filter (searchText) {
    this.collapse()
    let matches = this.module.getId().includes(searchText)
    matches = matches || this.module.getName().toLowerCase().includes(searchText)
    this.children.forEach((child) => {
      if (child.filter(searchText)) matches = true
    })
    if (matches) {
      this.markCollapsed()
      this.show()
      this.markExpanded()
    } else {
      this.hide()
    }
    return matches
  }
}
module.exports = ModuleView
},{"./expandable-view":14,"humanize-plus":186}],23:[function(require,module,exports){
'use strict'
class Module {
  constructor (path, resourcesPath, appName) {
    this.path = path
    this.resourcesPath = resourcesPath
    this.appName = appName
    this.size = -1
    this.version = ''
    this.children = []
  }
  setVersion (version) {
    this.version = version
    return this
  }
  getVersion () {
    return this.version
  }
  setSize (size) {
    this.size = size
    return this
  }
  getSize () {
    return this.size
  }
  hasChildren () {
    return this.children.length > 0
  }
  addChild (child) {
    this.children.push(child)
    child.parent = this
  }
  getPath () {
    return this.path
  }
  getDepth () {
    let depth = 1
    let parent = this.parent
    while (parent != null) {
      depth++
      parent = parent.parent
    }
    return depth
  }
  getName () {
    if (!this.name) this.name = /\/([^\/]+)$/.exec(this.path)[1]
    return this.name
  }
  getDirectory () {
    let directoryPath = /(.+)\/[^\/]+$/.exec(this.path)[1]
    if (directoryPath.indexOf(this.resourcesPath) === 0) {
      directoryPath = directoryPath.substring(this.resourcesPath.length + 1)
    }
    return directoryPath
  }
  computeLibrary () {
    if (/\/atom\.asar\/(browser|common|renderer)\//.test(this.path)) return 'Electron'
    const libraryPattern = /\/node_modules\/([^\/]+)(?=\/)/g
    let match = libraryPattern.exec(this.path)
    while (match != null) {
      let library = match[1]
      match = libraryPattern.exec(this.path)
      if (match == null) return library
    }
    return this.appName
  }
  getLibrary () {
    if (!this.library) this.library = this.computeLibrary()
    return this.library
  }
  getId () {
    if (!this.id) this.id = this.getLibrary().toLowerCase()
    return this.id
  }
  visit (callback) {
    callback(this)
    this.children.forEach((child) => child.visit(callback))
  }
  toArray () {
    const modules = []
    this.visit((module) => modules.push(module))
    return modules
  }
}
module.exports = Module
},{}],24:[function(require,module,exports){
'use strict'
const Humanize = require('humanize-plus')
const modules = require('./module-helpers')
const ModuleView = require('./module-view')
const View = require('./view')
class ModulesView extends View {
  constructor () {
    super('modules-view')
    this.handleEvents()
  }
  reload () {
    this.loadGraph()
  }
  focus () {
    if (this.mainProcessTable.classList.contains('hidden')) {
      this.renderRequireRows.focus()
    } else {
      this.mainRequireRows.focus()
    }
  }
  handleEvents () {
    this.loadButton.addEventListener('click', () => this.loadGraph())
    this.debounceInput(this.searchBox, () => this.filterGraph())
    this.mainProcessTab.addEventListener('click', () => {
      this.mainProcessTab.classList.add('active')
      this.renderProcessTab.classList.remove('active')
      this.mainProcessTable.classList.remove('hidden')
      this.renderProcessTable.classList.add('hidden')
      this.mainRequireRows.focus()
    })
    this.renderProcessTab.addEventListener('click', () => {
      this.mainProcessTab.classList.remove('active')
      this.renderProcessTab.classList.add('active')
      this.mainProcessTable.classList.add('hidden')
      this.renderProcessTable.classList.remove('hidden')
      this.renderRequireRows.focus()
    })
  }
  getTabLabelSuffix (mainModule) {
    const count = mainModule.count.toLocaleString()
    const size = Humanize.fileSize(mainModule.totalSize)
    return `- ${count} files, ${size}`
  }
  loadGraph () {
    modules.getRenderModules().then((mainModule) => {
      this.tableDescription.classList.add('hidden')
      const suffix = this.getTabLabelSuffix(mainModule)
      this.renderProcessTab.textContent = `Renderer Process ${suffix}`
      this.renderRequireRows.innerHTML = ''
      if (this.rootRenderView) this.rootRenderView.destroy()
      this.rootRenderView = new ModuleView(mainModule, this.renderRequireRows)
      this.rootRenderView.select()
    }).catch((error) => {
      console.error('Loading render modules failed')
      console.error(error.stack || error)
    })
    modules.getMainModules().then((mainModule) => {
      const suffix = this.getTabLabelSuffix(mainModule)
      this.mainProcessTab.textContent = `Main Process ${suffix}`
      this.mainRequireRows.innerHTML = ''
      if (this.rootMainView) this.rootMainView.destroy()
      this.rootMainView = new ModuleView(mainModule, this.mainRequireRows)
      this.rootMainView.select()
    }).catch((error) => {
      console.error('Loading main modules failed')
      console.error(error.stack || error)
    })
  }
  filterGraph () {
    const searchText = this.searchBox.value.toLowerCase()
    if (searchText) {
      this.rootRenderView.filter(searchText)
      this.rootMainView.filter(searchText)
    } else {
      this.rootRenderView.collapseAll()
      this.rootRenderView.expand()
      this.rootMainView.collapseAll()
      this.rootMainView.expand()
    }
  }
}
module.exports = ModulesView
},{"./module-helpers":21,"./module-view":22,"./view":28,"humanize-plus":186}],25:[function(require,module,exports){
'use strict'
const highlight = require('highlight.js')
const View = require('./view')
class NodeIntegrationView extends View {
  constructor () {
    super('node-integration-view')
    this.highlightBlocks()
  }
  highlightBlocks () {
    highlight.highlightBlock(this.browserWindowExample)
    highlight.highlightBlock(this.devtronExample)
    highlight.highlightBlock(this.envCheckExample)
  }
}
module.exports = NodeIntegrationView
},{"./view":28,"highlight.js":33}],26:[function(require,module,exports){
'use strict'
const View = require('./view')
class SelectableView extends View {
  select () {
    this.selected = true
    this.element.classList.add('active')
    this.element.scrollIntoViewIfNeeded()
  }
  deselect () {
    this.selected = false
    this.element.classList.remove('active')
  }
  selectNext () {
    let next = this.element.nextElementSibling
    while (next && (next.view instanceof SelectableView)) {
      if (next.view.isHidden()) {
        next = next.nextElementSibling
        continue
      }
      this.deselect()
      next.view.select()
      break
    }
  }
  selectPrevious () {
    let previous = this.element.previousElementSibling
    while (previous && (previous.view instanceof SelectableView)) {
      if (previous.view.isHidden()) {
        previous = previous.previousElementSibling
        continue
      }
      this.deselect()
      previous.view.select()
      break
    }
  }
  listenForSelection (emitter) {
    this.bindListener(emitter, 'mousedown', (event) => {
      if (this.element.contains(event.target)) {
        this.select()
      } else {
        this.deselect()
      }
    })
  }
  listenForSelectionKeys (emitter) {
    this.bindListener(emitter, 'keydown', (event) => {
      if (!this.selected) return
      if (event.altKey || event.metaKey || event.ctrlKey) return
      switch (event.code) {
        case 'ArrowDown':
          this.selectNext()
          event.stopImmediatePropagation()
          event.preventDefault()
          break
        case 'ArrowUp':
          this.selectPrevious()
          event.stopImmediatePropagation()
          event.preventDefault()
          break
      }
    })
  }
}
module.exports = SelectableView
},{"./view":28}],27:[function(require,module,exports){
'use strict'
const View = require('./view')
class SidebarView extends View {
  constructor () {
    super('sidebar-view')
    this.panes = []
    this.links = [this.requireLink, this.eventsLink, this.ipcLink, this.lintLink, this.accessibilityLink, this.aboutLink]
    this.panesElement = document.querySelector('#pane-group')
    this.panesElement.appendChild(this.element)
    this.handleEvents()
  }
  handleEvents () {
    document.body.addEventListener('keydown', (event) => {
      if (event.ctrlKey || event.metaKey) return
      if (!event.altKey) return
      switch (event.code) {
        case 'ArrowDown':
          this.selectNext()
          event.stopImmediatePropagation()
          event.preventDefault()
          break
        case 'ArrowUp':
          this.selectPrevious()
          event.stopImmediatePropagation()
          event.preventDefault()
          break
      }
    })
    document.body.addEventListener('keydown', (event) => {
      if ((event.ctrlKey || event.metaKey) && event.code === 'KeyE') {
        this.activePane.reload()
        this.activePane.focus()
        event.stopImmediatePropagation()
        event.preventDefault()
      }
    })
    this.element.addEventListener('mousedown', (event) => {
      let paneLink = event.target.dataset.paneLink
      if (paneLink) this.selectPane(paneLink)
    })
  }
  activateLink (name) {
    this.links.forEach((link) => {
      if (link.dataset.paneLink === name) {
        link.classList.add('active')
      } else {
        link.classList.remove('active')
      }
    })
  }
  addPane (view) {
    if (this.panes.length === 0) this.activePane = view
    this.panes.push(view)
    this.panesElement.appendChild(view.element)
  }
  findPane (name) {
    return this.panes.find((view) => view.element.dataset.pane === name)
  }
  selectPane (name) {
    const pane = this.findPane(name)
    if (!pane) return
    this.panes.forEach((view) => view.hide())
    pane.show()
    pane.focus()
    this.activePane = pane
    this.activateLink(name)
  }
  selectPrevious () {
    const selectedIndex = this.panes.indexOf(this.activePane)
    const previousIndex = Math.max(selectedIndex - 1, 0)
    this.selectPane(this.panes[previousIndex].element.dataset.pane)
  }
  selectNext () {
    const selectedIndex = this.panes.indexOf(this.activePane)
    const nextIndex = Math.min(selectedIndex + 1, this.panes.length - 1)
    this.selectPane(this.panes[nextIndex].element.dataset.pane)
  }
}
module.exports = SidebarView
},{"./view":28}],28:[function(require,module,exports){
'use strict'
class View {
  static queryForEach (element, selector, callback) {
    const elements = element.querySelectorAll(selector)
    Array.prototype.forEach.call(elements, callback)
  }
  constructor (viewId) {
    this.id = viewId
    this.listeners = []
    this.element = this.createElement()
    this.element.v