1
0
mirror of https://github.com/S2-/gitlit synced 2025-08-04 05:10:05 +02:00
Files
gitlit/app/node_modules/electron-in-page-search/src/index.js
2018-06-03 13:57:23 +02:00

327 lines
13 KiB
JavaScript

"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;