mirror of
https://github.com/S2-/gitlit
synced 2025-08-04 21:20:07 +02:00
add node modules to repo
This commit is contained in:
59
app/node_modules/electron-in-page-search/src/default-style.css
generated
vendored
Normal file
59
app/node_modules/electron-in-page-search/src/default-style.css
generated
vendored
Normal 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
326
app/node_modules/electron-in-page-search/src/index.js
generated
vendored
Normal 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;
|
1
app/node_modules/electron-in-page-search/src/index.js.map
generated
vendored
Normal file
1
app/node_modules/electron-in-page-search/src/index.js.map
generated
vendored
Normal file
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
366
app/node_modules/electron-in-page-search/src/index.ts
generated
vendored
Normal 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,
|
||||
);
|
||||
}
|
17
app/node_modules/electron-in-page-search/src/search-window.html
generated
vendored
Normal file
17
app/node_modules/electron-in-page-search/src/search-window.html
generated
vendored
Normal 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"><</div>
|
||||
<div class="inpage-search-forward">></div>
|
||||
<div class="inpage-search-close">✕</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>var exports = {}</script>
|
||||
</html>
|
78
app/node_modules/electron-in-page-search/src/search-window.js
generated
vendored
Normal file
78
app/node_modules/electron-in-page-search/src/search-window.js
generated
vendored
Normal 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
|
1
app/node_modules/electron-in-page-search/src/search-window.js.map
generated
vendored
Normal file
1
app/node_modules/electron-in-page-search/src/search-window.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"search-window.js","sourceRoot":"","sources":["search-window.ts"],"names":[],"mappings":"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"}
|
86
app/node_modules/electron-in-page-search/src/search-window.ts
generated
vendored
Normal file
86
app/node_modules/electron-in-page-search/src/search-window.ts
generated
vendored
Normal 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';
|
||||
});
|
Reference in New Issue
Block a user