浏览代码

增加工具:网页再造精灵,可以对任意页面做改造了

zxlie 6 年之前
父节点
当前提交
2e50e74902

+ 1 - 0
README.md

@@ -44,6 +44,7 @@ https://chrome.google.com/webstore/detail/pkgccpejnmalmdinmhkkfafefagiiiad?hl=zh
 - 页面性能检测(页面响应时间、Header监测)
 - 页面性能检测(页面响应时间、Header监测)
 - 页面栅格标尺(页面栅格化、屏幕标尺)
 - 页面栅格标尺(页面栅格化、屏幕标尺)
 - 多维小工具集(进制转换、RGB/HEX颜色转换、Crontab、还款计算器等)
 - 多维小工具集(进制转换、RGB/HEX颜色转换、Crontab、还款计算器等)
+- 网页再造精灵(网页特效、网页定制、脚本注入、自动刷新等)
 - Ajax调试功能(需在控制台中使用)
 - Ajax调试功能(需在控制台中使用)
 - 网页编码设置(UTF-8、GBK、日文、韩文等)
 - 网页编码设置(UTF-8、GBK、日文、韩文等)
 - 我的便签笔记(便签笔记,支持导出)
 - 我的便签笔记(便签笔记,支持导出)

+ 65 - 13
apps/background/index.js

@@ -217,21 +217,24 @@ var BgPageInstance = (function () {
 
 
         chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
         chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
             let tab = tabs[0];
             let tab = tabs[0];
-            callback && callback(feHelper.ajaxDebuggerMgr[tab.id]);
-
-            if (withAlert) {
-                let msg = '';
-                if (feHelper.ajaxDebuggerMgr[tab.id]) {
-                    if (devToolsDetected) {
-                        msg = 'DevTools已打开,确保已切换到【Console】界面,并关注信息输出,愉快的进行Ajax Debugger!'
+            if (tab) {
+                callback && callback(feHelper.ajaxDebuggerMgr[tab.id]);
+
+                if (withAlert) {
+                    let msg = '';
+                    if (feHelper.ajaxDebuggerMgr[tab.id]) {
+                        if (devToolsDetected) {
+                            msg = 'DevTools已打开,确保已切换到【Console】界面,并关注信息输出,愉快的进行Ajax Debugger!'
+                        } else {
+                            msg = '请打开DevTools,并切换到【Console】界面,关注信息输出,愉快的进行Ajax Debugger!';
+                        }
                     } else {
                     } else {
-                        msg = '请打开DevTools,并切换到【Console】界面,关注信息输出,愉快的进行Ajax Debugger!';
+                        msg = '已停止当前页面的Ajax Debugger功能!';
                     }
                     }
-                } else {
-                    msg = '已停止当前页面的Ajax Debugger功能!';
+                    alert(msg);
                 }
                 }
-                alert(msg);
             }
             }
+
         });
         });
     };
     };
 
 
@@ -496,6 +499,9 @@ var BgPageInstance = (function () {
                     MENU_MULTI_TOOLKIT: function (info, tab) {
                     MENU_MULTI_TOOLKIT: function (info, tab) {
                         _openFileAndRun(tab, MSG_TYPE.MULTI_TOOLKIT);
                         _openFileAndRun(tab, MSG_TYPE.MULTI_TOOLKIT);
                     },
                     },
+                    MENU_PAGE_MODIFIER: function (info, tab) {
+                        _openFileAndRun(tab, MSG_TYPE.PAGE_MODIFIER);
+                    },
                     MENU_GRID_RULER: function (info, tab) {
                     MENU_GRID_RULER: function (info, tab) {
                         _doGridDetect(tab);
                         _doGridDetect(tab);
                     },
                     },
@@ -706,7 +712,7 @@ var BgPageInstance = (function () {
                     return fileType;
                     return fileType;
                 }).toString() + ')()'
                 }).toString() + ')()'
             }, function (fileType) {
             }, function (fileType) {
-                if (fileType[0] === 'javascript' || fileType[0] === 'css') {
+                if (fileType && fileType.length && (fileType[0] === 'javascript' || fileType[0] === 'css')) {
                     Settings.getOptsFromBgPage(opts => {
                     Settings.getOptsFromBgPage(opts => {
                         opts.JS_CSS_PAGE_BEAUTIFY && chrome.tabs.sendMessage(tab.id, {
                         opts.JS_CSS_PAGE_BEAUTIFY && chrome.tabs.sendMessage(tab.id, {
                             type: MSG_TYPE.JS_CSS_PAGE_BEAUTIFY,
                             type: MSG_TYPE.JS_CSS_PAGE_BEAUTIFY,
@@ -719,6 +725,45 @@ var BgPageInstance = (function () {
         });
         });
     };
     };
 
 
+    /**
+     * 存储 网页涂鸦精灵 的配置
+     * @param params
+     * @param callback
+     * @private
+     */
+    let _savePageModifierConfigs = function (params, callback) {
+        !RegExp.prototype.toJSON && Object.defineProperty(RegExp.prototype, "toJSON", {
+            value: RegExp.prototype.toString
+        });
+        localStorage.setItem(MSG_TYPE.PAGE_MODIFIER_KEY, JSON.stringify(params));
+        callback && callback();
+    };
+
+    /**
+     * 获取 网页涂鸦精灵 的配置,如果指定了url参数,则表示只获取对应的一条配置,否则获取全部
+     * @param params
+     * @param callback
+     * @returns {*}
+     * @private
+     */
+    let _getPageModifierConfigs = function (params, callback) {
+        let cacheModifiers = JSON.parse(localStorage.getItem(MSG_TYPE.PAGE_MODIFIER_KEY) || '[]');
+        if (params && params.url) {
+            let result = null;
+            cacheModifiers.some(cm => {
+                let m = cm.mPattern.match(/\/(.*)\/(.*)?/);
+                if ((new RegExp(m[1], m[2] || "")).test(params.url)) {
+                    result = cm;
+                    return true;
+                }
+                return false;
+            });
+            callback && callback(result);
+        } else {
+            callback && callback(cacheModifiers);
+        }
+    };
+
 
 
     /**
     /**
      * 接收来自content_scripts发来的消息
      * 接收来自content_scripts发来的消息
@@ -776,7 +821,14 @@ var BgPageInstance = (function () {
             else if (request.type === MSG_TYPE.REMOVE_PERSON_IMG_BG) {
             else if (request.type === MSG_TYPE.REMOVE_PERSON_IMG_BG) {
                 Tarp.require('../remove-bg/proxy').addBackgroundRemoveListener(callback);
                 Tarp.require('../remove-bg/proxy').addBackgroundRemoveListener(callback);
             }
             }
-
+            // 网页涂鸦精灵:获取配置
+            else if (request.type === MSG_TYPE.GET_PAGE_MODIFIER_CONFIG) {
+                _getPageModifierConfigs(request.params, callback);
+            }
+            // 网页涂鸦精灵:保存配置
+            else if (request.type === MSG_TYPE.SAVE_PAGE_MODIFIER_CONFIG) {
+                _savePageModifierConfigs(request.params, callback);
+            }
 
 
             // ===========================以下为编码规范检测====start==================================
             // ===========================以下为编码规范检测====start==================================
             //处理CSS的请求
             //处理CSS的请求

+ 14 - 2
apps/manifest.json

@@ -1,9 +1,9 @@
 {
 {
   "name": "WEB前端助手(FeHelper)-Dev",
   "name": "WEB前端助手(FeHelper)-Dev",
-  "version": "2019.05.3115",
+  "version": "2019.06.1719",
   "manifest_version": 2,
   "manifest_version": 2,
   "default_locale": "zh_CN",
   "default_locale": "zh_CN",
-  "description": "FE助手:JSON格式化、JSON比对、二维码生成与解码、信息编解码、代码压缩&美化、页面取色、Markdown、网页截屏、编码设置、正则、Crontab、时间转换、网页性能检测、密码生成器、便签笔记、chrome插件下载等",
+  "description": "FE助手:JSON格式化、JSON比对、二维码、信息编解码、代码压缩&美化、页面取色、Markdown、网页截屏、编码设置、正则、Crontab、时间转换、网页性能检测、密码生成器、便签笔记、chrome插件下载等",
   "icons": {
   "icons": {
     "16": "static/img/fe-16.png",
     "16": "static/img/fe-16.png",
     "48": "static/img/fe-48.png",
     "48": "static/img/fe-48.png",
@@ -128,6 +128,18 @@
       ],
       ],
       "run_at": "document_end",
       "run_at": "document_end",
       "all_frames": true
       "all_frames": true
+    },
+    {
+      "matches": [
+        "http://*/*",
+        "https://*/*"
+      ],
+      "js": [
+        "static/js/msg_type.js",
+        "page-modifier/content-script.js"
+      ],
+      "run_at": "document_end",
+      "all_frames": false
     }
     }
   ],
   ],
   "content_security_policy": "style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-eval'; object-src 'self' ; media-src 'self' filesystem:",
   "content_security_policy": "style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-eval'; object-src 'self' ; media-src 'self' filesystem:",

文件差异内容过多而无法显示
+ 0 - 0
apps/options/index.html


+ 6 - 1
apps/options/settings.js

@@ -36,7 +36,8 @@ module.exports = (() => {
         'STICKY_NOTES',
         'STICKY_NOTES',
         'GRID_RULER',
         'GRID_RULER',
         'REMOVE_BG',
         'REMOVE_BG',
-        'MULTI_TOOLKIT'
+        'MULTI_TOOLKIT',
+        'PAGE_MODIFIER'
     ];
     ];
 
 
     // 默认处理关闭状态的功能,除非用户手动打开
     // 默认处理关闭状态的功能,除非用户手动打开
@@ -144,6 +145,10 @@ module.exports = (() => {
             icon: '⚘',
             icon: '⚘',
             text: '人像背景移除'
             text: '人像背景移除'
         },
         },
+        MENU_PAGE_MODIFIER: {
+            icon: '☃',
+            text: '网页再造精灵'
+        },
         MENU_DOWNLOAD_CRX: {
         MENU_DOWNLOAD_CRX: {
             icon:'↬',
             icon:'↬',
             text: '下载/分享插件'
             text: '下载/分享插件'

+ 310 - 0
apps/page-modifier/content-script.js

@@ -0,0 +1,310 @@
+/**
+ * 对整个页面增加多种滤镜选择,比如:增强对比度、反色等
+ * @example PageGrayTool.init('0/1/2/3/4/5')
+ */
+let PageGrayTool = (function () {
+
+    let mode;
+    let enabled = true;
+    let scheme = '4';
+
+    let svgContent = `
+	<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+		<defs>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_off">
+				<feComponentTransfer>
+					<feFuncR type="table" tableValues="0 1"/>
+					<feFuncG type="table" tableValues="0 1"/>
+					<feFuncB type="table" tableValues="0 1"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_highlight">
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="3.0"/>
+					<feFuncG type="gamma" exponent="3.0"/>
+					<feFuncB type="gamma" exponent="3.0"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_highlight_back">
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="0.33"/>
+					<feFuncG type="gamma" exponent="0.33"/>
+					<feFuncB type="gamma" exponent="0.33"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_grayscale">
+				<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="3"/>
+					<feFuncG type="gamma" exponent="3"/>
+					<feFuncB type="gamma" exponent="3"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_grayscale_back">
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="0.33"/>
+					<feFuncG type="gamma" exponent="0.33"/>
+					<feFuncB type="gamma" exponent="0.33"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_invert">
+				<feComponentTransfer>
+					<feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_invert_back">
+				<feComponentTransfer>
+					<feFuncR type="table" tableValues="1 0"/>
+					<feFuncG type="table" tableValues="1 0"/>
+					<feFuncB type="table" tableValues="1 0"/>
+				</feComponentTransfer>
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="1.7"/>
+					<feFuncG type="gamma" exponent="1.7"/>
+					<feFuncB type="gamma" exponent="1.7"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_invert_grayscale">
+				<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>
+				<feComponentTransfer>
+					<feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/>
+				</feComponentTransfer>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_yellow_on_black">
+				<feComponentTransfer>
+					<feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/>
+					<feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/>
+				</feComponentTransfer>
+				<feColorMatrix type="matrix" values="0.3 0.5 0.2 0 0 0.3 0.5 0.2 0 0 0 0 0 0 0 0 0 0 1 0"/>
+			</filter>
+			<filter x="0" y="0" width="99999" height="99999" id="_fh_page_modifier_yellow_on_black_back">
+				<feComponentTransfer>
+					<feFuncR type="table" tableValues="1 0"/>
+					<feFuncG type="table" tableValues="1 0"/>
+					<feFuncB type="table" tableValues="1 0"/>
+				</feComponentTransfer>
+				<feComponentTransfer>
+					<feFuncR type="gamma" exponent="0.33"/>
+					<feFuncG type="gamma" exponent="0.33"/>
+					<feFuncB type="gamma" exponent="0.33"/>
+				</feComponentTransfer>
+			</filter>
+		</defs>
+	</svg>`;
+
+    let cssTemplate = `
+	html[hc="a0"] {
+	    -webkit-filter: url("#_fh_page_modifier_off");
+	}
+	html[hcx="0"] img[src*="jpg"], 
+	html[hcx="0"] img[src*="jpeg"], 
+	html[hcx="0"] svg image, 
+	html[hcx="0"] img.rg_i, 
+	html[hcx="0"] embed, 
+	html[hcx="0"] object, 
+	html[hcx="0"] video {
+	    -webkit-filter: url("#_fh_page_modifier_off");
+	}
+	html[hc="a1"] {
+	    -webkit-filter: url("#_fh_page_modifier_highlight");
+	}
+	html[hcx="1"] img[src*="jpg"], 
+	html[hcx="1"] img[src*="jpeg"], 
+	html[hcx="1"] img.rg_i, 
+	html[hcx="1"] svg image, 
+	html[hcx="1"] embed, 
+	html[hcx="1"] object, 
+	html[hcx="1"] video {
+	    -webkit-filter: url("#_fh_page_modifier_highlight_back");
+	}
+	html[hc="a2"] {
+	    -webkit-filter: url("#_fh_page_modifier_grayscale");
+	}
+	html[hcx="2"] img[src*="jpg"], 
+	html[hcx="2"] img[src*="jpeg"], 
+	html[hcx="2"] img.rg_i, 
+	html[hcx="2"] svg image, 
+	html[hcx="2"] embed, 
+	html[hcx="2"] object, 
+	html[hcx="2"] video {
+	    -webkit-filter: url("#_fh_page_modifier_grayscale_back");
+	}
+	html[hc="a3"] {
+	    -webkit-filter: url("#_fh_page_modifier_invert");
+	}
+	html[hcx="3"] img[src*="jpg"], 
+	html[hcx="3"] img[src*="jpeg"], 
+	html[hcx="3"] img.rg_i, 
+	html[hcx="3"] svg image, 
+	html[hcx="3"] embed, 
+	html[hcx="3"] object, 
+	html[hcx="3"] video {
+	    -webkit-filter: url("#_fh_page_modifier_invert_back");
+	}
+	html[hc="a4"] {
+	    -webkit-filter: url("#_fh_page_modifier_invert_grayscale");
+	}
+	html[hcx="4"] img[src*="jpg"], 
+	html[hcx="4"] img[src*="jpeg"], 
+	html[hcx="4"] img.rg_i, 
+	html[hcx="4"] svg image, 
+	html[hcx="4"] embed, 
+	html[hcx="4"] object, 
+	html[hcx="4"] video {
+	    -webkit-filter: url("#_fh_page_modifier_invert_back");
+	}
+	html[hc="a5"] {
+	    -webkit-filter: url("#_fh_page_modifier_yellow_on_black");
+	}
+	html[hcx="5"] img[src*="jpg"], 
+	html[hcx="5"] img[src*="jpeg"], 
+	html[hcx="5"] img.rg_i, 
+	html[hcx="5"] svg image, 
+	html[hcx="5"] embed, 
+	html[hcx="5"] object, 
+	html[hcx="5"] video {
+	    -webkit-filter: url("#_fh_page_modifier_yellow_on_black_back");
+	}`;
+
+    /**
+     * Add the elements to the pgae that make high-contrast adjustments possible.
+     */
+    function addOrUpdateExtraElements() {
+        if (!enabled)
+            return;
+
+        let style = document.getElementById('hc_style');
+        if (!style) {
+            let baseUrl = window.location.href.replace(window.location.hash, '');
+            let css = cssTemplate.replace(/#/g, baseUrl + '#');
+            style = document.createElement('style');
+            style.id = 'hc_style';
+            style.setAttribute('type', 'text/css');
+            style.innerHTML = css;
+            document.head.appendChild(style);
+        }
+
+        let bg = document.getElementById('_fh_page_modifier_bkgnd');
+        if (!bg) {
+            bg = document.createElement('div');
+            bg.id = '_fh_page_modifier_bkgnd';
+            bg.style.position = 'fixed';
+            bg.style.left = '0px';
+            bg.style.top = '0px';
+            bg.style.right = '0px';
+            bg.style.bottom = '0px';
+            bg.style.zIndex = -1999999999;
+            document.body.appendChild(bg);
+        }
+        bg.style.display = 'block';
+        bg.style.background = window.getComputedStyle(document.body).background;
+
+        let c = bg.style.backgroundColor;
+        c = c.replace(/\s\s*/g, '');
+        if (m = /^rgba\(([\d]+),([\d]+),([\d]+),([\d]+|[\d]*.[\d]+)\)/.exec(c)) {
+            if (m[4] == '0') {
+                bg.style.backgroundColor = '#fff';
+            }
+        }
+
+        let wrap = document.getElementById('_fh_page_modifier_svg_filters');
+        if (wrap)
+            return;
+
+        wrap = document.createElement('span');
+        wrap.id = '_fh_page_modifier_svg_filters';
+        wrap.setAttribute('hidden', '');
+        wrap.innerHTML = svgContent;
+        document.body.appendChild(wrap);
+    }
+
+
+    function init(grayLevel) {
+
+        if (window === window.top) {
+            mode = 'a';
+        } else {
+            mode = 'b';
+        }
+
+        scheme = grayLevel || scheme;
+
+        let html = document.documentElement;
+
+        html.setAttribute('hc', mode + scheme);
+        html.setAttribute('hcx', scheme);
+
+        if (window === window.top) {
+            window.scrollBy(0, 1);
+            window.scrollBy(0, -1);
+        }
+
+        // Update again after a few seconds and again after load so that
+        // the background isn't wrong for long.
+        window.setTimeout(addOrUpdateExtraElements, 2000);
+        addOrUpdateExtraElements();
+
+        // Also update when the document body attributes change.
+        let config = {attributes: true, childList: true, characterData: true};
+        let observer = new MutationObserver(function (mutations) {
+            addOrUpdateExtraElements();
+        });
+        observer.observe(document.body, config);
+
+    }
+
+    return {
+        init: init
+    };
+})();
+
+/**
+ * 页面修改器
+ * @param pageConfig
+ * @constructor
+ */
+let PageModify = function (pageConfig) {
+    let pageUrl = location.href;
+    if (pageConfig.id && pageConfig.mPattern && !pageConfig.mDisabled) {
+
+        let m = pageConfig.mPattern.match(/\/(.*)\/(.*)?/);
+        // 如果正则匹配的话才生效
+        if ((new RegExp(m[1], m[2] || '')).test(pageUrl)) {
+
+            let el = document.createElement('script');
+            el.type = 'text/javascript';
+            el.textContent = pageConfig.mScript;
+            document.body.appendChild(el);
+
+            // 如果需要进行页面filter处理的话,直接处理
+            pageConfig.mFilter && PageGrayTool.init(pageConfig.mFilter);
+            // 自动刷新
+            parseInt(pageConfig.mRefresh) && setTimeout(() => {
+                location.reload(true);
+            }, parseInt(pageConfig.mRefresh) * 1000);
+        }
+
+    }
+};
+
+/**
+ * 获取当前页面modifier配置
+ */
+let ModifyCurrentPage = function () {
+
+    chrome.runtime.sendMessage({
+        type: MSG_TYPE.GET_PAGE_MODIFIER_CONFIG,
+        params: {
+            url: location.href
+        }
+    }, function (pageConfig) {
+        PageModify(pageConfig);
+    });
+};
+
+ModifyCurrentPage();

+ 139 - 0
apps/page-modifier/index.css

@@ -0,0 +1,139 @@
+@import url("../static/vendor/codemirror/codemirror.css");
+@import url("../static/css/bootstrap.min.css");
+
+.wrapper.wp-modifiers {
+	width:auto;
+	background: transparent;
+}
+.panel-title a.x-tooltip {
+	font-size: 12px;
+	color: #48b;
+	margin-left: 10px;
+}
+.panel-title a.x-tooltip:hover {
+	text-decoration: underline;
+}
+.x-toolbox {
+	float: right;
+}
+.x-toolbox .x-line {
+	font-size: 12px;
+	margin-left: 10px;
+	color: #bbb;
+}
+.x-wrapper {
+	position: relative;
+	color: #666;
+}
+.x-wrapper .x-sidebar {
+	position: absolute;
+	left: 0;
+	top:0;
+	width: 300px;
+	bottom: 0;
+	background: #fefefe;
+	border-right: 1px solid #ddd;
+}
+.x-sidebar .x-tools {
+	background: #f1f1f1;
+	padding: 2px 10px;
+	text-align: right;
+	border-bottom: 1px solid #ccc;
+}
+.x-sidebar .x-tools a{
+	color:#48b;
+	font-size: 12px;
+}
+.x-sidebar ul {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+}
+.x-sidebar ul li {
+	padding: 5px 10px;
+	background-color: #fff;
+	border-bottom: 1px solid #eee;
+	cursor: pointer;
+	font-size: 14px;
+	overflow: hidden;
+	text-overflow:ellipsis;
+	white-space: nowrap;
+	position: relative;
+}
+.x-sidebar ul li.x-selected:after {
+	content: '\2714';
+	position: absolute;
+	right: 10px;
+}
+.x-sidebar ul li:hover {
+	background: #f5f5f5;
+	color: #48b;
+}
+
+#main {
+	margin-left:300px;
+	padding: 20px;
+	position: relative;
+}
+.m-mask {
+	position: absolute;
+	top: 10px;
+	left: 10px;
+	right: 10px;
+	bottom: 10px;
+	background: #000;
+	opacity: 0.03;
+	z-index: 100;
+	border-radius: 10px;
+}
+.m-form {
+	padding: 0 20px;
+}
+.m-form.x-masked {
+	opacity: 0.4;
+}
+#mName,#mPattern,#mFilter,#mRefresh {
+	width: 600px;
+}
+.m-form .CodeMirror {
+	height: auto;
+}
+.m-form .CodeMirror-scroll {
+	min-height: 300px;
+}
+.xm-icon {
+	color: #fff;
+	display: inline-block;
+	vertical-align: middle;
+	font: normal normal bold 12px/1 "normal";
+	text-rendering: auto;
+	-webkit-font-smoothing: antialiased;
+	padding: 2px 5px;
+	height: 16px;
+	border-radius: 4px;
+	text-align: center;
+	font-weight: bold;
+}
+.xm-icon.xm-enable {
+	background-color: #07C160;
+}
+.xm-icon.xm-enable::before {
+	content: "\542F\7528\4E2D";
+}
+.xm-icon.xm-disable {
+	background-color: #f0ad4e;
+}
+.xm-icon.xm-disable::before {
+	content: "\5DF2\505C\7528";
+}
+.xm-icon.xm-create {
+	background-color: #868482;
+}
+.xm-icon.xm-create::before {
+	content: "\672A\4FDD\5B58";
+}
+.x-tips {
+	font-size: 12px;
+	color: #f00;
+	background-color: #ffffdd;
+}

+ 116 - 0
apps/page-modifier/index.html

@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html lang="zh-CN">
+    <head>
+        <meta charset="utf-8"/>
+        <title>网页再造精灵</title>
+        <link rel="stylesheet" href="index.css"/>
+        <script type="text/javascript" src="../static/vendor/vue/vue.js"></script>
+    </head>
+
+    <body>
+
+        <div class="wrapper wp-modifiers" id="pageContainer">
+            <div class="panel panel-default" style="margin-bottom: 0px;">
+                <div class="panel-heading">
+                    <h3 class="panel-title">
+                        <a href="http://www.baidufe.com/fehelper/feedback.html" target="_blank" class="x-a-high">
+                            <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper</a>:网页再造精灵
+
+                        <span class="x-toolbox">
+                            <a href="#" id="import" @click="importModifier" class="x-tooltip blue-tooltip">导入精灵</a><span class="x-line">|</span>
+                            <a href="#" id="export" @click="exportModifier" class="x-tooltip blue-tooltip">导出精灵</a><span class="x-line">|</span>
+                            <a href="#" id="disable" @click="disableModifier()" class="x-tooltip blue-tooltip">全部停用</a><span class="x-line">|</span>
+                            <a href="#" id="remove" @click="removeModifier" class="x-tooltip blue-tooltip">全部删除</a>
+                        </span>
+
+                    </h3>
+                </div>
+
+                <div class="x-wrapper">
+                    <div class="x-sidebar">
+                        <div class="x-tools">
+                            <a href="#" @click="createModifier">添加小精灵&gt;&gt;</a>
+                        </div>
+                        <ul id="modifiers">
+                            <li v-for="cm in cachedModifiers" :id="cm.id" :class="{'x-selected': editCM.id === cm.id}"
+                                @click="selectModifier(cm)">
+                                <i class="xm-icon" :class="cm.mDisabled === null ? 'xm-create' : (cm.mDisabled ? 'xm-disable' : 'xm-enable')"></i>
+                                <span v-html="cm.mName || '未命名'"></span>
+                            </li>
+                        </ul>
+                    </div>
+                    <div id="main">
+                        <form action="#" class="m-form x-masked" ref="mForm">
+                            <div class="row">
+                                <label for="mName">网页精灵名称:</label>
+                                <input type="text" id="mName" v-model="editCM.mName" class="form-control ui-d-ib" placeholder="比如:百度搜索结果过滤广告">
+                            </div>
+
+                            <div class="row ui-mt-10">
+                                <label for="mPattern">网址匹配规则:</label>
+                                <input type="text" id="mPattern" v-model="editCM.mPattern" class="form-control ui-d-ib" placeholder="如:/^https:\/\/www\.baidu\.com\/s/">
+                            </div>
+
+                            <div class="row ui-mt-10">
+                                <label for="mFilter">网页特效设定:</label>
+                                <select id="mFilter" v-model="editCM.mFilter" class="form-control ui-d-ib">
+                                    <option value="0">无特效</option>
+                                    <option value="1">特效模式一</option>
+                                    <option value="2">特效模式二</option>
+                                    <option value="3">特效模式三</option>
+                                    <option value="4">特效模式四</option>
+                                    <option value="5">特效模式五</option>
+                                </select>
+                            </div>
+
+                            <div class="row ui-mt-10">
+                                <label for="mRefresh">网页自动刷新:</label>
+                                <select id="mRefresh" v-model="editCM.mRefresh" class="form-control ui-d-ib">
+                                    <option value="0">不自动刷新</option>
+                                    <option value="1">每 1秒 一次</option>
+                                    <option value="3">每 3秒 一次</option>
+                                    <option value="5">每 5秒 一次</option>
+                                    <option value="10">每 10秒 一次</option>
+                                    <option value="15">每 15秒 一次</option>
+                                    <option value="30">每 30秒 一次</option>
+                                    <option value="60">每 1分(60秒) 一次</option>
+                                    <option value="90">每 1分半(90秒) 一次</option>
+                                    <option value="120">每 2分(120秒) 一次</option>
+                                    <option value="180">每 3分(180秒) 一次</option>
+                                    <option value="300">每 5分(300秒) 一次</option>
+                                    <option value="600">每 10分(600秒) 一次</option>
+                                </select>
+
+                            </div>
+
+                            <div class="row ui-mt-10">
+                                <label for="mScript">精灵注入脚本:</label>
+                                <span class="x-tips">(Tips:可以注入任意JS代码,甚至编写一个智能机器人也是可以的,比如网页定制、自动抢票啥的!)</span>
+                                <textarea id="mScript" ref="mScript" v-model="editCM.mScript" cols="30" rows="10" class="form-control"></textarea>
+                            </div>
+
+                            <div class="row ui-mt-10">
+                                <input type="button" @click="saveModifier(true)" value="保存" class="btn btn-success ui-mr-20">
+                                <input type="button" @click="disableModifier(editCM)" :value="editCM.mDisabled ? '启用' : '停用'" class="btn btn-warning ui-mr-20">
+                                <input type="button" @click="removeModifier(editCM)" value="删除" class="btn btn-danger">
+                            </div>
+                        </form>
+
+                        <div class="m-mask"></div>
+
+                    </div>
+                </div>
+                <div class="clearfix"></div>
+            </div>
+        </div>
+
+        <script src="../static/js/msg_type.js"></script>
+        <script src="../static/vendor/jquery/jquery-3.3.1.min.js"></script>
+        <script src="../static/vendor/codemirror/codemirror.js"></script>
+        <script src="../static/vendor/codemirror/javascript.js"></script>
+        <script src="../static/vendor/codemirror/active-line.js"></script>
+        <script src="../static/vendor/codemirror/matchbrackets.js"></script>
+        <script src="../static/vendor/codemirror/placeholder.js"></script>
+        <script src="index.js"></script>
+    </body>
+</html>

+ 257 - 0
apps/page-modifier/index.js

@@ -0,0 +1,257 @@
+/**
+ * 网页涂鸦精灵:可以针对任何网页进行任何涂鸦
+ * @author zhaoxianlie
+ */
+
+let editor = null;
+
+new Vue({
+    el: '#pageContainer',
+    data: {
+        editing: false,
+        editCM: {},
+
+        unSavedCMID: 0,
+
+        cachedModifiers: []
+    },
+    mounted: function () {
+        this.editCM = this.getANewCM();
+
+        // 编辑器初始化
+        editor = CodeMirror.fromTextArea(this.$refs.mScript, {
+            mode: "text/javascript",
+            lineNumbers: true,
+            matchBrackets: true,
+            styleActiveLine: true,
+            lineWrapping: true
+        });
+
+        // 退出的时候检测是否有未保存的数据
+        window.onbeforeunload = function (e) {
+            if (this.editing) {
+                (e || window.event).returnValue = '当前还有未保存的数据,确定要离开么?';
+            }
+        };
+
+        // 初始化获取数据
+        chrome.runtime.sendMessage({
+            type: MSG_TYPE.GET_PAGE_MODIFIER_CONFIG
+        }, (cmList) => {
+            this.cachedModifiers = cmList || [];
+        });
+    },
+
+    methods: {
+        getANewCM: function () {
+            return {
+                mName: '',
+                mPattern: '',
+                mFilter: '0',
+                mScript: '',
+                mRefresh: 0,
+                mDisabled: null
+            };
+        },
+
+        createModifier: function () {
+            if (this.editing) {
+                return alert('当前还有未保存的数据,无法继续创建!');
+            }
+            this.editing = true;
+
+            this.editCM = this.getANewCM();
+            this.editCM.id = 'mf_' + new Date() * 1;
+            this.cachedModifiers.push(this.editCM);
+
+            // 右侧面板,进行表单项编辑
+            this.$refs.mForm.reset();
+            $('.m-mask').slideUp();
+            $('.m-form').removeClass('x-masked');
+        },
+
+        selectModifier: function (cm) {
+            // 在编辑中,并且确定还要当前这个数据,就设定为选择无效
+            if (this.editing) {
+                if (confirm('当前还有未保存的数据,确定不要这个数据了吗?')) {
+                    this.editing = false;
+                    this.cachedModifiers.pop();
+                } else {
+                    return false;
+                }
+            }
+
+            // 如果当前数据还没保存,也点击无效
+            if (this.editCM.id === cm.id) {
+                return false;
+            }
+
+            // 把数据呈现到编辑面板
+            this.editCM = cm;
+            editor.setValue(cm.mScript);
+            $('.m-mask').slideUp();
+            $('.m-form').removeClass('x-masked');
+        },
+
+        saveModifier: function (isEditMode) {
+            if (isEditMode) {
+                this.mScript = editor.getValue();
+                this.cachedModifiers.some(cm => {
+                    if (cm.id === this.editCM.id) {
+                        cm.mName = this.editCM.mName;
+                        cm.mPattern = this.editCM.mPattern;
+                        cm.mFilter = this.editCM.mFilter;
+                        cm.mRefresh = this.editCM.mRefresh;
+                        cm.mScript = editor.getValue();
+                        return true;
+                    }
+                });
+            }
+
+            chrome.runtime.sendMessage({
+                type: MSG_TYPE.SAVE_PAGE_MODIFIER_CONFIG,
+                params: this.cachedModifiers
+            }, () => {
+                alert('数据操作成功!');
+                this.editCM = this.getANewCM();
+                this.editing = false;
+
+                this.$refs.mForm.reset();
+                $('.m-mask').slideDown();
+                $('.m-form').addClass('x-masked');
+            });
+        },
+
+        // 导入配置
+        importModifier: function () {
+            let that = this;
+            let fileInput = document.getElementById('fileInput');
+            if (!fileInput) {
+                fileInput = document.createElement('input');
+                fileInput.id = 'fileInput';
+                fileInput.type = 'file';
+                fileInput.accept = 'application/json';
+                fileInput.style.cssText = 'position:relative;top:-1000px;left:-1000px;';
+                fileInput.onchange = function (event) {
+                    let reader = new FileReader();
+                    reader.readAsText(fileInput.files[0], 'utf-8');
+                    reader.onload = (evt) => {
+                        let content = evt.target.result;
+                        try {
+                            // 过滤掉文件头部所有注释,然后转化成json
+                            let list = JSON.parse(content.replace(/^\/\*[^\*]*\*\//, ''));
+                            if (list && Array.isArray(list) && list.length) {
+                                let keys = 'id,mName,mPattern,mFilter,mRefresh,mScript,mDisabled'.split(',');
+                                let result = list.filter(item => {
+                                    if (typeof item === 'object') {
+                                        Object.keys(item).forEach(k => {
+                                            !keys.includes(k) && delete(item[k]);
+                                        });
+                                        if (Object.keys(item).length) {
+                                            return true;
+                                        }
+                                    }
+                                    return false;
+                                });
+                                if (result.length) {
+                                    let merge = null;
+                                    // 配置合并,如果有重复的,则弹框确认
+                                    result.forEach(r => {
+                                        let found = that.cachedModifiers.some(cm => {
+                                            if (r.id === cm.id || r.mName === cm.mName || r.mPattern === cm.mPattern) {
+                                                if (merge === null) {
+                                                    merge = confirm('发现有相同名称或规则的精灵,是否选择覆盖?');
+                                                }
+                                                if (merge) {
+                                                    cm = r;
+                                                } else {
+                                                    r.id += '_';
+                                                }
+                                                return merge;
+                                            }
+                                        });
+                                        if (!found) {
+                                            that.cachedModifiers.push(r);
+                                        }
+                                    });
+                                    return that.saveModifier();
+                                }
+                            }
+                            throw new Error();
+                        } catch (e) {
+                            alert('当前选择的JSON配置文件格式不正确!');
+                        }
+                    };
+                };
+                document.body.appendChild(fileInput);
+            }
+            fileInput.click();
+        },
+
+        // 导出配置
+        exportModifier: function () {
+            chrome.runtime.sendMessage({
+                type: MSG_TYPE.GET_PAGE_MODIFIER_CONFIG
+            }, (cmList) => {
+                if (cmList && cmList.length) {
+                    let timestamp = new Date() * 1;
+                    let exportPrefix = '/* Page modifier config, exported from FeHelper, timestamp:' + timestamp + ' */\n\n';
+                    let exportContent = JSON.stringify(cmList, null, 4);
+
+                    let blob = new Blob([exportPrefix + exportContent], {type: 'application/octet-stream'});
+
+                    // 请求权限
+                    chrome.permissions.request({
+                        permissions: ['downloads']
+                    }, (granted) => {
+                        if (granted) {
+                            chrome.downloads.download({
+                                url: URL.createObjectURL(blob),
+                                saveAs: true,
+                                conflictAction: 'overwrite',
+                                filename: 'FeHelper-PM-' + timestamp + '.json'
+                            }, () => {
+                                alert('数据导出成功!');
+                            });
+                        } else {
+                            alert('必须接受授权,才能正常导出!');
+                        }
+                    });
+                } else {
+                    alert('没有已保存/可使用的精灵,不可导出!');
+                }
+            });
+        },
+
+        // 清空精灵
+        removeModifier: function (theCM) {
+            if (confirm('你确定要删除所有的精灵吗,此操作不可撤销!')) {
+                if (theCM) {
+                    this.cachedModifiers = this.cachedModifiers.filter(cm => {
+                        return cm.id !== theCM.id;
+                    });
+                } else {
+                    this.cachedModifiers = [];
+                }
+                this.saveModifier();
+            }
+        },
+
+        // 停用精灵
+        disableModifier: function (theCM) {
+            if (theCM) {
+                if (confirm('请再次确认是否需要' + (theCM.mDisabled ? '启用' : '停用') + '此精灵?')) {
+                    this.editCM.mDisabled = !theCM.mDisabled;
+                    this.saveModifier(true);
+                }
+            } else {
+                if (confirm('停用精灵后,可单独编辑启用;是否继续此操作?')) {
+                    this.cachedModifiers.forEach(cm => {
+                        cm.mDisabled = true;
+                    });
+                    this.saveModifier();
+                }
+            }
+        }
+    }
+});

+ 3 - 0
apps/popup/index.css

@@ -160,6 +160,9 @@ ul.fe-function-list li.-x-remove-bg b {
 ul.fe-function-list li.-x-multi-toolkit b {
 ul.fe-function-list li.-x-multi-toolkit b {
     background-position: -176px -111px;
     background-position: -176px -111px;
 }
 }
+ul.fe-function-list li.-x-page-modifier b {
+    background-position: -32px -95px;
+}
 
 
 ul.fe-function-list li i {
 ul.fe-function-list li i {
     color: #aaa;
     color: #aaa;

+ 2 - 0
apps/popup/index.html

@@ -43,6 +43,8 @@
                     <b></b>页面性能检测</li>
                     <b></b>页面性能检测</li>
                 <li class="-x-grid-ruler" @click="runHelper('GRID_RULER',0)" v-if="canMeShow.GRID_RULER">
                 <li class="-x-grid-ruler" @click="runHelper('GRID_RULER',0)" v-if="canMeShow.GRID_RULER">
                     <b></b>页面栅格标尺</li>
                     <b></b>页面栅格标尺</li>
+                <li class="-x-page-modifier" @click="runHelper('PAGE_MODIFIER',1)" v-if="canMeShow.PAGE_MODIFIER">
+                    <b></b>网页再造精灵</li>
                 <li class="-x-multi-toolkit" @click="runHelper('MULTI_TOOLKIT',1)" v-if="canMeShow.MULTI_TOOLKIT">
                 <li class="-x-multi-toolkit" @click="runHelper('MULTI_TOOLKIT',1)" v-if="canMeShow.MULTI_TOOLKIT">
                     <b></b>多维小工具集</li>
                     <b></b>多维小工具集</li>
                 <li class="-x-ajax-debugger" title="在开发者工具-Console面板使用" @click="runHelper('AJAX_DEBUGGER',0)" v-if="canMeShow.AJAX_DEBUGGER">
                 <li class="-x-ajax-debugger" title="在开发者工具-Console面板使用" @click="runHelper('AJAX_DEBUGGER',0)" v-if="canMeShow.AJAX_DEBUGGER">

+ 9 - 0
apps/static/js/msg_type.js

@@ -114,6 +114,15 @@ const MSG_TYPE = {
     // 多维小工具
     // 多维小工具
     MULTI_TOOLKIT: 'toolkit',
     MULTI_TOOLKIT: 'toolkit',
 
 
+    // 打开page-modifier配置页
+    PAGE_MODIFIER:'page-modifier',
+    // 获取某个url对应的page-modifier配置
+    GET_PAGE_MODIFIER_CONFIG:'get_page_modifier_config',
+    // 保存page-modifier配置
+    SAVE_PAGE_MODIFIER_CONFIG:'save_page_modifier_config',
+    // page-config配置项的本地缓存key
+    PAGE_MODIFIER_KEY:'PAGE-MODIFIER-LOCAL-STORAGE-KEY',
+
     // 人像背景移除
     // 人像背景移除
     REMOVE_PERSON_IMG_BG:'remove-person-img-bg',
     REMOVE_PERSON_IMG_BG:'remove-person-img-bg',
     REMOVE_BG:'remove-bg'
     REMOVE_BG:'remove-bg'

部分文件因为文件数量过多而无法显示