├── .gitignore ├── CNAME ├── .eslintignore ├── src ├── img │ ├── icon-16.png │ ├── icon-48.png │ ├── icon-128.png │ └── coande-200.png ├── css │ ├── iconfont │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.woff2 │ │ ├── iconfont.css │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── iconfont.svg │ │ └── iconfont.js │ ├── base.css │ ├── options.css │ ├── youdao.css │ ├── baidu.css │ ├── google.css │ ├── kingsoft.css │ ├── sogou.css │ └── extension.css ├── lib │ ├── youdao-web-translate │ │ └── web2 │ │ │ ├── styles │ │ │ ├── cover.css │ │ │ └── all-packed.css │ │ │ ├── images │ │ │ ├── bar-sp.png │ │ │ ├── swipe_hr.png │ │ │ ├── ydd_tip.png │ │ │ ├── switch_button.png │ │ │ ├── bar-sp-repeat-x.png │ │ │ ├── switch_button_hover.png │ │ │ ├── trans_tip_submit_bg.png │ │ │ └── trans_tip_submit_bg_hover.png │ │ │ ├── seed.js │ │ │ ├── JTRAssist.swf │ │ │ └── conn.html │ ├── hot-reload.js │ └── loadCSS.js ├── js │ ├── popup-ga.js │ ├── options-ga.js │ ├── trans-css.js │ ├── iframe-messager.js │ ├── utils.js │ ├── ext-data.js │ ├── ga.js │ ├── fake-navigator.js │ ├── options.js │ ├── popup.js │ ├── background.js │ └── content-script.js ├── background.html ├── options.html └── popup.html ├── .editorconfig ├── .eslintrc.js ├── package.json ├── README.md ├── manifest.json └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | co-trans-ext.e12e.com -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | iconfont 3 | -------------------------------------------------------------------------------- /src/img/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/img/icon-16.png -------------------------------------------------------------------------------- /src/img/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/img/icon-48.png -------------------------------------------------------------------------------- /src/img/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/img/icon-128.png -------------------------------------------------------------------------------- /src/img/coande-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/img/coande-200.png -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/css/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/css/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/css/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/css/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/styles/cover.css: -------------------------------------------------------------------------------- 1 | /* 部分网页把iframe隐藏掉了,如Google */ 2 | iframe.OUTFOX_JTR_BAR { 3 | display: block !important; 4 | } -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/bar-sp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/bar-sp.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/swipe_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/swipe_hr.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/ydd_tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/ydd_tip.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/switch_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/switch_button.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/bar-sp-repeat-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/bar-sp-repeat-x.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/switch_button_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/switch_button_hover.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/trans_tip_submit_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/trans_tip_submit_bg.png -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/images/trans_tip_submit_bg_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coande/co-trans-ext/HEAD/src/lib/youdao-web-translate/web2/images/trans_tip_submit_bg_hover.png -------------------------------------------------------------------------------- /src/js/popup-ga.js: -------------------------------------------------------------------------------- 1 | // 谷歌分析 2 | function ga(...args) { 3 | args.push({ 4 | page: '/popup.html' 5 | }); 6 | const bg = chrome.extension.getBackgroundPage(); 7 | bg.ga(...args); 8 | } 9 | 10 | ga('send', 'pageview'); 11 | -------------------------------------------------------------------------------- /src/js/options-ga.js: -------------------------------------------------------------------------------- 1 | // 谷歌分析 2 | function ga(...args) { 3 | args.push({ 4 | page: '/options.html' 5 | }); 6 | const bg = chrome.extension.getBackgroundPage(); 7 | bg.ga(...args); 8 | } 9 | 10 | ga('send', 'pageview'); 11 | -------------------------------------------------------------------------------- /src/css/base.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar {/*滚动条整体样式*/ 2 | width: 10px; /*高宽分别对应横竖滚动条的尺寸*/ 3 | height: 10px; 4 | } 5 | ::-webkit-scrollbar-thumb {/*滚动条里面小方块*/ 6 | background: rgba(3, 169, 244, 0.9); 7 | } 8 | ::-webkit-scrollbar-track {/*滚动条里面轨道*/ 9 | background: #B3E5FC; 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true 5 | }, 6 | extends: 'airbnb-base', 7 | globals: { 8 | Atomics: 'readonly', 9 | SharedArrayBuffer: 'readonly' 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 2018 13 | }, 14 | rules: { 15 | 'no-undef': 'off', 16 | 'comma-dangle': ['error', { functions: 'ignore' }], 17 | 'no-plusplus': 'off', 18 | 'no-prototype-builtins': 'off', 19 | 'prefer-destructuring': 'off' 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 背景页 - co-trans-ext 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "co-trans-ext", 3 | "version": "1.0.0", 4 | "description": "![logo](https://i.loli.net/2019/04/22/5cbd80f8da40b.png)", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "lint": "eslint ." 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Coande/co-trans-ext.git" 16 | }, 17 | "author": "", 18 | "license": "GPL-3.0", 19 | "bugs": { 20 | "url": "https://github.com/Coande/co-trans-ext/issues" 21 | }, 22 | "homepage": "https://github.com/Coande/co-trans-ext#readme", 23 | "devDependencies": { 24 | "eslint": "^5.16.0", 25 | "eslint-config-airbnb-base": "^13.1.0", 26 | "eslint-plugin-import": "^2.17.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/js/trans-css.js: -------------------------------------------------------------------------------- 1 | (function loadCustomCss() { 2 | const url = new URL(window.location.href); 3 | const origin = url.origin; 4 | switch (origin) { 5 | case 'https://fanyi.sogou.com': 6 | loadCSS('https://cdn.jsdelivr.net/gh/Coande/co-trans-ext@master/src/css/sogou.css'); 7 | break; 8 | case 'https://fanyi.baidu.com': 9 | loadCSS('https://cdn.jsdelivr.net/gh/Coande/co-trans-ext@master/src/css/baidu.css'); 10 | break; 11 | case 'https://translate.google.cn': 12 | loadCSS('https://cdn.jsdelivr.net/gh/Coande/co-trans-ext@master/src/css/google.css'); 13 | break; 14 | case 'https://m.youdao.com': 15 | loadCSS('https://cdn.jsdelivr.net/gh/Coande/co-trans-ext@master/src/css/youdao.css'); 16 | break; 17 | case 'https://m.iciba.com': 18 | loadCSS('https://cdn.jsdelivr.net/gh/Coande/co-trans-ext@master/src/css/kingsoft.css'); 19 | break; 20 | 21 | default: 22 | break; 23 | } 24 | }()); 25 | -------------------------------------------------------------------------------- /src/js/iframe-messager.js: -------------------------------------------------------------------------------- 1 | // iframe页面通讯 2 | // https://www.cnblogs.com/syll/p/8640329.html 3 | // 接收iframe外部通过postMessage传递过来的数据 4 | window.addEventListener('message', (event) => { 5 | if (event.data.op === 'showDetail') { 6 | document.body.classList.add('trans-ext-detail'); 7 | } else if (event.data.op === 'hideDetail') { 8 | document.body.classList.remove('trans-ext-detail'); 9 | } 10 | }); 11 | 12 | window.addEventListener('message', (event) => { 13 | if (event.data.changeTransTool) { 14 | const keyword = $('#textarea-source').val() || $('#j-textarea').val() 15 | || $('#source').val() || $('#index-input-main').val() || $('#formInput').val() || $('#trans-input').val(); 16 | window.parent.postMessage({ changeTransTool: event.data.changeTransTool, keyword: keyword || '' }, '*'); 17 | } 18 | }); 19 | 20 | // 接收query上的数据并判断是否需要显示输入内容和语言切换等 21 | const isShowDetail = getQueryVariable(window.location.href, 'showDetail'); 22 | if (isShowDetail === 'true') { 23 | document.body.classList.add('trans-ext-detail'); 24 | } else { 25 | document.body.classList.remove('trans-ext-detail'); 26 | } 27 | -------------------------------------------------------------------------------- /src/css/options.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | font-family: "lucida sans unicode", lucida, helvetica, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; 4 | -webkit-font-smoothing: antialiased; 5 | color: #555555; 6 | margin: 0; 7 | } 8 | 9 | input[type='checkbox'] { 10 | vertical-align: top; 11 | } 12 | 13 | a { 14 | text-decoration: none; 15 | color: #03a9f4; 16 | } 17 | 18 | .page-wrapper { 19 | padding: 20px; 20 | width: 600px; 21 | margin: 0 auto; 22 | } 23 | 24 | .para + .para { 25 | margin-top: 30px; 26 | } 27 | 28 | .para__title { 29 | font-size: 20px; 30 | color: #333333; 31 | margin-bottom: 10px; 32 | } 33 | 34 | .reward__button { 35 | display: inline-block; 36 | height: 24px; 37 | line-height: 24px; 38 | margin-left: 10px; 39 | background: #03a9f4; 40 | padding: 0 6px; 41 | color: #ffffff; 42 | } 43 | .reward__list { 44 | width: 600px; 45 | height: 101px; 46 | margin-top: 10px; 47 | } 48 | 49 | .reward__list-iframe { 50 | display: block; 51 | width: 100%; 52 | height: 100%; 53 | border: none; 54 | } 55 | 56 | .already-latest, 57 | .not-latest { 58 | display: none; 59 | } -------------------------------------------------------------------------------- /src/css/youdao.css: -------------------------------------------------------------------------------- 1 | /* 有时候顶部会出现app广告 */ 2 | div[style="position: absolute; top: 0px; left: 0px; z-index: 201;"] { 3 | display: none !important; 4 | } 5 | 6 | /* 出现顶部app广告时会有个margin-top */ 7 | .p-dict { 8 | margin-top: 0 !important; 9 | } 10 | 11 | /* 页面头部,分词典和翻译,但是翻译不能用了,一律隐藏掉 */ 12 | .nav-label { 13 | display: none !important; 14 | } 15 | 16 | /* 搜索内容输入框 */ 17 | body:not(.trans-ext-detail) .search-area { 18 | display: none; 19 | } 20 | 21 | /* 基本释义右上角纠错按钮 */ 22 | .ec h2 .amend { 23 | display: none; 24 | } 25 | 26 | /* 底部App广告 */ 27 | .dict-dl { 28 | display: none !important; 29 | } 30 | 31 | /* 脚本备案信息 */ 32 | #ft { 33 | display: none; 34 | } 35 | 36 | /* 发音喇叭下面的音频大小 */ 37 | .speech-size { 38 | display: none; 39 | } 40 | 41 | /* 没有keyword时的页面 */ 42 | /* 顶部App广告 */ 43 | div[style*="position: fixed; top: 0px; left: 0px;"] { 44 | display: none; 45 | } 46 | 47 | .logo { 48 | display: none; 49 | } 50 | 51 | body { 52 | margin-top: 0 !important; 53 | } 54 | 55 | .other-links { 56 | display: none; 57 | } 58 | 59 | /* 有时候会有全屏广告 */ 60 | div[style*="position: absolute; width: 100%; height: 100%; z-index: 201; top: 0px; left: 0px;"] { 61 | display: none !important; 62 | } -------------------------------------------------------------------------------- /src/css/baidu.css: -------------------------------------------------------------------------------- 1 | /* 网页头部 */ 2 | .fanyi-sfr-container .header#shoubai-header { 3 | display: none !important; 4 | } 5 | .fanyi-sfr-container #new-header { 6 | display: none !important; 7 | } 8 | 9 | .fanyi-sfr-container .topnav-new { 10 | top: 0 !important; 11 | } 12 | /* 底部广告 */ 13 | .jifeng-container { 14 | display: none !important; 15 | } 16 | 17 | /* 语言选择栏 */ 18 | body:not(.trans-ext-detail) .fanyi-sfr-container .translang { 19 | /* 自动检测语言(如检测为中文)时,语言栏切换动画依赖该元素的可见性, 20 | display 隐藏后会导致页面无法正常加载,所以设置 height 为 0 来隐藏元素 */ 21 | /* display: none !important; */ 22 | height: 0px; 23 | overflow: hidden; 24 | } 25 | 26 | /* 内容输入框 */ 27 | body:not(.trans-ext-detail) .fanyi-sfr-container .trans-input { 28 | display: none; 29 | } 30 | 31 | /* 页面底部百度翻译app广告 */ 32 | .fanyi-sfr-container .bottom-intro { 33 | display: none !important; 34 | } 35 | 36 | /* 翻译结果下面的百度翻译app广告 */ 37 | .fanyi-sfr-container .app-bar { 38 | display: none !important; 39 | } 40 | 41 | /* 翻译来源条(如结果来自金山词霸) */ 42 | .fanyi-sfr-container .concise-dict .dict-origin-wrap { 43 | display: none !important; 44 | } 45 | 46 | /* 如果关键字为空时,会在底部显示英文文章 */ 47 | .fanyi-sfr-container .article { 48 | display: none !important; 49 | } 50 | -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // 动态加载css 3 | function loadCSS(file, id) { 4 | const linkObj = document.getElementById(id); 5 | if (linkObj) { 6 | return; 7 | } 8 | const link = document.createElement('link'); 9 | link.href = file; 10 | link.id = id; 11 | link.type = 'text/css'; 12 | link.rel = 'stylesheet'; 13 | document.getElementsByTagName('head')[0].appendChild(link); 14 | } 15 | 16 | // 移除css 17 | function unloadCSS(id) { 18 | const cssNode = document.getElementById(id); 19 | if (cssNode) { 20 | cssNode.parentNode.removeChild(cssNode); 21 | } 22 | } 23 | 24 | // 获取 query 参数 25 | function getQueryVariable(url, variable) { 26 | const strArr = new URL(url).search.split('?'); 27 | if (strArr[1]) { 28 | const query = strArr[1]; 29 | const vars = query.split('&'); 30 | for (let i = 0; i < vars.length; i++) { 31 | const pair = vars[i].split('='); 32 | if (pair[0] === variable) { 33 | return pair[1]; 34 | } 35 | } 36 | } 37 | return undefined; 38 | } 39 | 40 | // iframe 异步加载 41 | function loadIframeSrc(iframeObj, src) { 42 | const iframe = iframeObj[0] || iframeObj; 43 | setTimeout(() => { 44 | iframe.setAttribute('src', src); 45 | }, 0); 46 | } 47 | -------------------------------------------------------------------------------- /src/js/ext-data.js: -------------------------------------------------------------------------------- 1 | // 数据存储操作对象 2 | function ExtData() {} 3 | 4 | ExtData.prototype.set = (key, value, callback) => { 5 | chrome.storage.sync.set({ [key]: value }, () => { 6 | if (callback) callback(); 7 | }); 8 | this[key] = value; 9 | }; 10 | 11 | ExtData.prototype.get = (key, callback) => { 12 | const defaultOptions = { 13 | transTool: 'sogou', 14 | sogou: 15 | 'https://fanyi.sogou.com/?keyword=KEYWORD&transfrom=auto&transto=zh-CHS&x-from=co-translate-extension&showDetail=SHOWDETAIL', 16 | baidu: 17 | 'https://fanyi.baidu.com/?x-from=co-translate-extension&showDetail=SHOWDETAIL#en/zh/KEYWORD', 18 | // Google 如果不用 https,会自动跳转到 https ,导致 query 数据丢失 19 | google: 20 | 'https://translate.google.cn/?x-from=co-translate-extension&showDetail=SHOWDETAIL#view=home&op=translate&sl=auto&tl=zh-CN&text=KEYWORD', 21 | youdao: 22 | 'https://m.youdao.com/dict?le=eng&q=KEYWORD&x-from=co-translate-extension&showDetail=SHOWDETAIL', 23 | kingsoft: 24 | 'https://m.iciba.com/KEYWORD?flag=searchBack&x-from=co-translate-extension&showDetail=SHOWDETAIL', 25 | isEnabledAnalytics: true 26 | }; 27 | chrome.storage.sync.get([key], (result) => { 28 | if (result[key] === undefined) { 29 | callback(defaultOptions[key]); 30 | return; 31 | } 32 | callback(result[key]); 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /src/js/ga.js: -------------------------------------------------------------------------------- 1 | window.ga = window.ga 2 | // eslint-disable-next-line func-names 3 | || function () { 4 | // eslint-disable-next-line prefer-rest-params 5 | (ga.q = ga.q || []).push(arguments); 6 | }; 7 | ga.l = +new Date(); 8 | 9 | /** ************************* 初始化内容 ********************** */ 10 | const propertyID = 'UA-65836121-2'; 11 | ga('create', propertyID, 'auto'); 12 | // 以下必须设置,否则会检查协议,默认只支持http和https 13 | ga('set', 'checkProtocolTask', null); 14 | 15 | // 只要设置 appVersion,Google Analytics 就接收不到数据,是一个bug。只要自定义一个维度。 16 | // https://stackoverflow.com/questions/36508241/how-do-i-set-appversion-for-google-analytics-event-tracking 17 | // ga('set', 'appVersion', chrome.runtime.getManifest().version); 18 | ga('set', 'dimension1', chrome.runtime.getManifest().version); 19 | 20 | // 设置是否停用 21 | function setIsDisableGA(isDisable) { 22 | window[`ga-disable-${propertyID}`] = isDisable; 23 | } 24 | 25 | // 判断是否需要禁用 26 | const data = new ExtData(); 27 | data.get('isEnabledAnalytics', (isEnable) => { 28 | setIsDisableGA(!isEnable); 29 | }); 30 | 31 | /** ************************* 功能函数 *********************** */ 32 | 33 | // 运行时记录 34 | // 必须设置路径,否则谷歌分析拒绝接收 chrome-extension 协议路径 35 | ga('send', 'pageview', '/background.html'); 36 | 37 | // 监听从 content-script 传递过来的事件 38 | chrome.extension.onRequest.addListener((request) => { 39 | if (request.ga) { 40 | ga(...request.ga); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # co-trans-ext 2 | ![logo](https://i.loli.net/2019/04/22/5cbd80f8da40b.png) 3 | 4 | **co-trans-ext** 是一个集搜狗翻译、百度翻译、有道翻译、金山词霸、谷歌翻译于一体的翻译扩展。各平台可随时切换,取长补短,更适合于阅读各领域的文档。不止于翻译,解析详细,更利于对各门语言的深入学习与理解。支持划词翻译和网页翻译。项目地址:[https://github.com/Coande/co-trans-ext](https://github.com/Coande/co-trans-ext) 5 | 6 | 7 | ![web-popup](https://i.loli.net/2019/04/22/5cbd53632042a.gif) 8 | 9 | ![fixed](https://i.loli.net/2019/05/16/5cdd2ec1c5f7f57594.gif) 10 | 11 | ![browser-popup](https://i.loli.net/2019/04/22/5cbd536355514.gif) 12 | 13 | 14 | ## 下载 15 | 16 | [**进入下载页**](https://github.com/Coande/co-trans-ext/releases/) 17 | 18 | ## 安装 19 | 20 | #### 方法一 21 | 适用于 Chrome 第三方浏览器,如 [360极速浏览器](https://browser.360.cn/ee/)。直接拖拽 crx 文件到浏览器窗口即可安装。另外,360极速浏览器还可以直接从[360极速浏览器扩展中心](https://ext.chrome.360.cn/webstore/detail/keigenoolicjcehlbpjcfhdjdmaochie)进行安装。 22 | 23 | #### 方法二 24 | 适用于 Google Chrome 及 Chrome 第三方浏览器,可以从[chrome 网上应用店 25 | ](https://chrome.google.com/webstore/detail/co-trans-ext-%E7%BF%BB%E8%AF%91%E5%B7%A5%E5%85%B7/ncfklglegjceemdfaiblepihhlkoafge?utm_source=chrome-ntp-icon)(需自备梯子)进行安装。 26 | 27 | #### 方法三 28 | 适用于 Google Chrome,由于新版本 Google Chrome 限制了非 Chrome 应用商店内扩展的安装,而当前翻译扩展由于个人原因暂未上架 Chrome 应用商店,所以安装起来略麻烦,并不推荐。操作如下: 29 | 30 | - clone 本项目到本地 31 | - 地址栏输入 `chrome://extensions/` 打开扩展管理页面 32 | - 开启 `开发者模式` / `Developer mode` 33 | - `加载已解压的扩展程序` / `Load unpacked`,选择本项目以加载扩展 34 | 35 | 36 | ## 致谢 37 | 38 | 感谢以下朋友的 PR: 39 | [Teemwu](https://github.com/Teemwu) 40 | 41 | 欢迎大家提 PR 和反馈 issue 42 | -------------------------------------------------------------------------------- /src/css/google.css: -------------------------------------------------------------------------------- 1 | /* 页面头部 */ 2 | header.gb_sa { 3 | display: none !important; 4 | } 5 | 6 | /* 页面内容高度调整(原来是页面高度减去了页头高度) */ 7 | .frame { 8 | height: 100% !important; 9 | } 10 | 11 | /* 语言选择区 */ 12 | body:not(.trans-ext-detail) .ls-wrap { 13 | display: none; 14 | } 15 | 16 | /* 内容输入区 */ 17 | body:not(.trans-ext-detail) #input-wrap { 18 | display: none; 19 | } 20 | 21 | /* 内容输入区工具条 */ 22 | body:not(.trans-ext-detail) .source-header { 23 | display: none; 24 | } 25 | 26 | /* 包裹内容输入区和发音区的元素 */ 27 | body:not(.trans-ext-detail) .source-input { 28 | padding: 0 !important; 29 | } 30 | 31 | /* 内容输入区下语音工具栏定位 */ 32 | body:not(.trans-ext-detail) .source-or-target-footer { 33 | position: relative !important; 34 | bottom: auto !important; 35 | left: auto !important; 36 | } 37 | 38 | /* 音标 */ 39 | body:not(.trans-ext-detail) .transliteration-container { 40 | margin-top: 0 !important; 41 | } 42 | 43 | /* 语音输入按钮 */ 44 | body:not(.trans-ext-detail) .source-or-target-footer-button.speech-wrap { 45 | display: none !important; 46 | } 47 | 48 | /* 输入内容框右下方手写输入按钮 */ 49 | .source-input-tools { 50 | display: none !important; 51 | } 52 | 53 | /* 翻译结果右下角更多按钮(包含反馈和分享) */ 54 | .more-wrapper { 55 | display: none; 56 | } 57 | 58 | /* 底部反馈按钮 */ 59 | .feedback-link { 60 | display: none; 61 | } 62 | 63 | /* 底部历史记录按钮 */ 64 | .gp-footer { 65 | display: none; 66 | } 67 | 68 | /* 历史记录即将停用的通知 */ 69 | .notification-area { 70 | display: none; 71 | } 72 | 73 | /* 顶部菜单 */ 74 | #gb > div.gb_1d.gb_je.gb_ae.gb_9d { 75 | display: none !important; 76 | } 77 | -------------------------------------------------------------------------------- /src/lib/hot-reload.js: -------------------------------------------------------------------------------- 1 | // Chrome Extension Hot Reloader 2 | // https://github.com/xpl/crx-hotreload 3 | 4 | const filesInDirectory = dir => 5 | new Promise(resolve => 6 | dir.createReader().readEntries(entries => 7 | Promise.all( 8 | entries 9 | .filter(e => e.name[0] !== '.') 10 | .map(e => 11 | e.isDirectory 12 | ? filesInDirectory(e) 13 | : new Promise(resolve => e.file(resolve)) 14 | ) 15 | ) 16 | .then(files => [].concat(...files)) 17 | .then(resolve) 18 | ) 19 | ); 20 | 21 | const timestampForFilesInDirectory = dir => 22 | filesInDirectory(dir).then(files => 23 | files.map(f => f.name + f.lastModifiedDate).join() 24 | ); 25 | 26 | const reload = () => { 27 | chrome.tabs.query({ active: true, currentWindow: true }, tabs => { 28 | // NB: see https://github.com/xpl/crx-hotreload/issues/5 29 | 30 | if (tabs[0]) { 31 | chrome.tabs.reload(tabs[0].id); 32 | } 33 | 34 | chrome.runtime.reload(); 35 | }); 36 | }; 37 | 38 | const watchChanges = (dir, lastTimestamp) => { 39 | timestampForFilesInDirectory(dir).then(timestamp => { 40 | if (!lastTimestamp || lastTimestamp === timestamp) { 41 | setTimeout(() => watchChanges(dir, timestamp), 1000); // retry after 1s 42 | } else { 43 | reload(); 44 | } 45 | }); 46 | }; 47 | 48 | chrome.management.getSelf(self => { 49 | if (self.installType === 'development') { 50 | chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir)); 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/seed.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var b = function() { 3 | var l = new Image(); 4 | l.src = 5 | '//fanyi.youdao.com/web2/rl.do?keyfrom=chromeext&action=w_try&' + 6 | new Date().getTime(); 7 | }; 8 | var c = 'fanyi.youdao.com', 9 | i = '/web2/'; 10 | if (window.location.host === c && window.location.pathname === i) { 11 | b(); 12 | alert('请在浏览英文网页时使用有道网页翻译2.0'); 13 | return; 14 | } 15 | if ( 16 | !( 17 | window.location.protocol == 'http:' || 18 | window.location.protocol == 'https:' 19 | ) 20 | ) { 21 | alert('请在浏览网页时使用有道网页翻译2.0'); 22 | return; 23 | } 24 | var h = '//fanyi.youdao.com/web2'; 25 | if (!window.OUTFOX_JavascriptTranslatoR) { 26 | var e = document.createElement('script'); 27 | e.setAttribute( 28 | 'src', 29 | // h + '/scripts/all-packed-utf-8.js?242748M&' + Date.parse(new Date()) 30 | // 需要使用本地文件,本地的文件添加了https的支持 31 | chrome.extension.getURL( 32 | 'src/lib/youdao-web-translate/web2/scripts/all-packed-utf-8.js' 33 | ) 34 | ); 35 | e.setAttribute('type', 'text/javascript'); 36 | e.setAttribute('charset', 'utf-8'); 37 | document.body.appendChild(e); 38 | } else { 39 | var j = '//fanyi.youdao.com'; 40 | var a = '/web2/conn.html'; 41 | var k = h + '/index.do'; 42 | var g = j + '/jtr'; 43 | var d = h + '/rl.do'; 44 | var f = h + '/styles/all-packed.css'; 45 | J.loadCSS(document, f); 46 | window.OUTFOX_JavascriptTranslatoR = new J.TR.UI(document.body, { 47 | domain: j, 48 | update: false, 49 | updateTipMsg: '增加关闭按钮', 50 | updateDate: '2011-3-15', 51 | cssURL: f, 52 | tipsURL: k, 53 | transURL: g, 54 | logURL: d, 55 | connFilePath: a, 56 | reqSize: 20 57 | }); 58 | } 59 | })(); 60 | -------------------------------------------------------------------------------- /src/js/fake-navigator.js: -------------------------------------------------------------------------------- 1 | // 欺骗js获取的 navigator.userAgent(有道中有个判断,不欺骗会跳转网页) 2 | // https://stackoverflow.com/questions/23202136/changing-navigator-useragent-using-chrome-extension 3 | 4 | const actualCode = `(${ 5 | () => { 6 | const navigator = window.navigator; 7 | let modifiedNavigator; 8 | if ('userAgent' in Navigator.prototype) { 9 | // Chrome 43+ moved all properties from navigator to the prototype, 10 | // so we have to modify the prototype instead of navigator. 11 | modifiedNavigator = Navigator.prototype; 12 | } else { 13 | // Chrome 42- defined the property on navigator. 14 | modifiedNavigator = Object.create(navigator); 15 | Object.defineProperty(window, 'navigator', { 16 | value: modifiedNavigator, 17 | configurable: false, 18 | enumerable: false, 19 | writable: false 20 | }); 21 | } 22 | // Pretend to be Windows XP 23 | Object.defineProperties(modifiedNavigator, { 24 | userAgent: { 25 | value: 26 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 27 | configurable: false, 28 | enumerable: true, 29 | writable: false 30 | }, 31 | appVersion: { 32 | value: 33 | '5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 34 | configurable: false, 35 | enumerable: true, 36 | writable: false 37 | }, 38 | platform: { 39 | value: 'Win32', 40 | configurable: false, 41 | enumerable: true, 42 | writable: false 43 | } 44 | }); 45 | } 46 | })();`; 47 | 48 | const s = document.createElement('script'); 49 | s.textContent = actualCode; 50 | document.documentElement.appendChild(s); 51 | s.remove(); 52 | -------------------------------------------------------------------------------- /src/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 选项页 - co-trans-ext 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 选项 16 |
17 | 18 |
19 |
20 |
21 | 版本 22 |
23 |
当前版本:, 24 | 最新版本:获取中... 25 | ,已经是最新版 26 | ,可更新 27 |
28 |
29 |
30 |
31 | 关于 32 |
33 |
co-trans-ext 是一个集搜狗翻译、百度翻译、谷歌翻译、有道翻译、金山词霸于一体的浏览器翻译扩展。有任何问题或建议,欢迎随时发送到我邮箱 e12e#qq.com (#替换为@)。
34 |
35 | 如果您觉得不错,赞赏 一下我吧。以下为该项目赞赏记录: 36 |
37 |
38 | 40 |
41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | popup 页 - co-trans-ext 8 | 9 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/css/kingsoft.css: -------------------------------------------------------------------------------- 1 | /* 顶部导航 */ 2 | .body-week-up .nav { 3 | display: none; 4 | } 5 | 6 | /* 输入区域 */ 7 | .index-main { 8 | top: 0 !important; 9 | } 10 | 11 | /* 修正输入框文字不垂直居中问题 */ 12 | .index-input-main { 13 | padding: 0 6px 0 0.3611111111rem !important; 14 | } 15 | 16 | .index-top .index-main { 17 | top: 0 !important; 18 | } 19 | body:not(.trans-ext-detail) .index-top .index-main { 20 | display: none !important; 21 | } 22 | 23 | /* 翻译结果有margin-top */ 24 | body.trans-ext-detail { 25 | margin-top: 0; 26 | } 27 | 28 | body.trans-ext-detail .index .result { 29 | margin-top: 59px !important; 30 | } 31 | 32 | body:not(.trans-ext-detail) .index .result { 33 | margin-top: 0 !important; 34 | } 35 | 36 | /* 底部悬浮App广告 */ 37 | .dic-follow { 38 | display: none !important; 39 | } 40 | /* 去掉广告后的空白 */ 41 | #dic { 42 | padding-bottom: 0 !important; 43 | } 44 | 45 | /* 打开app查看全部按钮 */ 46 | .dic-more { 47 | display: none !important; 48 | } 49 | 50 | /* 牛津词典,需要app中才能用 */ 51 | .dic-nav li:nth-child(2) { 52 | display: none; 53 | } 54 | .dic-nav-tab li:nth-child(2) { 55 | display: none; 56 | } 57 | 58 | 59 | /* 柯林斯 词典 */ 60 | /* app广告 */ 61 | .download-text { 62 | display: none !important; 63 | } 64 | /* 去掉广告后的空白 */ 65 | .dic-collins { 66 | padding-bottom: .6944444444rem !important; 67 | } 68 | 69 | 70 | /* 当没有关键词时 */ 71 | /* 页头padding */ 72 | .index-bg { 73 | padding-top: 0 !important; 74 | } 75 | 76 | /* 底部广告 */ 77 | .index-bg { 78 | display: none; 79 | } 80 | 81 | /* 网站备案号 */ 82 | .record { 83 | display: none; 84 | } 85 | 86 | /* 偶尔会弹出全屏的App广告 */ 87 | .download-swiper { 88 | display: none !important; 89 | } 90 | 91 | /* 输入时智能提示 */ 92 | .index-cover-list-result { 93 | margin-top: 58px !important; 94 | } 95 | 96 | /* 跟读按钮(APP广告) */ 97 | .dic-basic-voice img { 98 | display: none; 99 | } 100 | 101 | /* 音标下的 APP 广告 */ 102 | .dic-recommend { 103 | display: none; 104 | } 105 | 106 | /* 页脚 app 广告 */ 107 | .dic-info { 108 | display: none; 109 | } 110 | 111 | .dic-bottom-list { 112 | display: none !important; 113 | } 114 | 115 | .dic-bottom-btn { 116 | display: none; 117 | } 118 | 119 | /* 高考讲解tab底部广告 */ 120 | .dic-echart-mask { 121 | display: none; 122 | } 123 | -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/JTRAssist.swf: -------------------------------------------------------------------------------- 1 | CWS ix�W�S����� �[`l i$X�`/e�B�){�[���)j�z,Œ�̌���c*���=�c�{�I��޷R�������������Ww�~���^#�3Bѿ"��(C�!���}�do�� �׵j�ۄ�g e�?��dNOOӧ�i�}��nlld�\&�[���[�W�ލ���@�{E�r�W�zJ�������B�j��3z�p���R1ë��뾗ɦ�`�Tܴ�f�[��q�R�������_�Z'|ŮZ^�~�BP���ʷ�K�O��uj-�}�H�E�p��[}״�v���2ǮSj�L6� 2 | ��U����Q�╹�ը��;��@�*d�.�|�D�&�U���a��[;�ڽupF��[��zju9�3���)�~F8��� 3 | ����c�?[y����峽8�V�?f�J;'aO���]9Xu r�����N������������NqKz�TJ�=��;�}�֭*� �ל~i�x��b�U���exGM�N�J�:�r��X%�"� r��Æ�;�/��< ��ʁ��%.N���B�/4c=/>���U:�Ep�l�JO��r��)�5zx��9�������̫�����z6���ڗ(U��u���+ώ~Nj�`��<�,�I2 � ���GXE�r����!��+w'��)•�;Qn� ��Ր�uY��=���~�m�I�ƈʈ�p��0�F��Ę<��0SFe�8cL�bd���L���������%�ށX���X�8I��s7��� 4 | ��PB���6�D^`���������(�j�����@Ӏ���&��R��bk�3'0��Ԧ�D�V�Y��b,ӄ���f�ڴ�"-�F��D�zF'�F�M�0~:� 4њ4��a�2#v���s����xLJ����n�fk�ma-�Rfl7��Z�?�9��<]����aK"��6�l���� s�1L�� s�5̤�4̄���BS��< 5 | �n�^� ��E�"㣠��H���]r��&�G��"��L��F*�5q]�]��&Lv��p�d�5�cU��&2ÔI�-����.�"���b-ch���m��v��<`�OL���Z̤��P���`�˯ K���}޸*��ݏz�+z�:������>�i�^tJ����%˷H �����=$�4R��F�:����`�����O��J��e�� ? #��e~}u��5\t ��a�g���_�S�������Ovv��A�블�Xո_v��T���v���qσ�E��)�[����*}�l����s���/��^^�N�Pv�����X����J�����ܡ�5#w�Ƒ�F6���Ҩ��CrR�Ա$�Dń2U ��:�$Y@<�1��ad@@�cJ�)����qF&�dp�0�b�.�7��)`����H�d��ղtE����I]��'��<����"u�p ԃe���a��ᙤ�ST� 6 | ��6""a��gD��a ��T*�r�R{�ښU����/ ��IlbRS�e[)P\`�s�)�C� �7�/���q��T� 7 | @M?����5��ۚ��5����s<��s��5��Do�r\��|3�g�7ɱ� A �ɭ1X�s7����8,Q���V�5!(0$KiM�/�^U 8 | �P-����l͎=����ǽ�5�#��-�%�N ���7�<å*���]��V�tJUZ���_���ߡ�*#��(����"�(��-/��ro�a�{� a�W�u�S����3��n�~Ċ�-���M����i�Q��JL���;�88˭}J�rw��A�nܽmd���5��&ٻ�0 �$��6��!�izB\�VNV���0� 9 | t�\R�IL�2)ƴ!���4°��( A�;��HL�%�UiQ�-1�D��(�7TƊ2�"ZG9 5���³s0n���q��%�6yP��4��&� &E���6��H��ɬ��~��& E#�v#Nξk��(L �$��$IT�A|�->�J�%m��0E��n�d��c�w����u�'m���<ܴ�B�;,a�p�4�%� �YЖ���xйF��=�<� response.text()) 9 | .then(str => new window.DOMParser().parseFromString(str, 'text/xml')) 10 | .then((doc) => { 11 | const appId = chrome.runtime.id; 12 | const $updateCheck = $(doc) 13 | .find(`[appid=${appId}]`) 14 | .find('updatecheck'); 15 | const latestVersion = $updateCheck.attr('version'); 16 | if (!latestVersion) { 17 | throw new Error('获取最新版本失败'); 18 | } 19 | $('.latest-version').text(latestVersion); 20 | if (latestVersion !== currentVersion) { 21 | // 有更新 22 | $('.not-latest').show(); 23 | $('.not-latest a').attr('href', $updateCheck.attr('codebase')); 24 | } else { 25 | $('.already-latest').show(); 26 | } 27 | }) 28 | .catch(() => { 29 | $('.latest-version').text('获取失败'); 30 | }); 31 | // 显示当前是否启用分析 32 | data.get('isEnabledAnalytics', (val) => { 33 | $('#isEnabledAnalytics').attr('checked', val); 34 | }); 35 | }()); 36 | 37 | // 监听是否启用分析checkbox的事件 38 | $('#isEnabledAnalytics').change((event) => { 39 | const { checked } = event.target; 40 | data.set('isEnabledAnalytics', checked); 41 | // 通知停用或启用 42 | const bg = chrome.extension.getBackgroundPage(); 43 | if (checked) { 44 | bg.setIsDisableGA(!checked); 45 | ga('send', 'event', 'ga-options', 'enable'); 46 | } else { 47 | ga('send', 'event', 'ga-options', 'disable'); 48 | bg.setIsDisableGA(!checked); 49 | } 50 | }); 51 | 52 | // 接收iframe内部通过postMessage传递过来的数据 53 | // 获取 iframe 里面内容高度,动态设置iframe高度,尽量避免不必要的滚动条 54 | window.addEventListener('message', (event) => { 55 | if (event.data.iframeHeight) { 56 | if (event.data.iframeHeight < 400) { 57 | $('.reward__list').height(event.data.iframeHeight); 58 | } else { 59 | $('.reward__list').height(400); 60 | } 61 | } 62 | }); 63 | 64 | // 谷歌分析 65 | $('.reward__button').click(() => { 66 | ga('send', 'event', 'reward', 'click-button'); 67 | }); 68 | 69 | // 谷歌分析 70 | $('.not-latest a').click(() => { 71 | ga('send', 'event', 'update', 'click-button'); 72 | }); 73 | -------------------------------------------------------------------------------- /src/css/sogou.css: -------------------------------------------------------------------------------- 1 | .J-logo { 2 | display: none; 3 | } 4 | 5 | /* 头部广告 */ 6 | .header-wap-new { 7 | display: none !important; 8 | } 9 | 10 | /* 语言选择栏 */ 11 | body:not(.trans-ext-detail) #J-langselect { 12 | display: none; 13 | } 14 | 15 | /* 输入框 */ 16 | body:not(.trans-ext-detail) #trans-input { 17 | display: none; 18 | } 19 | 20 | /* 由于默认为隐藏,js计算到高度为0,添加个最少高度恢复样式 */ 21 | body.trans-ext-detail #textarea-source { 22 | min-height: 26px; 23 | } 24 | 25 | /* 去掉输入框滚动条 */ 26 | #trans-input { 27 | height: 26px !important; 28 | } 29 | 30 | /* 清空按钮 */ 31 | body:not(.trans-ext-detail) .trans-box .trans-from .btn-clear { 32 | display: none !important; 33 | } 34 | 35 | body:not(.trans-ext-detail) .box-from-text .box-voice { 36 | margin: 0 !important; 37 | } 38 | 39 | body:not(.trans-ext-detail) .box-voice { 40 | padding-top: 0 !important; 41 | } 42 | 43 | /* 音标上边距 */ 44 | body:not(.trans-ext-detail) .trans-box .operate-box { 45 | padding-top: 0; 46 | } 47 | 48 | body:not(.trans-ext-detail) .trans-box .trans-from { 49 | padding-top: 0; 50 | } 51 | 52 | body:not(.trans-ext-detail) .box-from-text { 53 | padding: 5px 15px !important; 54 | } 55 | 56 | body:not(.trans-ext-detail) .box-to-text pre { 57 | margin: 0 !important; 58 | } 59 | 60 | /* 翻译结果右下角搜狗搜索按钮 */ 61 | .trans-box .toolbox .btn-search { 62 | display: none !important; 63 | } 64 | 65 | .ap-box { 66 | display: none !important; 67 | } 68 | 69 | /* 四六级真题、智能拍译 */ 70 | .app-box2 { 71 | display: none !important; 72 | } 73 | 74 | /* 页面脚部搜索app广告 */ 75 | .J-promotion-cz-bottom { 76 | display: none !important; 77 | } 78 | 79 | body:not(.trans-ext-detail) .translation-from-text { 80 | display: none; 81 | } 82 | 83 | .report-pop { 84 | display: none !important; 85 | } 86 | 87 | .trans-group { 88 | display: none !important; 89 | } 90 | 91 | .report-entry { 92 | display: none !important; 93 | } 94 | 95 | .trans-wap-banner { 96 | display: none !important; 97 | } 98 | 99 | .responsive .content-wrap { 100 | padding-top: 0 !important; 101 | } 102 | 103 | .app-promotion-middle { 104 | display: none !important; 105 | } 106 | 107 | .trans-box .toolbox .btn-feedback { 108 | display: none !important; 109 | } 110 | 111 | /* 顶部广告空位 */ 112 | .view .content-wrap { 113 | padding-top: 0 !important; 114 | } 115 | 116 | /* 翻译结果下的广告 */ 117 | .swiper-container { 118 | display: none !important; 119 | } 120 | 121 | /* 反馈按钮左侧竖线 */ 122 | .operate-box .operate-btn-box>div:first-child:after { 123 | display: none !important; 124 | } 125 | 126 | /* 反馈按钮 */ 127 | .btn-feedback { 128 | display: none !important; 129 | } 130 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "co-trans-ext 翻译工具", 3 | "version": "1.4", 4 | "manifest_version": 2, 5 | "description": "集搜狗翻译、百度翻译、有道翻译、金山词霸、谷歌翻译于一体的翻译工具", 6 | "icons": { 7 | "16": "src/img/icon-16.png", 8 | "48": "src/img/icon-48.png", 9 | "128": "src/img/icon-128.png" 10 | }, 11 | "browser_action": { 12 | "default_icon": "src/img/icon-48.png", 13 | "default_title": "翻译工具", 14 | "default_popup": "src/popup.html" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "matches": [""], 19 | "exclude_globs": ["*x-from=co-translate-extension*"], 20 | "all_frames": true, 21 | "js": [ 22 | "src/lib/jquery-3.4.0.slim.min.js", 23 | "src/js/utils.js", 24 | "src/js/ext-data.js", 25 | "src/js/content-script.js" 26 | ], 27 | "css": ["src/css/extension.css"] 28 | }, 29 | { 30 | "all_frames": true, 31 | "matches": ["*://*/*x-from=co-translate-extension*"], 32 | "js": ["src/lib/jquery-3.4.0.slim.min.js", "src/lib/loadCSS.js", "src/js/trans-css.js"] 33 | }, 34 | { 35 | "all_frames": true, 36 | "matches": ["*://fanyi.sogou.com/*x-from=co-translate-extension*"], 37 | "css": ["src/css/base.css", "src/css/sogou.css"], 38 | "js": ["src/js/utils.js", "src/js/iframe-messager.js"] 39 | }, 40 | { 41 | "all_frames": true, 42 | "matches": ["*://fanyi.baidu.com/*x-from=co-translate-extension*"], 43 | "css": ["src/css/base.css", "src/css/baidu.css"], 44 | "js": [ 45 | "src/js/utils.js", 46 | "src/lib/jquery-3.4.0.slim.min.js", 47 | "src/js/iframe-messager.js" 48 | ] 49 | }, 50 | { 51 | "all_frames": true, 52 | "matches": ["*://translate.google.cn/*x-from=co-translate-extension*"], 53 | "css": ["src/css/base.css", "src/css/google.css"], 54 | "js": ["src/js/utils.js", "src/js/iframe-messager.js"] 55 | }, 56 | { 57 | "run_at": "document_start", 58 | "all_frames": true, 59 | "matches": ["*://*/*x-from=co-translate-extension*"], 60 | "js": ["src/js/fake-navigator.js"] 61 | }, 62 | { 63 | "all_frames": true, 64 | "matches": ["*://m.youdao.com/*x-from=co-translate-extension*"], 65 | "css": ["src/css/base.css", "src/css/youdao.css"], 66 | "js": ["src/js/utils.js", "src/js/iframe-messager.js"] 67 | }, 68 | { 69 | "all_frames": true, 70 | "matches": ["*://m.iciba.com/*x-from=co-translate-extension*"], 71 | "css": ["src/css/base.css", "src/css/kingsoft.css"], 72 | "js": ["src/js/utils.js", "src/js/iframe-messager.js"] 73 | } 74 | ], 75 | "homepage_url": "https://e12e.com", 76 | "background": { 77 | "page": "src/background.html" 78 | }, 79 | "options_page": "src/options.html", 80 | "permissions": ["webRequest", "webRequestBlocking", "", "storage"], 81 | "update_url": "https://update.e12e.com/co-trans-ext/", 82 | "web_accessible_resources": ["src/lib/youdao-web-translate/*", "src/css/iconfont/*", "src/img/*"], 83 | "content_security_policy": "script-src 'self' blob: filesystem: chrome-extension-resource: https://www.google-analytics.com; object-src 'self'" 84 | } 85 | -------------------------------------------------------------------------------- /src/js/popup.js: -------------------------------------------------------------------------------- 1 | const data = new ExtData(); 2 | const transExt = $('.trans-ext__popup'); 3 | const transIframe = $('.trans-ext__iframe'); 4 | 5 | // 加载图标 6 | loadCSS(chrome.extension.getURL('src/css/iconfont/iconfont.css'), 'iconfont'); 7 | 8 | // iframe 加载超时处理 9 | let iframeTimer; 10 | const IFRAME_TIMEOUT = 6000; 11 | function iframeTimeOut() { 12 | // eslint-disable-next-line no-console 13 | console.log('iframe加载超时了'); 14 | window.stop(); 15 | } 16 | function startLoadIframe() { 17 | // 超时处理 18 | iframeTimer = setTimeout(iframeTimeOut, IFRAME_TIMEOUT); 19 | transIframe[0].onload = () => { 20 | clearTimeout(iframeTimer); 21 | }; 22 | } 23 | 24 | data.get('transTool', (val) => { 25 | data.get(val, (val2) => { 26 | startLoadIframe(); 27 | loadIframeSrc(transIframe, val2.replace('KEYWORD', '').replace('SHOWDETAIL', true)); 28 | }); 29 | }); 30 | 31 | // 获取并高亮当前使用的搜索工具 32 | data.get('transTool', (val) => { 33 | const activeTransTool = transExt.find( 34 | `.trans-ext__tool[data-trans-tool=${val}]` 35 | ); 36 | 37 | activeTransTool.addClass('active'); 38 | }); 39 | 40 | // 切换翻译工具 41 | let changeTransToolTimer; 42 | function changeTransTool(transTool, keyword) { 43 | clearTimeout(changeTransToolTimer); 44 | clearTimeout(iframeTimer); 45 | // 切换后先清空页面 46 | transIframe.attr('src', ''); 47 | transExt.find('.trans-ext__tool').removeClass('active'); 48 | transExt 49 | .find(`.trans-ext__tool[data-trans-tool=${transTool}]`) 50 | .addClass('active'); 51 | data.set('transTool', transTool); 52 | data.get('transTool', (val) => { 53 | data.get(val, (val2) => { 54 | startLoadIframe(); 55 | loadIframeSrc(transIframe, val2.replace('KEYWORD', keyword).replace('SHOWDETAIL', true)); 56 | }); 57 | }); 58 | } 59 | 60 | // 添加翻译工具图标点击事件 61 | transExt.find('.trans-ext__tool').click((event) => { 62 | const transTool = $(event.target).data('trans-tool'); 63 | // 避免 iframe 内页面出现异常、跳转验证页面等导致 iframe-message.js 无法加载进而导致无法切换其它翻译的问题 64 | changeTransToolTimer = setTimeout(() => { 65 | changeTransTool(transTool, ''); 66 | }, 200); 67 | transIframe.eq(0)[0].contentWindow.postMessage({ changeTransTool: transTool }, '*'); 68 | ga('send', 'event', 'trans-tool', 'change', transTool); 69 | }); 70 | 71 | // 获取 iframe 内输入值后切换 翻译 72 | window.addEventListener('message', (event) => { 73 | const transTool = event.data.changeTransTool; 74 | if (transTool) { 75 | const keyword = event.data.keyword; 76 | changeTransTool(transTool, keyword); 77 | } 78 | }); 79 | 80 | // 网页翻译 81 | transExt.find('.trans-ext__trans-page-tool').click((event) => { 82 | const transTool = $(event.target).data('trans-tool'); 83 | ga('send', 'event', 'page-trans-tool', 'translate', transTool); 84 | chrome.tabs.getSelected(null, (tab) => { 85 | if (transTool === 'yandex') { 86 | window.open( 87 | `https://translate.yandex.com/translate?url=${tab.url}&lang=en-zh` 88 | ); 89 | } else if (transTool === 'youdao') { 90 | // 有道网页翻译 91 | chrome.tabs.executeScript(tab.id, { 92 | file: 'src/lib/youdao-web-translate/web2/seed.js' 93 | }); 94 | chrome.tabs.insertCSS(tab.id, { 95 | file: 'src/lib/youdao-web-translate/web2/styles/cover.css' 96 | }); 97 | } 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/lib/loadCSS.js: -------------------------------------------------------------------------------- 1 | /*! loadCSS. [c]2020 Filament Group, Inc. MIT License */ 2 | (function(w){ 3 | "use strict"; 4 | /* exported loadCSS */ 5 | var loadCSS = function( href, before, media, attributes ){ 6 | // Arguments explained: 7 | // `href` [REQUIRED] is the URL for your CSS file. 8 | // `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet before 9 | // By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document. 10 | // `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all' 11 | // `attributes` [OPTIONAL] is the Object of attribute name/attribute value pairs to set on the stylesheet's DOM Element. 12 | var doc = w.document; 13 | var ss = doc.createElement( "link" ); 14 | var ref; 15 | if( before ){ 16 | ref = before; 17 | } 18 | else { 19 | var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes; 20 | ref = refs[ refs.length - 1]; 21 | } 22 | 23 | var sheets = doc.styleSheets; 24 | // Set any of the provided attributes to the stylesheet DOM Element. 25 | if( attributes ){ 26 | for( var attributeName in attributes ){ 27 | if( attributes.hasOwnProperty( attributeName ) ){ 28 | ss.setAttribute( attributeName, attributes[attributeName] ); 29 | } 30 | } 31 | } 32 | ss.rel = "stylesheet"; 33 | ss.href = href; 34 | // temporarily set media to something inapplicable to ensure it'll fetch without blocking render 35 | ss.media = "only x"; 36 | 37 | // wait until body is defined before injecting link. This ensures a non-blocking load in IE11. 38 | function ready( cb ){ 39 | if( doc.body ){ 40 | return cb(); 41 | } 42 | setTimeout(function(){ 43 | ready( cb ); 44 | }); 45 | } 46 | // Inject link 47 | // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs 48 | // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/ 49 | ready( function(){ 50 | ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) ); 51 | }); 52 | // A method (exposed on return object for external use) that mimics onload by polling document.styleSheets until it includes the new sheet. 53 | var onloadcssdefined = function( cb ){ 54 | var resolvedHref = ss.href; 55 | var i = sheets.length; 56 | while( i-- ){ 57 | if( sheets[ i ].href === resolvedHref ){ 58 | return cb(); 59 | } 60 | } 61 | setTimeout(function() { 62 | onloadcssdefined( cb ); 63 | }); 64 | }; 65 | 66 | function loadCB(){ 67 | if( ss.addEventListener ){ 68 | ss.removeEventListener( "load", loadCB ); 69 | } 70 | ss.media = media || "all"; 71 | } 72 | 73 | // once loaded, set link's media back to `all` so that the stylesheet applies once it loads 74 | if( ss.addEventListener ){ 75 | ss.addEventListener( "load", loadCB); 76 | } 77 | ss.onloadcssdefined = onloadcssdefined; 78 | onloadcssdefined( loadCB ); 79 | return ss; 80 | }; 81 | // commonjs 82 | if( typeof exports !== "undefined" ){ 83 | exports.loadCSS = loadCSS; 84 | } 85 | else { 86 | w.loadCSS = loadCSS; 87 | } 88 | }( typeof global !== "undefined" ? global : this )); 89 | -------------------------------------------------------------------------------- /src/css/extension.css: -------------------------------------------------------------------------------- 1 | co-div { 2 | display: block; 3 | } 4 | 5 | .trans-ext__trans-btn { 6 | user-select:none; 7 | cursor: pointer; 8 | position: absolute; 9 | z-index: 99999; 10 | width: 18px; 11 | height: 18px; 12 | text-align: center; 13 | line-height: 18px; 14 | background-color: #ffffff; 15 | box-shadow: 0 0 4px 2px rgba(3, 169, 244, 0.9); 16 | font-size: 14px; 17 | box-sizing: border-box; 18 | background-image: url('chrome-extension://__MSG_@@extension_id__/src/img/icon-48.png'); 19 | background-position: center; 20 | background-size: 18px 18px; 21 | background-repeat: no-repeat; 22 | border-radius: 0; 23 | } 24 | 25 | .trans-ext__trans-btn:hover { 26 | transform: translate(-3.7px, -3.7px); 27 | width: 25.4px; 28 | height: 25.4px; 29 | border-radius: 50%; 30 | transition: transform 0.5s, width 0.5s, height 0.5s, border-radius 0.5s; 31 | } 32 | 33 | .trans-ext__popup { 34 | width: 350px; 35 | height: 440px; 36 | box-shadow: 0 0 2px 0px #808080; 37 | background-color: rgba(255, 255, 255, 1); 38 | position: absolute; 39 | z-index: 99999; 40 | /* 去除行内元素的多余间隙 */ 41 | /* font-size: 0; */ 42 | } 43 | 44 | .trans-ext__popup-arrow{ 45 | width: 0; 46 | height: 0; 47 | display: none; 48 | position: absolute; 49 | top: 0; 50 | left: 0; 51 | z-index: 1000000; 52 | } 53 | .trans-ext__popup-arrow>co-div:first-child{ 54 | left: -9px; 55 | border-color: rgb(187, 187, 187) transparent; 56 | border-width: 9px; 57 | border-style: solid; 58 | border-image: initial; 59 | display: block; 60 | height: 0px; 61 | position: absolute; 62 | width: 0px; 63 | margin: 0; 64 | padding: 0; 65 | border-top-width: 0px; 66 | } 67 | .trans-ext__popup-arrow>co-div:last-child{ 68 | top: 1px; 69 | left: -8px; 70 | border-color: rgb(255, 255, 255) transparent; 71 | border-width: 8px; 72 | border-style: solid; 73 | border-image: initial; 74 | display: block; 75 | height: 0px; 76 | position: absolute; 77 | width: 0px; 78 | margin: 0; 79 | padding: 0; 80 | border-top-width: 0px; 81 | } 82 | 83 | .trans-ext__title-bar { 84 | height: 40px; 85 | background-color: rgba(3, 169, 244, 0.9); 86 | position: relative; 87 | cursor: move; 88 | } 89 | 90 | .trans-ext__title-bar .co-iconfont { 91 | cursor: pointer; 92 | color: #ffffff; 93 | font-size: 26px; 94 | line-height: 40px; 95 | } 96 | 97 | .trans-ext__title-bar .co-iconfont { 98 | margin-right: 10px; 99 | } 100 | 101 | .trans-ext__title-bar .co-iconfont.active, 102 | .trans-ext__title-bar .co-iconfont:hover { 103 | color: #FFEB3B; 104 | } 105 | 106 | .trans-ext__title-bar .co-iconfont:last-child { 107 | margin-right: 0; 108 | } 109 | 110 | .trans-ext__title-center { 111 | position: absolute; 112 | margin-left: 10px; 113 | font-size: 0; 114 | text-align: left; 115 | } 116 | 117 | .trans-ext__title-right { 118 | position: absolute; 119 | top: 0; 120 | right: 10px; 121 | } 122 | 123 | /* 全部important,避免外部样式影响 */ 124 | .trans-ext__iframe { 125 | display: block !important; 126 | width: 100% !important; 127 | height: calc(100% - 40px) !important; 128 | border: none !important; 129 | } 130 | 131 | 132 | /* 动画 */ 133 | html.co-transition { 134 | transition: all .2s; 135 | } 136 | 137 | html.co-transition .trans-ext__popup { 138 | transition: all .2s; 139 | } 140 | 141 | html.co-fixed-r { 142 | margin-right: 350px; 143 | } 144 | 145 | html.co-fixed-l { 146 | margin-left: 350px; 147 | } 148 | 149 | .co-fixed-r .trans-ext__popup, 150 | .co-fixed-l .trans-ext__popup { 151 | position: fixed; 152 | top: 0 !important; 153 | height: 100%; 154 | } 155 | 156 | .co-fixed-r .trans-ext__title-bar, 157 | .co-fixed-l .trans-ext__title-bar { 158 | cursor: auto; 159 | } 160 | 161 | .co-fixed-r .trans-ext__popup{ 162 | left: calc(100% - 350px) !important; 163 | } 164 | 165 | .co-fixed-l .trans-ext__popup{ 166 | left: 0 !important; 167 | } 168 | -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "co-iconfont"; 2 | src: url('iconfont.eot?t=1557940921579'); /* IE9 */ 3 | src: url('iconfont.eot?t=1557940921579#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAs0AAsAAAAAE9QAAArlAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEUgqYGJJkATYCJAM0CxwABCAFhREHgS8bOxCjoqTy5cj+8sAmQ13B/oroYNjGsrQt54UbNHyhPZMJKAL0L+F+L9ceIyGPbAhTBNXefj27+0L8MaoI6jwqSxRCUSiEwZ1FgZCURyE8LmRhsxF3OJf5McvMP7EakUKfppc0pWtoQMKh01N+M2rzz5/69yl6ifde26SImwoOykHKVEzt5ng7KX6KOR3IYqC+EyDAfGiOtdUb8W6HqCY8NLGEJxIRr1s9wWRz9N37WWkPL+14EOkuyCGyhLa90m8MzLefq0PEmmmjl/99J5+3mx/uN0Qk7RFJ3vV1EW+sQaOSIeTAQiN3zMzlrXwFlkNsc0YF/qV9mEDvVFnp3tHFNZlSMAx0ea4kI1Mzk+KICG1+bbiwiNcwbXqZPuBV+/3x36IpkirjEx8/OwzJzi/wuyEvV0vVtY60H8XlbmTcRpRL7Z3R8BZQ3EeN6hGaQnctqefXv/yivbnXylXnXf3e3w2rFeSn8Kwfj0vDrvbbYohG7y3+j6dT1AYhyUaiRmjEjDcz8y9QEs09poHogfkgKmAvQC6sANEBK0EUYBWIGjgPxABcDSKA9yKXxO96KvtZSZBfmNd0wSHiBsnvKd+x9rRF7c+6MOt+YCBpse5DAyOHmcNjs5kMhO0tvFhsPl2MC3j4ACqT5qkq48Mu57P0Jxnkn77bgUhYRykkUuVIqP+xgXN6BEvdLdLVQHH8teTU+G0xuNloCXe8gv9UEJ6JiBCZT/EpRzRE7xVeE3UOkTmofScz3+ytf6NJE8f5bTH74HpHGXZHND1OgOkiaWIllxYQFQxoPC0PN6W77O1sP6TpD6NnLz7kmLP6eo1xUVD76p9KLtqQD8RKTXsGdHwplrolD7cMXTCJytkJI7BWIpcbQKNB3iVO0x4UTjkc5Kr06gxRFbHHuJhvK0x3T6L6iNmmfQNGjtQGoifgllzSAKf2WHu9V6+CE5+TxQ2dfHqT+pU5BB7meYPKbKgNNGmO3y2umxxKLDRFQy4bHprdqn6Edp3+S0SL0hALOn6NHYjh6Frc1y9r05G7pbhVDfmN7I/xlL3SvUVVsD6tVvI3fbS9NMouRtXf++JurzMZDPYeddlFxUtD4FBGBdi7HdtZ4JW8Hodw3YY+rZTHRqUx6AvA1NOj1dEadMqEzmROmGq1XqA7tG80mk5O8YZ5zzlcNWiYiHmLnyLLqdiDZI1LZRnbKlhA4ZluOQtpcY3OUQOHYSKBwQEat9v2Y2I3q5V7ASw3x07nEcsiK5phNHYV0cVOUyeeZkA4GMydDX6VBbx40Vfg7YSNwwwhwyL3fkA6E8PhtJbb6vyS6Wl9bX9U5/dA0huReoeirt4ZvNbunRWuqKeBVg108sM2R00d+9QT/ZZuqTnoSR4V6PTXyqtWT+PrUmVzxEc7GswuszrqVz2qwyrG7+PDlloZ7RX4ad3bfr9jOlHv6CqE5Zphv37iLjnAmAzKkhxubNQQ1rQOcVw68xJu6xgGUm91L2nDYjsYBxBVDEOPvAcveHc+sm90mKPjuLmPtGX1b/6nL9ImvDGiYvJDd93U+LHOv2h+BbcbfqxIDtCaZWFz7YICW6XFdbcohIYrPsmCUCaCsa33Uq3LxkhWm/sRYFFWe2O2PjE6vK/PALgA22t+kyae68HvV9SUrpFEfznfq+BbLgYyglZZ98UxYFJC4RGODhGu9eia4uMTOH1WFZF5SFF649nMieWSVHhLmdTXmcEWrBOTjhhoqCSOVS9IdckVCSrWDL70m18hUxMzkF+bdcEkhq5sL+EOhX7O3JgjyYYnaBUJP5sWDsaWyYNUU0+vRGHceeViGCu4BGSMC3c3vrold50m7QyessH5LhUqQErYOZIpfoNBSRaaK5+zIkdvPm/BTFLy9rcaL7rA/24ZQA4s4GbvAt4C6nfD6ROMarlyy1EjgUtk39bPiZpa4qRzN46RtYldpSEFGaTyYPoV/eHC0KWFrulnqvAtij1DuRWUO0+2toqfY74Tnw9lFiEltU4pGv1/lHrxWL+26GoeXTvGwY3InWUR79M8jhoizaRJZu2yMGmsdxGmTAQdwt6G7K5hZKNJffVf13C974hCsZC/ojm+q9feAVdH5JB8fUVqcq74snwcuShx6oHLZF7g9iJeZENsZESxvSW2lM0q/Vu7/Vnst5Yw6jN0k4ujNZsdBeUTNTL1N1uqnxCdnzvS7l4QmZ9m/T1xHGuIsGaYooxbA5Z17pLD6zzLj6iUDo8bMfrs0s2jSMVVXWWmpzS7/PifabYjObytj21x3lDStrS7T4Uh2lQ0FVFWefl7a4qhYprrLBDbuqltnopdu23mGLAEPrQLXY+s2re/CR6DTSvAtBXUlnFm6lnsPxq6D975F/Mm5QD86iF8yLsSdFPob6YEa0+duuQ65fDJk8teeYaFMiEhxPzxYwIiIPCtXVh+HAjBcRFC8WKeOPzuxWfmFt/xietMu6jU/ft4SexGh+fmcYXP5ZRDoRg78DNbv2zwzHuCz5DfZL6fi0qPSsdRiHMculv2rCDlZyUBZ60CQdgR5UG3Ymbg6MW5INUvWS7eN1ahdpfCz52yiuhJFYe4QzKUfuxgl53WEKU1Kk6SXXxdhg8YDkqc3Mrp+lKxak2C117JCw4yxH9SyEKlfKhsqCJE7z9N6akVa8GxgCk13mk5akpp7NPjRDfy7g2yA9n+7nU3qvnx5O0F4UtfltiT20AVeoGNX0LB1Wb0PLn7UeXK0myRfV56WEdInndR5pRi3O6b546NHSg9Lx7Gyk4TWQnayNXpwS1BufIjN3t1rK7IPF/3uEG+1ySjOIo0oSWTMq4F0Kf5hP49TQUYYCweHZe7cO50bJQbNpE41D5x8rB9nTNHgF5y6n2s7kJd5n+XMAuMpRSfbPAYxurg9RAzXugUX+LH43PwheR5GSDQtlQyUu1lL8kPLW8pTcYWPs7LzhrTT66BcLCOXXJQ/nkWEIOsEouwaxlzTQ+pcHlzi0VkqVZYLqQVb+3R3CgEcVPuWoSWK12XTuTVbFwuCh2PTgCp0wcNuqK4wruvuD9osOYbwJs6DRpk4B9lMKDPjbN5IQ/Y7DnSc7PVtrlqO3JdFzedG1gicb1mIF0v9Clc+Jvwrsjf8BIA/rcdQKrbpP3dSDZz5DwSD1COAjhhXt/yGBlMjX2bEb+UWSeQ5f8zTkSRcfwIkiz9QzgIHFOsixrEif9Bw/9ILLoDjztAFf/brin2rdRKNhAP4uFfCpxRHhQ2T+Vu6kpxh5QpFqbrE3rtxIG/dNOvjLYTLNRgmEmXaUnjPC0bXHoUn4VH5XPHo/Z5lNa75artYMubUZhKd/DUvikt7PpJS2ZfORTv+oNH8fnXVSHNetQ+22m9J9G74eCi9/6uAeYgpMEjVRxzUbS4GY4VrH8J0qfMrEbeJt6CsWoxbVTrhpZjyMHsd4q9k03nBBUGM3p0/ThIU6SFwRi4q0bOFcNaTZhOrXLMyIy7DMC4OmuICjzmVTiUE4XOzHzuCv2cS0DyUoxxT+CevQUMSwK2h2qoqod5HKM8dMyXyFp3pKaKcAQVDJoz1FEaBaluRFRhpjFQjU5VlFBfGKqpuUQoqspr2W8B+YaOy19mihQ5SlRRRxNtdNHHEGNMMatfoA8ZjgdMhX5SIsoUGlPW44Ni2Jh4ZHkID+MWJfqpROXSonATNkKv1dRTxPKEqcaE9CwP1KI/yeVeIG4rHfq1ecyTR8cKx+KREAAA') format('woff2'), 5 | url('iconfont.woff?t=1557940921579') format('woff'), 6 | url('iconfont.ttf?t=1557940921579') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1557940921579#co-iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .co-iconfont { 11 | font-family: "co-iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .co-icon-youdao:before { 19 | content: "\e65c"; 20 | } 21 | 22 | .co-icon-baidu:before { 23 | content: "\e6b6"; 24 | } 25 | 26 | .co-icon-google1:before { 27 | content: "\e783"; 28 | } 29 | 30 | .co-icon-suoxiao1:before { 31 | content: "\e626"; 32 | } 33 | 34 | .co-icon-yandex:before { 35 | content: "\e612"; 36 | } 37 | 38 | .co-icon-sogou:before { 39 | content: "\e628"; 40 | } 41 | 42 | .co-icon-kingsoft:before { 43 | content: "\e600"; 44 | } 45 | 46 | .co-icon-shouqi:before { 47 | content: "\e61f"; 48 | } 49 | 50 | .co-icon-zhankai1:before { 51 | content: "\e625"; 52 | } 53 | 54 | .co-icon-guanbi:before { 55 | content: "\e608"; 56 | } 57 | 58 | .co-icon-xiangyoujiantou:before { 59 | content: "\e64b"; 60 | } 61 | 62 | .co-icon-xiangzuojiantou-copy:before { 63 | content: "\e784"; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/js/background.js: -------------------------------------------------------------------------------- 1 | // 请求头和响应头可能有不同大小写 2 | function isHeaderNameEqual(name1, name2) { 3 | return name1.toLowerCase() === name2.toLowerCase(); 4 | } 5 | 6 | /** ********************** 拦截请求头,改user-agent以访问手机版网页 **************** */ 7 | // 该部分参考了:https://github.com/jugglinmike/chrome-user-agent/blob/master/src/background.js 8 | chrome.webRequest.onBeforeSendHeaders.addListener( 9 | (details) => { 10 | const headers = details.requestHeaders; 11 | // 百度翻译要特殊处理,除了首页,以下两个api也需要改user-agent才能获取到数据 12 | // 且只有移动端才使用该api 13 | // https://fanyi.baidu.com/basetrans 14 | // https://fanyi.baidu.com/extendtrans 15 | // 搜狗有时候会检测异常,为方便用户输入验证码,也改 user-agent 16 | if ( 17 | details.url.indexOf('x-from=co-translate-extension') !== -1 18 | || details.url.indexOf('fanyi.baidu.com/basetrans') !== -1 19 | || details.url.indexOf('fanyi.baidu.com/extendtrans') !== -1 20 | || details.url.indexOf('www.sogou.com/antispider') !== -1 21 | ) { 22 | let i = 0; 23 | for (const l = headers.length; i < l; ++i) { 24 | if (isHeaderNameEqual(headers[i].name, 'User-Agent')) { 25 | break; 26 | } 27 | } 28 | if (i < headers.length) { 29 | headers[i].value = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'; 30 | } 31 | } 32 | return { 33 | requestHeaders: headers 34 | }; 35 | }, 36 | { 37 | urls: [ 38 | '*://fanyi.sogou.com/*x-from=co-translate-extension*', 39 | // 百度翻译要特殊处理 40 | '*://fanyi.baidu.com/*', 41 | '*://*.youdao.com/*x-from=co-translate-extension*', 42 | '*://*.ydstatic.com/*x-from=co-translate-extension*', 43 | '*://*.sogou.com/antispider/*' 44 | ] 45 | }, 46 | ['requestHeaders', 'blocking'] 47 | ); 48 | 49 | /** ********************* 处理部分网站CSP限制导致插件不工作问题 ***************** */ 50 | function isCSPHeader(headerName) { 51 | return ( 52 | isHeaderNameEqual(headerName, 'content-security-policy') 53 | || isHeaderNameEqual(headerName, 'x-webkit-csp') 54 | || isHeaderNameEqual(headerName, 'x-content-security-policy') 55 | ); 56 | } 57 | // 参考了:https://gist.github.com/dchinyee/5992397 58 | // Listens when new request 59 | chrome.webRequest.onHeadersReceived.addListener( 60 | (details) => { 61 | let xFrameOptionsIndex = -1; 62 | for (let i = 0; i < details.responseHeaders.length; i++) { 63 | if (isCSPHeader(details.responseHeaders[i].name)) { 64 | let csp = details.responseHeaders[i].value; 65 | 66 | // 有可能只有default-src或者child-src 67 | csp = csp.replace( 68 | 'default-src', 69 | 'default-src fanyi.sogou.com fanyi.baidu.com translate.google.cn m.youdao.com m.iciba.com fanyi.youdao.com at.alicdn.com data: cidian.youdao.com' 70 | ); 71 | csp = csp.replace( 72 | 'child-src', 73 | 'child-src fanyi.sogou.com fanyi.baidu.com translate.google.cn m.youdao.com m.iciba.com fanyi.youdao.com at.alicdn.com data: cidian.youdao.com' 74 | ); 75 | 76 | // append "https://fanyi.sogou.com/" to the authorized sites 77 | csp = csp.replace( 78 | 'frame-src', 79 | 'frame-src fanyi.sogou.com fanyi.baidu.com translate.google.cn m.youdao.com m.iciba.com fanyi.youdao.com' 80 | ); 81 | csp = csp.replace( 82 | 'style-src', 83 | 'style-src at.alicdn.com fanyi.youdao.com' 84 | ); 85 | csp = csp.replace('font-src', 'font-src at.alicdn.com data:'); 86 | csp = csp.replace('img-src', 'img-src fanyi.youdao.com'); 87 | 88 | // 有道网页翻译语音 89 | csp = csp.replace('object-src', 'object-src cidian.youdao.com'); 90 | 91 | // eslint-disable-next-line no-param-reassign 92 | details.responseHeaders[i].value = csp; 93 | } 94 | 95 | if ( 96 | isHeaderNameEqual(details.responseHeaders[i].name, 'X-FRAME-OPTIONS') 97 | ) { 98 | xFrameOptionsIndex = i; 99 | } 100 | } 101 | if (xFrameOptionsIndex !== -1) { 102 | // 因为没有初始选项,删除这个响应头 103 | details.responseHeaders.splice(xFrameOptionsIndex, 1); 104 | } 105 | 106 | return { 107 | // Return the new HTTP header 108 | responseHeaders: details.responseHeaders 109 | }; 110 | }, 111 | { 112 | urls: [''] 113 | }, 114 | ['blocking', 'responseHeaders'] 115 | ); 116 | 117 | /** ************* 保证从插件内跳转的网页都带 x-from=co-translate-extension 参数 ********** */ 118 | 119 | // 判断refer是否包含 co-translate-extension,保持 co-translate-extension 的存在 120 | // 参考了 https://stackoverflow.com/questions/26720766/redirecting-those-urls-which-are-not-having-any-referrer-urls 121 | const requestsToRedirect = {}; 122 | 123 | // 兼容 extraHeaders 124 | const extraInfoSpec = ['blocking', 'requestHeaders']; 125 | if ( 126 | chrome.webRequest.OnBeforeSendHeadersOptions.hasOwnProperty('EXTRA_HEADERS') 127 | ) { 128 | extraInfoSpec.push('extraHeaders'); 129 | } 130 | 131 | chrome.webRequest.onBeforeSendHeaders.addListener( 132 | (details) => { 133 | // get请求才做重定向 134 | if (details.method !== 'GET') { 135 | return {}; 136 | } 137 | 138 | const headers = details.requestHeaders; 139 | let i = 0; 140 | for (const l = headers.length; i < l; ++i) { 141 | if (isHeaderNameEqual(headers[i].name, 'Referer')) { 142 | break; 143 | } 144 | } 145 | 146 | if (i < headers.length) { 147 | // block 掉 百度统计,影响速度 148 | if ( 149 | headers[i].value.indexOf('x-from=co-translate-extension') !== -1 150 | && details.url.indexOf('hm.baidu.com') !== -1 151 | ) { 152 | return { cancel: true }; 153 | } 154 | // block掉广告 155 | if ( 156 | details.url.indexOf( 157 | '//dictionary.iciba.com/dictionary/getWebFeedsAd' 158 | ) !== -1 159 | ) { 160 | return { cancel: true }; 161 | } 162 | 163 | // refer 中有x-from 且 当前没有 x-from 才需要处理 164 | let needAddQuery = headers[i].value.indexOf('x-from=co-translate-extension') !== -1 165 | && details.url.indexOf('x-from=co-translate-extension') === -1; 166 | 167 | // 搜狗特殊处理,否则会出现反爬虫验证码 168 | needAddQuery = needAddQuery && details.url.indexOf('fanyi.sogou.com/logtrace') === -1; 169 | 170 | if (needAddQuery) { 171 | requestsToRedirect[details.requestId] = headers[i].value; 172 | return {}; 173 | } 174 | } 175 | 176 | // 有道比较特殊,如果没有关键字参数会302跳转,且不一定有refer(直接访问时),导致丢失 x-from 177 | if (details.url.indexOf('m.youdao.com/dict') !== -1) { 178 | if ( 179 | details.url.indexOf('x-from=co-translate-extension') !== -1 180 | && !new URL(details.url).searchParams.get('q') 181 | ) { 182 | requestsToRedirect[details.requestId] = details.url; 183 | } 184 | } 185 | return {}; 186 | }, 187 | { 188 | urls: [''] 189 | }, 190 | // 72版本起,访问refer需要extraHeaders,72之前加了会报错,需要兼容 191 | // https://groups.google.com/a/chromium.org/d/msg/chromium-extensions/vYIaeezZwfQ/hVln6g1OAgAJ 192 | extraInfoSpec 193 | ); 194 | 195 | chrome.webRequest.onHeadersReceived.addListener( 196 | (details) => { 197 | if (requestsToRedirect[details.requestId]) { 198 | const referUrl = requestsToRedirect[details.requestId]; 199 | delete requestsToRedirect[details.requestId]; 200 | 201 | const headers = details.responseHeaders; 202 | let i = 0; 203 | for (const l = headers.length; i < l; ++i) { 204 | if (isHeaderNameEqual(headers[i].name, 'content-type')) { 205 | break; 206 | } 207 | } 208 | // 当响应的是html 或者 响应码是3xx 时才需要处理 209 | if (i < headers.length) { 210 | if (headers[i].value.indexOf('text/html') !== -1) { 211 | const urlObj = new URL(details.url); 212 | urlObj.searchParams.set('x-from', 'co-translate-extension'); 213 | const showDetail = new URL(referUrl).searchParams.get('showDetail'); 214 | urlObj.searchParams.set('showDetail', showDetail); 215 | return { redirectUrl: urlObj.href }; 216 | } 217 | } 218 | if (`${details.statusCode}`.startsWith('3')) { 219 | i = 0; 220 | for (const l = headers.length; i < l; ++i) { 221 | if (isHeaderNameEqual(headers[i].name, 'Location')) { 222 | break; 223 | } 224 | } 225 | if (i < headers.length) { 226 | const urlObj = new URL(headers[i].value); 227 | urlObj.searchParams.set('x-from', 'co-translate-extension'); 228 | const showDetail = new URL(referUrl).searchParams.get('showDetail'); 229 | urlObj.searchParams.set('showDetail', showDetail); 230 | return { redirectUrl: urlObj.href }; 231 | } 232 | } 233 | } 234 | return {}; 235 | }, 236 | { urls: [''] }, 237 | ['responseHeaders', 'blocking'] 238 | ); 239 | 240 | chrome.webRequest.onErrorOccurred.addListener( 241 | (details) => { 242 | // Cleanup in case we don't reach onHeadersReceived 243 | delete requestsToRedirect[details.requestId]; 244 | }, 245 | { urls: [''] } 246 | ); 247 | -------------------------------------------------------------------------------- /src/css/iconfont/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/conn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 358 | 359 | 360 | 361 | 362 | -------------------------------------------------------------------------------- /src/lib/youdao-web-translate/web2/styles/all-packed.css: -------------------------------------------------------------------------------- 1 | html{_background:url(null) fixed;}.forbid-select{-moz-user-select:none;-khtml-user-select:none;user-select:none;}.OUTFOX_JTR_BAR{width:100%;margin:0;padding:0;border:none;position:fixed;z-index:2147483646;top:0;left:0;height:50px;_position:absolute;_top:expression((body.scrollTop+documentElement.scrollTop)+'px');}#OUTFOX_JTR_BAR_CLOSE_UPDATE_TIP{color:#429d3b;height:38px;width:200px;background:url("../images/ydd_tip.png") left top no-repeat;overflow:hidden;text-align:left;margin:0 auto;display:none;position:relative;top:-31px;z-index:1;}#OUTFOX_JTR_BAR_CLOSE_UPDATE_TIP div{margin-left:5px;color:#429d3b;height:38px;background:url("../images/ydd_tip.png") right top no-repeat;padding:3px 8px 3px 3px;}#OUTFOX_JTR_BAR_CLOSE_UPDATE_TIP .update-date{margin-left:20px;}#OUTFOX_JTR_BAR_UPDATE_SHADE{background-color:#818181;height:50px;left:0;position:absolute;top:0;width:100%;opacity:.75;filter:alpha(opacity = 75);display:none;}#OUTFOX_JTR_BAR_CLOSE_UPDATE_TIP_CONTENT_CLOSE{width:10px;height:10px;position:absolute;right:5px;top:5px;cursor:pointer;}.OUTFOX_JTR_BAR_CLOSE{display:block;background:url("../images/bar-sp.png") -35px -69px no-repeat;width:17px;height:13px;position:absolute;top:1px;right:1px;cursor:pointer;}.OUTFOX_JTR_BAR_CLOSE:hover{background:url("../images/bar-sp.png") -56px -69px no-repeat;}#OUTFOX_JTR_BAR_BODY{margin:0;padding:0;background:url("../images/bar-sp-repeat-x.png") 0 -92px repeat-x;font-size:12px;height:50px;overflow:hidden;color:#6F6F6F;text-align:center;border:0;}#OUTFOX_JTR_BAR_BODY #wrapper{*width:960px;padding:0 10px;max-width:960px;margin:0 auto;text-align:left;}#OUTFOX_JTR_BAR_BODY .OUTFOX_BAR_TOTAL_NUM{font-family:Arial;font-size:18px;margin:0 5px;}#OUTFOX_JTR_BAR_BODY #headerLogo{background:url("../images/bar-sp.png") -83px -66px no-repeat;text-indent:-999em;display:block;width:126px;height:20px;float:left;margin-top:19px;font-size:0;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/header_logo.png");_margin-top:17px;}#OUTFOX_JTR_BAR_BODY #sliderLabel{float:left;margin:22px 0 0 20px;}#OUTFOX_JTR_BAR_BODY #sliderWrapper{float:left;margin-top:6px;position:relative;left:15px;width:269px;}#OUTFOX_JTR_BAR_BODY #levelLabel{position:relative;height:20px;}#OUTFOX_JTR_BAR_BODY #level-0{right:10px;}#OUTFOX_JTR_BAR_BODY #level-1{left:149px;}#OUTFOX_JTR_BAR_BODY #level-2{left:83px;}#OUTFOX_JTR_BAR_BODY #level-3{left:17px;}#OUTFOX_JTR_BAR_BODY #levelLabel label{position:absolute;cursor:pointer;color:#4E86CC;}#OUTFOX_JTR_BAR_BODY.disable #levelLabel label{color:#ccc;cursor:default;}#OUTFOX_JTR_BAR_BODY #levelLabel .active{color:#707070;cursor:default;font-weight:bold;}#OUTFOX_JTR_BAR_BODY #status{float:left;margin:20px 10px 0 20px;_margin-top:22px;}#OUTFOX_JTR_BAR_BODY .statistic #status{margin-top:16px;}#OUTFOX_JTR_BAR_BODY #switchWrapper{float:right;margin-top:16px;}#OUTFOX_JTR_BAR_BODY #feedback{margin:18px 0 0 15px;float:right;text-align:right;}#OUTFOX_JTR_BAR_BODY #feedback a{color:#4E86CC;text-decoration:none;}#OUTFOX_JTR_BAR_BODY #feedback #fb{color:#FF8D3D;}#OUTFOX_JTR_BAR_BODY #feedback a:hover{text-decoration:underline;}#OUTFOX_JTR_BAR_BODY a:link{color:#4E86CC;text-decoration:none;}#OUTFOX_JTR_BAR_BODY #switch{color:#4D86CC;background-image:url("../images/switch_button.png");display:block;width:70px;height:23px;cursor:pointer;line-height:23px;text-align:center;text-decoration:none;}#OUTFOX_JTR_BAR_BODY #switch:hover{background-image:url("../images/switch_button_hover.png");}.OUTFOX_JTR_TRANSTIP_WRAPPER{z-index:2147483640;max-width:300px;*width:300px;}.OUTFOX_JTR_TRANSTIP_WRAPPER p{padding:10px;margin:0;}.OUTFOX_JTR_TRANSTIP_ADVISE_TOGGLE{display:block;cursor:pointer;}.OUTFOX_JTR_TRANSTIP_ADVISE_THANK_TIP .OUTFOX_JTR_TRANSTIP_ADVISE_TOGGLE{display:none;}.OUTFOX_JTR_TRANSTIP_ADVISE_THANK{display:none;}.OUTFOX_JTR_TRANSTIP_ADVISE_THANK_TIP .OUTFOX_JTR_TRANSTIP_ADVISE_THANK{display:inline;}.expand .OUTFOX_JTR_TRANSTIP_ADVISE_TEXT{width:250px;height:50px;margin:10px 0 0;resize:none;}.OUTFOX_JTR_NANCI_BAR{position:absolute;text-decoration:none;overflow:hidden;background:#fff;border:1px solid #AFCEF5;padding-left:3px;z-index:2147483641;-webkit-box-shadow:2px 2px 2px #ccc;-moz-box-shadow:2px 2px 2px #ccc;box-shadow:2px 2px 2px #ccc;}.OUTFOX_JTR_NANCI_BAR a{cursor:pointer;text-decoration:none;text-indent:-999em;width:15px;height:15px;display:inline-block;}.OUTFOX_JTR_NANCI_CTRL_DETAIL_BG{_width:15px;_height:15px;_zoom:1;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/icon_detail.png");}.OUTFOX_JTR_NANCI_CTRL_CLOSE_BG{_width:15px;_height:15px;_zoom:1;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/icon_delete.png");}.OUTFOX_JTR_NANCI_CTRL_DETAIL{_position:absolute;_margin-left:-8px;background:url("../images/bar-sp.png") 0 -68px no-repeat;_background-image:none;}.OUTFOX_JTR_NANCI_CTRL_CLOSE{_position:absolute;_margin-left:-6px;background:url("../images/bar-sp.png") -13px -70px no-repeat;_background-image:none;}.OUTFOX_JTR_TRANSTIP_ORIGIN .OUTFOX_JTR_TRANSTIP_ADVISE{padding-bottom:14px;}.OUTFOX_JTR_TRANSTIP_ADVISE_TEXT,.OUTFOX_JTR_TRANSTIP_ADVISE_SUBMIT{display:none;}.expand .OUTFOX_JTR_TRANSTIP_ADVISE_TEXT,.expand .OUTFOX_JTR_TRANSTIP_ADVISE_SUBMIT{display:block;}.finish .OUTFOX_JTR_TRANSTIP_ADVISE_TEXT,.finish .OUTFOX_JTR_TRANSTIP_ADVISE_SUBMIT{display:block;visibility:hidden;}div.expand a.OUTFOX_JTR_TRANSTIP_ADVISE_SUBMIT{background-image:url("../images/trans_tip_submit_bg.png");width:96px;height:23px;line-height:23px;text-align:center;cursor:pointer;float:right;margin-top:10px;outline:none;text-decoration:none;}div.expand a.OUTFOX_JTR_TRANSTIP_ADVISE_SUBMIT:hover{background-image:url("../images/trans_tip_submit_bg_hover.png");}.OUTFOX_NANCI_TIPS{white-space:nowrap;}.OUTFOX_JTR_NANCI_CTRL_WORD{color:#000;margin-right:5px;}#OUTFOX_JTR_BAR_BODY #failed{line-height:50px;}#OUTFOX_BAR_WRAPPER iframe.OUTFOX_JTR_BAR_HIDE{display:none;}*html .OUTFOX_JTR_TRANSTIP_WRAPPER .ydd-bg-bottom,*html .OUTFOX_JTR_TRANSTIP_WRAPPER .ydd-bg-top{width:300px;}*html .OUTFOX_JTR_TRANSTIP_WRAPPER{width:300px;border-width:0 1px;border-color:#d9d9d9;border-style:solid;}#yddWrapper{z-index:2147483640;max-width:280px;display:none;}.ydd-container *{padding:0;margin:0;font-size:12px;color:#2A2A2A;}.ydd-container{display:block;position:relative;width:100%;height:100%;font-size:12px;text-align:left;opacity:.95;*filter:alpha(opacity = 95);*background-color:#FFF;}.ydd-container div{display:block;float:none;}.ydd-container a{color:#4E86CC;}.ydd-container a:link{text-decoration:none;}.ydd-container a:hover{text-decoration:underline;}.ydd-body-wrapper{position:relative;padding:0 5px 0 6px;*background-color:transparent;}.ydd-body{padding:0 15px 10px;overflow:hidden;background-color:#fff;*background-color:transparent;}.ydd-lb{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAoCAYAAADdaosOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTUvMTDB0g89AAAANklEQVQoke3LsQ0AIAgF0YOEwsbZnPQPxSK6ALRWXPtyAA4EsIAt6WTmdZpKMLMa2mNgYOAbPAs0Bm/LtPJAAAAAAElFTkSuQmCC);*background-image:none;background-repeat:repeat-y;background-position:left 0;width:6px;height:100%;position:absolute;left:0;top:0;}.ydd-rb{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAoCAYAAADdaosOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTUvMTDB0g89AAAANElEQVQoke3JoREAIAwEwQuDwKDov7AvJhMaCBrzJ2/JzJJUwAE2sIA5IoKuN7TXYDB8hwuIjQgBYxgxJgAAAABJRU5ErkJggg==);*background-image:none;background-repeat:repeat-y;background-position:right 0;width:6px;height:100%;position:absolute;right:0;top:0;}.ydd-top-wrapper{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAUCAYAAAC07qxWAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAxMC8xNS8xMMHSDz0AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzVxteM2AAAA2ElEQVQokcWQMY6DMBRE37etSAsFot8bUFPlAjlDjrIHyFFyAK7henuKPQAIEAhHeBsnIsFC0TY70sguxuOZEe8970AAE+4+kMiJBg7hwSufoIEPQL0ItsKqqk55nvfWWrfnKHVde+cczrmfYRiuZVlegFvgcqc0TeO11izLQtd1tG37XRTFEXBrsRrHkb7vmaaJJEnIsqyw1n6F/PfsKBFBRPDeM88zxhjSND0H0aOkUUptxtVaf67dAIzIpuCj6HqBqGNky/jXEdddxye8L9wp80fH/8v4C8bURyFzKYfYAAAAAElFTkSuQmCC);*background-image:none;background-repeat:no-repeat;background-position:right 0;padding-right:10px;height:20px;}.ydd-top{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVAAAAAUCAYAAADbVmUXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZFJREFUeNrs2ktKw1AYhuFzSaIQLGRkRqEjR67A7Th1Irq0LqILElpDk3jS5nJ6/C0WYgn2fSAE21ILhZePpLppGtXSWiuB+CAAzNBf9ar58R8KAdXCB9IEFcDMRRMHsxEC2pwKqB9K6QCAubqZeHVKx1FE/YD2gTRdLE0QT8MKBTBj6cTr0z9q7zy8LgxoH03rnY0QUwCYm2zi5Vl3R9U9V4UR9a8Z6CCa0Wq1eiqK4jmO40djzD3fD4B/HtDePpxuYH66864bmd9uJvkL1HrxjNfr9Uue5+9pmqokSZS1lq8HwLUEVPVtdOet6+NWiqi4QNvlmWXZ62KxUFVVqc1mM7wZAFwT177bboXuwp97SgE1y+XyzcXTlmU5Pqm59Ang+rTt6yL6cfhzXKFiQKMoejDGEE0AOKxQK43JMKD7O+7uBXdtQAEAQx9PBvQIAQWAYYGq3wR0+J0nAQWA8wLKAgWAQF3XQzgJKACc6ayAcgceAMYeskAB4BILlIACAAEFgMsGlGugADD2UArolwADAA7gcFvj3gN0AAAAAElFTkSuQmCC);*background-image:none;background-repeat:no-repeat;background-position:left 0;height:20px;*width:240px;}.ydd-bottom-wrapper{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAeCAYAAAAVdY8wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTUvMTDB0g89AAAArElEQVQ4jeWUsQ3CMBBFX4AJEDukSJ2K/SgYhI3oU2SBFCnC4X8UOGCSECgoEJx0siw/ve9z4azrOmdQVVWR5/kGOAEGnFfuI26y3gclfdj4n9E/Ncz3RnvsWTA98FfgvDGEUANKrSNQEm3bHiLYw4/R7o6ZHcuy3AEhtS4kIQkzq5um2RdFsQXOEbzdMwPWcdNHhcR2j+b6bfgA9hTqQUum8ydNBiwHbze1cgH57aT/yHsyngAAAABJRU5ErkJggg==);*background-image:none;background-repeat:no-repeat;background-position:right 0;padding-right:10px;height:30px;}.ydd-bottom{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAAeCAYAAACsaJwUAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTUvMTDB0g89AAABXklEQVR4nO3dPY7TUBQF4PNshxSp0qVgB5Qp0rMMNkFHwYIowyqoUqJkJaTEpsAWJmQG6RWTjPJ90pOVn+JWR+e6eSVJk6RN0iVZ7Pf799vt9utmswkA13VP/TAMw0vOAfCqCE+AClfDcxiG9H3/0rMAvBqaJ0AF4QlQwdoOUEHzBKggPAEqWNsBKmieABU0T4AKmidABeEJUMHaDlBB8wSoIDwBKljbASpongAVpvAcxpOmaYZEeAI8p8sYmqNBeAL8X5uk5PclcCVJs16vf+x2u49N09x2MoA7VvInPKcbNN+cTqfvy+Xy7U0nA7hjl+88+yQ/z+fzl67rPmmfANeV/N0+m4z3tx+Px2+LxeJdKeWW8wHcpTJ7TuHZjqc7HA6fV6vVh7ZtrfAAM/PwvGygU4hOn8vs/wAPbf7Oc9LPvuvzb3AKUODhXQZheeLkyhPgYV0LwufCUnACJPkFXHpxDgJO4nYAAAAASUVORK5CYII=);*background-image:none;background-repeat:no-repeat;background-position:left 0;height:30px;_line-height:24px;line-height:24px;padding:0 15px;*width:210px;}.ydd-key-title{font-size:13px;font-weight:bold;}.ydd-phonetic{font-family:"lucida sans unicode",arial,sans-serif;color:#999;}.ydd-base-trans .ydd-tabs{display:none;}.ydd-trans-wrapper{margin:5px 0;}.ydd-trans-wrapper a{color:#999;}.ydd-tabs{background:url("../images/swipe_hr.png") 0 9px no-repeat;margin:5px 0;}.ydd-tab{color:#707070;background:#fff;padding-right:5px;}.ydd-voice{margin:0 5px;}.ydd-no-result{margin-top:7px;}html* .ydd-bg-top{background:url("../images/bar-sp-repeat-x.png") 0 0 repeat-x;width:280px;height:40px;position:absolute;z-index:-1;top:0;left:0;}html* .ydd-bottom-wrapper{background:url(../images/bar-sp-repeat-x.png) 0 -46px repeat-x;}html* #yddWrapper{width:280px;border-width:0 1px;border-color:#d9d9d9;border-style:solid;}html* .OUTFOX_JTR_TRANSTIP_WRAPPER #yddWrapper,html* .OUTFOX_JTR_TRANSTIP_WRAPPER .ydd-bg-bottom,html* .OUTFOX_JTR_TRANSTIP_WRAPPER .ydd-bg-top{width:300px;}html* .ydd-title{line-height:14px;}.slider-container{height:9px;width:269px;margin:0;cursor:pointer;background:url("../images/bar-sp.png") 0 -56px no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider_bg.png");}.disable .slider-container{cursor:default;}#sliderBackground{width:0;overflow:hidden;}.slider-background{height:100%;background:url("../images/bar-sp.png") 0 -23px no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider_bar.png");}.disable .slider-background{background:url("../images/bar-sp.png") 0 -39px no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider_bar_gray.png");}.slider{display:block;height:20px;width:75px;top:14px;position:absolute;background:url("../images/bar-sp.png") 0 0 no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider.png");cursor:pointer;}.slider:hover{background:url("../images/bar-sp.png") -157px 0 no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider_hover.png");}.disable .slider,.disable .slider:hover{background:url("../images/bar-sp.png") -78px 0 no-repeat;_background-image:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled = true,sizingMethod = image,src = "http://shared.ydstatic.com/jtr/v1/images/slider_gray.png");} -------------------------------------------------------------------------------- /src/js/content-script.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /** ******************* 插件用到的元素模板 *************** */ 3 | // 插件注入的页面结构 4 | const transExt = $(` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | `); 33 | 34 | /** ******************** 初始化相关 ********************** */ 35 | 36 | // 存储选中的文本内容 37 | let selectedText; 38 | // 获取数据操作对象 39 | const data = new ExtData(); 40 | 41 | // 弹窗移动相关数据 42 | // 翻译弹窗的移动 43 | let isMoving = false; 44 | 45 | // 当前是否处于固定右侧状态 46 | let isLockPosition = false; 47 | 48 | // 移入翻译图标相关数据 49 | let hoverTimeoutId; 50 | let isTriggerByHover = false; 51 | 52 | // 打开弹窗延时,避免hover弹出时点击导致弹窗隐藏 53 | let waitingForPopupShow = false; 54 | 55 | // 后续需要操作的相关元素 56 | const transBtn = transExt.find('.trans-ext__trans-btn'); 57 | transBtn.hide(); 58 | const transPopup = transExt.find('.trans-ext__popup'); 59 | transPopup.hide(); 60 | const transIframe = transExt.find('.trans-ext__iframe'); 61 | const arrow = transExt.find('.trans-ext__popup-arrow'); 62 | arrow.hide(); 63 | // 默认显示展开按钮 64 | transExt.find('.trans-ext__tool-up').hide(); 65 | // 默认显示 固定到右侧按钮 66 | transExt.find('.trans-ext__tool-left').hide(); 67 | transExt.find('.trans-ext__tool-position').hide(); 68 | 69 | // 获取并高亮当前使用的搜索工具 70 | data.get('transTool', (val) => { 71 | const activeTransTool = transExt.find( 72 | `.trans-ext__tool[data-trans-tool=${val}]` 73 | ); 74 | 75 | activeTransTool.addClass('active'); 76 | }); 77 | 78 | /** ************************ 事件处理 ********************** */ 79 | 80 | // 谷歌分析 81 | function ga(...args) { 82 | args.push({ 83 | page: '/' 84 | }); 85 | chrome.extension.sendRequest({ 86 | ga: args 87 | }); 88 | } 89 | 90 | // iframe 加载超时处理 91 | let iframeTimer; 92 | const IFRAME_TIMEOUT = 6000; 93 | function iframeTimeOut() { 94 | // eslint-disable-next-line no-console 95 | console.log('iframe加载超时了'); 96 | window.stop(); 97 | } 98 | function startLoadIframe() { 99 | // 超时处理 100 | iframeTimer = setTimeout(iframeTimeOut, IFRAME_TIMEOUT); 101 | transIframe[0].onload = () => { 102 | clearTimeout(iframeTimer); 103 | }; 104 | } 105 | 106 | // 切换是否显示输入内容、翻译语言切换等界面元素 107 | transExt.find('.trans-ext__tool-down').click(() => { 108 | ga('send', 'event', 'input-view', 'show'); 109 | transIframe.eq(0)[0].contentWindow.postMessage( 110 | { 111 | op: 'showDetail' 112 | }, 113 | '*' 114 | ); 115 | transExt.find('.trans-ext__tool-down').hide(); 116 | transExt.find('.trans-ext__tool-up').show(); 117 | }); 118 | transExt.find('.trans-ext__tool-up').click(() => { 119 | ga('send', 'event', 'input-view', 'hide'); 120 | transIframe.eq(0)[0].contentWindow.postMessage( 121 | { 122 | op: 'hideDetail' 123 | }, 124 | '*' 125 | ); 126 | transExt.find('.trans-ext__tool-down').show(); 127 | transExt.find('.trans-ext__tool-up').hide(); 128 | }); 129 | 130 | // 固定到右边 131 | transExt.find('.trans-ext__tool-right').click(() => { 132 | ga('send', 'event', 'lock-right', 'lock'); 133 | isLockPosition = true; 134 | transExt.find('.trans-ext__tool-right').hide(); 135 | transExt.find('.trans-ext__tool-left').show(); 136 | $('html').addClass('co-transition').addClass('co-fixed-r'); 137 | arrow.hide(); 138 | }); 139 | // 固定到左边 140 | transExt.find('.trans-ext__tool-left').click(() => { 141 | ga('send', 'event', 'lock-left', 'lock'); 142 | transExt.find('.trans-ext__tool-left').hide(); 143 | transExt.find('.trans-ext__tool-position').show(); 144 | $('html').removeClass('co-fixed-r').addClass('co-fixed-l'); 145 | arrow.hide(); 146 | }); 147 | 148 | // 样式初始化 149 | function resetStyle() { 150 | isLockPosition = false; 151 | transExt.find('.trans-ext__tool-left').hide(); 152 | transExt.find('.trans-ext__tool-position').hide(); 153 | transExt.find('.trans-ext__tool-right').show(); 154 | $('html').removeClass('co-fixed-r co-fixed-l'); 155 | setTimeout(() => { 156 | $('html').removeClass('co-transition'); 157 | }, 200); 158 | } 159 | 160 | // 取消固定 161 | transExt.find('.trans-ext__tool-position').click(() => { 162 | transPopup.css({ 163 | top: $(document).scrollTop(), 164 | left: '0' 165 | }); 166 | resetStyle(); 167 | }); 168 | 169 | // 处理拖动过程中事件可能被iframe捕获而丢失事件的问题 170 | // https://blog.csdn.net/zgrbsbf/article/details/71423401 171 | function setIsMoving(moving) { 172 | isMoving = moving; 173 | if (moving) { 174 | transIframe.css('pointer-events', 'none'); 175 | } else { 176 | transIframe.css('pointer-events', 'auto'); 177 | } 178 | } 179 | 180 | // 关闭 popup 的相关操作 181 | function handleClosePopup() { 182 | // 如果没有选中文本,确保隐藏插件内容 183 | transBtn.hide(); 184 | transPopup.hide(); 185 | arrow.hide(); 186 | transIframe.attr('src', ''); 187 | resetStyle(); 188 | 189 | // 恢复是否显示详情的箭头 190 | transExt.find('.trans-ext__tool-down').show(); 191 | transExt.find('.trans-ext__tool-up').hide(); 192 | 193 | setIsMoving(false); 194 | 195 | isTriggerByHover = false; 196 | } 197 | 198 | // 切换翻译工具 199 | let changeTransToolTimer; 200 | function changeTransTool(transTool, keyword) { 201 | clearTimeout(changeTransToolTimer); 202 | clearTimeout(iframeTimer); 203 | // 切换后先清空页面 204 | transIframe.attr('src', ''); 205 | 206 | // 避免页面错误时丢失 keyword 的问题 207 | selectedText = keyword || selectedText; 208 | transExt.find('.trans-ext__tool').removeClass('active'); 209 | transExt 210 | .find(`.trans-ext__tool[data-trans-tool=${transTool}]`) 211 | .addClass('active'); 212 | data.set('transTool', transTool); 213 | data.get(transTool, (val) => { 214 | let iframeURL = val; 215 | iframeURL = iframeURL.replace('KEYWORD', encodeURIComponent(selectedText)); 216 | iframeURL = iframeURL.replace( 217 | 'SHOWDETAIL', 218 | transExt.find('.trans-ext__tool-up').is(':visible') 219 | ); 220 | startLoadIframe(); 221 | loadIframeSrc(transIframe, iframeURL); 222 | }); 223 | } 224 | 225 | 226 | // 监听关闭 popup 按钮点击事件 227 | transExt.find('.trans-ext__tool-close').click(handleClosePopup); 228 | 229 | // 监听 popup 中翻译切换图标的点击事件 230 | transExt.find('.trans-ext__tool').click((event) => { 231 | const transTool = $(event.target).data('trans-tool'); 232 | // 避免 iframe 内页面出现异常、跳转验证页面等导致 iframe-message.js 无法加载进而导致无法切换其它翻译的问题 233 | changeTransToolTimer = setTimeout(() => { 234 | changeTransTool(transTool, ''); 235 | }, 200); 236 | transIframe.eq(0)[0].contentWindow.postMessage({ changeTransTool: transTool }, '*'); 237 | ga('send', 'event', 'trans-tool', 'change', transTool); 238 | }); 239 | 240 | // 获取 iframe 内输入值后切换 翻译 241 | window.addEventListener('message', (event) => { 242 | const transTool = event.data.changeTransTool; 243 | if (transTool) { 244 | selectedText = event.data.keyword; 245 | changeTransTool(transTool, selectedText); 246 | } 247 | }); 248 | 249 | // 点击翻译按钮时防止划选的文本消失掉 250 | transExt.on('mousedown mouseup', (event) => { 251 | event.preventDefault(); 252 | event.stopPropagation(); 253 | }); 254 | 255 | // 获取所选内容边界 256 | // 参考 https://stackoverflow.com/questions/12603397/calculate-width-height-of-the-selected-text-javascript 257 | function getSelectionDimensions() { 258 | let sel = document.selection; 259 | let range; 260 | let width = 0; 261 | let height = 0; 262 | let top = 0; 263 | let left = 0; 264 | let special = false; 265 | if (sel) { 266 | if (sel.type !== 'Control') { 267 | range = sel.createRange(); 268 | width = range.boundingWidth; 269 | height = range.boundingHeight; 270 | top = range.boundingTop; 271 | left = range.boundingLeft; 272 | } 273 | } else if (window.getSelection) { 274 | sel = window.getSelection(); 275 | if (sel.rangeCount) { 276 | range = sel.getRangeAt(0).cloneRange(); 277 | if (range.getBoundingClientRect) { 278 | const rect = range.getBoundingClientRect(); 279 | width = rect.right - rect.left; 280 | height = rect.bottom - rect.top; 281 | top = rect.top; 282 | left = rect.left; 283 | } 284 | } 285 | } 286 | // input 和 area 获取不到位置信息,需要特别处理 287 | if (width === 0 && height === 0) { 288 | width = transBtn.width(); 289 | height = transBtn.height(); 290 | // 获取transBtn位置并减去transBtn的偏移 291 | top = transBtn.position().top - $(document).scrollTop() - 15; 292 | left = transBtn.position().left - $(document).scrollLeft() - 15; 293 | special = true; 294 | } 295 | return { 296 | width, height, top, left, special 297 | }; 298 | } 299 | 300 | // 计算 popup 初始位置 301 | function calcInitPopupPosition() { 302 | const arrowChilds = transExt.find('.trans-ext__popup-arrow>co-div'); 303 | const arrowChild = transExt.find('.trans-ext__popup-arrow>co-div:last-child'); 304 | const selectedRect = getSelectionDimensions(); 305 | // 相对于浏览器的中心点位置 306 | const selectedCenter = { 307 | x: selectedRect.left + selectedRect.width / 2, 308 | y: selectedRect.top + selectedRect.height / 2 309 | }; 310 | const arrowHeight = 8; 311 | const arrowWidth = 16; 312 | 313 | // 假设选中文字下方空间足够容纳弹窗 314 | let popTop = $(document).scrollTop() + selectedRect.top + selectedRect.height + arrowHeight; 315 | let popLeft = $(document).scrollLeft() + selectedRect.left + (selectedRect.width - transPopup.width()) / 2; 316 | let arrowTop = popTop - arrowHeight; 317 | 318 | // 选中文本下部能否放下弹窗 319 | const canPlaceBottom = selectedRect.top + selectedRect.height + transPopup.height() < window.innerHeight; 320 | const canPlaceTop = selectedRect.top > transPopup.height(); 321 | if (!canPlaceBottom && canPlaceTop) { 322 | // 下方放不下弹窗,定位到上方 323 | popTop = $(document).scrollTop() + selectedRect.top - transPopup.height() - arrowHeight; 324 | arrowTop = $(document).scrollTop() + selectedRect.top - arrowHeight; 325 | arrowChilds.css({ transform: 'rotate(180deg)' }); 326 | arrowChild.css({ 327 | borderColor: 'rgb(255, 255, 255) transparent', 328 | top: '-1px' 329 | }); 330 | } else { 331 | arrowChilds.css({ transform: 'rotate(0deg)' }); 332 | arrowChild.css({ 333 | borderColor: `${transExt.find('.trans-ext__title-bar').css('background-color')} transparent`, 334 | top: '1px' 335 | }); 336 | } 337 | 338 | // 左边是否能容纳弹窗 339 | const disableLeft = selectedCenter.x < transPopup.width() / 2; 340 | if (disableLeft) { 341 | popLeft = $(document).scrollLeft() + selectedCenter.x - arrowWidth; 342 | } 343 | 344 | // 右边是否能容纳弹窗 345 | const disableRigth = selectedCenter.x > window.innerWidth - transPopup.width() / 2; 346 | if (disableRigth) { 347 | popLeft = $(document).scrollLeft() + selectedCenter.x - transPopup.width() + arrowWidth; 348 | } 349 | 350 | // 弹出位置 351 | transPopup.css({ 352 | top: popTop, 353 | left: popLeft 354 | }); 355 | 356 | // arrow 位置 357 | arrow.css({ 358 | top: arrowTop, 359 | left: selectedCenter.x + $(document).scrollLeft() 360 | }); 361 | 362 | // 弹出翻译窗,隐藏翻译按钮 363 | transBtn.hide(); 364 | transPopup.show(); 365 | arrow.show(); 366 | } 367 | 368 | // 移入翻译按钮 500ms 自动弹出 369 | transBtn.mouseenter(() => { 370 | hoverTimeoutId = setTimeout(() => { 371 | isTriggerByHover = true; 372 | transBtn.mouseup(); 373 | waitingForPopupShow = true; 374 | // 设置延时,避免刚好弹出时用户点击而又没有点击中弹窗的情况 375 | setTimeout(() => { 376 | waitingForPopupShow = false; 377 | }, 700); 378 | }, 500); 379 | }); 380 | 381 | transBtn.mouseleave(() => { 382 | clearTimeout(hoverTimeoutId); 383 | }); 384 | 385 | // 点击“译”字显示翻译内容页 popup 386 | transBtn.mouseup((event) => { 387 | clearTimeout(hoverTimeoutId); 388 | event.stopPropagation(); 389 | ga('send', 'pageview'); 390 | if (isTriggerByHover) { 391 | ga('send', 'event', 'trans-button', 'hover-translate'); 392 | } else { 393 | ga('send', 'event', 'trans-button', 'translate'); 394 | } 395 | // 加载图标 396 | loadCSS(chrome.extension.getURL('src/css/iconfont/iconfont.css'), 'iconfont'); 397 | 398 | data.get('transTool', (val) => { 399 | // 初始化并设置active 400 | transExt.find('.trans-ext__tool').removeClass('active'); 401 | const activeTransTool = transExt.find( 402 | `.trans-ext__tool[data-trans-tool=${val}]` 403 | ); 404 | activeTransTool.addClass('active'); 405 | 406 | data.get(val, (val2) => { 407 | startLoadIframe(); 408 | loadIframeSrc(transIframe, val2 409 | .replace('KEYWORD', encodeURIComponent(selectedText)) 410 | .replace('SHOWDETAIL', false)); 411 | }); 412 | }); 413 | calcInitPopupPosition(event); 414 | }); 415 | 416 | // 监听是否需要显示译字或者隐藏popup等 417 | $(document).mouseup((event) => { 418 | // 测试发现:选中文本后,再次keyup选中区域内内容才取消选中,keydown选中区域外内容马上取消选中 419 | // 猜测:这是为了处理拖拽选中内容而这样设计的吧 420 | // 异步获取选中的文本(会产生适当的延时),避免再次点击上次选中文本区域内时获取到上次选中内容的情况 421 | setTimeout(() => { 422 | // 获取选中的文本 423 | selectedText = window 424 | .getSelection() 425 | .toString() 426 | .trim(); 427 | if (isLockPosition) { 428 | if (selectedText) { 429 | // 直接翻译 430 | data.get('transTool', (transTool) => { 431 | data.get(transTool, (val) => { 432 | let iframeURL = val; 433 | iframeURL = iframeURL.replace('KEYWORD', encodeURIComponent(selectedText)); 434 | iframeURL = iframeURL.replace( 435 | 'SHOWDETAIL', 436 | transExt.find('.trans-ext__tool-up').is(':visible') 437 | ); 438 | startLoadIframe(); 439 | loadIframeSrc(transIframe, iframeURL); 440 | }); 441 | }); 442 | } 443 | return; 444 | } 445 | 446 | if (selectedText) { 447 | // 如果有选中的文本,显示“译”按钮 448 | if (!$('body').find(transExt).length) { 449 | $('body').append(transExt); 450 | } 451 | 452 | transBtn.css({ 453 | top: event.pageY + 15, 454 | left: event.pageX + 15 455 | }); 456 | transPopup.hide(); 457 | arrow.hide(); 458 | transIframe.attr('src', ''); 459 | transBtn.show(); 460 | } else if (!waitingForPopupShow) { 461 | handleClosePopup(); 462 | } 463 | }, 0); 464 | }); 465 | 466 | /** ******************** popup 拖拽处理 ********************** */ 467 | let startX = 0; 468 | let startY = 0; 469 | let startTop = 0; 470 | let startLeft = 0; 471 | 472 | // popup 拖拽开始 473 | $(transPopup).on('mousedown', '.trans-ext__title-bar', (event) => { 474 | if (isLockPosition || !$(event.target).hasClass('trans-ext__title-bar')) { 475 | return; 476 | } 477 | setIsMoving(true); 478 | startX = event.clientX; 479 | startY = event.clientY; 480 | startTop = transPopup.position().top; 481 | startLeft = transPopup.position().left; 482 | }); 483 | 484 | // popup 拖拽过程中 485 | $(document).on('mousemove', (event) => { 486 | if (isMoving) { 487 | const moveX = event.clientX - startX; 488 | const moveY = event.clientY - startY; 489 | 490 | // 避免出现滚动条 491 | let maxWidth = $(document.body).width(); 492 | let maxHeight = $(document.body).height(); 493 | if (maxWidth < $(window).width()) { 494 | maxWidth = $(window).width(); 495 | } 496 | if (maxHeight < $(window).height()) { 497 | maxHeight = $(window).height(); 498 | } 499 | 500 | let realTopPos = startTop + moveY; 501 | let realLeftPos = startLeft + moveX; 502 | 503 | const heightDiff = realTopPos + transPopup.height() - maxHeight; 504 | const widthDiff = realLeftPos + transPopup.width() - maxWidth; 505 | 506 | if (heightDiff > 0) { 507 | realTopPos -= heightDiff; 508 | } 509 | 510 | if (widthDiff > 0) { 511 | realLeftPos -= widthDiff; 512 | } 513 | transPopup.css({ 514 | top: realTopPos, 515 | left: realLeftPos 516 | }); 517 | // 移动时,隐藏箭头 518 | if (Math.abs(moveX) > 0 || Math.abs(moveY) > 0) { 519 | arrow.hide(); 520 | } 521 | } 522 | }); 523 | 524 | // popup 拖拽结束 525 | $(document).on('mouseup', () => { 526 | setIsMoving(false); 527 | }); 528 | // popup 拖拽结束 529 | // transPopup事件已经被阻止冒泡了,所以,document监听不到,需要额外监听 530 | $(transPopup).on('mouseup', () => { 531 | setIsMoving(false); 532 | }); 533 | -------------------------------------------------------------------------------- /src/css/iconfont/demo_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IconFont Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 29 |
30 |
31 |
    32 | 33 |
  • 34 | 35 |
    youdao_1193429_easyicon.net
    36 |
    &#xe65c;
    37 |
  • 38 | 39 |
  • 40 | 41 |
    baidu
    42 |
    &#xe6b6;
    43 |
  • 44 | 45 |
  • 46 | 47 |
    google
    48 |
    &#xe783;
    49 |
  • 50 | 51 |
  • 52 | 53 |
    缩小
    54 |
    &#xe626;
    55 |
  • 56 | 57 |
  • 58 | 59 |
    yandex
    60 |
    &#xe612;
    61 |
  • 62 | 63 |
  • 64 | 65 |
    sogou
    66 |
    &#xe628;
    67 |
  • 68 | 69 |
  • 70 | 71 |
    金山
    72 |
    &#xe600;
    73 |
  • 74 | 75 |
  • 76 | 77 |
    收起
    78 |
    &#xe61f;
    79 |
  • 80 | 81 |
  • 82 | 83 |
    展开
    84 |
    &#xe625;
    85 |
  • 86 | 87 |
  • 88 | 89 |
    关 闭
    90 |
    &#xe608;
    91 |
  • 92 | 93 |
  • 94 | 95 |
    向右箭头
    96 |
    &#xe64b;
    97 |
  • 98 | 99 |
  • 100 | 101 |
    向右箭头
    102 |
    &#xe784;
    103 |
  • 104 | 105 |
106 |
107 |

Unicode 引用

108 |
109 | 110 |

Unicode 是字体在网页端最原始的应用方式,特点是:

111 |
    112 |
  • 兼容性最好,支持 IE6+,及所有现代浏览器。
  • 113 |
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 114 |
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • 115 |
116 |
117 |

注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式

118 |
119 |

Unicode 使用步骤如下:

120 |

第一步:拷贝项目下面生成的 @font-face

121 |
@font-face {
123 |   font-family: 'co-iconfont';
124 |   src: url('iconfont.eot');
125 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
126 |       url('iconfont.woff2') format('woff2'),
127 |       url('iconfont.woff') format('woff'),
128 |       url('iconfont.ttf') format('truetype'),
129 |       url('iconfont.svg#co-iconfont') format('svg');
130 | }
131 | 
132 |

第二步:定义使用 iconfont 的样式

133 |
.co-iconfont {
135 |   font-family: "co-iconfont" !important;
136 |   font-size: 16px;
137 |   font-style: normal;
138 |   -webkit-font-smoothing: antialiased;
139 |   -moz-osx-font-smoothing: grayscale;
140 | }
141 | 
142 |

第三步:挑选相应图标并获取字体编码,应用于页面

143 |
144 | <span class="co-iconfont">&#x33;</span>
146 | 
147 |
148 |

"co-iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

149 |
150 |
151 |
152 |
153 |
    154 | 155 |
  • 156 | 157 |
    158 | youdao_1193429_easyicon.net 159 |
    160 |
    .co-icon-youdao 161 |
    162 |
  • 163 | 164 |
  • 165 | 166 |
    167 | baidu 168 |
    169 |
    .co-icon-baidu 170 |
    171 |
  • 172 | 173 |
  • 174 | 175 |
    176 | google 177 |
    178 |
    .co-icon-google1 179 |
    180 |
  • 181 | 182 |
  • 183 | 184 |
    185 | 缩小 186 |
    187 |
    .co-icon-suoxiao1 188 |
    189 |
  • 190 | 191 |
  • 192 | 193 |
    194 | yandex 195 |
    196 |
    .co-icon-yandex 197 |
    198 |
  • 199 | 200 |
  • 201 | 202 |
    203 | sogou 204 |
    205 |
    .co-icon-sogou 206 |
    207 |
  • 208 | 209 |
  • 210 | 211 |
    212 | 金山 213 |
    214 |
    .co-icon-kingsoft 215 |
    216 |
  • 217 | 218 |
  • 219 | 220 |
    221 | 收起 222 |
    223 |
    .co-icon-shouqi 224 |
    225 |
  • 226 | 227 |
  • 228 | 229 |
    230 | 展开 231 |
    232 |
    .co-icon-zhankai1 233 |
    234 |
  • 235 | 236 |
  • 237 | 238 |
    239 | 关 闭 240 |
    241 |
    .co-icon-guanbi 242 |
    243 |
  • 244 | 245 |
  • 246 | 247 |
    248 | 向右箭头 249 |
    250 |
    .co-icon-xiangyoujiantou 251 |
    252 |
  • 253 | 254 |
  • 255 | 256 |
    257 | 向右箭头 258 |
    259 |
    .co-icon-xiangzuojiantou-copy 260 |
    261 |
  • 262 | 263 |
264 |
265 |

font-class 引用

266 |
267 | 268 |

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

269 |

与 Unicode 使用方式相比,具有如下特点:

270 |
    271 |
  • 兼容性良好,支持 IE8+,及所有现代浏览器。
  • 272 |
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • 273 |
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • 274 |
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • 275 |
276 |

使用步骤如下:

277 |

第一步:引入项目下面生成的 fontclass 代码:

278 |
<link rel="stylesheet" href="./iconfont.css">
279 | 
280 |

第二步:挑选相应图标并获取类名,应用于页面:

281 |
<span class="co-iconfont co-icon-xxx"></span>
282 | 
283 |
284 |

" 285 | co-iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

286 |
287 |
288 |
289 |
290 |
    291 | 292 |
  • 293 | 296 |
    youdao_1193429_easyicon.net
    297 |
    #co-icon-youdao
    298 |
  • 299 | 300 |
  • 301 | 304 |
    baidu
    305 |
    #co-icon-baidu
    306 |
  • 307 | 308 |
  • 309 | 312 |
    google
    313 |
    #co-icon-google1
    314 |
  • 315 | 316 |
  • 317 | 320 |
    缩小
    321 |
    #co-icon-suoxiao1
    322 |
  • 323 | 324 |
  • 325 | 328 |
    yandex
    329 |
    #co-icon-yandex
    330 |
  • 331 | 332 |
  • 333 | 336 |
    sogou
    337 |
    #co-icon-sogou
    338 |
  • 339 | 340 |
  • 341 | 344 |
    金山
    345 |
    #co-icon-kingsoft
    346 |
  • 347 | 348 |
  • 349 | 352 |
    收起
    353 |
    #co-icon-shouqi
    354 |
  • 355 | 356 |
  • 357 | 360 |
    展开
    361 |
    #co-icon-zhankai1
    362 |
  • 363 | 364 |
  • 365 | 368 |
    关 闭
    369 |
    #co-icon-guanbi
    370 |
  • 371 | 372 |
  • 373 | 376 |
    向右箭头
    377 |
    #co-icon-xiangyoujiantou
    378 |
  • 379 | 380 |
  • 381 | 384 |
    向右箭头
    385 |
    #co-icon-xiangzuojiantou-copy
    386 |
  • 387 | 388 |
389 |
390 |

Symbol 引用

391 |
392 | 393 |

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 394 | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

395 |
    396 |
  • 支持多色图标了,不再受单色限制。
  • 397 |
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • 398 |
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • 399 |
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • 400 |
401 |

使用步骤如下:

402 |

第一步:引入项目下面生成的 symbol 代码:

403 |
<script src="./iconfont.js"></script>
404 | 
405 |

第二步:加入通用 CSS 代码(引入一次就行):

406 |
<style>
407 | .icon {
408 |   width: 1em;
409 |   height: 1em;
410 |   vertical-align: -0.15em;
411 |   fill: currentColor;
412 |   overflow: hidden;
413 | }
414 | </style>
415 | 
416 |

第三步:挑选相应图标并获取类名,应用于页面:

417 |
<svg class="icon" aria-hidden="true">
418 |   <use xlink:href="#icon-xxx"></use>
419 | </svg>
420 | 
421 |
422 |
423 | 424 |
425 |
426 | 445 | 446 | 447 | -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/css/iconfont/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(n){var c,o='',t=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(t&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var t=function(){document.removeEventListener("DOMContentLoaded",t,!1),c()};document.addEventListener("DOMContentLoaded",t,!1)}else document.attachEvent&&(o=c,l=n.document,e=!1,i=function(){e||(e=!0,o())},(a=function(){try{l.documentElement.doScroll("left")}catch(c){return void setTimeout(a,50)}i()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,i())});var o,l,e,i,a}(function(){var c,t;(c=document.createElement("div")).innerHTML=o,o=null,(t=c.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",function(c,t){t.firstChild?function(c,t){t.parentNode.insertBefore(c,t)}(c,t.firstChild):t.appendChild(c)}(t,document.body))})}(window); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------