diff --git a/src/Console/Console.scss b/src/Console/Console.scss
index 37499eb..9b8e90d 100644
--- a/src/Console/Console.scss
+++ b/src/Console/Console.scss
@@ -13,28 +13,8 @@
padding-bottom: 0;
}
.control {
- @include absolute(100%, 40px);
- cursor: default;
- font-size: 0;
+ @include control();
padding: 10px 10px 10px 35px;
- background: var(--darker-background);
- color: var(--primary);
- line-height: 20px;
- border-bottom: 1px solid var(--border);
- .icon-clear,
- .icon-filter,
- .icon-copy {
- display: inline-block;
- padding: 10px;
- font-size: $font-size-l;
- position: absolute;
- top: 0;
- cursor: pointer;
- transition: color $anim-duration;
- &:active {
- color: var(--accent);
- }
- }
.icon-clear {
padding-right: 0px;
left: 0;
diff --git a/src/Network/Detail.js b/src/Network/Detail.js
index fe2a348..164bf58 100644
--- a/src/Network/Detail.js
+++ b/src/Network/Detail.js
@@ -1,14 +1,16 @@
-import Emitter from 'licia/Emitter'
import trim from 'licia/trim'
import isEmpty from 'licia/isEmpty'
import map from 'licia/map'
import escape from 'licia/escape'
+import copy from 'licia/copy'
+import extend from 'licia/extend'
import { classPrefix as c } from '../lib/util'
+import { curlStr } from './util'
-export default class Detail extends Emitter {
- constructor($container) {
- super()
+export default class Detail {
+ constructor($container, devtools) {
this._$container = $container
+ this._devtools = devtools
this._detailData = {}
this._bindEvent()
@@ -54,9 +56,10 @@ export default class Detail extends Emitter {
resTxt = `
+ const html = `
- ${escape(data.url)}
+ ${escape(data.url)}
+
${postData}
@@ -85,9 +88,43 @@ export default class Detail extends Emitter {
hide() {
this._$container.hide()
}
+ _copyCurl = () => {
+ const detailData = this._detailData
+
+ copy(
+ curlStr({
+ requestMethod: detailData.method,
+ url() {
+ return detailData.url
+ },
+ requestFormData() {
+ return detailData.data
+ },
+ requestHeaders() {
+ const reqHeaders = detailData.reqHeaders || {}
+ extend(reqHeaders, {
+ 'User-Agent': navigator.userAgent,
+ Referer: location.href,
+ })
+
+ return map(reqHeaders, (value, name) => {
+ return {
+ name,
+ value,
+ }
+ })
+ },
+ })
+ )
+
+ this._devtools.notify('Copied')
+ }
_bindEvent() {
+ const devtools = this._devtools
+
this._$container
.on('click', c('.back'), () => this.hide())
+ .on('click', c('.copy-curl'), this._copyCurl)
.on('click', c('.http .response'), () => {
const data = this._detailData
const resTxt = data.resTxt
@@ -109,7 +146,14 @@ export default class Detail extends Emitter {
})
const showSources = (type, data) => {
- this.emit('showSources', type, data)
+ const sources = devtools.get('sources')
+ if (!sources) {
+ return
+ }
+
+ sources.set(type, data)
+
+ devtools.showTool('sources')
}
}
}
diff --git a/src/Network/Network.js b/src/Network/Network.js
index ff3cea1..6a81a6c 100644
--- a/src/Network/Network.js
+++ b/src/Network/Network.js
@@ -2,7 +2,6 @@ import Tool from '../DevTools/Tool'
import $ from 'licia/$'
import ms from 'licia/ms'
import each from 'licia/each'
-import last from 'licia/last'
import Detail from './Detail'
import throttle from 'licia/throttle'
import { getFileName, classPrefix as c } from '../lib/util'
@@ -10,6 +9,7 @@ import evalCss from '../lib/evalCss'
import chobitsu from '../lib/chobitsu'
import LunaDataGrid from 'luna-data-grid'
import ResizeSensor from 'licia/ResizeSensor'
+import { getType } from './util'
export default class Network extends Tool {
constructor() {
@@ -25,7 +25,7 @@ export default class Network extends Tool {
this._container = container
this._initTpl()
- this._detail = new Detail(this._$detail)
+ this._detail = new Detail(this._$detail, container)
this._requestDataGrid = new LunaDataGrid(this._$requests.get(0), {
columns: [
{
@@ -183,7 +183,6 @@ export default class Network extends Tool {
}
_bindEvent() {
const $el = this._$el
- const container = this._container
const self = this
@@ -198,15 +197,6 @@ export default class Network extends Tool {
self._detail.show(request)
})
- this._detail.on('showSources', function (type, data) {
- const sources = container.get('sources')
- if (!sources) return
-
- sources.set(type, data)
-
- container.showTool('sources')
- })
-
this._resizeSensor.addListener(
throttle(() => this._updateDataGridHeight(), 15)
)
@@ -247,14 +237,3 @@ export default class Network extends Tool {
this._$requests = $el.find(c('.requests'))
}
}
-
-function getType(contentType) {
- if (!contentType) return 'unknown'
-
- const type = contentType.split(';')[0].split('/')
-
- return {
- type: type[0],
- subType: last(type),
- }
-}
diff --git a/src/Network/Network.scss b/src/Network/Network.scss
index 19a2a76..5b260d4 100644
--- a/src/Network/Network.scss
+++ b/src/Network/Network.scss
@@ -29,29 +29,23 @@
display: none;
padding-top: 40px;
background: var(--background);
- .breadcrumb {
- @include breadcrumb();
- @include absolute(100%, 40px);
- font-size: $font-size-s;
+ .control {
+ @include control();
padding: 10px 35px;
- white-space: nowrap;
- overflow-x: hidden;
- text-overflow: ellipsis;
- .icon-arrow-left {
+ .url {
+ font-size: $font-size-s;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 100%;
display: inline-block;
- padding: 10px;
- font-size: $font-size-l;
- position: absolute;
- top: 0;
- cursor: pointer;
- transition: color $anim-duration;
- &:active {
- color: var(--accent);
- }
}
.icon-arrow-left {
left: 0;
}
+ .icon-copy {
+ right: 0;
+ }
}
.http {
@include overflow-auto(y);
@@ -90,7 +84,7 @@
@include overflow-auto(x);
padding: $padding;
font-size: $font-size-s;
- margin-bottom: 10px;
+ margin: 10px 0;
white-space: pre-wrap;
border-top: 1px solid var(--border);
color: var(--foreground);
diff --git a/src/Network/util.js b/src/Network/util.js
new file mode 100644
index 0000000..54e1266
--- /dev/null
+++ b/src/Network/util.js
@@ -0,0 +1,106 @@
+import last from 'licia/last'
+import detectOs from 'licia/detectOs'
+
+export function getType(contentType) {
+ if (!contentType) return 'unknown'
+
+ const type = contentType.split(';')[0].split('/')
+
+ return {
+ type: type[0],
+ subType: last(type),
+ }
+}
+
+export function curlStr(request) {
+ let platform = detectOs()
+ if (platform === 'windows') {
+ platform = 'win'
+ }
+ /* eslint-disable */
+ let command = []
+ const ignoredHeaders = new Set([
+ 'accept-encoding',
+ 'host',
+ 'method',
+ 'path',
+ 'scheme',
+ 'version',
+ ])
+
+ function escapeStringWin(str) {
+ const encapsChars = /[\r\n]/.test(str) ? '^"' : '"'
+ return (
+ encapsChars +
+ str
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`&]/g, '^$&')
+ .replace(/%(?=[a-zA-Z0-9_])/g, '%^')
+ .replace(/\r?\n/g, '^\n\n') +
+ encapsChars
+ )
+ }
+
+ function escapeStringPosix(str) {
+ function escapeCharacter(x) {
+ const code = x.charCodeAt(0)
+ let hexString = code.toString(16)
+ while (hexString.length < 4) {
+ hexString = '0' + hexString
+ }
+
+ return '\\u' + hexString
+ }
+
+ if (/[\0-\x1F\x7F-\x9F!]|\'/.test(str)) {
+ return (
+ "$'" +
+ str
+ .replace(/\\/g, '\\\\')
+ .replace(/\'/g, "\\'")
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r')
+ .replace(/[\0-\x1F\x7F-\x9F!]/g, escapeCharacter) +
+ "'"
+ )
+ }
+ return "'" + str + "'"
+ }
+
+ const escapeString = platform === 'win' ? escapeStringWin : escapeStringPosix
+
+ command.push(escapeString(request.url()).replace(/[[{}\]]/g, '\\$&'))
+
+ let inferredMethod = 'GET'
+ const data = []
+ const formData = request.requestFormData()
+ if (formData) {
+ data.push('--data-raw ' + escapeString(formData))
+ ignoredHeaders.add('content-length')
+ inferredMethod = 'POST'
+ }
+
+ if (request.requestMethod !== inferredMethod) {
+ command.push('-X ' + escapeString(request.requestMethod))
+ }
+
+ const requestHeaders = request.requestHeaders()
+ for (let i = 0; i < requestHeaders.length; i++) {
+ const header = requestHeaders[i]
+ const name = header.name.replace(/^:/, '')
+ if (ignoredHeaders.has(name.toLowerCase())) {
+ continue
+ }
+ command.push('-H ' + escapeString(name + ': ' + header.value))
+ }
+ command = command.concat(data)
+ command.push('--compressed')
+
+ return (
+ 'curl ' +
+ command.join(
+ command.length >= 3 ? (platform === 'win' ? ' ^\n ' : ' \\\n ') : ' '
+ )
+ )
+}
diff --git a/src/eruda.js b/src/eruda.js
index 108d648..5e6e588 100644
--- a/src/eruda.js
+++ b/src/eruda.js
@@ -148,22 +148,23 @@ export default {
if (!this._isInit) logger.error('Please call "eruda.init()" first')
return this._isInit
},
- _initContainer(el, useShadowDom) {
- if (!el) {
- el = document.createElement('div')
- document.documentElement.appendChild(el)
+ _initContainer(container, useShadowDom) {
+ if (!container) {
+ container = document.createElement('div')
+ document.documentElement.appendChild(container)
}
- el.id = 'eruda'
- el.style.all = 'initial'
- this._container = el
+ container.id = 'eruda'
+ container.style.all = 'initial'
+ this._container = container
let shadowRoot
+ let el
if (useShadowDom) {
- if (el.attachShadow) {
- shadowRoot = el.attachShadow({ mode: 'open' })
- } else if (el.createShadowRoot) {
- shadowRoot = el.createShadowRoot()
+ if (container.attachShadow) {
+ shadowRoot = container.attachShadow({ mode: 'open' })
+ } else if (container.createShadowRoot) {
+ shadowRoot = container.createShadowRoot()
}
if (shadowRoot) {
// font-face doesn't work inside shadow dom.
@@ -178,9 +179,8 @@ export default {
}
if (!this._shadowRoot) {
- const oldEl = el
el = document.createElement('div')
- oldEl.appendChild(el)
+ container.appendChild(el)
}
extend(el, {
diff --git a/src/style/mixin.scss b/src/style/mixin.scss
index c508e8d..0994f13 100644
--- a/src/style/mixin.scss
+++ b/src/style/mixin.scss
@@ -29,6 +29,29 @@
border-bottom: 1px solid var(--border);
}
+@mixin control {
+ @include absolute(100%, 40px);
+ cursor: default;
+ font-size: 0;
+ background: var(--darker-background);
+ color: var(--primary);
+ line-height: 20px;
+ border-bottom: 1px solid var(--border);
+ [class^='icon-'],
+ [class*=' icon-'] {
+ display: inline-block;
+ padding: 10px;
+ font-size: $font-size-l;
+ position: absolute;
+ top: 0;
+ cursor: pointer;
+ transition: color $anim-duration;
+ &:active {
+ color: var(--accent);
+ }
+ }
+}
+
@mixin clear-float {
&:after {
content: '';