Загрузка данных


Windows PowerShell 5.1
(C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.

PS C:\Users\str> $root = 'C:\Temp\pepper42-build\wgt-staging'
PS C:\Users\str> $js = Join-Path $root 'static\js'
PS C:\Users\str>
PS C:\Users\str> function Show-Range {
>>     param([string]$File, [int]$From, [int]$Count)
>>     $lines = Get-Content -LiteralPath $File
>>     $start = [Math]::Max(0, $From - 1)
>>     $end = [Math]::Min($lines.Length - 1, $start + $Count - 1)
>>     for ($i = $start; $i -le $end; $i++) {
>>         '{0,4}: {1}' -f ($i + 1), $lines[$i]
>>     }
>> }
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== INDEX.JS: НАЧАЛО ====="

===== INDEX.JS: НАЧАЛО =====
PS C:\Users\str> Show-Range (Join-Path $js 'index.js') 1 100
   1: var hosts = {}; // hosts is an associative array of NvHTTP objects, keyed by server UID
   2: var activePolls = {}; // hosts currently being polled.  An associated array of polling IDs, keyed by server UID
   3: var pairingCert;
   4: var myUniqueid = '0123456789ABCDEF'; // Use the same UID as other Moonlight clients to allow them to quit each other's games
   5: var api; // `api` should only be set if we're in a host-specific screen. on the initial screen it should always be null.
   6: var isInGame = false; // flag indicating whether the game stream started
   7:
   8: // Called by the common.js module.
   9: function attachListeners() {
  10:   changeUiModeForNaClLoad();
  11:   $('.resolutionMenu li').on('click', saveResolution);
  12:   $('.framerateMenu li').on('click', saveFramerate);
  13:   $('#bitrateSlider').on('input', updateBitrateField); // input occurs every notch you slide
  14:   //$('#bitrateSlider').on('change', saveBitrate); //FIXME: it seems not working
  15:   $("#remoteAudioEnabledSwitch").on('click', saveRemoteAudio);
  16:   $("#mouseLockEnabledSwitch").on('click', saveMouseLock);
  17:   $('#optimizeGamesSwitch').on('click', saveOptimize);
  18:   $('#addHostCell').on('click', addHost);
  19:   $('#backIcon').on('click', showHostsAndSettingsMode);
  20:   $('#quitCurrentApp').on('click', stopGameWithConfirmation);
  21:   $(window).resize(fullscreenNaclModule);
  22:   var registerMenu = function registerMenu(elementId, view) {
  23:     $("#".concat(elementId)).on('click', function () {
  24:       if (view.isActive()) Navigation.pop();else Navigation.push(view);
  25:     });
  26:   };
  27:   registerMenu('selectResolution', Views.SelectResolutionMenu);
  28:   registerMenu('selectFramerate', Views.SelectFramerateMenu);
  29:   registerMenu('bandwidthMenu', Views.SelectBitrateMenu);
  30:   Controller.startWatching();
  31:   window.addEventListener('gamepadbuttonpressed', function (e) {
  32:     var pressed = e.detail.pressed;
  33:     var key = e.detail.key;
  34:     if (!pressed) return;
  35:     var gamepadMapping = {
  36:       0: function _() {
  37:         return Navigation.accept();
  38:       },
  39:       1: function _() {
  40:         return Navigation.back();
  41:       },
  42:       8: function _() {
  43:         return Navigation.selectBtn();
  44:       },
  45:       9: function _() {
  46:         return Navigation.startBtn();
  47:       },
  48:       12: function _() {
  49:         return Navigation.up();
  50:       },
  51:       13: function _() {
  52:         return Navigation.down();
  53:       },
  54:       14: function _() {
  55:         return Navigation.left();
  56:       },
  57:       15: function _() {
  58:         return Navigation.right();
  59:       }
  60:     };
  61:     if (gamepadMapping[key]) {
  62:       gamepadMapping[key]();
  63:     }
  64:   });
  65: }
  66: function changeUiModeForNaClLoad() {
  67:   $('#main-navigation').children().hide();
  68:   $("#main-content").children().not("#listener, #naclSpinner").hide();
  69:   $('#naclSpinnerMessage').text('Loading Moonlight plugin...');
  70:   $('#naclSpinner').css('display', 'inline-block');
  71: }
  72: function startPollingHosts() {
  73:   for (var hostUID in hosts) {
  74:     beginBackgroundPollingOfHost(hosts[hostUID]);
  75:   }
  76: }
  77: function stopPollingHosts() {
  78:   for (var hostUID in hosts) {
  79:     stopBackgroundPollingOfHost(hosts[hostUID]);
  80:   }
  81: }
  82: function restoreUiAfterNaClLoad() {
  83:   $('#main-navigation').children().not("#quitCurrentApp").show();
  84:   $("#main-content").children().not("#listener, #naclSpinner, #game-grid").show();
  85:   $('#naclSpinner').hide();
  86:   $('#loadingSpinner').css('display', 'none');
  87:   Navigation.push(Views.Hosts);
  88:   showHostsAndSettingsMode();
  89:   findNvService(function (finder, opt_error) {
  90:     if (finder.byService_['_nvstream._tcp']) {
  91:       var ips = Object.keys(finder.byService_['_nvstream._tcp']);
  92:       for (var i in ips) {
  93:         var ip = ips[i];
  94:         if (finder.byService_['_nvstream._tcp'][ip]) {
  95:           var mDnsDiscoveredHost = new NvHTTP(ip, myUniqueid);
  96:           mDnsDiscoveredHost.pollServer(function (returneMdnsDiscoveredHost) {
  97:             // Just drop this if the host doesn't respond
  98:             if (!returneMdnsDiscoveredHost.online) {
  99:               return;
 100:             }
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== COMMON.JS: СОЗДАНИЕ EMBED ====="

===== COMMON.JS: СОЗДАНИЕ EMBED =====
PS C:\Users\str> Show-Range (Join-Path $js 'common.js') 1 205
   1: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
   2: // Use of this source code is governed by a BSD-style license that can be
   3: // found in the LICENSE file.
   4:
   5: // Set to true when loading a "Release" NaCl module, false when loading a
   6: // "Debug" NaCl module.
   7: var isRelease = true;
   8:
   9: // Javascript module pattern:
  10: //   see http://en.wikipedia.org/wiki/Unobtrusive_JavaScript#Namespaces
  11: // In essence, we define an anonymous function which is immediately called and
  12: // returns a new object. The new object contains only the exported definitions;
  13: // all other definitions in the anonymous function are inaccessible to external
  14: // code.
  15: var common = function () {
  16:   function isHostToolchain(tool) {
  17:     return tool == 'win' || tool == 'linux' || tool == 'mac';
  18:   }
  19:
  20:   /**
  21:    * Return the mime type for NaCl plugin.
  22:    *
  23:    * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
  24:    * @return {string} The mime-type for the kind of NaCl plugin matching
  25:    * the given toolchain.
  26:    */
  27:   function mimeTypeForTool(tool) {
  28:     console.log('%c[mimeTypeForTool, common.js]', 'color: gray;', 'tool: ' + tool);
  29:     // For NaCl modules use application/x-nacl.
  30:     var mimetype = 'application/x-nacl';
  31:     if (isHostToolchain(tool)) {
  32:       mimetype = 'application/x-ppapi-release';
  33:     } else if (tool == 'pnacl') {
  34:       mimetype = 'application/x-nacl'; //FIXME: replaced from 'application/x-pnacl' to work for tizen didn't bother to correctly do it.
  35:     }
  36:     console.log('%c[updateStatus, common.js]', 'color: gray;', 'mimetype: ' + mimetype);
  37:     return mimetype;
  38:   }
  39:
  40:   /**
  41:    * Check if the browser supports NaCl plugins.
  42:    *
  43:    * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
  44:    * @return {bool} True if the browser supports the type of NaCl plugin
  45:    * produced by the given toolchain.
  46:    */
  47:   function browserSupportsNaCl(tool) {
  48:     // Assume host toolchains always work with the given browser.
  49:     // The below mime-type checking might not work with
  50:     // --register-pepper-plugins.
  51:     if (isHostToolchain(tool)) {
  52:       return true;
  53:     }
  54:     console.log('%c[browserSupportsNaCl, common.js]', 'color: gray;', 'is NOT host toolchain for tool: ' + tool);
  55:     var mimetype = mimeTypeForTool(tool);
  56:     console.log('%c[browserSupportsNaCl, common.js]', 'color: gray;', 'mimetypes: ' + mimetype);
  57:     return navigator.mimeTypes[mimetype] !== undefined;
  58:   }
  59:
  60:   /**
  61:    * Create the Native Client <embed> element as a child of the DOM element
  62:    * named "listener".
  63:    *
  64:    * @param {string} name The name of the example.
  65:    * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
  66:    * @param {string} path Directory name where .nmf file can be found.
  67:    * @param {number} width The width to create the plugin.
  68:    * @param {number} height The height to create the plugin.
  69:    * @param {Object} attrs Dictionary of attributes to set on the module.
  70:    */
  71:   function createNaClModule(name, tool, path, width, height, attrs) {
  72:     console.log('Parameters:', name, tool, path, width, height, attrs);
  73:     var moduleEl = document.createElement('embed');
  74:     moduleEl.setAttribute('name', 'nacl_module');
  75:     moduleEl.setAttribute('id', 'nacl_module');
  76:     moduleEl.setAttribute('width', width);
  77:     moduleEl.setAttribute('height', height);
  78:     moduleEl.setAttribute('path', path);
  79:     moduleEl.setAttribute('src', path + '/' + name + '.nmf');
  80:
  81:     // Add any optional arguments
  82:     if (attrs) {
  83:       for (var key in attrs) {
  84:         moduleEl.setAttribute(key, attrs[key]);
  85:         console.log('After setting ' + key + ':', moduleEl);
  86:       }
  87:     }
  88:     var mimetype = mimeTypeForTool(tool);
  89:     moduleEl.setAttribute('type', mimetype);
  90:     console.log('%c[createNaClModule, common.js]', 'color: gray;', "moduleEl: " + moduleEl.outerHTML);
  91:
  92:     // The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
  93:     // and a 'message' event listener attached.  This wrapping method is used
  94:     // instead of attaching the event listeners directly to the <EMBED> element
  95:     // to ensure that the listeners are active before the NaCl module 'load'
  96:     // event fires.
  97:     var listenerDiv = document.getElementById('listener');
  98:     listenerDiv.appendChild(moduleEl);
  99:
 100:     // Request the offsetTop property to force a relayout. As of Apr 10, 2014
 101:     // this is needed if the module is being loaded on a Chrome App's
 102:     // background page (see crbug.com/350445).
 103:     moduleEl.offsetTop;
 104:
 105:     // Host plugins don't send a moduleDidLoad message. We'll fake it here.
 106:     var isHost = isHostToolchain(tool);
 107:     if (isHost) {
 108:       window.setTimeout(function () {
 109:         moduleEl.readyState = 1;
 110:         moduleEl.dispatchEvent(new CustomEvent('loadstart'));
 111:         moduleEl.readyState = 4;
 112:         moduleEl.dispatchEvent(new CustomEvent('load'));
 113:         moduleEl.dispatchEvent(new CustomEvent('loadend'));
 114:       }, 100); // 100 ms
 115:     }
 116:   }
 117:
 118:   /**
 119:    * Add the default "load" and "message" event listeners to the element with
 120:    * id "listener".
 121:    *
 122:    * The "load" event is sent when the module is successfully loaded. The
 123:    * "message" event is sent when the naclModule posts a message using
 124:    * PPB_Messaging.PostMessage() (in C) or pp::Instance().PostMessage() (in
 125:    * C++).
 126:    */
 127:   function attachDefaultListeners() {
 128:     var listenerDiv = document.getElementById('listener');
 129:     listenerDiv.addEventListener('load', moduleDidLoad, true);
 130:     listenerDiv.addEventListener('message', handleMessage, true);
 131:     listenerDiv.addEventListener('error', handleError, true);
 132:     listenerDiv.addEventListener('crash', handleCrash, true);
 133:     if (typeof window.attachListeners !== 'undefined') {
 134:       window.attachListeners();
 135:     }
 136:   }
 137:
 138:   /**
 139:    * Called when the NaCl module fails to load.
 140:    *
 141:    * This event listener is registered in createNaClModule above.
 142:    */
 143:   function handleError(event) {
 144:     // We can't use common.naclModule yet because the module has not been
 145:     // loaded.
 146:     console.log('%c[handleError, common.js]', 'color: red;', event);
 147:     var moduleEl = document.getElementById('nacl_module');
 148:     updateStatus('ERROR [' + moduleEl.lastError + ']');
 149:   }
 150:
 151:   /**
 152:    * Called when the Browser can not communicate with the Module
 153:    *
 154:    * This event listener is registered in attachDefaultListeners above.
 155:    */
 156:   function handleCrash(event) {
 157:     console.log('%c[handleCrash, common.js]', 'color: red;', event);
 158:     if (common.naclModule.exitStatus == -1) {
 159:       updateStatus('CRASHED');
 160:     } else {
 161:       updateStatus('EXITED [' + common.naclModule.exitStatus + ']');
 162:     }
 163:     if (typeof window.handleCrash !== 'undefined') {
 164:       window.handleCrash(common.naclModule.lastError);
 165:     }
 166:   }
 167:
 168:   /**
 169:    * Called when the NaCl module is loaded.
 170:    *
 171:    * This event listener is registered in attachDefaultListeners above.
 172:    */
 173:   function moduleDidLoad() {
 174:     common.naclModule = document.getElementById('nacl_module');
 175:     updateStatus('RUNNING');
 176:     if (typeof window.moduleDidLoad !== 'undefined') {
 177:       window.moduleDidLoad();
 178:     }
 179:   }
 180:
 181:   /**
 182:    * Hide the NaCl module's embed element.
 183:    *
 184:    * We don't want to hide by default; if we do, it is harder to determine that
 185:    * a plugin failed to load. Instead, call this function inside the example's
 186:    * "moduleDidLoad" function.
 187:    *
 188:    */
 189:   function hideModule() {
 190:     // Setting common.naclModule.style.display = "None" doesn't work; the
 191:     // module will no longer be able to receive postMessages.
 192:     common.naclModule.style.height = '0';
 193:   }
 194:
 195:   /**
 196:    * Remove the NaCl module from the page.
 197:    */
 198:   function removeModule() {
 199:     console.log('%c[removeModule, common.js]', 'color: gray;', "removing module...");
 200:     common.naclModule.parentNode.removeChild(common.naclModule);
 201:     common.naclModule = null;
 202:   }
 203:
 204:   /**
 205:    * Return true when |s| starts with the string |prefix|.
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== COMMON.JS: ОБРАБОТЧИКИ И СОЗДАНИЕ МОДУЛЯ ====="

===== COMMON.JS: ОБРАБОТЧИКИ И СОЗДАНИЕ МОДУЛЯ =====
PS C:\Users\str> Show-Range (Join-Path $js 'common.js') 75 135
  75:     moduleEl.setAttribute('id', 'nacl_module');
  76:     moduleEl.setAttribute('width', width);
  77:     moduleEl.setAttribute('height', height);
  78:     moduleEl.setAttribute('path', path);
  79:     moduleEl.setAttribute('src', path + '/' + name + '.nmf');
  80:
  81:     // Add any optional arguments
  82:     if (attrs) {
  83:       for (var key in attrs) {
  84:         moduleEl.setAttribute(key, attrs[key]);
  85:         console.log('After setting ' + key + ':', moduleEl);
  86:       }
  87:     }
  88:     var mimetype = mimeTypeForTool(tool);
  89:     moduleEl.setAttribute('type', mimetype);
  90:     console.log('%c[createNaClModule, common.js]', 'color: gray;', "moduleEl: " + moduleEl.outerHTML);
  91:
  92:     // The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
  93:     // and a 'message' event listener attached.  This wrapping method is used
  94:     // instead of attaching the event listeners directly to the <EMBED> element
  95:     // to ensure that the listeners are active before the NaCl module 'load'
  96:     // event fires.
  97:     var listenerDiv = document.getElementById('listener');
  98:     listenerDiv.appendChild(moduleEl);
  99:
 100:     // Request the offsetTop property to force a relayout. As of Apr 10, 2014
 101:     // this is needed if the module is being loaded on a Chrome App's
 102:     // background page (see crbug.com/350445).
 103:     moduleEl.offsetTop;
 104:
 105:     // Host plugins don't send a moduleDidLoad message. We'll fake it here.
 106:     var isHost = isHostToolchain(tool);
 107:     if (isHost) {
 108:       window.setTimeout(function () {
 109:         moduleEl.readyState = 1;
 110:         moduleEl.dispatchEvent(new CustomEvent('loadstart'));
 111:         moduleEl.readyState = 4;
 112:         moduleEl.dispatchEvent(new CustomEvent('load'));
 113:         moduleEl.dispatchEvent(new CustomEvent('loadend'));
 114:       }, 100); // 100 ms
 115:     }
 116:   }
 117:
 118:   /**
 119:    * Add the default "load" and "message" event listeners to the element with
 120:    * id "listener".
 121:    *
 122:    * The "load" event is sent when the module is successfully loaded. The
 123:    * "message" event is sent when the naclModule posts a message using
 124:    * PPB_Messaging.PostMessage() (in C) or pp::Instance().PostMessage() (in
 125:    * C++).
 126:    */
 127:   function attachDefaultListeners() {
 128:     var listenerDiv = document.getElementById('listener');
 129:     listenerDiv.addEventListener('load', moduleDidLoad, true);
 130:     listenerDiv.addEventListener('message', handleMessage, true);
 131:     listenerDiv.addEventListener('error', handleError, true);
 132:     listenerDiv.addEventListener('crash', handleCrash, true);
 133:     if (typeof window.attachListeners !== 'undefined') {
 134:       window.attachListeners();
 135:     }
 136:   }
 137:
 138:   /**
 139:    * Called when the NaCl module fails to load.
 140:    *
 141:    * This event listener is registered in createNaClModule above.
 142:    */
 143:   function handleError(event) {
 144:     // We can't use common.naclModule yet because the module has not been
 145:     // loaded.
 146:     console.log('%c[handleError, common.js]', 'color: red;', event);
 147:     var moduleEl = document.getElementById('nacl_module');
 148:     updateStatus('ERROR [' + moduleEl.lastError + ']');
 149:   }
 150:
 151:   /**
 152:    * Called when the Browser can not communicate with the Module
 153:    *
 154:    * This event listener is registered in attachDefaultListeners above.
 155:    */
 156:   function handleCrash(event) {
 157:     console.log('%c[handleCrash, common.js]', 'color: red;', event);
 158:     if (common.naclModule.exitStatus == -1) {
 159:       updateStatus('CRASHED');
 160:     } else {
 161:       updateStatus('EXITED [' + common.naclModule.exitStatus + ']');
 162:     }
 163:     if (typeof window.handleCrash !== 'undefined') {
 164:       window.handleCrash(common.naclModule.lastError);
 165:     }
 166:   }
 167:
 168:   /**
 169:    * Called when the NaCl module is loaded.
 170:    *
 171:    * This event listener is registered in attachDefaultListeners above.
 172:    */
 173:   function moduleDidLoad() {
 174:     common.naclModule = document.getElementById('nacl_module');
 175:     updateStatus('RUNNING');
 176:     if (typeof window.moduleDidLoad !== 'undefined') {
 177:       window.moduleDidLoad();
 178:     }
 179:   }
 180:
 181:   /**
 182:    * Hide the NaCl module's embed element.
 183:    *
 184:    * We don't want to hide by default; if we do, it is harder to determine that
 185:    * a plugin failed to load. Instead, call this function inside the example's
 186:    * "moduleDidLoad" function.
 187:    *
 188:    */
 189:   function hideModule() {
 190:     // Setting common.naclModule.style.display = "None" doesn't work; the
 191:     // module will no longer be able to receive postMessages.
 192:     common.naclModule.style.height = '0';
 193:   }
 194:
 195:   /**
 196:    * Remove the NaCl module from the page.
 197:    */
 198:   function removeModule() {
 199:     console.log('%c[removeModule, common.js]', 'color: gray;', "removing module...");
 200:     common.naclModule.parentNode.removeChild(common.naclModule);
 201:     common.naclModule = null;
 202:   }
 203:
 204:   /**
 205:    * Return true when |s| starts with the string |prefix|.
 206:    *
 207:    * @param {string} s The string to search.
 208:    * @param {string} prefix The prefix to search for in |s|.
 209:    */
PS C:\Users\str> Show-Range (Join-Path $js 'common.js') 125 205
 125:    * C++).
 126:    */
 127:   function attachDefaultListeners() {
 128:     var listenerDiv = document.getElementById('listener');
 129:     listenerDiv.addEventListener('load', moduleDidLoad, true);
 130:     listenerDiv.addEventListener('message', handleMessage, true);
 131:     listenerDiv.addEventListener('error', handleError, true);
 132:     listenerDiv.addEventListener('crash', handleCrash, true);
 133:     if (typeof window.attachListeners !== 'undefined') {
 134:       window.attachListeners();
 135:     }
 136:   }
 137:
 138:   /**
 139:    * Called when the NaCl module fails to load.
 140:    *
 141:    * This event listener is registered in createNaClModule above.
 142:    */
 143:   function handleError(event) {
 144:     // We can't use common.naclModule yet because the module has not been
 145:     // loaded.
 146:     console.log('%c[handleError, common.js]', 'color: red;', event);
 147:     var moduleEl = document.getElementById('nacl_module');
 148:     updateStatus('ERROR [' + moduleEl.lastError + ']');
 149:   }
 150:
 151:   /**
 152:    * Called when the Browser can not communicate with the Module
 153:    *
 154:    * This event listener is registered in attachDefaultListeners above.
 155:    */
 156:   function handleCrash(event) {
 157:     console.log('%c[handleCrash, common.js]', 'color: red;', event);
 158:     if (common.naclModule.exitStatus == -1) {
 159:       updateStatus('CRASHED');
 160:     } else {
 161:       updateStatus('EXITED [' + common.naclModule.exitStatus + ']');
 162:     }
 163:     if (typeof window.handleCrash !== 'undefined') {
 164:       window.handleCrash(common.naclModule.lastError);
 165:     }
 166:   }
 167:
 168:   /**
 169:    * Called when the NaCl module is loaded.
 170:    *
 171:    * This event listener is registered in attachDefaultListeners above.
 172:    */
 173:   function moduleDidLoad() {
 174:     common.naclModule = document.getElementById('nacl_module');
 175:     updateStatus('RUNNING');
 176:     if (typeof window.moduleDidLoad !== 'undefined') {
 177:       window.moduleDidLoad();
 178:     }
 179:   }
 180:
 181:   /**
 182:    * Hide the NaCl module's embed element.
 183:    *
 184:    * We don't want to hide by default; if we do, it is harder to determine that
 185:    * a plugin failed to load. Instead, call this function inside the example's
 186:    * "moduleDidLoad" function.
 187:    *
 188:    */
 189:   function hideModule() {
 190:     // Setting common.naclModule.style.display = "None" doesn't work; the
 191:     // module will no longer be able to receive postMessages.
 192:     common.naclModule.style.height = '0';
 193:   }
 194:
 195:   /**
 196:    * Remove the NaCl module from the page.
 197:    */
 198:   function removeModule() {
 199:     console.log('%c[removeModule, common.js]', 'color: gray;', "removing module...");
 200:     common.naclModule.parentNode.removeChild(common.naclModule);
 201:     common.naclModule = null;
 202:   }
 203:
 204:   /**
 205:    * Return true when |s| starts with the string |prefix|.
 206:    *
 207:    * @param {string} s The string to search.
 208:    * @param {string} prefix The prefix to search for in |s|.
 209:    */
 210:   function startsWith(s, prefix) {
 211:     // indexOf would search the entire string, lastIndexOf(p, 0) only checks at
 212:     // the first index. See: http://stackoverflow.com/a/4579228
 213:     return s.lastIndexOf(prefix, 0) === 0;
 214:   }
 215:
 216:   /** Maximum length of logMessageArray. */
 217:   var kMaxLogMessageLength = 20;
 218:
 219:   /** An array of messages to display in the element with id "log". */
 220:   var logMessageArray = [];
 221:
 222:   /**
 223:    * Add a message to an element with id "log".
 224:    *
 225:    * This function is used by the default "log:" message handler.
 226:    *
 227:    * @param {string} message The message to log.
 228:    */
 229:   function logMessage(message) {
 230:     logMessageArray.push(message);
 231:     if (logMessageArray.length > kMaxLogMessageLength) logMessageArray.shift();
 232:     document.getElementById('log').textContent = logMessageArray.join('\n');
 233:     console.log(message);
 234:   }
 235:
 236:   /**
 237:    */
 238:   var defaultMessageTypes = {
 239:     'alert': alert,
 240:     'log': logMessage
 241:   };
 242:
 243:   /**
 244:    * Called when the NaCl module sends a message to JavaScript (via
 245:    * PPB_Messaging.PostMessage())
 246:    *
 247:    * This event listener is registered in createNaClModule above.
 248:    *
 249:    * @param {Event} message_event A message event. message_event.data contains
 250:    *     the data sent from the NaCl module.
 251:    */
 252:   function handleMessage(message_event) {
 253:     if (typeof message_event.data === 'string') {
 254:       for (var type in defaultMessageTypes) {
 255:         if (defaultMessageTypes.hasOwnProperty(type)) {
 256:           if (startsWith(message_event.data, type + ':')) {
 257:             func = defaultMessageTypes[type];
 258:             func(message_event.data.slice(type.length + 1));
 259:             return;
 260:           }
 261:         }
 262:       }
 263:     }
 264:     if (typeof window.handleMessage !== 'undefined') {
 265:       window.handleMessage(message_event);
 266:     } else {
 267:       logMessage('Unhandled message: ' + message_event.data);
 268:     }
 269:   }
 270:
 271:   /**
 272:    * Called when the DOM content has loaded; i.e. the page's document is fully
 273:    * parsed. At this point, we can safely query any elements in the document via
 274:    * document.querySelector, document.getElementById, etc.
 275:    *
 276:    * @param {string} name The name of the example.
 277:    * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
 278:    * @param {string} path Directory name where .nmf file can be found.
 279:    * @param {number} width The width to create the plugin.
 280:    * @param {number} height The height to create the plugin.
 281:    * @param {Object} attrs Optional dictionary of additional attributes.
 282:    */
 283:   function domContentLoaded(name, tool, path, width, height, attrs) {
 284:     // If the page loads before the Native Client module loads, then set the
 285:     // status message indicating that the module is still loading.  Otherwise,
 286:     // do not change the status message.
 287:     updateStatus('Page loaded.');
 288:     if (!browserSupportsNaCl(tool)) {
 289:       updateStatus('Browser does not support NaCl (' + tool + '), or NaCl is disabled');
 290:     } else if (common.naclModule == null) {
 291:       updateStatus('Browser supports NaCl (' + tool + ')');
 292:       updateStatus('Creating embed: ' + tool);
 293:
 294:       // We use a non-zero sized embed to give Chrome space to place the bad
 295:       // plug-in graphic, if there is a problem.
 296:       width = typeof width !== 'undefined' ? width : 200;
 297:       height = typeof height !== 'undefined' ? height : 200;
 298:       attachDefaultListeners();
 299:       createNaClModule(name, tool, path, width, height, attrs);
 300:     } else {
 301:       // It's possible that the Native Client module onload event fired
 302:       // before the page's onload event.  In this case, the status message
 303:       // will reflect 'SUCCESS', but won't be displayed.  This call will
 304:       // display the current message.
 305:       updateStatus('Waiting.');
 306:     }
 307:   }
 308:
 309:   /** Saved text to display in the element with id 'statusField'. */
 310:   var statusText = 'NO-STATUSES';
 311:
 312:   /**
 313:    * Set the global status message. If the element with id 'statusField'
 314:    * exists, then set its HTML to the status message as well.
 315:    *
 316:    * @param {string} opt_message The message to set. If null or undefined, then
 317:    *     set element 'statusField' to the message from the last call to
 318:    *     updateStatus.
 319:    */
 320:   function updateStatus(opt_message) {
 321:     if (opt_message) {
 322:       statusText = opt_message;
 323:       console.log('%c[updateStatus, common.js]', 'color: gray;', statusText);
 324:     }
 325:     var statusField = document.getElementById('statusField');
 326:     if (statusField) {
 327:       statusField.innerHTML = statusText;
 328:     }
 329:   }
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== MESSAGES.JS: ЗАГРУЗКА СЕРТИФИКАТОВ ====="

===== MESSAGES.JS: ЗАГРУЗКА СЕРТИФИКАТОВ =====
PS C:\Users\str> Show-Range (Join-Path $js 'messages.js') 1 125
   1: var callbacks = {};
   2: var callbacks_ids = 1;
   3:
   4: /**
   5:  * var sendMessage - Sends a message with arguments to the NaCl module
   6:  *
   7:  * @param  {String} method A named method
   8:  * @param  {(String|Array)} params An array of options or a signle string
   9:  * @return {void}        The NaCl module calls back trought the handleMessage method
  10:  */
  11: var sendMessage = function sendMessage(method, params) {
  12:   return new Promise(function (resolve, reject) {
  13:     var id = callbacks_ids++;
  14:     callbacks[id] = {
  15:       'resolve': resolve,
  16:       'reject': reject
  17:     };
  18:     common.naclModule.postMessage({
  19:       'callbackId': id,
  20:       'method': method,
  21:       'params': params
  22:     });
  23:   });
  24: };
  25:
  26: /**
  27:  * handleMessage - Handles messages from the NaCl module
  28:  *
  29:  * @param  {Object} msg An object given by the NaCl module
  30:  * @return {void}
  31:  */
  32: function handleMessage(msg) {
  33:   if (msg.data.callbackId && callbacks[msg.data.callbackId]) {
  34:     // if it's a callback, treat it as such
  35:     callbacks[msg.data.callbackId][msg.data.type](msg.data.ret);
  36:     delete callbacks[msg.data.callbackId];
  37:   } else {
  38:     // else, it's just info, or an event
  39:     console.log('%c[messages.js, handleMessage]', 'color:gray;', 'Message data: ', msg.data);
  40:     if (msg.data.indexOf('streamTerminated: ') === 0) {
  41:       // if it's a recognized event, notify the appropriate function
  42:       // Release our keep awake request
  43:       // if (runningOnChrome) {
  44:       //   chrome.power.releaseKeepAwake();
  45:       // }
  46:
  47:       // Show a termination snackbar message if the termination was unexpected
  48:       var errorCode = parseInt(msg.data.replace('streamTerminated: ', ''));
  49:       switch (errorCode) {
  50:         case 0:
  51:           // ML_ERROR_GRACEFUL_TERMINATION
  52:           break;
  53:         case -100:
  54:           // ML_ERROR_NO_VIDEO_TRAFFIC
  55:           snackbarLogLong("No video received from host. Check the host PC's firewall and port forwarding rules.");
  56:           break;
  57:         case -101:
  58:           // ML_ERROR_NO_VIDEO_FRAME
  59:           snackbarLogLong("Your network connection isn't performing well. Reduce your video bitrate setting or try a faster connection.");
  60:           break;
  61:         default:
  62:           snackbarLogLong("Connection terminated");
  63:           break;
  64:       }
  65:       api.refreshServerInfo().then(function (ret) {
  66:         // Return to app list with new currentgame
  67:         showApps(api);
  68:       }, function () {
  69:         // Return to app list anyway
  70:         showApps(api);
  71:       });
  72:     } else if (msg.data === 'Connection Established') {
  73:       $('#loadingSpinner').css('display', 'none');
  74:       $('body').css('backgroundColor', 'black');
  75:
  76:       // Keep the display awake while streaming
  77:       // if (runningOnChrome) {
  78:       //   chrome.power.requestKeepAwake("display");
  79:       // }
  80:     } else if (msg.data.indexOf('ProgressMsg: ') === 0) {
  81:       $('#loadingMessage').text(msg.data.replace('ProgressMsg: ', ''));
  82:     } else if (msg.data.indexOf('TransientMsg: ') === 0) {
  83:       snackbarLog(msg.data.replace('TransientMsg: ', ''));
  84:     } else if (msg.data.indexOf('DialogMsg: ') === 0) {
  85:       // FIXME: Really use a dialog
  86:       snackbarLogLong(msg.data.replace('DialogMsg: ', ''));
  87:     } else if (msg.data === 'displayVideo') {
  88:       // Show the video stream now
  89:       $("#nacl_module")[0].style.opacity = 1.0;
  90:     } else if (msg.data.indexOf('controllerRumble: ') === 0) {
  91:       var eventData = msg.data.split(' ')[1].split(',');
  92:       var gamepadIdx = parseInt(eventData[0]);
  93:       var weakMagnitude = parseFloat(eventData[1]);
  94:       var strongMagnitude = parseFloat(eventData[2]);
  95:       console.log("Playing rumble on gamepad " + gamepadIdx + " with weakMagnitude " + weakMagnitude + " and strongMagnitude " + strongMagnitude);
  96:
  97:       // We may not actually have a gamepad at this index.
  98:       // Even if we do have a gamepad, it may not have a vibrationActuator associated with it.
  99:       gamepad.vibrationActuator.playEffect('dual-rumble', {
 100:         startDelay: 0,
 101:         duration: 5000,
 102:         // Moonlight should be sending another rumble event when stopping.
 103:         weakMagnitude: weakMagnitude,
 104:         strongMagnitude: strongMagnitude
 105:       });
 106:     }
 107:   }
 108: }
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== INDEX.JS: ЗАПУСК И ВОССТАНОВЛЕНИЕ UI ====="

===== INDEX.JS: ЗАПУСК И ВОССТАНОВЛЕНИЕ UI =====
PS C:\Users\str> Show-Range (Join-Path $js 'index.js') 55 115
  55:         return Navigation.left();
  56:       },
  57:       15: function _() {
  58:         return Navigation.right();
  59:       }
  60:     };
  61:     if (gamepadMapping[key]) {
  62:       gamepadMapping[key]();
  63:     }
  64:   });
  65: }
  66: function changeUiModeForNaClLoad() {
  67:   $('#main-navigation').children().hide();
  68:   $("#main-content").children().not("#listener, #naclSpinner").hide();
  69:   $('#naclSpinnerMessage').text('Loading Moonlight plugin...');
  70:   $('#naclSpinner').css('display', 'inline-block');
  71: }
  72: function startPollingHosts() {
  73:   for (var hostUID in hosts) {
  74:     beginBackgroundPollingOfHost(hosts[hostUID]);
  75:   }
  76: }
  77: function stopPollingHosts() {
  78:   for (var hostUID in hosts) {
  79:     stopBackgroundPollingOfHost(hosts[hostUID]);
  80:   }
  81: }
  82: function restoreUiAfterNaClLoad() {
  83:   $('#main-navigation').children().not("#quitCurrentApp").show();
  84:   $("#main-content").children().not("#listener, #naclSpinner, #game-grid").show();
  85:   $('#naclSpinner').hide();
  86:   $('#loadingSpinner').css('display', 'none');
  87:   Navigation.push(Views.Hosts);
  88:   showHostsAndSettingsMode();
  89:   findNvService(function (finder, opt_error) {
  90:     if (finder.byService_['_nvstream._tcp']) {
  91:       var ips = Object.keys(finder.byService_['_nvstream._tcp']);
  92:       for (var i in ips) {
  93:         var ip = ips[i];
  94:         if (finder.byService_['_nvstream._tcp'][ip]) {
  95:           var mDnsDiscoveredHost = new NvHTTP(ip, myUniqueid);
  96:           mDnsDiscoveredHost.pollServer(function (returneMdnsDiscoveredHost) {
  97:             // Just drop this if the host doesn't respond
  98:             if (!returneMdnsDiscoveredHost.online) {
  99:               return;
 100:             }
 101:             if (hosts[returneMdnsDiscoveredHost.serverUid] != null) {
 102:               // if we're seeing a host we've already seen before, update it for the current local IP.
 103:               hosts[returneMdnsDiscoveredHost.serverUid].address = returneMdnsDiscoveredHost.address;
 104:               hosts[returneMdnsDiscoveredHost.serverUid].updateExternalAddressIP4();
 105:             } else {
 106:               // Host must be in the grid before starting background polling
 107:               addHostToGrid(returneMdnsDiscoveredHost, true);
 108:               beginBackgroundPollingOfHost(returneMdnsDiscoveredHost);
 109:             }
 110:             saveHosts();
 111:           });
 112:         }
 113:       }
 114:     }
 115:   });
 116: }
 117: function beginBackgroundPollingOfHost(host) {
 118:   var el = document.querySelector('#hostgrid-' + host.serverUid);
 119:   if (host.online) {
 120:     el.classList.remove('host-cell-inactive');
 121:     // The host was already online. Just start polling in the background now.
 122:     activePolls[host.serverUid] = window.setInterval(function () {
 123:       // every 5 seconds, poll at the address we know it was live at
 124:       host.pollServer(function () {
 125:         if (host.online) {
 126:           el.classList.remove('host-cell-inactive');
 127:         } else {
 128:           el.classList.add('host-cell-inactive');
 129:         }
 130:       });
 131:     }, 5000);
 132:   } else {
 133:     el.classList.add('host-cell-inactive');
 134:     // The host was offline, so poll immediately.
 135:     host.pollServer(function () {
 136:       if (host.online) {
 137:         el.classList.remove('host-cell-inactive');
 138:       } else {
 139:         el.classList.add('host-cell-inactive');
 140:       }
 141:
 142:       // Now start background polling
 143:       activePolls[host.serverUid] = window.setInterval(function () {
 144:         // every 5 seconds, poll at the address we know it was live at
 145:         host.pollServer(function () {
 146:           if (host.online) {
 147:             el.classList.remove('host-cell-inactive');
 148:           } else {
 149:             el.classList.add('host-cell-inactive');
 150:           }
 151:         });
 152:       }, 5000);
 153:     });
 154:   }
 155: }
 156: function stopBackgroundPollingOfHost(host) {
 157:   console.log('%c[index.js, backgroundPolling]', 'color: green;', 'Stopping background polling of host ' + host.serverUid + '\n', host, host.toString()); //Logging both object (for console) and toString-ed object (for text logs)
 158:   window.clearInterval(activePolls[host.serverUid]);
 159:   delete activePolls[host.serverUid];
 160: }
 161: function snackbarLog(givenMessage) {
 162:   console.log('%c[index.js, snackbarLog]', 'color: green;', givenMessage);
 163:   var data = {
 164:     message: givenMessage,
 165:     timeout: 2000
 166:   };
 167:   document.querySelector('#snackbar').MaterialSnackbar.showSnackbar(data);
 168: }
 169: function snackbarLogLong(givenMessage) {
PS C:\Users\str> Show-Range (Join-Path $js 'index.js') 680 785
 680:         var status_code = $root.attr('status_code');
 681:         if (status_code != 200) {
 682:           var status_message = $root.attr('status_message');
 683:           if (status_code == 4294967295 && status_message == 'Invalid') {
 684:             // Special case handling an audio capture error which GFE doesn't
 685:             // provide any useful status message for.
 686:             status_code = 418;
 687:             status_message = 'Missing audio capture device. Reinstall GeForce Experience.';
 688:           }
 689:           snackbarLog('Error ' + status_code + ': ' + status_message);
 690:           showApps(host);
 691:           return;
 692:         }
 693:         sendMessage('startRequest', [host.address, streamWidth, streamHeight, frameRate, bitrate.toString(), rikey, rikeyid.toString(), mouse_lock_enabled, host.appVersion, host.gfeVersion, $root.find('sessionUrl0').text().trim()]);
 694:       }, function (failedLaunchApp) {
 695:         console.error('%c[index.js, launchApp]', 'color: green;', 'Failed to launch app width id: ' + appID + '\nReturned error was: ' + failedLaunchApp);
 696:         showApps(host);
 697:         return;
 698:       });
 699:     });
 700:   });
 701: }
 702: function playGameMode() {
 703:   console.log('%c[index.js, playGameMode]', 'color:green;', 'Entering play game mode');
 704:   isInGame = true;
 705:   $("#main-navigation").hide();
 706:   $("#main-content").children().not("#listener, #loadingSpinner").hide();
 707:   $("#main-content").addClass("fullscreen");
 708:   $("#listener").addClass("fullscreen");
 709:
 710:   // Hide the NaCl module until the first frame is rendered
 711:   $("#nacl_module")[0].style.opacity = 0.0;
 712:   fullscreenNaclModule();
 713:   $('#loadingSpinner').css('display', 'inline-block');
 714:   Navigation.stop();
 715: }
 716:
 717: // Maximize the size of the nacl module by scaling and resizing appropriately
 718: function fullscreenNaclModule() {
 719:   var streamWidth = $('#selectResolution').data('value').split(':')[0];
 720:   var streamHeight = $('#selectResolution').data('value').split(':')[1];
 721:   var screenWidth = window.innerWidth;
 722:   var screenHeight = window.innerHeight;
 723:   var xRatio = screenWidth / streamWidth;
 724:   var yRatio = screenHeight / streamHeight;
 725:   var zoom = Math.min(xRatio, yRatio);
 726:   var module = $("#nacl_module")[0];
 727:   module.width = zoom * streamWidth;
 728:   module.height = zoom * streamHeight;
 729:   module.style.marginTop = (screenHeight - module.height) / 2 + "px";
 730: }
 731: function stopGameWithConfirmation() {
 732:   if (api.currentGame === 0) {
 733:     snackbarLog('Nothing was running');
 734:   } else {
 735:     api.getAppById(api.currentGame).then(function (currentGame) {
 736:       var quitAppDialog = document.querySelector('#quitAppDialog');
 737:       document.getElementById('quitAppDialogText').innerHTML = ' Are you sure you would like to quit ' + currentGame.title + '?  Unsaved progress will be lost.';
 738:       quitAppDialog.showModal();
 739:       Navigation.push(Views.CloseAppDialog);
 740:       $('#cancelQuitApp').off('click');
 741:       $('#cancelQuitApp').on('click', function () {
 742:         console.log('%c[index.js, stopGameWithConfirmation]', 'color:green;', 'Closing app dialog, and returning');
 743:         quitAppDialog.close();
 744:         Navigation.pop();
 745:       });
 746:       $('#continueQuitApp').off('click');
 747:       $('#continueQuitApp').on('click', function () {
 748:         console.log('%c[index.js, stopGameWithConfirmation]', 'color:green;', 'Stopping game, and closing app dialog, and returning');
 749:         stopGame(api);
 750:         quitAppDialog.close();
 751:         Navigation.pop();
 752:       });
 753:     });
 754:   }
 755: }
 756: function stopGame(host, callbackFunction) {
 757:   isInGame = false;
 758:   if (!host.paired) {
 759:     return;
 760:   }
 761:   host.refreshServerInfo().then(function (ret) {
 762:     host.getAppById(host.currentGame).then(function (runningApp) {
 763:       if (!runningApp) {
 764:         snackbarLog('Nothing was running');
 765:         return;
 766:       }
 767:       var appName = runningApp.title;
 768:       snackbarLog('Stopping ' + appName);
 769:       host.quitApp().then(function (ret2) {
 770:         host.refreshServerInfo().then(function (ret3) {
 771:           // refresh to show no app is currently running.
 772:           showApps(host);
 773:           if (typeof callbackFunction === "function") callbackFunction();
 774:         }, function (failedRefreshInfo2) {
 775:           console.error('%c[index.js, stopGame]', 'color:green;', 'Failed to refresh server info! Returned error was:' + failedRefreshInfo + ' and failed server was:', host, host.toString());
 776:         });
 777:       }, function (failedQuitApp) {
 778:         console.error('%c[index.js, stopGame]', 'color:green;', 'Failed to quit app! Returned error was:' + failedQuitApp);
 779:       });
 780:     }, function (failedGetApp) {
 781:       console.error('%c[index.js, stopGame]', 'color:green;', 'Failed to get app ID! Returned error was:' + failedRefreshInfo);
 782:     });
 783:   }, function (failedRefreshInfo) {
 784:     console.error('%c[index.js, stopGame]', 'color:green;', 'Failed to refresh server info! Returned error was:' + failedRefreshInfo);
 785:   });
 786: }
 787: var indexedDB = null;
 788: var dbVersion = 1.0;
 789: var db = null;
 790: var dbName = 'GameStreamingDB';
 791: var storeName = 'GameStreamingStore';
 792:
 793: // Based on example from
 794: // https://hacks.mozilla.org/2012/02/storing-images-and-files-in-indexeddb/
 795: function createObjectStore(dataBase) {
 796:   if (!dataBase.objectStoreNames.contains(storeName)) {
 797:     dataBase.createObjectStore(storeName);
 798:   }
 799: }
 800: function openIndexDB(callback) {
 801:   if (db) {
 802:     // Database already opened
 803:     callback();
 804:     return;
 805:   }
 806:   console.log('Opening IndexDB');
 807:   if (!indexedDB) {
 808:     indexedDB = self.indexedDB;
 809:   }
 810:
 811:   // Create/open database
 812:   var request = indexedDB.open(dbName, dbVersion);
 813:   request.onerror = function (event) {
 814:     console.error('Error creating/accessing IndexedDB database', event);
 815:   };
 816:   request.onsuccess = function (event) {
 817:     console.log('Success creating/accessing IndexedDB database');
 818:     db = request.result;
 819:     db.onerror = function (event) {
 820:       console.error('Error creating/accessing IndexedDB database', event);
 821:     };
 822:     callback();
 823:   };
 824:   request.onupgradeneeded = function (event) {
 825:     createObjectStore(event.target.result);
 826:   };
 827: }
 828: function callCb(key, value, callbackFunction) {
 829:   var obj = {};
 830:   obj[key] = value;
 831:   callbackFunction(obj);
 832: }
 833: function getData(key, callbackFunction) {
 834:   var cb = function cb() {
 835:     try {
 836:       var transaction = db.transaction(storeName, 'readonly');
 837:       var readRequest = transaction.objectStore(storeName).get(key);
 838:
 839:       // Retrieve the data that was stored
 840:       readRequest.onsuccess = function (event) {
 841:         console.log('Read data from the DB key: ' + key + ' value: ' + readRequest.result);
 842:         var value = null;
 843:         if (readRequest.result) {
 844:           value = JSON.parse(readRequest.result);
 845:         }
 846:         callCb(key, value, callbackFunction);
 847:       };
 848:       transaction.onerror = function (e) {
 849:         console.error('Error reading data at key: "' + key + '" from IndexDB: ' + e);
 850:         callCb(key, value, callbackFunction);
 851:       };
 852:     } catch (e) {
 853:       console.log('getData: caught exception while reading key:' + key);
 854:       console.error(e);
 855:       callCb(key, value, callbackFunction);
 856:     }
 857:   };
 858:   if (db) {
 859:     cb();
 860:   } else {
 861:     openIndexDB(cb);
 862:   }
 863: }
 864: function storeData(key, data, callbackFunction) {
 865:   var cb = function cb() {
 866:     try {
 867:       var transaction = db.transaction(storeName, 'readwrite'); //open a transaction to the database
 868:       var put = transaction.objectStore(storeName).put(JSON.stringify(data), key);
 869:       transaction.oncomplete = function (e) {
 870:         console.log('Data at key: ' + key + ' stored as: ' + JSON.stringify(data));
 871:         if (callbackFunction) {
 872:           callbackFunction();
 873:         }
 874:       };
 875:       transaction.onerror = function (e) {
 876:         console.error('Error storing data in IndexDB: ' + e);
 877:       };
 878:     } catch (e) {
 879:       console.log('storeData: caught exception while storing key:' + key);
 880:       console.error(e);
 881:     }
 882:   };
 883:   if (db) {
 884:     cb();
 885:   } else {
 886:     openIndexDB(cb);
 887:   }
 888: }
 889: function saveResolution() {
 890:   var chosenResolution = $(this).data('value');
 891:   $('#selectResolution').text($(this).text()).data('value', chosenResolution);
 892:   storeData('resolution', chosenResolution, null);
 893:   updateDefaultBitrate();
 894:   Navigation.pop();
 895: }
 896: function saveOptimize() {
 897:   // MaterialDesignLight uses the mouseup trigger, so we give it some time to change the class name before
 898:   // checking the new state
 899:   setTimeout(function () {
 900:     var chosenOptimize = $("#optimizeGamesSwitch").parent().hasClass('is-checked');
 901:     console.log('%c[index.js, saveOptimize]', 'color: green;', 'Saving optimize state : ' + chosenOptimize);
 902:     storeData('optimize', chosenOptimize, null);
 903:   }, 100);
 904: }
 905: function saveFramerate() {
 906:   var chosenFramerate = $(this).data('value');
 907:   $('#selectFramerate').text($(this).text()).data('value', chosenFramerate);
 908:   storeData('frameRate', chosenFramerate, null);
 909:   updateDefaultBitrate();
 910:   Navigation.pop();
 911: }
 912:
 913: // storing data in chrome.storage takes the data as an object, and shoves it into JSON to store
 914: // unfortunately, objects with function instances (classes) are stripped of their function instances when converted to a raw object
 915: // so we cannot forget to revive the object after we load it.
 916: function saveHosts() {
 917:   storeData('hosts', hosts, null);
 918: }
 919: function saveBitrate() {
 920:   storeData('bitrate', $('#bitrateSlider').val(), null);
 921: }
 922: function saveRemoteAudio() {
 923:   // MaterialDesignLight uses the mouseup trigger, so we give it some time to change the class name before
 924:   // checking the new state
 925:   setTimeout(function () {
 926:     var remoteAudioState = $("#remoteAudioEnabledSwitch").parent().hasClass('is-checked');
 927:     console.log('%c[index.js, saveRemoteAudio]', 'color: green;', 'Saving remote audio state : ' + remoteAudioState);
 928:     storeData('remoteAudio', remoteAudioState, null);
 929:   }, 100);
 930: }
 931: function saveMouseLock() {
 932:   // MaterialDesignLight uses the mouseup trigger, so we give it some time to change the class name before
 933:   // checking the new state
 934:   setTimeout(function () {
 935:     var mouseLockState = $("#mouseLockEnabledSwitch").parent().hasClass('is-checked');
 936:     console.log('%c[index.js, saveMouseLock]', 'color: green;', 'Saving mouse lock state : ' + mouseLockState);
 937:     storeData('mouseLock', mouseLockState, null);
 938:   }, 100);
 939: }
 940: function updateDefaultBitrate() {
 941:   var res = $('#selectResolution').data('value');
 942:   var frameRate = $('#selectFramerate').data('value').toString();
 943:
 944:   // These quality presets include video resolution like 480p, 720p, 1080p, 1440p, 2160p (4K) and video frame rate like 30 FPS and 60 FPS
 945:   if (res === "858:480") {
 946:     if (frameRate === "30") {
 947:       // 480p, 30 FPS
 948:       $('#bitrateSlider')[0].MaterialSlider.change('2');
 949:     } else {
 950:       // 480p, 60 FPS
 951:       $('#bitrateSlider')[0].MaterialSlider.change('4');
 952:     }
 953:   } else if (res === "1280:720") {
 954:     if (frameRate === "30") {
 955:       // 720p, 30 FPS
 956:       $('#bitrateSlider')[0].MaterialSlider.change('5');
 957:     } else {
 958:       // 720p, 60 FPS
 959:       $('#bitrateSlider')[0].MaterialSlider.change('10');
 960:     }
 961:   } else if (res === "1920:1080") {
 962:     if (frameRate === "30") {
 963:       // 1080p, 30 FPS
 964:       $('#bitrateSlider')[0].MaterialSlider.change('10');
 965:     } else {
 966:       // 1080p, 60 FPS
 967:       $('#bitrateSlider')[0].MaterialSlider.change('20');
 968:     }
 969:   } else if (res === "2560:1440") {
 970:     if (frameRate === "30") {
 971:       // 1440p, 30 FPS
 972:       $('#bitrateSlider')[0].MaterialSlider.change('20');
 973:     } else {
 974:       // 1440p, 60 FPS
 975:       $('#bitrateSlider')[0].MaterialSlider.change('40');
 976:     }
 977:   } else if (res === "3840:2160") {
 978:     if (frameRate === "30") {
 979:       // 2160p (4K), 30 FPS
 980:       $('#bitrateSlider')[0].MaterialSlider.change('40');
 981:     } else {
 982:       // 2160p (4K), 60 FPS
 983:       $('#bitrateSlider')[0].MaterialSlider.change('80');
 984:     }
 985:   } else {
 986:     // unrecognized option. In case someone screws with the JS to add custom resolutions
 987:     $('#bitrateSlider')[0].MaterialSlider.change('10');
 988:   }
 989:   updateBitrateField();
 990:   saveBitrate();
 991: }
 992: function initSamsungKeys() {
 993:   console.log('initializing keys');
 994:   var handler = {
 995:     initRemoteController: true,
 996:     buttonsToRegister: [
 997:     // https://developer.samsung.com/tv/develop/guides/user-interaction/keyboardime
 998:     'ColorF0Red',
 999:     // F1
1000:     'ColorF1Green',
1001:     // F2
1002:     'ColorF2Yellow',
1003:     // F3
1004:     'ColorF3Blue',
1005:     // F4
1006:     // Not working...
1007:     //'SmartHub',      // F5
1008:     'Source',
1009:     // F6
1010:     'ChannelList',
1011:     // F7
1012:     'ChannelDown',
1013:     // F11
1014:     'ChannelUp' // F12
1015:     ],
1016:     onKeydownListener: remoteControllerHandler
1017:   };
1018:   console.log('Initializing SamsungTV platform');
1019:   platformOnLoad(handler);
1020: }
1021: function onWindowLoad() {
1022:   console.log('%c[index.js]', 'color: green;', 'Moonlight\'s main window loaded');
1023:   // don't show the game selection div
1024:   $('#gameSelection').css('display', 'none');
1025:   console.log('load stored resolution prefs');
1026:   getData('resolution', function (previousValue) {
1027:     if (previousValue.resolution != null) {
1028:       $('.resolutionMenu li').each(function () {
1029:         if ($(this).data('value') === previousValue.resolution) {
1030:           $('#selectResolution').text($(this).text()).data('value', previousValue.resolution);
1031:         }
1032:       });
1033:     }
1034:   });
1035:   console.log('Load stored remote audio prefs');
1036:   getData('remoteAudio', function (previousValue) {
1037:     if (previousValue.remoteAudio == null) {
1038:       document.querySelector('#externalAudioBtn').MaterialIconToggle.uncheck();
1039:     } else if (previousValue.remoteAudio == false) {
1040:       document.querySelector('#externalAudioBtn').MaterialIconToggle.uncheck();
1041:     } else {
1042:       document.querySelector('#externalAudioBtn').MaterialIconToggle.check();
1043:     }
1044:   });
1045:   console.log('load stored framerate prefs');
1046:   getData('frameRate', function (previousValue) {
1047:     if (previousValue.frameRate != null) {
1048:       $('.framerateMenu li').each(function () {
1049:         if ($(this).data('value') === previousValue.frameRate) {
1050:           $('#selectFramerate').text($(this).text()).data('value', previousValue.frameRate);
1051:         }
1052:       });
1053:     }
1054:   });
1055:   console.log('load stored optimization prefs');
1056:   getData('optimize', function (previousValue) {
1057:     if (previousValue.optimize == null) {
1058:       document.querySelector('#optimizeGamesBtn').MaterialIconToggle.check();
1059:     } else if (previousValue.optimize == false) {
1060:       document.querySelector('#optimizeGamesBtn').MaterialIconToggle.uncheck();
1061:     } else {
1062:       document.querySelector('#optimizeGamesBtn').MaterialIconToggle.check();
1063:     }
1064:   });
1065:   console.log('load stored bitrate prefs');
1066:   getData('bitrate', function (previousValue) {
1067:     $('#bitrateSlider')[0].MaterialSlider.change(previousValue.bitrate != null ? previousValue.bitrate : '20');
1068:     updateBitrateField();
1069:   });
1070:   initSamsungKeys();
1071: }
1072: window.onload = onWindowLoad;
1073:
1074: // Required on TizenTV, to get gamepad events.
1075: window.addEventListener('gamepadconnected', function (event) {
1076:   var connectedGamepad = event.gamepad;
1077:   console.log('%c[index.js, gamepadconnected] gamepad connected: ', 'color: green;', connectedGamepad);
1078:   if (connectedGamepad.vibrationActuator) {
1079:     // Check if the gamepad supports rumble
1080:     console.log('Gamepad supports vibration.');
1081:     connectedGamepad.vibrationActuator.playEffect('dual-rumble', {
1082:       duration: 1000,
1083:       strongMagnitude: 1.0,
1084:       weakMagnitude: 1.0
1085:     });
1086:   } else {
1087:     console.log('Gamepad does not support vibration.');
1088:   }
1089: });
1090: window.addEventListener('gamepaddisconnected', function (event) {
1091:   console.log('%c[index.js, gamepaddisconnected] gamepad disconnected: ' + JSON.stringify(event.gamepad), event.gamepad);
1092: });
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== NAVIGATION.JS: ПОЛНАЯ ЛОГИКА ФОКУСА ====="

===== NAVIGATION.JS: ПОЛНАЯ ЛОГИКА ФОКУСА =====
PS C:\Users\str> Show-Range (Join-Path $js 'navigation.js') 1 75
   1: var hoveredClassName = 'hovered';
   2: var gameCard;
   3: function mark(value) {
   4:   var element = typeof value === 'string' ? document.getElementById(value) : value;
   5:   if (element) {
   6:     element.classList.add(hoveredClassName);
   7:     element.dispatchEvent(new Event('mouseenter'));
   8:     if (!element.classList.contains('mdl-menu__item')) {
   9:       //#FIXME: needed to properly show the restart dialog. Doesn't allow focus() for mdl-menu__item.
  10:       element.focus();
  11:     }
  12:   }
  13: }
  14: function unmark(value) {
  15:   var element = typeof value === 'string' ? document.getElementById(value) : value;
  16:   if (element) {
  17:     element.classList.remove(hoveredClassName);
  18:     element.dispatchEvent(new Event('mouseleave'));
  19:     if (!element.classList.contains('mdl-menu__item')) {
  20:       //#FIXME
  21:       element.blur();
  22:     }
  23:   }
  24: }
  25: function isPopupActive(id) {
  26:   return document.getElementById(id).parentNode.children[1].classList.contains('is-visible');
  27: }
  28: function ListView(func) {
  29:   this.index = 0;
  30:   this.func = func;
  31: }
  32: ListView.prototype.prev = function () {
  33:   var array = this.func();
  34:   unmark(array[this.index]);
  35:   this.index = (this.index - 1 + array.length) % array.length;
  36:   mark(array[this.index]);
  37:   return array[this.index];
  38: };
  39: ListView.prototype.next = function () {
  40:   var array = this.func();
  41:   unmark(array[this.index]);
  42:   this.index = (this.index + 1) % array.length;
  43:   mark(array[this.index]);
  44:   return array[this.index];
  45: };
  46: ListView.prototype.current = function () {
  47:   return this.func()[this.index];
  48: };
  49: function navigateGameCards(offset) {
  50:   //helper function for Apps view
  51:   var gameCards = Array.from(document.getElementById('game-grid').children);
  52:   var currentIndex = gameCards.indexOf(gameCard);
  53:   var newIndex = currentIndex + offset;
  54:   if (newIndex >= 0 && newIndex < gameCards.length) {
  55:     gameCard = gameCards[newIndex];
  56:     gameCard.focus();
  57:   }
  58: }
  59: var Views = {
  60:   Hosts: {
  61:     view: new ListView(function () {
  62:       return document.getElementById('host-grid').children;
  63:     }),
  64:     up: function up() {
  65:       Navigation.change(Views.HostsNav);
  66:     },
  67:     down: function down() {
  68:       if ($('#gamepad-grid').children().length > 0) {
  69:         Navigation.change(Views.Gamepads);
  70:       }
  71:     },
  72:     left: function left() {
  73:       this.view.prev();
  74:     },
  75:     right: function right() {
PS C:\Users\str> Show-Range (Join-Path $js 'navigation.js') 270 465
 270:   },
 271:   AppsNav: {
 272:     view: new ListView(function () {
 273:       return ['backIcon', 'quitCurrentApp'];
 274:     }),
 275:     down: function down() {
 276:       Navigation.change(Views.Apps);
 277:     },
 278:     left: function left() {
 279:       this.view.prev();
 280:     },
 281:     right: function right() {
 282:       this.view.next();
 283:     },
 284:     accept: function accept() {
 285:       document.getElementById(this.view.current()).click();
 286:     },
 287:     back: function back() {
 288:       document.getElementById('backIcon').click();
 289:     },
 290:     enter: function enter() {
 291:       document.getElementById(this.view.current()).focus();
 292:     },
 293:     leave: function leave() {
 294:       document.getElementById(this.view.current()).blur();
 295:     }
 296:   },
 297:   Apps: {
 298:     view: new ListView(function () {
 299:       return document.getElementById('game-grid').children;
 300:     }),
 301:     up: function up() {
 302:       var gameCards = Array.from(document.getElementById('game-grid').children);
 303:       var currentIndex = gameCards.indexOf(gameCard);
 304:       var cardsPerRow = Math.min(6, gameCards.length);
 305:       if (currentIndex >= cardsPerRow) {
 306:         navigateGameCards(-cardsPerRow);
 307:       } else {
 308:         Navigation.change(Views.AppsNav);
 309:       }
 310:     },
 311:     down: function down() {
 312:       var gameCards = Array.from(document.getElementById('game-grid').children);
 313:       var currentIndex = gameCards.indexOf(gameCard);
 314:       var cardsPerRow = Math.min(6, gameCards.length);
 315:       if (currentIndex + cardsPerRow < gameCards.length) {
 316:         navigateGameCards(cardsPerRow);
 317:       }
 318:     },
 319:     left: function left() {
 320:       navigateGameCards(-1);
 321:     },
 322:     right: function right() {
 323:       navigateGameCards(1);
 324:     },
 325:     accept: function accept() {
 326:       gameCard.click();
 327:     },
 328:     back: function back() {
 329:       document.getElementById('backIcon').click();
 330:     },
 331:     enter: function enter() {
 332:       if (gameCard) {
 333:         gameCard.focus();
 334:       }
 335:     },
 336:     leave: function leave() {
 337:       unmark(this.view.current());
 338:     }
 339:   },
 340:   CloseAppDialog: {
 341:     isActive: function isActive() {
 342:       return isDialogActive('quitAppDialog');
 343:     },
 344:     view: new ListView(function () {
 345:       return ['continueQuitApp', 'cancelQuitApp'];
 346:     }),
 347:     down: function down() {
 348:       Navigation.change(Views.Apps);
 349:     },
 350:     left: function left() {
 351:       this.view.prev();
 352:     },
 353:     right: function right() {
 354:       this.view.next();
 355:     },
 356:     accept: function accept() {
 357:       document.getElementById(this.view.current()).click();
 358:     },
 359:     back: function back() {
 360:       document.getElementById('cancelQuitApp').click();
 361:     },
 362:     enter: function enter() {
 363:       mark(this.view.current());
 364:     },
 365:     leave: function leave() {
 366:       unmark(this.view.current());
 367:     }
 368:   }
 369: };
 370: var Navigation = function () {
 371:   var hasFocus = false;
 372:   var Stack = function () {
 373:     var viewStack = [];
 374:     function push(view) {
 375:       if (get()) {
 376:         get().leave();
 377:       }
 378:       viewStack.push(view);
 379:       get().enter();
 380:     }
 381:     function change(view) {
 382:       if (viewStack.length > 0) {
 383:         get().leave();
 384:         viewStack[viewStack.length - 1] = view;
 385:         get().enter();
 386:       } else {
 387:         push(view);
 388:       }
 389:     }
 390:     function pop() {
 391:       if (viewStack.length > 1) {
 392:         get().leave();
 393:         viewStack.pop();
 394:         get().enter();
 395:       }
 396:     }
 397:     function get() {
 398:       return viewStack[viewStack.length - 1];
 399:     }
 400:     return {
 401:       get: get,
 402:       push: push,
 403:       change: change,
 404:       pop: pop
 405:     };
 406:   }();
 407:   var State = function () {
 408:     var running = false;
 409:     function start() {
 410:       if (!running) {
 411:         running = true;
 412:         window.addEventListener('mousemove', loseFocus);
 413:       }
 414:     }
 415:     function stop() {
 416:       if (running) {
 417:         running = false;
 418:         window.removeEventListener('mousemove', loseFocus);
 419:       }
 420:     }
 421:     function isRunning() {
 422:       return running;
 423:     }
 424:     return {
 425:       start: start,
 426:       stop: stop,
 427:       isRunning: isRunning
 428:     };
 429:   }();
 430:   function loseFocus() {
 431:     if (hasFocus) {
 432:       hasFocus = false;
 433:       if (Stack.get()) {
 434:         Stack.get().leave();
 435:       }
 436:     }
 437:   }
 438:   function focus() {
 439:     if (!hasFocus) {
 440:       hasFocus = true;
 441:       if (Stack.get()) {
 442:         Stack.get().enter();
 443:       }
 444:     }
 445:   }
 446:   function runOp(name) {
 447:     return function () {
 448:       if (!State.isRunning()) {
 449:         return;
 450:       }
 451:       if (!hasFocus) {
 452:         focus();
 453:         return;
 454:       }
 455:       var view = Stack.get();
 456:       if (view && typeof view[name] === 'function') {
 457:         view[name]();
 458:       }
 459:     };
 460:   }
 461:   return {
 462:     accept: runOp('accept'),
 463:     back: runOp('back'),
 464:     left: runOp('left'),
 465:     right: runOp('right'),
 466:     up: runOp('up'),
 467:     down: runOp('down'),
 468:     startBtn: runOp('startBtn'),
 469:     push: Stack.push,
 470:     change: Stack.change,
 471:     pop: Stack.pop,
 472:     start: State.start,
 473:     stop: State.stop
 474:   };
 475: }();
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== CSS: ВЕСЬ ФАЙЛ ====="

===== CSS: ВЕСЬ ФАЙЛ =====
PS C:\Users\str> Get-Content (Join-Path $root 'static\css\style.css') -Raw
body {
    background-color: #282C38;
    color: #fff;
    font-family: 'Roboto', sans-serif;
}
.page-title {
    font-weight: 300;
    font-size: 26px;
    margin-top: 20px;
    margin-bottom: 30px;
    width: 100%;
    text-align: center;
}
span.mdl-layout-title {
    font-weight: 300;
}
span.mdl-layout-title > span {
    font-weight: 900;
    color: #00A3C6;
}

.mdl-layout__header-row {
    padding: 0 80px 0 80px;
}

main {
    padding: 0 80px 0 80px;
}

@media screen and (max-width: 600px) {
    #host-grid .mdl-card {
        flex-basis: 50% !important;
    }
    #game-grid .mdl-card {
        height: 100px !important;
        width: 75px !important;
    }
}

@media screen and (min-width: 600px) and (max-width: 800px) {
    #host-grid .mdl-card {
        flex-basis: 33% !important;
    }
    #game-grid .mdl-card {
        height: 150px !important;
        width: 112.5px !important;
    }
}

@media screen and (min-width: 800px) and (max-width: 1024px) {
    main {
        padding: 0 72px 0 72px !important;
    }
    .mdl-layout__header-row {
        padding: 0 72px 0 72px !important;
    }
    #host-grid .mdl-card {
        flex-basis: 25% !important;
    }
    #game-grid .mdl-card {
        height: 200px !important;
        width: 150px !important;
    }
}

@media screen and (min-width: 1024px) {
    #game-grid .mdl-card {
        height: 300px !important;
        width: 225px !important;
    }
}

.nav-menu-parent {
    position: relative;
    margin: 0 4px;
}
.mdl-menu__outline {
    background-color: #333846;
    color: white;
}
.mdl-menu__item {
    color: white;
}
.mdl-menu__item:hover, .mdl-menu__item.hovered, .mdl-menu__item:focus {
    background-color: #00A3C6;
}
#bitrateField {
    width: 100px;
    height: 36px;
    overflow: hidden;
    text-transform: none;
}
.bitrateMenu {
    width: 170px;
}

.mdl-button {
    color: #fff !important;
}
.mdl-grid {
    width: 100%;
}
.mdl-cell {
    text-align: center;
    color:#fff;
    padding:25px;
}
#backIcon {
    position: fixed;
    left: 28px;
}
#backIcon:hover {
    cursor: pointer;
}
.mdl-dialog {
    width: 400px;
    background-color: #333846;
    color: white;
}
.mdl-dialog__title {
    padding: 24px 24px 0;
    margin: 0;
    font-size: 2.5rem;
}
.mdl-dialog__content {
    padding: 20px 24px 24px 24px;
    color: rgba(255, 255, 255, 0.90);
}
.mdl-textfield__input {
    border-bottom: 1px solid rgba(255, 255, 255, 0.90);
}
.mdl-textfield__label {
    color: rgba(255, 255, 255, 0.90);
}
#naclSpinner {
    display: none;
    z-index: 2;
    position: fixed;
    left: 50%;
    bottom: 50%;
    transform: translate(-50%, -50%);
    margin: 0 auto;
    text-align: center;
}
#loadingSpinner {
    display: none;
    z-index: 1;
    position: fixed;
    left: 50%;
    bottom: 50%;
    transform: translate(-50%, -50%);
    margin: 0 auto;
    color: white;
    text-align: center;
}
#snackButton {
    width: 0px;
    height: 0px;
    display: none;
}
.mdl-snackbar__text {
    padding: 14px 30px 14px 30px;
}
.mdl-snackbar {
    min-width:0px;
}
#host-grid, #game-grid {
    padding: 0;
    margin: 0;
    -webkit-flex: 1;
    flex: 1;
    display: -webkit-flex;
    display: flex;
    -webkit-flex-flow: row wrap;
    flex-flow: row wrap;
    -webkit-align-items: center;
    align-items: center;
    -webkit-justify-content: center;
    justify-content: center;
}
#host-grid .mdl-card {
    flex-basis: 15%;
}
#game-grid .mdl-card {
    position: relative;
    background: url('../res/placeholder_game.svg') rgba(29, 29, 29, 1) center/cover no-repeat;
}
#host-grid .mdl-card, #game-grid .mdl-card {
    text-align: center;
    width: 170px;
    height: 170px;
    min-height: 170px;
    margin: 15px;
    cursor: pointer;
    transition: all .2s ease-in-out;
    will-change: transform;
}
#host-grid .mdl-card:hover, #host-grid .mdl-card:focus, #host-grid .mdl-card:active, #game-grid .mdl-card:focus, #game-grid .mdl-card:active {
    transform: scale(1.15);
    outline : #00A3C6 solid 2px;;
}
#host-grid .mdl-card__title {
    padding: 0;
}
#host-grid .mdl-card__title .mdl-card__title-text {
    width: 100%;
    font-size: 14px;
    padding: 10px 15px;
    background-color: rgba(0,0,0,0.7);
}
#game-grid .mdl-card img {
    height: 100%;
    width: 100%;
    opacity: 0;
    transition: opacity .3s;
    z-index: -1;
}
#game-grid .mdl-card img.fade-in {
    opacity: 1;
}
#game-grid .game-title {
    position: absolute;
    bottom: 0;
    min-height: 20px;
    font-size: 13px;
    font-weight: 300;
    padding: 5px 0;
    width: 100%;
    color: white;
    text-align: center;
    background-color: rgba(0, 0, 0, 0.6);
}
#game-grid .game-title span {
    padding: 0 10px;
}
.add-host-card > .mdl-card__title {
    color: #fff;
    background:
            url('../res/ic_add_to_queue_white_48px.svg') center no-repeat #3E4454;
}
.host-container > .mdl-card__title {
    color: #fff;
    background:
            url('../res/ic_tv_white_48px.svg') center no-repeat #3E4454;
}
.mdl-card.hovered {
    background-color: initial;
    outline: #00A3C6 solid 2px;
    transform: scale(1.15);
}
#host-grid p {
    margin: 0;
}
#host-grid .mdl-textfield {
    width: 200px;
    margin-right: 20px;
    margin-bottom: 0px;
}
#host-grid #GFEHostIPField {
    padding-bottom: 0px;
}
#host-grid #selectHost {
    padding: 0px;
    line-height: 20px;
    height: 20px;
    margin-top: 10px;
    margin-bottom: 10px;
    margin-right: 20px;
    background: none;
}
#host-grid .button-holder {
    padding: 0px;
    margin-top: 10px;
    margin-bottom: 10px;
}
.remove-host {
    position:absolute;
    right:5px;
    top:5px;
    width:24px;
    height:24px;
    z-index: 5;
    background-image: url('../res/ic_remove_circle_outline_white_48px.svg');
    background-repeat: no-repeat;
    background-size: 20px 20px;
    background-position: center;
    border: 0;
    cursor: pointer;
}
/*.remove-host:hover, .remove-host:focus {
    background-image: url('\\static\\res\\ic_remove_circle_white_24px.svg');
}*/
#pairButton {
    margin: 3px;
}
#showAppsButton {
    margin: 3px;
}
#listener {
    overflow: hidden;
    margin-top: 0px;
    height: 0px;
}
.fullscreen {
    height: 100vh !important;
    overflow: hidden !important;
    margin: auto !important;
    padding: 0 !important;
    border: none !important;
}
.current-game {
    outline: auto #8BC34A;
}
.host-cell-inactive {
    outline: auto #F44336;
}
.host-cell:hover {
    cursor: pointer;
    color: #000 !important;
}
.cancel-current:hover {
    cursor: pointer;
}
.cancel-current {
    border: 2px solid white;
    padding: 5px;
    border-radius: 5px;
}
.cancel-current img {
    margin-right: 5px;
}
.cancel-current .game-title {
    color: #fff;
}
#nacl_module {
    cursor: none;
}
PS C:\Users\str>
PS C:\Users\str> Write-Host "`n===== CONFIG.XML: ПРИВИЛЕГИИ И ПРОФИЛЬ ====="

===== CONFIG.XML: ПРИВИЛЕГИИ И ПРОФИЛЬ =====
PS C:\Users\str> Select-String -Path (Join-Path $root 'config.xml') `
>>     -Pattern 'profile|privilege|feature|content|tizen:application' -Context 1,2

  C:\Temp\pepper42-build\wgt-staging\config.xml:4:  </access>
> C:\Temp\pepper42-build\wgt-staging\config.xml:5:  <tizen:application id="ChqHQExU28.MoonlightNaCl" package="ChqHQExU2
8" required_version="2.4" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:6:  <content src="index.html" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:7:  <feature name="http://tizen.org/feature/screen.size.all" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:8:  <feature name="http://www.samsungdforum.com/feature/Keyboard" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:9:  <feature name="http://www.samsungdforum.com/feature/Mouse" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:10:  <feature name="http://www.samsungdforum.com/feature/External_Devic
e" />
  C:\Temp\pepper42-build\wgt-staging\config.xml:11:  <icon src="icon.png" />
  C:\Temp\pepper42-build\wgt-staging\config.xml:12:  <tizen:metadata key="http://samsung.com/tv/metadata/prelaunch.supp
ort" value="true" />
  C:\Temp\pepper42-build\wgt-staging\config.xml:16:  <name>MoonlightNaCl</name>
> C:\Temp\pepper42-build\wgt-staging\config.xml:17:  <tizen:privilege name="http://developer.samsung.com/privilege/netw
ork.public" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:18:  <tizen:privilege name="http://tizen.org/privilege/application.laun
ch" />
> C:\Temp\pepper42-build\wgt-staging\config.xml:19:  <tizen:privilege name="http://tizen.org/privilege/tv.inputdevice"
/>
> C:\Temp\pepper42-build\wgt-staging\config.xml:20:  <tizen:profile name="tv-samsung" />
  C:\Temp\pepper42-build\wgt-staging\config.xml:21:  <tizen:setting screen-orientation="landscape" context-menu="enable
" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable" />
  C:\Temp\pepper42-build\wgt-staging\config.xml:22:</widget>


PS C:\Users\str>