/** * Default config for all webviews created */ Ext.define('Hamsket.ux.WebView',{ extend: 'Ext.panel.Panel' ,xtype: 'webview' ,requires: [ 'Hamsket.util.Format' ,'Hamsket.util.Notifier' ,'Hamsket.util.UnreadCounter' ,'Hamsket.util.IconLoader' ] // private ,zoomLevel: 0 ,currentUnreadCount: 0 // CONFIG ,hideMode: 'offsets' ,initComponent(config) { const me = this; function getLocation(href) { const match = href.match(/^(https?):\/\/([-.\w]*)(\/[^#?]*)(\?[^#]*|)(#.*|)$/); return match && { protocol: match[1], host: match[2], hostname: match[3], port: match[4], pathname: match[5], search: match[6], hash: match[7] }; } // Allow Custom sites with self certificates //if ( me.record.get('trust') ) ipc.send('allowCertificate', me.src); Ext.apply(me, { items: me.webViewConstructor() ,title: me.record.get('tabname') ? Ext.String.htmlEncode(me.record.get('name')) : '' ,icon: me.record.get('type') === 'custom' ? (me.record.get('logo') === '' ? 'resources/icons/custom.png' : me.record.get('logo')) : 'resources/icons/'+me.record.get('logo') ,src: me.record.get('url') ,type: me.record.get('type') ,align: me.record.get('align') ,notifications: me.record.get('notifications') ,muted: me.record.get('muted') ,tabConfig: { listeners: { afterrender ( btn ) { btn.el.on('contextmenu', function(e) { btn.showMenu('contextmenu'); e.stopEvent(); }); } ,scope: me } ,clickEvent: '' ,style: !me.record.get('enabled') ? '-webkit-filter: grayscale(1)' : '' ,menu: { plain: true ,items: [ { xtype: 'toolbar' ,items: [ { xtype: 'segmentedbutton' ,allowToggle: false ,flex: 1 ,items: [ { text: 'Back' ,glyph: 'XF053@FontAwesome' ,flex: 1 ,scope: me ,handler: me.goBack } ,{ text: 'Forward' ,glyph: 'XF054@FontAwesome' ,iconAlign: 'right' ,flex: 1 ,scope: me ,handler: me.goForward } ] } ] } ,'-' ,{ text: 'Zoom In' ,glyph: 'XF00E@FontAwesome' ,scope: me ,handler: me.zoomIn } ,{ text: 'Zoom Out' ,glyph: 'XF010@FontAwesome' ,scope: me ,handler: me.zoomOut } ,{ text: 'Reset Zoom' ,glyph: 'XF002@FontAwesome' ,scope: me ,handler: me.resetZoom } ,'-' ,{ text: locale['app.webview[0]'] ,glyph: 'XF021@FontAwesome' ,scope: me ,handler: me.reloadService } ,'-' ,{ text: locale['app.webview[3]'] ,glyph: 'XF121@FontAwesome' ,scope: me ,handler: me.toggleDevTools } ] } } ,listeners: { afterrender: me.onAfterRender ,beforedestroy: me.onBeforeDestroy } }); me.items.push(me.statusBarConstructor(true)); me.callParent(config); } ,onBeforeDestroy() { const me = this; me.setUnreadCount(0); } ,webViewConstructor( enabled ) { const me = this; let cfg; enabled = enabled || me.record.get('enabled'); if ( !enabled ) { cfg = [{ xtype: 'container' ,html: '

Service Disabled

' ,style: 'text-align:center;' ,padding: 100 }]; } else { cfg = [{ xtype: 'component' ,hideMode: 'offsets' ,autoRender: true ,autoShow: true ,autoEl: { tag: 'webview' ,src: me.record.get('url') ,style: 'width:100%;height:100%;visibility:visible;' ,partition: 'persist:' + me.record.get('type') + '_' + me.id.replace('tab_', '') ,allowtransparency: 'on' ,autosize: 'on' ,webpreferences: 'enableRemoteModule=yes,spellcheck=yes,contextIsolation=no' //,nativeWindowOpen=true //,disablewebsecurity: 'on' // Disabled because some services (Like Google Drive) dont work with this enabled ,userAgent: me.getUserAgent() ,preload: './resources/js/hamsket-service-api.js' } }]; if ( Ext.getStore('ServicesList').getById(me.record.get('type')).get('allow_popups') ) cfg[0].autoEl.allowpopups = 'on'; } return cfg; } ,statusBarConstructor(floating) { const me = this; return { xtype: 'statusbar' ,hidden: true ,keep: false ,y: floating ? '-18px' : 'auto' ,height: 19 ,dock: 'bottom' ,defaultText: ' Ready' ,busyIconCls : '' ,busyText: ' '+locale['app.webview[4]'] ,items: [ { xtype: 'tbtext' ,itemId: 'url' } ,{ xtype: 'button' ,glyph: 'XF00D@FontAwesome' ,scale: 'small' ,ui: 'decline' ,padding: 0 ,scope: me ,hidden: floating ,handler: me.closeStatusBar ,tooltip: { text: 'Close statusbar until next time' ,mouseOffset: [0,-60] } } ] }; } ,onAfterRender() { const me = this; if ( !me.record.get('enabled') ) return; const webview = me.getWebView(); // Notifications in Webview me.setNotifications(localStorage.getItem('locked') || JSON.parse(localStorage.getItem('dontDisturb')) ? false : me.record.get('notifications')); // Show and hide spinner when is loading webview.addEventListener("did-start-loading", function() { console.info('Start loading...', me.src); if ( !me.down('statusbar').closed || !me.down('statusbar').keep ) me.down('statusbar').show(); me.down('statusbar').showBusy(); me.getWebContents().session.webRequest.onBeforeSendHeaders( { urls: [ 'https://accounts.google.com/', 'https://accounts.google.com/*' ] }, (details, callback) => { details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0'; callback({ requestHeaders: details.requestHeaders }); } ); }); webview.addEventListener("did-stop-loading", function() { me.down('statusbar').clearStatus({useDefaults: true}); if ( !me.down('statusbar').keep ) me.down('statusbar').hide(); }); webview.addEventListener("did-finish-load", function(e) { Hamsket.app.setTotalServicesLoaded( Hamsket.app.getTotalServicesLoaded() + 1 ); // Apply saved zoom level me.setZoomLevel(me.record.get('zoomLevel')); // Set special icon for some service (like Slack) Hamsket.util.IconLoader.loadServiceIconUrl(me, webview); }); // Open links in default browser webview.addEventListener('new-window', function(e) { switch ( me.type ) { case 'skype': // hack to fix multiple browser tabs on Skype link click, re #11 if ( e.url.match(/https:\/\/web.skype.com\/..\/undefined/) ) { e.preventDefault(); return; } else if ( e.url.indexOf('imgpsh_fullsize') >= 0 ) { ipc.send('image:download', e.url, e.target.partition); e.preventDefault(); return; } break; case 'hangouts': e.preventDefault(); if ( e.url.indexOf('plus.google.com/u/0/photos/albums') >= 0 ) { ipc.send('image:popup', e.url, e.target.partition); return; } else if ( e.url.indexOf('/el/CONVERSATION/') >= 0 ) { me.add({ xtype: 'window' ,title: 'Video Call' ,width: '80%' ,height: '80%' ,maximizable: true ,resizable: true ,draggable: true ,collapsible: true ,items: { xtype: 'component' ,hideMode: 'offsets' ,autoRender: true ,autoShow: true ,autoEl: { tag: 'webview' ,src: e.url ,style: 'width:100%;height:100%;' ,partition: me.getWebView().partition ,useragent: me.getUserAgent() } } }).show(); return; } break; case 'slack': if ( e.url.indexOf('slack.com/call/') >= 0 ) { me.add({ xtype: 'window' ,title: Ext.String.htmlEncode(e.options.title) ,width: e.options.width ,height: e.options.height ,maximizable: true ,resizable: true ,draggable: true ,collapsible: true ,items: { xtype: 'component' ,hideMode: 'offsets' ,autoRender: true ,autoShow: true ,autoEl: { tag: 'webview' ,src: e.url ,style: 'width:100%;height:100%;' ,partition: me.getWebView().partition ,useragent: me.getUserAgent() } } }).show(); e.preventDefault(); return; } break; case 'icloud': if ( e.url.indexOf('index.html#compose') >= 0 ) { me.add({ xtype: 'window' ,title: 'iCloud - Compose' ,width: 700 ,height: 500 ,maximizable: true ,resizable: true ,draggable: true ,collapsible: true ,items: { xtype: 'component' ,itemId: 'webview' ,hideMode: 'offsets' ,autoRender: true ,autoShow: true ,autoEl: { tag: 'webview' ,src: e.url ,style: 'width:100%;height:100%;' ,partition: me.getWebView().partition ,useragent: me.getUserAgent() ,preload: './resources/js/hamsket-modal-api.js' } } ,listeners: { show(win) { const webview = win.down('#webview').el.dom; webview.addEventListener('ipc-message', function(event) { const channel = event.channel; switch (channel) { case 'close': win.close(); break; default: break; } }); } } }).show(); e.preventDefault(); return; } break; case 'flowdock': if ( e.disposition === 'new-window' ) { e.preventDefault(); require('electron').remote.shell.openExternal(e.url); } return; default: break; } const protocol = require('url').parse(e.url).protocol; if (protocol === 'http:' || protocol === 'https:' || protocol === 'mailto:') { e.preventDefault(); require('electron').remote.shell.openExternal(e.url); } }); webview.addEventListener('will-navigate', function(e, url) { e.preventDefault(); }); function JSApplyCSS() { if ( me.record ) { let custom_css_complex = me.record.get('custom_css_complex'); if (custom_css_complex === true) { let custom_css = Ext.getStore('ServicesList').getById(me.record.get('type')).get('custom_css'); custom_css = custom_css + me.record.get('custom_css'); if ( custom_css !== '' ) { console.groupCollapsed(me.record.get('type').toUpperCase() + ' - Injected Custom CSS via JS'); console.info(me.type); console.log(custom_css); console.groupEnd(); let js_before = '{let mystyle=`'; let js_after = '`,mycss=document.createElement("style");mycss.type="text/css",mycss.styleSheet?mycss.styleSheet.cssText=mystyle:mycss.appendChild(document.createTextNode(mystyle));let myDocHead=document.head;null===myDocHead||myDocHead.hamsketStyled||(myDocHead.appendChild(mycss),myDocHead.hamsketStyled=!0);let myframes=document.getElementsByTagName("iframe");for(let myframe of myframes){let mydocument,mydochead=(myframe.contentDocument||myframe.contentWindow.document).head;if(null!==mydochead&&!mydochead.hamsketStyled){let myclonedcss=mycss.cloneNode(deep=!0);mydochead.appendChild(myclonedcss),mydochead.hamsketStyled=!0}}}'; webview.executeJavaScript(js_before + custom_css + js_after); } } } } webview.addEventListener("dom-ready", function(e) { // Mute Webview if ( me.record.get('muted') || localStorage.getItem('locked') || JSON.parse(localStorage.getItem('dontDisturb')) ) me.setAudioMuted(true, true); let js_inject = ''; let css_inject = ''; // Injected code to detect new messages if ( me.record ) { let js_unread = me.record.get('js_unread'); if (!js_unread) { js_unread += Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread'); } if ( js_unread !== '' ) { console.groupCollapsed(me.record.get('type').toUpperCase() + ' - JS Injected to Detect New Messages'); console.info(me.type); console.log(js_unread); console.groupEnd(); js_inject += '{' + js_unread + '}'; } let custom_js = Ext.getStore('ServicesList').getById(me.record.get('type')).get('custom_js'); custom_js += me.record.get('custom_js'); if ( custom_js !== '' ) { console.groupCollapsed(me.record.get('type').toUpperCase() + ' - Injected Custom JS'); console.info(me.type); console.log(custom_js); console.groupEnd(); js_inject += '{' + custom_js + '}'; } const custom_css_complex = me.record.get('custom_css_complex'); if (custom_css_complex === false) { let custom_css = Ext.getStore('ServicesList').getById(me.record.get('type')).get('custom_css'); custom_css += me.record.get('custom_css'); if ( custom_css !== '' ) { console.groupCollapsed(me.record.get('type').toUpperCase() + ' - Injected Custom CSS'); console.info(me.type); console.log(custom_css); console.groupEnd(); css_inject += custom_css; } } // Use passive listeners by default let passive_event_listeners = Ext.getStore('ServicesList').getById(me.record.get('type')).get('passive_event_listeners'); if (passive_event_listeners && me.record.get('passive_event_listeners')) { /* 3rdparty: This uses npm 'default-passive-events' 1.0.10 inline. Link to license: * https://github.com/zzarcon/default-passive-events/blob/master/LICENSE * Modified to remove unnecessary event hooks. * This should match behavior of Chrome >= 57. */ const passive_event_listeners_code = `const eventListenerOptionsSupported=()=>{let supported=!1;try{const opts=Object.defineProperty({},"passive",{get(){supported=!0}});window.addEventListener("test",null,opts),window.removeEventListener("test",null,opts)}catch(e){}return supported},defaultOptions={passive:!0,capture:!1},supportedPassiveTypes=["scroll","wheel","touchstart","touchmove","mousewheel"],getDefaultPassiveOption=(passive,eventName)=>void 0!==passive?passive:-1!==supportedPassiveTypes.indexOf(eventName)&&defaultOptions.passive,getWritableOptions=options=>{const passiveDescriptor=Object.getOwnPropertyDescriptor(options,"passive");return passiveDescriptor&&!0!==passiveDescriptor.writable&&void 0===passiveDescriptor.set?Object.assign({},options):options},overwriteAddEvent=superMethod=>{EventTarget.prototype.addEventListener=function(type,listener,options){const usesListenerOptions="object"==typeof options&&null!==options,useCapture=usesListenerOptions?options.capture:options;options=usesListenerOptions?getWritableOptions(options):{},options.passive=getDefaultPassiveOption(options.passive,type),options.capture=void 0===useCapture?defaultOptions.capture:useCapture,superMethod.call(this,type,listener,options)},EventTarget.prototype.addEventListener._original=superMethod},supportsPassive=eventListenerOptionsSupported();if(supportsPassive){const addEvent=EventTarget.prototype.addEventListener;overwriteAddEvent(addEvent)}`; js_inject += '{' + passive_event_listeners_code + '}'; } // Use slowed timers by default let slowed_timers = Ext.getStore('ServicesList').getById(me.record.get('type')).get('slowed_timers'); if (slowed_timers && me.record.get('slowed_timers')) { const slowed_timers_code = `window.setTimeout=window.setTimeout;const __setTimeout=window.setTimeout;window.setTimeout=function(func,time, ...func_args){let a=time;return a<100&&(a=100),__setTimeout(func,a, ...func_args)};`; js_inject += '{' + slowed_timers_code + '}'; } } // Prevent Title blinking (some services have) and only allow when the title have an unread regex match: "(3) Title" if ( Ext.getStore('ServicesList').getById(me.record.get('type')).get('titleBlink') ) { const js_preventBlink = `const originalTitle=document.title;Object.defineProperty(document,"title",{configurable:!0,set(a){null===a.match(new RegExp("[(]([0-9•]+)[)][ ](.*)","g"))&&a!==originalTitle||(document.getElementsByTagName("title")[0].innerHTML=a)},get:()=>document.getElementsByTagName("title")[0].innerHTML});`; js_inject += js_preventBlink; } // Scroll always to top (bug) js_inject += 'document.body.scrollTop=0;'; // Handles Certificate Errors me.getWebContents().on('certificate-error', function(event, url, error, certificate, callback) { if ( me.record.get('trust') ) { event.preventDefault(); callback(true); } else { callback(false); } me.down('statusbar').keep = true; me.down('statusbar').show(); me.down('statusbar').setStatus({ text: ' Certification Warning' }); me.down('statusbar').down('button').show(); }); webview.executeJavaScript(js_inject); webview.insertCSS(css_inject); }); webview.addEventListener('load-commit', function() { JSApplyCSS(); }); webview.addEventListener('did-frame-finish-load', function() { JSApplyCSS(); }); webview.addEventListener('ipc-message', function(event) { const channel = event.channel; switch (channel) { case 'hamsket.setUnreadCount': handleSetUnreadCount(event); break; case 'hamsket.clearUnreadCount': handleClearUnreadCount(event); break; case 'hamsket.updateBadge': handleUpdateBadge(event); break; case 'hamsket.showWindowAndActivateTab': showWindowAndActivateTab(event); break; } /** * Handles 'hamsket.clearUnreadCount' messages. * Clears the unread count. */ function handleClearUnreadCount() { me.tab.setBadgeText(''); me.currentUnreadCount = 0; me.setUnreadCount(0); } /** * Handles 'hamsket.setUnreadCount' messages. * Sets the badge text if the event contains an integer * or a '•' (indicating non-zero but unknown number of unreads) as first argument. * * @param event */ function handleSetUnreadCount(event) { if (Array.isArray(event.args) === true && event.args.length > 0) { const count = event.args[0]; if (count === parseInt(count, 10) || count === '•') { me.setUnreadCount(count); } } } function showWindowAndActivateTab(event) { const currentWindow = require('electron').remote.getCurrentWindow(); currentWindow.show(); currentWindow.focus(); const tabPanel = Ext.cq1('app-main'); tabPanel.getActiveTab().blur(); tabPanel.setActiveTab(me); tabPanel.getActiveTab().focus(); } function handleUpdateBadge(event) { if (Array.isArray(event.args) === true && event.args.length > 1) { const direct = event.args[0]; const indirect = event.args[1]; const count = direct > 0 ? direct : (indirect > 0 ? '•' : 0); if (count === parseInt(count, 10) || count === '•') { me.setUnreadCount(count); } else { me.handleClearUnreadCount(); } } } }); /** * Register page title update event listener only for services that don't specify a js_unread */ if (Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread') === '' && me.record.get('js_unread') === '') { webview.addEventListener("page-title-updated", function(e) { let count = e.title.match(/\(([^)]+)\)/); // Get text between (...) count = count ? count[1] : '0'; count = count === '•' ? count : Ext.isArray(count.match(/\d+/g)) ? count.match(/\d+/g).join("") : count.match(/\d+/g); // Some services have special characters. Example: (•) count = count === null ? '0' : count; me.setUnreadCount(count); }); } webview.addEventListener('did-navigate', function( e ) { if ( e.isMainFrame && me.record.get('type') === 'tweetdeck' ) Ext.defer(function() { webview.loadURL(e.newURL); }, 1000); // Applied a defer because sometimes is not redirecting. TweetDeck 2FA is an example. }); webview.addEventListener('update-target-url', function( url ) { me.down('statusbar #url').setText(url.url); }); } ,setUnreadCount(newUnreadCount) { const me = this; if ( !isNaN(newUnreadCount) && (function(x) { return (x | 0) === x; })(parseFloat(newUnreadCount)) && me.record.get('includeInGlobalUnreadCounter') === true) { Hamsket.util.UnreadCounter.setUnreadCountForService(me.record.get('id'), newUnreadCount); } else { Hamsket.util.UnreadCounter.clearUnreadCountForService(me.record.get('id')); } me.setTabBadgeText(Hamsket.util.Format.formatNumber(newUnreadCount)); me.doManualNotification(parseInt(newUnreadCount)); } ,refreshUnreadCount() { this.setUnreadCount(this.currentUnreadCount); } /** * Dispatch manual notification if * • service doesn't have notifications, so Hamsket does them * • count increased * • not in dnd mode * • notifications enabled * * @param {int} count */ ,doManualNotification(count) { const me = this; if (Ext.getStore('ServicesList').getById(me.type).get('manual_notifications') && me.currentUnreadCount < count && me.record.get('notifications') && !JSON.parse(localStorage.getItem('dontDisturb'))) { Hamsket.util.Notifier.dispatchNotification(me, count); } me.currentUnreadCount = count; } /** * Sets the tab badge text depending on the service config param "displayTabUnreadCounter". * * @param {string} badgeText */ ,setTabBadgeText(badgeText) { const me = this; if (me.record.get('displayTabUnreadCounter') === true) { me.tab.setBadgeText(badgeText); } else { me.tab.setBadgeText(''); } } /** * Clears the unread counter for this view: * • clears the badge text * • clears the global unread counter */ ,clearUnreadCounter() { const me = this; me.tab.setBadgeText(''); Hamsket.util.UnreadCounter.clearUnreadCountForService(me.record.get('id')); } ,reloadService(btn) { const me = this; const webview = me.getWebView(); if ( me.record.get('enabled') ) { me.clearUnreadCounter(); webview.loadURL(me.src); } } ,toggleDevTools(btn) { const me = this; const webview = me.getWebView(); if ( me.record.get('enabled')) { if (webview.isDevToolsOpened()) { webview.closeDevTools(); } else { webview.openDevTools(); } } } ,setURL(url) { const me = this; const webview = me.getWebView(); me.src = url; if ( me.record.get('enabled') ) webview.loadURL(url); } ,setAudioMuted(muted, calledFromDisturb) { const me = this; const webview = me.getWebView(); me.muted = muted; if ( !muted && !calledFromDisturb && JSON.parse(localStorage.getItem('dontDisturb')) ) return; if ( me.record.get('enabled') ) webview.setAudioMuted(muted); } ,closeStatusBar() { const me = this; me.down('statusbar').hide(); me.down('statusbar').closed = true; me.down('statusbar').keep = false; } ,setStatusBar(keep) { const me = this; me.removeDocked(me.down('statusbar'), true); if ( keep ) { me.addDocked(me.statusBarConstructor(false)); } else { me.add(me.statusBarConstructor(true)); } me.down('statusbar').keep = keep; } ,setNotifications(notification, calledFromDisturb) { const me = this; const webview = me.getWebView(); me.notifications = notification; if ( notification && !calledFromDisturb && JSON.parse(localStorage.getItem('dontDisturb')) ) return; if ( me.record.get('enabled') ) ipc.send('setServiceNotifications', webview.partition, notification); } ,setEnabled(enabled) { const me = this; me.clearUnreadCounter(); me.removeAll(); me.add(me.webViewConstructor(enabled)); if ( enabled ) { me.resumeEvent('afterrender'); me.show(); me.tab.setStyle('-webkit-filter', 'grayscale(0)'); me.onAfterRender(); } else { me.suspendEvent('afterrender'); me.tab.setStyle('-webkit-filter', 'grayscale(1)'); } } ,goBack() { const me = this; const webview = me.getWebView(); if ( me.record.get('enabled') ) webview.goBack(); } ,goForward() { const me = this; const webview = me.getWebView(); if ( me.record.get('enabled') ) webview.goForward(); } ,setZoomLevel(level) { this.getWebContents().zoomLevel = level; } ,zoomIn() { const me = this; me.zoomLevel = me.zoomLevel + 1; if ( me.record.get('enabled') ) { me.record.set('zoomLevel', me.zoomLevel); me.setZoomLevel(me.zoomLevel); } } ,zoomOut() { const me = this; me.zoomLevel = me.zoomLevel - 1; if ( me.record.get('enabled') ) { me.record.set('zoomLevel', me.zoomLevel); me.setZoomLevel(me.zoomLevel); } } ,resetZoom() { const me = this; me.zoomLevel = 0; if ( me.record.get('enabled') ) { me.record.set('zoomLevel', me.zoomLevel); me.setZoomLevel(me.zoomLevel); } } ,getWebView() { if ( this.record.get('enabled') ) { return this.down('component').el.dom; } else { return false; } } ,getWebContents() { if ( this.record.get('enabled') ) { const remote = require('electron').remote; const webview = this.getWebView(); const id = webview.getWebContentsId(); return remote.webContents.fromId(id); } else { return false; } } ,getUserAgent() { const me = this; const user_platform = me.record.get('os_override'); const service_platform = Ext.getStore('ServicesList').getById(me.record.get('type')).get('os_override'); const platform = user_platform ? user_platform : service_platform ? service_platform : ''; const user_version = me.record.get('chrome_version'); const service_version = Ext.getStore('ServicesList').getById(me.record.get('type')).get('chrome_version'); const chrome_version = user_version ? user_version : service_version ? service_version : ''; const default_ua = `Mozilla/5.0` + ` (${me.getOSPlatform(platform)})` + ` AppleWebKit/537.36 (KHTML, like Gecko)` + ` Chrome/${me.getChromeVersion(chrome_version)} Safari/537.36`; // NOTE: Keep just in case we need to go back to the basics. // const default_ua = window.navigator.userAgent // .replace(`Electron/${me.getElectronVersion()} `,'') // .replace(`Hamsket/${me.getAppVersion()} `, ''); const service_ua = Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent'); const user_ua = me.record.get('userAgent'); const ua = (platform || chrome_version) ? default_ua : user_ua ? user_ua : service_ua ? service_ua : default_ua; return ua; } ,updateUserAgent() { const me = this; const webcontents = me.getWebContents(); webcontents.setUserAgent(me.getUserAgent()); } ,getOSArch(platform) { const me = this; const remote = require('electron').remote; platform = platform ? platform : remote.require('os').platform(); let arch = remote.require('os').arch(); switch (platform) { case 'win32': arch = me.is32bit() ? 'WOW64' : 'Win64; x64'; break; case 'freebsd': arch = me.is32bit() ? 'i386' : 'amd64'; break; case 'sunos': arch = me.is32bit() ? 'i86pc' : 'x86_64'; break; case 'linux': default: arch = me.is32bit() ? 'i686' : 'x86_64'; break; } return arch; } ,getOSArchType() { let arch = require('electron').remote.require('os').arch(); switch(arch) { case 'x64': case 'ia32': case 'x32': arch='Intel'; break; case 'arm64': case 'arm': arch='ARM'; break; case 'mips': case 'mipsel': arch='MIPS'; break; case 'ppc64': case 'ppc': arch='PPC'; break; case 's390x': case 's390': arch='S390'; break; default: arch='Unknown'; break; } return arch; } ,getOSPlatform(platform) { const me = this; platform = platform ? platform : require('electron').remote.require('os').platform(); switch (platform) { case 'win32': platform = `${me.getOSRelease(platform)}; ${me.getOSArch(platform)}`; break; case 'linux': platform = `X11; Linux ${me.getOSArch(platform)}`; break; case 'darwin': platform = `Macintosh; ${me.getOSArchType()} Mac OS X ${me.getOSRelease(platform)}`; break; case 'freebsd': platform = `X11; FreeBSD ${me.getOSArch(platform)}`; break; case 'sunos': platform = `X11; SunOS i86pc`; break; default: platform = `X11; ${platform} ${me.getOSArch(platform)}`; } return platform; } ,isWindows(platform) { platform = platform ? platform : require('electron').remote.require('os').platform(); return platform === 'win32'; } ,isMac(platform) { platform = platform ? platform : require('electron').remote.require('os').platform(); return platform === 'darwin'; } ,is32bit() { const arch = require('electron').remote.require('os').arch(); if (arch === 'ia32' || arch === 'x32') return true; else return false; } ,getOSRelease(platform) { const me = this; const remote = require('electron').remote; if (me.isWindows(platform)) { if (platform) { return "Windows NT 10.0"; } else { return remote.require('os').release().match(/([0-9]+\.[0-9]+)/)[0]; } } else if (me.isMac(platform)) { return remote.require('os').release().split('.').join('_'); } else { return remote.require('os').release(); } } ,getChromeVersion(version) { return version || require('electron').remote.require('process').versions['chrome']; } ,getElectronVersion() { return require('electron').require('process').versions['electron']; } ,getAppVersion() { return require('electron').remote.app.getVersion(); } ,blur() { this.getWebView().blur(); } ,focus() { this.getWebView().focus(); } });