├── .prettierignore
├── .editorconfig
├── images
├── demo.jpg
├── folder.png
├── intro.png
├── wechat.png
└── screenshot.png
├── fonts
└── icomoon.woff
├── .prettierrc
├── background.js
├── lib
├── codemirror
│ ├── lib
│ │ ├── util
│ │ │ ├── simple-hint.css
│ │ │ ├── dialog.css
│ │ │ ├── runmode.js
│ │ │ ├── loadmode.js
│ │ │ ├── continuelist.js
│ │ │ ├── match-highlighter.js
│ │ │ ├── overlay.js
│ │ │ ├── dialog.js
│ │ │ ├── xml-hint.js
│ │ │ ├── multiplex.js
│ │ │ ├── runmode-standalone.js
│ │ │ ├── simple-hint.js
│ │ │ ├── pig-hint.js
│ │ │ ├── searchcursor.js
│ │ │ ├── javascript-hint.js
│ │ │ ├── search.js
│ │ │ ├── closetag.js
│ │ │ ├── foldcode.js
│ │ │ └── formatting.js
│ │ └── codemirror.css
│ ├── gfm
│ │ ├── index.html
│ │ └── gfm.js
│ ├── xml
│ │ └── index.html
│ ├── javascript
│ │ └── index.html
│ ├── overlay.js
│ ├── htmlmixed
│ │ └── htmlmixed.js
│ └── markdown
│ │ └── index.html
└── md5.js
├── package.json
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── content_script
├── wxtoken.js
├── blog.inject.js
└── weekly.inject.js
├── manifest.json
├── README.md
├── scripts
├── getContent.js
├── imgReduce.js
├── common.js
├── editor.js
└── weeklyInjector.js
├── styles
├── base16-light.css
├── prism-funky.css
├── prism-tomorrow.css
├── prism-okaidia.css
├── prism-dark.css
├── prism.css
├── prism-solarizedlight.css
├── prism-xonokai.css
└── prism-twilight.css
└── index.html
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
--------------------------------------------------------------------------------
/images/demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/images/demo.jpg
--------------------------------------------------------------------------------
/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/fonts/icomoon.woff
--------------------------------------------------------------------------------
/images/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/images/folder.png
--------------------------------------------------------------------------------
/images/intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/images/intro.png
--------------------------------------------------------------------------------
/images/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/images/wechat.png
--------------------------------------------------------------------------------
/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngusFu/wx-platform-editor/HEAD/images/screenshot.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "trailingComma": "none",
4 | "tabWidth": 2,
5 | "semi": false,
6 | "singleQuote": true,
7 | "useTabs": false,
8 | "bracketSpacing": true,
9 | "insertPragma": false,
10 | "arrowParens": "avoid",
11 | "htmlWhitespaceSensitivity": "ignore"
12 | }
13 |
--------------------------------------------------------------------------------
/background.js:
--------------------------------------------------------------------------------
1 | chrome.browserAction.onClicked.addListener(function updateIcon() {
2 | chrome.tabs.query(
3 | {
4 | url: new URL('./index.html', location.href).href
5 | },
6 | function(tabs) {
7 | if (!tabs.length) {
8 | chrome.tabs.create({
9 | url: './index.html'
10 | })
11 | } else {
12 | chrome.tabs.update(tabs[0].id, {
13 | selected: true
14 | })
15 | }
16 | }
17 | )
18 | })
19 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/simple-hint.css:
--------------------------------------------------------------------------------
1 | .CodeMirror-completions {
2 | position: absolute;
3 | z-index: 10;
4 | overflow: hidden;
5 | -webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
6 | -moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
7 | box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
8 | }
9 | .CodeMirror-completions select {
10 | background: #fafafa;
11 | outline: none;
12 | border: none;
13 | padding: 0;
14 | margin: 0;
15 | font-family: monospace;
16 | }
17 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/dialog.css:
--------------------------------------------------------------------------------
1 | .CodeMirror-dialog {
2 | position: relative;
3 | }
4 |
5 | .CodeMirror-dialog > div {
6 | position: absolute;
7 | top: 0;
8 | left: 0;
9 | right: 0;
10 | background: white;
11 | border-bottom: 1px solid #eee;
12 | z-index: 15;
13 | padding: 0.1em 0.8em;
14 | overflow: hidden;
15 | color: #333;
16 | }
17 |
18 | .CodeMirror-dialog input {
19 | border: none;
20 | outline: none;
21 | background: transparent;
22 | width: 20em;
23 | color: inherit;
24 | font-family: monospace;
25 | }
26 |
27 | .CodeMirror-dialog button {
28 | font-size: 70%;
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-platform-editor-master",
3 | "version": "2.0.0",
4 | "description": "微信公众号编辑助手插件",
5 | "main": "background.js",
6 | "directories": {
7 | "lib": "lib"
8 | },
9 | "scripts": {
10 | "format": "pretty-quick --pattern \"**/*.*(js|jsx|scss|css)\""
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/AngusFu/wx-platform-editor.git"
15 | },
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/AngusFu/wx-platform-editor/issues"
20 | },
21 | "homepage": "https://github.com/AngusFu/wx-platform-editor#readme",
22 | "devDependencies": {
23 | "prettier": "^1.18.2",
24 | "pretty-quick": "^1.11.1"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # OSX
40 | .DS_Store
41 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // 将设置放入此文件中以覆盖默认设置
2 | {
3 | "editor.insertSpaces": true,
4 | "editor.fontSize": 18,
5 | "editor.wrappingColumn": 100,
6 | "editor.mouseWheelScrollSensitivity": 2,
7 | "editor.renderWhitespace": "all",
8 | "editor.renderIndentGuides": true,
9 | "editor.renderControlCharacters": true,
10 | "editor.tabSize": 2,
11 | "typescript.check.tscVersion": false,
12 | "emmet.preferences": {
13 | "css.autoInsertVendorPrefixes": false,
14 | "scss.autoInsertVendorPrefixes": false,
15 | "sass.autoInsertVendorPrefixes": false,
16 | "less.autoInsertVendorPrefixes": false
17 | },
18 | "files.associations": {
19 | "*.html": "html",
20 | "*.css": "css",
21 | "*.demo": "html"
22 | },
23 | "window.zoomLevel": -1,
24 | "vsicons.dontShowNewVersionMessage": true,
25 | "jshint.enable": false
26 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 文蔺
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/content_script/wxtoken.js:
--------------------------------------------------------------------------------
1 | /**
2 | * get wexin token
3 | * so that we can open editor directly
4 | * using a template string
5 | */
6 | let url = new URL(location.href)
7 | let params = new URLSearchParams(url.search)
8 | let type = params.get('t')
9 | let token = params.get('token')
10 |
11 | let hasError =
12 | document.querySelector('.page_error') ||
13 | document.querySelector('.page_timeout')
14 | let isSendMsg = /masssendpage\?/.test(location.href)
15 | let isEditor = /media\/appmsg_edit/.test(type)
16 |
17 | // any wexin page that contains token
18 | // except editor page
19 | if (token && !hasError && !isEditor && !isSendMsg) {
20 | console.log(token)
21 |
22 | const onMessage = {
23 | noop() {},
24 |
25 | shakehands() {
26 | chrome.runtime.sendMessage(null, {
27 | type: 'token',
28 | token
29 | })
30 | }
31 | }
32 |
33 | // listen for handshake
34 | chrome.runtime.onMessage.addListener(function(message) {
35 | console.info(message)
36 | ;(onMessage[message.type] || onMessage['noop'])(message)
37 | })
38 |
39 | // send message at once
40 | chrome.runtime.sendMessage(null, {
41 | type: 'token',
42 | token
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "微信公众号编辑助手",
3 | "description": "weixin public platform editor",
4 | "version": "1.0.3",
5 | "permissions": [
6 | "tabs",
7 | "activeTab",
8 | "cookies",
9 | "storage",
10 | "webNavigation",
11 | "clipboardRead",
12 | "clipboardWrite",
13 | "\u003Call_urls\u003E"
14 | ],
15 | "browser_action": {
16 | "default_icon": "./images/wechat.png"
17 | },
18 | "icons": {
19 | "128": "./images/wechat.png"
20 | },
21 | "content_scripts": [{
22 | "run_at": "document_end",
23 | "matches": ["https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit*"],
24 | "js": ["./content_script/blog.inject.js", "./content_script/weekly.inject.js"]
25 | }, {
26 | "run_at": "document_end",
27 | "matches": ["https://mp.weixin.qq.com/cgi-bin/*"],
28 | "js": ["./content_script/wxtoken.js"]
29 | }],
30 | "background": {
31 | "scripts": ["./background.js"]
32 | },
33 | "options_page": "./index.html",
34 | "manifest_version": 2,
35 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
36 | }
--------------------------------------------------------------------------------
/lib/codemirror/gfm/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CodeMirror: GFM mode
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | CodeMirror: GFM mode
18 |
19 |
20 |
37 |
38 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 介绍下最近刚刚完成的简单的微信公众号编辑助手插件,可用于抓取博客,生成 `Markdown`,经过渲染后,可以注入一键注入到公众号编辑器:帮你省去 C+V 步骤,也无需从其他网页保存图片然后再手动上传。
2 |
3 | 当然,也可以当作代码高亮的工具来使用。如果您只需要高亮功能,建议访问 [https://angusfu.github.io/wx-editor/](https://angusfu.github.io/wx-editor/)。
4 |
5 | 
6 |
7 | ## Features
8 | - 专为微信图文推送定制,`Markdown` 编辑省心省力
9 | - 代码高亮保证好用,七种 Prims 代码主题任你选
10 | - 踩过的坑足够多,成功避开微信过滤机制造成视觉效果偏离预期
11 | - **无需 CV 大法**,一键帮你写入微信
12 | - **无需繁杂的保存图片、上传流程**,替你完成全部上传工作
13 | - 直接抓取[众成翻译](http://www.zcfy.cc/)的 Markdown,快速便捷
14 | - 使用 [Mercury Web Parser](https://mercury.postlight.com/) 抓取任意博客文章页
15 | - 内置[奇舞周刊](https://weekly.75team.com)编辑工具
16 |
17 | ## 使用方式
18 | 1. 下载 zip 压缩包到 PC 本地,并解压
19 | 2. 在 Chrome 中打开扩展程序设置页 [chrome://extensions/](chrome://extensions/)
20 | 3. 右上角勾选`开发者模式`
21 | 4. 点击`加载已解压的扩展程序`按钮,选择刚刚解压得到的文件夹
22 | 5. 完成,记得启用扩展程序哟~
23 | 6. 需要使用本插件的时候,请点击对应的图标:
24 |
25 | ## 选项说明
26 | - **New**:点击后会弹出输入框,可以输入新的文章地址
27 | - **Code Style**: 用于更换代码高亮主题
28 | - **Weekly**:奇舞周刊编辑工具
29 | - **Copy**:编辑完成后,将带有样式的代码复制到剪切板
30 | - **Inject**:编辑完成后,点击`Inject`即可
31 |
32 | 
33 |
34 | ## 注意事项
35 | 1. 所有图片尽量不超过 **2 M**
36 | 2. 仅支持 `jpg, gif, png` 等格式
37 | 3. **不支持** `webp, svg` 等其他格式
38 | 4. 发生问题的图片,在插入微信编辑器中时,会以默认图替代
39 | 5. 编辑完成后,请务必到微信中预览...
40 | 6. 编辑时请随时做好**整理代码格式**的准备(虽然程序真的已经帮你做了一部分)
41 | 7. 如果只需要代码高亮功能,请访问 [https://angusfu.github.io/wx-editor/](https://angusfu.github.io/wx-editor/)
42 |
43 | ## DEMO
44 |
45 | 
46 |
47 | ## TODO
48 |
49 | - 外站相对路径解析(主要是解决图片的问题): 暂不解决
50 | - 编辑器暂不支持行内 HTML 标签高亮(需要进一步学习 CodeMirror)
51 | - 微信公众号后台页面匹配需要更加精准
52 | - 应该能够允许自定义样式
53 | - 缓存问题,需要考虑容量 & 过期的问题
54 |
--------------------------------------------------------------------------------
/lib/codemirror/xml/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CodeMirror: XML mode
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | CodeMirror: XML mode
14 |
15 | <html style="color: green">
16 | <!-- this is a comment -->
17 | <head>
18 | <title>HTML Example</title>
19 | </head>
20 | <body>
21 | The indentation tries to be <em>somewhat "do what
22 | I mean"</em>... but might not match your style.
23 | </body>
24 | </html>
25 |
26 |
32 | The XML mode supports two configuration parameters:
33 |
34 | htmlMode (boolean)
35 | This switches the mode to parse HTML instead of XML. This
36 | means attributes do not have to be quoted, and some elements
37 | (such as br) do not require a closing tag.
38 | alignCDATA (boolean)
39 | Setting this to true will force the opening tag of CDATA
40 | blocks to not be indented.
41 |
42 |
43 | MIME types defined: application/xml, text/html.
44 |
45 |
46 |
--------------------------------------------------------------------------------
/scripts/getContent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * zcfy related utils
3 | */
4 | const zcfyRegex = /^https?:\/\/(www\.)?zcfy\.cc\/article\//
5 | const isZcfyURL = url => zcfyRegex.test(url)
6 |
7 | /**
8 | * fetch content by postlight
9 | */
10 | const fetchByPostLight = url => {
11 | let addr = `https://mercury.postlight.com/parser?url=${url}`
12 |
13 | return fetch(addr, {
14 | headers: {
15 | 'Content-Type': 'application/json',
16 | 'x-api-key': '4zZPsARTwZKZpHqMBtyKdMhdbxzI7rrptlu6kA8V'
17 | }
18 | }).catch(e => {
19 | console.error(e)
20 | return null
21 | })
22 | }
23 |
24 | /**
25 | * deal with text contents
26 | */
27 | const getMarkdownContent = url => {
28 | let config = {
29 | type: 'html',
30 | origUrl: url,
31 | url
32 | }
33 |
34 | if (isZcfyURL(url)) {
35 | // article page
36 | let reId = /zcfy\.cc\/article\/([^\/]+)/
37 | let id = url.match(reId)[1]
38 | let api = `http://www.zcfy.cc/api/articleDetail?token=7eec4e73-23e3-435f-a1fd-3b7f2368850a&id=${id}`
39 |
40 | return fetch(api)
41 | .then(r => r.json())
42 | .then(obj => {
43 | var data = obj.data
44 |
45 | return {
46 | title: data.title,
47 | author: data.translator_nickname || data.translator_name,
48 | url: data.seo_url_base,
49 | content: data.content,
50 | type: 'md'
51 | }
52 | })
53 | }
54 |
55 | return fetchByPostLight(url)
56 | .then(r => r.json())
57 | .catch(e => {
58 | console.error(e)
59 | return null
60 | })
61 | .then(obj => {
62 | if (!obj) {
63 | throw 'got null content'
64 | }
65 |
66 | let { title, author, content } = obj
67 |
68 | return {
69 | title,
70 | author,
71 | url: url,
72 | content: generateMdText(content),
73 | type: 'html'
74 | }
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/runmode.js:
--------------------------------------------------------------------------------
1 | CodeMirror.runMode = function(string, modespec, callback, options) {
2 | function esc(str) {
3 | return str.replace(/[<&]/, function(ch) {
4 | return ch == '<' ? '<' : '&'
5 | })
6 | }
7 |
8 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec)
9 | var isNode = callback.nodeType == 1
10 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize
11 | if (isNode) {
12 | var node = callback,
13 | accum = [],
14 | col = 0
15 | callback = function(text, style) {
16 | if (text == '\n') {
17 | accum.push(' ')
18 | col = 0
19 | return
20 | }
21 | var escaped = ''
22 | // HTML-escape and replace tabs
23 | for (var pos = 0; ; ) {
24 | var idx = text.indexOf('\t', pos)
25 | if (idx == -1) {
26 | escaped += esc(text.slice(pos))
27 | col += text.length - pos
28 | break
29 | } else {
30 | col += idx - pos
31 | escaped += esc(text.slice(pos, idx))
32 | var size = tabSize - (col % tabSize)
33 | col += size
34 | for (var i = 0; i < size; ++i) escaped += ' '
35 | pos = idx + 1
36 | }
37 | }
38 |
39 | if (style)
40 | accum.push('' + escaped + ' ')
41 | else accum.push(escaped)
42 | }
43 | }
44 | var lines = CodeMirror.splitLines(string),
45 | state = CodeMirror.startState(mode)
46 | for (var i = 0, e = lines.length; i < e; ++i) {
47 | if (i) callback('\n')
48 | var stream = new CodeMirror.StringStream(lines[i])
49 | while (!stream.eol()) {
50 | var style = mode.token(stream, state)
51 | callback(stream.current(), style, i, stream.start)
52 | stream.start = stream.pos
53 | }
54 | }
55 | if (isNode) node.innerHTML = accum.join('')
56 | }
57 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/loadmode.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | if (!CodeMirror.modeURL) CodeMirror.modeURL = '../mode/%N/%N.js'
3 |
4 | var loading = {}
5 | function splitCallback(cont, n) {
6 | var countDown = n
7 | return function() {
8 | if (--countDown == 0) cont()
9 | }
10 | }
11 | function ensureDeps(mode, cont) {
12 | var deps = CodeMirror.modes[mode].dependencies
13 | if (!deps) return cont()
14 | var missing = []
15 | for (var i = 0; i < deps.length; ++i) {
16 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
17 | }
18 | if (!missing.length) return cont()
19 | var split = splitCallback(cont, missing.length)
20 | for (var i = 0; i < missing.length; ++i)
21 | CodeMirror.requireMode(missing[i], split)
22 | }
23 |
24 | CodeMirror.requireMode = function(mode, cont) {
25 | if (typeof mode != 'string') mode = mode.name
26 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
27 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
28 |
29 | var script = document.createElement('script')
30 | script.src = CodeMirror.modeURL.replace(/%N/g, mode)
31 | var others = document.getElementsByTagName('script')[0]
32 | others.parentNode.insertBefore(script, others)
33 | var list = (loading[mode] = [cont])
34 | var count = 0,
35 | poll = setInterval(function() {
36 | if (++count > 100) return clearInterval(poll)
37 | if (CodeMirror.modes.hasOwnProperty(mode)) {
38 | clearInterval(poll)
39 | loading[mode] = null
40 | ensureDeps(mode, function() {
41 | for (var i = 0; i < list.length; ++i) list[i]()
42 | })
43 | }
44 | }, 200)
45 | }
46 |
47 | CodeMirror.autoLoadMode = function(instance, mode) {
48 | if (!CodeMirror.modes.hasOwnProperty(mode))
49 | CodeMirror.requireMode(mode, function() {
50 | instance.setOption('mode', instance.getOption('mode'))
51 | })
52 | }
53 | })()
54 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/continuelist.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | ;(function(mod) {
5 | if (typeof exports == 'object' && typeof module == 'object')
6 | // CommonJS
7 | mod(require('../../lib/codemirror'))
8 | else if (typeof define == 'function' && define.amd)
9 | // AMD
10 | define(['../../lib/codemirror'], mod)
11 | // Plain browser env
12 | else mod(CodeMirror)
13 | })(function(CodeMirror) {
14 | 'use strict'
15 |
16 | var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)\.)(\s*)/,
17 | emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)\.)(\s*)$/,
18 | unorderedListRE = /[*+-]\s/
19 |
20 | CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
21 | if (cm.getOption('disableInput')) return CodeMirror.Pass
22 | var ranges = cm.listSelections(),
23 | replacements = []
24 | for (var i = 0; i < ranges.length; i++) {
25 | var pos = ranges[i].head
26 | var eolState = cm.getStateAfter(pos.line)
27 | var inList = eolState.list !== false
28 | var inQuote = eolState.quote !== 0
29 |
30 | var line = cm.getLine(pos.line),
31 | match = listRE.exec(line)
32 | if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
33 | cm.execCommand('newlineAndIndent')
34 | return
35 | }
36 | if (emptyListRE.test(line)) {
37 | cm.replaceRange(
38 | '',
39 | {
40 | line: pos.line,
41 | ch: 0
42 | },
43 | {
44 | line: pos.line,
45 | ch: pos.ch + 1
46 | }
47 | )
48 | replacements[i] = '\n'
49 | } else {
50 | var indent = match[1],
51 | after = match[4]
52 | var bullet =
53 | unorderedListRE.test(match[2]) || match[2].indexOf('>') >= 0
54 | ? match[2]
55 | : parseInt(match[3], 10) + 1 + '.'
56 |
57 | replacements[i] = '\n' + indent + bullet + after
58 | }
59 | }
60 |
61 | cm.replaceSelections(replacements)
62 | }
63 | })
64 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/match-highlighter.js:
--------------------------------------------------------------------------------
1 | // Define match-highlighter commands. Depends on searchcursor.js
2 | // Use by attaching the following function call to the onCursorActivity event:
3 | //myCodeMirror.matchHighlight(minChars);
4 | // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
5 |
6 | ;(function() {
7 | var DEFAULT_MIN_CHARS = 2
8 |
9 | function MatchHighlightState() {
10 | this.marked = []
11 | }
12 | function getMatchHighlightState(cm) {
13 | return (
14 | cm._matchHighlightState ||
15 | (cm._matchHighlightState = new MatchHighlightState())
16 | )
17 | }
18 |
19 | function clearMarks(cm) {
20 | var state = getMatchHighlightState(cm)
21 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear()
22 | state.marked = []
23 | }
24 |
25 | function markDocument(cm, className, minChars) {
26 | clearMarks(cm)
27 | minChars = typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS
28 | if (
29 | cm.somethingSelected() &&
30 | cm.getSelection().replace(/^\s+|\s+$/g, '').length >= minChars
31 | ) {
32 | var state = getMatchHighlightState(cm)
33 | var query = cm.getSelection()
34 | cm.operation(function() {
35 | if (cm.lineCount() < 2000) {
36 | // This is too expensive on big documents.
37 | for (var cursor = cm.getSearchCursor(query); cursor.findNext(); ) {
38 | //Only apply matchhighlight to the matches other than the one actually selected
39 | if (
40 | !(
41 | cursor.from().line === cm.getCursor(true).line &&
42 | cursor.from().ch === cm.getCursor(true).ch
43 | )
44 | )
45 | state.marked.push(
46 | cm.markText(cursor.from(), cursor.to(), className)
47 | )
48 | }
49 | }
50 | })
51 | }
52 | }
53 |
54 | CodeMirror.defineExtension('matchHighlight', function(className, minChars) {
55 | markDocument(this, className, minChars)
56 | })
57 | })()
58 |
--------------------------------------------------------------------------------
/scripts/imgReduce.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | function dataURItoBlob(dataURI) {
3 | var byteString, mimestring
4 |
5 | if (dataURI.split(',')[0].indexOf('base64') !== -1) {
6 | byteString = atob(dataURI.split(',')[1])
7 | } else {
8 | byteString = decodeURI(dataURI.split(',')[1])
9 | }
10 |
11 | mimestring = dataURI
12 | .split(',')[0]
13 | .split(':')[1]
14 | .split(';')[0]
15 |
16 | var content = new Array()
17 | for (var i = 0; i < byteString.length; i++) {
18 | content[i] = byteString.charCodeAt(i)
19 | }
20 |
21 | var blobImg,
22 | arrays = new Uint8Array(content)
23 | try {
24 | blobImg = new Blob([arrays], {
25 | type: mimestring
26 | })
27 | } catch (e) {}
28 | return blobImg
29 | }
30 |
31 | function imgScale(src, opt, cbk) {
32 | if (!src) return cbk(false)
33 | var _canvas = document.createElement('canvas')
34 | var tImg = new Image()
35 | tImg.onload = function() {
36 | var _context = _canvas.getContext('2d'),
37 | tw = this.width * opt.scale,
38 | th = this.height * opt.scale
39 | _canvas.width = tw
40 | _canvas.height = th
41 |
42 | _context.drawImage(tImg, 0, 0, tw, th)
43 |
44 | src = _canvas.toDataURL(opt.type, opt.quality)
45 | var blob = dataURItoBlob(src)
46 | cbk(blob)
47 | }
48 | tImg.src = src
49 | }
50 |
51 | function imageReduce(file, cbk, opts) {
52 | var opt = Object.assign(
53 | {},
54 | {
55 | scale: 0.9,
56 | quality: 0.99,
57 | type: 'image/jpeg'
58 | },
59 | opts || {}
60 | )
61 |
62 | var fReader = new FileReader()
63 | fReader.onload = function(e) {
64 | var result = e.target.result
65 | imgScale(result, opt, function(file) {
66 | cbk(file)
67 | })
68 | }
69 | fReader.readAsDataURL(file)
70 | }
71 |
72 | window.imgReduce = function(files, opt) {
73 | return new Promise(resolve => {
74 | imageReduce(
75 | files,
76 | files => {
77 | resolve(files)
78 | },
79 | opt
80 | )
81 | })
82 | }
83 | })()
84 |
--------------------------------------------------------------------------------
/styles/base16-light.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Name: Base16 Default Light
4 | Author: Chris Kempson (http://chriskempson.com)
5 |
6 | CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools)
7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
8 |
9 | */
10 |
11 | .cm-s-base16-light.CodeMirror {
12 | background: #f5f5f5;
13 | color: #202020;
14 | }
15 | .cm-s-base16-light div.CodeMirror-selected {
16 | background: #e0e0e0 !important;
17 | }
18 | .cm-s-base16-light.CodeMirror ::selection {
19 | background: #e0e0e0;
20 | }
21 | .cm-s-base16-light.CodeMirror ::-moz-selection {
22 | background: #e0e0e0;
23 | }
24 | .cm-s-base16-light .CodeMirror-gutters {
25 | background: #f5f5f5;
26 | border-right: 0px;
27 | }
28 | .cm-s-base16-light .CodeMirror-guttermarker {
29 | color: #ac4142;
30 | }
31 | .cm-s-base16-light .CodeMirror-guttermarker-subtle {
32 | color: #b0b0b0;
33 | }
34 | .cm-s-base16-light .CodeMirror-linenumber {
35 | color: #b0b0b0;
36 | }
37 | .cm-s-base16-light .CodeMirror-cursor {
38 | border-left: 1px solid #505050 !important;
39 | }
40 |
41 | .cm-s-base16-light span.cm-comment {
42 | color: #8f5536;
43 | }
44 | .cm-s-base16-light span.cm-atom {
45 | color: #aa759f;
46 | }
47 | .cm-s-base16-light span.cm-number {
48 | color: #aa759f;
49 | }
50 |
51 | .cm-s-base16-light span.cm-property,
52 | .cm-s-base16-light span.cm-attribute {
53 | color: #90a959;
54 | }
55 | .cm-s-base16-light span.cm-keyword {
56 | color: #ac4142;
57 | }
58 | .cm-s-base16-light span.cm-string {
59 | color: #f4bf75;
60 | }
61 |
62 | .cm-s-base16-light span.cm-variable {
63 | color: #90a959;
64 | }
65 | .cm-s-base16-light span.cm-variable-2 {
66 | color: #6a9fb5;
67 | }
68 | .cm-s-base16-light span.cm-def {
69 | color: #d28445;
70 | }
71 | .cm-s-base16-light span.cm-bracket {
72 | color: #202020;
73 | }
74 | .cm-s-base16-light span.cm-tag {
75 | color: #ac4142;
76 | }
77 | .cm-s-base16-light span.cm-link {
78 | color: #aa759f;
79 | }
80 | .cm-s-base16-light span.cm-error {
81 | background: #ac4142;
82 | color: #505050;
83 | }
84 |
85 | .cm-s-base16-light .CodeMirror-activeline-background {
86 | background: #dddcdc !important;
87 | }
88 | .cm-s-base16-light .CodeMirror-matchingbracket {
89 | text-decoration: underline;
90 | color: white !important;
91 | }
92 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/overlay.js:
--------------------------------------------------------------------------------
1 | // Utility function that allows modes to be combined. The mode given
2 | // as the base argument takes care of most of the normal mode
3 | // functionality, but a second (typically simple) mode is used, which
4 | // can override the style of text. Both modes get to parse all of the
5 | // text, but when both assign a non-null style to a piece of code, the
6 | // overlay wins, unless the combine argument was true, in which case
7 | // the styles are combined.
8 |
9 | // overlayParser is the old, deprecated name
10 | CodeMirror.overlayMode = CodeMirror.overlayParser = function(
11 | base,
12 | overlay,
13 | combine
14 | ) {
15 | return {
16 | startState: function() {
17 | return {
18 | base: CodeMirror.startState(base),
19 | overlay: CodeMirror.startState(overlay),
20 | basePos: 0,
21 | baseCur: null,
22 | overlayPos: 0,
23 | overlayCur: null
24 | }
25 | },
26 | copyState: function(state) {
27 | return {
28 | base: CodeMirror.copyState(base, state.base),
29 | overlay: CodeMirror.copyState(overlay, state.overlay),
30 | basePos: state.basePos,
31 | baseCur: null,
32 | overlayPos: state.overlayPos,
33 | overlayCur: null
34 | }
35 | },
36 |
37 | token: function(stream, state) {
38 | if (stream.start == state.basePos) {
39 | state.baseCur = base.token(stream, state.base)
40 | state.basePos = stream.pos
41 | }
42 | if (stream.start == state.overlayPos) {
43 | stream.pos = stream.start
44 | state.overlayCur = overlay.token(stream, state.overlay)
45 | state.overlayPos = stream.pos
46 | }
47 | stream.pos = Math.min(state.basePos, state.overlayPos)
48 | if (stream.eol()) state.basePos = state.overlayPos = 0
49 |
50 | if (state.overlayCur == null) return state.baseCur
51 | if (state.baseCur != null && combine)
52 | return state.baseCur + ' ' + state.overlayCur
53 | else return state.overlayCur
54 | },
55 |
56 | indent:
57 | base.indent &&
58 | function(state, textAfter) {
59 | return base.indent(state.base, textAfter)
60 | },
61 | electricChars: base.electricChars,
62 |
63 | innerMode: function(state) {
64 | return { state: state.base, mode: base }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/styles/prism-funky.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js Funky theme
3 | * Based on “Polyfilling the gaps” talk slides http://lea.verou.me/polyfilling-the-gaps/
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*='language-'],
8 | pre[class*='language-'] {
9 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
10 | text-align: left;
11 | white-space: pre;
12 | word-spacing: normal;
13 | word-break: normal;
14 | word-wrap: normal;
15 | line-height: 1.5;
16 |
17 | -moz-tab-size: 4;
18 | -o-tab-size: 4;
19 | tab-size: 4;
20 |
21 | -webkit-hyphens: none;
22 | -moz-hyphens: none;
23 | -ms-hyphens: none;
24 | hyphens: none;
25 | }
26 |
27 | /* Code blocks */
28 | pre[class*='language-'] {
29 | padding: 0.4em 0.8em;
30 | margin: 0.5em 0;
31 | overflow: auto;
32 | background: #121558;
33 | background-size: 10px 10px;
34 | }
35 |
36 | code[class*='language-'] {
37 | background: black;
38 | color: white;
39 | box-shadow: -0.3em 0 0 0.3em black, 0.3em 0 0 0.3em black;
40 | }
41 |
42 | /* Inline code */
43 | :not(pre) > code[class*='language-'] {
44 | padding: 0.2em;
45 | border-radius: 0.3em;
46 | box-shadow: none;
47 | white-space: normal;
48 | }
49 |
50 | .token.comment,
51 | .token.prolog,
52 | .token.doctype,
53 | .token.cdata {
54 | color: #aaa;
55 | }
56 |
57 | .token.punctuation {
58 | color: #999;
59 | }
60 |
61 | .namespace {
62 | opacity: 0.7;
63 | }
64 |
65 | .token.property,
66 | .token.tag,
67 | .token.boolean,
68 | .token.number,
69 | .token.constant,
70 | .token.symbol {
71 | color: #0cf;
72 | }
73 |
74 | .token.selector,
75 | .token.attr-name,
76 | .token.string,
77 | .token.char,
78 | .token.builtin {
79 | color: yellow;
80 | }
81 |
82 | .token.operator,
83 | .token.entity,
84 | .token.url,
85 | .language-css .token.string,
86 | .toke.variable,
87 | .token.inserted {
88 | color: yellowgreen;
89 | }
90 |
91 | .token.atrule,
92 | .token.attr-value,
93 | .token.keyword {
94 | color: deeppink;
95 | }
96 |
97 | .token.regex,
98 | .token.important {
99 | color: orange;
100 | }
101 |
102 | .token.important,
103 | .token.bold {
104 | font-weight: bold;
105 | }
106 | .token.italic {
107 | font-style: italic;
108 | }
109 |
110 | .token.entity {
111 | cursor: help;
112 | }
113 |
114 | .token.deleted {
115 | color: red;
116 | }
117 |
--------------------------------------------------------------------------------
/styles/prism-tomorrow.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
3 | * Based on https://github.com/chriskempson/tomorrow-theme
4 | * @author Rose Pritchard
5 | */
6 |
7 | code[class*='language-'],
8 | pre[class*='language-'] {
9 | color: #ccc;
10 | background: none;
11 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
12 | text-align: left;
13 | white-space: pre;
14 | word-spacing: normal;
15 | word-break: normal;
16 | word-wrap: normal;
17 | line-height: 1.5;
18 |
19 | -moz-tab-size: 4;
20 | -o-tab-size: 4;
21 | tab-size: 4;
22 |
23 | -webkit-hyphens: none;
24 | -moz-hyphens: none;
25 | -ms-hyphens: none;
26 | hyphens: none;
27 | }
28 |
29 | /* Code blocks */
30 | pre[class*='language-'] {
31 | padding: 1em;
32 | margin: 0.5em 0;
33 | overflow: auto;
34 | }
35 |
36 | :not(pre) > code[class*='language-'],
37 | pre[class*='language-'] {
38 | background: #2d2d2d;
39 | }
40 |
41 | /* Inline code */
42 | :not(pre) > code[class*='language-'] {
43 | padding: 0.1em;
44 | border-radius: 0.3em;
45 | white-space: normal;
46 | }
47 |
48 | .token.comment,
49 | .token.block-comment,
50 | .token.prolog,
51 | .token.doctype,
52 | .token.cdata {
53 | color: #999;
54 | }
55 |
56 | .token.punctuation {
57 | color: #ccc;
58 | }
59 |
60 | .token.tag,
61 | .token.attr-name,
62 | .token.namespace,
63 | .token.deleted {
64 | color: #e2777a;
65 | }
66 |
67 | .token.function-name {
68 | color: #6196cc;
69 | }
70 |
71 | .token.boolean,
72 | .token.number,
73 | .token.function {
74 | color: #f08d49;
75 | }
76 |
77 | .token.property,
78 | .token.class-name,
79 | .token.constant,
80 | .token.symbol {
81 | color: #f8c555;
82 | }
83 |
84 | .token.selector,
85 | .token.important,
86 | .token.atrule,
87 | .token.keyword,
88 | .token.builtin {
89 | color: #cc99cd;
90 | }
91 |
92 | .token.string,
93 | .token.char,
94 | .token.attr-value,
95 | .token.regex,
96 | .token.variable {
97 | color: #7ec699;
98 | }
99 |
100 | .token.operator,
101 | .token.entity,
102 | .token.url {
103 | color: #67cdcc;
104 | }
105 |
106 | .token.important,
107 | .token.bold {
108 | font-weight: bold;
109 | }
110 | .token.italic {
111 | font-style: italic;
112 | }
113 |
114 | .token.entity {
115 | cursor: help;
116 | }
117 |
118 | .token.inserted {
119 | color: green;
120 | }
121 |
--------------------------------------------------------------------------------
/styles/prism-okaidia.css:
--------------------------------------------------------------------------------
1 | /**
2 | * okaidia theme for JavaScript, CSS and HTML
3 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/
4 | * @author ocodia
5 | */
6 |
7 | code[class*='language-'],
8 | pre[class*='language-'] {
9 | color: #f8f8f2;
10 | background: none;
11 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 | line-height: 1.5;
19 |
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 |
24 | -webkit-hyphens: none;
25 | -moz-hyphens: none;
26 | -ms-hyphens: none;
27 | hyphens: none;
28 | }
29 |
30 | /* Code blocks */
31 | pre[class*='language-'] {
32 | padding: 1em;
33 | margin: 0.5em 0;
34 | overflow: auto;
35 | border-radius: 0.3em;
36 | }
37 |
38 | :not(pre) > code[class*='language-'],
39 | pre[class*='language-'] {
40 | background: #272822;
41 | }
42 |
43 | /* Inline code */
44 | :not(pre) > code[class*='language-'] {
45 | padding: 0.1em;
46 | border-radius: 0.3em;
47 | white-space: normal;
48 | }
49 |
50 | .token.comment,
51 | .token.prolog,
52 | .token.doctype,
53 | .token.cdata {
54 | color: slategray;
55 | }
56 |
57 | .token.punctuation {
58 | color: #f8f8f2;
59 | }
60 |
61 | .namespace {
62 | opacity: 0.7;
63 | }
64 |
65 | .token.property,
66 | .token.tag,
67 | .token.constant,
68 | .token.symbol,
69 | .token.deleted {
70 | color: #f92672;
71 | }
72 |
73 | .token.boolean,
74 | .token.number {
75 | color: #ae81ff;
76 | }
77 |
78 | .token.selector,
79 | .token.attr-name,
80 | .token.string,
81 | .token.char,
82 | .token.builtin,
83 | .token.inserted {
84 | color: #a6e22e;
85 | }
86 |
87 | .token.operator,
88 | .token.entity,
89 | .token.url,
90 | .language-css .token.string,
91 | .style .token.string,
92 | .token.variable {
93 | color: #f8f8f2;
94 | }
95 |
96 | .token.atrule,
97 | .token.attr-value,
98 | .token.function {
99 | color: #e6db74;
100 | }
101 |
102 | .token.keyword {
103 | color: #66d9ef;
104 | }
105 |
106 | .token.regex,
107 | .token.important {
108 | color: #fd971f;
109 | }
110 |
111 | .token.important,
112 | .token.bold {
113 | font-weight: bold;
114 | }
115 | .token.italic {
116 | font-style: italic;
117 | }
118 |
119 | .token.entity {
120 | cursor: help;
121 | }
122 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/dialog.js:
--------------------------------------------------------------------------------
1 | // Open simple dialogs on top of an editor. Relies on dialog.css.
2 |
3 | ;(function() {
4 | function dialogDiv(cm, template) {
5 | var wrap = cm.getWrapperElement()
6 | var dialog = wrap.insertBefore(
7 | document.createElement('div'),
8 | wrap.firstChild
9 | )
10 | dialog.className = 'CodeMirror-dialog'
11 | dialog.innerHTML = '' + template + '
'
12 | return dialog
13 | }
14 |
15 | CodeMirror.defineExtension('openDialog', function(template, callback) {
16 | var dialog = dialogDiv(this, template)
17 | var closed = false,
18 | me = this
19 | function close() {
20 | if (closed) return
21 | closed = true
22 | dialog.parentNode.removeChild(dialog)
23 | }
24 | var inp = dialog.getElementsByTagName('input')[0],
25 | button
26 | if (inp) {
27 | CodeMirror.connect(inp, 'keydown', function(e) {
28 | if (e.keyCode == 13 || e.keyCode == 27) {
29 | CodeMirror.e_stop(e)
30 | close()
31 | me.focus()
32 | if (e.keyCode == 13) callback(inp.value)
33 | }
34 | })
35 | inp.focus()
36 | CodeMirror.connect(inp, 'blur', close)
37 | } else if ((button = dialog.getElementsByTagName('button')[0])) {
38 | CodeMirror.connect(button, 'click', function() {
39 | close()
40 | me.focus()
41 | })
42 | button.focus()
43 | CodeMirror.connect(button, 'blur', close)
44 | }
45 | return close
46 | })
47 |
48 | CodeMirror.defineExtension('openConfirm', function(template, callbacks) {
49 | var dialog = dialogDiv(this, template)
50 | var buttons = dialog.getElementsByTagName('button')
51 | var closed = false,
52 | me = this,
53 | blurring = 1
54 | function close() {
55 | if (closed) return
56 | closed = true
57 | dialog.parentNode.removeChild(dialog)
58 | me.focus()
59 | }
60 | buttons[0].focus()
61 | for (var i = 0; i < buttons.length; ++i) {
62 | var b = buttons[i]
63 | ;(function(callback) {
64 | CodeMirror.connect(b, 'click', function(e) {
65 | CodeMirror.e_preventDefault(e)
66 | close()
67 | if (callback) callback(me)
68 | })
69 | })(callbacks[i])
70 | CodeMirror.connect(b, 'blur', function() {
71 | --blurring
72 | setTimeout(function() {
73 | if (blurring <= 0) close()
74 | }, 200)
75 | })
76 | CodeMirror.connect(b, 'focus', function() {
77 | ++blurring
78 | })
79 | }
80 | })
81 | })()
82 |
--------------------------------------------------------------------------------
/styles/prism-dark.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js Dark theme for JavaScript, CSS and HTML
3 | * Based on the slides of the talk “/Reg(exp){2}lained/”
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*='language-'],
8 | pre[class*='language-'] {
9 | color: white;
10 | background: none;
11 | text-shadow: 0 -0.1em 0.2em black;
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 | line-height: 1.5;
19 |
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 |
24 | -webkit-hyphens: none;
25 | -moz-hyphens: none;
26 | -ms-hyphens: none;
27 | hyphens: none;
28 | }
29 |
30 | @media print {
31 | code[class*='language-'],
32 | pre[class*='language-'] {
33 | text-shadow: none;
34 | }
35 | }
36 |
37 | pre[class*='language-'],
38 | :not(pre) > code[class*='language-'] {
39 | background: hsl(30, 20%, 25%);
40 | }
41 |
42 | /* Code blocks */
43 | pre[class*='language-'] {
44 | padding: 1em;
45 | margin: 0.5em 0;
46 | overflow: auto;
47 | border: 0.3em solid hsl(30, 20%, 40%);
48 | border-radius: 0.5em;
49 | box-shadow: 1px 1px 0.5em black inset;
50 | }
51 |
52 | /* Inline code */
53 | :not(pre) > code[class*='language-'] {
54 | padding: 0.15em 0.2em 0.05em;
55 | border-radius: 0.3em;
56 | border: 0.13em solid hsl(30, 20%, 40%);
57 | box-shadow: 1px 1px 0.3em -0.1em black inset;
58 | white-space: normal;
59 | }
60 |
61 | .token.comment,
62 | .token.prolog,
63 | .token.doctype,
64 | .token.cdata {
65 | color: hsl(30, 20%, 50%);
66 | }
67 |
68 | .token.punctuation {
69 | opacity: 0.7;
70 | }
71 |
72 | .namespace {
73 | opacity: 0.7;
74 | }
75 |
76 | .token.property,
77 | .token.tag,
78 | .token.boolean,
79 | .token.number,
80 | .token.constant,
81 | .token.symbol {
82 | color: hsl(350, 40%, 70%);
83 | }
84 |
85 | .token.selector,
86 | .token.attr-name,
87 | .token.string,
88 | .token.char,
89 | .token.builtin,
90 | .token.inserted {
91 | color: hsl(75, 70%, 60%);
92 | }
93 |
94 | .token.operator,
95 | .token.entity,
96 | .token.url,
97 | .language-css .token.string,
98 | .style .token.string,
99 | .token.variable {
100 | color: hsl(40, 90%, 60%);
101 | }
102 |
103 | .token.atrule,
104 | .token.attr-value,
105 | .token.keyword {
106 | color: hsl(350, 40%, 70%);
107 | }
108 |
109 | .token.regex,
110 | .token.important {
111 | color: #e90;
112 | }
113 |
114 | .token.important,
115 | .token.bold {
116 | font-weight: bold;
117 | }
118 | .token.italic {
119 | font-style: italic;
120 | }
121 |
122 | .token.entity {
123 | cursor: help;
124 | }
125 |
126 | .token.deleted {
127 | color: red;
128 | }
129 |
--------------------------------------------------------------------------------
/lib/codemirror/javascript/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CodeMirror: JavaScript mode
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | CodeMirror: JavaScript mode
14 |
15 |
16 | // Demo code (the actual new parser character stream implementation)
17 |
18 | function StringStream(string) {
19 | this.pos = 0;
20 | this.string = string;
21 | }
22 |
23 | StringStream.prototype = {
24 | done: function() {return this.pos >= this.string.length;},
25 | peek: function() {return this.string.charAt(this.pos);},
26 | next: function() {
27 | if (this.pos < this.string.length)
28 | return this.string.charAt(this.pos++);
29 | },
30 | eat: function(match) {
31 | var ch = this.string.charAt(this.pos);
32 | if (typeof match == "string") var ok = ch == match;
33 | else var ok = ch && match.test ? match.test(ch) : match(ch);
34 | if (ok) {this.pos++; return ch;}
35 | },
36 | eatWhile: function(match) {
37 | var start = this.pos;
38 | while (this.eat(match));
39 | if (this.pos > start) return this.string.slice(start, this.pos);
40 | },
41 | backUp: function(n) {this.pos -= n;},
42 | column: function() {return this.pos;},
43 | eatSpace: function() {
44 | var start = this.pos;
45 | while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
46 | return this.pos - start;
47 | },
48 | match: function(pattern, consume, caseInsensitive) {
49 | if (typeof pattern == "string") {
50 | function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
51 | if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
52 | if (consume !== false) this.pos += str.length;
53 | return true;
54 | }
55 | }
56 | else {
57 | var match = this.string.slice(this.pos).match(pattern);
58 | if (match && consume !== false) this.pos += match[0].length;
59 | return match;
60 | }
61 | }
62 | };
63 |
64 |
65 |
71 |
72 | JavaScript mode supports a single configuration
73 | option, json, which will set the mode to expect JSON
74 | data rather than a JavaScript program.
75 |
76 | MIME types defined: text/javascript, application/json.
77 |
78 |
79 |
--------------------------------------------------------------------------------
/styles/prism.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js default theme for JavaScript, CSS and HTML
3 | * Based on dabblet (http://dabblet.com)
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*='language-'],
8 | pre[class*='language-'] {
9 | color: black;
10 | background: none;
11 | text-shadow: 0 1px white;
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 | line-height: 1.5;
19 |
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 |
24 | -webkit-hyphens: none;
25 | -moz-hyphens: none;
26 | -ms-hyphens: none;
27 | hyphens: none;
28 | }
29 |
30 | pre[class*='language-']::-moz-selection,
31 | pre[class*='language-'] ::-moz-selection,
32 | code[class*='language-']::-moz-selection,
33 | code[class*='language-'] ::-moz-selection {
34 | text-shadow: none;
35 | background: #b3d4fc;
36 | }
37 |
38 | pre[class*='language-']::selection,
39 | pre[class*='language-'] ::selection,
40 | code[class*='language-']::selection,
41 | code[class*='language-'] ::selection {
42 | text-shadow: none;
43 | background: #b3d4fc;
44 | }
45 |
46 | @media print {
47 | code[class*='language-'],
48 | pre[class*='language-'] {
49 | text-shadow: none;
50 | }
51 | }
52 |
53 | /* Code blocks */
54 | pre[class*='language-'] {
55 | padding: 1em;
56 | margin: 0.5em 0;
57 | overflow: auto;
58 | }
59 |
60 | :not(pre) > code[class*='language-'],
61 | pre[class*='language-'] {
62 | background: #f5f2f0;
63 | }
64 |
65 | /* Inline code */
66 | :not(pre) > code[class*='language-'] {
67 | padding: 0.1em;
68 | border-radius: 0.3em;
69 | white-space: normal;
70 | }
71 |
72 | .token.comment,
73 | .token.prolog,
74 | .token.doctype,
75 | .token.cdata {
76 | color: slategray;
77 | }
78 |
79 | .token.punctuation {
80 | color: #999;
81 | }
82 |
83 | .namespace {
84 | opacity: 0.7;
85 | }
86 |
87 | .token.property,
88 | .token.tag,
89 | .token.boolean,
90 | .token.number,
91 | .token.constant,
92 | .token.symbol,
93 | .token.deleted {
94 | color: #905;
95 | }
96 |
97 | .token.selector,
98 | .token.attr-name,
99 | .token.string,
100 | .token.char,
101 | .token.builtin,
102 | .token.inserted {
103 | color: #690;
104 | }
105 |
106 | .token.operator,
107 | .token.entity,
108 | .token.url,
109 | .language-css .token.string,
110 | .style .token.string {
111 | color: #a67f59;
112 | background: hsla(0, 0%, 100%, 0.5);
113 | }
114 |
115 | .token.atrule,
116 | .token.attr-value,
117 | .token.keyword {
118 | color: #07a;
119 | }
120 |
121 | .token.function {
122 | color: #dd4a68;
123 | }
124 |
125 | .token.regex,
126 | .token.important,
127 | .token.variable {
128 | color: #e90;
129 | }
130 |
131 | .token.important,
132 | .token.bold {
133 | font-weight: bold;
134 | }
135 | .token.italic {
136 | font-style: italic;
137 | }
138 |
139 | .token.entity {
140 | cursor: help;
141 | }
142 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/xml-hint.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | CodeMirror.xmlHints = []
3 |
4 | CodeMirror.xmlHint = function(cm, simbol) {
5 | if (simbol.length > 0) {
6 | var cursor = cm.getCursor()
7 | cm.replaceSelection(simbol)
8 | cursor = { line: cursor.line, ch: cursor.ch + 1 }
9 | cm.setCursor(cursor)
10 | }
11 |
12 | // dirty hack for simple-hint to receive getHint event on space
13 | var getTokenAt = editor.getTokenAt
14 |
15 | editor.getTokenAt = function() {
16 | return 'disabled'
17 | }
18 | CodeMirror.simpleHint(cm, getHint)
19 |
20 | editor.getTokenAt = getTokenAt
21 | }
22 |
23 | var getHint = function(cm) {
24 | var cursor = cm.getCursor()
25 |
26 | if (cursor.ch > 0) {
27 | var text = cm.getRange({ line: 0, ch: 0 }, cursor)
28 | var typed = ''
29 | var simbol = ''
30 | for (var i = text.length - 1; i >= 0; i--) {
31 | if (text[i] == ' ' || text[i] == '<') {
32 | simbol = text[i]
33 | break
34 | } else {
35 | typed = text[i] + typed
36 | }
37 | }
38 |
39 | text = text.slice(0, text.length - typed.length)
40 |
41 | var path = getActiveElement(cm, text) + simbol
42 | var hints = CodeMirror.xmlHints[path]
43 |
44 | if (typeof hints === 'undefined') hints = ['']
45 | else {
46 | hints = hints.slice(0)
47 | for (var i = hints.length - 1; i >= 0; i--) {
48 | if (hints[i].indexOf(typed) != 0) hints.splice(i, 1)
49 | }
50 | }
51 |
52 | return {
53 | list: hints,
54 | from: { line: cursor.line, ch: cursor.ch - typed.length },
55 | to: cursor
56 | }
57 | }
58 | }
59 |
60 | var getActiveElement = function(codeMirror, text) {
61 | var element = ''
62 |
63 | if (text.length >= 0) {
64 | var regex = new RegExp('<([^!?][^\\s/>]*).*?>', 'g')
65 |
66 | var matches = []
67 | var match
68 | while ((match = regex.exec(text)) != null) {
69 | matches.push({
70 | tag: match[1],
71 | selfclose: match[0].slice(match[0].length - 2) === '/>'
72 | })
73 | }
74 |
75 | for (var i = matches.length - 1, skip = 0; i >= 0; i--) {
76 | var item = matches[i]
77 |
78 | if (item.tag[0] == '/') {
79 | skip++
80 | } else if (item.selfclose == false) {
81 | if (skip > 0) {
82 | skip--
83 | } else {
84 | element = '<' + item.tag + '>' + element
85 | }
86 | }
87 | }
88 |
89 | element += getOpenTag(text)
90 | }
91 |
92 | return element
93 | }
94 |
95 | var getOpenTag = function(text) {
96 | var open = text.lastIndexOf('<')
97 | var close = text.lastIndexOf('>')
98 |
99 | if (close < open) {
100 | text = text.slice(open)
101 |
102 | if (text != '<') {
103 | var space = text.indexOf(' ')
104 | if (space < 0) space = text.indexOf('\t')
105 | if (space < 0) space = text.indexOf('\n')
106 |
107 | if (space < 0) space = text.length
108 |
109 | return text.slice(0, space)
110 | }
111 | }
112 |
113 | return ''
114 | }
115 | })()
116 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/multiplex.js:
--------------------------------------------------------------------------------
1 | CodeMirror.multiplexingMode = function(outer /*, others */) {
2 | // Others should be {open, close, mode [, delimStyle]} objects
3 | var others = Array.prototype.slice.call(arguments, 1)
4 | var n_others = others.length
5 |
6 | function indexOf(string, pattern, from) {
7 | if (typeof pattern == 'string') return string.indexOf(pattern, from)
8 | var m = pattern.exec(from ? string.slice(from) : string)
9 | return m ? m.index + from : -1
10 | }
11 |
12 | return {
13 | startState: function() {
14 | return {
15 | outer: CodeMirror.startState(outer),
16 | innerActive: null,
17 | inner: null
18 | }
19 | },
20 |
21 | copyState: function(state) {
22 | return {
23 | outer: CodeMirror.copyState(outer, state.outer),
24 | innerActive: state.innerActive,
25 | inner:
26 | state.innerActive &&
27 | CodeMirror.copyState(state.innerActive.mode, state.inner)
28 | }
29 | },
30 |
31 | token: function(stream, state) {
32 | if (!state.innerActive) {
33 | var cutOff = Infinity,
34 | oldContent = stream.string
35 | for (var i = 0; i < n_others; ++i) {
36 | var other = others[i]
37 | var found = indexOf(oldContent, other.open, stream.pos)
38 | if (found == stream.pos) {
39 | stream.match(other.open)
40 | state.innerActive = other
41 | state.inner = CodeMirror.startState(
42 | other.mode,
43 | outer.indent ? outer.indent(state.outer, '') : 0
44 | )
45 | return other.delimStyle
46 | } else if (found != -1 && found < cutOff) {
47 | cutOff = found
48 | }
49 | }
50 | if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff)
51 | var outerToken = outer.token(stream, state.outer)
52 | if (cutOff != Infinity) stream.string = oldContent
53 | return outerToken
54 | } else {
55 | var curInner = state.innerActive,
56 | oldContent = stream.string
57 | var found = indexOf(oldContent, curInner.close, stream.pos)
58 | if (found == stream.pos) {
59 | stream.match(curInner.close)
60 | state.innerActive = state.inner = null
61 | return curInner.delimStyle
62 | }
63 | if (found > -1) stream.string = oldContent.slice(0, found)
64 | var innerToken = curInner.mode.token(stream, state.inner)
65 | if (found > -1) stream.string = oldContent
66 | var cur = stream.current(),
67 | found = cur.indexOf(curInner.close)
68 | if (found > -1) stream.backUp(cur.length - found)
69 | return innerToken
70 | }
71 | },
72 |
73 | indent: function(state, textAfter) {
74 | var mode = state.innerActive ? state.innerActive.mode : outer
75 | if (!mode.indent) return CodeMirror.Pass
76 | return mode.indent(
77 | state.innerActive ? state.inner : state.outer,
78 | textAfter
79 | )
80 | },
81 |
82 | electricChars: outer.electricChars,
83 |
84 | innerMode: function(state) {
85 | return state.inner
86 | ? { state: state.inner, mode: state.innerActive.mode }
87 | : { state: state.outer, mode: outer }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/styles/prism-solarizedlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | Solarized Color Schemes originally by Ethan Schoonover
3 | http://ethanschoonover.com/solarized
4 |
5 | Ported for PrismJS by Hector Matos
6 | Website: https://krakendev.io
7 | Twitter Handle: https://twitter.com/allonsykraken)
8 | */
9 |
10 | /*
11 | SOLARIZED HEX
12 | --------- -------
13 | base03 #002b36
14 | base02 #073642
15 | base01 #586e75
16 | base00 #657b83
17 | base0 #839496
18 | base1 #93a1a1
19 | base2 #eee8d5
20 | base3 #fdf6e3
21 | yellow #b58900
22 | orange #cb4b16
23 | red #dc322f
24 | magenta #d33682
25 | violet #6c71c4
26 | blue #268bd2
27 | cyan #2aa198
28 | green #859900
29 | */
30 |
31 | code[class*='language-'],
32 | pre[class*='language-'] {
33 | color: #657b83; /* base00 */
34 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
35 | text-align: left;
36 | white-space: pre;
37 | word-spacing: normal;
38 | word-break: normal;
39 | word-wrap: normal;
40 |
41 | line-height: 1.5;
42 |
43 | -moz-tab-size: 4;
44 | -o-tab-size: 4;
45 | tab-size: 4;
46 |
47 | -webkit-hyphens: none;
48 | -moz-hyphens: none;
49 | -ms-hyphens: none;
50 | hyphens: none;
51 | }
52 |
53 | pre[class*='language-']::-moz-selection,
54 | pre[class*='language-'] ::-moz-selection,
55 | code[class*='language-']::-moz-selection,
56 | code[class*='language-'] ::-moz-selection {
57 | background: #073642; /* base02 */
58 | }
59 |
60 | pre[class*='language-']::selection,
61 | pre[class*='language-'] ::selection,
62 | code[class*='language-']::selection,
63 | code[class*='language-'] ::selection {
64 | background: #073642; /* base02 */
65 | }
66 |
67 | /* Code blocks */
68 | pre[class*='language-'] {
69 | padding: 1em;
70 | margin: 0.5em 0;
71 | overflow: auto;
72 | border-radius: 0.3em;
73 | }
74 |
75 | :not(pre) > code[class*='language-'],
76 | pre[class*='language-'] {
77 | background-color: #fdf6e3; /* base3 */
78 | }
79 |
80 | /* Inline code */
81 | :not(pre) > code[class*='language-'] {
82 | padding: 0.1em;
83 | border-radius: 0.3em;
84 | }
85 |
86 | .token.comment,
87 | .token.prolog,
88 | .token.doctype,
89 | .token.cdata {
90 | color: #93a1a1; /* base1 */
91 | }
92 |
93 | .token.punctuation {
94 | color: #586e75; /* base01 */
95 | }
96 |
97 | .namespace {
98 | opacity: 0.7;
99 | }
100 |
101 | .token.property,
102 | .token.tag,
103 | .token.boolean,
104 | .token.number,
105 | .token.constant,
106 | .token.symbol,
107 | .token.deleted {
108 | color: #268bd2; /* blue */
109 | }
110 |
111 | .token.selector,
112 | .token.attr-name,
113 | .token.string,
114 | .token.char,
115 | .token.builtin,
116 | .token.url,
117 | .token.inserted {
118 | color: #2aa198; /* cyan */
119 | }
120 |
121 | .token.entity {
122 | color: #657b83; /* base00 */
123 | background: #eee8d5; /* base2 */
124 | }
125 |
126 | .token.atrule,
127 | .token.attr-value,
128 | .token.keyword {
129 | color: #859900; /* green */
130 | }
131 |
132 | .token.function {
133 | color: #b58900; /* yellow */
134 | }
135 |
136 | .token.regex,
137 | .token.important,
138 | .token.variable {
139 | color: #cb4b16; /* orange */
140 | }
141 |
142 | .token.important,
143 | .token.bold {
144 | font-weight: bold;
145 | }
146 | .token.italic {
147 | font-style: italic;
148 | }
149 |
150 | .token.entity {
151 | cursor: help;
152 | }
153 |
--------------------------------------------------------------------------------
/lib/codemirror/overlay.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | // Utility function that allows modes to be combined. The mode given
5 | // as the base argument takes care of most of the normal mode
6 | // functionality, but a second (typically simple) mode is used, which
7 | // can override the style of text. Both modes get to parse all of the
8 | // text, but when both assign a non-null style to a piece of code, the
9 | // overlay wins, unless the combine argument was true and not overridden,
10 | // or state.overlay.combineTokens was true, in which case the styles are
11 | // combined.
12 |
13 | ;(function(mod) {
14 | if (typeof exports == 'object' && typeof module == 'object')
15 | // CommonJS
16 | mod(require('../../lib/codemirror'))
17 | else if (typeof define == 'function' && define.amd)
18 | // AMD
19 | define(['../../lib/codemirror'], mod)
20 | // Plain browser env
21 | else mod(CodeMirror)
22 | })(function(CodeMirror) {
23 | 'use strict'
24 |
25 | CodeMirror.overlayMode = function(base, overlay, combine) {
26 | return {
27 | startState: function() {
28 | return {
29 | base: CodeMirror.startState(base),
30 | overlay: CodeMirror.startState(overlay),
31 | basePos: 0,
32 | baseCur: null,
33 | overlayPos: 0,
34 | overlayCur: null,
35 | streamSeen: null
36 | }
37 | },
38 | copyState: function(state) {
39 | return {
40 | base: CodeMirror.copyState(base, state.base),
41 | overlay: CodeMirror.copyState(overlay, state.overlay),
42 | basePos: state.basePos,
43 | baseCur: null,
44 | overlayPos: state.overlayPos,
45 | overlayCur: null
46 | }
47 | },
48 |
49 | token: function(stream, state) {
50 | if (
51 | stream != state.streamSeen ||
52 | Math.min(state.basePos, state.overlayPos) < stream.start
53 | ) {
54 | state.streamSeen = stream
55 | state.basePos = state.overlayPos = stream.start
56 | }
57 |
58 | if (stream.start == state.basePos) {
59 | state.baseCur = base.token(stream, state.base)
60 | state.basePos = stream.pos
61 | }
62 | if (stream.start == state.overlayPos) {
63 | stream.pos = stream.start
64 | state.overlayCur = overlay.token(stream, state.overlay)
65 | state.overlayPos = stream.pos
66 | }
67 | stream.pos = Math.min(state.basePos, state.overlayPos)
68 |
69 | // state.overlay.combineTokens always takes precedence over combine,
70 | // unless set to null
71 | if (state.overlayCur == null) return state.baseCur
72 | else if (
73 | (state.baseCur != null && state.overlay.combineTokens) ||
74 | (combine && state.overlay.combineTokens == null)
75 | )
76 | return state.baseCur + ' ' + state.overlayCur
77 | else return state.overlayCur
78 | },
79 |
80 | indent:
81 | base.indent &&
82 | function(state, textAfter) {
83 | return base.indent(state.base, textAfter)
84 | },
85 | electricChars: base.electricChars,
86 |
87 | innerMode: function(state) {
88 | return { state: state.base, mode: base }
89 | },
90 |
91 | blankLine: function(state) {
92 | if (base.blankLine) base.blankLine(state.base)
93 | if (overlay.blankLine) overlay.blankLine(state.overlay)
94 | }
95 | }
96 | }
97 | })
98 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/runmode-standalone.js:
--------------------------------------------------------------------------------
1 | /* Just enough of CodeMirror to run runMode under node.js */
2 |
3 | function splitLines(string) {
4 | return string.split(/\r?\n|\r/)
5 | }
6 |
7 | function StringStream(string) {
8 | this.pos = this.start = 0
9 | this.string = string
10 | }
11 | StringStream.prototype = {
12 | eol: function() {
13 | return this.pos >= this.string.length
14 | },
15 | sol: function() {
16 | return this.pos == 0
17 | },
18 | peek: function() {
19 | return this.string.charAt(this.pos) || null
20 | },
21 | next: function() {
22 | if (this.pos < this.string.length) return this.string.charAt(this.pos++)
23 | },
24 | eat: function(match) {
25 | var ch = this.string.charAt(this.pos)
26 | if (typeof match == 'string') var ok = ch == match
27 | else var ok = ch && (match.test ? match.test(ch) : match(ch))
28 | if (ok) {
29 | ++this.pos
30 | return ch
31 | }
32 | },
33 | eatWhile: function(match) {
34 | var start = this.pos
35 | while (this.eat(match)) {}
36 | return this.pos > start
37 | },
38 | eatSpace: function() {
39 | var start = this.pos
40 | while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos
41 | return this.pos > start
42 | },
43 | skipToEnd: function() {
44 | this.pos = this.string.length
45 | },
46 | skipTo: function(ch) {
47 | var found = this.string.indexOf(ch, this.pos)
48 | if (found > -1) {
49 | this.pos = found
50 | return true
51 | }
52 | },
53 | backUp: function(n) {
54 | this.pos -= n
55 | },
56 | column: function() {
57 | return this.start
58 | },
59 | indentation: function() {
60 | return 0
61 | },
62 | match: function(pattern, consume, caseInsensitive) {
63 | if (typeof pattern == 'string') {
64 | function cased(str) {
65 | return caseInsensitive ? str.toLowerCase() : str
66 | }
67 | if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
68 | if (consume !== false) this.pos += pattern.length
69 | return true
70 | }
71 | } else {
72 | var match = this.string.slice(this.pos).match(pattern)
73 | if (match && consume !== false) this.pos += match[0].length
74 | return match
75 | }
76 | },
77 | current: function() {
78 | return this.string.slice(this.start, this.pos)
79 | }
80 | }
81 | exports.StringStream = StringStream
82 |
83 | exports.startState = function(mode, a1, a2) {
84 | return mode.startState ? mode.startState(a1, a2) : true
85 | }
86 |
87 | var modes = (exports.modes = {}),
88 | mimeModes = (exports.mimeModes = {})
89 | exports.defineMode = function(name, mode) {
90 | modes[name] = mode
91 | }
92 | exports.defineMIME = function(mime, spec) {
93 | mimeModes[mime] = spec
94 | }
95 | exports.getMode = function(options, spec) {
96 | if (typeof spec == 'string' && mimeModes.hasOwnProperty(spec))
97 | spec = mimeModes[spec]
98 | if (typeof spec == 'string')
99 | var mname = spec,
100 | config = {}
101 | else if (spec != null)
102 | var mname = spec.name,
103 | config = spec
104 | var mfactory = modes[mname]
105 | if (!mfactory) throw new Error('Unknown mode: ' + spec)
106 | return mfactory(options, config || {})
107 | }
108 |
109 | exports.runMode = function(string, modespec, callback) {
110 | var mode = exports.getMode({ indentUnit: 2 }, modespec)
111 | var lines = splitLines(string),
112 | state = exports.startState(mode)
113 | for (var i = 0, e = lines.length; i < e; ++i) {
114 | if (i) callback('\n')
115 | var stream = new exports.StringStream(lines[i])
116 | while (!stream.eol()) {
117 | var style = mode.token(stream, state)
118 | callback(stream.current(), style, i, stream.start)
119 | stream.start = stream.pos
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/styles/prism-xonokai.css:
--------------------------------------------------------------------------------
1 | /**
2 | * xonokai theme for JavaScript, CSS and HTML
3 | * based on: https://github.com/MoOx/sass-prism-theme-base by Maxime Thirouin ~ MoOx --> http://moox.fr/ , which is Loosely based on Monokai textmate theme by http://www.monokai.nl/
4 | * license: MIT; http://moox.mit-license.org/
5 | */
6 | code[class*='language-'],
7 | pre[class*='language-'] {
8 | -moz-tab-size: 2;
9 | -o-tab-size: 2;
10 | tab-size: 2;
11 | -webkit-hyphens: none;
12 | -moz-hyphens: none;
13 | -ms-hyphens: none;
14 | hyphens: none;
15 | white-space: pre;
16 | white-space: pre-wrap;
17 | word-wrap: normal;
18 | font-family: Menlo, Monaco, 'Courier New', monospace;
19 | font-size: 14px;
20 | color: #76d9e6;
21 | text-shadow: none;
22 | }
23 | pre[class*='language-'],
24 | :not(pre) > code[class*='language-'] {
25 | background: #2a2a2a;
26 | }
27 | pre[class*='language-'] {
28 | padding: 15px;
29 | border-radius: 4px;
30 | border: 1px solid #e1e1e8;
31 | overflow: auto;
32 | }
33 |
34 | pre[class*='language-'] {
35 | position: relative;
36 | }
37 | pre[class*='language-'] code {
38 | white-space: pre;
39 | display: block;
40 | }
41 |
42 | :not(pre) > code[class*='language-'] {
43 | padding: 0.15em 0.2em 0.05em;
44 | border-radius: 0.3em;
45 | border: 0.13em solid #7a6652;
46 | box-shadow: 1px 1px 0.3em -0.1em #000 inset;
47 | }
48 | .token.namespace {
49 | opacity: 0.7;
50 | }
51 | .token.comment,
52 | .token.prolog,
53 | .token.doctype,
54 | .token.cdata {
55 | color: #6f705e;
56 | }
57 | .token.operator,
58 | .token.boolean,
59 | .token.number {
60 | color: #a77afe;
61 | }
62 | .token.attr-name,
63 | .token.string {
64 | color: #e6d06c;
65 | }
66 | .token.entity,
67 | .token.url,
68 | .language-css .token.string,
69 | .style .token.string {
70 | color: #e6d06c;
71 | }
72 | .token.selector,
73 | .token.inserted {
74 | color: #a6e22d;
75 | }
76 | .token.atrule,
77 | .token.attr-value,
78 | .token.keyword,
79 | .token.important,
80 | .token.deleted {
81 | color: #ef3b7d;
82 | }
83 | .token.regex,
84 | .token.statement {
85 | color: #76d9e6;
86 | }
87 | .token.placeholder,
88 | .token.variable {
89 | color: #fff;
90 | }
91 | .token.important,
92 | .token.statement,
93 | .token.bold {
94 | font-weight: bold;
95 | }
96 | .token.punctuation {
97 | color: #bebec5;
98 | }
99 | .token.entity {
100 | cursor: help;
101 | }
102 | .token.italic {
103 | font-style: italic;
104 | }
105 |
106 | code.language-markup {
107 | color: #f9f9f9;
108 | }
109 | code.language-markup .token.tag {
110 | color: #ef3b7d;
111 | }
112 | code.language-markup .token.attr-name {
113 | color: #a6e22d;
114 | }
115 | code.language-markup .token.attr-value {
116 | color: #e6d06c;
117 | }
118 | code.language-markup .token.style,
119 | code.language-markup .token.script {
120 | color: #76d9e6;
121 | }
122 | code.language-markup .token.script .token.keyword {
123 | color: #76d9e6;
124 | }
125 |
126 | /* Line highlight plugin */
127 | pre[class*='language-'][data-line] {
128 | position: relative;
129 | padding: 1em 0 1em 3em;
130 | }
131 | pre[data-line] .line-highlight {
132 | position: absolute;
133 | left: 0;
134 | right: 0;
135 | padding: 0;
136 | margin-top: 1em;
137 | background: rgba(255, 255, 255, 0.08);
138 | pointer-events: none;
139 | line-height: inherit;
140 | white-space: pre;
141 | }
142 | pre[data-line] .line-highlight:before,
143 | pre[data-line] .line-highlight[data-end]:after {
144 | content: attr(data-start);
145 | position: absolute;
146 | top: 0.4em;
147 | left: 0.6em;
148 | min-width: 1em;
149 | padding: 0.2em 0.5em;
150 | background-color: rgba(255, 255, 255, 0.4);
151 | color: black;
152 | font: bold 65%/1 sans-serif;
153 | height: 1em;
154 | line-height: 1em;
155 | text-align: center;
156 | border-radius: 999px;
157 | text-shadow: none;
158 | box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
159 | }
160 | pre[data-line] .line-highlight[data-end]:after {
161 | content: attr(data-end);
162 | top: auto;
163 | bottom: 0.4em;
164 | }
165 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/simple-hint.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | CodeMirror.simpleHint = function(editor, getHints, givenOptions) {
3 | // Determine effective options based on given values and defaults.
4 | var options = {},
5 | defaults = CodeMirror.simpleHint.defaults
6 | for (var opt in defaults)
7 | if (defaults.hasOwnProperty(opt))
8 | options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt)
9 | ? givenOptions
10 | : defaults)[opt]
11 |
12 | function collectHints(previousToken) {
13 | // We want a single cursor position.
14 | if (editor.somethingSelected()) return
15 |
16 | var tempToken = editor.getTokenAt(editor.getCursor())
17 |
18 | // Don't show completions if token has changed and the option is set.
19 | if (
20 | options.closeOnTokenChange &&
21 | previousToken != null &&
22 | (tempToken.start != previousToken.start ||
23 | tempToken.className != previousToken.className)
24 | ) {
25 | return
26 | }
27 |
28 | var result = getHints(editor)
29 | if (!result || !result.list.length) return
30 | var completions = result.list
31 | function insert(str) {
32 | editor.replaceRange(str, result.from, result.to)
33 | }
34 | // When there is only one completion, use it directly.
35 | if (completions.length == 1) {
36 | insert(completions[0])
37 | return true
38 | }
39 |
40 | // Build the select widget
41 | var complete = document.createElement('div')
42 | complete.className = 'CodeMirror-completions'
43 | var sel = complete.appendChild(document.createElement('select'))
44 | // Opera doesn't move the selection when pressing up/down in a
45 | // multi-select, but it does properly support the size property on
46 | // single-selects, so no multi-select is necessary.
47 | if (!window.opera) sel.multiple = true
48 | for (var i = 0; i < completions.length; ++i) {
49 | var opt = sel.appendChild(document.createElement('option'))
50 | opt.appendChild(document.createTextNode(completions[i]))
51 | }
52 | sel.firstChild.selected = true
53 | sel.size = Math.min(10, completions.length)
54 | var pos = editor.cursorCoords()
55 | complete.style.left = pos.x + 'px'
56 | complete.style.top = pos.yBot + 'px'
57 | document.body.appendChild(complete)
58 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
59 | var winW =
60 | window.innerWidth ||
61 | Math.max(
62 | document.body.offsetWidth,
63 | document.documentElement.offsetWidth
64 | )
65 | if (winW - pos.x < sel.clientWidth)
66 | complete.style.left = pos.x - sel.clientWidth + 'px'
67 | // Hack to hide the scrollbar.
68 | if (completions.length <= 10)
69 | complete.style.width = sel.clientWidth - 1 + 'px'
70 |
71 | var done = false
72 | function close() {
73 | if (done) return
74 | done = true
75 | complete.parentNode.removeChild(complete)
76 | }
77 | function pick() {
78 | insert(completions[sel.selectedIndex])
79 | close()
80 | setTimeout(function() {
81 | editor.focus()
82 | }, 50)
83 | }
84 | CodeMirror.connect(sel, 'blur', close)
85 | CodeMirror.connect(sel, 'keydown', function(event) {
86 | var code = event.keyCode
87 | // Enter
88 | if (code == 13) {
89 | CodeMirror.e_stop(event)
90 | pick()
91 | }
92 | // Escape
93 | else if (code == 27) {
94 | CodeMirror.e_stop(event)
95 | close()
96 | editor.focus()
97 | } else if (
98 | code != 38 &&
99 | code != 40 &&
100 | code != 33 &&
101 | code != 34 &&
102 | !CodeMirror.isModifierKey(event)
103 | ) {
104 | close()
105 | editor.focus()
106 | // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
107 | editor.triggerOnKeyDown(event)
108 | // Don't show completions if the code is backspace and the option is set.
109 | if (!options.closeOnBackspace || code != 8) {
110 | setTimeout(function() {
111 | collectHints(tempToken)
112 | }, 50)
113 | }
114 | }
115 | })
116 | CodeMirror.connect(sel, 'dblclick', pick)
117 |
118 | sel.focus()
119 | // Opera sometimes ignores focusing a freshly created node
120 | if (window.opera)
121 | setTimeout(function() {
122 | if (!done) sel.focus()
123 | }, 100)
124 | return true
125 | }
126 | return collectHints()
127 | }
128 | CodeMirror.simpleHint.defaults = {
129 | closeOnBackspace: true,
130 | closeOnTokenChange: false
131 | }
132 | })()
133 |
--------------------------------------------------------------------------------
/lib/codemirror/gfm/gfm.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | ;(function(mod) {
5 | if (typeof exports == 'object' && typeof module == 'object')
6 | // CommonJS
7 | mod(
8 | require('../../lib/codemirror'),
9 | require('../markdown/markdown'),
10 | require('../../addon/mode/overlay')
11 | )
12 | else if (typeof define == 'function' && define.amd)
13 | // AMD
14 | define([
15 | '../../lib/codemirror',
16 | '../markdown/markdown',
17 | '../../addon/mode/overlay'
18 | ], mod)
19 | // Plain browser env
20 | else mod(CodeMirror)
21 | })(function(CodeMirror) {
22 | 'use strict'
23 |
24 | CodeMirror.defineMode(
25 | 'gfm',
26 | function(config, modeConfig) {
27 | var codeDepth = 0
28 | function blankLine(state) {
29 | state.code = false
30 | return null
31 | }
32 | var gfmOverlay = {
33 | startState: function() {
34 | return {
35 | code: false,
36 | codeBlock: false,
37 | ateSpace: false
38 | }
39 | },
40 | copyState: function(s) {
41 | return {
42 | code: s.code,
43 | codeBlock: s.codeBlock,
44 | ateSpace: s.ateSpace
45 | }
46 | },
47 | token: function(stream, state) {
48 | state.combineTokens = null
49 |
50 | // Hack to prevent formatting override inside code blocks (block and inline)
51 | if (state.codeBlock) {
52 | if (stream.match(/^```/)) {
53 | state.codeBlock = false
54 | return null
55 | }
56 | stream.skipToEnd()
57 | return null
58 | }
59 | if (stream.sol()) {
60 | state.code = false
61 | }
62 | if (stream.sol() && stream.match(/^```/)) {
63 | stream.skipToEnd()
64 | state.codeBlock = true
65 | return null
66 | }
67 | // If this block is changed, it may need to be updated in Markdown mode
68 | if (stream.peek() === '`') {
69 | stream.next()
70 | var before = stream.pos
71 | stream.eatWhile('`')
72 | var difference = 1 + stream.pos - before
73 | if (!state.code) {
74 | codeDepth = difference
75 | state.code = true
76 | } else {
77 | if (difference === codeDepth) {
78 | // Must be exact
79 | state.code = false
80 | }
81 | }
82 | return null
83 | } else if (state.code) {
84 | stream.next()
85 | return null
86 | }
87 | // Check if space. If so, links can be formatted later on
88 | if (stream.eatSpace()) {
89 | state.ateSpace = true
90 | return null
91 | }
92 | if (stream.sol() || state.ateSpace) {
93 | state.ateSpace = false
94 | if (
95 | stream.match(
96 | /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/
97 | )
98 | ) {
99 | // User/Project@SHA
100 | // User@SHA
101 | // SHA
102 | state.combineTokens = true
103 | return 'link'
104 | } else if (
105 | stream.match(
106 | /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/
107 | )
108 | ) {
109 | // User/Project#Num
110 | // User#Num
111 | // #Num
112 | state.combineTokens = true
113 | return 'link'
114 | }
115 | }
116 | if (
117 | stream.match(
118 | /^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
119 | ) &&
120 | stream.string.slice(stream.start - 2, stream.start) != ']('
121 | ) {
122 | // URLs
123 | // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
124 | // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
125 | state.combineTokens = true
126 | return 'link'
127 | }
128 | stream.next()
129 | return null
130 | },
131 | blankLine: blankLine
132 | }
133 |
134 | var markdownConfig = {
135 | underscoresBreakWords: false,
136 | taskLists: true,
137 | fencedCodeBlocks: true,
138 | strikethrough: true
139 | }
140 | for (var attr in modeConfig) {
141 | markdownConfig[attr] = modeConfig[attr]
142 | }
143 | markdownConfig.name = 'markdown'
144 | CodeMirror.defineMIME('gfmBase', markdownConfig)
145 | return CodeMirror.overlayMode(
146 | CodeMirror.getMode(config, 'gfmBase'),
147 | gfmOverlay
148 | )
149 | },
150 | 'markdown'
151 | )
152 | })
153 |
--------------------------------------------------------------------------------
/styles/prism-twilight.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js Twilight theme
3 | * Based (more or less) on the Twilight theme originally of Textmate fame.
4 | * @author Remy Bach
5 | */
6 | code[class*='language-'],
7 | pre[class*='language-'] {
8 | color: white;
9 | background: none;
10 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
11 | text-align: left;
12 | text-shadow: 0 -0.1em 0.2em black;
13 | white-space: pre;
14 | word-spacing: normal;
15 | word-break: normal;
16 | word-wrap: normal;
17 | line-height: 1.5;
18 |
19 | -moz-tab-size: 4;
20 | -o-tab-size: 4;
21 | tab-size: 4;
22 |
23 | -webkit-hyphens: none;
24 | -moz-hyphens: none;
25 | -ms-hyphens: none;
26 | hyphens: none;
27 | }
28 |
29 | pre[class*='language-'],
30 | :not(pre) > code[class*='language-'] {
31 | background: hsl(0, 0%, 8%); /* #141414 */
32 | }
33 |
34 | /* Code blocks */
35 | pre[class*='language-'] {
36 | border-radius: 0.5em;
37 | border: 0.3em solid hsl(0, 0%, 33%); /* #282A2B */
38 | box-shadow: 1px 1px 0.5em black inset;
39 | margin: 0.5em 0;
40 | overflow: auto;
41 | padding: 1em;
42 | }
43 |
44 | pre[class*='language-']::-moz-selection {
45 | /* Firefox */
46 | background: hsl(200, 4%, 16%); /* #282A2B */
47 | }
48 |
49 | pre[class*='language-']::selection {
50 | /* Safari */
51 | background: hsl(200, 4%, 16%); /* #282A2B */
52 | }
53 |
54 | /* Text Selection colour */
55 | pre[class*='language-']::-moz-selection,
56 | pre[class*='language-'] ::-moz-selection,
57 | code[class*='language-']::-moz-selection,
58 | code[class*='language-'] ::-moz-selection {
59 | text-shadow: none;
60 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
61 | }
62 |
63 | pre[class*='language-']::selection,
64 | pre[class*='language-'] ::selection,
65 | code[class*='language-']::selection,
66 | code[class*='language-'] ::selection {
67 | text-shadow: none;
68 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
69 | }
70 |
71 | /* Inline code */
72 | :not(pre) > code[class*='language-'] {
73 | border-radius: 0.3em;
74 | border: 0.13em solid hsl(0, 0%, 33%); /* #545454 */
75 | box-shadow: 1px 1px 0.3em -0.1em black inset;
76 | padding: 0.15em 0.2em 0.05em;
77 | white-space: normal;
78 | }
79 |
80 | .token.comment,
81 | .token.prolog,
82 | .token.doctype,
83 | .token.cdata {
84 | color: hsl(0, 0%, 47%); /* #777777 */
85 | }
86 |
87 | .token.punctuation {
88 | opacity: 0.7;
89 | }
90 |
91 | .namespace {
92 | opacity: 0.7;
93 | }
94 |
95 | .token.tag,
96 | .token.boolean,
97 | .token.number,
98 | .token.deleted {
99 | color: hsl(14, 58%, 55%); /* #CF6A4C */
100 | }
101 |
102 | .token.keyword,
103 | .token.property,
104 | .token.selector,
105 | .token.constant,
106 | .token.symbol,
107 | .token.builtin {
108 | color: hsl(53, 89%, 79%); /* #F9EE98 */
109 | }
110 |
111 | .token.attr-name,
112 | .token.attr-value,
113 | .token.string,
114 | .token.char,
115 | .token.operator,
116 | .token.entity,
117 | .token.url,
118 | .language-css .token.string,
119 | .style .token.string,
120 | .token.variable,
121 | .token.inserted {
122 | color: hsl(76, 21%, 52%); /* #8F9D6A */
123 | }
124 |
125 | .token.atrule {
126 | color: hsl(218, 22%, 55%); /* #7587A6 */
127 | }
128 |
129 | .token.regex,
130 | .token.important {
131 | color: hsl(42, 75%, 65%); /* #E9C062 */
132 | }
133 |
134 | .token.important,
135 | .token.bold {
136 | font-weight: bold;
137 | }
138 | .token.italic {
139 | font-style: italic;
140 | }
141 |
142 | .token.entity {
143 | cursor: help;
144 | }
145 |
146 | pre[data-line] {
147 | padding: 1em 0 1em 3em;
148 | position: relative;
149 | }
150 |
151 | /* Markup */
152 | .language-markup .token.tag,
153 | .language-markup .token.attr-name,
154 | .language-markup .token.punctuation {
155 | color: hsl(33, 33%, 52%); /* #AC885B */
156 | }
157 |
158 | /* Make the tokens sit above the line highlight so the colours don't look faded. */
159 | .token {
160 | position: relative;
161 | z-index: 1;
162 | }
163 |
164 | .line-highlight {
165 | background: hsla(0, 0%, 33%, 0.25); /* #545454 */
166 | background: linear-gradient(
167 | to right,
168 | hsla(0, 0%, 33%, 0.1) 70%,
169 | hsla(0, 0%, 33%, 0)
170 | ); /* #545454 */
171 | border-bottom: 1px dashed hsl(0, 0%, 33%); /* #545454 */
172 | border-top: 1px dashed hsl(0, 0%, 33%); /* #545454 */
173 | left: 0;
174 | line-height: inherit;
175 | margin-top: 0.75em; /* Same as .prism’s padding-top */
176 | padding: inherit 0;
177 | pointer-events: none;
178 | position: absolute;
179 | right: 0;
180 | white-space: pre;
181 | z-index: 0;
182 | }
183 |
184 | .line-highlight:before,
185 | .line-highlight[data-end]:after {
186 | background-color: hsl(215, 15%, 59%); /* #8794A6 */
187 | border-radius: 999px;
188 | box-shadow: 0 1px white;
189 | color: hsl(24, 20%, 95%); /* #F5F2F0 */
190 | content: attr(data-start);
191 | font: bold 65%/1.5 sans-serif;
192 | left: 0.6em;
193 | min-width: 1em;
194 | padding: 0 0.5em;
195 | position: absolute;
196 | text-align: center;
197 | text-shadow: none;
198 | top: 0.4em;
199 | vertical-align: 0.3em;
200 | }
201 |
202 | .line-highlight[data-end]:after {
203 | bottom: 0.4em;
204 | content: attr(data-end);
205 | top: auto;
206 | }
207 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 微信公众号助手
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
35 |
36 |
42 |
43 |
44 |
45 |
52 |
53 | 请输入正确格式的 url
54 |
55 |
确认抓取
56 |
57 |
58 |
59 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/pig-hint.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | function forEach(arr, f) {
3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i])
4 | }
5 |
6 | function arrayContains(arr, item) {
7 | if (!Array.prototype.indexOf) {
8 | var i = arr.length
9 | while (i--) {
10 | if (arr[i] === item) {
11 | return true
12 | }
13 | }
14 | return false
15 | }
16 | return arr.indexOf(item) != -1
17 | }
18 |
19 | function scriptHint(editor, keywords, getToken) {
20 | // Find the token at the cursor
21 | var cur = editor.getCursor(),
22 | token = getToken(editor, cur),
23 | tprop = token
24 | // If it's not a 'word-style' token, ignore the token.
25 |
26 | if (!/^[\w$_]*$/.test(token.string)) {
27 | token = tprop = {
28 | start: cur.ch,
29 | end: cur.ch,
30 | string: '',
31 | state: token.state,
32 | className: token.string == ':' ? 'pig-type' : null
33 | }
34 | }
35 |
36 | if (!context) var context = []
37 | context.push(tprop)
38 |
39 | var completionList = getCompletions(token, context)
40 | completionList = completionList.sort()
41 | //prevent autocomplete for last word, instead show dropdown with one word
42 | if (completionList.length == 1) {
43 | completionList.push(' ')
44 | }
45 |
46 | return {
47 | list: completionList,
48 | from: { line: cur.line, ch: token.start },
49 | to: { line: cur.line, ch: token.end }
50 | }
51 | }
52 |
53 | CodeMirror.pigHint = function(editor) {
54 | return scriptHint(editor, pigKeywordsU, function(e, cur) {
55 | return e.getTokenAt(cur)
56 | })
57 | }
58 |
59 | function toTitleCase(str) {
60 | return str.replace(/(?:^|\s)\w/g, function(match) {
61 | return match.toUpperCase()
62 | })
63 | }
64 |
65 | var pigKeywords =
66 | 'VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP ' +
67 | 'JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL ' +
68 | 'PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE ' +
69 | 'SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE ' +
70 | 'NEQ MATCHES TRUE FALSE'
71 | var pigKeywordsU = pigKeywords.split(' ')
72 | var pigKeywordsL = pigKeywords.toLowerCase().split(' ')
73 |
74 | var pigTypes =
75 | 'BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP'
76 | var pigTypesU = pigTypes.split(' ')
77 | var pigTypesL = pigTypes.toLowerCase().split(' ')
78 |
79 | var pigBuiltins =
80 | 'ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL ' +
81 | 'CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS ' +
82 | 'DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG ' +
83 | 'FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN ' +
84 | 'INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER ' +
85 | 'ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS ' +
86 | 'LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA ' +
87 | 'PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE ' +
88 | 'SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG ' +
89 | 'TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER'
90 | var pigBuiltinsU = pigBuiltins
91 | .split(' ')
92 | .join('() ')
93 | .split(' ')
94 | var pigBuiltinsL = pigBuiltins
95 | .toLowerCase()
96 | .split(' ')
97 | .join('() ')
98 | .split(' ')
99 | var pigBuiltinsC = (
100 | 'BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs ' +
101 | 'DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax ' +
102 | 'FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum ' +
103 | 'InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker ' +
104 | 'IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize ' +
105 | 'MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax ' +
106 | 'StringMin StringSize TextLoader TupleSize Utf8StorageConverter'
107 | )
108 | .split(' ')
109 | .join('() ')
110 | .split(' ')
111 |
112 | function getCompletions(token, context) {
113 | var found = [],
114 | start = token.string
115 | function maybeAdd(str) {
116 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str)
117 | }
118 |
119 | function gatherCompletions(obj) {
120 | if (obj == ':') {
121 | forEach(pigTypesL, maybeAdd)
122 | } else {
123 | forEach(pigBuiltinsU, maybeAdd)
124 | forEach(pigBuiltinsL, maybeAdd)
125 | forEach(pigBuiltinsC, maybeAdd)
126 | forEach(pigTypesU, maybeAdd)
127 | forEach(pigTypesL, maybeAdd)
128 | forEach(pigKeywordsU, maybeAdd)
129 | forEach(pigKeywordsL, maybeAdd)
130 | }
131 | }
132 |
133 | if (context) {
134 | // If this is a property, see if it belongs to some object we can
135 | // find in the current environment.
136 | var obj = context.pop(),
137 | base
138 |
139 | if (obj.className == 'pig-word') base = obj.string
140 | else if (obj.className == 'pig-type') base = ':' + obj.string
141 |
142 | while (base != null && context.length) base = base[context.pop().string]
143 | if (base != null) gatherCompletions(base)
144 | }
145 | return found
146 | }
147 | })()
148 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/searchcursor.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | function SearchCursor(cm, query, pos, caseFold) {
3 | this.atOccurrence = false
4 | this.cm = cm
5 | if (caseFold == null && typeof query == 'string') caseFold = false
6 |
7 | pos = pos ? cm.clipPos(pos) : { line: 0, ch: 0 }
8 | this.pos = { from: pos, to: pos }
9 |
10 | // The matches method is filled in based on the type of query.
11 | // It takes a position and a direction, and returns an object
12 | // describing the next occurrence of the query, or null if no
13 | // more matches were found.
14 | if (typeof query != 'string') {
15 | // Regexp match
16 | if (!query.global)
17 | query = new RegExp(query.source, query.ignoreCase ? 'ig' : 'g')
18 | this.matches = function(reverse, pos) {
19 | if (reverse) {
20 | query.lastIndex = 0
21 | var line = cm.getLine(pos.line).slice(0, pos.ch),
22 | match = query.exec(line),
23 | start = 0
24 | while (match) {
25 | start += match.index + 1
26 | line = line.slice(start)
27 | query.lastIndex = 0
28 | var newmatch = query.exec(line)
29 | if (newmatch) match = newmatch
30 | else break
31 | }
32 | start--
33 | } else {
34 | query.lastIndex = pos.ch
35 | var line = cm.getLine(pos.line),
36 | match = query.exec(line),
37 | start = match && match.index
38 | }
39 | if (match)
40 | return {
41 | from: { line: pos.line, ch: start },
42 | to: { line: pos.line, ch: start + match[0].length },
43 | match: match
44 | }
45 | }
46 | } else {
47 | // String query
48 | if (caseFold) query = query.toLowerCase()
49 | var fold = caseFold
50 | ? function(str) {
51 | return str.toLowerCase()
52 | }
53 | : function(str) {
54 | return str
55 | }
56 | var target = query.split('\n')
57 | // Different methods for single-line and multi-line queries
58 | if (target.length == 1)
59 | this.matches = function(reverse, pos) {
60 | var line = fold(cm.getLine(pos.line)),
61 | len = query.length,
62 | match
63 | if (
64 | reverse
65 | ? pos.ch >= len &&
66 | (match = line.lastIndexOf(query, pos.ch - len)) != -1
67 | : (match = line.indexOf(query, pos.ch)) != -1
68 | )
69 | return {
70 | from: { line: pos.line, ch: match },
71 | to: { line: pos.line, ch: match + len }
72 | }
73 | }
74 | else
75 | this.matches = function(reverse, pos) {
76 | var ln = pos.line,
77 | idx = reverse ? target.length - 1 : 0,
78 | match = target[idx],
79 | line = fold(cm.getLine(ln))
80 | var offsetA = reverse
81 | ? line.indexOf(match) + match.length
82 | : line.lastIndexOf(match)
83 | if (
84 | reverse
85 | ? offsetA >= pos.ch || offsetA != match.length
86 | : offsetA <= pos.ch || offsetA != line.length - match.length
87 | )
88 | return
89 | for (;;) {
90 | if (reverse ? !ln : ln == cm.lineCount() - 1) return
91 | line = fold(cm.getLine((ln += reverse ? -1 : 1)))
92 | match = target[reverse ? --idx : ++idx]
93 | if (idx > 0 && idx < target.length - 1) {
94 | if (line != match) return
95 | else continue
96 | }
97 | var offsetB = reverse
98 | ? line.lastIndexOf(match)
99 | : line.indexOf(match) + match.length
100 | if (
101 | reverse
102 | ? offsetB != line.length - match.length
103 | : offsetB != match.length
104 | )
105 | return
106 | var start = { line: pos.line, ch: offsetA },
107 | end = { line: ln, ch: offsetB }
108 | return { from: reverse ? end : start, to: reverse ? start : end }
109 | }
110 | }
111 | }
112 | }
113 |
114 | SearchCursor.prototype = {
115 | findNext: function() {
116 | return this.find(false)
117 | },
118 | findPrevious: function() {
119 | return this.find(true)
120 | },
121 |
122 | find: function(reverse) {
123 | var self = this,
124 | pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to)
125 | function savePosAndFail(line) {
126 | var pos = { line: line, ch: 0 }
127 | self.pos = { from: pos, to: pos }
128 | self.atOccurrence = false
129 | return false
130 | }
131 |
132 | for (;;) {
133 | if ((this.pos = this.matches(reverse, pos))) {
134 | this.atOccurrence = true
135 | return this.pos.match || true
136 | }
137 | if (reverse) {
138 | if (!pos.line) return savePosAndFail(0)
139 | pos = {
140 | line: pos.line - 1,
141 | ch: this.cm.getLine(pos.line - 1).length
142 | }
143 | } else {
144 | var maxLine = this.cm.lineCount()
145 | if (pos.line == maxLine - 1) return savePosAndFail(maxLine)
146 | pos = { line: pos.line + 1, ch: 0 }
147 | }
148 | }
149 | },
150 |
151 | from: function() {
152 | if (this.atOccurrence) return this.pos.from
153 | },
154 | to: function() {
155 | if (this.atOccurrence) return this.pos.to
156 | },
157 |
158 | replace: function(newText) {
159 | var self = this
160 | if (this.atOccurrence)
161 | self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to)
162 | }
163 | }
164 |
165 | CodeMirror.defineExtension('getSearchCursor', function(query, pos, caseFold) {
166 | return new SearchCursor(this, query, pos, caseFold)
167 | })
168 | })()
169 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/javascript-hint.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | function forEach(arr, f) {
3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i])
4 | }
5 |
6 | function arrayContains(arr, item) {
7 | if (!Array.prototype.indexOf) {
8 | var i = arr.length
9 | while (i--) {
10 | if (arr[i] === item) {
11 | return true
12 | }
13 | }
14 | return false
15 | }
16 | return arr.indexOf(item) != -1
17 | }
18 |
19 | function scriptHint(editor, keywords, getToken) {
20 | // Find the token at the cursor
21 | var cur = editor.getCursor(),
22 | token = getToken(editor, cur),
23 | tprop = token
24 | // If it's not a 'word-style' token, ignore the token.
25 | if (!/^[\w$_]*$/.test(token.string)) {
26 | token = tprop = {
27 | start: cur.ch,
28 | end: cur.ch,
29 | string: '',
30 | state: token.state,
31 | className: token.string == '.' ? 'property' : null
32 | }
33 | }
34 | // If it is a property, find out what it is a property of.
35 | while (tprop.className == 'property') {
36 | tprop = getToken(editor, { line: cur.line, ch: tprop.start })
37 | if (tprop.string != '.') return
38 | tprop = getToken(editor, { line: cur.line, ch: tprop.start })
39 | if (tprop.string == ')') {
40 | var level = 1
41 | do {
42 | tprop = getToken(editor, { line: cur.line, ch: tprop.start })
43 | switch (tprop.string) {
44 | case ')':
45 | level++
46 | break
47 | case '(':
48 | level--
49 | break
50 | default:
51 | break
52 | }
53 | } while (level > 0)
54 | tprop = getToken(editor, { line: cur.line, ch: tprop.start })
55 | if (tprop.className == 'variable') tprop.className = 'function'
56 | else return // no clue
57 | }
58 | if (!context) var context = []
59 | context.push(tprop)
60 | }
61 | return {
62 | list: getCompletions(token, context, keywords),
63 | from: { line: cur.line, ch: token.start },
64 | to: { line: cur.line, ch: token.end }
65 | }
66 | }
67 |
68 | CodeMirror.javascriptHint = function(editor) {
69 | return scriptHint(editor, javascriptKeywords, function(e, cur) {
70 | return e.getTokenAt(cur)
71 | })
72 | }
73 |
74 | function getCoffeeScriptToken(editor, cur) {
75 | // This getToken, it is for coffeescript, imitates the behavior of
76 | // getTokenAt method in javascript.js, that is, returning "property"
77 | // type and treat "." as indepenent token.
78 | var token = editor.getTokenAt(cur)
79 | if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
80 | token.end = token.start
81 | token.string = '.'
82 | token.className = 'property'
83 | } else if (/^\.[\w$_]*$/.test(token.string)) {
84 | token.className = 'property'
85 | token.start++
86 | token.string = token.string.replace(/\./, '')
87 | }
88 | return token
89 | }
90 |
91 | CodeMirror.coffeescriptHint = function(editor) {
92 | return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken)
93 | }
94 |
95 | var stringProps = (
96 | 'charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight ' +
97 | 'toUpperCase toLowerCase split concat match replace search'
98 | ).split(' ')
99 | var arrayProps = (
100 | 'length concat join splice push pop shift unshift slice reverse sort indexOf ' +
101 | 'lastIndexOf every some filter forEach map reduce reduceRight '
102 | ).split(' ')
103 | var funcProps = 'prototype apply call bind'.split(' ')
104 | var javascriptKeywords = (
105 | 'break case catch continue debugger default delete do else false finally for function ' +
106 | 'if in instanceof new null return switch throw true try typeof var void while with'
107 | ).split(' ')
108 | var coffeescriptKeywords = (
109 | 'and break catch class continue delete do else extends false finally for ' +
110 | 'if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes'
111 | ).split(' ')
112 |
113 | function getCompletions(token, context, keywords) {
114 | var found = [],
115 | start = token.string
116 | function maybeAdd(str) {
117 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str)
118 | }
119 | function gatherCompletions(obj) {
120 | if (typeof obj == 'string') forEach(stringProps, maybeAdd)
121 | else if (obj instanceof Array) forEach(arrayProps, maybeAdd)
122 | else if (obj instanceof Function) forEach(funcProps, maybeAdd)
123 | for (var name in obj) maybeAdd(name)
124 | }
125 |
126 | if (context) {
127 | // If this is a property, see if it belongs to some object we can
128 | // find in the current environment.
129 | var obj = context.pop(),
130 | base
131 | if (obj.className == 'variable') base = window[obj.string]
132 | else if (obj.className == 'string') base = ''
133 | else if (obj.className == 'atom') base = 1
134 | else if (obj.className == 'function') {
135 | if (
136 | window.jQuery != null &&
137 | (obj.string == '$' || obj.string == 'jQuery') &&
138 | typeof window.jQuery == 'function'
139 | )
140 | base = window.jQuery()
141 | else if (
142 | window._ != null &&
143 | obj.string == '_' &&
144 | typeof window._ == 'function'
145 | )
146 | base = window._()
147 | }
148 | while (base != null && context.length) base = base[context.pop().string]
149 | if (base != null) gatherCompletions(base)
150 | } else {
151 | // If not, just look in the window object and any local scope
152 | // (reading into JS mode internals to get at the local variables)
153 | for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name)
154 | gatherCompletions(window)
155 | forEach(keywords, maybeAdd)
156 | }
157 | return found
158 | }
159 | })()
160 |
--------------------------------------------------------------------------------
/scripts/common.js:
--------------------------------------------------------------------------------
1 | const log = console.log.bind(console)
2 | const create = tag => document.createElement(tag)
3 | const getDOM = s => document.querySelector(s)
4 | const getAll = (s, target = document) => {
5 | return Array.prototype.slice.call(target.querySelectorAll(s))
6 | }
7 |
8 | // 插入 loading
9 | const Loading = (function() {
10 | var html =
11 | ' '
13 | let div = document.createElement('div')
14 | div.innerHTML = html
15 | document.body.appendChild(div)
16 | let elem = getAll('#loading-wx', div)[0]
17 |
18 | return {
19 | show: () => (elem.style.display = 'block'),
20 | hide: () => (elem.style.display = 'none')
21 | }
22 | })()
23 |
24 | /**
25 | * md5
26 | */
27 | const md5 = (function() {
28 | let cache = new Map()
29 |
30 | return s => {
31 | if (cache.has(s)) {
32 | return cache.get(s)
33 | } else {
34 | let hash = CryptoJS.MD5(String(s)).toString()
35 | cache.set(s, hash)
36 | return hash
37 | }
38 | }
39 | })()
40 |
41 | /**
42 | * get/set Object using localStorage
43 | */
44 | const store = {
45 | db: window.localStorage,
46 | prefix: '__wx__',
47 | set(key, value) {
48 | this.db.setItem(this.prefix + key, JSON.stringify(value))
49 | },
50 |
51 | get(key) {
52 | return (
53 | (this.has(key) && JSON.parse(this.db.getItem(this.prefix + key))) || null
54 | )
55 | },
56 |
57 | has(key) {
58 | return !!this.db.getItem(this.prefix + key)
59 | },
60 |
61 | keys() {
62 | let prefix = this.prefix
63 | return Object.keys(this.db)
64 | .filter(k => k.indexOf(prefix) === 0)
65 | .map(k => k.slice(prefix.length))
66 | }
67 | }
68 |
69 | /**
70 | * generate DOM from string
71 | */
72 | const divWrap = html => {
73 | let div = create('div')
74 | div.innerHTML = html
75 | return div
76 | }
77 |
78 | /**
79 | * dispatch event
80 | */
81 | const dispatch = (elem, name) => {
82 | let evt = document.createEvent('HTMLEvents')
83 | evt.initEvent(name, false, false)
84 | elem.dispatchEvent(evt)
85 | }
86 |
87 | /**
88 | * copy text
89 | */
90 | const copy = element => {
91 | let range = document.createRange()
92 | range.selectNode(element)
93 |
94 | let selection = window.getSelection()
95 | if (selection.rangeCount > 0) {
96 | selection.removeAllRanges()
97 | }
98 | selection.addRange(range)
99 | document.execCommand('copy')
100 | selection.removeAllRanges()
101 | }
102 |
103 | /**
104 | * paste
105 | */
106 | const paste = () => {
107 | let textarea = createHelperTextArea()
108 | textarea.focus()
109 | document.execCommand('paste')
110 | textarea.remove()
111 | }
112 |
113 | /**
114 | * helper
115 | * create a temp textarea
116 | */
117 | const createHelperTextArea = () => {
118 | const helperTextArea = create('textarea')
119 | helperTextArea.setAttribute('helper', 'true')
120 | helperTextArea.style.width = 0
121 | helperTextArea.style.height = 0
122 | document.body.appendChild(helperTextArea)
123 | return helperTextArea
124 | }
125 |
126 | /**
127 | * read blob as DataURI
128 | */
129 | const blobToDataURI = blob =>
130 | new Promise((resolve, reject) => {
131 | let reader = new FileReader()
132 | reader.readAsDataURL(blob)
133 | reader.onload = e => resolve(e.target.result)
134 | })
135 |
136 | /**
137 | * https://zhitu.isux.us/
138 | */
139 | const isuxUpload = blob =>
140 | new Promise((resolve, reject) => {
141 | let fileType = blob.type.replace('image/', '')
142 | let fileName = Date.now() + '.' + fileType
143 | let oriSize = (blob.size / 1024) | 0
144 |
145 | let file = new File([blob], fileName, {
146 | type: blob.type,
147 | lastModified: new Date(Date.now() - Math.random() * 1000 * 3600 * 24 * 50)
148 | })
149 |
150 | let data = new FormData()
151 | data.append('name', fileName)
152 | // 10,20,30, ..., 100
153 | // TODO: set options for choosing `compress` param
154 | data.append('compress', 10)
155 | data.append('oriSize', oriSize)
156 | data.append('type', fileType)
157 | data.append('fileSelect', file)
158 | data.append('pngLess', 1)
159 | data.append('isOa', 0)
160 | data.append('typeChange', 1)
161 |
162 | let xhr = new XMLHttpRequest()
163 |
164 | xhr.onreadystatechange = function() {
165 | if (xhr.readyState === 4 && xhr.status === 200) {
166 | try {
167 | let res = JSON.parse(xhr.response)
168 | if (res.code === 0) {
169 | console.info('[isux compress]:', res)
170 | resolve(`https:${res.url}`)
171 | } else {
172 | throw res
173 | }
174 | } catch (e) {
175 | reject({
176 | error: e,
177 | text: 'Error occurred: [' + xhr.responseText + ']'
178 | })
179 | }
180 | }
181 | }
182 |
183 | xhr.open('POST', 'https://zhitu.isux.us/index.php/preview/imgcompress')
184 | xhr.send(data)
185 | })
186 |
187 | /**
188 | * use `zhitu.isux.us`
189 | */
190 | const isuxCompress = blob => {
191 | return isuxUpload(blob)
192 | .then(url => fetch(url))
193 | .then(res => res.blob())
194 | }
195 |
196 | /**
197 | * fetch image
198 | * transform it into BASE64
199 | */
200 | const fetchImage = ({ url, middleware }) => {
201 | middleware =
202 | middleware ||
203 | function(o) {
204 | return o
205 | }
206 |
207 | return fetch(url)
208 | .then(r => r.blob())
209 | .then(middleware)
210 | .then(blobToDataURI)
211 | .catch(e => null)
212 | }
213 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/search.js:
--------------------------------------------------------------------------------
1 | // Define search commands. Depends on dialog.js or another
2 | // implementation of the openDialog method.
3 |
4 | // Replace works a little oddly -- it will do the replace on the next
5 | // Ctrl-G (or whatever is bound to findNext) press. You prevent a
6 | // replace by making sure the match is no longer selected when hitting
7 | // Ctrl-G.
8 |
9 | ;(function() {
10 | function SearchState() {
11 | this.posFrom = this.posTo = this.query = null
12 | this.marked = []
13 | }
14 | function getSearchState(cm) {
15 | return cm._searchState || (cm._searchState = new SearchState())
16 | }
17 | function getSearchCursor(cm, query, pos) {
18 | // Heuristic: if the query string is all lowercase, do a case insensitive search.
19 | return cm.getSearchCursor(
20 | query,
21 | pos,
22 | typeof query == 'string' && query == query.toLowerCase()
23 | )
24 | }
25 | function dialog(cm, text, shortText, f) {
26 | if (cm.openDialog) cm.openDialog(text, f)
27 | else f(prompt(shortText, ''))
28 | }
29 | function confirmDialog(cm, text, shortText, fs) {
30 | if (cm.openConfirm) cm.openConfirm(text, fs)
31 | else if (confirm(shortText)) fs[0]()
32 | }
33 | function parseQuery(query) {
34 | var isRE = query.match(/^\/(.*)\/([a-z]*)$/)
35 | return isRE
36 | ? new RegExp(isRE[1], isRE[2].indexOf('i') == -1 ? '' : 'i')
37 | : query
38 | }
39 | var queryDialog =
40 | 'Search: (Use /re/ syntax for regexp search) '
41 | function doSearch(cm, rev) {
42 | var state = getSearchState(cm)
43 | if (state.query) return findNext(cm, rev)
44 | dialog(cm, queryDialog, 'Search for:', function(query) {
45 | cm.operation(function() {
46 | if (!query || state.query) return
47 | state.query = parseQuery(query)
48 | if (cm.lineCount() < 2000) {
49 | // This is too expensive on big documents.
50 | for (
51 | var cursor = getSearchCursor(cm, state.query);
52 | cursor.findNext();
53 |
54 | )
55 | state.marked.push(
56 | cm.markText(cursor.from(), cursor.to(), 'CodeMirror-searching')
57 | )
58 | }
59 | state.posFrom = state.posTo = cm.getCursor()
60 | findNext(cm, rev)
61 | })
62 | })
63 | }
64 | function findNext(cm, rev) {
65 | cm.operation(function() {
66 | var state = getSearchState(cm)
67 | var cursor = getSearchCursor(
68 | cm,
69 | state.query,
70 | rev ? state.posFrom : state.posTo
71 | )
72 | if (!cursor.find(rev)) {
73 | cursor = getSearchCursor(
74 | cm,
75 | state.query,
76 | rev ? { line: cm.lineCount() - 1 } : { line: 0, ch: 0 }
77 | )
78 | if (!cursor.find(rev)) return
79 | }
80 | cm.setSelection(cursor.from(), cursor.to())
81 | state.posFrom = cursor.from()
82 | state.posTo = cursor.to()
83 | })
84 | }
85 | function clearSearch(cm) {
86 | cm.operation(function() {
87 | var state = getSearchState(cm)
88 | if (!state.query) return
89 | state.query = null
90 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear()
91 | state.marked.length = 0
92 | })
93 | }
94 |
95 | var replaceQueryDialog =
96 | 'Replace: (Use /re/ syntax for regexp search) '
97 | var replacementQueryDialog = 'With: '
98 | var doReplaceConfirm =
99 | 'Replace? Yes No Stop '
100 | function replace(cm, all) {
101 | dialog(cm, replaceQueryDialog, 'Replace:', function(query) {
102 | if (!query) return
103 | query = parseQuery(query)
104 | dialog(cm, replacementQueryDialog, 'Replace with:', function(text) {
105 | if (all) {
106 | cm.compoundChange(function() {
107 | cm.operation(function() {
108 | for (
109 | var cursor = getSearchCursor(cm, query);
110 | cursor.findNext();
111 |
112 | ) {
113 | if (typeof query != 'string') {
114 | var match = cm
115 | .getRange(cursor.from(), cursor.to())
116 | .match(query)
117 | cursor.replace(
118 | text.replace(/\$(\d)/, function(w, i) {
119 | return match[i]
120 | })
121 | )
122 | } else cursor.replace(text)
123 | }
124 | })
125 | })
126 | } else {
127 | clearSearch(cm)
128 | var cursor = getSearchCursor(cm, query, cm.getCursor())
129 | function advance() {
130 | var start = cursor.from(),
131 | match
132 | if (!(match = cursor.findNext())) {
133 | cursor = getSearchCursor(cm, query)
134 | if (
135 | !(match = cursor.findNext()) ||
136 | (start &&
137 | cursor.from().line == start.line &&
138 | cursor.from().ch == start.ch)
139 | )
140 | return
141 | }
142 | cm.setSelection(cursor.from(), cursor.to())
143 | confirmDialog(cm, doReplaceConfirm, 'Replace?', [
144 | function() {
145 | doReplace(match)
146 | },
147 | advance
148 | ])
149 | }
150 | function doReplace(match) {
151 | cursor.replace(
152 | typeof query == 'string'
153 | ? text
154 | : text.replace(/\$(\d)/, function(w, i) {
155 | return match[i]
156 | })
157 | )
158 | advance()
159 | }
160 | advance()
161 | }
162 | })
163 | })
164 | }
165 |
166 | CodeMirror.commands.find = function(cm) {
167 | clearSearch(cm)
168 | doSearch(cm)
169 | }
170 | CodeMirror.commands.findNext = doSearch
171 | CodeMirror.commands.findPrev = function(cm) {
172 | doSearch(cm, true)
173 | }
174 | CodeMirror.commands.clearSearch = clearSearch
175 | CodeMirror.commands.replace = replace
176 | CodeMirror.commands.replaceAll = function(cm) {
177 | replace(cm, true)
178 | }
179 | })()
180 |
--------------------------------------------------------------------------------
/lib/codemirror/htmlmixed/htmlmixed.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | ;(function(mod) {
5 | if (typeof exports == 'object' && typeof module == 'object')
6 | // CommonJS
7 | mod(
8 | require('../../lib/codemirror'),
9 | require('../xml/xml'),
10 | require('../javascript/javascript'),
11 | require('../css/css')
12 | )
13 | else if (typeof define == 'function' && define.amd)
14 | // AMD
15 | define([
16 | '../../lib/codemirror',
17 | '../xml/xml',
18 | '../javascript/javascript',
19 | '../css/css'
20 | ], mod)
21 | // Plain browser env
22 | else mod(CodeMirror)
23 | })(function(CodeMirror) {
24 | 'use strict'
25 |
26 | CodeMirror.defineMode(
27 | 'htmlmixed',
28 | function(config, parserConfig) {
29 | var htmlMode = CodeMirror.getMode(config, {
30 | name: 'xml',
31 | htmlMode: true,
32 | multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
33 | multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
34 | })
35 | var cssMode = CodeMirror.getMode(config, 'css')
36 |
37 | var scriptTypes = [],
38 | scriptTypesConf = parserConfig && parserConfig.scriptTypes
39 | scriptTypes.push({
40 | matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i,
41 | mode: CodeMirror.getMode(config, 'javascript')
42 | })
43 | if (scriptTypesConf)
44 | for (var i = 0; i < scriptTypesConf.length; ++i) {
45 | var conf = scriptTypesConf[i]
46 | scriptTypes.push({
47 | matches: conf.matches,
48 | mode: conf.mode && CodeMirror.getMode(config, conf.mode)
49 | })
50 | }
51 | scriptTypes.push({
52 | matches: /./,
53 | mode: CodeMirror.getMode(config, 'text/plain')
54 | })
55 |
56 | function html(stream, state) {
57 | var tagName = state.htmlState.tagName
58 | if (tagName) tagName = tagName.toLowerCase()
59 | var style = htmlMode.token(stream, state.htmlState)
60 | if (
61 | tagName == 'script' &&
62 | /\btag\b/.test(style) &&
63 | stream.current() == '>'
64 | ) {
65 | // Script block: mode to change to depends on type attribute
66 | var scriptType = stream.string
67 | .slice(Math.max(0, stream.pos - 100), stream.pos)
68 | .match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i)
69 | scriptType = scriptType ? scriptType[1] : ''
70 | if (scriptType && /[\"\']/.test(scriptType.charAt(0)))
71 | scriptType = scriptType.slice(1, scriptType.length - 1)
72 | for (var i = 0; i < scriptTypes.length; ++i) {
73 | var tp = scriptTypes[i]
74 | if (
75 | typeof tp.matches == 'string'
76 | ? scriptType == tp.matches
77 | : tp.matches.test(scriptType)
78 | ) {
79 | if (tp.mode) {
80 | state.token = script
81 | state.localMode = tp.mode
82 | state.localState =
83 | tp.mode.startState &&
84 | tp.mode.startState(htmlMode.indent(state.htmlState, ''))
85 | }
86 | break
87 | }
88 | }
89 | } else if (
90 | tagName == 'style' &&
91 | /\btag\b/.test(style) &&
92 | stream.current() == '>'
93 | ) {
94 | state.token = css
95 | state.localMode = cssMode
96 | state.localState = cssMode.startState(
97 | htmlMode.indent(state.htmlState, '')
98 | )
99 | }
100 | return style
101 | }
102 | function maybeBackup(stream, pat, style) {
103 | var cur = stream.current()
104 | var close = cur.search(pat),
105 | m
106 | if (close > -1) stream.backUp(cur.length - close)
107 | else if ((m = cur.match(/<\/?$/))) {
108 | stream.backUp(cur.length)
109 | if (!stream.match(pat, false)) stream.match(cur)
110 | }
111 | return style
112 | }
113 | function script(stream, state) {
114 | if (stream.match(/^<\/\s*script\s*>/i, false)) {
115 | state.token = html
116 | state.localState = state.localMode = null
117 | return null
118 | }
119 | return maybeBackup(
120 | stream,
121 | /<\/\s*script\s*>/,
122 | state.localMode.token(stream, state.localState)
123 | )
124 | }
125 | function css(stream, state) {
126 | if (stream.match(/^<\/\s*style\s*>/i, false)) {
127 | state.token = html
128 | state.localState = state.localMode = null
129 | return null
130 | }
131 | return maybeBackup(
132 | stream,
133 | /<\/\s*style\s*>/,
134 | cssMode.token(stream, state.localState)
135 | )
136 | }
137 |
138 | return {
139 | startState: function() {
140 | var state = htmlMode.startState()
141 | return {
142 | token: html,
143 | localMode: null,
144 | localState: null,
145 | htmlState: state
146 | }
147 | },
148 |
149 | copyState: function(state) {
150 | if (state.localState)
151 | var local = CodeMirror.copyState(state.localMode, state.localState)
152 | return {
153 | token: state.token,
154 | localMode: state.localMode,
155 | localState: local,
156 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)
157 | }
158 | },
159 |
160 | token: function(stream, state) {
161 | return state.token(stream, state)
162 | },
163 |
164 | indent: function(state, textAfter) {
165 | if (!state.localMode || /^\s*<\//.test(textAfter))
166 | return htmlMode.indent(state.htmlState, textAfter)
167 | else if (state.localMode.indent)
168 | return state.localMode.indent(state.localState, textAfter)
169 | else return CodeMirror.Pass
170 | },
171 |
172 | innerMode: function(state) {
173 | return {
174 | state: state.localState || state.htmlState,
175 | mode: state.localMode || htmlMode
176 | }
177 | }
178 | }
179 | },
180 | 'xml',
181 | 'javascript',
182 | 'css'
183 | )
184 |
185 | CodeMirror.defineMIME('text/html', 'htmlmixed')
186 | })
187 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/closetag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tag-closer extension for CodeMirror.
3 | *
4 | * This extension adds a "closeTag" utility function that can be used with key bindings to
5 | * insert a matching end tag after the ">" character of a start tag has been typed. It can
6 | * also complete "" if a matching start tag is found. It will correctly ignore signal
7 | * characters for empty tags, comments, CDATA, etc.
8 | *
9 | * The function depends on internal parser state to identify tags. It is compatible with the
10 | * following CodeMirror modes and will ignore all others:
11 | * - htmlmixed
12 | * - xml
13 | *
14 | * See demos/closetag.html for a usage example.
15 | *
16 | * @author Nathan Williams
17 | * Contributed under the same license terms as CodeMirror.
18 | */
19 | ;(function() {
20 | /** Option that allows tag closing behavior to be toggled. Default is true. */
21 | CodeMirror.defaults['closeTagEnabled'] = true
22 |
23 | /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */
24 | CodeMirror.defaults['closeTagIndent'] = [
25 | 'applet',
26 | 'blockquote',
27 | 'body',
28 | 'button',
29 | 'div',
30 | 'dl',
31 | 'fieldset',
32 | 'form',
33 | 'frameset',
34 | 'h1',
35 | 'h2',
36 | 'h3',
37 | 'h4',
38 | 'h5',
39 | 'h6',
40 | 'head',
41 | 'html',
42 | 'iframe',
43 | 'layer',
44 | 'legend',
45 | 'object',
46 | 'ol',
47 | 'p',
48 | 'select',
49 | 'table',
50 | 'ul'
51 | ]
52 |
53 | /** Array of tag names where an end tag is forbidden. */
54 | CodeMirror.defaults['closeTagVoid'] = [
55 | 'area',
56 | 'base',
57 | 'br',
58 | 'col',
59 | 'command',
60 | 'embed',
61 | 'hr',
62 | 'img',
63 | 'input',
64 | 'keygen',
65 | 'link',
66 | 'meta',
67 | 'param',
68 | 'source',
69 | 'track',
70 | 'wbr'
71 | ]
72 |
73 | function innerState(cm, state) {
74 | return CodeMirror.innerMode(cm.getMode(), state).state
75 | }
76 |
77 | /**
78 | * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass.
79 | * - cm: The editor instance.
80 | * - ch: The character being processed.
81 | * - indent: Optional. An array of tag names to indent when closing. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option.
82 | * Pass false to disable indentation. Pass an array to override the default list of tag names.
83 | * - vd: Optional. An array of tag names that should not be closed. Omit to use the default void (end tag forbidden) tag list defined in the 'closeTagVoid' option. Ignored in xml mode.
84 | */
85 | CodeMirror.defineExtension('closeTag', function(cm, ch, indent, vd) {
86 | if (!cm.getOption('closeTagEnabled')) {
87 | throw CodeMirror.Pass
88 | }
89 |
90 | /*
91 | * Relevant structure of token:
92 | *
93 | * htmlmixed
94 | * className
95 | * state
96 | * htmlState
97 | * type
98 | * tagName
99 | * context
100 | * tagName
101 | * mode
102 | *
103 | * xml
104 | * className
105 | * state
106 | * tagName
107 | * type
108 | */
109 |
110 | var pos = cm.getCursor()
111 | var tok = cm.getTokenAt(pos)
112 | var state = innerState(cm, tok.state)
113 |
114 | if (state) {
115 | if (ch == '>') {
116 | var type = state.type
117 |
118 | if (tok.className == 'tag' && type == 'closeTag') {
119 | throw CodeMirror.Pass // Don't process the '>' at the end of an end-tag.
120 | }
121 |
122 | cm.replaceSelection('>') // Mode state won't update until we finish the tag.
123 | pos = { line: pos.line, ch: pos.ch + 1 }
124 | cm.setCursor(pos)
125 |
126 | tok = cm.getTokenAt(cm.getCursor())
127 | state = innerState(cm, tok.state)
128 | if (!state) throw CodeMirror.Pass
129 | var type = state.type
130 |
131 | if (tok.className == 'tag' && type != 'selfcloseTag') {
132 | var tagName = state.tagName
133 | if (tagName.length > 0 && shouldClose(cm, vd, tagName)) {
134 | insertEndTag(cm, indent, pos, tagName)
135 | }
136 | return
137 | }
138 |
139 | // Undo the '>' insert and allow cm to handle the key instead.
140 | cm.setSelection({ line: pos.line, ch: pos.ch - 1 }, pos)
141 | cm.replaceSelection('')
142 | } else if (ch == '/') {
143 | if (tok.className == 'tag' && tok.string == '<') {
144 | var ctx = state.context,
145 | tagName = ctx ? ctx.tagName : ''
146 | if (tagName.length > 0) {
147 | completeEndTag(cm, pos, tagName)
148 | return
149 | }
150 | }
151 | }
152 | }
153 |
154 | throw CodeMirror.Pass // Bubble if not handled
155 | })
156 |
157 | function insertEndTag(cm, indent, pos, tagName) {
158 | if (shouldIndent(cm, indent, tagName)) {
159 | cm.replaceSelection('\n\n' + tagName + '>', 'end')
160 | cm.indentLine(pos.line + 1)
161 | cm.indentLine(pos.line + 2)
162 | cm.setCursor({ line: pos.line + 1, ch: cm.getLine(pos.line + 1).length })
163 | } else {
164 | cm.replaceSelection('' + tagName + '>')
165 | cm.setCursor(pos)
166 | }
167 | }
168 |
169 | function shouldIndent(cm, indent, tagName) {
170 | if (typeof indent == 'undefined' || indent == null || indent == true) {
171 | indent = cm.getOption('closeTagIndent')
172 | }
173 | if (!indent) {
174 | indent = []
175 | }
176 | return indexOf(indent, tagName.toLowerCase()) != -1
177 | }
178 |
179 | function shouldClose(cm, vd, tagName) {
180 | if (cm.getOption('mode') == 'xml') {
181 | return true // always close xml tags
182 | }
183 | if (typeof vd == 'undefined' || vd == null) {
184 | vd = cm.getOption('closeTagVoid')
185 | }
186 | if (!vd) {
187 | vd = []
188 | }
189 | return indexOf(vd, tagName.toLowerCase()) == -1
190 | }
191 |
192 | // C&P from codemirror.js...would be nice if this were visible to utilities.
193 | function indexOf(collection, elt) {
194 | if (collection.indexOf) return collection.indexOf(elt)
195 | for (var i = 0, e = collection.length; i < e; ++i)
196 | if (collection[i] == elt) return i
197 | return -1
198 | }
199 |
200 | function completeEndTag(cm, pos, tagName) {
201 | cm.replaceSelection('/' + tagName + '>')
202 | cm.setCursor({ line: pos.line, ch: pos.ch + tagName.length + 2 })
203 | }
204 | })()
205 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/foldcode.js:
--------------------------------------------------------------------------------
1 | // the tagRangeFinder function is
2 | // Copyright (C) 2011 by Daniel Glazman
3 | // released under the MIT license (../../LICENSE) like the rest of CodeMirror
4 | CodeMirror.tagRangeFinder = function(cm, line, hideEnd) {
5 | var nameStartChar =
6 | 'A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'
7 | var nameChar = nameStartChar + '-:.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040'
8 | var xmlNAMERegExp = new RegExp('^[' + nameStartChar + '][' + nameChar + ']*')
9 |
10 | var lineText = cm.getLine(line)
11 | var found = false
12 | var tag = null
13 | var pos = 0
14 | while (!found) {
15 | pos = lineText.indexOf('<', pos)
16 | if (-1 == pos)
17 | // no tag on line
18 | return
19 | if (pos + 1 < lineText.length && lineText[pos + 1] == '/') {
20 | // closing tag
21 | pos++
22 | continue
23 | }
24 | // ok we weem to have a start tag
25 | if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) {
26 | // not a tag name...
27 | pos++
28 | continue
29 | }
30 | var gtPos = lineText.indexOf('>', pos + 1)
31 | if (-1 == gtPos) {
32 | // end of start tag not in line
33 | var l = line + 1
34 | var foundGt = false
35 | var lastLine = cm.lineCount()
36 | while (l < lastLine && !foundGt) {
37 | var lt = cm.getLine(l)
38 | var gt = lt.indexOf('>')
39 | if (-1 != gt) {
40 | // found a >
41 | foundGt = true
42 | var slash = lt.lastIndexOf('/', gt)
43 | if (-1 != slash && slash < gt) {
44 | var str = lineText.substr(slash, gt - slash + 1)
45 | if (!str.match(/\/\s*\>/)) {
46 | // yep, that's the end of empty tag
47 | if (hideEnd === true) l++
48 | return l
49 | }
50 | }
51 | }
52 | l++
53 | }
54 | found = true
55 | } else {
56 | var slashPos = lineText.lastIndexOf('/', gtPos)
57 | if (-1 == slashPos) {
58 | // cannot be empty tag
59 | found = true
60 | // don't continue
61 | } else {
62 | // empty tag?
63 | // check if really empty tag
64 | var str = lineText.substr(slashPos, gtPos - slashPos + 1)
65 | if (!str.match(/\/\s*\>/)) {
66 | // finally not empty
67 | found = true
68 | // don't continue
69 | }
70 | }
71 | }
72 | if (found) {
73 | var subLine = lineText.substr(pos + 1)
74 | tag = subLine.match(xmlNAMERegExp)
75 | if (tag) {
76 | // we have an element name, wooohooo !
77 | tag = tag[0]
78 | // do we have the close tag on same line ???
79 | if (-1 != lineText.indexOf('' + tag + '>', pos)) {
80 | // yep
81 | found = false
82 | }
83 | // we don't, so we have a candidate...
84 | } else found = false
85 | }
86 | if (!found) pos++
87 | }
88 |
89 | if (found) {
90 | var startTag =
91 | '(\\<\\/' +
92 | tag +
93 | '\\>)|(\\<' +
94 | tag +
95 | '\\>)|(\\<' +
96 | tag +
97 | '\\s)|(\\<' +
98 | tag +
99 | '$)'
100 | var startTagRegExp = new RegExp(startTag, 'g')
101 | var endTag = '' + tag + '>'
102 | var depth = 1
103 | var l = line + 1
104 | var lastLine = cm.lineCount()
105 | while (l < lastLine) {
106 | lineText = cm.getLine(l)
107 | var match = lineText.match(startTagRegExp)
108 | if (match) {
109 | for (var i = 0; i < match.length; i++) {
110 | if (match[i] == endTag) depth--
111 | else depth++
112 | if (!depth) {
113 | if (hideEnd === true) l++
114 | return l
115 | }
116 | }
117 | }
118 | l++
119 | }
120 | return
121 | }
122 | }
123 |
124 | CodeMirror.braceRangeFinder = function(cm, line, hideEnd) {
125 | var lineText = cm.getLine(line),
126 | at = lineText.length,
127 | startChar,
128 | tokenType
129 | for (;;) {
130 | var found = lineText.lastIndexOf('{', at)
131 | if (found < 0) break
132 | tokenType = cm.getTokenAt({ line: line, ch: found }).className
133 | if (!/^(comment|string)/.test(tokenType)) {
134 | startChar = found
135 | break
136 | }
137 | at = found - 1
138 | }
139 | if (startChar == null || lineText.lastIndexOf('}') > startChar) return
140 | var count = 1,
141 | lastLine = cm.lineCount(),
142 | end
143 | outer: for (var i = line + 1; i < lastLine; ++i) {
144 | var text = cm.getLine(i),
145 | pos = 0
146 | for (;;) {
147 | var nextOpen = text.indexOf('{', pos),
148 | nextClose = text.indexOf('}', pos)
149 | if (nextOpen < 0) nextOpen = text.length
150 | if (nextClose < 0) nextClose = text.length
151 | pos = Math.min(nextOpen, nextClose)
152 | if (pos == text.length) break
153 | if (cm.getTokenAt({ line: i, ch: pos + 1 }).className == tokenType) {
154 | if (pos == nextOpen) ++count
155 | else if (!--count) {
156 | end = i
157 | break outer
158 | }
159 | }
160 | ++pos
161 | }
162 | }
163 | if (end == null || end == line + 1) return
164 | if (hideEnd === true) end++
165 | return end
166 | }
167 |
168 | CodeMirror.indentRangeFinder = function(cm, line) {
169 | var tabSize = cm.getOption('tabSize')
170 | var myIndent = cm.getLineHandle(line).indentation(tabSize),
171 | last
172 | for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
173 | var handle = cm.getLineHandle(i)
174 | if (!/^\s*$/.test(handle.text)) {
175 | if (handle.indentation(tabSize) <= myIndent) break
176 | last = i
177 | }
178 | }
179 | if (!last) return null
180 | return last + 1
181 | }
182 |
183 | CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) {
184 | var folded = []
185 | if (markText == null)
186 | markText =
187 | '▼
%N%'
188 |
189 | function isFolded(cm, n) {
190 | for (var i = 0; i < folded.length; ++i) {
191 | var start = cm.lineInfo(folded[i].start)
192 | if (!start) folded.splice(i--, 1)
193 | else if (start.line == n) return { pos: i, region: folded[i] }
194 | }
195 | }
196 |
197 | function expand(cm, region) {
198 | cm.clearMarker(region.start)
199 | for (var i = 0; i < region.hidden.length; ++i) cm.showLine(region.hidden[i])
200 | }
201 |
202 | return function(cm, line) {
203 | cm.operation(function() {
204 | var known = isFolded(cm, line)
205 | if (known) {
206 | folded.splice(known.pos, 1)
207 | expand(cm, known.region)
208 | } else {
209 | var end = rangeFinder(cm, line, hideEnd)
210 | if (end == null) return
211 | var hidden = []
212 | for (var i = line + 1; i < end; ++i) {
213 | var handle = cm.hideLine(i)
214 | if (handle) hidden.push(handle)
215 | }
216 | var first = cm.setMarker(line, markText)
217 | var region = { start: first, hidden: hidden }
218 | cm.onDeleteLine(first, function() {
219 | expand(cm, region)
220 | })
221 | folded.push(region)
222 | }
223 | })
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/util/formatting.js:
--------------------------------------------------------------------------------
1 | // ============== Formatting extensions ============================
2 | ;(function() {
3 | // Define extensions for a few modes
4 | CodeMirror.extendMode('css', {
5 | commentStart: '/*',
6 | commentEnd: '*/',
7 | wordWrapChars: [';', '\\{', '\\}'],
8 | autoFormatLineBreaks: function(text) {
9 | return text.replace(new RegExp('(;|\\{|\\})([^\r\n])', 'g'), '$1\n$2')
10 | }
11 | })
12 |
13 | function jsNonBreakableBlocks(text) {
14 | var nonBreakableRegexes = [
15 | /for\s*?\((.*?)\)/,
16 | /\"(.*?)(\"|$)/,
17 | /\'(.*?)(\'|$)/,
18 | /\/\*(.*?)(\*\/|$)/,
19 | /\/\/.*/
20 | ]
21 | var nonBreakableBlocks = []
22 | for (var i = 0; i < nonBreakableRegexes.length; i++) {
23 | var curPos = 0
24 | while (curPos < text.length) {
25 | var m = text.substr(curPos).match(nonBreakableRegexes[i])
26 | if (m != null) {
27 | nonBreakableBlocks.push({
28 | start: curPos + m.index,
29 | end: curPos + m.index + m[0].length
30 | })
31 | curPos += m.index + Math.max(1, m[0].length)
32 | } else {
33 | // No more matches
34 | break
35 | }
36 | }
37 | }
38 | nonBreakableBlocks.sort(function(a, b) {
39 | return a.start - b.start
40 | })
41 |
42 | return nonBreakableBlocks
43 | }
44 |
45 | CodeMirror.extendMode('javascript', {
46 | commentStart: '/*',
47 | commentEnd: '*/',
48 | wordWrapChars: [';', '\\{', '\\}'],
49 |
50 | autoFormatLineBreaks: function(text) {
51 | var curPos = 0
52 | var reLinesSplitter = /(;|\{|\})([^\r\n;])/g
53 | var nonBreakableBlocks = jsNonBreakableBlocks(text)
54 | if (nonBreakableBlocks != null) {
55 | var res = ''
56 | for (var i = 0; i < nonBreakableBlocks.length; i++) {
57 | if (nonBreakableBlocks[i].start > curPos) {
58 | // Break lines till the block
59 | res += text
60 | .substring(curPos, nonBreakableBlocks[i].start)
61 | .replace(reLinesSplitter, '$1\n$2')
62 | curPos = nonBreakableBlocks[i].start
63 | }
64 | if (
65 | nonBreakableBlocks[i].start <= curPos &&
66 | nonBreakableBlocks[i].end >= curPos
67 | ) {
68 | // Skip non-breakable block
69 | res += text.substring(curPos, nonBreakableBlocks[i].end)
70 | curPos = nonBreakableBlocks[i].end
71 | }
72 | }
73 | if (curPos < text.length)
74 | res += text.substr(curPos).replace(reLinesSplitter, '$1\n$2')
75 | return res
76 | } else {
77 | return text.replace(reLinesSplitter, '$1\n$2')
78 | }
79 | }
80 | })
81 |
82 | CodeMirror.extendMode('xml', {
83 | commentStart: '',
85 | wordWrapChars: ['>'],
86 |
87 | autoFormatLineBreaks: function(text) {
88 | var lines = text.split('\n')
89 | var reProcessedPortion = new RegExp(
90 | '(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)'
91 | )
92 | var reOpenBrackets = new RegExp('<', 'g')
93 | var reCloseBrackets = new RegExp('(>)([^\r\n])', 'g')
94 | for (var i = 0; i < lines.length; i++) {
95 | var mToProcess = lines[i].match(reProcessedPortion)
96 | if (mToProcess != null && mToProcess.length > 3) {
97 | // The line starts with whitespaces and ends with whitespaces
98 | lines[i] =
99 | mToProcess[1] +
100 | mToProcess[2]
101 | .replace(reOpenBrackets, '\n$&')
102 | .replace(reCloseBrackets, '$1\n$2') +
103 | mToProcess[3]
104 | continue
105 | }
106 | }
107 | return lines.join('\n')
108 | }
109 | })
110 |
111 | function localModeAt(cm, pos) {
112 | return CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(pos).state).mode
113 | }
114 |
115 | function enumerateModesBetween(cm, line, start, end) {
116 | var outer = cm.getMode(),
117 | text = cm.getLine(line)
118 | if (end == null) end = text.length
119 | if (!outer.innerMode) return [{ from: start, to: end, mode: outer }]
120 | var state = cm.getTokenAt({ line: line, ch: start }).state
121 | var mode = CodeMirror.innerMode(outer, state).mode
122 | var found = [],
123 | stream = new CodeMirror.StringStream(text)
124 | stream.pos = stream.start = start
125 | for (;;) {
126 | outer.token(stream, state)
127 | var curMode = CodeMirror.innerMode(outer, state).mode
128 | if (curMode != mode) {
129 | var cut = stream.start
130 | // Crappy heuristic to deal with the fact that a change in
131 | // mode can occur both at the end and the start of a token,
132 | // and we don't know which it was.
133 | if (mode.name == 'xml' && text.charAt(stream.pos - 1) == '>')
134 | cut = stream.pos
135 | found.push({ from: start, to: cut, mode: mode })
136 | start = cut
137 | mode = curMode
138 | }
139 | if (stream.pos >= end) break
140 | stream.start = stream.pos
141 | }
142 | if (start < end) found.push({ from: start, to: end, mode: mode })
143 | return found
144 | }
145 |
146 | // Comment/uncomment the specified range
147 | CodeMirror.defineExtension('commentRange', function(isComment, from, to) {
148 | var curMode = localModeAt(this, from),
149 | cm = this
150 | this.operation(function() {
151 | if (isComment) {
152 | // Comment range
153 | cm.replaceRange(curMode.commentEnd, to)
154 | cm.replaceRange(curMode.commentStart, from)
155 | if (from.line == to.line && from.ch == to.ch)
156 | // An empty comment inserted - put cursor inside
157 | cm.setCursor(from.line, from.ch + curMode.commentStart.length)
158 | } else {
159 | // Uncomment range
160 | var selText = cm.getRange(from, to)
161 | var startIndex = selText.indexOf(curMode.commentStart)
162 | var endIndex = selText.lastIndexOf(curMode.commentEnd)
163 | if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
164 | // Take string till comment start
165 | selText =
166 | selText.substr(0, startIndex) +
167 | // From comment start till comment end
168 | selText.substring(
169 | startIndex + curMode.commentStart.length,
170 | endIndex
171 | ) +
172 | // From comment end till string end
173 | selText.substr(endIndex + curMode.commentEnd.length)
174 | }
175 | cm.replaceRange(selText, from, to)
176 | }
177 | })
178 | })
179 |
180 | // Applies automatic mode-aware indentation to the specified range
181 | CodeMirror.defineExtension('autoIndentRange', function(from, to) {
182 | var cmInstance = this
183 | this.operation(function() {
184 | for (var i = from.line; i <= to.line; i++) {
185 | cmInstance.indentLine(i, 'smart')
186 | }
187 | })
188 | })
189 |
190 | // Applies automatic formatting to the specified range
191 | CodeMirror.defineExtension('autoFormatRange', function(from, to) {
192 | var cm = this
193 | cm.operation(function() {
194 | for (var cur = from.line, end = to.line; cur <= end; ++cur) {
195 | var f = { line: cur, ch: cur == from.line ? from.ch : 0 }
196 | var t = { line: cur, ch: cur == end ? to.ch : null }
197 | var modes = enumerateModesBetween(cm, cur, f.ch, t.ch),
198 | mangled = ''
199 | var text = cm.getRange(f, t)
200 | for (var i = 0; i < modes.length; ++i) {
201 | var part =
202 | modes.length > 1 ? text.slice(modes[i].from, modes[i].to) : text
203 | if (mangled) mangled += '\n'
204 | if (modes[i].mode.autoFormatLineBreaks) {
205 | mangled += modes[i].mode.autoFormatLineBreaks(part)
206 | } else mangled += text
207 | }
208 | if (mangled != text) {
209 | for (
210 | var count = 0, pos = mangled.indexOf('\n');
211 | pos != -1;
212 | pos = mangled.indexOf('\n', pos + 1), ++count
213 | ) {}
214 | cm.replaceRange(mangled, f, t)
215 | cur += count
216 | end += count
217 | }
218 | }
219 | for (var cur = from.line + 1; cur <= end; ++cur)
220 | cm.indentLine(cur, 'smart')
221 | cm.setSelection(from, cm.getCursor(false))
222 | })
223 | })
224 | })()
225 |
--------------------------------------------------------------------------------
/scripts/editor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * fix `width, height` properties on
3 | * transform them to inline css values
4 | */
5 | const fixImageDimension = img => {
6 | let width = parseInt(img.getAttribute('width'))
7 | let height = parseInt(img.getAttribute('height'))
8 |
9 | if (!isNaN(width)) {
10 | img.style.width = width + 'px'
11 | img.removeAttribute('width')
12 | }
13 | if (!isNaN(height)) {
14 | img.style.height = height + 'px'
15 | img.removeAttribute('height')
16 | }
17 | }
18 |
19 | /**
20 | * weixin preprocess
21 | */
22 | const wxProof = content => {
23 | let offlineDIV = divWrap(content)
24 | let query = (s, cb) => getAll(s, offlineDIV).forEach(el => cb && cb(el))
25 |
26 | // remove `meta`
27 | query('meta', a => a.remove())
28 | query('link', a => a.remove())
29 | query('script', a => a.remove())
30 |
31 | // 处理代码换行
32 | query('pre', pre => {
33 | // pre>code.lang-x
34 | let codeElem = pre.firstElementChild
35 | let lines = codeElem.innerHTML.trim().split('\n')
36 |
37 | let match = codeElem.className.match(/(?:^|\s)lang-([^\s]+)/)
38 | let lang = (match && match[1]) || ''
39 |
40 | pre.innerHTML = lines
41 | .map(line => {
42 | line = line.replace(/(^\s+)/g, m => ' '.repeat(m.length))
43 | return !!line.trim()
44 | ? `${line}
`
45 | : `
`
46 | })
47 | .join('')
48 |
49 | pre.classList.add(`language-${lang}`)
50 | })
51 |
52 | // 处理行间 code 样式
53 | query('code', code => {
54 | let span = create('span')
55 | span.innerHTML = code.innerHTML
56 | span.className = 'code'
57 | code.parentNode.replaceChild(span, code)
58 | })
59 |
60 | // 处理行间 li 样式
61 | query('li', li => {
62 | li.innerHTML = '' + li.innerHTML + '
'
63 | })
64 |
65 | // blockquote 添加类名
66 | query('blockquote', blockquote => {
67 | blockquote.className = 'blockquote'
68 |
69 | let lines = blockquote.firstElementChild.innerHTML.trim().split('\n')
70 |
71 | lines = lines.map(line => {
72 | line = line.replace(/(^\s+)/g, m => ' '.repeat(m.length))
73 | return !!line.trim() ? `${line}
` : `
`
74 | })
75 | blockquote.innerHTML = lines.join('')
76 | })
77 |
78 | // 所有 pre 外面包裹一层 blockquote
79 | // 使用 figure 等标签都不行
80 | // 会导致微信编辑器中粘贴时会多出一个 p 标签
81 | query('pre', pre => {
82 | let clone = pre.cloneNode()
83 | clone.innerHTML = pre.innerHTML
84 |
85 | let wrap = create('blockquote')
86 | wrap.className = 'code-wrap'
87 | wrap.appendChild(clone)
88 |
89 | pre.parentNode.replaceChild(wrap, pre)
90 | })
91 |
92 | // 处理所有 a 链接
93 | // query('a', a => {
94 | // let span = create('span');
95 | // span.innerHTML = a.innerHTML;
96 | // span.className = 'link';
97 | // a.parentNode.replaceChild(span, a);
98 | // });
99 |
100 | // img 处理
101 | // 只针对单个成一段的 img
102 | query('img', img => {
103 | // case:
104 | var isFirstElemChild = img.parentNode.firstElementChild === img
105 | if (isFirstElemChild) {
106 | img.parentNode.className += 'img-wrap'
107 | }
108 | fixImageDimension(img)
109 | })
110 | // h2 处理
111 | // h2标签内嵌span
112 | // query('h2', img => {
113 | // // case:
114 | // var isFirstElemChild = img.parentNode.firstElementChild === img;
115 | // if (isFirstElemChild) {
116 | // img.parentNode.className += 'img-wrap';
117 | // }
118 | // fixImageDimension(img);
119 | // });
120 |
121 | // list
122 | // let listNum = create('img');
123 | // listNum.src = 'https://p1.ssl.qhimg.com/t01fb3e28751b757288.png';
124 | // query('li', li => {
125 | // li.insertAdjacentElement('afterbegin', listNum.cloneNode());
126 | // });
127 |
128 | return offlineDIV.innerHTML
129 | }
130 |
131 | /**
132 | * render markdown content
133 | */
134 | const renderPreview = function(targetDOM, markdown) {
135 | store.set('__lastContent', markdown)
136 |
137 | var weeklyStart = `
138 | 编者按:这里是前言,可以随便写~~~~
139 | `
140 | var weeklyEnd = `
141 | 关于奇舞周刊
142 | 《奇舞周刊》是360公司专业前端团队「奇舞团 」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。
143 | 奇舞团是360集团最大的大前端团队,代表集团参与W3C和Ecma会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队Leader等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
144 |
145 | `
146 |
147 | //添加文章前边部门和文章末尾部门
148 | targetDOM.innerHTML = wxProof(
149 | weeklyStart + window.marked(markdown) + weeklyEnd
150 | )
151 | }
152 |
153 | /**
154 | * transform clipboard data (text/html)
155 | * to markdown text
156 | */
157 | const generateMdText = content => {
158 | return toMarkdown(content, {
159 | gfm: false,
160 | converters: [
161 | {
162 | filter: 'code',
163 | replacement: function(t, n) {
164 | return /\n/.test(t) ? t : '`' + t + '`'
165 | }
166 | },
167 | {
168 | filter: 'pre',
169 | replacement: function(t, n) {
170 | let lang = ''
171 | let result = t
172 |
173 | let firstChild = n.children[0]
174 | if (firstChild) {
175 | let match = firstChild.className.match(
176 | /(^|\s)(lang|language)-([^\s]+)/
177 | )
178 | lang = (match && match[3]) || ''
179 | }
180 | // TODO: deal with language...
181 | // this is a really annoying thing
182 | switch (lang) {
183 | case 'js':
184 | case 'javascript':
185 | result = js_beautify(t)
186 | break
187 | case 'css':
188 | result = css_beautify(t)
189 | break
190 | case 'html':
191 | result = html_beautify(t)
192 | break
193 | }
194 |
195 | return '\n```' + lang + '\n' + result + '\n```\n'
196 | }
197 | },
198 | {
199 | filter: 'span',
200 | replacement: function(t, n) {
201 | return t
202 | }
203 | },
204 | {
205 | filter: ['section', 'div'],
206 | replacement: function(t, n) {
207 | return '\n\n' + t + '\n\n'
208 | }
209 | }
210 | ]
211 | })
212 | }
213 |
214 | // configure window.marked
215 | window.marked.setOptions({
216 | highlight(code, lang, callback) {
217 | lang = lang === 'js' ? 'javascript' : lang || 'javascript'
218 | let langConfig = Prism.languages[lang] || Prism.languages.javascript
219 | return Prism.highlight(code, langConfig)
220 | }
221 | })
222 |
223 | window.MdEditor = CodeMirror.fromTextArea(getDOM('#jsMdEditor'), {
224 | mode: 'gfm',
225 | lineNumbers: false,
226 | matchBrackets: true,
227 | lineWrapping: true,
228 | theme: 'base16-light',
229 | extraKeys: {
230 | Enter: 'newlineAndIndentContinueMarkdownList'
231 | }
232 | })
233 |
234 | // getter/setter of editor's text content
235 | MdEditor.val = function(val) {
236 | if (!val) {
237 | return this.getValue()
238 | } else {
239 | return this.setValue(val)
240 | }
241 | }
242 |
243 | // replace/insert content
244 | MdEditor.paste = function(data) {
245 | let cursor = this.getCursor()
246 | // replace
247 | if (this.somethingSelected()) {
248 | this.replaceSelection(data)
249 | }
250 | // insert
251 | else {
252 | this.replaceRange(data, cursor)
253 | }
254 | }
255 |
256 | // view sync
257 | MdEditor.on('change', editor => {
258 | renderPreview(getDOM('.md-preview'), editor.val())
259 | })
260 |
261 | // paste on editor
262 | getDOM('.md-editor').addEventListener('paste', function(e) {
263 | e.preventDefault()
264 |
265 | let data = e.clipboardData.getData('text/html')
266 | let markdown = ''
267 |
268 | if (!data) {
269 | markdown = e.clipboardData.getData('text/plain')
270 | } else {
271 | let divDOM = divWrap(data)
272 | let query = (s, cb) => getAll(s, divDOM).forEach(el => cb && cb(el))
273 |
274 | query('*', el => {
275 | el.removeAttribute('style')
276 | el.removeAttribute('class')
277 | })
278 | query('meta', a => a.remove())
279 | query('link', a => a.remove())
280 | query('script', a => a.remove())
281 |
282 | markdown = generateMdText(divDOM.innerHTML)
283 | }
284 |
285 | MdEditor.paste(markdown)
286 | })
287 |
--------------------------------------------------------------------------------
/content_script/blog.inject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 1. upload images on notification `upload`
3 | * 2. inject HTML on notification `inject`
4 | */
5 |
6 | /**
7 | * basic utils
8 | */
9 | const qs = sel => document.querySelector(sel)
10 | const qsa = sel => Array.from(document.querySelectorAll(sel))
11 | const isVisible = el => {
12 | let rect = el.getBoundingClientRect()
13 | return rect.width > 0 && rect.height > 0
14 | }
15 |
16 | /**
17 | * weixin editor element BODY
18 | */
19 | const getWxEditor = () => {
20 | let visibleFrames = qsa('iframe[id^=ueditor_]').filter(isVisible)
21 | if (!visibleFrames.length) {
22 | return null
23 | }
24 | return visibleFrames[0].contentDocument.body
25 | }
26 |
27 | /**
28 | * random filename
29 | *
30 | * @retrurn {String}
31 | */
32 | const guid = function() {
33 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
34 | .replace(/[xy]/g, function(c) {
35 | var r = (Math.random() * 16) | 0,
36 | v = c == 'x' ? r : (r & 0x3) | 0x8
37 | return v.toString(16)
38 | })
39 | .toUpperCase()
40 | }
41 |
42 | /**
43 | * random date in the last 50 days
44 | *
45 | * @return {Date}
46 | */
47 | const getRandomDate = function() {
48 | return new Date(Date.now() - Math.random() * 1000 * 3600 * 24 * 50)
49 | }
50 |
51 | /**
52 | * dataURI to Blob
53 | *
54 | * @param {Base64 String} dataURI
55 | * @return {Blob}
56 | */
57 | const dataURItoBlob = function(dataURI) {
58 | var byteString, mimestring
59 |
60 | if (dataURI.split(',')[0].indexOf('base64') !== -1) {
61 | byteString = atob(dataURI.split(',')[1])
62 | } else {
63 | byteString = decodeURI(dataURI.split(',')[1])
64 | }
65 |
66 | mimestring = dataURI
67 | .split(',')[0]
68 | .split(':')[1]
69 | .split(';')[0]
70 |
71 | var content = new Array()
72 | for (var i = 0; i < byteString.length; i++) {
73 | content[i] = byteString.charCodeAt(i)
74 | }
75 |
76 | var blobImg,
77 | arrays = new Uint8Array(content)
78 | try {
79 | blobImg = new Blob([arrays], {
80 | type: mimestring
81 | })
82 | } catch (e) {}
83 | return blobImg
84 | }
85 |
86 | /**
87 | * get related params violently
88 | * so that we can upload images here,
89 | * all we need are held by `window.wx`,
90 | * but we don't have access to global vars here,
91 | * yet we can reach DOMs.
92 | * and fortunately `window.wx` was assigned in a
93 | * inline script, aha, comes the trick!
94 | */
95 | const uploadImage = (function() {
96 | // 暴力破解
97 | var scripts = Array.from(
98 | document.querySelectorAll('script[type="text/javascript"]')
99 | ).filter(s => s.src === '')
100 | var len = scripts.length
101 | var content = ''
102 |
103 | while (len--)
104 | if ((content = scripts[len].textContent)) {
105 | if (/try\s*\{\s*[\s\S]*window\.wx\s*\=\s*\{\s*\n/m.test(content)) {
106 | try {
107 | eval(content)
108 | } catch (e) {}
109 | break
110 | }
111 | }
112 |
113 | if (!content || !wx) {
114 | console.warn('window.wx.data parsing failed!')
115 | return function() {
116 | Promise.resolve('{}')
117 | }
118 | }
119 |
120 | // groupid=3
121 | // put all images to the category `文章配图`,
122 | // in this way, we'll get less bothered.
123 | // by following API, we can get all available categories:
124 | // https://mp.weixin.qq.com/cgi-bin/filepage?1=1&token=129636898&lang=zh_CN&token=129636898&lang=zh_CN&f=json&ajax=1&random=0.8964656583498374&group_id=0&begin=0&count=10&type=2
125 | // in this way, customization is no fairy.
126 | var url = `https://mp.weixin.qq.com/cgi-bin/filetransfer?action=upload_material&f=json&scene=1&writetype=doublewrite&groupid=3&ticket_id=${wx.data.user_name}&ticket=${wx.data.ticket}&svr_time=${wx.data.time}&token=${wx.data.t}&lang=zh_CN&seq=1`
127 |
128 | return function(base64) {
129 | var blob = dataURItoBlob(base64)
130 | var fileType = blob.type
131 | var typeName = fileType.replace('image/', '')
132 | var fileExt = typeName === 'svg+xml' ? 'svg' : typeName
133 | var fileName = `${guid()}.${fileExt}`
134 |
135 | // why not Blob directly?
136 | // since the prop `filename` will not change,
137 | // uploading work fails.
138 | // show time for File API:
139 | // see https://www.w3.org/TR/FileAPI/
140 | var file = new File([blob], fileName, {
141 | type: fileType,
142 | lastModified: getRandomDate()
143 | })
144 | var fileSize = file.size
145 |
146 | return new Promise(function(resolve, reject) {
147 | // validate first
148 | // 1. invalid type
149 | if (!/jpeg|png|gif/i.test(fileExt)) {
150 | resolve(
151 | JSON.stringify(ERROR_IMAGES[fileExt] || ERROR_IMAGES['typeError'])
152 | )
153 | return
154 | }
155 | // 2. size of image too large
156 | if (fileSize >= 2 * 1024 * 1024) {
157 | resolve(
158 | JSON.stringify(ERROR_IMAGES[fileExt === 'gif' ? 'gif' : 'sizeError'])
159 | )
160 | return
161 | }
162 |
163 | var form = new FormData()
164 | form.append('file', file)
165 | form.append('name', fileName)
166 | form.append('type', fileType)
167 | form.append('size', fileSize)
168 | form.append('id', 'WU_FILE_0')
169 | form.append('lastModifiedDate', file.lastModifiedDate)
170 |
171 | var xhr = new XMLHttpRequest()
172 | xhr.open('POST', url)
173 | xhr.send(form)
174 | xhr.onerror = xhr.ontimeout = reject
175 | xhr.onreadystatechange = () => {
176 | if (/^2\d{2}$/.test(xhr.status) && xhr.readyState === 4) {
177 | resolve(xhr.response)
178 | }
179 | }
180 | })
181 | }
182 | })()
183 |
184 | /**
185 | * error warning images
186 | */
187 | const ERROR_IMAGES = {
188 | gif: {
189 | cdn_url:
190 | 'https://mmbiz.qlogo.cn/mmbiz_png/wic3OZ3sEjfwAGmvH7C0ROMb9aAfjvickkJI3TurmjVUd20tGB8fd1kgddYI45OkvcrZlvnu4vAjkS0ibSiafHco5g/0?wx_fmt=png',
191 | cdn_id: 515897144,
192 | is_placeholder: true
193 | },
194 | svg: {
195 | cdn_url:
196 | 'https://mmbiz.qlogo.cn/mmbiz_png/wic3OZ3sEjfwAGmvH7C0ROMb9aAfjvickkfIVWBgj34x3hIV71YeDr9S8qCHPZquiaIm5db4jcI4s379QcSyCfAsA/0?wx_fmt=png',
197 | cdn_id: 515897145,
198 | is_placeholder: true
199 | },
200 | webp: {
201 | cdn_url:
202 | 'https://mmbiz.qlogo.cn/mmbiz_png/wic3OZ3sEjfyrMXEzibBfqZsMhpl1aoibcK5SJ6Aib3exfj1v0UgNrXUvpibU0dwyESN3uHda5PGRwIRnxL8tA8FqSQ/0?wx_fmt=png',
203 | cdn_id: 515897167,
204 | is_placeholder: true
205 | },
206 | sizeError: {
207 | cdn_url:
208 | 'https://mmbiz.qlogo.cn/mmbiz_png/wic3OZ3sEjfyrMXEzibBfqZsMhpl1aoibcKj4Cvx37S18WjHrvS8TP4eybIhkdr07B3jzsQbeWUia9hIulnUeNDXSQ/0?wx_fmt=png',
209 | cdn_id: 515897166,
210 | is_placeholder: true
211 | },
212 | typeError: {
213 | cdn_url:
214 | 'https://mmbiz.qlogo.cn/mmbiz_png/wic3OZ3sEjfyrMXEzibBfqZsMhpl1aoibcKE20R9gsYOuiaz5AZ1ezUbT7iaIuliawQowL0Dibj3ElvJr70K6Q1ChyicdA/0?wx_fmt=png',
215 | cdn_id: 515897169,
216 | is_placeholder: true
217 | }
218 | }
219 |
220 | const onMessage = {
221 | noop() {},
222 |
223 | inject(message) {
224 | // hide editor "placeholder"
225 | qs('.editor_content_placeholder ').style.display = 'none'
226 |
227 | let editor = getWxEditor()
228 | editor.innerHTML = message.data
229 |
230 | // author
231 | if (message.author) {
232 | //qs('#author').value = message.author.slice(0, 8);
233 | }
234 |
235 | // url
236 | if (message.url) {
237 | if (!qs('.js_url_checkbox').checked) {
238 | qs('.js_url_checkbox').click()
239 | }
240 |
241 | qs('.js_url').value = message.url
242 | }
243 |
244 | // TODO: we can do more...
245 | },
246 |
247 | upload(message) {
248 | uploadImage(message.data)
249 | .then(res => JSON.parse(res))
250 | .then(data => {
251 | let msg = {
252 | hash: message.hash,
253 | type: message.type
254 | }
255 |
256 | if (data && data['cdn_url']) {
257 | msg.data = {
258 | is_placeholder: data['is_placeholder'],
259 | cdn_url: data['cdn_url'],
260 | cdn_id: data['content']
261 | }
262 | } else {
263 | msg.err = JSON.stringify(data)
264 | }
265 |
266 | return msg
267 | })
268 | .catch(e => ({ err: e }))
269 | .then(msg => chrome.runtime.sendMessage(null, msg))
270 | }
271 | }
272 |
273 | chrome.runtime.onMessage.addListener(function(message) {
274 | console.info(message)
275 | ;(onMessage[message.type] || onMessage['noop'])(message)
276 | })
277 |
--------------------------------------------------------------------------------
/scripts/weeklyInjector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 奇舞周刊微信工具
3 | */
4 | const weeklyInjector = {
5 | element: getDOM('.pop-form-mask'),
6 | cache: {},
7 | abstract: [
8 | '又到周刊时间啦,快快看本期周刊君为你准备了什么吧~',
9 | '新一期周刊出炉啦~周末转眼又来到了,周末愉快~',
10 | '周刊君如约而至~周末愉快~',
11 | '一周飞逝~周刊君如约而至~周末愉快~',
12 | '快,奇舞周刊喊你来充电啦~'
13 | ],
14 | leading: [
15 | '约定好的周五又来啦,今天的天空特别蓝,周刊君的心情棒棒哒~ 周末嗨起来~',
16 | '时间过得好快,转眼又到周五~ 新一期周刊出炉啦,虽然周刊君的 bug 还没改完 TAT,啊快赐予周刊君力量!~',
17 | '时间过得好快,又到周五啦~ 周刊君赴约来啦~',
18 | '来来来~ 转眼间又到周五啦~ 周末到啦,不要放弃学习哟~ 加油吧童鞋~',
19 | '嘿又到周五啦~ 又一个周末即将来临,奇舞周刊如约而至。好开森~'
20 | ],
21 | // get random text
22 | getText: function(key) {
23 | let data = (Array.isArray(this[key]) && this[key]) || []
24 | return data[~~(data.length * Math.random())]
25 | },
26 |
27 | // newest index
28 | fetchNewestIndex: function() {
29 | return new Promise(function(resolve, reject) {
30 | let xhr = new XMLHttpRequest()
31 | xhr.open('get', 'https://weekly.75team.com/rss.php')
32 | xhr.send(null)
33 |
34 | xhr.onreadystatechange = function() {
35 | if (!/^2\d{2}$/.test(xhr.status) || xhr.readyState < 4) return
36 | let response = xhr.responseText
37 | let reNewest = / https?\:\/\/www\.75team.com\/weekly\/issue(\d{1,5})\.html<\/link>/
38 | let match = response.match(reNewest)
39 |
40 | if (match && match[1]) {
41 | resolve(match[1])
42 | } else {
43 | reject()
44 | }
45 | }
46 |
47 | xhr.onerror = xhr.ontimeout = function() {
48 | reject()
49 | }
50 | })
51 | },
52 |
53 | /**
54 | * 抓取周刊数据
55 | * @param {Number} index 期数
56 | * @return {Promise}
57 | */
58 | fetchWeekly: function(index) {
59 | let self = this
60 | let cache = this.cache
61 | let url = `https://weekly.75team.com/issue${index}.html`
62 | let err = {
63 | error: `第${index}期周刊不存在!`
64 | }
65 |
66 | this.origin = url
67 | getDOM('.net-err').style.display = 'none'
68 | if (cache[index]) {
69 | return Promise.resolve(cache[index])
70 | }
71 |
72 | return fetch(url)
73 | .then(r => r.text())
74 | .then(function(text) {
75 | let data = text.match(
76 | /<\/section>\s*([\s\S]+)<\/ul>\s*/
77 | )
78 |
79 | if (data && data[0]) {
80 | cache[index] = self.getWeeklyAbstract(data[0])
81 | console.info(`第 ${index} 期周刊数据已缓存...`, cache[index])
82 | return cache[index]
83 | }
84 |
85 | return err
86 | })
87 | .catch(function(e) {
88 | getDOM('.net-err').style.display = 'inline'
89 | console.error(e, err)
90 | return err
91 | })
92 | },
93 |
94 | /**
95 | * 从周刊页面中拿到数据摘要
96 | * @param {String} data 字符串
97 | * @return {Object}
98 | */
99 | getWeeklyAbstract: function(html) {
100 | let div = document.createElement('div')
101 | div.innerHTML = html
102 |
103 | let listItems = Array.from(div.querySelectorAll('ul li'))
104 | let data = listItems.reduce(
105 | function(prev, curr) {
106 | let category = ''
107 | let children = null
108 | let link = null
109 | let provider = ''
110 |
111 | if (!curr.classList.contains('article')) {
112 | category = curr.innerText.trim()
113 | prev[category] = prev[category] || []
114 | prev['_last_'] = prev[category]
115 | prev['_key_'].push(category)
116 | } else {
117 | children = curr.children
118 | link = children[0].children[0]
119 |
120 | provider = [].filter.call(children[2].children, function(meta) {
121 | return meta.classList.contains('provider')
122 | })[0]
123 |
124 | prev['_last_'].push({
125 | title: link.innerText.trim(),
126 | url: link.href,
127 | desc: children[1].innerText.trim(),
128 | provider: (provider && provider.innerText.trim()) || ''
129 | })
130 | }
131 | return prev
132 | },
133 | { _key_: [] }
134 | )
135 |
136 | delete data['_last_']
137 | return data
138 | },
139 |
140 | fileToURL: function(fileId, urlId) {
141 | let upload = this.upload
142 |
143 | let promise = null
144 | let url = getDOM(urlId).value.trim()
145 | let coverFileDOM = getDOM(fileId)
146 | if (coverFileDOM.files[0]) {
147 | promise = blobToDataURI(coverFileDOM.files[0]).then(u => {
148 | console.log(u)
149 | return upload(u)
150 | })
151 | } else if (/^https?:\/\//.test(url)) {
152 | promise = Promise.resolve(url).then(upload)
153 | }
154 | return promise
155 | },
156 |
157 | upload: function(url) {
158 | if (!url) {
159 | return null
160 | }
161 |
162 | let hash = md5(url)
163 | if (store.has(hash)) {
164 | return store.get(hash)
165 | }
166 |
167 | return requestUpload(url)
168 | .then(waitUploadDone)
169 | .then(({ type, hash, data, err }) => {
170 | if (err) {
171 | console.error(err)
172 | return
173 | }
174 | // cache data
175 | store.set(hash, data)
176 | return data
177 | })
178 | },
179 |
180 | bindEvents: function() {
181 | let self = this
182 |
183 | this.element.addEventListener('click', function(e) {
184 | if (e.target === this) {
185 | this.style.display = 'none'
186 | }
187 | })
188 |
189 | // 期数变化的时候
190 | // 先获取周刊数据缓存下来
191 | getDOM('#weeklyIndex').addEventListener('change', function() {
192 | self.index = +this.value
193 | self.fetchWeekly(+this.value)
194 | })
195 |
196 | // 刷新摘要
197 | getDOM('#randomAbstract').addEventListener('click', function() {
198 | getDOM('#weeklyAbstract').value = self.getText('abstract')
199 | })
200 |
201 | // 刷新导语
202 | getDOM('#randomLeading').addEventListener('click', function() {
203 | getDOM('#weeklyLeading').value = self.getText('leading')
204 | })
205 |
206 | // 动图
207 | getDOM('#weeklyAnimFile').addEventListener('change', function() {
208 | getDOM('#weeklyAnimURL').value = this.value
209 | })
210 |
211 | // 封面
212 | getDOM('#weeklyCoverFile').addEventListener('change', function() {
213 | getDOM('#weeklyCoverURL').value = this.value
214 | })
215 |
216 | // done
217 | let isSubmiting = false
218 |
219 | getDOM('#weeklyDone').addEventListener('click', function() {
220 | if (isSubmiting) {
221 | return
222 | }
223 | isSubmiting = true
224 | Loading.show()
225 |
226 | let dataStore = {
227 | index: self.index,
228 | origin: self.origin,
229 | author: getDOM('#weeklyAuthor').value,
230 | dataStore: self.cache[self.index],
231 | abstract: getDOM('#weeklyAbstract').value,
232 | leading: getDOM('#weeklyLeading').value,
233 | animSrc: getDOM('#weeklyAnimSrc').value
234 | // coverURL: getDOM('#weeklyCoverURL').value,
235 | // coverID: 100000,
236 | // animURL: getDOM('#weeklyAnimSrc').value
237 | }
238 |
239 | Promise.all([
240 | self.fileToURL('#weeklyCoverFile', '#weeklyCoverURL'),
241 | self.fileToURL('#weeklyAnimFile', '#weeklyAnimURL')
242 | ])
243 | .then(([cover, anim]) => {
244 | dataStore.coverURL = (cover && cover.cdn_url) || ''
245 | dataStore.coverID = (cover && cover.cdn_id) || 0
246 | dataStore.animURL = (anim && anim.cdn_url) || ''
247 | })
248 | .then(() => {
249 | console.log(dataStore)
250 |
251 | // inject
252 | sendMsg(WX_EDITOR_PATTERN, {
253 | type: 'inject_weekly',
254 | info: dataStore
255 | })
256 | // focus
257 | focusTab(WX_EDITOR_PATTERN)
258 | })
259 | .then(() => (isSubmiting = false))
260 | .catch(() => (isSubmiting = false))
261 | .then(() => Loading.hide())
262 | })
263 | },
264 |
265 | init: function() {
266 | this.element.style.display = 'block'
267 |
268 | if (this.__initialized__ === true) {
269 | return
270 | }
271 |
272 | this.__initialized__ = true
273 | this.bindEvents()
274 | Loading.show()
275 |
276 | this.fetchNewestIndex()
277 | .then(index => {
278 | let indexDOM = getDOM('#weeklyIndex')
279 | indexDOM.setAttribute('max', index)
280 | indexDOM.value = index
281 | dispatch(indexDOM, 'change')
282 | })
283 | .catch(e => {
284 | console.error('通过 rss 获取最新期数失败')
285 | alert('发生错误,请检查网络状态!')
286 | getDOM('.net-err').style.display = 'inline'
287 | })
288 | .then(() => {
289 | Loading.hide()
290 | })
291 |
292 | dispatch(getDOM('#randomAbstract'), 'click')
293 | dispatch(getDOM('#randomLeading'), 'click')
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/lib/codemirror/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | color: black;
8 | }
9 |
10 | /* PADDING */
11 |
12 | .CodeMirror-lines {
13 | padding: 4px 0; /* Vertical padding around content */
14 | }
15 | .CodeMirror pre {
16 | padding: 0 4px; /* Horizontal padding of content */
17 | }
18 |
19 | .CodeMirror-scrollbar-filler,
20 | .CodeMirror-gutter-filler {
21 | background-color: white; /* The little square between H and V scrollbars */
22 | }
23 |
24 | /* GUTTER */
25 |
26 | .CodeMirror-gutters {
27 | border-right: 1px solid #ddd;
28 | background-color: #f7f7f7;
29 | white-space: nowrap;
30 | }
31 | .CodeMirror-linenumbers {
32 | }
33 | .CodeMirror-linenumber {
34 | padding: 0 3px 0 5px;
35 | min-width: 20px;
36 | text-align: right;
37 | color: #999;
38 | white-space: nowrap;
39 | }
40 |
41 | .CodeMirror-guttermarker {
42 | color: black;
43 | }
44 | .CodeMirror-guttermarker-subtle {
45 | color: #999;
46 | }
47 |
48 | /* CURSOR */
49 |
50 | .CodeMirror div.CodeMirror-cursor {
51 | border-left: 1px solid black;
52 | }
53 | /* Shown when moving in bi-directional text */
54 | .CodeMirror div.CodeMirror-secondarycursor {
55 | border-left: 1px solid silver;
56 | }
57 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
58 | width: auto;
59 | border: 0;
60 | background: #7e7;
61 | }
62 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
63 | z-index: 1;
64 | }
65 |
66 | .cm-animate-fat-cursor {
67 | width: auto;
68 | border: 0;
69 | -webkit-animation: blink 1.06s steps(1) infinite;
70 | -moz-animation: blink 1.06s steps(1) infinite;
71 | animation: blink 1.06s steps(1) infinite;
72 | }
73 | @-moz-keyframes blink {
74 | 0% {
75 | background: #7e7;
76 | }
77 | 50% {
78 | background: none;
79 | }
80 | 100% {
81 | background: #7e7;
82 | }
83 | }
84 | @-webkit-keyframes blink {
85 | 0% {
86 | background: #7e7;
87 | }
88 | 50% {
89 | background: none;
90 | }
91 | 100% {
92 | background: #7e7;
93 | }
94 | }
95 | @keyframes blink {
96 | 0% {
97 | background: #7e7;
98 | }
99 | 50% {
100 | background: none;
101 | }
102 | 100% {
103 | background: #7e7;
104 | }
105 | }
106 |
107 | /* Can style cursor different in overwrite (non-insert) mode */
108 | div.CodeMirror-overwrite div.CodeMirror-cursor {
109 | }
110 |
111 | .cm-tab {
112 | display: inline-block;
113 | text-decoration: inherit;
114 | }
115 |
116 | .CodeMirror-ruler {
117 | border-left: 1px solid #ccc;
118 | position: absolute;
119 | }
120 |
121 | /* DEFAULT THEME */
122 |
123 | .cm-s-default .cm-keyword {
124 | color: #708;
125 | }
126 | .cm-s-default .cm-atom {
127 | color: #219;
128 | }
129 | .cm-s-default .cm-number {
130 | color: #164;
131 | }
132 | .cm-s-default .cm-def {
133 | color: #00f;
134 | }
135 | .cm-s-default .cm-variable,
136 | .cm-s-default .cm-punctuation,
137 | .cm-s-default .cm-property,
138 | .cm-s-default .cm-operator {
139 | }
140 | .cm-s-default .cm-variable-2 {
141 | color: #05a;
142 | }
143 | .cm-s-default .cm-variable-3 {
144 | color: #085;
145 | }
146 | .cm-s-default .cm-comment {
147 | color: #a50;
148 | }
149 | .cm-s-default .cm-string {
150 | color: #a11;
151 | }
152 | .cm-s-default .cm-string-2 {
153 | color: #f50;
154 | }
155 | .cm-s-default .cm-meta {
156 | color: #555;
157 | }
158 | .cm-s-default .cm-qualifier {
159 | color: #555;
160 | }
161 | .cm-s-default .cm-builtin {
162 | color: #30a;
163 | }
164 | .cm-s-default .cm-bracket {
165 | color: #997;
166 | }
167 | .cm-s-default .cm-tag {
168 | color: #170;
169 | }
170 | .cm-s-default .cm-attribute {
171 | color: #00c;
172 | }
173 | .cm-s-default .cm-header {
174 | color: blue;
175 | }
176 | .cm-s-default .cm-quote {
177 | color: #090;
178 | }
179 | .cm-s-default .cm-hr {
180 | color: #999;
181 | }
182 | .cm-s-default .cm-link {
183 | color: #00c;
184 | }
185 |
186 | .cm-negative {
187 | color: #d44;
188 | }
189 | .cm-positive {
190 | color: #292;
191 | }
192 | .cm-header,
193 | .cm-strong {
194 | font-weight: bold;
195 | }
196 | .cm-em {
197 | font-style: italic;
198 | }
199 | .cm-link {
200 | text-decoration: underline;
201 | }
202 | .cm-strikethrough {
203 | text-decoration: line-through;
204 | }
205 |
206 | .cm-s-default .cm-error {
207 | color: #f00;
208 | }
209 | .cm-invalidchar {
210 | color: #f00;
211 | }
212 |
213 | /* Default styles for common addons */
214 |
215 | div.CodeMirror span.CodeMirror-matchingbracket {
216 | color: #0f0;
217 | }
218 | div.CodeMirror span.CodeMirror-nonmatchingbracket {
219 | color: #f22;
220 | }
221 | .CodeMirror-matchingtag {
222 | background: rgba(255, 150, 0, 0.3);
223 | }
224 | .CodeMirror-activeline-background {
225 | background: #e8f2ff;
226 | }
227 |
228 | /* STOP */
229 |
230 | /* The rest of this file contains styles related to the mechanics of
231 | the editor. You probably shouldn't touch them. */
232 |
233 | .CodeMirror {
234 | position: relative;
235 | overflow: hidden;
236 | background: white;
237 | }
238 |
239 | .CodeMirror-scroll {
240 | overflow: scroll !important; /* Things will break if this is overridden */
241 | /* 30px is the magic margin used to hide the element's real scrollbars */
242 | /* See overflow: hidden in .CodeMirror */
243 | margin-bottom: -30px;
244 | margin-right: -30px;
245 | padding-bottom: 30px;
246 | height: 100%;
247 | outline: none; /* Prevent dragging from highlighting the element */
248 | position: relative;
249 | }
250 | .CodeMirror-sizer {
251 | position: relative;
252 | border-right: 30px solid transparent;
253 | }
254 |
255 | /* The fake, visible scrollbars. Used to force redraw during scrolling
256 | before actuall scrolling happens, thus preventing shaking and
257 | flickering artifacts. */
258 | .CodeMirror-vscrollbar,
259 | .CodeMirror-hscrollbar,
260 | .CodeMirror-scrollbar-filler,
261 | .CodeMirror-gutter-filler {
262 | position: absolute;
263 | z-index: 6;
264 | display: none;
265 | }
266 | .CodeMirror-vscrollbar {
267 | right: 0;
268 | top: 0;
269 | overflow-x: hidden;
270 | overflow-y: scroll;
271 | }
272 | .CodeMirror-hscrollbar {
273 | bottom: 0;
274 | left: 0;
275 | overflow-y: hidden;
276 | overflow-x: scroll;
277 | }
278 | .CodeMirror-scrollbar-filler {
279 | right: 0;
280 | bottom: 0;
281 | }
282 | .CodeMirror-gutter-filler {
283 | left: 0;
284 | bottom: 0;
285 | }
286 |
287 | .CodeMirror-gutters {
288 | position: absolute;
289 | left: 0;
290 | top: 0;
291 | z-index: 3;
292 | }
293 | .CodeMirror-gutter {
294 | white-space: normal;
295 | height: 100%;
296 | display: inline-block;
297 | margin-bottom: -30px;
298 | /* Hack to make IE7 behave */
299 | *zoom: 1;
300 | *display: inline;
301 | }
302 | .CodeMirror-gutter-wrapper {
303 | position: absolute;
304 | z-index: 4;
305 | height: 100%;
306 | }
307 | .CodeMirror-gutter-elt {
308 | position: absolute;
309 | cursor: default;
310 | z-index: 4;
311 | }
312 | .CodeMirror-gutter-wrapper {
313 | -webkit-user-select: none;
314 | -moz-user-select: none;
315 | user-select: none;
316 | }
317 |
318 | .CodeMirror-lines {
319 | cursor: text;
320 | min-height: 1px; /* prevents collapsing before first draw */
321 | }
322 | .CodeMirror pre {
323 | /* Reset some styles that the rest of the page might have set */
324 | -moz-border-radius: 0;
325 | -webkit-border-radius: 0;
326 | border-radius: 0;
327 | border-width: 0;
328 | background: transparent;
329 | font-family: inherit;
330 | font-size: inherit;
331 | margin: 0;
332 | white-space: pre;
333 | word-wrap: normal;
334 | line-height: inherit;
335 | color: inherit;
336 | z-index: 2;
337 | position: relative;
338 | overflow: visible;
339 | -webkit-tap-highlight-color: transparent;
340 | }
341 | .CodeMirror-wrap pre {
342 | word-wrap: break-word;
343 | white-space: pre-wrap;
344 | word-break: normal;
345 | }
346 |
347 | .CodeMirror-linebackground {
348 | position: absolute;
349 | left: 0;
350 | right: 0;
351 | top: 0;
352 | bottom: 0;
353 | z-index: 0;
354 | }
355 |
356 | .CodeMirror-linewidget {
357 | position: relative;
358 | z-index: 2;
359 | overflow: auto;
360 | }
361 |
362 | .CodeMirror-widget {
363 | }
364 |
365 | .CodeMirror-code {
366 | outline: none;
367 | }
368 |
369 | /* Force content-box sizing for the elements where we expect it */
370 | .CodeMirror-scroll,
371 | .CodeMirror-sizer,
372 | .CodeMirror-gutter,
373 | .CodeMirror-gutters,
374 | .CodeMirror-linenumber {
375 | -moz-box-sizing: content-box;
376 | box-sizing: content-box;
377 | }
378 |
379 | .CodeMirror-measure {
380 | position: absolute;
381 | width: 100%;
382 | height: 0;
383 | overflow: hidden;
384 | visibility: hidden;
385 | }
386 | .CodeMirror-measure pre {
387 | position: static;
388 | }
389 |
390 | .CodeMirror div.CodeMirror-cursor {
391 | position: absolute;
392 | border-right: none;
393 | width: 0;
394 | }
395 |
396 | div.CodeMirror-cursors {
397 | visibility: hidden;
398 | position: relative;
399 | z-index: 3;
400 | }
401 | .CodeMirror-focused div.CodeMirror-cursors {
402 | visibility: visible;
403 | }
404 |
405 | .CodeMirror-selected {
406 | background: #d9d9d9;
407 | }
408 | .CodeMirror-focused .CodeMirror-selected {
409 | background: #d7d4f0;
410 | }
411 | .CodeMirror-crosshair {
412 | cursor: crosshair;
413 | }
414 | .CodeMirror ::selection {
415 | background: #d7d4f0;
416 | }
417 | .CodeMirror ::-moz-selection {
418 | background: #d7d4f0;
419 | }
420 |
421 | .cm-searching {
422 | background: #ffa;
423 | background: rgba(255, 255, 0, 0.4);
424 | }
425 |
426 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
427 | .CodeMirror span {
428 | *vertical-align: text-bottom;
429 | }
430 |
431 | /* Used to force a border model for a node */
432 | .cm-force-border {
433 | padding-right: 0.1px;
434 | }
435 |
436 | @media print {
437 | /* Hide the cursor when printing */
438 | .CodeMirror div.CodeMirror-cursors {
439 | visibility: hidden;
440 | }
441 | }
442 |
443 | /* See issue #2901 */
444 | .cm-tab-wrap-hack:after {
445 | content: '';
446 | }
447 |
448 | /* Help users use markselection to safely style text background */
449 | span.CodeMirror-selectedtext {
450 | background: none;
451 | }
452 |
--------------------------------------------------------------------------------
/lib/codemirror/markdown/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CodeMirror: Markdown mode
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | CodeMirror: Markdown mode
15 |
16 |
17 |
18 | Markdown: Basics
19 | ================
20 |
21 | <ul id="ProjectSubmenu">
22 | <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
23 | <li><a class="selected" title="Markdown Basics">Basics</a></li>
24 | <li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li>
25 | <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
26 | <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
27 | </ul>
28 |
29 |
30 | Getting the Gist of Markdown's Formatting Syntax
31 | ------------------------------------------------
32 |
33 | This page offers a brief overview of what it's like to use Markdown.
34 | The [syntax page] [s] provides complete, detailed documentation for
35 | every feature, but Markdown should be very easy to pick up simply by
36 | looking at a few examples of it in action. The examples on this page
37 | are written in a before/after style, showing example syntax and the
38 | HTML output produced by Markdown.
39 |
40 | It's also helpful to simply try Markdown out; the [Dingus] [d] is a
41 | web application that allows you type your own Markdown-formatted text
42 | and translate it to XHTML.
43 |
44 | **Note:** This document is itself written using Markdown; you
45 | can [see the source for it by adding '.text' to the URL] [src].
46 |
47 | [s]: /projects/markdown/syntax "Markdown Syntax"
48 | [d]: /projects/markdown/dingus "Markdown Dingus"
49 | [src]: /projects/markdown/basics.text
50 |
51 |
52 | ## Paragraphs, Headers, Blockquotes ##
53 |
54 | A paragraph is simply one or more consecutive lines of text, separated
55 | by one or more blank lines. (A blank line is any line that looks like
56 | a blank line -- a line containing nothing but spaces or tabs is
57 | considered blank.) Normal paragraphs should not be indented with
58 | spaces or tabs.
59 |
60 | Markdown offers two styles of headers: *Setext* and *atx*.
61 | Setext-style headers for `<h1>` and `<h2>` are created by
62 | "underlining" with equal signs (`=`) and hyphens (`-`), respectively.
63 | To create an atx-style header, you put 1-6 hash marks (`#`) at the
64 | beginning of the line -- the number of hashes equals the resulting
65 | HTML header level.
66 |
67 | Blockquotes are indicated using email-style '`>`' angle brackets.
68 |
69 | Markdown:
70 |
71 | A First Level Header
72 | ====================
73 |
74 | A Second Level Header
75 | ---------------------
76 |
77 | Now is the time for all good men to come to
78 | the aid of their country. This is just a
79 | regular paragraph.
80 |
81 | The quick brown fox jumped over the lazy
82 | dog's back.
83 |
84 | ### Header 3
85 |
86 | > This is a blockquote.
87 | >
88 | > This is the second paragraph in the blockquote.
89 | >
90 | > ## This is an H2 in a blockquote
91 |
92 |
93 | Output:
94 |
95 | <h1>A First Level Header</h1>
96 |
97 | <h2>A Second Level Header</h2>
98 |
99 | <p>Now is the time for all good men to come to
100 | the aid of their country. This is just a
101 | regular paragraph.</p>
102 |
103 | <p>The quick brown fox jumped over the lazy
104 | dog's back.</p>
105 |
106 | <h3>Header 3</h3>
107 |
108 | <blockquote>
109 | <p>This is a blockquote.</p>
110 |
111 | <p>This is the second paragraph in the blockquote.</p>
112 |
113 | <h2>This is an H2 in a blockquote</h2>
114 | </blockquote>
115 |
116 |
117 |
118 | ### Phrase Emphasis ###
119 |
120 | Markdown uses asterisks and underscores to indicate spans of emphasis.
121 |
122 | Markdown:
123 |
124 | Some of these words *are emphasized*.
125 | Some of these words _are emphasized also_.
126 |
127 | Use two asterisks for **strong emphasis**.
128 | Or, if you prefer, __use two underscores instead__.
129 |
130 | Output:
131 |
132 | <p>Some of these words <em>are emphasized</em>.
133 | Some of these words <em>are emphasized also</em>.</p>
134 |
135 | <p>Use two asterisks for <strong>strong emphasis</strong>.
136 | Or, if you prefer, <strong>use two underscores instead</strong>.</p>
137 |
138 |
139 |
140 | ## Lists ##
141 |
142 | Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
143 | `+`, and `-`) as list markers. These three markers are
144 | interchangable; this:
145 |
146 | * Candy.
147 | * Gum.
148 | * Booze.
149 |
150 | this:
151 |
152 | + Candy.
153 | + Gum.
154 | + Booze.
155 |
156 | and this:
157 |
158 | - Candy.
159 | - Gum.
160 | - Booze.
161 |
162 | all produce the same output:
163 |
164 | <ul>
165 | <li>Candy.</li>
166 | <li>Gum.</li>
167 | <li>Booze.</li>
168 | </ul>
169 |
170 | Ordered (numbered) lists use regular numbers, followed by periods, as
171 | list markers:
172 |
173 | 1. Red
174 | 2. Green
175 | 3. Blue
176 |
177 | Output:
178 |
179 | <ol>
180 | <li>Red</li>
181 | <li>Green</li>
182 | <li>Blue</li>
183 | </ol>
184 |
185 | If you put blank lines between items, you'll get `<p>` tags for the
186 | list item text. You can create multi-paragraph list items by indenting
187 | the paragraphs by 4 spaces or 1 tab:
188 |
189 | * A list item.
190 |
191 | With multiple paragraphs.
192 |
193 | * Another item in the list.
194 |
195 | Output:
196 |
197 | <ul>
198 | <li><p>A list item.</p>
199 | <p>With multiple paragraphs.</p></li>
200 | <li><p>Another item in the list.</p></li>
201 | </ul>
202 |
203 |
204 |
205 | ### Links ###
206 |
207 | Markdown supports two styles for creating links: *inline* and
208 | *reference*. With both styles, you use square brackets to delimit the
209 | text you want to turn into a link.
210 |
211 | Inline-style links use parentheses immediately after the link text.
212 | For example:
213 |
214 | This is an [example link](http://example.com/).
215 |
216 | Output:
217 |
218 | <p>This is an <a href="http://example.com/">
219 | example link</a>.</p>
220 |
221 | Optionally, you may include a title attribute in the parentheses:
222 |
223 | This is an [example link](http://example.com/ "With a Title").
224 |
225 | Output:
226 |
227 | <p>This is an <a href="http://example.com/" title="With a Title">
228 | example link</a>.</p>
229 |
230 | Reference-style links allow you to refer to your links by names, which
231 | you define elsewhere in your document:
232 |
233 | I get 10 times more traffic from [Google][1] than from
234 | [Yahoo][2] or [MSN][3].
235 |
236 | [1]: http://google.com/ "Google"
237 | [2]: http://search.yahoo.com/ "Yahoo Search"
238 | [3]: http://search.msn.com/ "MSN Search"
239 |
240 | Output:
241 |
242 | <p>I get 10 times more traffic from <a href="http://google.com/"
243 | title="Google">Google</a> than from <a href="http://search.yahoo.com/"
244 | title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
245 | title="MSN Search">MSN</a>.</p>
246 |
247 | The title attribute is optional. Link names may contain letters,
248 | numbers and spaces, but are *not* case sensitive:
249 |
250 | I start my morning with a cup of coffee and
251 | [The New York Times][NY Times].
252 |
253 | [ny times]: http://www.nytimes.com/
254 |
255 | Output:
256 |
257 | <p>I start my morning with a cup of coffee and
258 | <a href="http://www.nytimes.com/">The New York Times</a>.</p>
259 |
260 |
261 | ### Images ###
262 |
263 | Image syntax is very much like link syntax.
264 |
265 | Inline (titles are optional):
266 |
267 | 
268 |
269 | Reference-style:
270 |
271 | ![alt text][id]
272 |
273 | [id]: /path/to/img.jpg "Title"
274 |
275 | Both of the above examples produce the same output:
276 |
277 | <img src="/path/to/img.jpg" alt="alt text" title="Title" />
278 |
279 |
280 |
281 | ### Code ###
282 |
283 | In a regular paragraph, you can create code span by wrapping text in
284 | backtick quotes. Any ampersands (`&`) and angle brackets (`<` or
285 | `>`) will automatically be translated into HTML entities. This makes
286 | it easy to use Markdown to write about HTML example code:
287 |
288 | I strongly recommend against using any `<blink>` tags.
289 |
290 | I wish SmartyPants used named entities like `—`
291 | instead of decimal-encoded entites like `—`.
292 |
293 | Output:
294 |
295 | <p>I strongly recommend against using any
296 | <code><blink></code> tags.</p>
297 |
298 | <p>I wish SmartyPants used named entities like
299 | <code>&mdash;</code> instead of decimal-encoded
300 | entites like <code>&#8212;</code>.</p>
301 |
302 |
303 | To specify an entire block of pre-formatted code, indent every line of
304 | the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`,
305 | and `>` characters will be escaped automatically.
306 |
307 | Markdown:
308 |
309 | If you want your page to validate under XHTML 1.0 Strict,
310 | you've got to put paragraph tags in your blockquotes:
311 |
312 | <blockquote>
313 | <p>For example.</p>
314 | </blockquote>
315 |
316 | Output:
317 |
318 | <p>If you want your page to validate under XHTML 1.0 Strict,
319 | you've got to put paragraph tags in your blockquotes:</p>
320 |
321 | <pre><code><blockquote>
322 | <p>For example.</p>
323 | </blockquote>
324 | </code></pre>
325 |
326 |
327 |
335 |
336 | Optionally depends on the XML mode for properly highlighted inline XML blocks.
337 |
338 | MIME types defined: text/x-markdown.
339 |
340 | Parsing/Highlighting Tests: normal , verbose .
341 |
342 |
343 |
344 |
--------------------------------------------------------------------------------
/content_script/weekly.inject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author: angusfu1126@qq.com
3 | * @date: 2016-07-16 14:06:11
4 | */
5 | Promise.prototype.delay = function(onResolve, onReject) {
6 | let delay = Promise.delay || 200
7 | return this.then(function(value) {
8 | if (delay >= 0) {
9 | // delay 执行
10 | return new Promise(function(resolve, reject) {
11 | setTimeout(resolve, delay)
12 | }).then(function() {
13 | onResolve && onResolve(value)
14 | })
15 | } else {
16 | onResolve && onResolve(value)
17 | }
18 | }).catch(function(err) {
19 | onReject && onReject(err)
20 | })
21 | }
22 |
23 | const getDOM = sel =>
24 | document.querySelector(sel) || document.createElement('div')
25 |
26 | const TMPL_MAP = {
27 | title:
28 | '@@title@@
',
29 | desc:
30 | '@@desc@@ ',
31 | provider:
32 | '@@provider@@ ',
33 | content: '@@content@@
',
34 | centerP: '@@centerP@@
',
35 | leading:
36 | '@@leading@@ ',
37 | image:
38 | ' ',
39 | copyright:
40 | '(by @@@copyright@@) ',
41 | sectionTitle:
42 | '@@sectionTitle@@ ■ ■ ■ ',
43 | blank: '
',
44 | bottom: `
45 | 关于奇舞周刊
46 | 《奇舞周刊》是360公司专业前端团队「奇舞团 」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。
47 | 奇舞团是360集团最大的大前端团队,代表集团参与W3C和Ecma会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队Leader等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
48 |
49 | `,
50 | warning:
51 | '
记得点击文章末尾的“阅读原文 ”查看哟~
下面先一起看下本期周刊摘要 吧~
'
52 | }
53 |
54 | const renderComponent = (key, content = '') => {
55 | if (!TMPL_MAP[key]) return ''
56 | if (!content) return ''
57 | return TMPL_MAP[key].replace(new RegExp(`@@${key}@@`, 'gi'), content || '')
58 | }
59 |
60 | const BLANK_PARAGRAPH = renderComponent('blank', true)
61 | const TIP_BLOCK = renderComponent('warning', true)
62 | const FOOTER_BLOCK = renderComponent('bottom', true)
63 |
64 | const renderLeading = leading => {
65 | let content = renderComponent('leading', leading)
66 | return renderComponent('content', content)
67 | }
68 |
69 | // 单篇文章
70 | const rendorSingleArticle = source => {
71 | let desc = renderComponent('desc', source['desc'])
72 | let provider = renderComponent('provider', source['provider'])
73 | return [
74 | renderComponent('title', source['title']),
75 | renderComponent('content', desc + provider),
76 | BLANK_PARAGRAPH
77 | ].join('')
78 | }
79 |
80 | // 头图片
81 | const renderHeadingImage = (src, copyright = '') => {
82 | if (!src.trim()) {
83 | return ''
84 | }
85 |
86 | let imgContent = renderComponent('image', src.trim())
87 | let cpContent = renderComponent('copyright', copyright.trim())
88 | let temp = renderComponent('centerP', imgContent + ' ' + cpContent)
89 | return temp + BLANK_PARAGRAPH
90 | }
91 |
92 | const injectWeekly = info => {
93 | let {
94 | index,
95 | origin,
96 | author,
97 | abstract,
98 | leading,
99 | dataStore,
100 | animSrc,
101 | animURL,
102 | coverID,
103 | coverURL
104 | } = info
105 |
106 | /* 取第一条的标题 */
107 | let title = `奇舞周刊第 ${index} 期 —— ${
108 | dataStore[dataStore['_key_'][0]][0]['title']
109 | }`
110 | title = title.slice(0, 64)
111 | getDOM('#title').value = title
112 | getDOM('.cover_appmsg_item .appmsg_title .js_appmsg_title').innerHTML = title
113 |
114 | // 作者
115 | getDOM('#author').value = author.slice(0, 8)
116 | // 摘要
117 | getDOM('.js_desc').value = abstract
118 |
119 | // 原文链接
120 | if (!getDOM('.js_url_checkbox').checked) {
121 | getDOM('.js_url_checkbox').click()
122 | }
123 | getDOM('.js_url').value = origin
124 |
125 | // 选中留言
126 | getDOM('.frm_checkbox.js_comment.js_field').checked = 'checked'
127 | getDOM('.frm_checkbox_label.comment_checkbox').className += ' selected'
128 |
129 | // 加上封面图
130 | if (coverID && coverURL) {
131 | getDOM('input.js_file_id').value = coverID
132 | getDOM('input.js_cdn_url').value = coverURL
133 | getDOM(
134 | '.cover_appmsg_item .appmsg_thumb_wrp.js_appmsg_thumb'
135 | ).style.backgroundImage = `url(${coverURL})`
136 | getDOM(
137 | '.cover_preview.js_cover_preview'
138 | ).style.backgroundImage = `url(${coverURL})`
139 | getDOM(
140 | '.cover_appmsg_item .appmsg_thumb_wrp.js_appmsg_thumb .appmsg_thumb'
141 | ).style.display = 'none'
142 | }
143 |
144 | /**
145 | * 渲染各部分
146 | */
147 | let weeklyBody = dataStore['_key_'].reduce((prev, key) => {
148 | if (key !== '_key_') {
149 | let secData = dataStore[key]
150 | prev += renderComponent('sectionTitle', key)
151 | prev += secData.reduce((accu, now) => accu + rendorSingleArticle(now), '')
152 | }
153 | return prev
154 | }, '')
155 |
156 | let html = [
157 | TIP_BLOCK,
158 | BLANK_PARAGRAPH,
159 |
160 | renderLeading(leading),
161 | BLANK_PARAGRAPH,
162 |
163 | renderHeadingImage(animURL, animSrc),
164 | weeklyBody,
165 |
166 | FOOTER_BLOCK
167 | ].join('')
168 |
169 | // 写入文本
170 | getDOM('.editor_content_placeholder').style.display = 'none'
171 | getDOM('#ueditor_0').contentDocument.body.innerHTML = html
172 |
173 | // 原创
174 | Promise.delay = 300
175 | Promise.resolve()
176 | .delay(function() {
177 | getDOM('#js_original .btn.js_original_apply').click()
178 | })
179 | .delay(function() {
180 | getDOM('.original_dialog .js_btn[data-index="0"]').click()
181 | })
182 | .delay(function() {
183 | getDOM('#js_original_article_type li a[data-name="科技互联网"]').click()
184 | })
185 | .delay(function() {
186 | getDOM('.original_dialog .js_btn[data-index="2"]').click()
187 | })
188 | .catch(function(err) {
189 | console.log(err)
190 | })
191 | }
192 |
193 | chrome.runtime.onMessage.addListener(function(message) {
194 | console.log(message)
195 | if (message.type === 'inject_weekly') {
196 | injectWeekly(message.info)
197 | }
198 | })
199 |
--------------------------------------------------------------------------------
/lib/md5.js:
--------------------------------------------------------------------------------
1 | var CryptoJS =
2 | CryptoJS ||
3 | (function(s, p) {
4 | var m = {},
5 | l = (m.lib = {}),
6 | n = function() {},
7 | r = (l.Base = {
8 | extend: function(b) {
9 | n.prototype = this
10 | var h = new n()
11 | b && h.mixIn(b)
12 | h.hasOwnProperty('init') ||
13 | (h.init = function() {
14 | h.$super.init.apply(this, arguments)
15 | })
16 | h.init.prototype = h
17 | h.$super = this
18 | return h
19 | },
20 | create: function() {
21 | var b = this.extend()
22 | b.init.apply(b, arguments)
23 | return b
24 | },
25 | init: function() {},
26 | mixIn: function(b) {
27 | for (var h in b) b.hasOwnProperty(h) && (this[h] = b[h])
28 | b.hasOwnProperty('toString') && (this.toString = b.toString)
29 | },
30 | clone: function() {
31 | return this.init.prototype.extend(this)
32 | }
33 | }),
34 | q = (l.WordArray = r.extend({
35 | init: function(b, h) {
36 | b = this.words = b || []
37 | this.sigBytes = h != p ? h : 4 * b.length
38 | },
39 | toString: function(b) {
40 | return (b || t).stringify(this)
41 | },
42 | concat: function(b) {
43 | var h = this.words,
44 | a = b.words,
45 | j = this.sigBytes
46 | b = b.sigBytes
47 | this.clamp()
48 | if (j % 4)
49 | for (var g = 0; g < b; g++)
50 | h[(j + g) >>> 2] |=
51 | ((a[g >>> 2] >>> (24 - 8 * (g % 4))) & 255) <<
52 | (24 - 8 * ((j + g) % 4))
53 | else if (65535 < a.length)
54 | for (g = 0; g < b; g += 4) h[(j + g) >>> 2] = a[g >>> 2]
55 | else h.push.apply(h, a)
56 | this.sigBytes += b
57 | return this
58 | },
59 | clamp: function() {
60 | var b = this.words,
61 | h = this.sigBytes
62 | b[h >>> 2] &= 4294967295 << (32 - 8 * (h % 4))
63 | b.length = s.ceil(h / 4)
64 | },
65 | clone: function() {
66 | var b = r.clone.call(this)
67 | b.words = this.words.slice(0)
68 | return b
69 | },
70 | random: function(b) {
71 | for (var h = [], a = 0; a < b; a += 4)
72 | h.push((4294967296 * s.random()) | 0)
73 | return new q.init(h, b)
74 | }
75 | })),
76 | v = (m.enc = {}),
77 | t = (v.Hex = {
78 | stringify: function(b) {
79 | var a = b.words
80 | b = b.sigBytes
81 | for (var g = [], j = 0; j < b; j++) {
82 | var k = (a[j >>> 2] >>> (24 - 8 * (j % 4))) & 255
83 | g.push((k >>> 4).toString(16))
84 | g.push((k & 15).toString(16))
85 | }
86 | return g.join('')
87 | },
88 | parse: function(b) {
89 | for (var a = b.length, g = [], j = 0; j < a; j += 2)
90 | g[j >>> 3] |= parseInt(b.substr(j, 2), 16) << (24 - 4 * (j % 8))
91 | return new q.init(g, a / 2)
92 | }
93 | }),
94 | a = (v.Latin1 = {
95 | stringify: function(b) {
96 | var a = b.words
97 | b = b.sigBytes
98 | for (var g = [], j = 0; j < b; j++)
99 | g.push(
100 | String.fromCharCode((a[j >>> 2] >>> (24 - 8 * (j % 4))) & 255)
101 | )
102 | return g.join('')
103 | },
104 | parse: function(b) {
105 | for (var a = b.length, g = [], j = 0; j < a; j++)
106 | g[j >>> 2] |= (b.charCodeAt(j) & 255) << (24 - 8 * (j % 4))
107 | return new q.init(g, a)
108 | }
109 | }),
110 | u = (v.Utf8 = {
111 | stringify: function(b) {
112 | try {
113 | return decodeURIComponent(escape(a.stringify(b)))
114 | } catch (g) {
115 | throw Error('Malformed UTF-8 data')
116 | }
117 | },
118 | parse: function(b) {
119 | return a.parse(unescape(encodeURIComponent(b)))
120 | }
121 | }),
122 | g = (l.BufferedBlockAlgorithm = r.extend({
123 | reset: function() {
124 | this._data = new q.init()
125 | this._nDataBytes = 0
126 | },
127 | _append: function(b) {
128 | 'string' == typeof b && (b = u.parse(b))
129 | this._data.concat(b)
130 | this._nDataBytes += b.sigBytes
131 | },
132 | _process: function(b) {
133 | var a = this._data,
134 | g = a.words,
135 | j = a.sigBytes,
136 | k = this.blockSize,
137 | m = j / (4 * k),
138 | m = b ? s.ceil(m) : s.max((m | 0) - this._minBufferSize, 0)
139 | b = m * k
140 | j = s.min(4 * b, j)
141 | if (b) {
142 | for (var l = 0; l < b; l += k) this._doProcessBlock(g, l)
143 | l = g.splice(0, b)
144 | a.sigBytes -= j
145 | }
146 | return new q.init(l, j)
147 | },
148 | clone: function() {
149 | var b = r.clone.call(this)
150 | b._data = this._data.clone()
151 | return b
152 | },
153 | _minBufferSize: 0
154 | }))
155 | l.Hasher = g.extend({
156 | cfg: r.extend(),
157 | init: function(b) {
158 | this.cfg = this.cfg.extend(b)
159 | this.reset()
160 | },
161 | reset: function() {
162 | g.reset.call(this)
163 | this._doReset()
164 | },
165 | update: function(b) {
166 | this._append(b)
167 | this._process()
168 | return this
169 | },
170 | finalize: function(b) {
171 | b && this._append(b)
172 | return this._doFinalize()
173 | },
174 | blockSize: 16,
175 | _createHelper: function(b) {
176 | return function(a, g) {
177 | return new b.init(g).finalize(a)
178 | }
179 | },
180 | _createHmacHelper: function(b) {
181 | return function(a, g) {
182 | return new k.HMAC.init(b, g).finalize(a)
183 | }
184 | }
185 | })
186 | var k = (m.algo = {})
187 | return m
188 | })(Math)
189 | ;(function(s) {
190 | function p(a, k, b, h, l, j, m) {
191 | a = a + ((k & b) | (~k & h)) + l + m
192 | return ((a << j) | (a >>> (32 - j))) + k
193 | }
194 | function m(a, k, b, h, l, j, m) {
195 | a = a + ((k & h) | (b & ~h)) + l + m
196 | return ((a << j) | (a >>> (32 - j))) + k
197 | }
198 | function l(a, k, b, h, l, j, m) {
199 | a = a + (k ^ b ^ h) + l + m
200 | return ((a << j) | (a >>> (32 - j))) + k
201 | }
202 | function n(a, k, b, h, l, j, m) {
203 | a = a + (b ^ (k | ~h)) + l + m
204 | return ((a << j) | (a >>> (32 - j))) + k
205 | }
206 | for (
207 | var r = CryptoJS,
208 | q = r.lib,
209 | v = q.WordArray,
210 | t = q.Hasher,
211 | q = r.algo,
212 | a = [],
213 | u = 0;
214 | 64 > u;
215 | u++
216 | )
217 | a[u] = (4294967296 * s.abs(s.sin(u + 1))) | 0
218 | q = q.MD5 = t.extend({
219 | _doReset: function() {
220 | this._hash = new v.init([1732584193, 4023233417, 2562383102, 271733878])
221 | },
222 | _doProcessBlock: function(g, k) {
223 | for (var b = 0; 16 > b; b++) {
224 | var h = k + b,
225 | w = g[h]
226 | g[h] =
227 | (((w << 8) | (w >>> 24)) & 16711935) |
228 | (((w << 24) | (w >>> 8)) & 4278255360)
229 | }
230 | var b = this._hash.words,
231 | h = g[k + 0],
232 | w = g[k + 1],
233 | j = g[k + 2],
234 | q = g[k + 3],
235 | r = g[k + 4],
236 | s = g[k + 5],
237 | t = g[k + 6],
238 | u = g[k + 7],
239 | v = g[k + 8],
240 | x = g[k + 9],
241 | y = g[k + 10],
242 | z = g[k + 11],
243 | A = g[k + 12],
244 | B = g[k + 13],
245 | C = g[k + 14],
246 | D = g[k + 15],
247 | c = b[0],
248 | d = b[1],
249 | e = b[2],
250 | f = b[3],
251 | c = p(c, d, e, f, h, 7, a[0]),
252 | f = p(f, c, d, e, w, 12, a[1]),
253 | e = p(e, f, c, d, j, 17, a[2]),
254 | d = p(d, e, f, c, q, 22, a[3]),
255 | c = p(c, d, e, f, r, 7, a[4]),
256 | f = p(f, c, d, e, s, 12, a[5]),
257 | e = p(e, f, c, d, t, 17, a[6]),
258 | d = p(d, e, f, c, u, 22, a[7]),
259 | c = p(c, d, e, f, v, 7, a[8]),
260 | f = p(f, c, d, e, x, 12, a[9]),
261 | e = p(e, f, c, d, y, 17, a[10]),
262 | d = p(d, e, f, c, z, 22, a[11]),
263 | c = p(c, d, e, f, A, 7, a[12]),
264 | f = p(f, c, d, e, B, 12, a[13]),
265 | e = p(e, f, c, d, C, 17, a[14]),
266 | d = p(d, e, f, c, D, 22, a[15]),
267 | c = m(c, d, e, f, w, 5, a[16]),
268 | f = m(f, c, d, e, t, 9, a[17]),
269 | e = m(e, f, c, d, z, 14, a[18]),
270 | d = m(d, e, f, c, h, 20, a[19]),
271 | c = m(c, d, e, f, s, 5, a[20]),
272 | f = m(f, c, d, e, y, 9, a[21]),
273 | e = m(e, f, c, d, D, 14, a[22]),
274 | d = m(d, e, f, c, r, 20, a[23]),
275 | c = m(c, d, e, f, x, 5, a[24]),
276 | f = m(f, c, d, e, C, 9, a[25]),
277 | e = m(e, f, c, d, q, 14, a[26]),
278 | d = m(d, e, f, c, v, 20, a[27]),
279 | c = m(c, d, e, f, B, 5, a[28]),
280 | f = m(f, c, d, e, j, 9, a[29]),
281 | e = m(e, f, c, d, u, 14, a[30]),
282 | d = m(d, e, f, c, A, 20, a[31]),
283 | c = l(c, d, e, f, s, 4, a[32]),
284 | f = l(f, c, d, e, v, 11, a[33]),
285 | e = l(e, f, c, d, z, 16, a[34]),
286 | d = l(d, e, f, c, C, 23, a[35]),
287 | c = l(c, d, e, f, w, 4, a[36]),
288 | f = l(f, c, d, e, r, 11, a[37]),
289 | e = l(e, f, c, d, u, 16, a[38]),
290 | d = l(d, e, f, c, y, 23, a[39]),
291 | c = l(c, d, e, f, B, 4, a[40]),
292 | f = l(f, c, d, e, h, 11, a[41]),
293 | e = l(e, f, c, d, q, 16, a[42]),
294 | d = l(d, e, f, c, t, 23, a[43]),
295 | c = l(c, d, e, f, x, 4, a[44]),
296 | f = l(f, c, d, e, A, 11, a[45]),
297 | e = l(e, f, c, d, D, 16, a[46]),
298 | d = l(d, e, f, c, j, 23, a[47]),
299 | c = n(c, d, e, f, h, 6, a[48]),
300 | f = n(f, c, d, e, u, 10, a[49]),
301 | e = n(e, f, c, d, C, 15, a[50]),
302 | d = n(d, e, f, c, s, 21, a[51]),
303 | c = n(c, d, e, f, A, 6, a[52]),
304 | f = n(f, c, d, e, q, 10, a[53]),
305 | e = n(e, f, c, d, y, 15, a[54]),
306 | d = n(d, e, f, c, w, 21, a[55]),
307 | c = n(c, d, e, f, v, 6, a[56]),
308 | f = n(f, c, d, e, D, 10, a[57]),
309 | e = n(e, f, c, d, t, 15, a[58]),
310 | d = n(d, e, f, c, B, 21, a[59]),
311 | c = n(c, d, e, f, r, 6, a[60]),
312 | f = n(f, c, d, e, z, 10, a[61]),
313 | e = n(e, f, c, d, j, 15, a[62]),
314 | d = n(d, e, f, c, x, 21, a[63])
315 | b[0] = (b[0] + c) | 0
316 | b[1] = (b[1] + d) | 0
317 | b[2] = (b[2] + e) | 0
318 | b[3] = (b[3] + f) | 0
319 | },
320 | _doFinalize: function() {
321 | var a = this._data,
322 | k = a.words,
323 | b = 8 * this._nDataBytes,
324 | h = 8 * a.sigBytes
325 | k[h >>> 5] |= 128 << (24 - (h % 32))
326 | var l = s.floor(b / 4294967296)
327 | k[(((h + 64) >>> 9) << 4) + 15] =
328 | (((l << 8) | (l >>> 24)) & 16711935) |
329 | (((l << 24) | (l >>> 8)) & 4278255360)
330 | k[(((h + 64) >>> 9) << 4) + 14] =
331 | (((b << 8) | (b >>> 24)) & 16711935) |
332 | (((b << 24) | (b >>> 8)) & 4278255360)
333 | a.sigBytes = 4 * (k.length + 1)
334 | this._process()
335 | a = this._hash
336 | k = a.words
337 | for (b = 0; 4 > b; b++)
338 | (h = k[b]),
339 | (k[b] =
340 | (((h << 8) | (h >>> 24)) & 16711935) |
341 | (((h << 24) | (h >>> 8)) & 4278255360))
342 | return a
343 | },
344 | clone: function() {
345 | var a = t.clone.call(this)
346 | a._hash = this._hash.clone()
347 | return a
348 | }
349 | })
350 | r.MD5 = t._createHelper(q)
351 | r.HmacMD5 = t._createHmacHelper(q)
352 | })(Math)
353 |
--------------------------------------------------------------------------------