1
0
mirror of https://github.com/S2-/gitlit synced 2025-08-03 21:00:04 +02:00

add node modules to repo

This commit is contained in:
s2
2018-06-03 13:47:11 +02:00
parent e8c95255e8
commit d002126b72
4115 changed files with 440218 additions and 7519 deletions

21
app/node_modules/electron-in-page-search/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 rhysd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.

280
app/node_modules/electron-in-page-search/README.md generated vendored Normal file
View File

@@ -0,0 +1,280 @@
In-Page Search for Electron Applications
========================================
[![npm version](https://badge.fury.io/js/electron-in-page-search.svg)](https://badge.fury.io/js/electron-in-page-search)
[![Build Status on Travis CI](https://travis-ci.org/rhysd/electron-in-page-search.svg?branch=master)](https://travis-ci.org/rhysd/electron-in-page-search)
[![Build Status on AppVeyor](https://ci.appveyor.com/api/projects/status/k80y8ccgpbt1ba57?svg=true)](https://ci.appveyor.com/project/rhysd/electron-in-page-search)
This package provides Chrome's native in-page search feature to Electron applications.
Electron exposes Chrome's native API to JavaScript. But native in-page search API has
some pitfalls and stateful. So this package wraps it and provide provide more easy,
pitfall-free APIs.
![screenshot](https://github.com/rhysd/ss/blob/master/electron-in-page-search/main.gif?raw=true)
In-page search can be used for browser window or webview (`BrowserWindow` instance or
`<webview>` tag) in Electron app. You can use only one function for both of them
in renderer process.
```javascript
// Pass current browser window's WebContents instance
const searchInWindow = searchInPage(remote.getCurrentWebContents());
// Pass <webview> instance
const searchInWebview = searchInPage(document.getElementById('my-webview'));
// Open inner window made with <webview> for in-page search
// Search some text in the browser window
searchInWindow.openSearchWindow();
// Search some text in the webview
searchInWebview.openSearchWindow();
```
This package works cross platform (macOS, Linux and Windows) with running CI on them
(Travis CI for macOS and Linux, AppVeyor for Windows).
## Installation
```
$ npm install --save electron-in-page-search
```
## Examples
Two examples are added. So please see the code of working app there.
- [Search in browser window](example/browser-window)
- [Search in `<webview>`](example/webview)
You can try them by cloning this repository.
```
$ git clone https://github.com/rhysd/electron-in-page-search.git
$ cd electron-in-page-search
$ npm install
$ npm run build
$ npm run example # Run browser window example
$ cd example/webview/
$ npm start # Run webview example
```
You can also see [the real world example](https://github.com/rhysd/Chromenu).
To know APIs for this package, you can see [TypeScript's type definitions](index.d.ts).
## Usage
When you want to use in-page search in app, call `searchInPage` function to create an `InPageSearch` instance.
```javascript
import searchInPage from 'electron-in-page-search';
// or
const searchInPage = require('electron-in-page-search').default;
import {remote} from 'electron';
const inPageSearch = searchInPage(remote.getCurrentWebContents());
document.getElementById('some-button').addEventListener('click', () => {
inPageSearch.openSearchWindow();
});
```
When calling `searchInPage`, it creates a `<webview>` element for search window.
This `<webview>` can avoid that in-page search finds the text in the search window.
The webview has a class property `electron-in-page-search-window search-inactive` by default.
Then `openSearchWindow` is called, the webview has a class property `electron-in-page-search-window search-active`
while searching. So you can styling the search window webview by CSS like below:
```css
.electron-in-page-search-window {
width: 300px;
height: 36px;
background-color: white;
}
.electron-in-page-search-window.search-inactive {
visibility: hidden;
}
.electron-in-page-search-window.search-active {
visibility: visible;
}
```
You can control background color of search window by adding `background-color`
(in above, `white` is specified). You can customize CSS further (please see below
'Customization' section).
Please see [example's style](example/browser-window/style.css) for live example.
The search window contains 'back' button, 'forward' button, 'close' button and query form.
Application users can input a query and click them (or press enter key in the form) to start
the in-page search.
Repeating to press enter key or clicking 'back'/'forward' buttons moves a focus on hit words.
Finally the users can close a search window by clicking 'close' button to stop the search.
After a search window closing, the window's class property will be `electron-in-page-search-window search-inactive`
again.
The search window `<webview>` is mounted to `document.body` (or an element specified with `searchWindowParent` option).
When you want to destroy `InPageSearch` instance, please ensure to call `.finalize()` method.
It will unmount the search window `<webview>` from DOM.
## Development
### Debugging
If you want to see a DevTools of search window, please pass `openDevToolsOfSearchWindow`
property to `searchInPage` function as below.
```javascript
searchInPage(webContents, { openDevToolsOfSearchWindow: true });
```
It opens the DevTools with detach mode.
And this package also supports logging. When `$ELECTRON_IN_PAGE_SEARCH_DEBUG` environment
variable is not empty, it outputs logs with `console.log` in rendrer process.
### TypeScript
This package is written in [TypeScript](https://github.com/Microsoft/TypeScript) and ready for TypeScript.
You need not to prepare type definition file for this package because [index.d.ts](index.d.ts) is
already in this package.
```typescript
import searchInPage, {InPageSearch} from 'electron-in-page-search';
let search: InPageSearch;
const elem = document.createElement('webview');
elem.src = 'https://example.com';
document.getElementById('main').appendChild(elem);
elem.on('dom-ready', () => {
search = searchInPage(elem);
});
document.getElementById('search-button').addEventListener('click', () => {
if (search) {
search.openSearchWindow();
}
});
```
### My Environment
I'm testing this package with below OS
- macOS 10.12, OS X 10.11.6
- Ubuntu Linux 16.04 LTS
- Windows 8.1
## Customization
### Use my own CSS for search window
If you want to use a default search window but don't want to use a default CSS,
you can use your own CSS file.
e.g.
```javascript
const path = require('path');
searchInPage(webview, {
customCssPath: path.join(__dirname, 'my_awesome_styles.css')
});
```
Below is a list of `class` property of each parts in search window.
Please write your CSS styles for below classes.
| class name | description | element |
|-------------------------|-----------------------------|-----------|
| `inpage-search-body` | Body of whole search window | `<div>` |
| `inpage-search-input` | Query form | `<input>` |
| `inpage-search-matches` | 'N/M' search count | `<div>` |
| `inpage-search-back` | 'back' button | `<div>` |
| `inpage-search-forward` | 'forward' button | `<div>` |
| `inpage-search-close` | 'close' button | `<div>` |
### Use my own HTML for search window
If you want to control the whole search window, you can pass a path to your own HTML file.
```javascript
const path = require('path');
searchInPage(webview, {
customCssPath: path.join(__dirname, 'my_awesome_styles.css'),
customSearchWindowHtmlPath: path.join(__dirname, 'my_awesome_search_window.html')
});
```
electron-in-page-search package injects `<script>` tag to setup IPC messaging between
a search window `<webview>` and a renderer process. It finds each elements and
sets listners through class names.
So you need to maintain above class names also in your own search window HTML.
### Lifetime hooks for search
`InPageSearch` instance (returned from `searchInPage`) extends `EventEmitter`.
It emits some events on some timings.
You can hook them to execute your code at some points.
Below is a list of hook names.
| hook name | description | listener args |
|---------------|------------------------------------------|-------------------------------------------|
| 'open' | On window opened | `()` |
| 'start' | On in-page search started | `(query: string)` |
| 'next' | On finding next match | `(query: string, forward: boolean)` |
| 'focus-input' | On focusing on search window | `()` |
| 'found' | On some word matched to the search query | `(activeMatch: number, allMatch: number)` |
### Animation for search window
You can use CSS animation for animation of search window. If you don't want to animate a search window
when the webview is mounted, please use `search-firstpaint` class name as below:
```css
.electron-in-page-search-window.search-firstpaint {
visibility: hidden;
}
.electron-in-page-search-window.search-inactive {
animation-duration: 0.2s;
animation-name: yourAwesomeAnimationOnClosing;
}
.electron-in-page-search-window.search-active {
animation-duration: 0.2s;
animation-name: yourAwesomeAnimationOnOpening;
}
```
The `search-firstpaint` class will be removed when opening search window at first.
### Preload a search window
`InPageSearch` instance delays creating `<webview>` element for a search window
until first `openSearchWindow` is called at first.
This is better in terms of memory efficiency because `<webview>` forks a new process.
If you want to load a search window in advance, please set `preloadSearchWindow: true`
to the second argument of `searchInPage()` call.

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>electron-in-page-search Example</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<button id="search-page-button">Search This Page</button>
<section>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nec turpis sed leo viverra auctor ut eget risus. Nullam consectetur orci eget semper suscipit. Cras suscipit porttitor semper. Curabitur semper, purus nec cursus tristique, mi augue maximus sem, et aliquet tellus sapien non est. Pellentesque ipsum velit, pretium et enim in, hendrerit tincidunt dui. In tortor sem, semper at luctus at, eleifend vel turpis. Morbi non justo non nisi accumsan pharetra eu non risus. Vivamus eleifend quam vitae ornare sagittis. Nam ac ante egestas, luctus magna nec, consectetur nunc. Duis suscipit id nibh eget faucibus. Nam at viverra massa. Donec sed sodales nunc. Vestibulum ac sapien velit. Sed magna mi, lacinia in enim ac, tincidunt feugiat lorem. In tincidunt accumsan leo, non aliquet ex lobortis vel.
</p><p>
Nam hendrerit erat nec posuere sodales. Duis id ligula id ante varius luctus nec nec dui. Nunc eu rutrum metus. Mauris purus ligula, tempus eget cursus nec, sagittis quis mi. Praesent sit amet urna id ante sodales eleifend. Curabitur tristique pretium maximus. Curabitur sit amet diam dictum, luctus sem a, vestibulum leo. In pellentesque ac lacus ut gravida. Fusce porttitor tincidunt velit, ac condimentum nunc efficitur ut. Mauris non fermentum enim. Suspendisse mauris magna, commodo eu odio ut, fermentum mollis lorem. Vivamus cursus consequat orci, at eleifend tortor tincidunt at.
</p><p>
Sed aliquet urna scelerisque dolor volutpat lobortis. Sed pharetra tempus tempor. Vivamus et libero non nibh sodales aliquet. Cras pulvinar nulla ac quam pharetra, id tincidunt justo malesuada. Nunc eu arcu varius, tincidunt nunc vitae, vehicula lorem. Fusce in posuere tellus. Cras ut quam venenatis, ullamcorper quam vel, laoreet felis. Donec cursus felis dui, a facilisis eros vestibulum at. Praesent commodo ligula ac ante pretium facilisis. Curabitur quis enim id massa vulputate egestas vitae et ex. Etiam gravida ultricies pretium. Sed sed est mauris.
</p><p>
Vestibulum tincidunt, orci nec vestibulum posuere, ligula sem dictum risus, vitae rhoncus ex elit quis dui. Praesent tincidunt, diam quis suscipit posuere, velit sem vestibulum ante, vitae congue libero dui eu arcu. Donec tincidunt ac sapien in accumsan. Nunc ut libero fringilla, tincidunt magna sed, vestibulum tellus. Cras congue fermentum orci, a consectetur leo placerat sed. Nam tortor nulla, lacinia vel suscipit non, sodales ac orci. Maecenas ornare turpis urna, vitae posuere metus laoreet eu. In sodales vitae nulla nec eleifend. Aenean ullamcorper rutrum ex vitae suscipit. Fusce sit amet feugiat metus, vel volutpat arcu.
</p><p>
Etiam dignissim, justo et fringilla fermentum, augue turpis interdum purus, in tincidunt libero eros at lorem. Curabitur eu est ullamcorper est porta congue quis vel dolor. Cras ut nisl sed velit sagittis malesuada. Etiam tempus venenatis lacus. Nulla facilisi. Donec tellus augue, laoreet eget pharetra vitae, vestibulum ut tortor. Aliquam finibus condimentum vehicula. Donec vitae justo eu turpis rhoncus porttitor. Donec non felis eros. Ut eleifend interdum diam quis faucibus. Vestibulum non suscipit augue. Nulla sit amet lectus nulla. Integer mattis fermentum augue.
</p><p>
Nulla felis est, euismod ac tortor sit amet, ornare rhoncus ipsum. Integer quis lobortis nulla. Morbi ultrices euismod ligula sit amet ultricies. Maecenas tincidunt lorem in neque porta, non viverra eros egestas. Integer ex quam, laoreet eget viverra eget, interdum eget neque. Nunc interdum efficitur quam, eu vehicula diam volutpat quis. Maecenas at nisi ultricies, dignissim dui eu, tincidunt justo.
</p><p>
Proin in laoreet odio, nec vehicula augue. Aenean vitae elit a diam congue rhoncus. Ut in tempus ante. Nunc ut urna sit amet dui iaculis viverra. Praesent euismod justo ac justo egestas, vitae maximus mauris efficitur. Pellentesque et enim ac risus vestibulum ultrices vel nec lectus. Nulla eu rutrum magna. In fringilla sed leo eget ullamcorper. Aliquam nisl nulla, fermentum vel dapibus a, vehicula a dui. Phasellus luctus pellentesque augue, in eleifend tellus sodales non. Curabitur sit amet interdum quam. Nullam ullamcorper mauris vitae massa semper malesuada. Nulla ac ullamcorper mauris. Vivamus ac mi vel arcu luctus lacinia id egestas erat. Donec id lorem rhoncus, efficitur nisi sed, sodales est.
</p><p>
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean sed est quis tellus convallis auctor. Praesent euismod elementum ligula, eu aliquet est rutrum in. In aliquet purus sed ipsum mattis tempus. Praesent cursus massa scelerisque orci molestie tincidunt. Integer pellentesque commodo nibh, sit amet semper est laoreet vel. Ut cursus lectus ac sem mattis laoreet. Nam interdum, velit vel tincidunt vehicula, metus elit fermentum neque, vitae aliquet risus justo quis libero. Suspendisse facilisis ligula eu viverra vehicula. Aliquam maximus a erat a scelerisque. Nulla vehicula, dolor ut faucibus congue, orci augue rutrum ante, non placerat felis dui luctus massa.
</p><p>
Nunc mollis pretium tortor. Nam eget dolor tempus, malesuada lorem non, aliquam ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla vitae viverra nulla, ut fermentum sem. In consectetur magna et ornare dictum. Aliquam ultrices lorem vel congue tempor. In iaculis ac dui aliquet laoreet. Proin non pulvinar elit. Pellentesque semper diam eu magna molestie, nec commodo eros pulvinar. Nulla faucibus nulla libero, sit amet auctor risus elementum eget.
</p><p>
Maecenas pulvinar, dolor nec sodales vulputate, nisi mauris egestas neque, a tempor neque nisl id nulla. Fusce ut arcu interdum, venenatis mauris sit amet, facilisis enim. Aliquam vulputate congue metus, non sollicitudin ante. Mauris quis egestas dolor, a lacinia tellus. Pellentesque scelerisque suscipit ligula id accumsan. Nulla sed libero hendrerit, sodales justo at, fringilla elit. In hac habitasse platea dictumst. Etiam hendrerit nisl et felis feugiat, in pellentesque erat sagittis. Duis dignissim velit accumsan fermentum maximus. Donec id eros lorem. Praesent quis ultricies diam, vitae interdum libero. Aenean justo mauris, ullamcorper sed tincidunt eu, tempus ut erat. Morbi commodo erat ut laoreet aliquam. Duis in tortor a urna pretium sodales ut a magna.
</p>
</section>
<script src="renderer.js"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const join = require('path').join;
app.once('window-all-closed',function() { app.quit(); });
app.once('ready', function() {
let w = new BrowserWindow();
w.once('closed', function() { w = null; });
w.loadURL('file://' + join(__dirname, 'index.html'));
if (process.env.ELECTRON_IN_PAGE_SEARCH_DEBUG) {
w.webContents.openDevTools({mode: 'detach'});
}
});

View File

@@ -0,0 +1,17 @@
{
"name": "electron-in-page-search-browser-window-example",
"version": "0.0.0",
"private": true,
"description": "",
"main": "main.js",
"scripts": {
"start": "../../node_modules/.bin/electron .",
"debug": "ELECTRON_IN_PAGE_SEARCH_DEBUG=true ../../node_modules/.bin/electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "rhysd <lin90162@yahoo.co.jp>",
"license": "MIT",
"devDependencies": {},
"dependencies": {}
}

View File

@@ -0,0 +1,16 @@
const searchInPage = require('../..').default;
const remote = require('electron').remote;
/*
* Create a search instance for the current page.
* Make sure that create the search instance per one WebContents instance.
*/
const search = searchInPage(remote.getCurrentWebContents());
document.getElementById('search-page-button').addEventListener('click', () => {
/*
* .openSearchWindow() method opens and activates a search window.
* User can input the query and start the word seatch in page.
*/
search.openSearchWindow();
});

View File

@@ -0,0 +1,29 @@
/*
* .electron-in-page-search-window is a class specified to default
* <webview> element for search window.
*/
.electron-in-page-search-window {
position: fixed;
top: 0;
right: 0;
border: solid grey 1px;
background-color: white;
width: 300px;
height: 36px;
}
/*
* .search-inactive is added to search window <webview> when the window
* is inactive.
*/
.search-inactive {
visibility: hidden;
}
/*
* .search-inactive is added to search window <webview> when the window
* is active.
*/
.search-active {
visibility: visible;
}

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>electron-in-page-search Example</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<button id="search-page-button">Search This Page</button>
<section>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nec turpis sed leo viverra auctor ut eget risus. Nullam consectetur orci eget semper suscipit. Cras suscipit porttitor semper. Curabitur semper, purus nec cursus tristique, mi augue maximus sem, et aliquet tellus sapien non est. Pellentesque ipsum velit, pretium et enim in, hendrerit tincidunt dui. In tortor sem, semper at luctus at, eleifend vel turpis. Morbi non justo non nisi accumsan pharetra eu non risus. Vivamus eleifend quam vitae ornare sagittis. Nam ac ante egestas, luctus magna nec, consectetur nunc. Duis suscipit id nibh eget faucibus. Nam at viverra massa. Donec sed sodales nunc. Vestibulum ac sapien velit. Sed magna mi, lacinia in enim ac, tincidunt feugiat lorem. In tincidunt accumsan leo, non aliquet ex lobortis vel.
</p>
</section>
<webview id="target-webview" src="webview-content.html" autosize="on"></webview>
<script src="renderer.js"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const join = require('path').join;
app.once('window-all-closed',function() { app.quit(); });
app.once('ready', function() {
let w = new BrowserWindow();
w.once('closed', function() { w = null; });
w.loadURL('file://' + join(__dirname, 'index.html'));
if (process.env.ELECTRON_IN_PAGE_SEARCH_DEBUG) {
w.webContents.openDevTools({mode: 'detach'});
}
});

View File

@@ -0,0 +1,17 @@
{
"name": "electron-in-page-search-webview-example",
"version": "0.0.0",
"private": true,
"description": "",
"main": "main.js",
"scripts": {
"start": "../../node_modules/.bin/electron .",
"debug": "ELECTRON_IN_PAGE_SEARCH_DEBUG=true ../../node_modules/.bin/electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "rhysd <lin90162@yahoo.co.jp>",
"license": "MIT",
"devDependencies": {},
"dependencies": {}
}

View File

@@ -0,0 +1,20 @@
const searchInPage = require('../..').default;
/*
* Create a search instance for the current page.
* Make sure that create the search instance per one WebContents instance.
*/
let search;
const webview = document.getElementById('target-webview');
webview.addEventListener('dom-ready', () => {
search = searchInPage(webview);
console.log(search);
});
document.getElementById('search-page-button').addEventListener('click', () => {
/*
* .openSearchWindow() method opens and activates a search window.
* User can input the query and start the word seatch in page.
*/
search.openSearchWindow();
});

View File

@@ -0,0 +1,35 @@
/*
* .electron-in-page-search-window is a class specified to default
* <webview> element for search window.
*/
.electron-in-page-search-window {
position: fixed;
top: 0;
right: 0;
border: solid grey 1px;
background-color: white;
width: 300px;
height: 36px;
}
/*
* .search-inactive is added to search window <webview> when the window
* is inactive.
*/
.search-inactive {
visibility: hidden;
}
/*
* .search-inactive is added to search window <webview> when the window
* is active.
*/
.search-active {
visibility: visible;
}
#target-webview {
width: 100%;
height: 350px;
border: solid black 1px;
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>In &lt;webview&gt;</h1>
<section>
<p>
Nam hendrerit erat nec posuere sodales. Duis id ligula id ante varius luctus nec nec dui. Nunc eu rutrum metus. Mauris purus ligula, tempus eget cursus nec, sagittis quis mi. Praesent sit amet urna id ante sodales eleifend. Curabitur tristique pretium maximus. Curabitur sit amet diam dictum, luctus sem a, vestibulum leo. In pellentesque ac lacus ut gravida. Fusce porttitor tincidunt velit, ac condimentum nunc efficitur ut. Mauris non fermentum enim. Suspendisse mauris magna, commodo eu odio ut, fermentum mollis lorem. Vivamus cursus consequat orci, at eleifend tortor tincidunt at.
</p><p>
Sed aliquet urna scelerisque dolor volutpat lobortis. Sed pharetra tempus tempor. Vivamus et libero non nibh sodales aliquet. Cras pulvinar nulla ac quam pharetra, id tincidunt justo malesuada. Nunc eu arcu varius, tincidunt nunc vitae, vehicula lorem. Fusce in posuere tellus. Cras ut quam venenatis, ullamcorper quam vel, laoreet felis. Donec cursus felis dui, a facilisis eros vestibulum at. Praesent commodo ligula ac ante pretium facilisis. Curabitur quis enim id massa vulputate egestas vitae et ex. Etiam gravida ultricies pretium. Sed sed est mauris.
</p><p>
Vestibulum tincidunt, orci nec vestibulum posuere, ligula sem dictum risus, vitae rhoncus ex elit quis dui. Praesent tincidunt, diam quis suscipit posuere, velit sem vestibulum ante, vitae congue libero dui eu arcu. Donec tincidunt ac sapien in accumsan. Nunc ut libero fringilla, tincidunt magna sed, vestibulum tellus. Cras congue fermentum orci, a consectetur leo placerat sed. Nam tortor nulla, lacinia vel suscipit non, sodales ac orci. Maecenas ornare turpis urna, vitae posuere metus laoreet eu. In sodales vitae nulla nec eleifend. Aenean ullamcorper rutrum ex vitae suscipit. Fusce sit amet feugiat metus, vel volutpat arcu.
</p><p>
Etiam dignissim, justo et fringilla fermentum, augue turpis interdum purus, in tincidunt libero eros at lorem. Curabitur eu est ullamcorper est porta congue quis vel dolor. Cras ut nisl sed velit sagittis malesuada. Etiam tempus venenatis lacus. Nulla facilisi. Donec tellus augue, laoreet eget pharetra vitae, vestibulum ut tortor. Aliquam finibus condimentum vehicula. Donec vitae justo eu turpis rhoncus porttitor. Donec non felis eros. Ut eleifend interdum diam quis faucibus. Vestibulum non suscipit augue. Nulla sit amet lectus nulla. Integer mattis fermentum augue.
</p><p>
Nulla felis est, euismod ac tortor sit amet, ornare rhoncus ipsum. Integer quis lobortis nulla. Morbi ultrices euismod ligula sit amet ultricies. Maecenas tincidunt lorem in neque porta, non viverra eros egestas. Integer ex quam, laoreet eget viverra eget, interdum eget neque. Nunc interdum efficitur quam, eu vehicula diam volutpat quis. Maecenas at nisi ultricies, dignissim dui eu, tincidunt justo.
</p><p>
Proin in laoreet odio, nec vehicula augue. Aenean vitae elit a diam congue rhoncus. Ut in tempus ante. Nunc ut urna sit amet dui iaculis viverra. Praesent euismod justo ac justo egestas, vitae maximus mauris efficitur. Pellentesque et enim ac risus vestibulum ultrices vel nec lectus. Nulla eu rutrum magna. In fringilla sed leo eget ullamcorper. Aliquam nisl nulla, fermentum vel dapibus a, vehicula a dui. Phasellus luctus pellentesque augue, in eleifend tellus sodales non. Curabitur sit amet interdum quam. Nullam ullamcorper mauris vitae massa semper malesuada. Nulla ac ullamcorper mauris. Vivamus ac mi vel arcu luctus lacinia id egestas erat. Donec id lorem rhoncus, efficitur nisi sed, sodales est.
</p><p>
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean sed est quis tellus convallis auctor. Praesent euismod elementum ligula, eu aliquet est rutrum in. In aliquet purus sed ipsum mattis tempus. Praesent cursus massa scelerisque orci molestie tincidunt. Integer pellentesque commodo nibh, sit amet semper est laoreet vel. Ut cursus lectus ac sem mattis laoreet. Nam interdum, velit vel tincidunt vehicula, metus elit fermentum neque, vitae aliquet risus justo quis libero. Suspendisse facilisis ligula eu viverra vehicula. Aliquam maximus a erat a scelerisque. Nulla vehicula, dolor ut faucibus congue, orci augue rutrum ante, non placerat felis dui luctus massa.
</p><p>
Nunc mollis pretium tortor. Nam eget dolor tempus, malesuada lorem non, aliquam ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla vitae viverra nulla, ut fermentum sem. In consectetur magna et ornare dictum. Aliquam ultrices lorem vel congue tempor. In iaculis ac dui aliquet laoreet. Proin non pulvinar elit. Pellentesque semper diam eu magna molestie, nec commodo eros pulvinar. Nulla faucibus nulla libero, sit amet auctor risus elementum eget.
</p><p>
Maecenas pulvinar, dolor nec sodales vulputate, nisi mauris egestas neque, a tempor neque nisl id nulla. Fusce ut arcu interdum, venenatis mauris sit amet, facilisis enim. Aliquam vulputate congue metus, non sollicitudin ante. Mauris quis egestas dolor, a lacinia tellus. Pellentesque scelerisque suscipit ligula id accumsan. Nulla sed libero hendrerit, sodales justo at, fringilla elit. In hac habitasse platea dictumst. Etiam hendrerit nisl et felis feugiat, in pellentesque erat sagittis. Duis dignissim velit accumsan fermentum maximus. Donec id eros lorem. Praesent quis ultricies diam, vitae interdum libero. Aenean justo mauris, ullamcorper sed tincidunt eu, tempus ut erat. Morbi commodo erat ut laoreet aliquam. Duis in tortor a urna pretium sodales ut a magna.
</p>
</section>
</body>
</html>

39
app/node_modules/electron-in-page-search/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
/// <reference types="electron" />
/// <reference types="node" />
import { EventEmitter } from 'events';
export interface InPageSearchOptions {
searchWindowWebview?: Electron.WebViewElement;
searchWindowParent?: HTMLElement;
preloadSearchWindow?: boolean;
customCssPath?: string;
customSearchWindowHtmlPath?: string;
openDevToolsOfSearchWindow?: boolean;
}
export declare type SearchTarget = Electron.WebContents | Electron.WebViewElement;
export default function searchInPage(searchTarget: SearchTarget, options?: InPageSearchOptions): InPageSearch;
export declare class InPageSearch extends EventEmitter {
searcher: Electron.WebViewElement;
searcherParent: HTMLElement;
searchTarget: SearchTarget;
opened: boolean;
private requestId;
private prevQuery;
private activeIdx;
private initialized;
constructor(searcher: Electron.WebViewElement, searcherParent: HTMLElement, searchTarget: SearchTarget, preload: boolean);
openSearchWindow(): void;
closeSearchWindow(): void;
isSearching(): boolean;
startToFind(query: string): void;
findNext(forward: boolean): void;
stopFind(): void;
finalize(): void;
private initialize();
private onSearchQuery(text);
private onFoundInPage(result);
private registerFoundCallback();
private setupSearchWindowWebview();
private focusOnInput();
private focusOnInputOnBrowserWindow();
private sendResult(nth, all);
}

71
app/node_modules/electron-in-page-search/package.json generated vendored Normal file
View File

@@ -0,0 +1,71 @@
{
"_args": [
[
"electron-in-page-search@1.3.2",
"/home/s2/Documents/Code/gitlit/app"
]
],
"_from": "electron-in-page-search@1.3.2",
"_id": "electron-in-page-search@1.3.2",
"_inBundle": false,
"_integrity": "sha512-gHZtUV3t5g3UXndAOUC+QM0ZzvB43mtKvba4zig65SsOnYqu1G5SMeRq41OIKnDftZN8iXD0NgTzuK3fWh4F8g==",
"_location": "/electron-in-page-search",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "electron-in-page-search@1.3.2",
"name": "electron-in-page-search",
"escapedName": "electron-in-page-search",
"rawSpec": "1.3.2",
"saveSpec": null,
"fetchSpec": "1.3.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/electron-in-page-search/-/electron-in-page-search-1.3.2.tgz",
"_spec": "1.3.2",
"_where": "/home/s2/Documents/Code/gitlit/app",
"author": {
"name": "rhysd",
"email": "lin90162@yahoo.co.jp"
},
"bugs": {
"url": "https://github.com/rhysd/electron-in-page-search/issues"
},
"description": "Electron module to provide in-page search feature",
"devDependencies": {
"@types/mocha": "^5.0.0",
"@types/sinon": "^4.3.0",
"electron": "^1.8.4",
"electron-mocha": "^6.0.1",
"sinon": "^4.4.9",
"tslint": "^5.9.1",
"typescript": "^2.8.1"
},
"homepage": "https://github.com/rhysd/electron-in-page-search#readme",
"keywords": [
"electron",
"module",
"in-page",
"search"
],
"license": "MIT",
"main": "src/index.js",
"name": "electron-in-page-search",
"repository": {
"type": "git",
"url": "git+https://github.com/rhysd/electron-in-page-search.git"
},
"scripts": {
"build": "tsc -p .",
"example": "npm install && npm run build && cd example/browser-window && npm start",
"gen-dts": "tsc -d src/index.ts && mv src/index.d.ts .",
"lint": "tslint -p .",
"preversion": "npm run lint && npm run build",
"test": "electron-mocha --timeout 10000 --renderer test/*.js",
"watch": "guard --watchdir src test"
},
"version": "1.3.2"
}

View File

@@ -0,0 +1,59 @@
html, body {
margin: 0;
width: 100%;
height: 100%;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Meiryo", sans-serif;
overflow: hidden;
}
.inpage-search-body {
display: flex;
flex-direction: row;
align-items: center;
margin: 8px;
}
.inpage-search-input {
flex: auto;
-webkit-appearance: textfield;
box-sizing: border-box;
margin-right: 6px;
outline: 0;
}
.inpage-search-input:focus {
border: solid #83bffc 1px;
}
.inpage-search-matches {
color: #999;
font-size: 0.8em;
}
.inpage-search-back {
margin-left: 2px;
padding-left: 6px;
padding-right: 2px;
cursor: pointer;
}
.inpage-search-forward {
padding-left: 2px;
padding-right: 6px;
cursor: pointer;
}
.inpage-search-close {
margin-left: 4px;
padding: 0 2px;
cursor: pointer;
}
.inpage-search-back:hover,
.inpage-search-forward:hover,
.inpage-search-close:hover {
background-color: #e2e0e2;
border-radius: 0.2em;
}

326
app/node_modules/electron-in-page-search/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,326 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
var events_1 = require("events");
var path = require("path");
var DefaultSearchWindowHtml = "file://" + path.join(__dirname, 'search-window.html');
var ShouldDebug = !!process.env.ELECTRON_IN_PAGE_SEARCH_DEBUG;
var log = ShouldDebug
? console.log.bind(console)
: function nop() {
/* nop */
};
var InPageSearch = /** @class */ (function (_super) {
__extends(InPageSearch, _super);
function InPageSearch(searcher, searcherParent, searchTarget, preload) {
var _this = _super.call(this) || this;
_this.searcher = searcher;
_this.searcherParent = searcherParent;
_this.searchTarget = searchTarget;
_this.opened = false;
_this.requestId = null;
_this.prevQuery = '';
_this.activeIdx = 0;
_this.maxIdx = 0;
_this.initialized = false;
if (preload) {
_this.initialize();
}
return _this;
}
InPageSearch.prototype.openSearchWindow = function () {
if (this.opened) {
log('Already opened');
return;
}
this.initialize();
this.searcher.classList.remove('search-inactive');
this.searcher.classList.remove('search-firstpaint');
this.searcher.classList.add('search-active');
this.opened = true;
this.emit('open');
this.focusOnInput();
};
InPageSearch.prototype.closeSearchWindow = function () {
if (!this.opened) {
log('Already closed');
return;
}
this.stopFind();
this.searcher.send('electron-in-page-search:close');
this.searcher.classList.remove('search-active');
this.searcher.classList.add('search-inactive');
this.emit('stop');
this.requestId = null;
this.prevQuery = '';
this.opened = false;
};
InPageSearch.prototype.isSearching = function () {
return this.requestId !== null;
};
InPageSearch.prototype.startToFind = function (query) {
this.requestId = this.searchTarget.findInPage(query);
this.activeIdx = 0;
this.maxIdx = 0;
this.prevQuery = query;
this.emit('start', query);
this.focusOnInputOnBrowserWindow();
};
InPageSearch.prototype.findNext = function (forward) {
if (!this.isSearching()) {
throw new Error('Search did not start yet. Use .startToFind() method to start the search');
}
this.requestId = this.searchTarget.findInPage(this.prevQuery, {
forward: forward,
findNext: true
});
this.emit('next', this.prevQuery, forward);
this.focusOnInputOnBrowserWindow();
};
InPageSearch.prototype.stopFind = function () {
this.searchTarget.stopFindInPage('clearSelection');
};
// You need to call this method when destroying InPageSearch instance.
// Or the <webview> element will ramain in DOM and leaks memory.
InPageSearch.prototype.finalize = function () {
this.searcherParent.removeChild(this.searcher);
};
InPageSearch.prototype.initialize = function () {
if (this.initialized) {
return;
}
this.registerFoundCallback();
this.setupSearchWindowWebview();
this.initialized = true;
};
InPageSearch.prototype.onSearchQuery = function (text) {
log('Query from search window webview:', text);
if (text === '') {
this.closeSearchWindow();
return;
}
if (!this.isSearching() || this.prevQuery !== text) {
this.startToFind(text);
}
else {
this.findNext(true);
}
};
InPageSearch.prototype.onFoundInPage = function (result) {
log('Found:', result);
if (this.requestId !== result.requestId) {
return;
}
if (typeof result.activeMatchOrdinal === 'number') {
this.activeIdx = result.activeMatchOrdinal;
}
if (typeof result.matches === 'number') {
this.maxIdx = result.matches;
}
if (result.finalUpdate) {
this.sendResult();
}
};
InPageSearch.prototype.registerFoundCallback = function () {
var _this = this;
if (isWebView(this.searchTarget)) {
this.searchTarget.addEventListener('found-in-page', function (event) {
_this.onFoundInPage(event.result);
});
}
else {
// When target is WebContents
this.searchTarget.on('found-in-page', function (_, result) {
_this.onFoundInPage(result);
});
}
};
InPageSearch.prototype.setupSearchWindowWebview = function () {
var _this = this;
this.searcher.classList.add('search-inactive');
this.searcher.classList.add('search-firstpaint');
if (this.searcher.parentElement === null) {
this.searcherParent.appendChild(this.searcher);
}
this.searcher.addEventListener('ipc-message', function (event) {
switch (event.channel) {
case 'electron-in-page-search:query': {
var text = event.args[0];
_this.onSearchQuery(text);
break;
}
case 'electron-in-page-search:close': {
_this.closeSearchWindow();
break;
}
case 'electron-in-page-search:back': {
var text = event.args[0];
if (_this.isSearching() && text === _this.prevQuery) {
_this.findNext(false);
}
else {
if (text) {
_this.onSearchQuery(text);
}
}
break;
}
case 'electron-in-page-search:forward': {
var text = event.args[0];
if (_this.isSearching() && text === _this.prevQuery) {
_this.findNext(true);
}
else {
if (text) {
_this.onSearchQuery(text);
}
}
break;
}
default:
break;
}
});
if (ShouldDebug) {
this.searcher.addEventListener('console-message', function (e) {
log('Console message from search window:', "line:" + e.line + ": " + e.message, e.sourceId);
});
}
};
InPageSearch.prototype.focusOnInput = function () {
var _this = this;
log('Set focus on search window');
// XXX:
// Directly calling .focus() doesn't focus on <webview> here.
// We need to delay the call in order to fix it.
setImmediate(function () {
_this.searcher.focus();
_this.searcher.send('electron-in-page-search:focus');
_this.emit('focus-input');
});
};
// XXX:
// Search API's behavior is different depending on a target.
//
// When the search target is BrowserWindow, focus to <webview> will be
// cleared after calling .findInPage(). So we need to focus on <webview>
// after that. Below method does it.
//
// When the search target is <webview>, focus to <webview> (for search window)
// won't be cleared. So we need to focus on search window <webview> again after
// calling .findInPage(). Futhermore, we should not focus on it because of
// <webview> bug. calling .focus() on search window <webview> also gives a focus
// to another <webview>. As the result, search window <webview> can't have a focus.
//
// https://github.com/electron/electron/issues/7939
//
// At opening search window webview, it needs to give a focus to the webview
// anyway in order to set first focus to <input> in it.
InPageSearch.prototype.focusOnInputOnBrowserWindow = function () {
if (isWebView(this.searchTarget)) {
return;
}
if (this.maxIdx !== 0 && this.activeIdx === this.maxIdx) {
// XXX:
// Add 100ms delay before putting focus when scrolling up for search wrap (#8).
// When scrolling up, clearing webview focus is delayed and calling this.focusOnInput()
// directly focuses on input before removing focus from <input>.
setTimeout(this.focusOnInput.bind(this), 100);
return;
}
this.focusOnInput();
};
InPageSearch.prototype.sendResult = function () {
var nth = this.activeIdx;
var all = this.maxIdx;
log('Send result:', nth, all);
this.searcher.send('electron-in-page-search:result', nth, all);
this.emit('found', this.prevQuery, nth, all);
};
return InPageSearch;
}(events_1.EventEmitter));
exports.InPageSearch = InPageSearch;
function isWebView(target) {
return target.tagName !== undefined && target.tagName === 'WEBVIEW';
}
function fixPathSlashes(p) {
if (process.platform !== 'win32') {
return p;
}
// Note:
// On Windows, path separator is not '/' but browser seems to understand
// '/' separator only. So we need to convert separator manually.
//
// e.g.
// C:\Users\foo\bar\piyo.html -> /C:/Users/foo/bar/piyo.html
//
// c.f.
// https://github.com/electron/electron/issues/1298
var replaced = p.replace(/\\/g, '/');
if (replaced[0] !== '/') {
replaced = '/' + replaced;
}
return replaced;
}
function injectScriptToWebView(target, opts) {
var injected_script = fixPathSlashes(path.join(__dirname, 'search-window.js'));
var css = fixPathSlashes(opts.customCssPath || path.join(__dirname, 'default-style.css'));
var script = "(function(){\n const l = document.createElement('link');\n l.rel = 'stylesheet';\n l.href = '" + css + "';\n document.head.appendChild(l);\n const s = document.createElement('script');\n s.src = 'file://" + injected_script + "';\n document.body.appendChild(s);\n })()";
// XXX:
// Before <webview> completes to load its web contents, .getWebContents()
// (and some other APIs) have some 'statuses'.
//
// 1. .getWebContents property does not exist
// 2. .getWebContents property exsit but .getWebContents() returns undefined
//
// So we need to check both 1. and 2. Note that <webview> instance doesn't
// have the method to check whether it's dom-ready or not such as .isReady()
// of app instance.
if (target.getWebContents && target.getWebContents()) {
target.executeJavaScript(script, false);
}
else {
target.addEventListener('dom-ready', function () {
target.executeJavaScript(script, false);
});
}
}
function searchInPage(searchTarget, options) {
options = options || {};
if (!options.searchWindowWebview) {
options.searchWindowWebview = document.createElement('webview');
options.searchWindowWebview.className = 'electron-in-page-search-window';
options.searchWindowWebview.setAttribute('nodeintegration', '');
options.searchWindowWebview.style.outline = '0';
}
var wv = options.searchWindowWebview;
if (!wv.src) {
wv.src = options.customSearchWindowHtmlPath || DefaultSearchWindowHtml;
}
injectScriptToWebView(wv, options);
if (options.openDevToolsOfSearchWindow) {
// XXX:
// Please check the comment in injectScriptToWebView() function to know
// why .getWebContents property is checked here.
var wc = wv.getWebContents && wv.getWebContents();
if (wc) {
wc.openDevTools({ mode: 'detach' });
}
else {
wv.addEventListener('dom-ready', function () {
wv.getWebContents().openDevTools({ mode: 'detach' });
});
}
}
return new InPageSearch(options.searchWindowWebview, options.searchWindowParent || document.body, searchTarget, !!options.preloadSearchWindow);
}
exports["default"] = searchInPage;

File diff suppressed because one or more lines are too long

366
app/node_modules/electron-in-page-search/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,366 @@
import { EventEmitter } from 'events';
import * as path from 'path';
const DefaultSearchWindowHtml = `file://${path.join(__dirname, 'search-window.html')}`;
const ShouldDebug = !!process.env.ELECTRON_IN_PAGE_SEARCH_DEBUG;
const log = ShouldDebug
? console.log.bind(console)
: function nop() {
/* nop */
};
export interface InPageSearchOptions {
searchWindowWebview?: Electron.WebviewTag;
searchWindowParent?: HTMLElement;
preloadSearchWindow?: boolean;
customCssPath?: string;
customSearchWindowHtmlPath?: string;
openDevToolsOfSearchWindow?: boolean;
}
type RequestId = number;
export type SearchTarget = Electron.WebContents | Electron.WebviewTag;
export class InPageSearch extends EventEmitter {
public opened = false;
private requestId: RequestId | null = null;
private prevQuery = '';
private activeIdx = 0;
private maxIdx = 0;
private initialized = false;
constructor(
public searcher: Electron.WebviewTag,
public searcherParent: HTMLElement,
public searchTarget: SearchTarget,
preload: boolean,
) {
super();
if (preload) {
this.initialize();
}
}
openSearchWindow() {
if (this.opened) {
log('Already opened');
return;
}
this.initialize();
this.searcher.classList.remove('search-inactive');
this.searcher.classList.remove('search-firstpaint');
this.searcher.classList.add('search-active');
this.opened = true;
this.emit('open');
this.focusOnInput();
}
closeSearchWindow() {
if (!this.opened) {
log('Already closed');
return;
}
this.stopFind();
this.searcher.send('electron-in-page-search:close');
this.searcher.classList.remove('search-active');
this.searcher.classList.add('search-inactive');
this.emit('stop');
this.requestId = null;
this.prevQuery = '';
this.opened = false;
}
isSearching() {
return this.requestId !== null;
}
startToFind(query: string) {
this.requestId = this.searchTarget.findInPage(query);
this.activeIdx = 0;
this.maxIdx = 0;
this.prevQuery = query;
this.emit('start', query);
this.focusOnInputOnBrowserWindow();
}
findNext(forward: boolean) {
if (!this.isSearching()) {
throw new Error('Search did not start yet. Use .startToFind() method to start the search');
}
this.requestId = this.searchTarget.findInPage(this.prevQuery, {
forward,
findNext: true,
});
this.emit('next', this.prevQuery, forward);
this.focusOnInputOnBrowserWindow();
}
stopFind() {
this.searchTarget.stopFindInPage('clearSelection');
}
// You need to call this method when destroying InPageSearch instance.
// Or the <webview> element will ramain in DOM and leaks memory.
finalize() {
this.searcherParent.removeChild(this.searcher);
}
private initialize() {
if (this.initialized) {
return;
}
this.registerFoundCallback();
this.setupSearchWindowWebview();
this.initialized = true;
}
private onSearchQuery(text: string) {
log('Query from search window webview:', text);
if (text === '') {
this.closeSearchWindow();
return;
}
if (!this.isSearching() || this.prevQuery !== text) {
this.startToFind(text);
} else {
this.findNext(true);
}
}
private onFoundInPage(result: Electron.FoundInPageResult) {
log('Found:', result);
if (this.requestId !== result.requestId) {
return;
}
if (typeof result.activeMatchOrdinal === 'number') {
this.activeIdx = result.activeMatchOrdinal;
}
if (typeof result.matches === 'number') {
this.maxIdx = result.matches;
}
if (result.finalUpdate) {
this.sendResult();
}
}
private registerFoundCallback() {
if (isWebView(this.searchTarget)) {
this.searchTarget.addEventListener('found-in-page', event => {
this.onFoundInPage(event.result);
});
} else {
// When target is WebContents
this.searchTarget.on('found-in-page', (_, result) => {
this.onFoundInPage(result);
});
}
}
private setupSearchWindowWebview() {
this.searcher.classList.add('search-inactive');
this.searcher.classList.add('search-firstpaint');
if (this.searcher.parentElement === null) {
this.searcherParent.appendChild(this.searcher);
}
this.searcher.addEventListener('ipc-message', event => {
switch (event.channel) {
case 'electron-in-page-search:query': {
const text = event.args[0] as string;
this.onSearchQuery(text);
break;
}
case 'electron-in-page-search:close': {
this.closeSearchWindow();
break;
}
case 'electron-in-page-search:back': {
const text = event.args[0] as string;
if (this.isSearching() && text === this.prevQuery) {
this.findNext(false);
} else {
if (text) {
this.onSearchQuery(text);
}
}
break;
}
case 'electron-in-page-search:forward': {
const text = event.args[0] as string;
if (this.isSearching() && text === this.prevQuery) {
this.findNext(true);
} else {
if (text) {
this.onSearchQuery(text);
}
}
break;
}
default:
break;
}
});
if (ShouldDebug) {
this.searcher.addEventListener('console-message', e => {
log('Console message from search window:', `line:${e.line}: ${e.message}`, e.sourceId);
});
}
}
private focusOnInput() {
log('Set focus on search window');
// XXX:
// Directly calling .focus() doesn't focus on <webview> here.
// We need to delay the call in order to fix it.
setImmediate(() => {
this.searcher.focus();
this.searcher.send('electron-in-page-search:focus');
this.emit('focus-input');
});
}
// XXX:
// Search API's behavior is different depending on a target.
//
// When the search target is BrowserWindow, focus to <webview> will be
// cleared after calling .findInPage(). So we need to focus on <webview>
// after that. Below method does it.
//
// When the search target is <webview>, focus to <webview> (for search window)
// won't be cleared. So we need to focus on search window <webview> again after
// calling .findInPage(). Futhermore, we should not focus on it because of
// <webview> bug. calling .focus() on search window <webview> also gives a focus
// to another <webview>. As the result, search window <webview> can't have a focus.
//
// https://github.com/electron/electron/issues/7939
//
// At opening search window webview, it needs to give a focus to the webview
// anyway in order to set first focus to <input> in it.
private focusOnInputOnBrowserWindow() {
if (isWebView(this.searchTarget)) {
return;
}
if (this.maxIdx !== 0 && this.activeIdx === this.maxIdx) {
// XXX:
// Add 100ms delay before putting focus when scrolling up for search wrap (#8).
// When scrolling up, clearing webview focus is delayed and calling this.focusOnInput()
// directly focuses on input before removing focus from <input>.
setTimeout(this.focusOnInput.bind(this), 100);
return;
}
this.focusOnInput();
}
private sendResult() {
const nth = this.activeIdx;
const all = this.maxIdx;
log('Send result:', nth, all);
this.searcher.send('electron-in-page-search:result', nth, all);
this.emit('found', this.prevQuery, nth, all);
}
}
function isWebView(target: any): target is Electron.WebviewTag {
return target.tagName !== undefined && target.tagName === 'WEBVIEW';
}
function fixPathSlashes(p: string) {
if (process.platform !== 'win32') {
return p;
}
// Note:
// On Windows, path separator is not '/' but browser seems to understand
// '/' separator only. So we need to convert separator manually.
//
// e.g.
// C:\Users\foo\bar\piyo.html -> /C:/Users/foo/bar/piyo.html
//
// c.f.
// https://github.com/electron/electron/issues/1298
let replaced = p.replace(/\\/g, '/');
if (replaced[0] !== '/') {
replaced = '/' + replaced;
}
return replaced;
}
function injectScriptToWebView(target: Electron.WebviewTag, opts: InPageSearchOptions) {
const injected_script = fixPathSlashes(path.join(__dirname, 'search-window.js'));
const css = fixPathSlashes(opts.customCssPath || path.join(__dirname, 'default-style.css'));
const script = `(function(){
const l = document.createElement('link');
l.rel = 'stylesheet';
l.href = '${css}';
document.head.appendChild(l);
const s = document.createElement('script');
s.src = 'file://${injected_script}';
document.body.appendChild(s);
})()`;
// XXX:
// Before <webview> completes to load its web contents, .getWebContents()
// (and some other APIs) have some 'statuses'.
//
// 1. .getWebContents property does not exist
// 2. .getWebContents property exsit but .getWebContents() returns undefined
//
// So we need to check both 1. and 2. Note that <webview> instance doesn't
// have the method to check whether it's dom-ready or not such as .isReady()
// of app instance.
if (target.getWebContents && target.getWebContents()) {
target.executeJavaScript(script, false);
} else {
target.addEventListener('dom-ready', () => {
target.executeJavaScript(script, false);
});
}
}
export default function searchInPage(searchTarget: SearchTarget, options?: InPageSearchOptions) {
options = options || {};
if (!options.searchWindowWebview) {
options.searchWindowWebview = document.createElement('webview');
options.searchWindowWebview.className = 'electron-in-page-search-window';
options.searchWindowWebview.setAttribute('nodeintegration', '');
options.searchWindowWebview.style.outline = '0';
}
const wv = options.searchWindowWebview;
if (!wv.src) {
wv.src = options.customSearchWindowHtmlPath || DefaultSearchWindowHtml;
}
injectScriptToWebView(wv, options);
if (options.openDevToolsOfSearchWindow) {
// XXX:
// Please check the comment in injectScriptToWebView() function to know
// why .getWebContents property is checked here.
const wc = wv.getWebContents && wv.getWebContents();
if (wc) {
wc.openDevTools({ mode: 'detach' });
} else {
wv.addEventListener('dom-ready', () => {
wv.getWebContents().openDevTools({ mode: 'detach' });
});
}
}
return new InPageSearch(
options.searchWindowWebview,
options.searchWindowParent || document.body,
searchTarget,
!!options.preloadSearchWindow,
);
}

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes" />
</head>
<body>
<div class="inpage-search-body">
<input class="inpage-search-input" type="search" label="Search..." autocomplete="off" autofocus></input>
<div class="inpage-search-matches">0/0</div>
<div class="inpage-search-back">&lt;</div>
<div class="inpage-search-forward">&gt;</div>
<div class="inpage-search-close"></div>
</div>
</body>
<script>var exports = {}</script>
</html>

View File

@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron");
const search_button = document.querySelector('.inpage-search-button');
const matches = document.querySelector('.inpage-search-matches');
const back_button = document.querySelector('.inpage-search-back');
const forward_button = document.querySelector('.inpage-search-forward');
const close_button = document.querySelector('.inpage-search-close');
const search_input = document.querySelector('.inpage-search-input');
let in_composition = false;
if (search_button !== null) {
search_button.addEventListener('click', () => {
const input = search_input.value;
if (input === '') {
return;
}
electron_1.ipcRenderer.sendToHost('electron-in-page-search:query', input);
});
}
if (back_button !== null) {
back_button.addEventListener('click', () => {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:back', search_input.value);
});
}
if (forward_button !== null) {
forward_button.addEventListener('click', () => {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:forward', search_input.value);
});
}
close_button.addEventListener('click', () => {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:close');
});
search_input.addEventListener('keydown', e => {
if (in_composition) {
return;
}
switch (e.code) {
case 'Enter':
case 'NumpadEnter':
if (e.shiftKey) {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:back', search_input.value);
}
else {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:query', search_input.value);
}
break;
case 'Escape':
electron_1.ipcRenderer.sendToHost('electron-in-page-search:close');
break;
case 'KeyG':
if (e.ctrlKey) {
electron_1.ipcRenderer.sendToHost('electron-in-page-search:close');
}
break;
default:
return;
}
console.log('Keydown:', e);
});
search_input.addEventListener('compositionstart', () => {
in_composition = true;
});
search_input.addEventListener('compositionend', () => {
in_composition = false;
});
electron_1.ipcRenderer.on('electron-in-page-search:focus', () => {
console.log('Focus on input');
search_input.focus();
});
electron_1.ipcRenderer.on('electron-in-page-search:result', (_, nth, all) => {
matches.innerText = `${nth}/${all}`;
search_input.classList.toggle('inpage-search-input-noresults', all === 0);
});
electron_1.ipcRenderer.on('electron-in-page-search:close', () => {
search_input.value = '';
matches.innerText = '0/0';
});
//# sourceMappingURL=search-window.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"search-window.js","sourceRoot":"","sources":["search-window.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,86 @@
import { ipcRenderer as ipc } from 'electron';
const search_button = document.querySelector('.inpage-search-button') as HTMLButtonElement;
const matches = document.querySelector('.inpage-search-matches') as HTMLDivElement;
const back_button = document.querySelector('.inpage-search-back') as HTMLButtonElement;
const forward_button = document.querySelector('.inpage-search-forward') as HTMLButtonElement;
const close_button = document.querySelector('.inpage-search-close') as HTMLButtonElement;
const search_input = document.querySelector('.inpage-search-input') as HTMLInputElement;
let in_composition = false;
if (search_button !== null) {
search_button.addEventListener('click', () => {
const input = search_input.value;
if (input === '') {
return;
}
ipc.sendToHost('electron-in-page-search:query', input);
});
}
if (back_button !== null) {
back_button.addEventListener('click', () => {
ipc.sendToHost('electron-in-page-search:back', search_input.value);
});
}
if (forward_button !== null) {
forward_button.addEventListener('click', () => {
ipc.sendToHost('electron-in-page-search:forward', search_input.value);
});
}
close_button.addEventListener('click', () => {
ipc.sendToHost('electron-in-page-search:close');
});
search_input.addEventListener('keydown', e => {
if (in_composition) {
return;
}
switch (e.code) {
case 'Enter':
case 'NumpadEnter':
if (e.shiftKey) {
ipc.sendToHost('electron-in-page-search:back', search_input.value);
} else {
ipc.sendToHost('electron-in-page-search:query', search_input.value);
}
break;
case 'Escape':
ipc.sendToHost('electron-in-page-search:close');
break;
case 'KeyG':
if (e.ctrlKey) {
ipc.sendToHost('electron-in-page-search:close');
}
break;
default:
return;
}
console.log('Keydown:', e);
});
search_input.addEventListener('compositionstart', () => {
in_composition = true;
});
search_input.addEventListener('compositionend', () => {
in_composition = false;
});
ipc.on('electron-in-page-search:focus', () => {
console.log('Focus on input');
search_input.focus();
});
ipc.on('electron-in-page-search:result', (_: any, nth: number, all: number) => {
matches.innerText = `${nth}/${all}`;
search_input.classList.toggle('inpage-search-input-noresults', all === 0);
});
ipc.on('electron-in-page-search:close', () => {
search_input.value = '';
matches.innerText = '0/0';
});

View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../src/index");
const A = require("assert");
const electron_1 = require("electron");
const sinon_1 = require("sinon");
function waitForReady(w) {
return new Promise(resolve => {
const c = w.getWebContents && w.getWebContents();
if (c) {
resolve(w);
return;
}
w.addEventListener('dom-ready', resolve);
});
}
function pause1000ms() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
context('For browser window', function () {
before(function () {
document.body.innerHTML = '<div>foo bar baz foo bar piyo poyo</div>';
});
describe('searchInPage()', function () {
it('creates search instance which enables in-page search', function () {
const s = index_1.default(electron_1.remote.getCurrentWebContents());
A.ok(s);
A.ok(!s.opened);
A.equal(document.querySelector('webview'), null);
const opened = sinon_1.spy();
s.on('open', opened);
s.openSearchWindow();
A.ok(opened.called);
A.ok(s.opened);
const w = document.querySelector('webview');
A.equal(w.className, 'electron-in-page-search-window search-active');
const started = sinon_1.spy();
s.on('start', started);
const stopped = sinon_1.spy();
s.on('stop', stopped);
const next = sinon_1.spy();
return waitForReady(w)
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
document.querySelector('.inpage-search-forward').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(started.called);
A.equal(started.args[0][0], 'foo');
s.on('next', next);
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-forward').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(next.called);
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-close').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.ok(stopped.called);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('webview'), null);
});
});
it('can search words multiple times', function () {
const s = index_1.default(electron_1.remote.getCurrentWebContents());
s.openSearchWindow();
const w = document.querySelector('webview');
const next = sinon_1.spy();
const start = sinon_1.spy();
s.on('next', next);
s.on('start', start);
return waitForReady(w)
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'ba';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.equal(start.args[0][0], 'foo');
A.equal(start.args[1][0], 'ba');
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
A.equal(next.args[1][0], 'ba');
A.ok(next.args[1][1]);
})
.then(() => {
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-close').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('webview'), null);
});
});
});
});
//# sourceMappingURL=smoke_browser_window_test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"smoke_browser_window_test.js","sourceRoot":"","sources":["smoke_browser_window_test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,162 @@
import searchInPage from '../src/index';
import * as A from 'assert';
import { remote } from 'electron';
import { spy } from 'sinon';
function waitForReady(w: Electron.WebviewTag) {
return new Promise(resolve => {
const c = w.getWebContents && w.getWebContents();
if (c) {
resolve(w);
return;
}
w.addEventListener('dom-ready', resolve);
});
}
function pause1000ms() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
context('For browser window', function() {
before(function() {
document.body.innerHTML = '<div>foo bar baz foo bar piyo poyo</div>';
});
describe('searchInPage()', function() {
it('creates search instance which enables in-page search', function() {
const s = searchInPage(remote.getCurrentWebContents());
A.ok(s);
A.ok(!s.opened);
A.equal(document.querySelector('webview'), null);
const opened = spy();
s.on('open', opened);
s.openSearchWindow();
A.ok(opened.called);
A.ok(s.opened);
const w = document.querySelector('webview') as Electron.WebviewTag;
A.equal(w.className, 'electron-in-page-search-window search-active');
const started = spy();
s.on('start', started);
const stopped = spy();
s.on('stop', stopped);
const next = spy();
return waitForReady(w)
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
document.querySelector('.inpage-search-forward').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(started.called);
A.equal(started.args[0][0], 'foo');
s.on('next', next);
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-forward').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(next.called);
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-close').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.ok(stopped.called);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('webview'), null);
});
});
it('can search words multiple times', function() {
const s = searchInPage(remote.getCurrentWebContents());
s.openSearchWindow();
const w = document.querySelector('webview') as Electron.WebviewTag;
const next = spy();
const start = spy();
s.on('next', next);
s.on('start', start);
return waitForReady(w)
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'ba';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.equal(start.args[0][0], 'foo');
A.equal(start.args[1][0], 'ba');
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
A.equal(next.args[1][0], 'ba');
A.ok(next.args[1][1]);
})
.then(() => {
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-close').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('webview'), null);
});
});
});
});

View File

@@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../src/index");
const A = require("assert");
const electron_1 = require("electron");
const sinon_1 = require("sinon");
function waitForReady(w) {
return new Promise(resolve => {
const c = w.getWebContents && w.getWebContents();
if (c) {
resolve(w);
return;
}
w.addEventListener('dom-ready', resolve);
});
}
function pause1000ms() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
context('For <webview>', function () {
let wv;
before(function (done) {
document.body.innerHTML = '';
wv = document.createElement('webview');
wv.src = 'https://example.com';
document.body.appendChild(wv);
wv.addEventListener('dom-ready', () => {
wv.executeJavaScript(`document.body.innerText = 'foo bar baz foo bar piyo poyo'`, false);
done();
});
});
describe('searchInPage()', function () {
it('creates search instance which enables in-page search', function () {
const s = index_1.default(wv);
A.ok(s);
A.ok(!s.opened);
const opened = sinon_1.spy();
s.on('open', opened);
s.openSearchWindow();
A.ok(opened.called);
A.ok(s.opened);
const w = document.querySelector('.electron-in-page-search-window');
A.equal(w.className, 'electron-in-page-search-window search-active');
const started = sinon_1.spy();
s.on('start', started);
const stopped = sinon_1.spy();
s.on('stop', stopped);
const next = sinon_1.spy();
return waitForReady(w)
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
document.querySelector('.inpage-search-forward').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(started.called);
A.equal(started.args[0][0], 'foo');
s.on('next', next);
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-forward').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(next.called);
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-close').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.ok(stopped.called);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
});
});
it('can search words multiple times', function () {
const s = index_1.default(wv);
s.openSearchWindow();
const w = document.querySelector('.electron-in-page-search-window');
const next = sinon_1.spy();
const start = sinon_1.spy();
s.on('next', next);
s.on('start', start);
return waitForReady(w)
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
electron_1.remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-input').value = 'ba';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.equal(start.args[0][0], 'foo');
A.equal(start.args[1][0], 'ba');
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
A.equal(next.args[1][0], 'ba');
A.ok(next.args[1][1]);
})
.then(() => {
w.executeJavaScript(`(function() {
document.querySelector('.inpage-search-close').click();
})()`, false);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('.electron-in-page-search-window'), null);
});
});
});
});
//# sourceMappingURL=smoke_webview_test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"smoke_webview_test.js","sourceRoot":"","sources":["smoke_webview_test.ts"],"names":[],"mappings":";;AAAA,wCAAwC;AACxC,4BAA4B;AAC5B,uCAAkC;AAClC,iCAA4B;AAE5B,sBAAsB,CAAsB;IACxC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACzB,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;QACjD,IAAI,CAAC,EAAE;YACH,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,OAAO;SACV;QACD,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC;AAED;IACI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACzB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACP,CAAC;AAED,OAAO,CAAC,eAAe,EAAE;IACrB,IAAI,EAAuB,CAAC;IAE5B,MAAM,CAAC,UAAS,IAAI;QAChB,QAAQ,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QAC7B,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QACvC,EAAE,CAAC,GAAG,GAAG,qBAAqB,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9B,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE;YAClC,EAAE,CAAC,iBAAiB,CAAC,2DAA2D,EAAE,KAAK,CAAC,CAAC;YACzF,IAAI,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE;QACvB,EAAE,CAAC,sDAAsD,EAAE;YACvD,MAAM,CAAC,GAAG,eAAY,CAAC,EAAE,CAAC,CAAC;YAC3B,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACR,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAEhB,MAAM,MAAM,GAAG,WAAG,EAAE,CAAC;YACrB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAErB,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACrB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAEf,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,iCAAiC,CAAwB,CAAC;YAC3F,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,8CAA8C,CAAC,CAAC;YAErE,MAAM,OAAO,GAAG,WAAG,EAAE,CAAC;YACtB,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAEvB,MAAM,OAAO,GAAG,WAAG,EAAE,CAAC;YACtB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAEtB,MAAM,IAAI,GAAG,WAAG,EAAE,CAAC;YACnB,OAAO,YAAY,CAAC,CAAC,CAAC;iBACjB,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,iBAAM,CAAC,gBAAgB,EAAE,CAAC,cAAc,EAAE,CAAC;gBAC3C,CAAC,CAAC,iBAAiB,CACf;;;6BAGK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACrB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAEnC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAEnB,CAAC,CAAC,iBAAiB,CACf;;6BAEK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAChC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtB,CAAC,CAAC,iBAAiB,CACf;;6BAEK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBAChB,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACrB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,gDAAgD,CAAC,CAAC;gBACvE,CAAC,CAAC,QAAQ,EAAE,CAAC;YACjB,CAAC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE;YAClC,MAAM,CAAC,GAAG,eAAY,CAAC,EAAE,CAAC,CAAC;YAC3B,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,iCAAiC,CAAwB,CAAC;YAC3F,MAAM,IAAI,GAAG,WAAG,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,WAAG,EAAE,CAAC;YACpB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACnB,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACrB,OAAO,YAAY,CAAC,CAAC,CAAC;iBACjB,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,iBAAM,CAAC,gBAAgB,EAAE,CAAC,cAAc,EAAE,CAAC;gBAC3C,CAAC,CAAC,iBAAiB,CACf;;;;;6BAKK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,iBAAM,CAAC,gBAAgB,EAAE,CAAC,cAAc,EAAE,CAAC;gBAC3C,CAAC,CAAC,iBAAiB,CACf;;;;;6BAKK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACjC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;gBAChC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAChC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC/B,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,CAAC,CAAC;iBACD,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,iBAAiB,CACf;;6BAEK,EACL,KAAK,CACR,CAAC;YACN,CAAC,CAAC;iBACD,IAAI,CAAC,WAAW,CAAC;iBACjB,IAAI,CAAC,GAAG,EAAE;gBACP,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBAChB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,gDAAgD,CAAC,CAAC;gBACvE,CAAC,CAAC,QAAQ,EAAE,CAAC;gBACb,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,iCAAiC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC7E,CAAC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1,168 @@
import searchInPage from '../src/index';
import * as A from 'assert';
import { remote } from 'electron';
import { spy } from 'sinon';
function waitForReady(w: Electron.WebviewTag) {
return new Promise(resolve => {
const c = w.getWebContents && w.getWebContents();
if (c) {
resolve(w);
return;
}
w.addEventListener('dom-ready', resolve);
});
}
function pause1000ms() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
context('For <webview>', function() {
let wv: Electron.WebviewTag;
before(function(done) {
document.body.innerHTML = '';
wv = document.createElement('webview');
wv.src = 'https://example.com';
document.body.appendChild(wv);
wv.addEventListener('dom-ready', () => {
wv.executeJavaScript(`document.body.innerText = 'foo bar baz foo bar piyo poyo'`, false);
done();
});
});
describe('searchInPage()', function() {
it('creates search instance which enables in-page search', function() {
const s = searchInPage(wv);
A.ok(s);
A.ok(!s.opened);
const opened = spy();
s.on('open', opened);
s.openSearchWindow();
A.ok(opened.called);
A.ok(s.opened);
const w = document.querySelector('.electron-in-page-search-window') as Electron.WebviewTag;
A.equal(w.className, 'electron-in-page-search-window search-active');
const started = spy();
s.on('start', started);
const stopped = spy();
s.on('stop', stopped);
const next = spy();
return waitForReady(w)
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
document.querySelector('.inpage-search-forward').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(started.called);
A.equal(started.args[0][0], 'foo');
s.on('next', next);
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-forward').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(next.called);
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-close').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.ok(stopped.called);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
});
});
it('can search words multiple times', function() {
const s = searchInPage(wv);
s.openSearchWindow();
const w = document.querySelector('.electron-in-page-search-window') as Electron.WebviewTag;
const next = spy();
const start = spy();
s.on('next', next);
s.on('start', start);
return waitForReady(w)
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'foo';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
remote.getCurrentWindow().focusOnWebView();
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-input').value = 'ba';
const b = document.querySelector('.inpage-search-forward');
b.click();
b.click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.equal(start.args[0][0], 'foo');
A.equal(start.args[1][0], 'ba');
A.equal(next.args[0][0], 'foo');
A.ok(next.args[0][1]);
A.equal(next.args[1][0], 'ba');
A.ok(next.args[1][1]);
})
.then(() => {
w.executeJavaScript(
`(function() {
document.querySelector('.inpage-search-close').click();
})()`,
false,
);
})
.then(pause1000ms)
.then(() => {
A.ok(!s.opened);
A.equal(w.className, 'electron-in-page-search-window search-inactive');
s.finalize();
A.equal(document.querySelector('.electron-in-page-search-window'), null);
});
});
});
});

21
app/node_modules/electron-in-page-search/tsconfig.json generated vendored Normal file
View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"removeComments": true,
"preserveConstEnums": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmitOnError": true,
"strictNullChecks": true,
"target": "es2015",
"sourceMap": true
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
]
}