diff --git a/app/js/index.js b/app/js/index.js
index 936b64dc..41dc6a8d 100644
--- a/app/js/index.js
+++ b/app/js/index.js
@@ -1,5 +1,8 @@
(function($) {
const ipcRenderer = require('electron').ipcRenderer;
+ const remote = require('electron').remote;
+ const electronFind = require('electron-find');
+ let findInPage = new electronFind.FindInPage(remote.getCurrentWebContents());
//events
ipcRenderer.on('fileList', (event, files) => {
@@ -71,6 +74,11 @@
});
$(document).on('keypress', (ev) => {
+ //ctrl + f
+ if (ev.ctrlKey && ev.charCode == 6) {
+ findInPage.openFindWindow();
+ }
+
//ctrl + r
if (ev.ctrlKey && ev.keyCode == 18) {
window.location.reload(false);
diff --git a/app/node_modules/electron-find/README.md b/app/node_modules/electron-find/README.md
new file mode 100644
index 00000000..be99b479
--- /dev/null
+++ b/app/node_modules/electron-find/README.md
@@ -0,0 +1,126 @@
+# electron-find
+
+English | [简体中文](./README.zh-CN.md)
+
+## Introduction
+Find all matches for the text in electron app
+
+## Features
+- depend on the API of electron's findInPage
+- support user config UI of find interface
+- support case-sensitive
+- Auto find when user inputing is change
+- The find interface is separated from electron view
+- support electron version ^1.8.0, ^2.0.0, ^3.0.0, ^4.0.0
+- support platform of Windows, Linux, Mac
+
+## Demo
+
+### Default UI
+
+
+### Custom UI
+
+
+## Install
+```
+$ npm install electron-find --save
+```
+
+## Usage
+```
+# import module
+import { remote, ipcRenderer } from 'electron'
+import { FindInPage } from 'electron-find'
+
+# create instance of FindInPage with default config
+let findInPage = new FindInPage(remote.getCurrentWebContents())
+findInPage.openFindWindow()
+
+# use preload option, the find interface will be loaded when create instance
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ preload: true
+})
+findInPage.openFindWindow()
+
+# config parentElement of find interface, default is document.body
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ parentElement: document.querySelector('#id')
+})
+findInPage.openFindWindow()
+
+# config duration of find interface moving, default is 300 (ms)
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ duration: 200
+})
+findInPage.openFindWindow()
+
+# config offset relative to parentElement
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ offsetTop: 20,
+ offsetRight: 30
+})
+findInPage.openFindWindow()
+
+# config UI of find interface
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ boxBgColor: '#333',
+ boxShadowColor: '#000',
+ inputColor: '#aaa',
+ inputBgColor: '#222',
+ inputFocusColor: '#555',
+ textColor: '#aaa',
+ textHoverBgColor: '#555',
+ caseSelectedColor: '#555'
+})
+findInPage.openFindWindow()
+
+# there is a simply demo for reference
+npm install
+npm run e
+```
+## Shortcut
+| keys | function |
+| ------ | ------ |
+| Enter | find next |
+| Shift + Enter| find back |
+| Esc | close |
+
+ Besides, you can also register global shortcut to open the find window, just like the demo.
+
+ ## API
+ ### Class: FindInPage
+ ` new FindInPage(webContents, [options]) `
+- ` webContents ` Object(required) - The webContents of renderer process
+- ` options ` Object(optional)
+ - ` preload ` Boolean - Whether load the find interface when create instance. Default is `false`.
+ - ` parentElement ` Object - Specify parent dom of the find interface. Default is `document.body`.
+ - ` duration ` Number - Specify moving time when the find window open or close. Default is `300` (ms).
+ - ` offsetTop ` Number - Specify offset relative to the top of parentElement. Default is `5`.
+ - ` offsetRight ` Number - Specify offset relative to the right of parentElement. Default is `5`.
+ - ` boxBgColor ` String - Specify background color of the find interface. Default is `"#ffffff"`.
+ - ` boxShadowColor ` String - Specify shadow color of the find interface. Default is `"#909399"`.
+ - ` inputColor ` String - Specify text color of the input form. Default is "#606266".
+ - ` inputBgColor ` String - Specify background color of the input form. Default is `"#f0f0f0"`.
+ - ` inputFocusColor ` String - Specify border color of the input form when focusing. Default is `"#c5ade0"`.
+ - ` textColor ` String - Specify color of the text in find interface. Default is `"#606266"`.
+ - ` textHoverBgColor ` String - Specify background color of text in find interface when hovering. Default is `"#eaeaea"`.
+ - ` caseSelectedColor ` String - Specify border color of the matchCase button when selected. Default is `"#c5ade0"`.
+
+ ### Instance Methods
+ Objects created with new FindInPage have the following instance methods:
+
+ ` findInPage.openFindWindow() `
+ Open the find window when it is closed. Focus input form when the find window has opened.
+
+ ` findInPage.closeFindWindow() `
+ Close the find window when it has opened.
+
+ ` findInPage.destroy() `
+ Close the find window, and release memery.
+
+
+
+
+
+
diff --git a/app/node_modules/electron-find/README.zh-CN.md b/app/node_modules/electron-find/README.zh-CN.md
new file mode 100644
index 00000000..54a08868
--- /dev/null
+++ b/app/node_modules/electron-find/README.zh-CN.md
@@ -0,0 +1,126 @@
+# electron-find
+
+简体中文 | [English](./README.md)
+
+## 简介
+在Electron app页内查找匹配关键字的所有文本字符
+
+## 特征
+- 依赖于Electron的findInPage API
+- 支持使用者灵活配置UI界面
+- 支持区分大小写
+- 当用户输入时自动查找
+- 查找输入框文本隔离,不会被匹配到
+- 支持以下Electron版本 ^1.8.7, ^2.0.0, ^3.0.0, ^4.0.0
+- 支持以下平台 Windows, Linux, Mac
+
+## 演示
+
+### 默认UI
+
+
+### 定制化UI
+
+
+## 安装
+```
+$ npm install electron-find --save
+```
+
+## 使用
+```
+# 引入模块
+import { remote, ipcRenderer } from 'electron'
+import { FindInPage } from 'electron-find'
+
+# 使用默认配置来创建实例
+let findInPage = new FindInPage(remote.getCurrentWebContents())
+findInPage.openFindWindow()
+
+# 开启预加载选项,创建实例的时候会同时加载查找窗口相关dom
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ preload: true
+})
+findInPage.openFindWindow()
+
+# 配置父节点元素, 默认为 document.body
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ parentElement: document.querySelector('#id')
+})
+findInPage.openFindWindow()
+
+# 配置查找窗口显示或隐藏的过渡周期, 默认为 300 (ms)
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ duration: 200
+})
+findInPage.openFindWindow()
+
+# 配置查找窗口相对于父级定位节点的偏移量
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ offsetTop: 20,
+ offsetRight: 30
+})
+findInPage.openFindWindow()
+
+# 自定义UI界面颜色
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ boxBgColor: '#333',
+ boxShadowColor: '#000',
+ inputColor: '#aaa',
+ inputBgColor: '#222',
+ inputFocusColor: '#555',
+ textColor: '#aaa',
+ textHoverBgColor: '#555',
+ caseSelectedColor: '#555'
+})
+findInPage.openFindWindow()
+
+# 参考demo
+npm install
+npm run e
+```
+## 快捷键
+| 键 | 功能 |
+| ------ | ------ |
+| Enter | 查找上一个 |
+| Shift + Enter| 查找下一个 |
+| Esc | 关闭窗口 |
+
+ 另外, 可以参考demo,使用全局快捷键来打开窗口。
+
+ ## API
+ ### Class: FindInPage
+ ` new FindInPage(webContents, [options]) `
+- ` webContents ` Object(required) - 渲染进程的webContents对象
+- ` options ` Object(optional)
+ - ` preload ` Boolean - 创建实例的时候是否预加载查找窗口。 默认为 `false`。
+ - ` parentElement ` Object - 指定查找窗口的父级节点。 默认为 `document.body`。
+ - ` duration ` Number - 指定查找窗口显示或隐藏的过渡周期。 默认为 `300` (ms)。
+ - ` offsetTop ` Number - 指定查找窗口相对于父级定位元素顶部偏移量。 默认为 `5`。
+ - ` offsetRight ` Number - 指定查找窗口相对于父级定位元素右边偏移量。 默认为 `5`。
+ - ` boxBgColor ` String - 配置查找窗口背景色。 默认为 `"#ffffff"`。
+ - ` boxShadowColor ` String - 配置查找窗口阴影色。 默认为 `"#909399"`。
+ - ` inputColor ` String - 配置输入框文本颜色。 默认为 "#606266"。
+ - ` inputBgColor ` String - 配置输入框背景颜色。 默认为 `"#f0f0f0"`。
+ - ` inputFocusColor ` String - 配置输入框聚焦时的边框颜色。 默认为 `"#c5ade0"`。
+ - ` textColor ` String - 配置查找窗口中文本颜色。 默认为 `"#606266"`。
+ - ` textHoverBgColor ` String - 配置鼠标悬停文本时的背景色。 默认为 `"#eaeaea"`。
+ - ` caseSelectedColor ` String - 配置区分大小写选项选中时的边框颜色。 默认为 `"#c5ade0"`。
+
+ ### Instance Methods
+ 使用new FindInPage 创建的实例具有以下方法:
+
+ ` findInPage.openFindWindow() `
+ 当查找窗口关闭时,打开窗口。 当查找窗口已经打开时,聚焦输入框。
+
+ ` findInPage.closeFindWindow() `
+ 关闭窗口。
+
+ ` findInPage.destroy() `
+ 关闭窗口,清除对象的引用,释放内存。
+
+
+
+
+
+
diff --git a/app/node_modules/electron-find/example/example.css b/app/node_modules/electron-find/example/example.css
new file mode 100644
index 00000000..d128d7ae
--- /dev/null
+++ b/app/node_modules/electron-find/example/example.css
@@ -0,0 +1,14 @@
+html,
+body{
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ overflow: auto;
+}
+section{
+ padding: 10px 20px;
+}
+h3{
+ margin-top: 30px;
+}
+
diff --git a/app/node_modules/electron-find/example/example.js b/app/node_modules/electron-find/example/example.js
new file mode 100644
index 00000000..c858d23a
--- /dev/null
+++ b/app/node_modules/electron-find/example/example.js
@@ -0,0 +1,26 @@
+const { remote, ipcRenderer } = require('electron')
+const { FindInPage } = require('../src/index.js')
+
+
+let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+ preload: true,
+ offsetTop: 6,
+ offsetRight: 10
+})
+
+// let findInPage = new FindInPage(remote.getCurrentWebContents(), {
+// boxBgColor: '#333',
+// boxShadowColor: '#000',
+// inputColor: '#aaa',
+// inputBgColor: '#222',
+// inputFocusColor: '#555',
+// textColor: '#aaa',
+// textHoverBgColor: '#555',
+// caseSelectedColor: '#555',
+// offsetTop: 8,
+// offsetRight: 12
+// })
+
+ipcRenderer.on('on-find', (e, args) => {
+ findInPage.openFindWindow()
+})
diff --git a/app/node_modules/electron-find/example/index.html b/app/node_modules/electron-find/example/index.html
new file mode 100644
index 00000000..766b7d36
--- /dev/null
+++ b/app/node_modules/electron-find/example/index.html
@@ -0,0 +1,54 @@
+
+
+
+
+ Example
+
+
+
+
+ 关于 Electron
+
+ Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。
+
+
+ Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。
+
+
+ Electron于2013年作为构建Github上可编程的文本编辑器Atom的框架而被开发出来。这两个项目在2014春季开源。
+
+
+ 目前它已成为开源开发者、初创企业和老牌公司常用的开发工具。
+
+
+ 长期支持
+
+ 当前并不存在对Electron旧版本的长期支持
+
+
+ 如果现在你使用的Electron版本跑得不错,你就可以一直使用这个版本。
+
+
+ 如果你想使用新发布的特性,那就升级到更新的版本。
+
+
+ 版本v1.0.0发布了重大的更新。 如果你现在没有在用这个版本,你应该了解更多关于v1.0.0的改变。
+
+
+ 核心理念
+
+ 为了保持Electron的小巧 (文件体积) 和可持续性开发 (以防依赖库和API的泛滥) ,Electron限制了所使用的核心项目的数量。
+
+
+ 比如Electron只用了Chromium的渲染库而不是其全部组件。
+
+
+ 这使得升级Chromium更加容易,但也意味着Electron缺少了Google Chrome里的一些浏览器相关的特性
+
+
+ 添加到Electron的新功能应该主要是原生 API。 如果可以的话,一个功能应该尽可能的成为一个Node.js模块。
+
+
+
+
+
diff --git a/app/node_modules/electron-find/example/main.js b/app/node_modules/electron-find/example/main.js
new file mode 100644
index 00000000..32fdf376
--- /dev/null
+++ b/app/node_modules/electron-find/example/main.js
@@ -0,0 +1,47 @@
+const electron = require('electron')
+const { app, BrowserWindow, globalShortcut } = electron
+const path = require('path')
+let win
+const winURL = 'file://' + path.normalize(`${__dirname}/index.html`)
+
+function createWindow () {
+ win = new BrowserWindow({
+ width: 1280,
+ height: 1040,
+ center: false,
+ webPreferences: {
+ nodeIntegration: true,
+ plugins: true,
+ }
+ })
+ win.loadURL(winURL)
+ //win.webContents.openDevTools()
+ win.on('closed', () => {
+ win = null
+ })
+
+ win.on('focus', () => {
+ globalShortcut.register('CommandOrControl+F', function () {
+ if (win && win.webContents) {
+ win.webContents.send('on-find', '')
+ }
+ })
+ })
+ win.on('blur', () => {
+ globalShortcut.unregister('CommandOrControl+F')
+ })
+
+}
+
+app.on('ready', createWindow)
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit()
+ }
+ globalShortcut.unregister('CommandOrControl+F')
+})
+
+app.on('activate', () => {
+ if (win === null) createWindow()
+})
+
diff --git a/app/node_modules/electron-find/example2/inner1.html b/app/node_modules/electron-find/example2/inner1.html
new file mode 100644
index 00000000..e96d9823
--- /dev/null
+++ b/app/node_modules/electron-find/example2/inner1.html
@@ -0,0 +1,22 @@
+
+
+
+
+ webview 1
+
+
+
+
+
In webview 1
+
content 1
+
content 1
+
+
+
diff --git a/app/node_modules/electron-find/example2/inner2.html b/app/node_modules/electron-find/example2/inner2.html
new file mode 100644
index 00000000..0403a381
--- /dev/null
+++ b/app/node_modules/electron-find/example2/inner2.html
@@ -0,0 +1,22 @@
+
+
+
+
+ webview 2
+
+
+
+
+
In webview 2
+
content 2
+
content 2
+
+
+
diff --git a/app/node_modules/electron-find/example2/main.js b/app/node_modules/electron-find/example2/main.js
new file mode 100644
index 00000000..324a80a2
--- /dev/null
+++ b/app/node_modules/electron-find/example2/main.js
@@ -0,0 +1,47 @@
+const electron = require('electron')
+const { app, BrowserWindow, globalShortcut } = electron
+const path = require('path')
+let win
+const winURL = 'file://' + path.normalize(`${__dirname}/outer.html`)
+
+function createWindow () {
+ win = new BrowserWindow({
+ width: 1280,
+ height: 1040,
+ center: false,
+ webPreferences: {
+ nodeIntegration: true,
+ plugins: true,
+ }
+ })
+ win.loadURL(winURL)
+ //win.webContents.openDevTools()
+ win.on('closed', () => {
+ win = null
+ })
+
+ win.on('focus', () => {
+ globalShortcut.register('CommandOrControl+F', function () {
+ if (win && win.webContents) {
+ win.webContents.send('on-find', '')
+ }
+ })
+ })
+ win.on('blur', () => {
+ globalShortcut.unregister('CommandOrControl+F')
+ })
+
+}
+
+app.on('ready', createWindow)
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit()
+ }
+ globalShortcut.unregister('CommandOrControl+F')
+})
+
+app.on('activate', () => {
+ if (win === null) createWindow()
+})
+
diff --git a/app/node_modules/electron-find/example2/outer.html b/app/node_modules/electron-find/example2/outer.html
new file mode 100644
index 00000000..dbb7d3a7
--- /dev/null
+++ b/app/node_modules/electron-find/example2/outer.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Outer
+
+
+
+
+
+
Is in app
+
Main content
+
+
+
+
+
+
+
+
diff --git a/app/node_modules/electron-find/example2/outer.js b/app/node_modules/electron-find/example2/outer.js
new file mode 100644
index 00000000..bc780e36
--- /dev/null
+++ b/app/node_modules/electron-find/example2/outer.js
@@ -0,0 +1,37 @@
+const { remote, ipcRenderer } = require('electron')
+const { FindInPage } = require('../src/index.js')
+
+let findInPage = null
+const webview1 = document.querySelector('#webview1')
+webview1.addEventListener('dom-ready', () => {
+ findInPage = new FindInPage(webview1.getWebContents())
+ ipcRenderer.on('on-find', (e, args) => {
+ findInPage.openFindWindow()
+ })
+})
+webview1.addEventListener('close', () => {
+ console.log('webview1 close', )
+ if (findInPage) {
+ findInPage.destroy()
+ findInPage = null
+ }
+})
+webview1.addEventListener('destroyed', () => {
+ console.log('webview1 destroyed', )
+ if (findInPage) {
+ findInPage.destroy()
+ findInPage = null
+ }
+})
+
+webview1.addEventListener('crashed', () => {
+ console.log('webview1 crashed', )
+ if (findInPage) {
+ findInPage.destroy()
+ findInPage = null
+ }
+})
+
+ipcRenderer.on('on-find', (e, args) => {
+ findInPage ? findInPage.openFindWindow() : ''
+})
diff --git a/app/node_modules/electron-find/find.gif b/app/node_modules/electron-find/find.gif
new file mode 100644
index 00000000..02cd873e
Binary files /dev/null and b/app/node_modules/electron-find/find.gif differ
diff --git a/app/node_modules/electron-find/find2.png b/app/node_modules/electron-find/find2.png
new file mode 100644
index 00000000..213a63f2
Binary files /dev/null and b/app/node_modules/electron-find/find2.png differ
diff --git a/app/node_modules/electron-find/package.json b/app/node_modules/electron-find/package.json
new file mode 100644
index 00000000..88073b26
--- /dev/null
+++ b/app/node_modules/electron-find/package.json
@@ -0,0 +1,57 @@
+{
+ "_from": "electron-find",
+ "_id": "electron-find@1.0.6",
+ "_inBundle": false,
+ "_integrity": "sha512-RenjzlCCzX7edLywLy+qRYvzds11sBv8+SrJu/3l3eVLt9d9uNqCPk+uFZ525uAhSUaUalgZWDlhQdxIgT1khg==",
+ "_location": "/electron-find",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "tag",
+ "registry": true,
+ "raw": "electron-find",
+ "name": "electron-find",
+ "escapedName": "electron-find",
+ "rawSpec": "",
+ "saveSpec": null,
+ "fetchSpec": "latest"
+ },
+ "_requiredBy": [
+ "#USER",
+ "/"
+ ],
+ "_resolved": "https://registry.npmjs.org/electron-find/-/electron-find-1.0.6.tgz",
+ "_shasum": "e10e4be3eb0b634ed0d3acf383720fef39698cdb",
+ "_spec": "electron-find",
+ "_where": "F:\\projects\\p\\gitlit\\app",
+ "author": {
+ "name": "TheoXiong"
+ },
+ "bugs": {
+ "url": "https://github.com/TheoXiong/electron-find/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Find all matches for the text in electron app",
+ "devDependencies": {
+ "electron": "^2.0.0"
+ },
+ "homepage": "https://github.com/TheoXiong/electron-find#readme",
+ "keywords": [
+ "find",
+ "electron",
+ "search",
+ "match"
+ ],
+ "license": "MIT",
+ "main": "src/index.js",
+ "name": "electron-find",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/TheoXiong/electron-find.git"
+ },
+ "scripts": {
+ "e": "electron example/main.js",
+ "e2": "electron example2/main.js"
+ },
+ "version": "1.0.6"
+}
diff --git a/app/node_modules/electron-find/src/find.js b/app/node_modules/electron-find/src/find.js
new file mode 100644
index 00000000..3e5dab89
--- /dev/null
+++ b/app/node_modules/electron-find/src/find.js
@@ -0,0 +1,93 @@
+const EventEmitter = require('events')
+const { print } = require('./utils.js')
+
+const stopActions = ['clearSelection', 'keepSelection', 'activateSelection']
+const wcs = Symbol('webContents')
+const opts = Symbol('options')
+const requestId = Symbol('requestId')
+const activeMatch = Symbol('activeMatch')
+const matches = Symbol('matches')
+const initd = Symbol('initd')
+const preText = Symbol('preText')
+
+class Find extends EventEmitter {
+ constructor (webContents, options = {}) {
+ super()
+ this[wcs] = webContents
+ this[opts] = options
+ this[requestId] = null
+ this[activeMatch] = 0
+ this[matches] = 0
+ this[initd] = false
+ this[preText] = ''
+ }
+ initFind () {
+ if (this[initd]) return false
+ if (isWebContents.call(this)) {
+ bindFound.call(this)
+ return this[initd] = true
+ } else {
+ throw new Error('[Find] In need of a valid webContents !')
+ }
+ }
+ destroyFind () {
+ this[wcs] = null
+ this[opts] = null
+ this[requestId] = null
+ this[activeMatch] = 0
+ this[matches] = 0
+ this[initd] = false
+ this[preText] = ''
+ }
+ isFinding () {
+ return !!this[requestId]
+ }
+ startFind (text = '', forward = true, matchCase = false) {
+ if (!text) return
+ this[activeMatch] = 0
+ this[matches] = 0
+ this[preText] = text
+ this[requestId] = this[wcs].findInPage(this[preText], {
+ forward,
+ matchCase
+ })
+ print(`[Find] startFind text=${text} forward=${forward} matchCase=${matchCase}`)
+ }
+ findNext (forward, matchCase = false) {
+ if (!this.isFinding()) throw new Error('Finding did not start yet !')
+ this[requestId] = this[wcs].findInPage(this[preText], {
+ forward,
+ matchCase,
+ findNext: true
+ })
+ print(`[Find] findNext text=${this[preText]} forward=${forward} matchCase=${matchCase}`)
+ }
+ stopFind (action) {
+ stopActions.includes(action) ? '' : action = 'clearSelection'
+ this[wcs].stopFindInPage(action)
+ print(`[Find] stopFind action=${action}`)
+ }
+}
+function isWebContents () {
+ return (this[wcs] &&
+ typeof this[wcs].findInPage === 'function' &&
+ typeof this[wcs].stopFindInPage === 'function')
+}
+function bindFound () {
+ this[wcs].on('found-in-page', (e, r) => {
+ onFoundInPage.call(this, r)
+ })
+}
+function onFoundInPage (result) {
+ print('[Find] onFoundInPage, ', result)
+ if (this[requestId] !== result.requestId) return
+ typeof result.activeMatchOrdinal === 'number' ? this[activeMatch] = result.activeMatchOrdinal : ''
+ typeof result.matches === 'number' ? this[matches] = result.matches : ''
+ result.finalUpdate ? reportResult.call(this) : ''
+}
+function reportResult () {
+ this.emit('result', this[activeMatch], this[matches])
+ typeof this[opts].onResult === 'function' ? this[opts].onResult(this[activeMatch], this[matches]) : ''
+}
+
+module.exports = Find
diff --git a/app/node_modules/electron-find/src/findInPage.js b/app/node_modules/electron-find/src/findInPage.js
new file mode 100644
index 00000000..0f5ec221
--- /dev/null
+++ b/app/node_modules/electron-find/src/findInPage.js
@@ -0,0 +1,470 @@
+const Find = require('./find.js')
+const { print, on, off, move } = require('./utils.js')
+
+const INPUT_INTERVAL_THRESHOLD = 360
+
+const findBox = Symbol('findBox')
+const findInput = Symbol('findInput')
+const findMatches = Symbol('findMatches')
+const findCase = Symbol('findCase')
+const findBack = Symbol('findBack')
+const findForward = Symbol('findForward')
+const findClose = Symbol('findClose')
+const hasOpened = Symbol('hasOpened')
+const matchCase = Symbol('matchCase')
+
+const documentKeydown = Symbol('documentKeydown')
+const inputFocus = Symbol('inputFocus')
+const inputBlur = Symbol('inputBlur')
+const inputEvent = Symbol('inputEvent')
+const compositionstart = Symbol('compositionstart')
+const compositionend = Symbol('compositionend')
+const caseMouseenter = Symbol('caseMouseenter')
+const caseMouseleave = Symbol('caseMouseleave')
+const caseClick = Symbol('caseClick')
+const backMouseenter = Symbol('backMouseenter')
+const backMouseleave = Symbol('backMouseleave')
+const backClick = Symbol('backClick')
+const forwardMouseenter = Symbol('forwardMouseenter')
+const forwardMouseleave = Symbol('forwardMouseleave')
+const forwardClick = Symbol('forwardClick')
+const closeMouseenter = Symbol('closeMouseenter')
+const closeMouseleave = Symbol('closeMouseleave')
+const closeClick = Symbol('closeClick')
+const events = Symbol('events')
+
+const inComposition = Symbol('inComposition')
+const action = Symbol('action')
+const lastText = Symbol('lastText')
+const inputCnt = Symbol('inputCnt')
+const initialized = Symbol('initialized')
+const config = Symbol('config')
+
+class FindInPage extends Find{
+ constructor (webContents, options = {}) {
+ super(webContents)
+ this[findBox] = null
+ this[findInput] = null
+ this[findMatches] = null
+ this[findCase] = null
+ this[findBack] = null
+ this[findForward] = null
+ this[findClose] = null
+ this[hasOpened] = false
+ this[matchCase] = false
+ this[inComposition] = false
+ this[action] = ''
+ this[lastText] = ''
+ this[inputCnt] = 0
+ this[initialized] = false
+ this[config] = {}
+ this[events] = []
+ this.parentElement = options.parentElement ? options.parentElement : document.body
+ this.duration = (typeof options.duration === 'number' && options.duration > 0) ? options.duration : 300
+ this.options = options
+ this.options.preload ? this.initialize() : ''
+ }
+ initialize () {
+ if (this[initialized]) {
+ print('[FindInPage] Has initialize.')
+ return true
+ }
+ if (!this.initFind()) {
+ print('[FindInPage] Failed to initialize.')
+ return false
+ }
+ this[findBox] = creatElement('find-box')
+ this[findInput] = creatElement('find-input', 'input')
+ this[findMatches] = creatElement('find-matches')
+ this[findCase] = creatElement('find-case')
+ this[findBack] = creatElement('find-back')
+ this[findForward] = creatElement('find-forward')
+ this[findClose] = creatElement('find-close')
+ getUserConfig.call(this, this.options)
+ setBoxStyle.call(this)
+ setInputStyle.call(this)
+ setMatchesStyle.call(this)
+ setCaseStyle.call(this)
+ setBackStyle.call(this)
+ setForwardStyle.call(this)
+ setCloseStyle.call(this)
+ lockNext.call(this)
+ creatEventHandler.call(this)
+ bindEvents.call(this)
+ appendElement.call(this)
+ onResult.call(this)
+ move(this[findBox], (0 - this[findBox].offsetHeight - 10), this.duration)
+ return this[initialized] = true
+ }
+ openFindWindow () {
+ if (this[hasOpened]) {
+ focusInput.call(this)
+ return false
+ }
+ if (!this.initialize()) return false
+ setTimeout(() => {
+ this[findBox].style['visibility'] = 'visible'
+ lockNext.call(this)
+ focusInput.call(this)
+ }, 10)
+ move(this[findBox], parseInt(this[config].offsetTop), this.duration)
+ .then(() => {})
+ .catch(err => { throw err })
+ return this[hasOpened] = true
+ }
+ closeFindWindow () {
+ if (!this[hasOpened]) return false
+ this[findInput].value = ''
+ this[action] = ''
+ this[lastText] = ''
+ this[findMatches].innerText = '0/0'
+ this[hasOpened] = false
+ lockNext.call(this)
+ move(this[findBox], (0 - this[findBox].offsetHeight - 10), this.duration)
+ .then(() => { this[findBox].style['visibility'] = 'hidden' })
+ .catch(err => { throw err })
+ return true
+ }
+ destroy () {
+ this.destroyFind()
+ unbindEvents.call(this)
+ this.closeFindWindow()
+ removeElement.call(this)
+ }
+}
+
+function creatElement (className = '', tag = 'div') {
+ const ele = document.createElement(tag)
+ ele.classList.add(className)
+ return ele
+}
+function getUserConfig (options) {
+ this[config].offsetTop = typeof options.offsetTop === 'number' ? `${options.offsetTop}px` : '5px'
+ this[config].offsetRight = typeof options.offsetRight === 'number' ? `${options.offsetRight}px` : '5px'
+ this[config].boxBgColor = typeof options.boxBgColor === 'string' ? options.boxBgColor : '#fff'
+ this[config].boxShadowColor = typeof options.boxShadowColor === 'string' ? options.boxShadowColor : '#909399'
+ this[config].inputColor = typeof options.inputColor === 'string' ? options.inputColor : '#606266'
+ this[config].inputBgColor = typeof options.inputBgColor === 'string' ? options.inputBgColor : '#f0f0f0'
+ this[config].inputFocusColor = typeof options.inputFocusColor === 'string' ? options.inputFocusColor : '#c5ade0'
+ this[config].textColor = typeof options.textColor === 'string' ? options.textColor : '#606266'
+ this[config].textHoverBgColor = typeof options.textHoverBgColor === 'string' ? options.textHoverBgColor : '#eaeaea'
+ this[config].caseSelectedColor = typeof options.caseSelectedColor === 'string' ? options.caseSelectedColor : '#c5ade0'
+}
+function setBoxStyle () {
+ this[findBox].style.cssText = `position:fixed; top:-110%; z-index: 3001; max-height:48px; min-height:30px;
+ right:${this[config].offsetRight}; display:flex; align-items:center; box-sizing:border-box !important;
+ padding:6px; visibility: hidden; background:${this[config].boxBgColor};
+ box-shadow: 1px 1px 2px 0.5px ${this[config].boxShadowColor};`
+}
+function setInputStyle () {
+ this[findInput].style.cssText = `width:168px; outline:0; border:1px solid ${this[config].inputBgColor};
+ background:${this[config].inputBgColor}; margin-right:6px; border-radius:2px; color:${this[config].inputColor}`
+}
+function setMatchesStyle () {
+ this[findMatches].innerText = '0/0'
+ this[findMatches].style.cssText = `color:${this[config].textColor}; font-size:14px; display:flex; align-items:center;
+ justify-content:center; min-width:40px; max-width:64px; overflow:hidden; margin-right:4px;`
+}
+function setCaseStyle () {
+ this[findCase].innerText = 'Aa'
+ this[findCase].style.cssText = `font-size:14px; font-weight:700; cursor:pointer; -webkit-user-select:none; color:${this[config].textColor};
+ padding:0px 2px; border-radius:2px; border:1px solid transparent; margin-right:4px; display:flex; align-items:center;`
+}
+function setBackStyle () {
+ this[findBack].style.cssText = `cursor:pointer; -webkit-user-select:none; position: relative; height: 20px; width: 20px; border-radius:2px;
+ overflow: hidden; display: inline-block; background:${this[config].boxBgColor}; border:0px solid ${this[config].boxBgColor};`
+
+ let backLine = creatElement('find-back-line')
+ backLine.style.cssText = `width:0; height:0; border:7px solid transparent; border-right-color:${this[config].textColor};
+ position: absolute; top:3px; left:-1px;`
+ this[findBack].appendChild(backLine)
+
+ let backCover = creatElement('find-back-cover')
+ backCover.style.cssText = `width:0; height:0; border:7px solid transparent; border-right-color:inherit;
+ position: absolute; top:3px; left:2px; z-index:1001;`
+ this[findBack].appendChild(backCover)
+}
+function setForwardStyle () {
+ this[findForward].style.cssText = `cursor:pointer; -webkit-user-select:none; position: relative; height: 20px; width: 20px; border-radius:2px;
+ overflow: hidden; display: inline-block; background:${this[config].boxBgColor}; border:0px solid ${this[config].boxBgColor};`
+
+ let forwardLine = creatElement('find-forward-line')
+ forwardLine.style.cssText = `width:0; height:0; border:7px solid transparent; border-left-color:${this[config].textColor};
+ position: absolute; top:3px; left:6px;`
+ this[findForward].appendChild(forwardLine)
+
+ let forwardCover = creatElement('find-forward-cover')
+ forwardCover.style.cssText = `width:0; height:0; border:7px solid transparent; border-left-color:inherit;
+ position: absolute; top:3px; left:3px; z-index:1001;`
+ this[findForward].appendChild(forwardCover)
+}
+function setCloseStyle () {
+ this[findClose].style.cssText = `cursor:pointer; -webkit-user-select:none; position: relative; height: 20px; width: 20px;
+ overflow: hidden; display: inline-block; background:${this[config].boxBgColor}; border-radius:2px;`
+
+ let closeInner1 = creatElement('find-close-inner1')
+ closeInner1.style.cssText = `width:14px; height:2px; background:${this[config].textColor}; transform:rotate(45deg);
+ position: absolute; top:9px; left:3px;`
+ this[findClose].appendChild(closeInner1)
+
+ let closeInner2 = creatElement('find-close-inner2')
+ closeInner2.style.cssText = `width:14px; height:2px; background:${this[config].textColor}; transform:rotate(-45deg);
+ position: absolute; top:9px; left:3px;`
+ this[findClose].appendChild(closeInner2)
+}
+function appendElement () {
+ [this[findInput], this[findMatches], this[findCase], this[findBack], this[findForward], this[findClose]].forEach((item) => {
+ this[findBox].appendChild(item)
+ })
+ this.parentElement.appendChild(this[findBox])
+}
+function removeElement () {
+ this.parentElement.removeChild(this[findBox])
+}
+function creatEventHandler () {
+ this[documentKeydown] = (function (e) {
+ if (!this[hasOpened]) return
+ onKeydown.call(this, e)
+ }).bind(this)
+ this[events].push({ ele: document, name: 'keydown', fn: this[documentKeydown] })
+
+ this[inputFocus] = (function () {
+ this[findInput].style.border = `1px solid ${this[config].inputFocusColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findInput], name: 'focus', fn: this[inputFocus] })
+
+ this[inputBlur] = (function () {
+ this[findInput].style.border = `1px solid ${this[config].inputBgColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findInput], name: 'blur', fn: this[inputBlur] })
+
+ this[inputEvent] = (function () {
+ updateCnt.call(this)
+ isInputing.call(this)
+ .then(res => {
+ res ? '' : onInput.call(this)
+ })
+ }).bind(this)
+ this[events].push({ ele: this[findInput], name: 'input', fn: this[inputEvent] })
+
+ this[compositionstart] = (function () {
+ print('compositionstart')
+ this[inComposition] = true
+ }).bind(this)
+ this[events].push({ ele: this[findInput], name: 'compositionstart', fn: this[compositionstart] })
+
+ this[compositionend] = (function () {
+ print('compositionend')
+ this[inComposition] = false
+ }).bind(this)
+ this[events].push({ ele: this[findInput], name: 'compositionend', fn: this[compositionend] })
+
+ this[caseMouseenter] = (function () {
+ this[findCase].style['background'] = this[config].textHoverBgColor
+ }).bind(this)
+ this[events].push({ ele: this[findCase], name: 'mouseenter', fn: this[caseMouseenter] })
+
+ this[caseMouseleave] = (function () {
+ this[findCase].style['background'] = this[config].boxBgColor
+ }).bind(this)
+ this[events].push({ ele: this[findCase], name: 'mouseleave', fn: this[caseMouseleave] })
+
+ this[caseClick] = (function () {
+ onCaseClick.call(this)
+ }).bind(this)
+ this[events].push({ ele: this[findCase], name: 'click', fn: this[caseClick] })
+
+ this[backMouseenter] = (function () {
+ this[findBack].style['background'] = this[config].textHoverBgColor
+ this[findBack].style['border'] = `0px solid ${this[config].textHoverBgColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findBack], name: 'mouseenter', fn: this[backMouseenter] })
+
+ this[backMouseleave] = (function () {
+ this[findBack].style['background'] = this[config].boxBgColor
+ this[findBack].style['border'] = `0px solid ${this[config].boxBgColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findBack], name: 'mouseleave', fn: this[backMouseleave] })
+
+ this[backClick] = (function () {
+ onBackClick.call(this)
+ }).bind(this)
+ this[events].push({ ele: this[findBack], name: 'click', fn: this[backClick] })
+
+ this[forwardMouseenter] = (function () {
+ this[findForward].style['background'] = this[config].textHoverBgColor
+ this[findForward].style['border'] = `0px solid ${this[config].textHoverBgColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findForward], name: 'mouseenter', fn: this[forwardMouseenter] })
+
+ this[forwardMouseleave] = (function () {
+ this[findForward].style['background'] = this[config].boxBgColor
+ this[findForward].style['border'] = `0px solid ${this[config].boxBgColor}`
+ }).bind(this)
+ this[events].push({ ele: this[findForward], name: 'mouseleave', fn: this[forwardMouseleave] })
+
+ this[forwardClick] = (function () {
+ onForwardClick.call(this)
+ }).bind(this)
+ this[events].push({ ele: this[findForward], name: 'click', fn: this[forwardClick] })
+
+ this[closeMouseenter] = (function () {
+ this[findClose].style['background'] = this[config].textHoverBgColor
+ }).bind(this)
+ this[events].push({ ele: this[findClose], name: 'mouseenter', fn: this[closeMouseenter] })
+
+ this[closeMouseleave] = (function () {
+ this[findClose].style['background'] = this[config].boxBgColor
+ }).bind(this)
+ this[events].push({ ele: this[findClose], name: 'mouseleave', fn: this[closeMouseleave] })
+
+ this[closeClick] = (function () {
+ onCloseClick.call(this)
+ }).bind(this)
+ this[events].push({ ele: this[findClose], name: 'click', fn: this[closeClick] })
+}
+
+function bindEvents () {
+ this[events].forEach((item) => {
+ on(item.ele, item.name, item.fn)
+ })
+}
+function unbindEvents () {
+ this[events].forEach((item) => {
+ off(item.ele, item.name, item.fn)
+ })
+}
+
+function updateCnt () {
+ if (this[inputCnt] >= 0xFFFFFFFE) {
+ this[inputCnt] = 0
+ }
+ this[inputCnt]++
+}
+
+function isInputing () {
+ return new Promise((resolve, reject) => {
+ let currCnt = this[inputCnt]
+ setTimeout(() => {
+ currCnt !== this[inputCnt] ? resolve(true) : resolve(false)
+ }, INPUT_INTERVAL_THRESHOLD)
+ })
+}
+
+function focusInput (doBlur = false) {
+ setTimeout(() => {
+ doBlur ? this[findInput].blur() : ''
+ this[findInput].focus()
+ }, 50)
+}
+
+function wrapInput (inputEle, caseEle, timeout = 50) {
+ inputEle.type = 'password'
+ caseEle.style['visibility'] = 'hidden'
+
+ setTimeout(() => {
+ if (inputEle.type !== 'text') {
+ print('[FindInPage] wrapInput timeout..')
+ unwrapInput(inputEle, caseEle)
+ }
+ }, timeout)
+}
+function unwrapInput (inputEle, caseEle) {
+ inputEle.type = 'text'
+ caseEle.style['visibility'] = 'visible'
+}
+
+function onInput () {
+ setTimeout(() => {
+ if (this[inComposition]) return
+ this[action] = 'input'
+ let text = this[findInput].value
+ if (text && text !== this[lastText]) {
+ this[lastText] = text
+ wrapInput(this[findInput], this[findCase], 100)
+ this.startFind(text, true, this[matchCase])
+ } else if (this[lastText] && text === '') {
+ this.stopFind()
+ this[findMatches].innerText = '0/0'
+ lockNext.call(this)
+ focusInput.call(this, true)
+ }
+ }, 50)
+}
+
+function onKeydown (e) {
+ if (this[inComposition] || !e) return
+ switch (e.code) {
+ case 'Enter':
+ case 'NumpadEnter':
+ let text = this[findInput].value
+ if (!text) return
+ e.shiftKey ? findKeep.call(this, false) : findKeep.call(this, true)
+ break
+ case 'Escape':
+ onCloseClick.call(this)
+ break
+ default:
+ break
+ }
+}
+
+function findKeep (forward) {
+ if (!this.isFinding()) return
+ forward ? onForwardClick.call(this) : onBackClick.call(this)
+}
+
+function onCaseClick () {
+ if (!this[matchCase]) {
+ this[matchCase] = true
+ this[findCase].style['border-color'] = this[config].caseSelectedColor
+ wrapInput(this[findInput], this[findCase], 100)
+ this.startFind(this[findInput].value, true, this[matchCase])
+ } else {
+ this[matchCase] = false
+ this[findCase].style['border-color'] = 'transparent'
+ wrapInput(this[findInput], this[findCase], 100)
+ this.startFind(this[findInput].value, true, this[matchCase])
+ }
+}
+
+function onBackClick () {
+ this[action] = 'back'
+ wrapInput(this[findInput], this[findCase], 100)
+ this.findNext(false, this[matchCase])
+}
+
+function onForwardClick () {
+ this[action] = 'forward'
+ wrapInput(this[findInput], this[findCase], 100)
+ this.findNext(true, this[matchCase])
+}
+
+function onCloseClick () {
+ this.closeFindWindow() ? this.stopFind() : ''
+}
+
+function onResult () {
+ this.on('result', (activeMatch, matches) => {
+ unwrapInput(this[findInput], this[findCase])
+ this[findMatches].innerText = `${activeMatch}/${matches}`
+ matches > 0 ? unlockNext.call(this) : lockNext.call(this)
+ this[action] === 'input' ? focusInput.call(this) : ''
+ })
+}
+
+function lockNext () {
+ this[findBack].style['opacity'] = 0.6
+ this[findBack].style['pointer-events'] = 'none'
+ this[findForward].style['opacity'] = 0.6
+ this[findForward].style['pointer-events'] = 'none'
+}
+
+function unlockNext () {
+ this[findBack].style['opacity'] = 1
+ this[findBack].style['pointer-events'] = 'auto'
+ this[findForward].style['opacity'] = 1
+ this[findForward].style['pointer-events'] = 'auto'
+}
+
+module.exports = FindInPage
diff --git a/app/node_modules/electron-find/src/index.js b/app/node_modules/electron-find/src/index.js
new file mode 100644
index 00000000..84d03064
--- /dev/null
+++ b/app/node_modules/electron-find/src/index.js
@@ -0,0 +1,8 @@
+const FindInPage =require('./findInPage')
+const Find = require('./find.js')
+
+
+module.exports = {
+ FindInPage,
+ Find
+}
\ No newline at end of file
diff --git a/app/node_modules/electron-find/src/utils.js b/app/node_modules/electron-find/src/utils.js
new file mode 100644
index 00000000..9bf44006
--- /dev/null
+++ b/app/node_modules/electron-find/src/utils.js
@@ -0,0 +1,107 @@
+const debug = false
+const print = debug ? console.log.bind(console) : () => {}
+
+const on = (() => {
+ if (document && document.addEventListener) {
+ return (element, event, handler) => {
+ if (element && event && handler) {
+ element.addEventListener(event, handler, false)
+ }
+ }
+ } else if (document && document.attachEvent) {
+ return (element, event, handler) => {
+ if (element && event && handler) {
+ element.attachEvent(`on${event}`, handler)
+ }
+ }
+ } else {
+ return () => {}
+ }
+})()
+
+const off = (() => {
+ if (document && document.removeEventListener) {
+ return (element, event, handler) => {
+ if (element && event && handler) {
+ element.removeEventListener(event, handler, false)
+ }
+ }
+ } else if (document && document.detachEvent) {
+ return (element, event, handler) => {
+ if (element && event && handler) {
+ element.detachEvent(`on${event}`, handler)
+ }
+ }
+ } else {
+ return () => {}
+ }
+})()
+
+const once = (element, event, fn) => {
+ let listener = function () {
+ if (typeof fn === 'function') {
+ fn.apply(this, arguments)
+ }
+ off(element, event, listener)
+ }
+ on(element, event, listener)
+}
+
+window.requestAnimationFrame = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ function (callback) {
+ return window.setTimeout(callback, 1000 / 60)
+ }
+
+window.cancelAnimationFrame = window.cancelAnimationFrame ||
+ window.webkitCancelAnimationFrame ||
+ window.mozCancelAnimationFrame ||
+ window.msCancelAnimationFrame ||
+ window.oCancelAnimationFrame ||
+ function (id) {
+ window.clearTimeout(id)
+ }
+
+const move = (element, end, duration = 300) => {
+ return new Promise((resolve, reject) => {
+ try {
+ let winFrameId = null
+ let stepTime = duration / (1000 / 60)
+ let curr = parseInt(element.style.top)
+ let stepDist = (end - curr) / stepTime
+ let stepCnt = 0
+
+ const step = function () {
+ curr += stepDist
+ stepCnt++
+ if (stepCnt >= stepTime || (Math.abs(end - curr) <= (stepDist + 1))) {
+ element.style.top = `${end}px`
+ if (winFrameId) {
+ window.cancelAnimationFrame(winFrameId)
+ winFrameId = null
+ }
+ resolve()
+ } else {
+ element.style.top = `${curr}px`
+ winFrameId = window.requestAnimationFrame(step)
+ }
+ }
+ step()
+ } catch (error) {
+ reject(error)
+ }
+ })
+
+
+}
+
+module.exports = {
+ print,
+ on,
+ off,
+ once,
+ move
+}
diff --git a/app/package-lock.json b/app/package-lock.json
index c804e903..a441629f 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -419,6 +419,11 @@
}
}
},
+ "electron-find": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/electron-find/-/electron-find-1.0.6.tgz",
+ "integrity": "sha512-RenjzlCCzX7edLywLy+qRYvzds11sBv8+SrJu/3l3eVLt9d9uNqCPk+uFZ525uAhSUaUalgZWDlhQdxIgT1khg=="
+ },
"electron-is-accelerator": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz",
diff --git a/app/package.json b/app/package.json
index 2e06f909..e523ed92 100644
--- a/app/package.json
+++ b/app/package.json
@@ -7,6 +7,7 @@
"animate.css": "^3.5.2",
"bootstrap": "^4.1.3",
"ejs": "^2.6.1",
+ "electron-find": "^1.0.6",
"electron-localshortcut": "^3.1.0",
"jquery": "^3.3.1",
"material-design-icons": "^3.0.1",