From 5e72f04de79dcb6676571a6396adea4a36cb316f Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 2 Apr 2020 15:12:09 +0200 Subject: [PATCH] A lot of updates --- modules/core/main.ts | 1 + modules/core/url-preview/html/index.css | 33 +- modules/core/url-preview/html/index.html | 2 +- modules/core/url-preview/html/index.ts | 6 +- modules/core/url-preview/html/navigation.ts | 778 ++++++++++++++++++++ modules/core/url-preview/index.ts | 5 +- package.json | 1 - 7 files changed, 812 insertions(+), 14 deletions(-) create mode 100644 modules/core/url-preview/html/navigation.ts diff --git a/modules/core/main.ts b/modules/core/main.ts index c23eb46..3058917 100644 --- a/modules/core/main.ts +++ b/modules/core/main.ts @@ -9,6 +9,7 @@ import MessageBoxOptions = electron.MessageBoxOptions; import {process_args, parse_arguments, Arguments} from "../shared/process-arguments"; import {open as open_changelog} from "./app-updater/changelog"; import * as crash_handler from "../crash_handler"; +import {open_preview} from "./url-preview"; async function execute_app() { if(process_args.has_value("update-execute")) { diff --git a/modules/core/url-preview/html/index.css b/modules/core/url-preview/html/index.css index 5bc86f2..02571cc 100644 --- a/modules/core/url-preview/html/index.css +++ b/modules/core/url-preview/html/index.css @@ -1,13 +1,22 @@ #nav-body-ctrls { background-color: #2a2a2a; padding: 20px; - font-family: arial + font-family: arial, serif } #nav-body-tabs { + flex-shrink: 0; + flex-grow: 1; + + min-height: 36px; + max-height: fit-content; + + overflow-y: hidden; + overflow-x: auto; + background: linear-gradient(#2a2a2a 75%, #404040); height: 36px; - font-family: arial + font-family: arial, serif } #nav-body-views { @@ -52,11 +61,15 @@ .nav-tabs-tab { border-radius: 2px; - height: 35px + cursor: pointer; +} + +.nav-tabs-tab:hover { + background-color: hsla(0, 0%, 20%, 1); } .nav-tabs-tab.active { - background: #404040 + background-color: #404040 } .nav-tabs-favicon { @@ -67,20 +80,24 @@ padding-left: 5px; font-style: normal; font-weight: 700; - color: #fcfcfc + color: #fcfcfc; + cursor: pointer!important; /* for some reason something tries to override this */ } -.nav-tabs-title:hover { +.nav-tabs-tab:hover .nav-tabs-title { color: #c2c2c2 } .nav-tabs-close { width: 20px; height: 20px; - margin: 6px; - margin-left: 2px + margin: 6px 6px 6px 2px; } .nav-tabs-close:hover { fill: #dc143c !important +} + +img[src=unknown] { + visibility: hidden; } \ No newline at end of file diff --git a/modules/core/url-preview/html/index.html b/modules/core/url-preview/html/index.html index 4f3bafa..74ea7cb 100644 --- a/modules/core/url-preview/html/index.html +++ b/modules/core/url-preview/html/index.html @@ -1,5 +1,5 @@ - + TeaClient - URL preview '); + } else { + jq('head').append(''); + } + /** + * EVENTS + */ + // + // switch active view and tab on click + // + jq('#nav-body-tabs').on('click', '.nav-tabs-tab', function () { + jq('.nav-tabs-tab, .nav-views-view').removeClass('active'); + + var sessionID = jq(this).data('session'); + jq('.nav-tabs-tab, .nav-views-view') + .filter('[data-session="' + sessionID + '"]') + .addClass('active'); + + var session = jq('.nav-views-view[data-session="' + sessionID + '"]')[0]; + (NAV.changeTabCallback || (() => {}))(session); + NAV._updateUrl((session as WebviewTag).getURL()); + NAV._updateCtrls(); + + // + // close tab and view + // + }).on('click', '.nav-tabs-close', function() { + var sessionID = jq(this).parent('.nav-tabs-tab').data('session'); + var session = jq('.nav-tabs-tab, .nav-views-view').filter('[data-session="' + sessionID + '"]'); + + if (session.hasClass('active')) { + if (session.next('.nav-tabs-tab').length) { + session.next().addClass('active'); + (NAV.changeTabCallback || (() => {}))(session.next()[1]); + } else { + session.prev().addClass('active'); + (NAV.changeTabCallback || (() => {}))(session.prev()[1]); + } + } + session.remove(); + NAV._updateUrl(); + NAV._updateCtrls(); + return false; + }); + // + // add a tab, default to google.com + // + jq('#nav-body-tabs').on('click', '#nav-tabs-add', function () { + let params; + if(typeof options.newTabParams === "function"){ + params = options.newTabParams(); + } + else if(options.newTabParams instanceof Array){ + params = options.newTabParams + } else { + params = ['http://www.google.com/', { + close: options.closableTabs, + icon: NAV.TAB_ICON + }]; + } + NAV.newTab(...params); + }); + // + // go back + // + jq('#nav-body-ctrls').on('click', '#nav-ctrls-back', function () { + NAV.back(); + }); + // + // go forward + // + jq('#nav-body-ctrls').on('click', '#nav-ctrls-forward', function () { + NAV.forward(); + }); + // + // reload page + // + jq('#nav-body-ctrls').on('click', '#nav-ctrls-reload', function () { + if (jq(this).find('#nav-ready').length) { + NAV.reload(); + } else { + NAV.stop(); + } + }); + // + // highlight address input text on first select + // + jq('#nav-ctrls-url').on('focus', function (e) { + jq(this) + .one('mouseup', function () { + jq(this).select(); + return false; + }) + .select(); + }); + // + // load or search address on enter / shift+enter + // + jq('#nav-ctrls-url').keyup(function (this: HTMLInputElement, e) { + if (e.keyCode == 13) { + if (e.shiftKey) { + NAV.newTab(this.value, { + close: options.closableTabs, + icon: NAV.TAB_ICON + }); + } else { + if (jq('.nav-tabs-tab').length) { + NAV.changeTab(this.value); + } else { + NAV.newTab(this.value, { + close: options.closableTabs, + icon: NAV.TAB_ICON + }); + } + } + } + }); + /** + * FUNCTIONS + */ + // + // update controls like back, forward, etc... + // + this._updateCtrls = function () { + let webview = jq('.nav-views-view.active')[0] as WebviewTag; + if (!webview) { + jq('#nav-ctrls-back').addClass('disabled'); + jq('#nav-ctrls-forward').addClass('disabled'); + jq('#nav-ctrls-reload').html(this.SVG_RELOAD).addClass('disabled'); + return; + } + if (webview.canGoBack()) { + jq('#nav-ctrls-back').removeClass('disabled'); + } else { + jq('#nav-ctrls-back').addClass('disabled'); + } + if (webview.canGoForward()) { + jq('#nav-ctrls-forward').removeClass('disabled'); + } else { + jq('#nav-ctrls-forward').addClass('disabled'); + } + if (webview.isLoading()) { + this._loading(); + } else { + this._stopLoading(); + } + if (webview.getAttribute('data-readonly') == 'true') { + jq('#nav-ctrls-url').attr('readonly', 'readonly'); + } else { + jq('#nav-ctrls-url').removeAttr('readonly'); + } + + } //:_updateCtrls() + // + // start loading animations + // + this._loading = function (tab) { + tab = tab || null; + + if (tab == null) { + tab = jq('.nav-tabs-tab.active'); + } + + tab.find('.nav-tabs-favicon').css('animation', 'nav-spin 2s linear infinite'); + jq('#nav-ctrls-reload').html(this.SVG_CLEAR); + } //:_loading() + // + // stop loading animations + // + this._stopLoading = function (tab) { + tab = tab || null; + + if (tab == null) { + tab = jq('.nav-tabs-tab.active'); + } + + tab.find('.nav-tabs-favicon').css('animation', ''); + jq('#nav-ctrls-reload').html(this.SVG_RELOAD); + } //:_stopLoading() + // + // auto add http protocol to url input or do a search + // + this._purifyUrl = function (url) { + if (urlRegex({ + strict: false, + exact: true + }).test(url)) { + url = (url.match(/^https?:\/\/.*/)) ? url : 'http://' + url; + } else { + url = (!url.match(/^[a-zA-Z]+:\/\//)) ? 'https://www.google.com/search?q=' + url.replace(' ', '+') : url; + } + return url; + } //:_purifyUrl() + // + // set the color of the tab based on the favicon + // + this._setTabColor = function (url, currtab) { + const getHexColor = new Color(url, { + amount: 1, + format: 'hex' + }); + getHexColor.mostUsed(result => { + currtab.find('.nav-tabs-favicon svg').attr('fill', result); + }); + } //:_setTabColor() + // + // add event listeners to current webview + // + this._addEvents = function (sessionID, options) { + let currtab = jq('.nav-tabs-tab[data-session="' + sessionID + '"]'); + let webview = jq('.nav-views-view[data-session="' + sessionID + '"]') as JQuery; + + webview.on('dom-ready', function () { + if (options.contextMenu) { + contextMenu({ + window: webview[0], + labels: { + cut: 'Cut', + copy: 'Copy', + paste: 'Paste', + save: 'Save', + copyLink: 'Copy Link', + inspect: 'Inspect' + } + }); + } + }); + webview.on('page-title-updated', function () { + if (options.title == 'default') { + currtab.find('.nav-tabs-title').text(webview[0].getTitle()); + currtab.find('.nav-tabs-title').attr('title', webview[0].getTitle()); + } + }); + webview.on('did-start-loading', function () { + NAV._loading(currtab); + }); + webview.on('did-stop-loading', function () { + NAV._stopLoading(currtab); + }); + webview.on('enter-html-full-screen', function () { + jq('.nav-views-view.active').siblings().not('script').hide(); + jq('.nav-views-view.active').parents().not('script').siblings().hide(); + }); + webview.on('leave-html-full-screen', function () { + jq('.nav-views-view.active').siblings().not('script').show(); + jq('.nav-views-view.active').parents().siblings().not('script').show(); + }); + webview.on('load-commit', function () { + NAV._updateCtrls(); + }); + webview[0].addEventListener('did-navigate', (res) => { + if(currtab[0] === jq('.nav-tabs-tab.active')[0]) + NAV._updateUrl(res.url); + }); + webview[0].addEventListener('did-fail-load', (res) => { + if(currtab[0] === jq('.nav-tabs-tab.active')[0]) + NAV._updateUrl(res.validatedURL); + }); + webview[0].addEventListener('did-navigate-in-page', (res) => { + if(currtab[0] === jq('.nav-tabs-tab.active')[0]) + NAV._updateUrl(res.url); + }); + webview[0].addEventListener("new-window", (res) => { + if (!(options.newWindowFrameNameBlacklistExpression instanceof RegExp && options.newWindowFrameNameBlacklistExpression.test(res.frameName))) { + NAV.newTab(res.url, { + icon: NAV.TAB_ICON + }); + } + }); + webview[0].addEventListener('page-favicon-updated', (res) => { + currtab.find('.nav-tabs-favicon').replaceWith(jq('')); + }); + webview[0].addEventListener('did-fail-load', (res) => { + if (res.validatedURL == jq('#nav-ctrls-url').val() && res.errorCode != -3) { + this.executeJavaScript('document.body.innerHTML=' + + '
' + + '

Oops, this page failed to load correctly.

' + + '

ERROR [ ' + res.errorCode + ', ' + res.errorDescription + ' ]

' + + '

' + + '

Try this

' + + '
  • Check your spelling - "' + res.validatedURL + '".

  • ' + + '
  • Refresh the page.

  • ' + + '
  • Perform a search instead.

  • ' + + '
    ' + ); + } + }); + return webview[0]; + } //:_addEvents() + // + // update #nav-ctrls-url to given url or active tab's url + // + this._updateUrl = function (url) { + url = url || null; + let urlInput = jq('#nav-ctrls-url'); + if (url == null) { + if (jq('.nav-views-view').length) { + url = (jq('.nav-views-view.active')[0] as WebviewTag).getURL(); + } else { + url = ''; + } + } + urlInput.off('blur'); + if (!urlInput.is(':focus')) { + urlInput.prop('value', url); + urlInput.data('last', url); + } else { + urlInput.on('blur', function () { + // if url not edited + if (urlInput.val() == urlInput.data('last')) { + urlInput.prop('value', url); + urlInput.data('last', url); + } + urlInput.off('blur'); + }); + } + } //:_updateUrl() +} //:Navigation() +/** + * PROTOTYPES + */ +// +// create a new tab and view with an url and optional id +// +Navigation.prototype.newTab = function (url, options) { + var defaults = { + id: null, // null, 'yourIdHere' + node: false, + webviewAttributes: {}, + icon: "clean", // 'default', 'clean', 'c:\location\to\image.png' + title: "default", // 'default', 'your title here' + close: true, + readonlyUrl: false, + contextMenu: true, + newTabCallback: this.newTabCallback, + changeTabCallback: this.changeTabCallback + } + options = options ? Object.assign(defaults,options) : defaults; + if(typeof options.newTabCallback === "function"){ + let result = options.newTabCallback(url, options); + if(!result){ + return null; + } + if(result.url){ + url = result.url; + } + if(result.options){ + options = result.options; + } + if(typeof result.postTabOpenCallback === "function"){ + options.postTabOpenCallback = result.postTabOpenCallback; + } + } + + // validate options.id + jq('.nav-tabs-tab, .nav-views-view').removeClass('active'); + if (jq('#' + options.id).length) { + console.log('ERROR[electron-navigation][func "newTab();"]: The ID "' + options.id + '" already exists. Please use another one.'); + return false; + } + if (!(/^[A-Za-z]+[\w\-\:\.]*jq/.test(options.id))) { + console.log('ERROR[electron-navigation][func "newTab();"]: The ID "' + options.id + '" is not valid. Please use another one.'); + return false; + } + // build tab + var tab = ''; + // favicon + if (options.icon == 'clean') { + tab += '' + this.SVG_FAVICON + ''; + } else if (options.icon === 'default') { + tab += ''; + } else { + tab += ''; + } + // title + if (options.title == 'default') { + tab += ' . . . '; + } else { + tab += '' + options.title + ''; + } + // close + if (options.close && globalCloseableTabsOverride) { + tab += '' + this.SVG_CLEAR + ''; + } + // finish tab + tab += ''; + // add tab to correct position + if (jq('#nav-body-tabs').has('#nav-tabs-add').length) { + jq('#nav-tabs-add').before(tab); + } else { + jq('#nav-body-tabs').append(tab); + } + // add webview + let composedWebviewTag = ` { + composedWebviewTag += ` jq{key}="jq{options.webviewAttributes[key]}"`; + }); + } + jq('#nav-body-views').append(`jq{composedWebviewTag}>`); + // enable reload button + jq('#nav-ctrls-reload').removeClass('disabled'); + + // update url and add events + this._updateUrl(this._purifyUrl(url)); + let newWebview = this._addEvents(this.SESSION_ID++, options); + if(typeof options.postTabOpenCallback === "function"){ + options.postTabOpenCallback(newWebview) + } + (this.changeTabCallback || (() => {}))(newWebview); + return newWebview; + +} //:newTab() +// +// change current or specified tab and view +// +Navigation.prototype.changeTab = function (url, id) { + id = id || null; + if (id == null) { + jq('.nav-views-view.active').attr('src', this._purifyUrl(url)); + } else { + if (jq('#' + id).length) { + jq('#' + id).attr('src', this._purifyUrl(url)); + } else { + console.log('ERROR[electron-navigation][func "changeTab();"]: Cannot find the ID "' + id + '"'); + } + } +} //:changeTab() +// +// close current or specified tab and view +// +Navigation.prototype.closeTab = function (id) { + id = id || null; + + var session; + if (id == null) { + session = jq('.nav-tabs-tab.active, .nav-views-view.active'); + } else { + if (jq('#' + id).length) { + var sessionID = jq('#' + id).data('session'); + session = jq('.nav-tabs-tab, .nav-views-view').filter('[data-session="' + sessionID + '"]'); + } else { + console.log('ERROR[electron-navigation][func "closeTab();"]: Cannot find the ID "' + id + '"'); + return false; + } + } + if (session.next('.nav-tabs-tab').length) { + session.next().addClass('active'); + (this.changeTabCallback || (() => {}))(session.next()[1]); + } else { + session.prev().addClass('active'); + (this.changeTabCallback || (() => {}))(session.prev()[1]); + } + + session.remove(); + this._updateUrl(); + this._updateCtrls(); +} //:closeTab() +// +// go back on current or specified view +// +Navigation.prototype.back = function (id) { + id = id || null; + if (id == null) { + (jq('.nav-views-view.active')[0] as WebviewTag).goBack(); + } else { + if (jq('#' + id).length) { + (jq('#' + id)[0] as WebviewTag).goBack(); + } else { + console.log('ERROR[electron-navigation][func "back();"]: Cannot find the ID "' + id + '"'); + } + } +} //:back() +// +// go forward on current or specified view +// +Navigation.prototype.forward = function (id) { + id = id || null; + if (id == null) { + (jq('.nav-views-view.active')[0] as WebviewTag).goForward(); + } else { + if (jq('#' + id).length) { + (jq('#' + id)[0] as WebviewTag).goForward(); + } else { + console.log('ERROR[electron-navigation][func "forward();"]: Cannot find the ID "' + id + '"'); + } + } +} //:forward() +// +// reload current or specified view +// +Navigation.prototype.reload = function (id) { + id = id || null; + if (id == null) { + (jq('.nav-views-view.active')[0] as WebviewTag).reload(); + } else { + if (jq('#' + id).length) { + (jq('#' + id)[0] as WebviewTag).reload(); + } else { + console.log('ERROR[electron-navigation][func "reload();"]: Cannot find the ID "' + id + '"'); + } + } +} //:reload() +// +// stop loading current or specified view +// +Navigation.prototype.stop = function (id) { + id = id || null; + if (id == null) { + (jq('.nav-views-view.active')[0] as WebviewTag).stop(); + } else { + if (jq('#' + id).length) { + (jq('#' + id)[0] as WebviewTag).stop(); + } else { + console.log('ERROR[electron-navigation][func "stop();"]: Cannot find the ID "' + id + '"'); + } + } +} //:stop() +// +// listen for a message from webview +// +Navigation.prototype.listen = function (id, callback) { + let webview = null; + + //check id + if (jq('#' + id).length) { + webview = document.getElementById(id); + } else { + console.log('ERROR[electron-navigation][func "listen();"]: Cannot find the ID "' + id + '"'); + } + + // listen for message + if (webview != null) { + try { + webview.addEventListener('ipc-message', (event) => { + callback(event.channel, event.args, webview); + }); + } catch (e) { + webview.addEventListener("dom-ready", function (event) { + webview.addEventListener('ipc-message', (event) => { + callback(event.channel, event.args, webview); + }); + }); + } + } +} //:listen() +// +// send message to webview +// +Navigation.prototype.send = function (id, channel, args) { + let webview = null; + + // check id + if (jq('#' + id).length) { + webview = document.getElementById(id); + } else { + console.log('ERROR[electron-navigation][func "send();"]: Cannot find the ID "' + id + '"'); + } + + // send a message + if (webview != null) { + try { + webview.send(channel, args); + } catch (e) { + webview.addEventListener("dom-ready", function (event) { + webview.send(channel, args); + }); + } + } +} //:send() +// +// open developer tools of current or ID'd webview +// +Navigation.prototype.openDevTools = function (id) { + id = id || null; + let webview = null; + + // check id + if (id == null) { + webview = jq('.nav-views-view.active')[0]; + } else { + if (jq('#' + id).length) { + webview = document.getElementById(id); + } else { + console.log('ERROR[electron-navigation][func "openDevTools();"]: Cannot find the ID "' + id + '"'); + } + } + + // open dev tools + if (webview != null) { + try { + webview.openDevTools(); + } catch (e) { + webview.addEventListener("dom-ready", function (event) { + webview.openDevTools(); + }); + } + } +} //:openDevTools() +// +// print current or specified tab and view +// +Navigation.prototype.printTab = function (id, opts) { + id = id || null + let webview = null + + // check id + if (id == null) { + webview = jq('.nav-views-view.active')[0] + } else { + if (jq('#' + id).length) { + webview = document.getElementById(id) + } else { + console.log('ERROR[electron-navigation][func "printTab();"]: Cannot find the ID "' + id + '"') + } + } + + // print + if (webview != null) { + webview.print(opts || {}); + } +} +//:nextTab() +// +// toggle next available tab +// +Navigation.prototype.nextTab = function () { + var tabs = jq('.nav-tabs-tab').toArray(); + var activeTabIndex = tabs.indexOf(jq('.nav-tabs-tab.active')[0]); + var nexti = activeTabIndex + 1; + if (nexti > tabs.length - 1) nexti = 0; + jq(jq('.nav-tabs-tab')[nexti]).trigger('click'); + return false +} //:nextTab() +//:prevTab() +// +// toggle previous available tab +// +Navigation.prototype.prevTab = function () { + var tabs = jq('.nav-tabs-tab').toArray(); + var activeTabIndex = tabs.indexOf(jq('.nav-tabs-tab.active')[0]); + var nexti = activeTabIndex - 1; + if (nexti < 0) nexti = tabs.length - 1; + jq(jq('.nav-tabs-tab')[nexti]).trigger('click'); + return false +} //:prevTab() +// go to a tab by index or keyword +// +Navigation.prototype.goToTab = function (index) { + let jqactiveTabAndView = jq('#nav-body-tabs .nav-tabs-tab.active, #nav-body-views .nav-views-view.active'); + let jqtabAndViewToActivate; + + if (index == 'previous') { + jqtabAndViewToActivate = jqactiveTabAndView.prev('#nav-body-tabs .nav-tabs-tab, #nav-body-views .nav-views-view'); + } else if (index == 'next') { + jqtabAndViewToActivate = jqactiveTabAndView.next('#nav-body-tabs .nav-tabs-tab, #nav-body-views .nav-views-view'); + } else if (index == 'last') { + jqtabAndViewToActivate = jq('#nav-body-tabs .nav-tabs-tab:last-of-type, #nav-body-views .nav-views-view:last-of-type'); + } else { + jqtabAndViewToActivate = jq('#nav-body-tabs .nav-tabs-tab:nth-of-type(' + index + '), #nav-body-views .nav-views-view:nth-of-type(' + index + ')'); + } + + if (jqtabAndViewToActivate.length) { + jq('#nav-ctrls-url').blur(); + jqactiveTabAndView.removeClass('active'); + jqtabAndViewToActivate.addClass('active'); + + this._updateUrl(); + this._updateCtrls(); + } +} //:goToTab() +// go to a tab by id of the webview tag +Navigation.prototype.goToTabByWebviewId = function(id){ + const webviews = document.querySelectorAll("webview.nav-views-view"); + for(let index in webviews){ + if(webviews[index].id == id){ + this.goToTab(+index + 1); + return; + } + } +} //:goToTabByWebviewId() +/** + * MODULE EXPORTS + */ +module.exports = Navigation; \ No newline at end of file diff --git a/modules/core/url-preview/index.ts b/modules/core/url-preview/index.ts index 408ab4b..b1b6401 100644 --- a/modules/core/url-preview/index.ts +++ b/modules/core/url-preview/index.ts @@ -35,11 +35,14 @@ export async function open_preview(url: string) { }, center: true, show: false, + + minHeight: 400, + minWidth: 400 }); global_window.setMenuBarVisibility(false); global_window.setMenu(null); global_window.loadFile(path.join(__dirname, "html", "index.html")).then(() => { - //global_window.webContents.openDevTools(); + global_window.webContents.openDevTools(); }); global_window.on('close', event => { global_window = undefined; diff --git a/package.json b/package.json index c4540a8..561e9e9 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "aws-sign2": "^0.7.0", "aws4": "^1.8.0", "electron": "8.0.0", - "electron-navigation": "^1.5.8", "electron-rebuild": "^1.8.6", "extend": "^3.0.2", "extsprintf": "^1.4.0",