1
0
mirror of synced 2025-12-12 09:41:28 +08:00

Compare commits

..

20 Commits

Author SHA1 Message Date
surunzi
e63387bdb8 release: v1.9.0 2019-10-20 16:20:29 +08:00
surunzi
61f5fd9b64 feat: add fit screen snippet 2019-10-16 21:28:22 +08:00
redhoodsu
b2266cdd98 fix(console): filter shouldn't affect group 2019-10-16 20:34:40 +08:00
redhoodsu
8f7ede10e9 chore: small changes 2019-10-15 15:13:31 +08:00
redhoodsu
dbe5ae164f feat: add snippet for loading touches plugin 2019-10-15 14:48:34 +08:00
redhoodsu
289d9942b2 release: v1.8.1 2019-10-14 19:15:25 +08:00
surunzi
b5d2ed64d8 fix(network): style #121 2019-10-14 18:22:23 +08:00
surunzi
2ba6216df4 chore: small changes 2019-10-14 07:32:44 +08:00
surunzi
8a70008710 release: v1.8.0 2019-10-13 20:42:57 +08:00
surunzi
376404e235 chore: small changes 2019-10-13 20:35:14 +08:00
surunzi
8babafb6d1 feat(network): display optimization 2019-10-13 20:26:24 +08:00
surunzi
e5b8c55df7 chore: small changes 2019-10-13 16:32:01 +08:00
surunzi
0ae4b9fa66 feat: move http view from sources to network 2019-10-13 16:10:31 +08:00
surunzi
7d9445a59f fix(console): group object expansion 2019-10-13 13:33:26 +08:00
surunzi
d3476779e6 docs: update screenshot 2019-10-12 12:01:13 +08:00
surunzi
bf08ec381a docs: plugin 2019-10-12 11:37:17 +08:00
redhoodsu
3784fe06ec release: v1.7.2 2019-10-11 18:20:51 +08:00
redhoodsu
eaad70a7e4 fix(console): blank bottom if js input is disabled 2019-10-11 16:31:56 +08:00
redhoodsu
6576fa87bc chore: typo 2019-10-11 14:24:52 +08:00
redhoodsu
bd520e8195 chore: update eruda-dom version 2019-10-11 14:20:32 +08:00
23 changed files with 1605 additions and 1346 deletions

View File

@@ -1,6 +1,9 @@
{
"cSpell.words": [
"Eruda",
"callout",
"eval",
"licia",
"unenumerable"
]
}

View File

@@ -1,3 +1,24 @@
## v1.9.0 (20 Oct 2019)
* feat: add snippet for loading touches plugin
* feat: add fit screen snippet
* fix(console): filter shouldn't affect group
## v1.8.1 (14 Oct 2019)
* fix(network): style [#121](https://github.com/liriliri/eruda/issues/121)
## v1.8.0 (13 Oct 2019)
* feat(network): display optimization
* feat: move http view from sources to network
* fix(console): group object expansion
## v1.7.2 (11 Oct 2019)
* fix(console): blank bottom if js input is disabled
* chore: update eruda-dom version
## v1.7.1 (10 Oct 2019)
* fix: resize

View File

@@ -26,10 +26,6 @@ Console for Mobile Browsers.
![Eruda](./doc/screenshot.jpg)
## Why
Logging things out on mobile browser is never an easy stuff. I used to include `window onerror alert` script inside pages to find out JavaScript errors, kind of stupid and inefficient. Desktop browser DevTools is great, and I wish there is a similar one on mobile side, which leads to the creation of Eruda.
## Demo
![Demo](./doc/qrcode.png)
@@ -107,8 +103,6 @@ eruda.init({
## Plugins
It is possible to enhance Eruda with more features by writing plugins. Check source code of plugins below to learn how to write your own custom tool panels. Besides, [eruda-plugin](https://github.com/liriliri/eruda-plugin) is available for plugin initialization.
* [eruda-fps](https://github.com/liriliri/eruda-fps): Display page fps info.
* [eruda-features](https://github.com/liriliri/eruda-features): Browser feature detections.
* [eruda-timing](https://github.com/liriliri/eruda-timing): Show performance and resource timing.
@@ -118,8 +112,9 @@ It is possible to enhance Eruda with more features by writing plugins. Check sou
* [eruda-geolocation](https://github.com/liriliri/eruda-geolocation): Test geolocation.
* [eruda-dom](https://github.com/liriliri/eruda-dom): Navigate dom tree.
* [eruda-orientation](https://github.com/liriliri/eruda-orientation): Test orientation api.
* [eruda-touches](https://github.com/liriliri/eruda-touches): Visualize screen touches.
When writing plugins, you can use utilities exposed by Eruda, see [docs](doc/UTIL_API.md) here.
If you want to create a plugin yourself, follow the guides [here](./doc/PLUGIN.md).
## Related Projects

79
doc/PLUGIN.md Normal file
View File

@@ -0,0 +1,79 @@
# Writing a Plugin
It is possible to enhance Eruda with more features by writing plugins, which means, creating your own custom panels.
## Creating a Plugin
Adding plugins is super easy for eruda. All you have to do is passing an object with a few methods implemented.
```javascript
eruda.add({
name: 'test',
init($el) {
$el.html('Hello, this is my first eruda plugin!');
this._$el = $el;
},
show() {
this._$el.show();
},
hide() {
this._$el.hide();
},
destroy() {}
});
```
## Basic Structure
### name
Every plugin must have a unique name, which will be shown as the tab name on the top.
### init
Called when plugin is added, and a document element used to display content is passed in.
The element is wrapped as a jQuery like object, provided by the [licia](https://licia.liriliri.io/docs.html) utility library.
### show
Called when switch to the panel. Usually all you need to do is to show the container element.
### hide
Called when switch to other panel. You should at least hide the container element here.
### destroy
Called when plugin is removed using `eruda.remove('plugin name')`.
## Worth Mentioning
Apart from passing an object, you can also pass in a function that returns an object. Eruda will automatically invoke the function and use the object it returns.
When writing plugins, you can use utilities exposed by Eruda, see [docs](./UTIL_API.md) here.
```javascript
eruda.add(function (eruda) {
// eruda.Tool implements those four methods.
class Test extends eruda.Tool {
constructor() {
super()
this.name = 'test';
this.style = eruda.util.evalCss('.eruda-test { background: #000; }');
}
init($el) {
super.init($el);
}
destroy() {
super.destroy();
eruda.util.evalCss.remove(this.style);
}
}
return new Test();
});
```
There is also a tool for plugin initialization, check it out [here](https://github.com/liriliri/eruda-plugin).

View File

@@ -104,8 +104,6 @@ eruda.init({
## 插件
你可以为 Eruda 编写插件来增强功能。关于怎么编写插件,请查看下边官方插件的源码。另外 [eruda-plugin](https://github.com/liriliri/eruda-plugin) 可以用来初始化一个新插件。
* [eruda-fps](https://github.com/liriliri/eruda-fps):展示页面的 fps 信息。
* [eruda-features](https://github.com/liriliri/eruda-features):浏览器特性检测。
* [eruda-timing](https://github.com/liriliri/eruda-timing):展示性能资源数据。
@@ -115,8 +113,9 @@ eruda.init({
* [eruda-geolocation](https://github.com/liriliri/eruda-geolocation):测试地理位置接口。
* [eruda-dom](https://github.com/liriliri/eruda-dom):浏览 dom 树。
* [eruda-orientation](https://github.com/liriliri/eruda-orientation):测试重力感应接口。
* [eruda-touches](https://github.com/liriliri/eruda-orientation):可视化屏幕 Touch 事件触发。
编写插件的时候,你可以使用 Eruda 提供的工具函数,相关文档请点击[这里](doc/UTIL_API.md)查看
如果你想要自己编写插件,可以查看这里的[教程](./PLUGIN.md)。
## 相关项目

View File

@@ -127,7 +127,7 @@ LocalStorage, sessionStorage, cookies, scripts, styleSheets and images.
## Sources
View json, html, js, css and http request detail.
View json, html, js, and css.
### Config

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "eruda",
"version": "1.7.1",
"version": "1.9.0",
"description": "Console for Mobile Browsers",
"main": "eruda.js",
"scripts": {

View File

@@ -91,9 +91,16 @@ export default class Console extends Tool {
this._rmCfg()
}
_enableJsExecution(enabled) {
const $el = this._$el.find('.eruda-js-input')
const $el = this._$el
const $jsInput = $el.find('.eruda-js-input')
enabled ? $el.show() : $el.hide()
if (enabled) {
$jsInput.show()
$el.rmClass('eruda-js-input-hidden')
} else {
$jsInput.hide()
$el.addClass('eruda-js-input-hidden')
}
}
_registerListener() {
this._scaleListener = scale => (this._scale = scale)
@@ -299,7 +306,7 @@ export default class Console extends Tool {
settings
.text('Console')
.switch(cfg, 'jsExecution', 'Enable JavaScript execution')
.switch(cfg, 'jsExecution', 'Enable JavaScript Execution')
.switch(cfg, 'catchGlobalErr', 'Catch Global Errors')
.switch(cfg, 'overrideConsole', 'Override Console')
.switch(cfg, 'displayIfErr', 'Auto Display If Error Occurs')

View File

@@ -4,6 +4,9 @@
.dev-tools {
.tools {
.console {
&.js-input-hidden {
padding-bottom: 0;
}
.control {
@include absolute(100%, 40px);
cursor: default;

View File

@@ -281,6 +281,8 @@ export default class Log {
case 'output':
case 'table':
case 'dir':
case 'group':
case 'groupCollapsed':
if (log.src) {
if (Log.showSrcInSources) {
return logger.emit('viewJson', log.src)
@@ -300,6 +302,8 @@ export default class Log {
Log.click(type, log, $el, logger)
delete log.args
})
} else if (log.type === 'group' || log.type === 'groupCollapsed') {
logger.toggleGroup(log)
}
break
case 'error':

View File

@@ -198,10 +198,18 @@ export default class Logger extends Emitter {
return this.insert('warn', args)
}
group(...args) {
return this.insert('group', args)
return this.insert({
type: 'group',
args,
ignoreFilter: true
})
}
groupCollapsed(...args) {
return this.insert('groupCollapsed', args)
return this.insert({
type: 'groupCollapsed',
args,
ignoreFilter: true
})
}
groupEnd() {
const lastLog = this._lastLog
@@ -270,14 +278,14 @@ export default class Logger extends Emitter {
displayHeader: this._displayHeader
})
if (type === 'group' || type === 'groupCollapsed') {
if (options.type === 'group' || options.type === 'groupCollapsed') {
const group = {
id: uniqId('group'),
collapsed: false,
parent: groupStack.peek(),
indentLevel: groupStack.size + 1
}
if (type === 'groupCollapsed') group.collapsed = true
if (options.type === 'groupCollapsed') group.collapsed = true
options.targetGroup = group
groupStack.push(group)
}
@@ -322,6 +330,10 @@ export default class Logger extends Emitter {
el.scrollTop = el.scrollHeight - el.offsetHeight
}
toggleGroup(log) {
const { targetGroup } = log
targetGroup.collapsed ? this._openGroup(log) : this._collapseGroup(log)
}
_injectGlobal() {
each(this._global, (val, name) => {
if (window[name]) return

View File

@@ -4,20 +4,5 @@
<span {{{class 'icon-clear'}}}></span>
</div>
</div>
<ul {{{class 'requests'}}}>
{{#if requests}}
{{#each requests}}
<li class="eruda-request {{#if hasErr}}eruda-error{{/if}}" data-id="{{@key}}">
<span>{{name}}</span>
<span>{{status}}</span>
<span>{{method}}</span>
<span>{{subType}}</span>
<span>{{size}}</span>
<span>{{displayTime}}</span>
<span {{{class 'blue'}}}>{{url}}</span>
</li>
{{/each}}
{{else}}
<li><span>Empty</span></li>
{{/if}}
</ul>
<ul {{{class 'requests'}}}></ul>
<div {{{class 'detail'}}}></div>

View File

@@ -10,7 +10,8 @@ import {
extend,
isEmpty,
$,
ms
ms,
trim
} from '../lib/util'
export default class Network extends Tool {
@@ -22,6 +23,9 @@ export default class Network extends Tool {
this.name = 'network'
this._requests = {}
this._tpl = require('./Network.hbs')
this._detailTpl = require('./detail.hbs')
this._requestsTpl = require('./requests.hbs')
this._datailData = {}
this._isFetchSupported = false
if (window.fetch) this._isFetchSupported = isNative(window.fetch)
}
@@ -32,6 +36,7 @@ export default class Network extends Tool {
this._bindEvent()
this._initCfg()
this.overrideXhr()
this._appendTpl()
}
show() {
super.show()
@@ -166,9 +171,29 @@ export default class Network extends Tool {
if (!data.done) return
showSources('http', data)
self._showDetail(data)
})
.on('click', '.eruda-clear-request', () => this.clear())
.on('click', '.eruda-back', () => this._hideDetail())
.on('click', '.eruda-http .eruda-response', () => {
const data = this._detailData
const resTxt = data.resTxt
switch (data.subType) {
case 'css':
return showSources('css', resTxt)
case 'html':
return showSources('html', resTxt)
case 'javascript':
return showSources('js', resTxt)
case 'json':
return showSources('json', resTxt)
}
switch (data.type) {
case 'image':
return showSources('img', data.url)
}
})
function showSources(type, data) {
const sources = container.get('sources')
@@ -187,6 +212,15 @@ export default class Network extends Tool {
this.restoreFetch()
this._rmCfg()
}
_showDetail(data) {
if (data.resTxt && trim(data.resTxt) === '') delete data.resTxt
if (isEmpty(data.resHeaders)) delete data.resHeaders
this._$detail.html(this._detailTpl(data)).show()
this._detailData = data
}
_hideDetail() {
this._$detail.hide()
}
_rmCfg() {
const cfg = this.config
@@ -196,6 +230,12 @@ export default class Network extends Tool {
settings.remove(cfg, 'overrideFetch').remove('Network')
}
_appendTpl() {
const $el = this._$el
$el.html(this._tpl())
this._$detail = $el.find('.eruda-detail')
this._$requests = $el.find('.eruda-requests')
}
_initCfg() {
const cfg = (this.config = Settings.createCfg('network', {
overrideFetch: true
@@ -223,11 +263,11 @@ export default class Network extends Tool {
if (!isEmpty(this._requests)) renderData.requests = this._requests
this._renderHtml(this._tpl(renderData))
this._renderHtml(this._requestsTpl(renderData))
}
_renderHtml(html) {
if (html === this._lastHtml) return
this._lastHtml = html
this._$el.html(html)
this._$requests.html(html)
}
}

View File

@@ -4,19 +4,24 @@
.dev-tools {
.tools {
.network {
@include overflow-auto(y);
padding-top: 36px;
.title {
@include absolute(100%, 36px);
@include right-circle-btn();
background: $gray;
padding: $padding;
color: #fff;
height: 36px;
}
.requests {
@include overflow-auto(y);
height: 100%;
background: #fff;
border-bottom: 1px solid $gray-light;
margin-bottom: 10px;
li {
@include overflow-auto(x);
display: flex;
width: 100%;
cursor: pointer;
border-top: 1px solid $gray-light;
height: 41px;
@@ -27,18 +32,104 @@
}
}
span {
display: inline-block;
display: block;
line-height: 40px;
height: 40px;
padding: 0 10px;
padding: 0 5px;
font-size: $font-size-s;
vertical-align: top;
text-overflow: ellipsis;
overflow: hidden;
}
.name {
flex: 1;
padding-left: $padding;
}
.status {
width: 40px;
}
.method,
.type {
width: 50px;
}
.size {
width: 70px;
}
.time {
width: 60px;
padding-right: $padding;
}
&:nth-child(even) {
background: $gray-light;
}
}
}
.detail {
@include absolute();
z-index: 10;
display: none;
padding-bottom: 40px;
background: #f8f9fa;
.http {
@include overflow-auto(y);
height: 100%;
.breadcrumb {
@include breadcrumb();
}
.section {
background: #fff;
h2 {
background: $blue;
padding: $padding;
color: #fff;
font-size: $font-size;
}
margin-bottom: 10px;
table {
* {
user-select: text;
}
td {
font-size: $font-size-s;
padding: 5px 10px;
word-break: break-all;
}
.key {
white-space: nowrap;
}
}
}
.response,
.data {
user-select: text;
@include overflow-auto(x);
background: #fff;
padding: $padding;
font-size: $font-size-s;
margin-bottom: 10px;
white-space: pre-wrap;
}
}
.back {
position: absolute;
left: 0;
bottom: 0;
color: #fff;
width: 100%;
background: $blue;
display: block;
height: 40px;
line-height: 40px;
text-decoration: none;
text-align: center;
margin-top: 10px;
transition: background 0.3s;
cursor: pointer;
&:active {
background: $blue-dark;
}
}
}
}
}
}

View File

@@ -42,4 +42,5 @@
{{#if resTxt}}
<pre {{{class 'response'}}}>{{resTxt}}</pre>
{{/if}}
</div>
</div>
<div {{{class 'back'}}}>Back to the List</div>

14
src/Network/requests.hbs Normal file
View File

@@ -0,0 +1,14 @@
{{#if requests}}
{{#each requests}}
<li class="eruda-request {{#if hasErr}}eruda-error{{/if}}" data-id="{{@key}}">
<span {{{class 'name'}}}>{{name}}</span>
<span {{{class 'status'}}}>{{status}}</span>
<span {{{class 'method'}}}>{{method}}</span>
<span {{{class 'type'}}}>{{subType}}</span>
<span {{{class 'size'}}}>{{size}}</span>
<span {{{class 'time'}}}>{{displayTime}}</span>
</li>
{{/each}}
{{else}}
<li><span>Empty</span></li>
{{/if}}

View File

@@ -64,6 +64,37 @@ export default [
},
desc: 'Toggle body contentEditable'
},
{
name: 'Fit Screen',
// https://achrafkassioui.com/birdview/
fn() {
const body = document.body
const html = document.documentElement
const $body = $(body)
if ($body.data('scaled')) {
window.scrollTo(0, +$body.data('scaled'))
$body.rmAttr('data-scaled')
$body.css('transform', 'none')
} else {
const documentHeight = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight
)
const viewportHeight = Math.max(
document.documentElement.clientHeight,
window.innerHeight || 0
)
const scaleVal = viewportHeight / documentHeight
$body.css('transform', `scale(${scaleVal})`)
$body.data('scaled', window.scrollY)
window.scrollTo(0, documentHeight / 2 - viewportHeight / 2)
}
},
desc: 'Scale down the whole page to fit screen'
},
{
name: 'Load Fps Plugin',
fn() {
@@ -127,6 +158,13 @@ export default [
},
desc: 'Test orientation api'
},
{
name: 'Load Touches Plugin',
fn() {
loadPlugin('touches')
},
desc: 'Visualize screen touches'
},
{
name: 'Restore Settings',
fn() {
@@ -216,6 +254,7 @@ const pluginVersion = {
code: '1.0.0',
benchmark: '1.0.0',
geolocation: '1.1.0',
dom: '1.0.2',
orientation: '1.0.0'
dom: '1.1.0',
orientation: '1.0.0',
touches: '1.0.1'
}

View File

@@ -2,15 +2,7 @@ import Tool from '../DevTools/Tool'
import beautify from 'js-beautify'
import JsonViewer from '../lib/JsonViewer'
import Settings from '../Settings/Settings'
import {
evalCss,
ajax,
isEmpty,
escape,
trim,
isStr,
highlight
} from '../lib/util'
import { evalCss, ajax, escape, trim, isStr, highlight } from '../lib/util'
export default class Sources extends Tool {
constructor() {
@@ -112,31 +104,10 @@ export default class Sources extends Tool {
delete this._data
}
})
this._$el.on('click', '.eruda-http .eruda-response', () => {
const data = this._data.val
const resTxt = data.resTxt
switch (data.subType) {
case 'css':
return this.set('css', resTxt)
case 'html':
return this.set('html', resTxt)
case 'javascript':
return this.set('js', resTxt)
case 'json':
return this.set('json', resTxt)
}
switch (data.type) {
case 'image':
return this.set('img', data.url)
}
})
}
_loadTpl() {
this._codeTpl = require('./code.hbs')
this._imgTpl = require('./image.hbs')
this._httpTpl = require('./http.hbs')
this._jsonTpl = require('./json.hbs')
this._rawTpl = require('./raw.hbs')
this._iframeTpl = require('./iframe.hbs')
@@ -192,8 +163,6 @@ export default class Sources extends Tool {
return this._renderCode()
case 'img':
return this._renderImg()
case 'http':
return this._renderHttp()
case 'json':
return this._renderJson()
case 'raw':
@@ -205,14 +174,6 @@ export default class Sources extends Tool {
_renderImg() {
this._renderHtml(this._imgTpl(this._data.val))
}
_renderHttp() {
const val = this._data.val
if (val.resTxt.trim() === '') delete val.resTxt
if (isEmpty(val.resHeaders)) delete val.resHeaders
this._renderHtml(this._httpTpl(this._data.val))
}
_renderCode() {
const data = this._data

View File

@@ -66,44 +66,6 @@
user-select: text;
}
}
.http {
.breadcrumb {
@include breadcrumb();
}
.section {
background: #fff;
h2 {
background: $blue;
padding: $padding;
color: #fff;
font-size: $font-size;
}
margin-bottom: 10px;
table {
* {
user-select: text;
}
td {
font-size: $font-size-s;
padding: 5px 10px;
word-break: break-all;
}
.key {
white-space: nowrap;
}
}
}
.response,
.data {
user-select: text;
@include overflow-auto(x);
background: #fff;
padding: $padding;
font-size: $font-size-s;
margin-bottom: 10px;
white-space: pre-wrap;
}
}
iframe {
width: 100%;
height: 100%;

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,9 @@
<li>
<a href="#" id="stringify-timing">Stringify Timing</a>
</li>
<li>
<a href="#" id="log">Log</a>
</li>
</ul>
</nav>
<script>
@@ -123,7 +126,42 @@
console.time('stringify window')
eruda.util.stringifyAll(window)
console.timeEnd('stringify window')
})
});
addClickEvent('log', () => {
console.clear();
console.log('log');
console.warn('warn');
console.error(Error('test'));
console.info('info');
console.debug('debug');
console.dir(document.createElement('div'));
console.time('test');
console.timeEnd('test');
console.count('eruda');
console.count('eruda');
console.assert(true, 'assert msg');
var site1 = { name: 'Runoob', site: 'www.runoob.com' };
var site2 = { name: 'Google', site: 'www.google.com' };
var site3 = { name: 'Taobao', site: 'www.taobao.com' };
console.table([site1, site2, site3], ['site']);
console.log('%c Oh my heavens!', 'background: #222; color: #bada55');
console.log('This is the outer level');
console.group();
console.log('Level 2');
console.group();
console.log('Level 3');
console.warn('More of level 3');
console.groupEnd();
console.log('Back to level 2');
console.groupEnd();
console.log('Back to the outer level');
console.log(navigator);
console.log(location);
console.log(performance);
var arr = [];
for (var i = 0; i < 10000; i++) arr.push(i);
console.log(arr);
});
</script>
<script>
eruda.init();

File diff suppressed because it is too large Load Diff