mirror of
https://github.com/liriliri/eruda.git
synced 2026-03-20 09:38:37 +08:00
Add: Resources observer
This commit is contained in:
@@ -106,8 +106,9 @@ LocalStorage, sessionStorage, cookies, scripts, styleSheets and images.
|
||||
### Config
|
||||
|
||||
|Name |Type |Desc |
|
||||
|----------------|-------|------------------|
|
||||
|hideErudaSetting|boolean|Hide Eruda Setting|
|
||||
|----------------|-------|---------------------|
|
||||
|hideErudaSetting|boolean|Hide Eruda Setting |
|
||||
|observeElement |boolean|Auto Refresh Elements|
|
||||
|
||||
## Sources
|
||||
|
||||
|
||||
132
doc/UTIL_API.md
132
doc/UTIL_API.md
@@ -450,6 +450,19 @@ logger.on('debug', function (argList)
|
||||
});
|
||||
```
|
||||
|
||||
## SafeMutationObserver
|
||||
|
||||
Safe MutationObserver, does nothing if MutationObserver is not supported.
|
||||
|
||||
```javascript
|
||||
var observer = new DomObserver(function (mutations)
|
||||
{
|
||||
// Do something.
|
||||
});
|
||||
observer.observe(document.htmlElement);
|
||||
observer.disconnect();
|
||||
```
|
||||
|
||||
## Select
|
||||
|
||||
Simple wrapper of querySelectorAll to make dom selection easier.
|
||||
@@ -711,6 +724,19 @@ Any nested objects or arrays will be copied by reference, not duplicated.
|
||||
clone({name: 'eustia'}); // -> {name: 'eustia'}
|
||||
```
|
||||
|
||||
## concat
|
||||
|
||||
Concat multiple arrays into a single array.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------|-----|------------------|
|
||||
|...arr|array|Arrays to concat |
|
||||
|return|array|Concatenated array|
|
||||
|
||||
```javascript
|
||||
concat([1, 2], [3], [4, 5]); // -> [1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
## contain
|
||||
|
||||
Check if the value is present in the list.
|
||||
@@ -887,7 +913,7 @@ if (browser.name === 'ie' && browser.version < 9)
|
||||
|
||||
## each
|
||||
|
||||
Iterates over elements of collection and invokes iteratee for each element.
|
||||
Iterate over elements of collection and invokes iteratee for each element.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|--------|------------|------------------------------|
|
||||
@@ -993,6 +1019,24 @@ fileSize(1500000000); // -> '1.4G'
|
||||
fileSize(1500000000000); // -> '1.36T'
|
||||
```
|
||||
|
||||
## filter
|
||||
|
||||
Iterates over elements of collection, returning an array of all the values that pass a truth test.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|---------|--------|---------------------------------------|
|
||||
|obj |array |Collection to iterate over |
|
||||
|predicate|function|Function invoked per iteration |
|
||||
|[ctx] |* |Predicate context |
|
||||
|return |array |Array of all values that pass predicate|
|
||||
|
||||
```javascript
|
||||
filter([1, 2, 3, 4, 5], function (val)
|
||||
{
|
||||
return val % 2 === 0;
|
||||
}); // -> [2, 4]
|
||||
```
|
||||
|
||||
## freeze
|
||||
|
||||
Shortcut for Object.freeze.
|
||||
@@ -1106,6 +1150,20 @@ Check if value is classified as an arguments object.
|
||||
})();
|
||||
```
|
||||
|
||||
## isArr
|
||||
|
||||
Check if value is an `Array` object.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------|-------|----------------------------------|
|
||||
|val |* |The value to check |
|
||||
|return|boolean|True if value is an `Array` object|
|
||||
|
||||
```javascript
|
||||
isArr([]); // -> true
|
||||
isArr({}); // -> false
|
||||
```
|
||||
|
||||
## isArrLike
|
||||
|
||||
Check if value is array-like.
|
||||
@@ -1239,6 +1297,19 @@ Check if keys and values in src are contained in obj.
|
||||
isMatch({a: 1, b: 2}, {a: 1}); // -> true
|
||||
```
|
||||
|
||||
## isMobile
|
||||
|
||||
Check whether client is using a mobile browser using ua.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------------------------|-------|-------------------------------------|
|
||||
|[ua=navigator.userAgent]|string |User agent |
|
||||
|return |boolean|True if ua belongs to mobile browsers|
|
||||
|
||||
```javascript
|
||||
isMobile(navigator.userAgent);
|
||||
```
|
||||
|
||||
## isNull
|
||||
|
||||
Check if value is an Null.
|
||||
@@ -1252,6 +1323,21 @@ Check if value is an Null.
|
||||
isNull(null); // -> true
|
||||
```
|
||||
|
||||
## isNum
|
||||
|
||||
Checks if value is classified as a Number primitive or object.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------|-------|-------------------------------------|
|
||||
|value |* |Value to check |
|
||||
|return|boolean|True if value is correctly classified|
|
||||
|
||||
```javascript
|
||||
isNum(5); // -> true
|
||||
isNum(5.1); // -> true
|
||||
isNum({}); // -> false
|
||||
```
|
||||
|
||||
## isObj
|
||||
|
||||
Check if value is the language type of Object.
|
||||
@@ -1268,6 +1354,19 @@ isObj({}); // -> true
|
||||
isObj([]); // -> true
|
||||
```
|
||||
|
||||
## isRegExp
|
||||
|
||||
Check if value is a regular expression.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------|-------|-------------------------------------|
|
||||
|val |* |Value to check |
|
||||
|return|boolean|True if value is a regular expression|
|
||||
|
||||
```javascript
|
||||
isRegExp(/a/); // -> true
|
||||
```
|
||||
|
||||
## isStr
|
||||
|
||||
Check if value is a string primitive.
|
||||
@@ -1281,6 +1380,20 @@ Check if value is a string primitive.
|
||||
isStr('eris'); // -> true
|
||||
```
|
||||
|
||||
## isUndef
|
||||
|
||||
Check if value is undefined.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|------|-------|--------------------------|
|
||||
|val |* |Value to check |
|
||||
|return|boolean|True if value is undefined|
|
||||
|
||||
```javascript
|
||||
isUndef(void 0); // -> true
|
||||
isUndef(null); // -> false
|
||||
```
|
||||
|
||||
## kebabCase
|
||||
|
||||
Convert string to "kebabCase".
|
||||
@@ -1416,6 +1529,23 @@ var localStorage = window.localStorage || memStorage;
|
||||
localStorage.setItem('test', 'eris');
|
||||
```
|
||||
|
||||
## memoize
|
||||
|
||||
Memoize a given function by caching the computed result.
|
||||
|
||||
|Name |Type |Desc |
|
||||
|--------|--------|------------------------------------|
|
||||
|fn |function|Function to have its output memoized|
|
||||
|[hashFn]|function|Function to create cache key |
|
||||
|return |function|New memoized function |
|
||||
|
||||
```javascript
|
||||
var fibonacci = memoize(function(n)
|
||||
{
|
||||
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
|
||||
});
|
||||
```
|
||||
|
||||
## meta
|
||||
|
||||
Document meta manipulation, turn name and content into key value pairs.
|
||||
|
||||
23
eustia/SafeMutationObserver.js
Normal file
23
eustia/SafeMutationObserver.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Safe MutationObserver, does nothing if MutationObserver is not supported.
|
||||
*
|
||||
* ```javascript
|
||||
* var observer = new DomObserver(function (mutations)
|
||||
* {
|
||||
* // Do something.
|
||||
* });
|
||||
* observer.observe(document.htmlElement);
|
||||
* observer.disconnect();
|
||||
* ```
|
||||
*/
|
||||
|
||||
exports = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||
|
||||
if (!exports)
|
||||
{
|
||||
exports = class MutationObserver {
|
||||
constructor() {}
|
||||
observe() {}
|
||||
disconnect() {}
|
||||
takeRecords() {}
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export default class Elements extends Tool
|
||||
this._rmDefComputedStyle = true;
|
||||
this._highlightElement = false;
|
||||
this._selectElement = false;
|
||||
this._observeElement = true;
|
||||
}
|
||||
init($el, parent)
|
||||
{
|
||||
@@ -40,9 +41,16 @@ export default class Elements extends Tool
|
||||
{
|
||||
super.show();
|
||||
|
||||
if (this._observeElement) this._enableObserver();
|
||||
if (!this._curEl) this._setEl(this._htmlEl);
|
||||
this._render();
|
||||
}
|
||||
hide()
|
||||
{
|
||||
this._disableObserver();
|
||||
|
||||
return super.hide();
|
||||
}
|
||||
set(e)
|
||||
{
|
||||
this._setEl(e);
|
||||
@@ -90,6 +98,7 @@ export default class Elements extends Tool
|
||||
util.evalCss.remove(this._style);
|
||||
this._select.disable();
|
||||
this._highlight.destroy();
|
||||
this._disableObserver();
|
||||
this.restoreEventTarget();
|
||||
}
|
||||
_back()
|
||||
@@ -187,18 +196,17 @@ export default class Elements extends Tool
|
||||
|
||||
this._render();
|
||||
}
|
||||
_toggleObserver(flag)
|
||||
_enableObserver()
|
||||
{
|
||||
let observer = this._observer;
|
||||
|
||||
if (!observer) return;
|
||||
|
||||
flag ? observer.observe(this._htmlEl, {
|
||||
this._observer.observe(this._htmlEl, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
}) : observer.disconnect();
|
||||
});
|
||||
}
|
||||
_disableObserver()
|
||||
{
|
||||
this._observer.disconnect();
|
||||
}
|
||||
_toggleHighlight()
|
||||
{
|
||||
@@ -291,11 +299,7 @@ export default class Elements extends Tool
|
||||
}
|
||||
_initObserver()
|
||||
{
|
||||
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
||||
|
||||
if (!MutationObserver) return;
|
||||
|
||||
this._observer = new MutationObserver(mutations =>
|
||||
this._observer = new util.SafeMutationObserver(mutations =>
|
||||
{
|
||||
util.each(mutations, mutation => this._handleMutation(mutation));
|
||||
});
|
||||
@@ -341,14 +345,16 @@ export default class Elements extends Tool
|
||||
}));
|
||||
|
||||
if (cfg.get('overrideEventTarget')) this.overrideEventTarget();
|
||||
if (cfg.get('observeElement')) this._toggleObserver(true);
|
||||
if (cfg.get('observeElement')) this._observeElement = false;
|
||||
|
||||
cfg.on('change', (key, val) =>
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'overrideEventTarget': return val ? this.overrideEventTarget(): this.restoreEventTarget();
|
||||
case 'observeElement': return this._toggleObserver(val);
|
||||
case 'observeElement':
|
||||
this._observeElement = val;
|
||||
return val ? this._enableObserver() :this._disableObserver();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export default class Resources extends Tool
|
||||
this._scriptData = [];
|
||||
this._stylesheetData = [];
|
||||
this._imageData = [];
|
||||
this._observeElement = true;
|
||||
this._tpl = require('./Resources.hbs');
|
||||
}
|
||||
init($el, parent)
|
||||
@@ -27,9 +28,8 @@ export default class Resources extends Tool
|
||||
|
||||
this.refresh();
|
||||
this._bindEvent();
|
||||
this._initObserver();
|
||||
this._initCfg();
|
||||
|
||||
util.ready(() => this._observeScript());
|
||||
}
|
||||
refresh()
|
||||
{
|
||||
@@ -44,8 +44,8 @@ export default class Resources extends Tool
|
||||
{
|
||||
super.destroy();
|
||||
|
||||
this._disableObserver();
|
||||
util.evalCss.remove(this._style);
|
||||
this._unobserveScript();
|
||||
}
|
||||
refreshScript()
|
||||
{
|
||||
@@ -173,9 +173,16 @@ export default class Resources extends Tool
|
||||
show()
|
||||
{
|
||||
super.show();
|
||||
if (this._observeElement) this._enableObserver();
|
||||
|
||||
return this.refresh();
|
||||
}
|
||||
hide()
|
||||
{
|
||||
this._disableObserver();
|
||||
|
||||
return super.hide();
|
||||
}
|
||||
_bindEvent()
|
||||
{
|
||||
let self = this,
|
||||
@@ -299,22 +306,28 @@ export default class Resources extends Tool
|
||||
let cfg = this.config = util.createCfg('resources');
|
||||
|
||||
cfg.set(util.defaults(cfg.get(), {
|
||||
hideErudaSetting: true
|
||||
hideErudaSetting: true,
|
||||
observeElement: true
|
||||
}));
|
||||
|
||||
if (cfg.get('hideErudaSetting')) this._hideErudaSetting = true;
|
||||
if (!cfg.get('observeElement')) this._observeElement = false;
|
||||
|
||||
cfg.on('change', (key, val) =>
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'hideErudaSetting': this._hideErudaSetting = val; return;
|
||||
case 'observeElement':
|
||||
this._observeElement = val;
|
||||
return val ? this._enableObserver() : this._disableObserver();
|
||||
}
|
||||
});
|
||||
|
||||
let settings = this._parent.get('settings');
|
||||
settings.text('Resources')
|
||||
.switch(cfg, 'hideErudaSetting', 'Hide Eruda Setting')
|
||||
.switch(cfg, 'observeElement', 'Auto Refresh Elements')
|
||||
.separator();
|
||||
}
|
||||
_render()
|
||||
@@ -352,36 +365,63 @@ export default class Resources extends Tool
|
||||
this._lastHtml = html;
|
||||
this._$el.html(html);
|
||||
}
|
||||
_observeScript()
|
||||
_initObserver()
|
||||
{
|
||||
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
||||
if (!MutationObserver) return;
|
||||
|
||||
this._scriptObserver = new MutationObserver(mutations =>
|
||||
this._observer = new util.SafeMutationObserver(mutations =>
|
||||
{
|
||||
if (!this.active) return;
|
||||
|
||||
mutations.forEach(mutation =>
|
||||
let needToRender = false;
|
||||
util.each(mutations, mutation =>
|
||||
{
|
||||
mutation.addedNodes.forEach(node =>
|
||||
{
|
||||
if (/^script$/i.test(node.tagName) && node.src !== '')
|
||||
{
|
||||
this._scriptData.push(node.src);
|
||||
if (this._handleMutation(mutation)) needToRender = true;
|
||||
});
|
||||
if (needToRender) this._render();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this._scriptData = util.unique(this._scriptData);
|
||||
this._render();
|
||||
});
|
||||
});
|
||||
|
||||
this._scriptObserver.observe(document.head, { childList: true });
|
||||
this._scriptObserver.observe(document.body, { childList: true });
|
||||
}
|
||||
_unobserveScript()
|
||||
_handleMutation(mutation)
|
||||
{
|
||||
if (this._scriptObserver) this._scriptObserver.disconnect();
|
||||
if (util.isErudaEl(mutation.target)) return;
|
||||
|
||||
let checkEl = el =>
|
||||
{
|
||||
let tagName = getLowerCaseTagName(el);
|
||||
switch (tagName)
|
||||
{
|
||||
case 'script': this.refreshScript(); return true;
|
||||
case 'img': this.refreshImage(); return true;
|
||||
case 'link': this.refreshStylesheet(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mutation.type === 'attributes')
|
||||
{
|
||||
if (checkEl(mutation.target)) return true;
|
||||
} else if (mutation.type === 'childList')
|
||||
{
|
||||
if (checkEl(mutation.target)) return true;
|
||||
let nodes = util.toArr(mutation.addedNodes);
|
||||
nodes = util.concat(nodes, util.toArr(mutation.removedNodes));
|
||||
|
||||
for (let node of nodes)
|
||||
{
|
||||
if (checkEl(node)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
_enableObserver()
|
||||
{
|
||||
this._observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
_disableObserver()
|
||||
{
|
||||
this._observer.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,4 +487,10 @@ function delCookie(key)
|
||||
}
|
||||
}
|
||||
|
||||
function getLowerCaseTagName(el)
|
||||
{
|
||||
if (!el.tagName) return '';
|
||||
return el.tagName.toLowerCase();
|
||||
}
|
||||
|
||||
let sliceStr = (str, len) => str.length < len ? str : str.slice(0, len) + '...';
|
||||
|
||||
125
src/lib/util.js
125
src/lib/util.js
@@ -359,6 +359,37 @@ module.exports = (function ()
|
||||
return exports;
|
||||
})();
|
||||
|
||||
/* ------------------------------ SafeMutationObserver ------------------------------ */
|
||||
|
||||
_.SafeMutationObserver = (function (exports)
|
||||
{
|
||||
/* Safe MutationObserver, does nothing if MutationObserver is not supported.
|
||||
*
|
||||
* ```javascript
|
||||
* var observer = new DomObserver(function (mutations)
|
||||
* {
|
||||
* // Do something.
|
||||
* });
|
||||
* observer.observe(document.htmlElement);
|
||||
* observer.disconnect();
|
||||
* ```
|
||||
*/
|
||||
|
||||
exports = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||
|
||||
if (!exports)
|
||||
{
|
||||
exports = class MutationObserver {
|
||||
constructor() {}
|
||||
observe() {}
|
||||
disconnect() {}
|
||||
takeRecords() {}
|
||||
};
|
||||
}
|
||||
|
||||
return exports;
|
||||
})({});
|
||||
|
||||
/* ------------------------------ noop ------------------------------ */
|
||||
|
||||
var noop = _.noop = (function ()
|
||||
@@ -1343,14 +1374,14 @@ module.exports = (function ()
|
||||
*/
|
||||
|
||||
/* dependencies
|
||||
* isNum has isFn
|
||||
* isNum isFn
|
||||
*/
|
||||
|
||||
var MAX_ARR_IDX = Math.pow(2, 53) - 1;
|
||||
|
||||
function exports(val)
|
||||
{
|
||||
if (!has(val, 'length')) return false;
|
||||
if (!val) return false;
|
||||
|
||||
var len = val.length;
|
||||
|
||||
@@ -3913,6 +3944,47 @@ module.exports = (function ()
|
||||
return exports;
|
||||
})({});
|
||||
|
||||
/* ------------------------------ concat ------------------------------ */
|
||||
|
||||
_.concat = (function ()
|
||||
{
|
||||
/* Concat multiple arrays into a single array.
|
||||
*
|
||||
* |Name |Type |Desc |
|
||||
* |------|-----|------------------|
|
||||
* |...arr|array|Arrays to concat |
|
||||
* |return|array|Concatenated array|
|
||||
*
|
||||
* ```javascript
|
||||
* concat([1, 2], [3], [4, 5]); // -> [1, 2, 3, 4, 5]
|
||||
* ```
|
||||
*/
|
||||
|
||||
/* module
|
||||
* env: all
|
||||
* test: all
|
||||
*/
|
||||
|
||||
/* dependencies
|
||||
* toArr
|
||||
*/
|
||||
|
||||
function exports()
|
||||
{
|
||||
var args = toArr(arguments),
|
||||
ret = [];
|
||||
|
||||
for (var i = 0, len = args.length; i < len; i++)
|
||||
{
|
||||
ret = ret.concat(toArr(args[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return exports;
|
||||
})();
|
||||
|
||||
/* ------------------------------ some ------------------------------ */
|
||||
|
||||
var some = _.some = (function ()
|
||||
@@ -5329,55 +5401,6 @@ module.exports = (function ()
|
||||
return exports;
|
||||
})();
|
||||
|
||||
/* ------------------------------ ready ------------------------------ */
|
||||
|
||||
_.ready = (function ()
|
||||
{
|
||||
/* Invoke callback when dom is ready, similar to jQuery ready.
|
||||
*
|
||||
* |Name|Type |Desc |
|
||||
* |----|--------|-----------------|
|
||||
* |fn |function|Callback function|
|
||||
*
|
||||
* ```javascript
|
||||
* ready(function ()
|
||||
* {
|
||||
* // It's safe to manipulate dom here.
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
/* module
|
||||
* env: browser
|
||||
* test: browser
|
||||
*/
|
||||
|
||||
var fns = [],
|
||||
listener,
|
||||
doc = document,
|
||||
hack = doc.documentElement.doScroll,
|
||||
domContentLoaded = 'DOMContentLoaded',
|
||||
loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState);
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
doc.addEventListener(domContentLoaded, listener = function ()
|
||||
{
|
||||
doc.removeEventListener(domContentLoaded, listener);
|
||||
loaded = 1;
|
||||
/* eslint-disable no-cond-assign */
|
||||
while (listener = fns.shift()) listener();
|
||||
});
|
||||
}
|
||||
|
||||
function exports(fn)
|
||||
{
|
||||
loaded ? setTimeout(fn, 0) : fns.push(fn)
|
||||
}
|
||||
|
||||
return exports;
|
||||
})();
|
||||
|
||||
/* ------------------------------ rtrim ------------------------------ */
|
||||
|
||||
var rtrim = _.rtrim = (function ()
|
||||
|
||||
Reference in New Issue
Block a user