<commit_before>"use strict";
var app = angular.module('SubmitQueueModule', ['ngMaterial', 'md.data.table', 'angular-toArrayFilter', 'angularMoment']);

app.config(['$compileProvider', function ($compileProvider) {
  $compileProvider.debugInfoEnabled(false);
}]);

app.controller('SQCntl', ['DataService', '$interval', '$location', SQCntl]);

function SQCntl(dataService, $interval, $location) {
  var self = this;
  self.prs = {};
  self.users = {};
  self.builds = {};
  self.health = {};
  self.lastMergeTime = Date();
  self.prQuerySearch = prQuerySearch;
  self.historyQuerySearch = historyQuerySearch;
  self.goToPerson = goToPerson;
  self.selectTab = selectTab;
  self.tabLoaded = {};
  self.queryNum = 0;
  self.selected = 1;
  self.OverallHealth = "";
  self.StartTime = "";
  self.StatOrder = "-Count";
  self.location = $location;

  // http://submit-queue.k8s.io/#?prDisplay=eparis&historyDisplay=15999
  //  will show all PRs opened by eparis and all historic decisions for PR #15999
  var vars = $location.search();
  self.prDisplayValue = vars.prDisplay;
  self.historyDisplayValue = vars.historyDisplay;

  var path = $location.path();
  if (path.length > 0) {
      switch (path) {
      case "/prs":
          self.selected=0;
          break;
      case "/queue":
          self.selected=1;
          break;
      case "/history":
          self.selected=2;
          break;
      case "/e2e":
          self.selected=3;
          break;
      case "/info":
          self.selected=4;
          break;
      default:
          console.log("unknown path: " + path);
          break;
      }
  }

  loadTab(self.selected);

  // Defer loading of data for a tab until it's actually needed.
  // This speeds up the first-boot experience a LOT.
  function loadTab() {
    // tab: [reloading-function, update interval in minutes, ...] (can repeat)
    var tabFunctionReloads = {
      0: [refreshPRs, 10],
      1: [refreshGithubE2E, 0.5, refreshSQStats, 30],
      2: [refreshHistoryPRs, 1],
      3: [refreshE2EHealth, 10],
      4: [refreshUsers, 15, refreshBotStats, 10],
    }
    if (self.tabLoaded[self.selected])
      return;
    var tabReload = tabFunctionReloads[self.selected];
    if (tabReload !== undefined) {
      for (var i = 0; i < tabReload.length; i += 2) {
        var func = tabReload[i];
        var updateIntervalMinutes = tabReload[i + 1];
        func();
        $interval(func, updateIntervalMinutes * 60 * 1000);
      }
      self.tabLoaded[self.selected] = true;
    }
  }

  // This data is shown in a top banner (when the Queue is blocked),
  // so it's always loaded.
  refreshGoogleInternalCI();
  $interval(refreshGoogleInternalCI, 60000);  // Refresh every minute

  // Request Avatars that are only as large necessary (CSS limits them to 40px)
  function fixPRAvatars(prs) {
    angular.forEach(prs, function(pr) {
      if (/^https:\/\/avatars.githubusercontent.com\/u\/\d+\?v=3$/.test(pr.AvatarURL)) {
        pr.AvatarURL += '&size=40';
      }
    });
  }

  function refreshPRs() {
    dataService.getData('prs').then(function successCallback(response) {
      var prs = response.data.PRStatus;
      fixPRAvatars(prs);
      self.prs = prs;
      self.prSearchTerms = getPRSearchTerms();
    }, function errorCallback(response) {
      console.log("Error: Getting SubmitQueue Status");
    });
  }

  function refreshGithubE2E() {
    dataService.getData('github-e2e-queue').then(function successCallback(response) {
      if (response.data.E2ERunning.Number == 0) {
        self.e2erunning = [];
      } else {
        self.e2erunning = [response.data.E2ERunning];
      }
      self.e2equeue = response.data.E2EQueue;
      fixPRAvatars(self.e2equeue);
    });
  }

  function refreshHistoryPRs() {
    dataService.getData('history').then(function successCallback(response) {
      var prs = response.data;
      fixPRAvatars(prs);
      self.historyPRs = prs;
      self.historySearchTerms = getHistorySearchTerms();
    });
  }

  function refreshSQStats() {
    dataService.getData('sq-stats').then(function successCallback(response) {
      self.sqStats = response.data;
      self.lastMergeTime = new Date(response.data.LastMergeTime)
    });
  }

  function refreshBotStats() {
    dataService.getData('stats').then(function successCallback(response) {
      var nextLoop = new Date(response.data.NextLoopTime);
      self.nextRunTime = nextLoop.toLocaleTimeString();

      self.botStats = response.data.Analytics;
      self.APICount = response.data.APICount;
      self.CachedAPICount = response.data.CachedAPICount;
      self.apiCallsPerSec = response.data.APIPerSec;
      self.githubApiLimitCount = response.data.LimitRemaining;
      var nextReset = new Date(response.data.LimitResetTime);
      self.githubApiLimitReset = nextReset.toLocaleTimeString();
    });
  }

  function refreshUsers() {
    dataService.getData('users').then(function successCallback(response) {
      self.users = response.data;
    });
  }

  function refreshE2EHealth() {
    dataService.getData("health").then(function successCallback(response) {
      self.health = response.data;
      if (self.health.TotalLoops !== 0) {
        var percentStable = self.health.NumStable * 100.0 / self.health.TotalLoops;
        self.OverallHealth = Math.round(percentStable) + "%";
        self.StartTime = new Date(self.health.StartTime).toLocaleString();
      }
      updateBuildStability(self.builds, self.health);
    });
  }

  function refreshGoogleInternalCI() {
    dataService.getData('google-internal-ci').then(function successCallback(response) {
      var result = getE2E(response.data);
      self.builds = result.builds;
      updateBuildStability(self.builds, self.health);
      self.failedBuild = result.failedBuild;
    });
  }

  function getE2E(builds) {
    var result = [];
    var failedBuild = false;
    angular.forEach(builds, function(job, key) {
      var obj = {
        'name': key,
        'id': job.ID,
      };
      if (job.Status == 'Stable') {
        // green check mark
        obj.state = '\u2713';
        obj.color = 'green';
      } else if (job.Status == 'Not Stable') {
        // red X mark
        obj.state = '\u2716';
        obj.color = 'red';
        failedBuild = true;
      } else {
        obj.state = 'Error';
        obj.color = 'red';
        obj.msg = job.Status;
        failedBuild = true;
      }
      obj.stability = '';
      result.push(obj);
    });
    return {
      builds: result,
      failedBuild: failedBuild,
    };
  }

  function updateBuildStability(builds, health) {
    if (Object.keys(builds).length === 0 ||
        health.TotalLoops === 0 || health.NumStablePerJob === undefined) {
      return;
    }
    angular.forEach(builds, function(build) {
      var key = build.name;
      if (key in self.health.NumStablePerJob) {
        var percentStable = health.NumStablePerJob[key] * 100.0 / health.TotalLoops;
        build.stability = Math.round(percentStable) + "%"
      }
    });
  }

  function searchTermsContain(terms, value) {
    var found = false;
    angular.forEach(terms, function(term) {
      if (term.value === value) {
        found = true;
      }
    });
    return found;
  }

  function getPRSearchTerms() {
    return getSearchTerms(self.prs);
  }

  function getHistorySearchTerms() {
    return getSearchTerms(self.historyPRs);
  }

  function getSearchTerms(prs) {
    var result = [];
    angular.forEach(prs, function(pr) {
      var llogin = pr.Login.toLowerCase();
      if (!searchTermsContain(result, llogin)) {
        var loginobj = {
          value: llogin,
          display: pr.Login,
        };
        result.push(loginobj);
      }
      if (!searchTermsContain(result, pr.Num)) {
        var numobj = {
          value: pr.Number.toString(),
          display: pr.Number,
        };
        result.push(numobj);
      }
    });
    result.sort(compareSearchTerms);
    return result;
  }

  /* We need to compare the 'value' to get a sane sort */
  function compareSearchTerms(a, b) {
    if (a.value > b.value) {
      return 1;
    }
    if (a.value < b.value) {
      return -1;
    }
    return 0;
  }

  function prQuerySearch(query) {
    return querySearch(query, self.prSearchTerms);
  }

  function historyQuerySearch(query) {
    return querySearch(query, self.historySearchTerms);
  }

  function querySearch(query, terms) {
    var results = query ? terms.filter(createFilterFor(query)) : terms;
    return results;
  }

  /**
   * Create filter function for a query string
   */
  function createFilterFor(query) {
    var lowercaseQuery = angular.lowercase(query);
    return function filterFn(state) {
      return (state.value.indexOf(lowercaseQuery) === 0);
    };
  }

  function goToPerson(person) {
    console.log(person);
    window.location.href = 'https://github.com/' + person;
  }

  function selectTab(tabName) {
    self.location.path('/' + tabName);
    loadTab();
  }

  getPriorityInfo()

  function getPriorityInfo() {
    dataService.getData('priority-info').then(function successCallback(response) {
      document.getElementById("priority-info").innerHTML = response.data;
    });
  }

  getMergeInfo()

  function getMergeInfo() {
    dataService.getData('merge-info').then(function successCallback(response) {
      document.getElementById("merge-info").innerHTML = response.data;
    });
  }
}

app.filter('loginOrPR', function() {
  return function(prs, searchVal) {
    searchVal = searchVal || "";
    prs = prs || [];
    searchVal = angular.lowercase(searchVal);
    var out = [];

    angular.forEach(prs, function(pr) {
      var shouldPush = false;
      var llogin = pr.Login.toLowerCase();
      if (llogin.indexOf(searchVal) === 0) {
        shouldPush = true;
      }
      if (pr.Number.toString().indexOf(searchVal) === 0) {
        shouldPush = true;
      }
      if (shouldPush) {
        out.push(pr);
      }
    });
    return out;
  };
});

app.service('DataService', ['$http', dataService]);

function dataService($http) {
  return ({
    getData: getData,
  });

  function getData(file) {
    return $http.get(file);
  }
}
<commit_msg>Allow multiple tabs to require the same refreshed functions<commit_after>"use strict";
var app = angular.module('SubmitQueueModule', ['ngMaterial', 'md.data.table', 'angular-toArrayFilter', 'angularMoment']);

app.config(['$compileProvider', function ($compileProvider) {
  $compileProvider.debugInfoEnabled(false);
}]);

app.controller('SQCntl', ['DataService', '$interval', '$location', SQCntl]);

function SQCntl(dataService, $interval, $location) {
  var self = this;
  self.prs = {};
  self.users = {};
  self.builds = {};
  self.health = {};
  self.lastMergeTime = Date();
  self.prQuerySearch = prQuerySearch;
  self.historyQuerySearch = historyQuerySearch;
  self.goToPerson = goToPerson;
  self.selectTab = selectTab;
  self.tabLoaded = {};
  self.functionLoading = {};
  self.queryNum = 0;
  self.selected = 1;
  self.OverallHealth = "";
  self.StartTime = "";
  self.StatOrder = "-Count";
  self.location = $location;

  // http://submit-queue.k8s.io/#?prDisplay=eparis&historyDisplay=15999
  //  will show all PRs opened by eparis and all historic decisions for PR #15999
  var vars = $location.search();
  self.prDisplayValue = vars.prDisplay;
  self.historyDisplayValue = vars.historyDisplay;

  var path = $location.path();
  if (path.length > 0) {
      switch (path) {
      case "/prs":
          self.selected=0;
          break;
      case "/queue":
          self.selected=1;
          break;
      case "/history":
          self.selected=2;
          break;
      case "/e2e":
          self.selected=3;
          break;
      case "/info":
          self.selected=4;
          break;
      default:
          console.log("unknown path: " + path);
          break;
      }
  }

  loadTab(self.selected);

  // Defer loading of data for a tab until it's actually needed.
  // This speeds up the first-boot experience a LOT.
  function loadTab() {
    // reloadFunctions is a map from functions to how often they should run (in minutes)
    var reloadFunctions = {}
    reloadFunctions[refreshPRs] = 10;
    reloadFunctions[refreshGithubE2E] = 0.5;
    reloadFunctions[refreshSQStats] = 30;
    reloadFunctions[refreshHistoryPRs] = 1;
    reloadFunctions[refreshE2EHealth] = 10;
    reloadFunctions[refreshUsers] = 15;
    reloadFunctions[refreshBotStats] = 10;

    // tabFunctionReloads is a map of which tabs need which functions to refresh
    var tabFunctionReloads = {
      0: [refreshPRs],
      1: [refreshGithubE2E, refreshSQStats],
      2: [refreshHistoryPRs],
      3: [refreshE2EHealth],
      4: [refreshUsers, refreshBotStats],
    }
    if (self.tabLoaded[self.selected])
      return;
    self.tabLoaded[self.selected] = true;

    var reloadFuncs = tabFunctionReloads[self.selected];
    if (reloadFuncs === undefined)
      return;

    for (var i = 0; i < reloadFuncs.length; i++) {
      var func = reloadFuncs[i];
      if (self.functionLoading[func] == true)
        continue;
      self.functionLoading[func] = true;

      var updateIntervalMinutes = reloadFunctions[func];
      func();
      $interval(func, updateIntervalMinutes * 60 * 1000);
    }
  }

  // This data is shown in a top banner (when the Queue is blocked),
  // so it's always loaded.
  refreshGoogleInternalCI();
  $interval(refreshGoogleInternalCI, 60000);  // Refresh every minute

  // Request Avatars that are only as large necessary (CSS limits them to 40px)
  function fixPRAvatars(prs) {
    angular.forEach(prs, function(pr) {
      if (/^https:\/\/avatars.githubusercontent.com\/u\/\d+\?v=3$/.test(pr.AvatarURL)) {
        pr.AvatarURL += '&size=40';
      }
    });
  }

  function refreshPRs() {
    dataService.getData('prs').then(function successCallback(response) {
      var prs = response.data.PRStatus;
      fixPRAvatars(prs);
      self.prs = prs;
      self.prSearchTerms = getPRSearchTerms();
    }, function errorCallback(response) {
      console.log("Error: Getting SubmitQueue Status");
    });
  }

  function refreshGithubE2E() {
    dataService.getData('github-e2e-queue').then(function successCallback(response) {
      if (response.data.E2ERunning.Number == 0) {
        self.e2erunning = [];
      } else {
        self.e2erunning = [response.data.E2ERunning];
      }
      self.e2equeue = response.data.E2EQueue;
      fixPRAvatars(self.e2equeue);
    });
  }

  function refreshHistoryPRs() {
    dataService.getData('history').then(function successCallback(response) {
      var prs = response.data;
      fixPRAvatars(prs);
      self.historyPRs = prs;
      self.historySearchTerms = getHistorySearchTerms();
    });
  }

  function refreshSQStats() {
    dataService.getData('sq-stats').then(function successCallback(response) {
      self.sqStats = response.data;
      self.lastMergeTime = new Date(response.data.LastMergeTime)
    });
  }

  function refreshBotStats() {
    dataService.getData('stats').then(function successCallback(response) {
      var nextLoop = new Date(response.data.NextLoopTime);
      self.nextRunTime = nextLoop.toLocaleTimeString();

      self.botStats = response.data.Analytics;
      self.APICount = response.data.APICount;
      self.CachedAPICount = response.data.CachedAPICount;
      self.apiCallsPerSec = response.data.APIPerSec;
      self.githubApiLimitCount = response.data.LimitRemaining;
      var nextReset = new Date(response.data.LimitResetTime);
      self.githubApiLimitReset = nextReset.toLocaleTimeString();
    });
  }

  function refreshUsers() {
    dataService.getData('users').then(function successCallback(response) {
      self.users = response.data;
    });
  }

  function refreshE2EHealth() {
    dataService.getData("health").then(function successCallback(response) {
      self.health = response.data;
      if (self.health.TotalLoops !== 0) {
        var percentStable = self.health.NumStable * 100.0 / self.health.TotalLoops;
        self.OverallHealth = Math.round(percentStable) + "%";
        self.StartTime = new Date(self.health.StartTime).toLocaleString();
      }
      updateBuildStability(self.builds, self.health);
    });
  }

  function refreshGoogleInternalCI() {
    dataService.getData('google-internal-ci').then(function successCallback(response) {
      var result = getE2E(response.data);
      self.builds = result.builds;
      updateBuildStability(self.builds, self.health);
      self.failedBuild = result.failedBuild;
    });
  }

  function getE2E(builds) {
    var result = [];
    var failedBuild = false;
    angular.forEach(builds, function(job, key) {
      var obj = {
        'name': key,
        'id': job.ID,
      };
      if (job.Status == 'Stable') {
        // green check mark
        obj.state = '\u2713';
        obj.color = 'green';
      } else if (job.Status == 'Not Stable') {
        // red X mark
        obj.state = '\u2716';
        obj.color = 'red';
        failedBuild = true;
      } else {
        obj.state = 'Error';
        obj.color = 'red';
        obj.msg = job.Status;
        failedBuild = true;
      }
      obj.stability = '';
      result.push(obj);
    });
    return {
      builds: result,
      failedBuild: failedBuild,
    };
  }

  function updateBuildStability(builds, health) {
    if (Object.keys(builds).length === 0 ||
        health.TotalLoops === 0 || health.NumStablePerJob === undefined) {
      return;
    }
    angular.forEach(builds, function(build) {
      var key = build.name;
      if (key in self.health.NumStablePerJob) {
        var percentStable = health.NumStablePerJob[key] * 100.0 / health.TotalLoops;
        build.stability = Math.round(percentStable) + "%"
      }
    });
  }

  function searchTermsContain(terms, value) {
    var found = false;
    angular.forEach(terms, function(term) {
      if (term.value === value) {
        found = true;
      }
    });
    return found;
  }

  function getPRSearchTerms() {
    return getSearchTerms(self.prs);
  }

  function getHistorySearchTerms() {
    return getSearchTerms(self.historyPRs);
  }

  function getSearchTerms(prs) {
    var result = [];
    angular.forEach(prs, function(pr) {
      var llogin = pr.Login.toLowerCase();
      if (!searchTermsContain(result, llogin)) {
        var loginobj = {
          value: llogin,
          display: pr.Login,
        };
        result.push(loginobj);
      }
      if (!searchTermsContain(result, pr.Num)) {
        var numobj = {
          value: pr.Number.toString(),
          display: pr.Number,
        };
        result.push(numobj);
      }
    });
    result.sort(compareSearchTerms);
    return result;
  }

  /* We need to compare the 'value' to get a sane sort */
  function compareSearchTerms(a, b) {
    if (a.value > b.value) {
      return 1;
    }
    if (a.value < b.value) {
      return -1;
    }
    return 0;
  }

  function prQuerySearch(query) {
    return querySearch(query, self.prSearchTerms);
  }

  function historyQuerySearch(query) {
    return querySearch(query, self.historySearchTerms);
  }

  function querySearch(query, terms) {
    var results = query ? terms.filter(createFilterFor(query)) : terms;
    return results;
  }

  /**
   * Create filter function for a query string
   */
  function createFilterFor(query) {
    var lowercaseQuery = angular.lowercase(query);
    return function filterFn(state) {
      return (state.value.indexOf(lowercaseQuery) === 0);
    };
  }

  function goToPerson(person) {
    console.log(person);
    window.location.href = 'https://github.com/' + person;
  }

  function selectTab(tabName) {
    self.location.path('/' + tabName);
    loadTab();
  }

  getPriorityInfo()

  function getPriorityInfo() {
    dataService.getData('priority-info').then(function successCallback(response) {
      document.getElementById("priority-info").innerHTML = response.data;
    });
  }

  getMergeInfo()

  function getMergeInfo() {
    dataService.getData('merge-info').then(function successCallback(response) {
      document.getElementById("merge-info").innerHTML = response.data;
    });
  }
}

app.filter('loginOrPR', function() {
  return function(prs, searchVal) {
    searchVal = searchVal || "";
    prs = prs || [];
    searchVal = angular.lowercase(searchVal);
    var out = [];

    angular.forEach(prs, function(pr) {
      var shouldPush = false;
      var llogin = pr.Login.toLowerCase();
      if (llogin.indexOf(searchVal) === 0) {
        shouldPush = true;
      }
      if (pr.Number.toString().indexOf(searchVal) === 0) {
        shouldPush = true;
      }
      if (shouldPush) {
        out.push(pr);
      }
    });
    return out;
  };
});

app.service('DataService', ['$http', dataService]);

function dataService($http) {
  return ({
    getData: getData,
  });

  function getData(file) {
    return $http.get(file);
  }
}
<|endoftext|>


<commit_before>import { app, BrowserWindow, ipcMain, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import * as EVENTS from '../shared/events';
import {
  listBoardsHandler,
  listPortsHandler,
  loadTargetBoardHandler,
  saveTargetBoardHandler,
  uploadToArduinoHandler,
} from './arduinoActions';
import * as settings from './settings';
import { errorToPlainObject, IS_DEV } from './utils';
import * as WA from './workspaceActions';
import { configureAutoUpdater, subscribeOnAutoUpdaterEvents } from './autoupdate';

// =============================================================================
//
// Configure application
//
// =============================================================================

app.setName('xod');

configureAutoUpdater(autoUpdater, log);

if (process.env.USERDATA_DIR) {
  app.setPath('userData', process.env.USERDATA_DIR);
  settings.rewriteElectronSettingsFilePath(app.getPath('userData'));
}

if (IS_DEV) {
  // To prevent GL_ERROR in development version (black rectangles).
  app.disableHardwareAcceleration();
}

// =============================================================================
//
// Application main process
//
// =============================================================================

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    width: 1280,
    height: 720,
    minWidth: 800,
    minHeight: 600,
    title: 'XOD IDE',
    show: false,
  });
  win.maximize();
  // and load the index.html of the app.
  win.loadURL(`file://${__dirname}/../index.html`);

  // Open the DevTools.
  // win.webContents.openDevTools();

  const { webContents } = win;

  const handleRedirect = (e, url) => {
    if (url !== webContents.getURL()) {
      e.preventDefault();
      shell.openExternal(url);
    }
  };
  webContents.on('will-navigate', handleRedirect);
  webContents.on('new-window', handleRedirect);

  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null;
  });

  win.on('ready-to-show', win.show);
}

const subscribeToRemoteAction = (processName, remoteAction) => {
  ipcMain.on(processName, (event, data) => {
    event.sender.send(`${processName}:process`);
    remoteAction(event, data).then((result) => {
      event.sender.send(`${processName}:complete`, result);
    }).catch((err) => {
      event.sender.send(`${processName}:error`, errorToPlainObject(err));
    });
  });
};

configureAutoUpdater(autoUpdater, log);

const onReady = () => {
  if (IS_DEV) {
    require('devtron').install(); // eslint-disable-line global-require
  }
  settings.setDefaults();

  subscribeToRemoteAction(EVENTS.SAVE_PROJECT, WA.subscribeToSaveProject);

  WA.subscribeToWorkspaceEvents(ipcMain);
  ipcMain.on('UPLOAD_TO_ARDUINO', uploadToArduinoHandler);
  ipcMain.on(EVENTS.LIST_PORTS, listPortsHandler);
  ipcMain.on(EVENTS.LIST_BOARDS, listBoardsHandler);
  ipcMain.on(EVENTS.GET_SELECTED_BOARD, loadTargetBoardHandler);
  ipcMain.on(EVENTS.SET_SELECTED_BOARD, saveTargetBoardHandler);

  createWindow();
  win.webContents.on('did-finish-load', () => {
    WA.prepareWorkspaceOnLaunch((eventName, data) => {
      win.webContents.send(eventName, data);
    });

    subscribeOnAutoUpdaterEvents(
      (eventName, data) => win.webContents.send(eventName, data),
      ipcMain,
      autoUpdater
    );

    autoUpdater.checkForUpdates();
  });
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady);

// Quit when all windows are closed.
app.on('window-all-closed', app.quit);

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow();
  }
});
<commit_msg>fix(xod-client-electron): fix subpixel antialiasing<commit_after>import { app, BrowserWindow, ipcMain, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import * as EVENTS from '../shared/events';
import {
  listBoardsHandler,
  listPortsHandler,
  loadTargetBoardHandler,
  saveTargetBoardHandler,
  uploadToArduinoHandler,
} from './arduinoActions';
import * as settings from './settings';
import { errorToPlainObject, IS_DEV } from './utils';
import * as WA from './workspaceActions';
import { configureAutoUpdater, subscribeOnAutoUpdaterEvents } from './autoupdate';

// =============================================================================
//
// Configure application
//
// =============================================================================

app.setName('xod');

configureAutoUpdater(autoUpdater, log);

if (process.env.USERDATA_DIR) {
  app.setPath('userData', process.env.USERDATA_DIR);
  settings.rewriteElectronSettingsFilePath(app.getPath('userData'));
}

if (IS_DEV) {
  // To prevent GL_ERROR in development version (black rectangles).
  app.disableHardwareAcceleration();
}

// =============================================================================
//
// Application main process
//
// =============================================================================

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    width: 1280,
    height: 720,
    minWidth: 800,
    minHeight: 600,
    title: 'XOD IDE',
    show: false,
    // this is required for subpixel antialiasing to work
    backgroundColor: '#FFF',
  });
  win.maximize();
  // and load the index.html of the app.
  win.loadURL(`file://${__dirname}/../index.html`);

  // Open the DevTools.
  // win.webContents.openDevTools();

  const { webContents } = win;

  const handleRedirect = (e, url) => {
    if (url !== webContents.getURL()) {
      e.preventDefault();
      shell.openExternal(url);
    }
  };
  webContents.on('will-navigate', handleRedirect);
  webContents.on('new-window', handleRedirect);

  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null;
  });

  win.on('ready-to-show', win.show);
}

const subscribeToRemoteAction = (processName, remoteAction) => {
  ipcMain.on(processName, (event, data) => {
    event.sender.send(`${processName}:process`);
    remoteAction(event, data).then((result) => {
      event.sender.send(`${processName}:complete`, result);
    }).catch((err) => {
      event.sender.send(`${processName}:error`, errorToPlainObject(err));
    });
  });
};

configureAutoUpdater(autoUpdater, log);

const onReady = () => {
  if (IS_DEV) {
    require('devtron').install(); // eslint-disable-line global-require
  }
  settings.setDefaults();

  subscribeToRemoteAction(EVENTS.SAVE_PROJECT, WA.subscribeToSaveProject);

  WA.subscribeToWorkspaceEvents(ipcMain);
  ipcMain.on('UPLOAD_TO_ARDUINO', uploadToArduinoHandler);
  ipcMain.on(EVENTS.LIST_PORTS, listPortsHandler);
  ipcMain.on(EVENTS.LIST_BOARDS, listBoardsHandler);
  ipcMain.on(EVENTS.GET_SELECTED_BOARD, loadTargetBoardHandler);
  ipcMain.on(EVENTS.SET_SELECTED_BOARD, saveTargetBoardHandler);

  createWindow();
  win.webContents.on('did-finish-load', () => {
    WA.prepareWorkspaceOnLaunch((eventName, data) => {
      win.webContents.send(eventName, data);
    });

    subscribeOnAutoUpdaterEvents(
      (eventName, data) => win.webContents.send(eventName, data),
      ipcMain,
      autoUpdater
    );

    autoUpdater.checkForUpdates();
  });
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady);

// Quit when all windows are closed.
app.on('window-all-closed', app.quit);

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow();
  }
});
<|endoftext|>


<commit_before>'use strict';

//
//	PictureCanvas
//

// ■ PictureCanvas
// canvasを重ねた描画領域を表す。
// 領域内にてマウスまたはタッチパネルイベントを受け付け、
// 登録された描画ツールに座標と対象レイヤーの情報を配送する。
// (描画のためのcanvas要素の操作は描画ツールに委譲する。)
// キャンバス全クリアとレイヤー合成の機能を備える。

/// 新しいインスタンスを初期化する。
function PictureCanvas()
{
	// DOMオブジェクト取得
	this.m_view_port = document.getElementById("viewport");
	this.m_layers = [
		document.getElementById("canvas_0"),
		document.getElementById("canvas_1"),
		document.getElementById("canvas_2")
	];
	this.m_joint_canvas = document.getElementById("joint_canvas");

	// 描画担当ツール
	this.m_drawer = null;

	// 描画担当ツールに引き渡す情報
	this.m_nTargetLayerNo = this.m_layers.length - 1;		// 描画対象レイヤー
	this.m_rect_view_port = this.m_view_port.getBoundingClientRect();
	this.m_rect_layer = this.m_layers[0].getBoundingClientRect();

	// ポインタデバイスのドラッグ状態
	this.m_bDragging = false;

	// イベントハンドラ登録
	register_pointer_event_handler(this.m_view_port, this);

	// {	/*UTEST*/	// 座標確認
	// 	this.m_layres[0].
	// 	let rectZ = document.documentElement.getBoundingClientRect();
	// 	let rect0 = document.body.getBoundingClientRect();
	// 	let rect1 = this.m_view_port.getBoundingClientRect();
	// 	let rect2 = this.m_layers[0].getBoundingClientRect();
	// 	console.log("rectZ=(" + rectZ.left + ", " + rectZ.top + ", " + rectZ.width + ", " + rectZ.height + ")");
	// 	console.log("rect0=(" + rect0.left + ", " + rect0.top + ", " + rect0.width + ", " + rect0.height + ")");
	// 	console.log("rect1=(" + rect1.left + ", " + rect1.top + ", " + rect1.width + ", " + rect1.height + ")");
	// 	console.log("rect2=(" + rect2.left + ", " + rect2.top + ", " + rect2.width + ", " + rect2.height + ")");
	// }
}

/// イベントリスナ。
PictureCanvas.prototype.handleEvent = function(e)
{
	// console.log("Event: " + e.type);
	// console.dir(e);

	// 描画ツールに引き渡す情報を構成
	let mod_e;
	if (this.m_drawer) {
		// mod_e = Object.assign({ }, e);  // 値コピー	// NG。うまく機能しない。
		mod_e = e;		// Alias
		let dx = this.m_rect_view_port.left - this.m_rect_layer.left;
		let dy = this.m_rect_view_port.top - this.m_rect_layer.top;
		//mod_e.clientX += dx;	// 不要かつできない(eはconstオブジェクト)
		//mod_e.clientY += dy;	// 同上
		// console.dir(this.m_rect_view_port);	// UTEST
		// console.dir(this.m_rect_layer);			// UTEST
		// console.dir(mod_e);		// UTEST
		// console.dir(e);		// UTEST
		// 座標周りの考え方は、実は(e.clientX, e.clientY)はHTMLページ左上原点とする
		// 共通の座標系であり、全HTML要素共通なので、HTML要素毎に変換する必要は無い。
		// HTML要素内のローカル座標にしたければ、
		// (HTML要素).getBoundingClientRect()が返すDOMRectの(.x, .y)を引く。
	}

	// イベント別処理
	switch (e.type) {
	case 'mousedown':
	case 'touchistart':
		{		/*UTEST*/	// スクロールバー込みの座標確認
			// レイヤーのbounding rect
			// スクロールバーに移動につれクライアント座標値が動く。
			let bounds = this.m_layers[0].getBoundingClientRect();
			console.dir(bounds);

			// クリック座標をレイヤーローカルな座標に変換
			// こうするとスクロールバーの影響を受けない。
			// つまり、HTMLページ左上を原点とする絶対座標系が(clientX, clientY)であり、
			// かつgetBoundingClientRect()が返す座標系も同じなのでこれで辻褄が合う。
			let pt = new JsPoint(e.clientX - bounds.x, e.clientY - bounds.y);
			console.dir(pt);

			// スクロールバーの幅を得る試み
			console.log(
				  "scrollHeight=" + this.m_view_port.scrollHeight
				+ ", offsetHeight=" + this.m_view_port.offsetHeight
				);
				console.log(
					  "scrollHeight=" + this.m_layers[0].scrollHeight
					+ ", offsetHeight=" + this.m_layers[0].offsetHeight
				);
				let bounds2 = this.m_view_port.getBoundingClientRect();
				console.log("viewport.height - layer[0].height=" + (bounds2.height - bounds.height));

				// ブラウザの表示領域サイズ取得
				let clientWidth = document.documentElement.clientWidth;
				let clientHeight = document.documentElement.clientHeight;
				console.log("clientWidth=" + clientWidth + ", clientHeight=" + clientHeight);

				clientWidth = document.body.clientWidth;
				clientHeight = document.body.clientHeight;
				console.log("clientWidth=" + clientWidth + ", clientHeight=" + clientHeight);
				let bounds3 = document.body.getBoundingClientRect();
				console.dir(bounds3);
		}

		// mouseupやtouchendを確実に補足するための登録
		g_pointManager.notifyPointStart(this, e);

		// 描画開始を通知
		this.m_bDragging = true;
		if (this.m_drawer) {
			this.m_drawer.OnDrawStart(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		break;
	case 'mouseup':
	case 'touchend':
		// 描画終了を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawEnd(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		this.m_bDragging = false;
		break;
	case 'mousemove':
	case 'touchmove':
		// ポインタの移動を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawing(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		break;
	default:
		break;
	}
}

/// 要素の絶対座標を返す。
PictureCanvas.prototype.getBoundingClientRect = function()
{
	return this.m_view_port.getBoundingClientRect();
}

/// 描画ツールを設定する。
PictureCanvas.prototype.setDrawer = function(drawer)
{
	assert(!this.m_bDragging);
	let prev = this.m_drawer;
	this.m_drawer = drawer;
	return prev;
}

/// キャンバスを全クリアする。
PictureCanvas.prototype.eraseCanvas = function()
{
	erase_canvas(this.m_layers);

	// {	/*UTEST*/	// アンチエリアシング無し描画テスト
	// 	let ctx = this.m_layers[0].getContext('2d');
	// 	// ctx.strokeStyle = "#000000";
	// 	ctx.fillStyle = "#000000";
	// 	ctx.beginPath();
	// 	draw_line(0.5, 0.5, 200.5, 200.5, ctx, 1);
	// 	ctx.stroke();
	// 	ctx.closePath();
	// }
}

/// 全レイヤーを合成する。
PictureCanvas.prototype.getJointImage = function(dstCanvas)
{
	get_joint_image(this.m_layers, dstCanvas);
}
<commit_msg>レイヤーサイズに合わせてview portサイズを調整する処理を追加した。 DOMの座標関連がだいたいわかったので、関連する実験用コードをこの機に削除した。<commit_after>'use strict';

//
//	PictureCanvas
//

// ■ PictureCanvas
// canvasを重ねた描画領域を表す。
// 領域内にてマウスまたはタッチパネルイベントを受け付け、
// 登録された描画ツールに座標と対象レイヤーの情報を配送する。
// (描画のためのcanvas要素の操作は描画ツールに委譲する。)
// キャンバス全クリアとレイヤー合成の機能を備える。

/// 新しいインスタンスを初期化する。
function PictureCanvas()
{
	// DOMオブジェクト取得
	this.m_view_port = document.getElementById("viewport");
	this.m_layers = [
		document.getElementById("canvas_0"),
		document.getElementById("canvas_1"),
		document.getElementById("canvas_2")
	];
	this.m_joint_canvas = document.getElementById("joint_canvas");

	// 描画担当ツール
	this.m_drawer = null;

	// 描画担当ツールに引き渡す情報
	this.m_nTargetLayerNo = this.m_layers.length - 1;		// 描画対象レイヤー
	this.m_rect_view_port = this.m_view_port.getBoundingClientRect();
	this.m_rect_layer = this.m_layers[0].getBoundingClientRect();

	// ポインタデバイスのドラッグ状態
	this.m_bDragging = false;

	// イベントハンドラ登録
	register_pointer_event_handler(this.m_view_port, this);

	// レイヤーのサイズ調整
	this.fitCanvas();
}

/// イベントリスナ。
PictureCanvas.prototype.handleEvent = function(e)
{
	// console.log("Event: " + e.type);
	// console.dir(e);

	// 描画ツールに引き渡す情報を構成
	let mod_e;
	if (this.m_drawer) {
		// mod_e = Object.assign({ }, e);  // 値コピー	// NG。うまく機能しない。
		mod_e = e;		// Alias
		let dx = this.m_rect_view_port.left - this.m_rect_layer.left;
		let dy = this.m_rect_view_port.top - this.m_rect_layer.top;
		//mod_e.clientX += dx;	// 不要かつできない(eはconstオブジェクト)
		//mod_e.clientY += dy;	// 同上
		// console.dir(this.m_rect_view_port);	// UTEST
		// console.dir(this.m_rect_layer);			// UTEST
		// console.dir(mod_e);		// UTEST
		// console.dir(e);		// UTEST
		// 座標周りの考え方は、実は(e.clientX, e.clientY)はHTMLページ左上原点とする
		// 共通の座標系であり、全HTML要素共通なので、HTML要素毎に変換する必要は無い。
		// HTML要素内のローカル座標にしたければ、
		// (HTML要素).getBoundingClientRect()が返すDOMRectの(.x, .y)を引く。
	}

	// イベント別処理
	switch (e.type) {
	case 'mousedown':
	case 'touchistart':
		// mouseupやtouchendを確実に補足するための登録
		g_pointManager.notifyPointStart(this, e);

		// 描画開始を通知
		this.m_bDragging = true;
		if (this.m_drawer) {
			this.m_drawer.OnDrawStart(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		break;
	case 'mouseup':
	case 'touchend':
		// 描画終了を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawEnd(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		this.m_bDragging = false;
		break;
	case 'mousemove':
	case 'touchmove':
		// ポインタの移動を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawing(mod_e, this.m_layers, this.m_nTargetLayerNo);
		}
		break;
	default:
		break;
	}
}

/// 要素の絶対座標を返す。
PictureCanvas.prototype.getBoundingClientRect = function()
{
	return this.m_view_port.getBoundingClientRect();
}

/// 描画ツールを設定する。
PictureCanvas.prototype.setDrawer = function(drawer)
{
	assert(!this.m_bDragging);
	let prev = this.m_drawer;
	this.m_drawer = drawer;
	return prev;
}

/// キャンバスを全クリアする。
PictureCanvas.prototype.eraseCanvas = function()
{
	erase_canvas(this.m_layers);

	// {	/*UTEST*/	// アンチエリアシング無し描画テスト
	// 	let ctx = this.m_layers[0].getContext('2d');
	// 	// ctx.strokeStyle = "#000000";
	// 	ctx.fillStyle = "#000000";
	// 	ctx.beginPath();
	// 	draw_line(0.5, 0.5, 200.5, 200.5, ctx, 1);
	// 	ctx.stroke();
	// 	ctx.closePath();
	// }
}

/// 全レイヤーを合成する。
PictureCanvas.prototype.getJointImage = function(dstCanvas)
{
	get_joint_image(this.m_layers, dstCanvas);
}

/// View portをキャンバスにfitさせる。
PictureCanvas.prototype.fitCanvas = function()
{
	const width_margin = 100;
	const height_margin = 50;
	const layer_margin_horz = 5;
	const layer_margin_vert = 5;
	const layer_client_width_min = 400;
	const layer_client_height_min = 400;

	// レイヤーのオフセット設定
	// なぜか明示的に設定せねばプログラムで取得できない。FireFoxにて確認。
	for (let i = 0; i < this.m_layers.length; ++i) {
		this.m_layers[0].style.left = layer_margin_horz + "px";
		this.m_layers[0].style.top = layer_margin_vert + "px";
	}

	// View portの寸法取得
	let vport_client_width = this.m_view_port.clientWidth;
	let vport_client_height = this.m_view_port.clientHeight;
	let vport_outer_width = this.m_view_port.offsetWidth;
	let vport_outer_height = this.m_view_port.offsetHeight;
	{	/*UTEST*/		// スクロールバーの幅の算出
		// 次の関係が成り立つ。
		//   vport_client_width < vport_outer_width == vport_bounds.width
		// vport_client_widthがスクロールバーを含まない領域幅のようだ。
		let vport_bounds = this.m_view_port.getBoundingClientRect();
		console.log("vport_client_width=" + vport_client_width
		 	+ ", vport_outer_width=" + vport_outer_width
			+ ", vport_bounds.width=" + vport_bounds.width
		);
		console.log("scroll bar width(?)=" + (vport_outer_width - vport_client_width));
		console.log("layer client width=" + this.m_layers[0].clientWidth);
	}

	// レイヤーの寸法取得
	let layer_outer_width = this.m_layers[0].offsetWidth;
	let layer_outer_height = this.m_layers[0].offsetHeight;
	{
		let layer_client_width = this.m_layers[0].clientWidth;
		let layer_client_height = this.m_layers[0].clientHeight;
		if (layer_client_width < layer_client_width_min) {
			layer_outer_width += (layer_client_width_min - layer_client_width);
		}
		if (layer_client_height < layer_client_height_min) {
			layer_outer_height += (layer_client_height_min - layer_client_height);
		}
	}

	// レイヤーのオフセット取得
	let layer_left = parseInt(this.m_layers[0].style.left);
	let layer_top = parseInt(this.m_layers[0].style.top);
	// console.log("layer_left=" + layer_left + ", layer_top=" + layer_top);

	// View portの必要なサイズを取得
	let delta_w = layer_left + layer_outer_width - vport_client_width;
	let delta_h = layer_top + layer_outer_height - vport_client_height;
	let vport_outer_width_min = vport_outer_width + delta_w;
	let vport_outer_height_min = vport_outer_height + delta_h;

	// ウィンドウの表示領域サイズで制限をかける
	// console.log("window.innerWidth=" + window.innerWidth);
	// console.log("window.innerHeight=" + window.innerHeight);
	if (vport_outer_width_min > window.innerWidth - width_margin) {
		vport_outer_width_min = window.innerWidth - width_margin;
	}
	if (vport_outer_height_min > window.innerHeight - height_margin) {
		vport_outer_height_min = window.innerHeight - height_margin;
	}

	// View portのサイズ設定
	this.m_view_port.style.width = vport_outer_width_min + "px";
	this.m_view_port.style.height = vport_outer_height_min + "px";
}
<|endoftext|>


<commit_before>'use strict';

import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { INSTREAM, OUTSTREAM } from '../src/video.js';
import { Renderer } from '../src/Renderer.js';
import {find} from '../src/polyfill.js';
import { getStorageManager } from '../src/storageManager.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { createEidsArray } from './userId/eids.js';
import { deepClone, logError, deepAccess } from '../src/utils.js';

const ENDPOINT = 'https://onetag-sys.com/prebid-request';
const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/';
const BIDDER_CODE = 'onetag';
const GVLID = 241;

const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE});

/**
 * Determines whether or not the given bid request is valid.
 *
 * @param {BidRequest} bid The bid params to validate.
 * @return boolean True if this is a valid bid, and false otherwise.
 */
function isBidRequestValid(bid) {
  if (typeof bid === 'undefined' || typeof bid.params === 'undefined' || typeof bid.params.pubId !== 'string') {
    return false;
  }
  return isValid(BANNER, bid) || isValid(VIDEO, bid);
}

export function hasTypeVideo(bid) {
  return typeof bid.mediaTypes !== 'undefined' && typeof bid.mediaTypes.video !== 'undefined';
}

export function isValid(type, bid) {
  if (type === BANNER) {
    return parseSizes(bid).length > 0;
  } else if (type === VIDEO && hasTypeVideo(bid)) {
    const context = bid.mediaTypes.video.context;
    if (context === 'outstream' || context === 'instream') {
      return parseVideoSize(bid).length > 0;
    }
  }
  return false;
}

/**
 * Make a server request from the list of BidRequests.
 *
 * @param {validBidRequests[]} - an array of bids
 * @return ServerRequest Info describing the request to the server.
 */
function buildRequests(validBidRequests, bidderRequest) {
  const payload = {
    bids: requestsToBids(validBidRequests),
    ...getPageInfo()
  };
  if (bidderRequest && bidderRequest.gdprConsent) {
    payload.gdprConsent = {
      consentString: bidderRequest.gdprConsent.consentString,
      consentRequired: bidderRequest.gdprConsent.gdprApplies
    };
  }
  if (bidderRequest && bidderRequest.uspConsent) {
    payload.usPrivacy = bidderRequest.uspConsent;
  }
  if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userId) {
    payload.userId = createEidsArray(validBidRequests[0].userId);
  }
  if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) {
    payload.schain = validBidRequests[0].schain;
  }
  try {
    if (storage.hasLocalStorage()) {
      payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid');
    }
  } catch (e) {}
  return {
    method: 'POST',
    url: ENDPOINT,
    data: JSON.stringify(payload)
  }
}

function interpretResponse(serverResponse, bidderRequest) {
  const body = serverResponse.body;
  const bids = [];
  const requestData = JSON.parse(bidderRequest.data);
  if (!body || (body.nobid && body.nobid === true)) {
    return bids;
  }
  if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) {
    return bids;
  }
  body.bids.forEach(bid => {
    const responseBid = {
      requestId: bid.requestId,
      cpm: bid.cpm,
      width: bid.width,
      height: bid.height,
      creativeId: bid.creativeId,
      dealId: bid.dealId == null ? bid.dealId : '',
      currency: bid.currency,
      netRevenue: bid.netRevenue || false,
      mediaType: bid.mediaType,
      meta: {
        mediaType: bid.mediaType,
        advertiserDomains: bid.adomain
      },
      ttl: bid.ttl || 300
    };
    if (bid.mediaType === BANNER) {
      responseBid.ad = bid.ad;
    } else if (bid.mediaType === VIDEO) {
      const {context, adUnitCode} = find(requestData.bids, (item) =>
        item.bidId === bid.requestId &&
        item.type === VIDEO
      );
      if (context === INSTREAM) {
        responseBid.vastUrl = bid.vastUrl;
        responseBid.videoCacheKey = bid.videoCacheKey;
      } else if (context === OUTSTREAM) {
        responseBid.vastXml = bid.ad;
        responseBid.vastUrl = bid.vastUrl;
        if (bid.rendererUrl) {
          responseBid.renderer = createRenderer({ ...bid, adUnitCode });
        }
      }
    }
    bids.push(responseBid);
  });
  return bids;
}

function createRenderer(bid, rendererOptions = {}) {
  const renderer = Renderer.install({
    id: bid.requestId,
    url: bid.rendererUrl,
    config: rendererOptions,
    adUnitCode: bid.adUnitCode,
    loaded: false
  });
  try {
    renderer.setRender(({renderer, width, height, vastXml, adUnitCode}) => {
      renderer.push(() => {
        window.onetag.Player.init({
          ...bid,
          width,
          height,
          vastXml,
          nodeId: adUnitCode,
          config: renderer.getConfig()
        });
      });
    });
  } catch (e) {

  }
  return renderer;
}

function getFrameNesting() {
  let topmostFrame = window;
  let parent = window.parent;
  let currentFrameNesting = 0;
  try {
    while (topmostFrame !== topmostFrame.parent) {
      parent = topmostFrame.parent;
      // eslint-disable-next-line no-unused-expressions
      parent.location.href;
      topmostFrame = topmostFrame.parent;
    }
  } catch (e) {
    currentFrameNesting = parent === topmostFrame.top ? 1 : 2;
  }
  return {
    topmostFrame,
    currentFrameNesting
  }
}

function getDocumentVisibility(window) {
  try {
    if (typeof window.document.hidden !== 'undefined') {
      return window.document.hidden;
    } else if (typeof window.document['msHidden'] !== 'undefined') {
      return window.document['msHidden'];
    } else if (typeof window.document['webkitHidden'] !== 'undefined') {
      return window.document['webkitHidden'];
    } else {
      return null;
    }
  } catch (e) {
    return null;
  }
}

/**
 * Returns information about the page needed by the server in an object to be converted in JSON
 * @returns {{location: *, referrer: (*|string), masked: *, wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}}
 */
function getPageInfo() {
  const { topmostFrame, currentFrameNesting } = getFrameNesting();
  return {
    location: topmostFrame.location.href,
    referrer:
      topmostFrame.document.referrer !== ''
        ? topmostFrame.document.referrer
        : null,
    ancestorOrigin:
      window.location.ancestorOrigins && window.location.ancestorOrigins.length > 0
        ? window.location.ancestorOrigins[window.location.ancestorOrigins.length - 1]
        : null,
    masked: currentFrameNesting,
    wWidth: topmostFrame.innerWidth,
    wHeight: topmostFrame.innerHeight,
    oWidth: topmostFrame.outerWidth,
    oHeight: topmostFrame.outerHeight,
    sWidth: topmostFrame.screen.width,
    sHeight: topmostFrame.screen.height,
    aWidth: topmostFrame.screen.availWidth,
    aHeight: topmostFrame.screen.availHeight,
    sLeft: 'screenLeft' in topmostFrame ? topmostFrame.screenLeft : topmostFrame.screenX,
    sTop: 'screenTop' in topmostFrame ? topmostFrame.screenTop : topmostFrame.screenY,
    xOffset: topmostFrame.pageXOffset,
    yOffset: topmostFrame.pageYOffset,
    docHidden: getDocumentVisibility(topmostFrame),
    docHeight: topmostFrame.document.body ? topmostFrame.document.body.scrollHeight : null,
    hLength: history.length,
    timing: getTiming(),
    version: {
      prebid: '$prebid.version$',
      adapter: '1.1.0'
    }
  };
}

function requestsToBids(bidRequests) {
  const videoBidRequests = bidRequests.filter(bidRequest => hasTypeVideo(bidRequest)).map(bidRequest => {
    const videoObj = {};
    setGeneralInfo.call(videoObj, bidRequest);
    // Pass parameters
    // Context: instream - outstream - adpod
    videoObj['context'] = bidRequest.mediaTypes.video.context;
    // Sizes
    videoObj['playerSize'] = parseVideoSize(bidRequest);
    // Other params
    videoObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.video);
    videoObj['type'] = VIDEO;
    videoObj['priceFloors'] = getBidFloor(bidRequest, VIDEO, videoObj['playerSize']);
    return videoObj;
  });
  const bannerBidRequests = bidRequests.filter(bidRequest => isValid(BANNER, bidRequest)).map(bidRequest => {
    const bannerObj = {};
    setGeneralInfo.call(bannerObj, bidRequest);
    bannerObj['sizes'] = parseSizes(bidRequest);
    bannerObj['type'] = BANNER;
    bannerObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.banner);
    bannerObj['priceFloors'] = getBidFloor(bidRequest, BANNER, bannerObj['sizes']);
    return bannerObj;
  });
  return videoBidRequests.concat(bannerBidRequests);
}

function setGeneralInfo(bidRequest) {
  const params = bidRequest.params;
  this['adUnitCode'] = bidRequest.adUnitCode;
  this['bidId'] = bidRequest.bidId;
  this['bidderRequestId'] = bidRequest.bidderRequestId;
  this['auctionId'] = bidRequest.auctionId;
  this['transactionId'] = bidRequest.transactionId;
  this['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot');
  this['pubId'] = params.pubId;
  this['ext'] = params.ext;
  if (params.pubClick) {
    this['click'] = params.pubClick;
  }
  if (params.dealId) {
    this['dealId'] = params.dealId;
  }
  const coords = getSpaceCoords(bidRequest.adUnitCode);
  if (coords) {
    this['coords'] = coords;
  }
}

function getSpaceCoords(id) {
  const space = document.getElementById(id);
  try {
    const { top, left, width, height } = space.getBoundingClientRect();
    let window = space.ownerDocument.defaultView;
    const coords = { top: top + window.pageYOffset, left: left + window.pageXOffset, width, height };
    let frame = window.frameElement;
    while (frame != null) {
      const { top, left } = frame.getBoundingClientRect();
      coords.top += top + window.pageYOffset;
      coords.left += left + window.pageXOffset;
      window = window.parent;
      frame = window.frameElement;
    }
    return coords;
  } catch (e) {
    return null;
  }
}

function getTiming() {
  try {
    if (window.performance != null && window.performance.timing != null) {
      const timing = {};
      const perf = window.performance.timing;
      timing.pageLoadTime = perf.loadEventEnd - perf.navigationStart;
      timing.connectTime = perf.responseEnd - perf.requestStart;
      timing.renderTime = perf.domComplete - perf.domLoading;
      return timing;
    }
  } catch (e) {
    return null;
  }
  return null;
}

function parseVideoSize(bid) {
  const playerSize = bid.mediaTypes.video.playerSize;
  if (typeof playerSize !== 'undefined' && Array.isArray(playerSize) && playerSize.length > 0) {
    return getSizes(playerSize)
  }
  return [];
}

function parseSizes(bid) {
  let ret = [];
  if (typeof bid.mediaTypes !== 'undefined' && typeof bid.mediaTypes.banner !== 'undefined' && typeof bid.mediaTypes.banner.sizes !== 'undefined' && Array.isArray(bid.mediaTypes.banner.sizes) && bid.mediaTypes.banner.sizes.length > 0) {
    return getSizes(bid.mediaTypes.banner.sizes)
  }
  const isVideoBidRequest = hasTypeVideo(bid);
  if (!isVideoBidRequest && bid.sizes && Array.isArray(bid.sizes)) {
    return getSizes(bid.sizes);
  }
  return ret;
}

function getSizes(sizes) {
  const ret = [];
  for (let i = 0; i < sizes.length; i++) {
    const size = sizes[i];
    ret.push({width: size[0], height: size[1]})
  }
  return ret;
}

function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) {
  let syncs = [];
  let params = '';
  if (gdprConsent) {
    if (typeof gdprConsent.gdprApplies === 'boolean') {
      params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0);
    }
    if (typeof gdprConsent.consentString === 'string') {
      params += '&gdpr_consent=' + gdprConsent.consentString;
    }
  }
  if (uspConsent && typeof uspConsent === 'string') {
    params += '&us_privacy=' + uspConsent;
  }
  if (syncOptions.iframeEnabled) {
    syncs.push({
      type: 'iframe',
      url: USER_SYNC_ENDPOINT + '?cb=' + new Date().getTime() + params
    });
  }
  if (syncOptions.pixelEnabled) {
    syncs.push({
      type: 'image',
      url: USER_SYNC_ENDPOINT + '?tag=img' + params
    });
  }
  return syncs;
}

function getBidFloor(bidRequest, mediaType, sizes) {
  const priceFloors = [];
  if (typeof bidRequest.getFloor === 'function') {
    sizes.forEach(size => {
      const floor = bidRequest.getFloor({
        currency: 'EUR',
        mediaType: mediaType || '*',
        size: [size.width, size.height]
      });
      floor.size = deepClone(size);
      if (!floor.floor) { floor.floor = null; }
      priceFloors.push(floor);
    });
  }
  return priceFloors;
}

export function isSchainValid(schain) {
  let isValid = false;
  const requiredFields = ['asi', 'sid', 'hp'];
  if (!schain || !schain.nodes) return isValid;
  isValid = schain.nodes.reduce((status, node) => {
    if (!status) return status;
    return requiredFields.every(field => node.hasOwnProperty(field));
  }, true);
  if (!isValid) {
    logError('OneTag: required schain params missing');
  }
  return isValid;
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVLID,
  supportedMediaTypes: [BANNER, VIDEO],
  isBidRequestValid: isBidRequestValid,
  buildRequests: buildRequests,
  interpretResponse: interpretResponse,
  getUserSyncs: getUserSyncs

};

registerBidder(spec);
<commit_msg>onetag fix (#8968)<commit_after>'use strict';

import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { INSTREAM, OUTSTREAM } from '../src/video.js';
import { Renderer } from '../src/Renderer.js';
import {find} from '../src/polyfill.js';
import { getStorageManager } from '../src/storageManager.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { deepClone, logError, deepAccess } from '../src/utils.js';

const ENDPOINT = 'https://onetag-sys.com/prebid-request';
const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/';
const BIDDER_CODE = 'onetag';
const GVLID = 241;

const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE});

/**
 * Determines whether or not the given bid request is valid.
 *
 * @param {BidRequest} bid The bid params to validate.
 * @return boolean True if this is a valid bid, and false otherwise.
 */
function isBidRequestValid(bid) {
  if (typeof bid === 'undefined' || typeof bid.params === 'undefined' || typeof bid.params.pubId !== 'string') {
    return false;
  }
  return isValid(BANNER, bid) || isValid(VIDEO, bid);
}

export function hasTypeVideo(bid) {
  return typeof bid.mediaTypes !== 'undefined' && typeof bid.mediaTypes.video !== 'undefined';
}

export function isValid(type, bid) {
  if (type === BANNER) {
    return parseSizes(bid).length > 0;
  } else if (type === VIDEO && hasTypeVideo(bid)) {
    const context = bid.mediaTypes.video.context;
    if (context === 'outstream' || context === 'instream') {
      return parseVideoSize(bid).length > 0;
    }
  }
  return false;
}

/**
 * Make a server request from the list of BidRequests.
 *
 * @param {validBidRequests[]} - an array of bids
 * @return ServerRequest Info describing the request to the server.
 */
function buildRequests(validBidRequests, bidderRequest) {
  const payload = {
    bids: requestsToBids(validBidRequests),
    ...getPageInfo()
  };
  if (bidderRequest && bidderRequest.gdprConsent) {
    payload.gdprConsent = {
      consentString: bidderRequest.gdprConsent.consentString,
      consentRequired: bidderRequest.gdprConsent.gdprApplies
    };
  }
  if (bidderRequest && bidderRequest.uspConsent) {
    payload.usPrivacy = bidderRequest.uspConsent;
  }
  if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userIdAsEids) {
    payload.userId = validBidRequests[0].userIdAsEids;
  }
  if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) {
    payload.schain = validBidRequests[0].schain;
  }
  try {
    if (storage.hasLocalStorage()) {
      payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid');
    }
  } catch (e) {}
  return {
    method: 'POST',
    url: ENDPOINT,
    data: JSON.stringify(payload)
  }
}

function interpretResponse(serverResponse, bidderRequest) {
  const body = serverResponse.body;
  const bids = [];
  const requestData = JSON.parse(bidderRequest.data);
  if (!body || (body.nobid && body.nobid === true)) {
    return bids;
  }
  if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) {
    return bids;
  }
  body.bids.forEach(bid => {
    const responseBid = {
      requestId: bid.requestId,
      cpm: bid.cpm,
      width: bid.width,
      height: bid.height,
      creativeId: bid.creativeId,
      dealId: bid.dealId == null ? bid.dealId : '',
      currency: bid.currency,
      netRevenue: bid.netRevenue || false,
      mediaType: bid.mediaType,
      meta: {
        mediaType: bid.mediaType,
        advertiserDomains: bid.adomain
      },
      ttl: bid.ttl || 300
    };
    if (bid.mediaType === BANNER) {
      responseBid.ad = bid.ad;
    } else if (bid.mediaType === VIDEO) {
      const {context, adUnitCode} = find(requestData.bids, (item) =>
        item.bidId === bid.requestId &&
        item.type === VIDEO
      );
      if (context === INSTREAM) {
        responseBid.vastUrl = bid.vastUrl;
        responseBid.videoCacheKey = bid.videoCacheKey;
      } else if (context === OUTSTREAM) {
        responseBid.vastXml = bid.ad;
        responseBid.vastUrl = bid.vastUrl;
        if (bid.rendererUrl) {
          responseBid.renderer = createRenderer({ ...bid, adUnitCode });
        }
      }
    }
    bids.push(responseBid);
  });
  return bids;
}

function createRenderer(bid, rendererOptions = {}) {
  const renderer = Renderer.install({
    id: bid.requestId,
    url: bid.rendererUrl,
    config: rendererOptions,
    adUnitCode: bid.adUnitCode,
    loaded: false
  });
  try {
    renderer.setRender(({renderer, width, height, vastXml, adUnitCode}) => {
      renderer.push(() => {
        window.onetag.Player.init({
          ...bid,
          width,
          height,
          vastXml,
          nodeId: adUnitCode,
          config: renderer.getConfig()
        });
      });
    });
  } catch (e) {

  }
  return renderer;
}

function getFrameNesting() {
  let topmostFrame = window;
  let parent = window.parent;
  let currentFrameNesting = 0;
  try {
    while (topmostFrame !== topmostFrame.parent) {
      parent = topmostFrame.parent;
      // eslint-disable-next-line no-unused-expressions
      parent.location.href;
      topmostFrame = topmostFrame.parent;
    }
  } catch (e) {
    currentFrameNesting = parent === topmostFrame.top ? 1 : 2;
  }
  return {
    topmostFrame,
    currentFrameNesting
  }
}

function getDocumentVisibility(window) {
  try {
    if (typeof window.document.hidden !== 'undefined') {
      return window.document.hidden;
    } else if (typeof window.document['msHidden'] !== 'undefined') {
      return window.document['msHidden'];
    } else if (typeof window.document['webkitHidden'] !== 'undefined') {
      return window.document['webkitHidden'];
    } else {
      return null;
    }
  } catch (e) {
    return null;
  }
}

/**
 * Returns information about the page needed by the server in an object to be converted in JSON
 * @returns {{location: *, referrer: (*|string), masked: *, wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}}
 */
function getPageInfo() {
  const { topmostFrame, currentFrameNesting } = getFrameNesting();
  return {
    location: topmostFrame.location.href,
    referrer:
      topmostFrame.document.referrer !== ''
        ? topmostFrame.document.referrer
        : null,
    ancestorOrigin:
      window.location.ancestorOrigins && window.location.ancestorOrigins.length > 0
        ? window.location.ancestorOrigins[window.location.ancestorOrigins.length - 1]
        : null,
    masked: currentFrameNesting,
    wWidth: topmostFrame.innerWidth,
    wHeight: topmostFrame.innerHeight,
    oWidth: topmostFrame.outerWidth,
    oHeight: topmostFrame.outerHeight,
    sWidth: topmostFrame.screen.width,
    sHeight: topmostFrame.screen.height,
    aWidth: topmostFrame.screen.availWidth,
    aHeight: topmostFrame.screen.availHeight,
    sLeft: 'screenLeft' in topmostFrame ? topmostFrame.screenLeft : topmostFrame.screenX,
    sTop: 'screenTop' in topmostFrame ? topmostFrame.screenTop : topmostFrame.screenY,
    xOffset: topmostFrame.pageXOffset,
    yOffset: topmostFrame.pageYOffset,
    docHidden: getDocumentVisibility(topmostFrame),
    docHeight: topmostFrame.document.body ? topmostFrame.document.body.scrollHeight : null,
    hLength: history.length,
    timing: getTiming(),
    version: {
      prebid: '$prebid.version$',
      adapter: '1.1.0'
    }
  };
}

function requestsToBids(bidRequests) {
  const videoBidRequests = bidRequests.filter(bidRequest => hasTypeVideo(bidRequest)).map(bidRequest => {
    const videoObj = {};
    setGeneralInfo.call(videoObj, bidRequest);
    // Pass parameters
    // Context: instream - outstream - adpod
    videoObj['context'] = bidRequest.mediaTypes.video.context;
    // Sizes
    videoObj['playerSize'] = parseVideoSize(bidRequest);
    // Other params
    videoObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.video);
    videoObj['type'] = VIDEO;
    videoObj['priceFloors'] = getBidFloor(bidRequest, VIDEO, videoObj['playerSize']);
    return videoObj;
  });
  const bannerBidRequests = bidRequests.filter(bidRequest => isValid(BANNER, bidRequest)).map(bidRequest => {
    const bannerObj = {};
    setGeneralInfo.call(bannerObj, bidRequest);
    bannerObj['sizes'] = parseSizes(bidRequest);
    bannerObj['type'] = BANNER;
    bannerObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.banner);
    bannerObj['priceFloors'] = getBidFloor(bidRequest, BANNER, bannerObj['sizes']);
    return bannerObj;
  });
  return videoBidRequests.concat(bannerBidRequests);
}

function setGeneralInfo(bidRequest) {
  const params = bidRequest.params;
  this['adUnitCode'] = bidRequest.adUnitCode;
  this['bidId'] = bidRequest.bidId;
  this['bidderRequestId'] = bidRequest.bidderRequestId;
  this['auctionId'] = bidRequest.auctionId;
  this['transactionId'] = bidRequest.transactionId;
  this['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot');
  this['pubId'] = params.pubId;
  this['ext'] = params.ext;
  if (params.pubClick) {
    this['click'] = params.pubClick;
  }
  if (params.dealId) {
    this['dealId'] = params.dealId;
  }
  const coords = getSpaceCoords(bidRequest.adUnitCode);
  if (coords) {
    this['coords'] = coords;
  }
}

function getSpaceCoords(id) {
  const space = document.getElementById(id);
  try {
    const { top, left, width, height } = space.getBoundingClientRect();
    let window = space.ownerDocument.defaultView;
    const coords = { top: top + window.pageYOffset, left: left + window.pageXOffset, width, height };
    let frame = window.frameElement;
    while (frame != null) {
      const { top, left } = frame.getBoundingClientRect();
      coords.top += top + window.pageYOffset;
      coords.left += left + window.pageXOffset;
      window = window.parent;
      frame = window.frameElement;
    }
    return coords;
  } catch (e) {
    return null;
  }
}

function getTiming() {
  try {
    if (window.performance != null && window.performance.timing != null) {
      const timing = {};
      const perf = window.performance.timing;
      timing.pageLoadTime = perf.loadEventEnd - perf.navigationStart;
      timing.connectTime = perf.responseEnd - perf.requestStart;
      timing.renderTime = perf.domComplete - perf.domLoading;
      return timing;
    }
  } catch (e) {
    return null;
  }
  return null;
}

function parseVideoSize(bid) {
  const playerSize = bid.mediaTypes.video.playerSize;
  if (typeof playerSize !== 'undefined' && Array.isArray(playerSize) && playerSize.length > 0) {
    return getSizes(playerSize)
  }
  return [];
}

function parseSizes(bid) {
  let ret = [];
  if (typeof bid.mediaTypes !== 'undefined' && typeof bid.mediaTypes.banner !== 'undefined' && typeof bid.mediaTypes.banner.sizes !== 'undefined' && Array.isArray(bid.mediaTypes.banner.sizes) && bid.mediaTypes.banner.sizes.length > 0) {
    return getSizes(bid.mediaTypes.banner.sizes)
  }
  const isVideoBidRequest = hasTypeVideo(bid);
  if (!isVideoBidRequest && bid.sizes && Array.isArray(bid.sizes)) {
    return getSizes(bid.sizes);
  }
  return ret;
}

function getSizes(sizes) {
  const ret = [];
  for (let i = 0; i < sizes.length; i++) {
    const size = sizes[i];
    ret.push({width: size[0], height: size[1]})
  }
  return ret;
}

function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) {
  let syncs = [];
  let params = '';
  if (gdprConsent) {
    if (typeof gdprConsent.gdprApplies === 'boolean') {
      params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0);
    }
    if (typeof gdprConsent.consentString === 'string') {
      params += '&gdpr_consent=' + gdprConsent.consentString;
    }
  }
  if (uspConsent && typeof uspConsent === 'string') {
    params += '&us_privacy=' + uspConsent;
  }
  if (syncOptions.iframeEnabled) {
    syncs.push({
      type: 'iframe',
      url: USER_SYNC_ENDPOINT + '?cb=' + new Date().getTime() + params
    });
  }
  if (syncOptions.pixelEnabled) {
    syncs.push({
      type: 'image',
      url: USER_SYNC_ENDPOINT + '?tag=img' + params
    });
  }
  return syncs;
}

function getBidFloor(bidRequest, mediaType, sizes) {
  const priceFloors = [];
  if (typeof bidRequest.getFloor === 'function') {
    sizes.forEach(size => {
      const floor = bidRequest.getFloor({
        currency: 'EUR',
        mediaType: mediaType || '*',
        size: [size.width, size.height]
      });
      floor.size = deepClone(size);
      if (!floor.floor) { floor.floor = null; }
      priceFloors.push(floor);
    });
  }
  return priceFloors;
}

export function isSchainValid(schain) {
  let isValid = false;
  const requiredFields = ['asi', 'sid', 'hp'];
  if (!schain || !schain.nodes) return isValid;
  isValid = schain.nodes.reduce((status, node) => {
    if (!status) return status;
    return requiredFields.every(field => node.hasOwnProperty(field));
  }, true);
  if (!isValid) {
    logError('OneTag: required schain params missing');
  }
  return isValid;
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVLID,
  supportedMediaTypes: [BANNER, VIDEO],
  isBidRequestValid: isBidRequestValid,
  buildRequests: buildRequests,
  interpretResponse: interpretResponse,
  getUserSyncs: getUserSyncs

};

registerBidder(spec);
<|endoftext|>


<commit_before>"use strict";

let {addMethod} = require("genfun"),
    {clone, init} = require("./lib/proto");

let $ = require("jquery");
let {EventListener, listen} = require("./lib/eventListener");

let can = require("./shims/can");
require("./shims/can.route.pushstate");

/**
 * Storychat router
 *
 * Sets up all the routes for the site and handles switching between them.
 */
let Router = clone();

let listener = clone(EventListener, {
  ":page route": page,
  "route": page
});

let pages = {
  "home": require("./pages/home"),
  "404": require("./pages/404")
};

addMethod(init, [Router], function(router, conn) {
  router.listenerHandle = listen(listener, router, window);
  router.conn = conn;
  initCanRoute();
});

function initCanRoute() {
  if (window.history && window.history.pushState && window.location.hash) {
    let oldPushState,
        oldSetURL;
    window.history.replaceState(null, null, window.location.hash.substr(2));
    window.location.hash = "";
    // XXX HACK - This maneuver is done to prevent the initial route event from
    //            pushing an unnecessary entry into the browser's history.
    oldPushState = window.history.pushState;
    oldSetURL = can.route.bindings.pushstate.setURL;
    can.route.bindings.pushstate.setURL = function() {
      window.history.pushState = window.history.replaceState;
      oldSetURL.apply(this, arguments);
      window.history.pushState = oldPushState;
      can.route.bindings.pushstate.setURL = oldSetURL;
    };
  }
  can.route.ready();
}

function page(router, data) {
  let nextPage = pages[data.page || "home"] || pages["404"];
  $("#page").remove();
  nextPage($("
").appendTo("body"), router.conn, data);
}

module.exports.Router = Router;
<commit_msg>Keep the page around in the router<commit_after>"use strict";

let {addMethod} = require("genfun"),
    {clone, init} = require("./lib/proto");

let $ = require("jquery");
let {EventListener, listen} = require("./lib/eventListener");

let can = require("./shims/can");
require("./shims/can.route.pushstate");

/**
 * Storychat router
 *
 * Sets up all the routes for the site and handles switching between them.
 */
let Router = clone();

let listener = clone(EventListener, {
  ":page route": page,
  "route": page
});

let pages = {
  "home": require("./pages/home"),
  "404": require("./pages/404")
};

addMethod(init, [Router], function(router, conn) {
  router.listenerHandle = listen(listener, router, window);
  router.conn = conn;
  initCanRoute();
});

function initCanRoute() {
  if (window.history && window.history.pushState && window.location.hash) {
    let oldPushState,
        oldSetURL;
    window.history.replaceState(null, null, window.location.hash.substr(2));
    window.location.hash = "";
    // XXX HACK - This maneuver is done to prevent the initial route event from
    //            pushing an unnecessary entry into the browser's history.
    oldPushState = window.history.pushState;
    oldSetURL = can.route.bindings.pushstate.setURL;
    can.route.bindings.pushstate.setURL = function() {
      window.history.pushState = window.history.replaceState;
      oldSetURL.apply(this, arguments);
      window.history.pushState = oldPushState;
      can.route.bindings.pushstate.setURL = oldSetURL;
    };
  }
  can.route.ready();
}

function page(router, data) {
  let next = pages[data.page || "home"] || pages["404"];
  $("#page").remove();
  router.page = next($("
").appendTo("body"), router.conn, data);
}

module.exports.Router = Router;
<|endoftext|>


<commit_before>const os = require('os');

const net = require('net');
const path = require('path');
const electron = require('electron');
const fs = require('fs-extra');
const hidefile = require('hidefile');
const windowStateKeeper = require('electron-window-state');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const int_envelope_port = parseInt(Math.random().toString().substring(2)) % (65535 - 1024) + 1024;

const child_process = require('child_process');
var envelopeProc = null;
var postgresqlProc = null;

const postgresql_module_name = 'node_modules/postgresql-portable-' + (os.platform() != 'win32' ? (os.platform() + (os.arch() === 'x64' ? '64' : '32')) : 'windows');
const envelope_module_name = 'node_modules/envelope-portable-' + (os.platform() != 'win32' ? (os.platform() + (os.arch() === 'x64' ? '64' : '32')) : 'windows');

function spawnEnvelope(strAppName, callback) {
	envelopeProc = child_process.spawn(
		path.normalize(app.getAppPath() + '/' + envelope_module_name + '/bin/envelope' + (process.platform == 'win32' ? '.exe' : '')), [
			'-c', path.normalize(app.getAppPath() + '/envelope/config/envelope.conf'),
			'-d', path.normalize(os.homedir() + '/.' + strAppName + '/envelope-connections.conf'),
			'-r', path.normalize(app.getAppPath() + '/envelope/web_root'),
			'-y', path.normalize(app.getAppPath() + '/envelope/app'),
			//'-z', path.normalize(app.getAppPath() + '/envelope/role'),
			'-x', 't',
			'-p', int_envelope_port,
			(process.platform == 'win32' ? '-o' : ''), (process.platform == 'win32' ? 'stderr' : '')
		], {
			detached: true
		}
	);

	console.log('test');
	envelopeProc.stdout.on('data', function(data) {
		console.log('envelope ' + envelopeProc.pid + ' got data (stdout):\n' + data);
		if (data.indexOf('<this computer\'s ip>') > -1) {
			callback(int_envelope_port);
		}
	});
	envelopeProc.stderr.on('data', function(data) {
		console.log('envelope ' + envelopeProc.pid + ' got data (stderr):\n' + data);
	});
	envelopeProc.on('close', function(code) {
		console.log('envelope ' + envelopeProc.pid + ' closed with code ' + code);
	});
}

exports.init = function (strAppName, callback) {
	try {
		// Check for postgres data
		fs.statSync(os.homedir() + '/.' + strAppName + '/data');

		// Start postgres
		postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
			'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
			'-k', '/tmp'
		], {
			cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
		});
		postgresqlProc.stderr.on('data', function(data) {
			console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);

			if (data.indexOf('database system is ready to accept connections') > -1) {
				spawnEnvelope(strAppName, callback);
			}
		});
		postgresqlProc.on('close', function(code) {
			console.log(postgresqlProc.pid + ' closed with code ' + code);
		});
	} catch (e) {
		// Open the progress window
		var progressWindow = new BrowserWindow({
			width: 800,
			height: 100,
			frame: false
		});

		// Set the content
		progressWindow.loadURL('about:blank');
		progressWindow.webContents.executeJavaScript(
			'document.body.innerHTML = \'' +
			'	   Performing one-time setup' +
			'	   <progress style="width: 100%; display: inline-block; text-align: center;" value="0" max="1000" />' +
			'\';' +
			'document.body.style.background = \'none\';' +
			'document.body.style.overflow = \'none\';' +
			'document.body.style.margin = \'0\';' +
			'document.body.style.padding = \'0\';' +
			'document.body.style.position = \'relative\';',
			function() {
				// Create the data directory
				fs.mkdirsSync(os.homedir() + '/.' + strAppName + '/');
				hidefile.hideSync(os.homedir() + '/.' + strAppName + '/');
				fs.mkdirsSync(os.homedir() + '/.' + strAppName + '/data');

				const int_postgres_port = parseInt(Math.random().toString().substring(2)) % (65535 - 1024) + 1024;
				postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/initdb' + (process.platform == 'win32' ? '.exe' : '')), [
					'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
					'-E', 'UTF8',
					'-U', 'postgres'
				], {
					cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
				});

				// Every line of stdout advances the progress bar
				postgresqlProc.stdout.on('data', function(data) {
					progressWindow.webContents.executeJavaScript(
						'var progress = document.getElementsByTagName(\'progress\')[0];' +
						'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 1.5 + ';'
					);
					console.log('initdb ' + postgresqlProc.pid + ' got data:\n' + data);
				});
				postgresqlProc.stderr.on('data', function(data) {
					console.log('initdb ' + postgresqlProc.pid + ' got data:\n' + data);
				});
				postgresqlProc.on('close', function(code) {
					console.log('initdb ' + postgresqlProc.pid + ' closed with code ' + code);

					// Add some stuff to postgresql.conf
					fs.appendFileSync(path.normalize(os.homedir() + '/.' + strAppName + '/data/postgresql.conf'),
						'\n\nport = ' + int_postgres_port + '\nlog_destination = stderr\nlogging_collector = off\n\n');

					// spawn postgres
					postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
						'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
						'-k', '/tmp'
					], {
						cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
					});

					postgresqlProc.stdout.on('data', function(data) {
						console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
					});
					postgresqlProc.stderr.on('data', function(data) {
						console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
						progressWindow.webContents.executeJavaScript(
							'var progress = document.getElementsByTagName(\'progress\')[0];' +
							'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
						);

						// When we are ready
						if (data.indexOf('database system is ready to accept connections') > -1) {
							// Run init.sql againts the database
							var psqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/psql' + (process.platform == 'win32' ? '.exe' : '')), [
								'-f', path.normalize(app.getAppPath() + '/init.sql'),
								'-h', (process.platform == 'win32' ? '127.0.0.1' : '/tmp'),
								'-p', int_postgres_port,
								'-U', 'postgres'
							], {
								cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
							});

							psqlProc.stdout.on('data', function(data) {
								console.log('psql ' + psqlProc.pid + ' got data:\n' + data);
							});
							psqlProc.stderr.on('data', function(data) {
								console.log('psql ' + psqlProc.pid + ' got data:\n' + data);
								progressWindow.webContents.executeJavaScript(
									'var progress = document.getElementsByTagName(\'progress\')[0];' +
									'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
								);
							});
							psqlProc.on('close', function(code) {
								console.log('psql ' + psqlProc.pid + ' closed with code ' + code);

								// Set up pg_hba.conf
								if (process.platform == 'win32') {
									fs.writeFileSync(
										path.normalize(os.homedir() + '/.' + strAppName + '/data/pg_hba.conf'),
										'host		  all			 all			127.0.0.1/32			 md5'
									);
								} else {
									fs.writeFileSync(
										path.normalize(os.homedir() + '/.' + strAppName + '/data/pg_hba.conf'),
										'local		  all			 all			 md5'
									);
								}
								// Restart postgresql (by listening for close, and then killing)
								postgresqlProc.on('close', function(code) {
									postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
										'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
										'-k', '/tmp'
									], {
										cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
									});

									postgresqlProc.stdout.on('data', function(data) {
										console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
									});
									postgresqlProc.stderr.on('data', function thisCallback(data) {
										console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
										progressWindow.webContents.executeJavaScript(
											'var progress = document.getElementsByTagName(\'progress\')[0];' +
											'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
										);

										if (data.indexOf('database system is ready to accept connections') > -1) {
											postgresqlProc.stderr.removeListener('data', thisCallback);
											fs.writeFileSync(
												path.normalize(os.homedir() + '/.' + strAppName + '/envelope-connections.conf'),
												'data:  host=' + (process.platform == 'win32' ? '127.0.0.1' : '/tmp') + ' port=' + int_postgres_port + ' dbname=postgres'
											);
											// Start up envelope
											spawnEnvelope(strAppName, function (envelopePort) {
												callback(envelopePort);

												// Close progress window
												progressWindow.close();
											});
										}
									});
									postgresqlProc.on('close', function(code) {
										console.log('postgres ' + postgresqlProc.pid + ' closed with code ' + code);
									});
								});

								var pipe = net.connect('\\\\.\\pipe\\pgsignal_' + postgresqlProc.pid, function () {
									var uint8Data = new Uint8Array(1),
										data = new Buffer(uint8Data.buffer);
									uint8Data[0] = 15; // SIGTERM
									pipe.on('error', function () {
										console.log('error', arguments);
									});

									pipe.write(data);
									pipe.end();
								});
								//postgresqlProc.kill();
							});
						}
					});
					postgresqlProc.on('close', function(code) {
						console.log('postgres ' + postgresqlProc.pid + ' closed with code ' + code);
					});
				});
			}
		);
	}
};

exports.quit = function () {
	console.log('quitting');
	envelopeProc.kill();
	var pipe = net.connect('\\\\.\\pipe\\pgsignal_' + postgresqlProc.pid, function () {
		var uint8Data = new Uint8Array(1),
			data = new Buffer(uint8Data.buffer);
		uint8Data[0] = 15; // SIGTERM
		pipe.on('error', function () {
			console.log('error', arguments);
		});

		pipe.write(data);
		pipe.end();
	});
	process.exit();
};
<commit_msg>envelope/web_root > web_root<commit_after>const os = require('os');

const net = require('net');
const path = require('path');
const electron = require('electron');
const fs = require('fs-extra');
const hidefile = require('hidefile');
const windowStateKeeper = require('electron-window-state');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const int_envelope_port = parseInt(Math.random().toString().substring(2)) % (65535 - 1024) + 1024;

const child_process = require('child_process');
var envelopeProc = null;
var postgresqlProc = null;

const postgresql_module_name = 'node_modules/postgresql-portable-' + (os.platform() != 'win32' ? (os.platform() + (os.arch() === 'x64' ? '64' : '32')) : 'windows');
const envelope_module_name = 'node_modules/envelope-portable-' + (os.platform() != 'win32' ? (os.platform() + (os.arch() === 'x64' ? '64' : '32')) : 'windows');

function spawnEnvelope(strAppName, callback) {
	envelopeProc = child_process.spawn(
		path.normalize(app.getAppPath() + '/' + envelope_module_name + '/bin/envelope' + (process.platform == 'win32' ? '.exe' : '')), [
			'-d', path.normalize(os.homedir() + '/.' + strAppName + '/envelope-connections.conf'),
			'-r', path.normalize(app.getAppPath() + '/web_root'),
			'-y', path.normalize(app.getAppPath() + '/app'),
			//'-z', path.normalize(app.getAppPath() + '/envelope/role'),
			'-x', 't',
			'-p', int_envelope_port,
			(process.platform == 'win32' ? '-o' : ''), (process.platform == 'win32' ? 'stderr' : '')
		], {
			detached: true
		}
	);

	console.log('test');
	envelopeProc.stdout.on('data', function(data) {
		console.log('envelope ' + envelopeProc.pid + ' got data (stdout):\n' + data);
		if (data.indexOf('<this computer\'s ip>') > -1) {
			callback(int_envelope_port);
		}
	});
	envelopeProc.stderr.on('data', function(data) {
		console.log('envelope ' + envelopeProc.pid + ' got data (stderr):\n' + data);
	});
	envelopeProc.on('close', function(code) {
		console.log('envelope ' + envelopeProc.pid + ' closed with code ' + code);
	});
}

exports.init = function (strAppName, callback) {
	try {
		// Check for postgres data
		fs.statSync(os.homedir() + '/.' + strAppName + '/data');

		// Start postgres
		postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
			'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
			'-k', '/tmp'
		], {
			cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
		});
		postgresqlProc.stderr.on('data', function(data) {
			console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);

			if (data.indexOf('database system is ready to accept connections') > -1) {
				spawnEnvelope(strAppName, callback);
			}
		});
		postgresqlProc.on('close', function(code) {
			console.log(postgresqlProc.pid + ' closed with code ' + code);
		});
	} catch (e) {
		// Open the progress window
		var progressWindow = new BrowserWindow({
			width: 800,
			height: 100,
			frame: false
		});

		// Set the content
		progressWindow.loadURL('about:blank');
		progressWindow.webContents.executeJavaScript(
			'document.body.innerHTML = \'' +
			'	   Performing one-time setup' +
			'	   <progress style="width: 100%; display: inline-block; text-align: center;" value="0" max="1000" />' +
			'\';' +
			'document.body.style.background = \'none\';' +
			'document.body.style.overflow = \'none\';' +
			'document.body.style.margin = \'0\';' +
			'document.body.style.padding = \'0\';' +
			'document.body.style.position = \'relative\';',
			function() {
				// Create the data directory
				fs.mkdirsSync(os.homedir() + '/.' + strAppName + '/');
				hidefile.hideSync(os.homedir() + '/.' + strAppName + '/');
				fs.mkdirsSync(os.homedir() + '/.' + strAppName + '/data');

				const int_postgres_port = parseInt(Math.random().toString().substring(2)) % (65535 - 1024) + 1024;
				postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/initdb' + (process.platform == 'win32' ? '.exe' : '')), [
					'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
					'-E', 'UTF8',
					'-U', 'postgres'
				], {
					cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
				});

				// Every line of stdout advances the progress bar
				postgresqlProc.stdout.on('data', function(data) {
					progressWindow.webContents.executeJavaScript(
						'var progress = document.getElementsByTagName(\'progress\')[0];' +
						'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 1.5 + ';'
					);
					console.log('initdb ' + postgresqlProc.pid + ' got data:\n' + data);
				});
				postgresqlProc.stderr.on('data', function(data) {
					console.log('initdb ' + postgresqlProc.pid + ' got data:\n' + data);
				});
				postgresqlProc.on('close', function(code) {
					console.log('initdb ' + postgresqlProc.pid + ' closed with code ' + code);

					// Add some stuff to postgresql.conf
					fs.appendFileSync(path.normalize(os.homedir() + '/.' + strAppName + '/data/postgresql.conf'),
						'\n\nport = ' + int_postgres_port + '\nlog_destination = stderr\nlogging_collector = off\n\n');

					// spawn postgres
					postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
						'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
						'-k', '/tmp'
					], {
						cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
					});

					postgresqlProc.stdout.on('data', function(data) {
						console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
					});
					postgresqlProc.stderr.on('data', function(data) {
						console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
						progressWindow.webContents.executeJavaScript(
							'var progress = document.getElementsByTagName(\'progress\')[0];' +
							'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
						);

						// When we are ready
						if (data.indexOf('database system is ready to accept connections') > -1) {
							// Run init.sql againts the database
							var psqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/psql' + (process.platform == 'win32' ? '.exe' : '')), [
								'-f', path.normalize(app.getAppPath() + '/init.sql'),
								'-h', (process.platform == 'win32' ? '127.0.0.1' : '/tmp'),
								'-p', int_postgres_port,
								'-U', 'postgres'
							], {
								cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
							});

							psqlProc.stdout.on('data', function(data) {
								console.log('psql ' + psqlProc.pid + ' got data:\n' + data);
							});
							psqlProc.stderr.on('data', function(data) {
								console.log('psql ' + psqlProc.pid + ' got data:\n' + data);
								progressWindow.webContents.executeJavaScript(
									'var progress = document.getElementsByTagName(\'progress\')[0];' +
									'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
								);
							});
							psqlProc.on('close', function(code) {
								console.log('psql ' + psqlProc.pid + ' closed with code ' + code);

								// Set up pg_hba.conf
								if (process.platform == 'win32') {
									fs.writeFileSync(
										path.normalize(os.homedir() + '/.' + strAppName + '/data/pg_hba.conf'),
										'host		  all			 all			127.0.0.1/32			 md5'
									);
								} else {
									fs.writeFileSync(
										path.normalize(os.homedir() + '/.' + strAppName + '/data/pg_hba.conf'),
										'local		  all			 all			 md5'
									);
								}
								// Restart postgresql (by listening for close, and then killing)
								postgresqlProc.on('close', function(code) {
									postgresqlProc = child_process.spawn(path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/postgres' + (process.platform == 'win32' ? '.exe' : '')), [
										'-D', path.normalize(os.homedir() + '/.' + strAppName + '/data'),
										'-k', '/tmp'
									], {
										cwd: path.normalize(app.getAppPath() + '/' + postgresql_module_name + '/bin/')
									});

									postgresqlProc.stdout.on('data', function(data) {
										console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
									});
									postgresqlProc.stderr.on('data', function thisCallback(data) {
										console.log('postgres ' + postgresqlProc.pid + ' got data:\n' + data);
										progressWindow.webContents.executeJavaScript(
											'var progress = document.getElementsByTagName(\'progress\')[0];' +
											'progress.value = parseInt(progress.value, 10) + ' + data.toString().length / 2 + ';'
										);

										if (data.indexOf('database system is ready to accept connections') > -1) {
											postgresqlProc.stderr.removeListener('data', thisCallback);
											fs.writeFileSync(
												path.normalize(os.homedir() + '/.' + strAppName + '/envelope-connections.conf'),
												'data:  host=' + (process.platform == 'win32' ? '127.0.0.1' : '/tmp') + ' port=' + int_postgres_port + ' dbname=postgres'
											);
											// Start up envelope
											spawnEnvelope(strAppName, function (envelopePort) {
												callback(envelopePort);

												// Close progress window
												progressWindow.close();
											});
										}
									});
									postgresqlProc.on('close', function(code) {
										console.log('postgres ' + postgresqlProc.pid + ' closed with code ' + code);
									});
								});

								var pipe = net.connect('\\\\.\\pipe\\pgsignal_' + postgresqlProc.pid, function () {
									var uint8Data = new Uint8Array(1),
										data = new Buffer(uint8Data.buffer);
									uint8Data[0] = 15; // SIGTERM
									pipe.on('error', function () {
										console.log('error', arguments);
									});

									pipe.write(data);
									pipe.end();
								});
								//postgresqlProc.kill();
							});
						}
					});
					postgresqlProc.on('close', function(code) {
						console.log('postgres ' + postgresqlProc.pid + ' closed with code ' + code);
					});
				});
			}
		);
	}
};

exports.quit = function () {
	console.log('quitting');
	envelopeProc.kill();
	var pipe = net.connect('\\\\.\\pipe\\pgsignal_' + postgresqlProc.pid, function () {
		var uint8Data = new Uint8Array(1),
			data = new Buffer(uint8Data.buffer);
		uint8Data[0] = 15; // SIGTERM
		pipe.on('error', function () {
			console.log('error', arguments);
		});

		pipe.write(data);
		pipe.end();
	});
	process.exit();
};
<|endoftext|>


<commit_before><commit_msg>Order comments by time closes #16<commit_after><|endoftext|>