<commit_before>/*
 #
 # Copyright (c) 2017 nexB Inc. and others. All rights reserved.
 # https://nexb.com and https://github.com/nexB/aboutcode-manager/
 # The ScanCode software is licensed under the Apache License version 2.0.
 # AboutCode is a trademark of nexB Inc.
 #
 # You may not use this software except in compliance with the License.
 # You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
 # Unless required by applicable law or agreed to in writing, software distributed
 # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 #
 */

const AboutCodeDB = require('./aboutCodeDB');
const Splitter = require('./helpers/splitter');
const Progress = require('./helpers/progress');

const DejaCodeExportDialog = require('./controllers/dejacodeExportDialog');
const ComponentDialog = require('./controllers/componentDialog');
const AboutCodeDashboard = require('./controllers/aboutCodeDashboard');
const AboutCodeBarChart = require('./controllers/aboutCodeBarChart');
const AboutCodeJsTree = require('./controllers/aboutCodeJsTree');
const AboutCodeClueDataTable = require('./controllers/aboutCodeClueDataTable');
const AboutCodeComponentDataTable = require('./controllers/aboutCodeComponentDataTable');

const fs = require('fs');
const shell = require('electron').shell;
const dialog = require('electron').remote.dialog;
const path = require('path');

// The Electron module used to communicate asynchronously from a renderer process to the main process.
const ipcRenderer = require('electron').ipcRenderer;
const aboutCodeVersion = require('../../../package.json').version;

/**
 * This is the UI Controller for the application. It's responsible for
 * initializing and updating all of the views and handling user interaction
 */
$(document).ready(() => {
  // Create default values for all of the data and ui classes
  let aboutCodeDB = new AboutCodeDB();

  const dashboard = new AboutCodeDashboard('#tab-dashboard', aboutCodeDB);

  const barChart = new AboutCodeBarChart('#tab-barchart', aboutCodeDB)
    .on('bar-clicked', (attribute, value) => {
      // Show files that contain attribute value selected by user in bar chart
      cluesTable.clearColumnFilters();
      if (value !== 'No Value Detected') {
        cluesTable.setColumnFilter(attribute, value);
      } else {
        cluesTable.setColumnFilter(attribute, 'about_code_data_table_no_value_detected');
      }

      updateViewsByPath(cluesTable._selectedPath);

      // This needs to be done only when the column is visible.
      // So we do it last to try our best
      showClueButton.trigger('click');
    });

  const cluesTable = new AboutCodeClueDataTable('#tab-clues', aboutCodeDB);

  const componentsTable = new AboutCodeComponentDataTable('#tab-component', aboutCodeDB)
    .on('upload-clicked', (components) => {
      if (components.length > 0) {
        dejaCodeExportDialog.show();
      } else {
        dialog.showErrorBox(
          'No Components to Upload',
          'You have no Components to upload.\n\n' +
                    'Please create at least one Component and try again.');
      }
    })
    .on('export-json', exportJsonComponents);

  const componentDialog = new ComponentDialog('#componentDialog', aboutCodeDB)
    .on('save', () => {
      componentsTable.needsReload(true);
      redrawCurrentView();
    })
    .on('delete', () => {
      componentsTable.needsReload(true);
      redrawCurrentView();
    });

  const dejaCodeExportDialog =
        new DejaCodeExportDialog('#componentExportModal', aboutCodeDB);

  const jstree = new AboutCodeJsTree('#jstree', aboutCodeDB)
    .on('node-edit', (node) => componentDialog.show(node.id))
    .on('node-selected', (node) => {
      updateViewsByPath(node.id);
    });

  $(document).on('click', '#activate-filters-button', () => {
    cluesTable.genFilters();
    updateViewsByPath(cluesTable._selectedPath);
  });

  $(document).on('click', '#clear-filters-button', () => {
    cluesTable.resetColumnFilters();
    updateViewsByPath(cluesTable._selectedPath);
  });

  const splitter = new Splitter('#leftCol', '#rightCol')
    .on('drag-end', () => redrawCurrentView());

  // The id of the currently selected nav bar button.
  const currentNavButtonId = '#sidebar-wrapper .sidebar-nav .active button';

  // Defines DOM element constants for sidebar buttons.
  const saveSQLiteFileButton = $('#save-file');
  const openSQLiteFileButton = $('#open-file');
  const showClueButton = $('#show-tab-clues');
  const showComponentButton = $('#show-tab-component');
  const showBarChartButton = $('#show-tab-barchart');
  const showDashboardButton = $('#show-tab-dashboard');

  // Open a SQLite Database File
  openSQLiteFileButton.click(openSQLite);

  // Save a SQLite Database file
  saveSQLiteFileButton.click(saveSQLite);

  // Show clue DataTable. Hide node view and component summary table
  showClueButton.click(() => {
    splitter.show();
    cluesTable.redraw();
  });

  // Show component summary table. Hide DataTable and node view
  showComponentButton.click(() => {
    splitter.hide();
    componentsTable.redraw();
  });

  showBarChartButton.click(() => {
    splitter.show();
    barChart.redraw();
  });

  showDashboardButton.click(() => {
    splitter.hide();
    dashboard.redraw();
  });

  // Open links in default browser
  $('.open-in-default').click((evt) => {
    evt.preventDefault();
    shell.openExternal(evt.target.href);
  });

  ipcRenderer.on('table-view', () => showClueButton.trigger('click'));
  ipcRenderer.on('component-summary-view', () => showComponentButton.trigger('click'));
  ipcRenderer.on('open-SQLite', () => openSQLiteFileButton.trigger('click'));
  ipcRenderer.on('chart-summary-view', () => showBarChartButton.trigger('click'));
  ipcRenderer.on('save-SQLite', () => saveSQLiteFileButton.trigger('click'));
  ipcRenderer.on('import-JSON', importJson);
  ipcRenderer.on('export-JSON', exportJson);
  ipcRenderer.on('export-JSON-components-only', exportJsonComponents);
  ipcRenderer.on('get-ScanInfo', getScanInfo);

  // Opens the dashboard view when the app is first opened
  showDashboardButton.trigger('click');

  function updateViewsByPath(path) {
    // Update all the views with the given path string
    cluesTable.columns(0).search(path);

    componentDialog.selectedPath(path);
    dejaCodeExportDialog.selectedPath(path);
    jstree.selectedPath(path);
    cluesTable.selectedPath(path);
    componentsTable.selectedPath(path);
    dashboard.selectedPath(path);
    barChart.selectedPath(path);

    redrawCurrentView();
  }

  /** Creates the database and all View objects from a SQLite file */
  function loadDatabase(fileName) {
    // Create a new database when importing a json file
    aboutCodeDB = new AboutCodeDB({
      dbName: 'aboutcode_db',
      dbStorage: fileName
    });

    // Check that the database has the correct header information.
    aboutCodeDB.sync
      .then((db) => db.Header.findAll())
      .then((headers) => {
        if (headers.length === 0) {
          dialog.showErrorBox(
            'Invalid SQLite file: ' + fileName,
            'The SQLite file is invalid. Try re-importing the ScanCode JSON ' +
            'file and creating a new SQLite file.');
        }
      });

    return updateViews();
  }

  // Get the ScanCode version and options data from the DB and populate and open the modal
  function getScanInfo() {
    return aboutCodeDB.sync
    .then((db) => db.Header.findById(1).then(header => {
      scancode_label = $('#scancode-label').find('#scancode-display');
      scancode_label.text('ScanCode version: ' + header.scancode_version + '\n\nScanCode options: ' + JSON.stringify(header.scancode_options, null, 2));
    }))
    .then($("#myModal").modal('show'));
  }

  /** Loads data for all views based on the current data */
  function updateViews() {
    return aboutCodeDB.sync
      .then(() => {
        const currFile = aboutCodeDB.sequelize.options.storage;
        document.title = 'AboutCode Manager - ' + path.basename(currFile);
        cluesTable.clearColumnFilters();

        // update all views with the new database.
        componentDialog.db(aboutCodeDB);
        dejaCodeExportDialog.db(aboutCodeDB);
        jstree.db(aboutCodeDB);
        cluesTable.db(aboutCodeDB);
        componentsTable.db(aboutCodeDB);
        dashboard.db(aboutCodeDB);
        barChart.db(aboutCodeDB);

        // Reload the jstree, then trigger the current view to reload.
        jstree.redraw();
      })
      .catch((reason) => { throw reason; });
  }

  function redrawCurrentView() {
    $(currentNavButtonId).trigger('click');
  }

  /** Open a SQLite Database File */
  function openSQLite() {
    dialog.showOpenDialog({
      properties: ['openFile'],
      title: 'Open a SQLite File',
      filters: [{
        name: 'SQLite File',
        extensions: ['sqlite']
      }]
    }, (fileNames) => {
      if (fileNames && fileNames[0]) {
        loadDatabase(fileNames[0]);
      }
    });
  }

  /** Save a SQLite Database File */
  function saveSQLite() {
    dialog.showSaveDialog({
      title: 'Save as a Database File',
      filters: [
        { name: 'SQLite File', extensions: ['sqlite'] }
      ]
    }, (newFileName) => {
      if (newFileName) {
        const oldFileName = aboutCodeDB.sequelize.options.storage;
        const reader = fs.createReadStream(oldFileName);
        const writer = fs.createWriteStream(newFileName);
        reader.pipe(writer);
        reader.on('end', () => loadDatabase(newFileName));
      }
    });
  }

  /** Import a ScanCode JSON file and create a SQLite database */
  function importJson() {
    dialog.showOpenDialog({
      title: 'Open a JSON File',
      filters: [{
        name: 'JSON File',
        extensions: ['json']
      }]
    }, (fileNames) => {
      if (fileNames === undefined) {
        return;
      }

      const jsonFileName = fileNames[0];

      // Immediately ask for a SQLite to save and create the database
      dialog.showSaveDialog({
        title: 'Save a SQLite Database File',
        filters: [{
          name: 'SQLite File',
          extensions: ['sqlite']
        }]
      }, (sqliteFileName) => {
        if (sqliteFileName === undefined) {
          return;
        }

        // Overwrite existing sqlite file
        if (fs.existsSync(sqliteFileName)) {
          fs.unlink(sqliteFileName, (err) => {
            if (err) {
              throw err;
            }
            console.info(`Deleted ${sqliteFileName}`);
          });
        }

        // Create a new database when importing a json file
        aboutCodeDB = new AboutCodeDB({
          dbName: 'demo_schema',
          dbStorage: sqliteFileName,
        });

        const progressbar = new Progress('#content', {
          title: 'Creating Database...',
          size: 100,
        });

        aboutCodeDB.sync
          .then(() => progressbar.showDeterminate())
          .then(() => aboutCodeDB.addFromJson(
            jsonFileName,
            aboutCodeVersion,
            (progress) => progressbar.update(progress / 100)))
          .then(() => progressbar.hide())
          .then(updateViews)
          .catch((err) => {
            progressbar.hide();
            if (err instanceof AboutCodeDB.MissingFileInfoError) {
              dialog.showErrorBox(
                'Missing File Type Information',
                'Missing file \'type\' information in the scanned data. ' +
                '\n\nThis probably means you ran the scan without the -i ' +
                'option in ScanCode. The app requires file information from ' +
                'a ScanCode scan. Rerun the scan using \n./scancode ' +
                '-clipeu options.'
              );
            } else {
              // Show error for problem with the JSON file
              dialog.showErrorBox(
                'JSON Error',
                'There is a problem with your JSON file. It may be malformed ' +
                '(e.g., the addition of a trailing comma), ' +
                'or there could be some other problem with the file. ' +
                '\n\nPlease check your file and try again. ' +
                '\n\nThe error thrown by the system is: \n\n' + err
              );
            }
            console.error(err);
          });
      });
    });
  }

  /** Export JSON file with original ScanCode data and components that have been created */
  function exportJson() {
    dialog.showSaveDialog({
      properties: ['openFile'],
      title: 'Save as JSON file',
      filters: [{
        name: 'JSON File Type',
        extensions: ['json']
      }]
    }, (fileName) => {
      if (fileName === undefined) {
        return;
      }

      const scanCodeInfoPromise = aboutCodeDB.getScanCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const aboutCodeInfoPromise = aboutCodeDB.getAboutCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const clueFilesPromise = aboutCodeDB.findAll({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const componentsPromise = aboutCodeDB.findAllComponents({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const filesCountPromise = aboutCodeDB.getFileCount({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      Promise.all([scanCodeInfoPromise, aboutCodeInfoPromise,
        filesCountPromise, clueFilesPromise, componentsPromise])
        .then(([scanCodeInfo, aboutCodeInfo, filesCount, clueFiles, components]) => {
          const json = {
            aboutcode_manager_notice: aboutCodeInfo.aboutcode_manager_notice,
            aboutcode_manager_version: aboutCodeInfo.aboutcode_manager_version,
            scancode_version: scanCodeInfo.scancode_version,
            scancode_options: scanCodeInfo.scancode_options,
            files_count: filesCount,
            files: clueFiles,
            components: components
          };

          fs.writeFile(fileName, JSON.stringify(json), (err) => {
            if (err) {
              throw err;
            }
          });
        });
    });
  }

  /** Export JSON file with only components that have been created */
  function exportJsonComponents() {
    dialog.showSaveDialog({
      properties: ['openFile'],
      title: 'Save as JSON file',
      filters: [{
        name: 'JSON File Type',
        extensions: ['json']
      }]
    }, (fileName) => {
      if (fileName === undefined) {
        return;
      }

      const aboutCodeInfoPromise = aboutCodeDB.getAboutCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const componentsPromise = aboutCodeDB.findAllComponents({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      Promise.all([aboutCodeInfoPromise, componentsPromise])
        .then(([aboutCodeInfo, components]) => {
          const json = {
            aboutcode_manager_notice: aboutCodeInfo.aboutcode_manager_notice,
            aboutcode_manager_version: aboutCodeInfo.aboutcode_manager_version,
            components: components
          };

          fs.writeFile(fileName, JSON.stringify(json), (err) => {
            if (err) {
              throw err;
            }
          });
        });
    });
  }
});

module.exports = aboutCodeVersion;
<commit_msg>Fix ESLint errors #239<commit_after>/*
 #
 # Copyright (c) 2017 nexB Inc. and others. All rights reserved.
 # https://nexb.com and https://github.com/nexB/aboutcode-manager/
 # The ScanCode software is licensed under the Apache License version 2.0.
 # AboutCode is a trademark of nexB Inc.
 #
 # You may not use this software except in compliance with the License.
 # You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
 # Unless required by applicable law or agreed to in writing, software distributed
 # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 #
 */

const AboutCodeDB = require('./aboutCodeDB');
const Splitter = require('./helpers/splitter');
const Progress = require('./helpers/progress');

const DejaCodeExportDialog = require('./controllers/dejacodeExportDialog');
const ComponentDialog = require('./controllers/componentDialog');
const AboutCodeDashboard = require('./controllers/aboutCodeDashboard');
const AboutCodeBarChart = require('./controllers/aboutCodeBarChart');
const AboutCodeJsTree = require('./controllers/aboutCodeJsTree');
const AboutCodeClueDataTable = require('./controllers/aboutCodeClueDataTable');
const AboutCodeComponentDataTable = require('./controllers/aboutCodeComponentDataTable');

const fs = require('fs');
const shell = require('electron').shell;
const dialog = require('electron').remote.dialog;
const path = require('path');

// The Electron module used to communicate asynchronously from a renderer process to the main process.
const ipcRenderer = require('electron').ipcRenderer;
const aboutCodeVersion = require('../../../package.json').version;

/**
 * This is the UI Controller for the application. It's responsible for
 * initializing and updating all of the views and handling user interaction
 */
$(document).ready(() => {
  // Create default values for all of the data and ui classes
  let aboutCodeDB = new AboutCodeDB();

  const dashboard = new AboutCodeDashboard('#tab-dashboard', aboutCodeDB);

  const barChart = new AboutCodeBarChart('#tab-barchart', aboutCodeDB)
    .on('bar-clicked', (attribute, value) => {
      // Show files that contain attribute value selected by user in bar chart
      cluesTable.clearColumnFilters();
      if (value !== 'No Value Detected') {
        cluesTable.setColumnFilter(attribute, value);
      } else {
        cluesTable.setColumnFilter(attribute, 'about_code_data_table_no_value_detected');
      }

      updateViewsByPath(cluesTable._selectedPath);

      // This needs to be done only when the column is visible.
      // So we do it last to try our best
      showClueButton.trigger('click');
    });

  const cluesTable = new AboutCodeClueDataTable('#tab-clues', aboutCodeDB);

  const componentsTable = new AboutCodeComponentDataTable('#tab-component', aboutCodeDB)
    .on('upload-clicked', (components) => {
      if (components.length > 0) {
        dejaCodeExportDialog.show();
      } else {
        dialog.showErrorBox(
          'No Components to Upload',
          'You have no Components to upload.\n\n' +
                    'Please create at least one Component and try again.');
      }
    })
    .on('export-json', exportJsonComponents);

  const componentDialog = new ComponentDialog('#componentDialog', aboutCodeDB)
    .on('save', () => {
      componentsTable.needsReload(true);
      redrawCurrentView();
    })
    .on('delete', () => {
      componentsTable.needsReload(true);
      redrawCurrentView();
    });

  const dejaCodeExportDialog =
        new DejaCodeExportDialog('#componentExportModal', aboutCodeDB);

  const jstree = new AboutCodeJsTree('#jstree', aboutCodeDB)
    .on('node-edit', (node) => componentDialog.show(node.id))
    .on('node-selected', (node) => {
      updateViewsByPath(node.id);
    });

  $(document).on('click', '#activate-filters-button', () => {
    cluesTable.genFilters();
    updateViewsByPath(cluesTable._selectedPath);
  });

  $(document).on('click', '#clear-filters-button', () => {
    cluesTable.resetColumnFilters();
    updateViewsByPath(cluesTable._selectedPath);
  });

  const splitter = new Splitter('#leftCol', '#rightCol')
    .on('drag-end', () => redrawCurrentView());

  // The id of the currently selected nav bar button.
  const currentNavButtonId = '#sidebar-wrapper .sidebar-nav .active button';

  // Defines DOM element constants for sidebar buttons.
  const saveSQLiteFileButton = $('#save-file');
  const openSQLiteFileButton = $('#open-file');
  const showClueButton = $('#show-tab-clues');
  const showComponentButton = $('#show-tab-component');
  const showBarChartButton = $('#show-tab-barchart');
  const showDashboardButton = $('#show-tab-dashboard');

  // Open a SQLite Database File
  openSQLiteFileButton.click(openSQLite);

  // Save a SQLite Database file
  saveSQLiteFileButton.click(saveSQLite);

  // Show clue DataTable. Hide node view and component summary table
  showClueButton.click(() => {
    splitter.show();
    cluesTable.redraw();
  });

  // Show component summary table. Hide DataTable and node view
  showComponentButton.click(() => {
    splitter.hide();
    componentsTable.redraw();
  });

  showBarChartButton.click(() => {
    splitter.show();
    barChart.redraw();
  });

  showDashboardButton.click(() => {
    splitter.hide();
    dashboard.redraw();
  });

  // Open links in default browser
  $('.open-in-default').click((evt) => {
    evt.preventDefault();
    shell.openExternal(evt.target.href);
  });

  ipcRenderer.on('table-view', () => showClueButton.trigger('click'));
  ipcRenderer.on('component-summary-view', () => showComponentButton.trigger('click'));
  ipcRenderer.on('open-SQLite', () => openSQLiteFileButton.trigger('click'));
  ipcRenderer.on('chart-summary-view', () => showBarChartButton.trigger('click'));
  ipcRenderer.on('save-SQLite', () => saveSQLiteFileButton.trigger('click'));
  ipcRenderer.on('import-JSON', importJson);
  ipcRenderer.on('export-JSON', exportJson);
  ipcRenderer.on('export-JSON-components-only', exportJsonComponents);
  ipcRenderer.on('get-ScanInfo', getScanInfo);

  // Opens the dashboard view when the app is first opened
  showDashboardButton.trigger('click');

  function updateViewsByPath(path) {
    // Update all the views with the given path string
    cluesTable.columns(0).search(path);

    componentDialog.selectedPath(path);
    dejaCodeExportDialog.selectedPath(path);
    jstree.selectedPath(path);
    cluesTable.selectedPath(path);
    componentsTable.selectedPath(path);
    dashboard.selectedPath(path);
    barChart.selectedPath(path);

    redrawCurrentView();
  }

  /** Creates the database and all View objects from a SQLite file */
  function loadDatabase(fileName) {
    // Create a new database when importing a json file
    aboutCodeDB = new AboutCodeDB({
      dbName: 'aboutcode_db',
      dbStorage: fileName
    });

    // Check that the database has the correct header information.
    aboutCodeDB.sync
      .then((db) => db.Header.findAll())
      .then((headers) => {
        if (headers.length === 0) {
          dialog.showErrorBox(
            'Invalid SQLite file: ' + fileName,
            'The SQLite file is invalid. Try re-importing the ScanCode JSON ' +
            'file and creating a new SQLite file.');
        }
      });

    return updateViews();
  }

  // Get the ScanCode version and options data from the DB and populate and open the modal
  function getScanInfo() {
    return aboutCodeDB.sync
      .then((db) => db.Header.findById(1)
        .then((header) => {
          const scancode_label = $('#scancode-label').find('#scancode-display');
          scancode_label.text('ScanCode version: ' + header.scancode_version + '\n\nScanCode options: ' + JSON.stringify(header.scancode_options, null, 2));
        }))
      .then($('#myModal').modal('show'));
  }

  /** Loads data for all views based on the current data */
  function updateViews() {
    return aboutCodeDB.sync
      .then(() => {
        const currFile = aboutCodeDB.sequelize.options.storage;
        document.title = 'AboutCode Manager - ' + path.basename(currFile);
        cluesTable.clearColumnFilters();

        // update all views with the new database.
        componentDialog.db(aboutCodeDB);
        dejaCodeExportDialog.db(aboutCodeDB);
        jstree.db(aboutCodeDB);
        cluesTable.db(aboutCodeDB);
        componentsTable.db(aboutCodeDB);
        dashboard.db(aboutCodeDB);
        barChart.db(aboutCodeDB);

        // Reload the jstree, then trigger the current view to reload.
        jstree.redraw();
      })
      .catch((reason) => { throw reason; });
  }

  function redrawCurrentView() {
    $(currentNavButtonId).trigger('click');
  }

  /** Open a SQLite Database File */
  function openSQLite() {
    dialog.showOpenDialog({
      properties: ['openFile'],
      title: 'Open a SQLite File',
      filters: [{
        name: 'SQLite File',
        extensions: ['sqlite']
      }]
    }, (fileNames) => {
      if (fileNames && fileNames[0]) {
        loadDatabase(fileNames[0]);
      }
    });
  }

  /** Save a SQLite Database File */
  function saveSQLite() {
    dialog.showSaveDialog({
      title: 'Save as a Database File',
      filters: [
        { name: 'SQLite File', extensions: ['sqlite'] }
      ]
    }, (newFileName) => {
      if (newFileName) {
        const oldFileName = aboutCodeDB.sequelize.options.storage;
        const reader = fs.createReadStream(oldFileName);
        const writer = fs.createWriteStream(newFileName);
        reader.pipe(writer);
        reader.on('end', () => loadDatabase(newFileName));
      }
    });
  }

  /** Import a ScanCode JSON file and create a SQLite database */
  function importJson() {
    dialog.showOpenDialog({
      title: 'Open a JSON File',
      filters: [{
        name: 'JSON File',
        extensions: ['json']
      }]
    }, (fileNames) => {
      if (fileNames === undefined) {
        return;
      }

      const jsonFileName = fileNames[0];

      // Immediately ask for a SQLite to save and create the database
      dialog.showSaveDialog({
        title: 'Save a SQLite Database File',
        filters: [{
          name: 'SQLite File',
          extensions: ['sqlite']
        }]
      }, (sqliteFileName) => {
        if (sqliteFileName === undefined) {
          return;
        }

        // Overwrite existing sqlite file
        if (fs.existsSync(sqliteFileName)) {
          fs.unlink(sqliteFileName, (err) => {
            if (err) {
              throw err;
            }
            console.info(`Deleted ${sqliteFileName}`);
          });
        }

        // Create a new database when importing a json file
        aboutCodeDB = new AboutCodeDB({
          dbName: 'demo_schema',
          dbStorage: sqliteFileName,
        });

        const progressbar = new Progress('#content', {
          title: 'Creating Database...',
          size: 100,
        });

        aboutCodeDB.sync
          .then(() => progressbar.showDeterminate())
          .then(() => aboutCodeDB.addFromJson(
            jsonFileName,
            aboutCodeVersion,
            (progress) => progressbar.update(progress / 100)))
          .then(() => progressbar.hide())
          .then(updateViews)
          .catch((err) => {
            progressbar.hide();
            if (err instanceof AboutCodeDB.MissingFileInfoError) {
              dialog.showErrorBox(
                'Missing File Type Information',
                'Missing file \'type\' information in the scanned data. ' +
                '\n\nThis probably means you ran the scan without the -i ' +
                'option in ScanCode. The app requires file information from ' +
                'a ScanCode scan. Rerun the scan using \n./scancode ' +
                '-clipeu options.'
              );
            } else {
              // Show error for problem with the JSON file
              dialog.showErrorBox(
                'JSON Error',
                'There is a problem with your JSON file. It may be malformed ' +
                '(e.g., the addition of a trailing comma), ' +
                'or there could be some other problem with the file. ' +
                '\n\nPlease check your file and try again. ' +
                '\n\nThe error thrown by the system is: \n\n' + err
              );
            }
            console.error(err);
          });
      });
    });
  }

  /** Export JSON file with original ScanCode data and components that have been created */
  function exportJson() {
    dialog.showSaveDialog({
      properties: ['openFile'],
      title: 'Save as JSON file',
      filters: [{
        name: 'JSON File Type',
        extensions: ['json']
      }]
    }, (fileName) => {
      if (fileName === undefined) {
        return;
      }

      const scanCodeInfoPromise = aboutCodeDB.getScanCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const aboutCodeInfoPromise = aboutCodeDB.getAboutCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const clueFilesPromise = aboutCodeDB.findAll({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const componentsPromise = aboutCodeDB.findAllComponents({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const filesCountPromise = aboutCodeDB.getFileCount({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      Promise.all([scanCodeInfoPromise, aboutCodeInfoPromise,
        filesCountPromise, clueFilesPromise, componentsPromise])
        .then(([scanCodeInfo, aboutCodeInfo, filesCount, clueFiles, components]) => {
          const json = {
            aboutcode_manager_notice: aboutCodeInfo.aboutcode_manager_notice,
            aboutcode_manager_version: aboutCodeInfo.aboutcode_manager_version,
            scancode_version: scanCodeInfo.scancode_version,
            scancode_options: scanCodeInfo.scancode_options,
            files_count: filesCount,
            files: clueFiles,
            components: components
          };

          fs.writeFile(fileName, JSON.stringify(json), (err) => {
            if (err) {
              throw err;
            }
          });
        });
    });
  }

  /** Export JSON file with only components that have been created */
  function exportJsonComponents() {
    dialog.showSaveDialog({
      properties: ['openFile'],
      title: 'Save as JSON file',
      filters: [{
        name: 'JSON File Type',
        extensions: ['json']
      }]
    }, (fileName) => {
      if (fileName === undefined) {
        return;
      }

      const aboutCodeInfoPromise = aboutCodeDB.getAboutCodeInfo({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      const componentsPromise = aboutCodeDB.findAllComponents({
        attributes: {
          exclude: ['id', 'createdAt', 'updatedAt']
        }
      });

      Promise.all([aboutCodeInfoPromise, componentsPromise])
        .then(([aboutCodeInfo, components]) => {
          const json = {
            aboutcode_manager_notice: aboutCodeInfo.aboutcode_manager_notice,
            aboutcode_manager_version: aboutCodeInfo.aboutcode_manager_version,
            components: components
          };

          fs.writeFile(fileName, JSON.stringify(json), (err) => {
            if (err) {
              throw err;
            }
          });
        });
    });
  }
});

module.exports = aboutCodeVersion;
<|endoftext|>


<commit_before>/**
 * Experience Sampling event page.
 *
 * This background page handles the various events for registering participants
 * and showing new surveys in response to API events.
 *
 * Participants must fill out both a consent form and a startup survey (with
 * demographics) before they can begin to answer real survey questions.
 */

var cesp = {};  // namespace variable

cesp.operatingSystem = '';
cesp.openTabId = -1;

// Settings.
cesp.SERVER_URL = 'https://chrome-experience-sampling.appspot.com';
cesp.SUBMIT_SURVEY_ACTION = '/_ah/api/cesp/v1/submitsurvey';
cesp.XHR_TIMEOUT = 4000;
cesp.NOTIFICATION_TITLE = 'Take a Chrome user experience survey!';
cesp.NOTIFICATION_BODY = 'Your feedback makes Chrome better.';
cesp.NOTIFICATION_BUTTON = 'Take survey!';
cesp.NOTIFICATION_CONSENT_LINK = 'What is this?';
cesp.MAX_SURVEYS_PER_DAY = 8;
cesp.ICON_FILE = 'icons/cues_85.png';
cesp.NOTIFICATION_DEFAULT_TIMEOUT = 10;  // minutes
cesp.NOTIFICATION_TAG = 'chromeSurvey';
cesp.SURVEY_COUNT_RESET_ALARM_NAME = 'surveyCountReset';
cesp.NOTIFICATION_ALARM_NAME = 'notificationTimeout';
cesp.UNINSTALL_ALARM_NAME = 'uninstallAlarm';
cesp.READY_FOR_SURVEYS = 'readyForSurveys';

// SETUP

/**
 * A helper method for updating the value in local storage.
 * @param {bool} newState The desired new state for the ready for surveys flag.
 */
function setReadyForSurveysStorageValue(newState) {
  var items = {};
  items[cesp.READY_FOR_SURVEYS] = newState;
  chrome.storage.local.set(items);
}

/**
 * A helper method for updating the value in local storage.
 * @param {int} newCount The desired new survey count value.
 */
function setSurveysShownStorageValue(newCount) {
  var items = {};
  items[cesp.SURVEYS_SHOWN_TODAY] = newCount;
  chrome.storage.local.set(items);
}

/**
 * Sets up basic state for the extension. Called when extension is installed.
 * @param {object} details The details of the chrome.runtime.onInstalled event.
 */
function setupState(details) {
  // We check the event reason because onInstalled can trigger for other
  // reasons (extension or browser update).
  if (details.reason === 'install') {
    setReadyForSurveysStorageValue(false);
    chrome.storage.local.set({'pending_responses': []});
    chrome.runtime.getPlatformInfo(function(platformInfo) {
      cesp.operatingSystem = platformInfo.os;
    });
    // Automatically uninstall the extension after 120 days.
    chrome.alarms.create(cesp.UNINSTALL_ALARM_NAME, {delayInMinutes: 172800});
    // Set the count of surveys shown to 0, and reset it each day at midnight.
    setSurveysShownStorageValue(0);
    var midnight = new Date();
    midnight.setHours(0, 0, 0, 0);
    // Midnight is the last midnight, so we set the alarm for one day from it.
    chrome.alarms.create(cesp.SURVEY_THROTTLE_RESET_ALARM,
        {when: midnight.getTime() + 86400000, periodInMinutes: 1440});
  }
}

/**
 * Handles the uninstall alarm.
 * @param {Alarm} alarm The alarm object from the onAlarm event.
*/
function handleUninstallAlarm(alarm) {
  if (alarm.name === cesp.UNINSTALL_ALARM_NAME)
    chrome.management.uninstallSelf();
}
chrome.alarms.onAlarm.addListener(handleUninstallAlarm);

/**
 * Resets the count of surveys shown to 0.
 * @param {Alarm} alarm The alarm object from the onAlarm event.
 */
function resetSurveyCount(alarm) {
  if (alarm.name === cesp.SURVEY_THROTTLE_RESET_ALARM)
    setSurveysShownStorageValue(0);
}
chrome.alarms.onAlarm.addListener(resetSurveyCount);

/**
 * Retrieves the registration status from Local Storage.
 */
function getConsentStatus() {
  chrome.storage.local.get(constants.CONSENT_KEY, maybeShowConsentForm);
}

/**
 * Checks whether consent has been granted yet; if not, opens the consent form.
 * @param {object} consentLookup Object containing consent status (or empty).
 */
function maybeShowConsentForm(consentLookup) {
  if (!consentLookup || consentLookup[constants.CONSENT_KEY] == null ||
      consentLookup[constants.CONSENT_KEY] == constants.CONSENT_PENDING) {
    chrome.storage.onChanged.addListener(storageUpdated);
    chrome.tabs.create({'url': chrome.extension.getURL('consent.html')});
  } else if (consentLookup[constants.CONSENT_KEY] ==
             constants.CONSENT_REJECTED) {
    chrome.management.uninstallSelf();
  } else if (consentLookup[constants.CONSENT_KEY] ==
             constants.CONSENT_GRANTED) {
    // Someone might have filled out the consent form previously but not
    // filled out the setup survey. Check to see if that's the case.
    chrome.storage.local.get(constants.SETUP_KEY, maybeShowSetupSurvey);
  }
}

/**
 * Checks whether the setup survey has been completed yet. If it has been, we
 * are now ready to start showing surveys. If not, we need to listen for
 * when it's completed.
 * @param {object} setupLookup Object containing setup survey status (or empty).
 */
function maybeShowSetupSurvey(setupLookup) {
  if (!setupLookup || setupLookup[constants.SETUP_KEY] == null ||
      setupLookup[constants.SETUP_KEY] == constants.SETUP_PENDING) {
    chrome.tabs.create({'url': chrome.extension.getURL('surveys/setup.html')});
  } else if (setupLookup[constants.SETUP_KEY] == constants.SETUP_COMPLETED) {
    setReadyForSurveysStorageValue(true);
  }
}

/**
 * Listens for the setup survey submission. When that happens, signals that
 * the experience sampling is now ready to begin.
 * @param {object} changes The changed portions of the database.
 * @param {string} areaName The name of the storage area.
 */
function storageUpdated(changes, areaName) {
  if (changes && changes[constants.SETUP_KEY] &&
      changes[constants.SETUP_KEY].newValue == constants.SETUP_COMPLETED) {
    setReadyForSurveysStorageValue(true);
  }
}

// Performs consent and registration checks on startup and install.
chrome.runtime.onInstalled.addListener(getConsentStatus);
chrome.runtime.onStartup.addListener(getConsentStatus);
chrome.runtime.onInstalled.addListener(setupState);

// SURVEY HANDLING

/**
 * Clears our existing notification(s).
 * @param {Alarm} alarm The alarm object from the onAlarm event (optional).
 */
function clearNotifications(alarm) {
  if (alarm && alarm.name !== cesp.NOTIFICATION_ALARM_NAME)
    return;
  chrome.notifications.clear(cesp.NOTIFICATION_TAG, function(unused) {});
  chrome.alarms.clear(cesp.NOTIFICATION_ALARM_NAME);
}
// Clear the notification state when the survey times out.
chrome.alarms.onAlarm.addListener(clearNotifications);

/**
 * Creates a new notification to prompt the participant to take an experience
 * sampling survey.
 * @param {object} element The browser element of interest.
 * @param {object} decision The decision the participant made.
 */
function showSurveyNotification(element, decision) {
  chrome.storage.local.get(cesp.READY_FOR_SURVEYS, function(items) {
    if (!items[cesp.READY_FOR_SURVEYS]) return;

    chrome.storage.local.get(cesp.SURVEYS_SHOWN_TODAY, function(items) {
      if (items[cesp.SURVEYS_SHOWN_TODAY] >= cesp.MAX_SURVEYS_PER_DAY) {
        return;
      }

      clearNotifications();

      var timePromptShown = new Date();
      var clickHandler = function(notificationId, buttonIndex) {
        if (buttonIndex === 1) {
          chrome.tabs.create({'url': chrome.extension.getURL('consent.html')});
        } else {
          var timePromptClicked = new Date();
          loadSurvey(element, decision, timePromptShown, timePromptClicked);
          clearNotifications();
        }
      };
      var opt = {
        type: 'basic',
        iconUrl: cesp.ICON_FILE,
        title: cesp.NOTIFICATION_TITLE,
        message: cesp.NOTIFICATION_BODY,
        eventTime: Date.now(),
        buttons: [
          {title: cesp.NOTIFICATION_BUTTON},
          {title: cesp.NOTIFICATION_CONSENT_LINK}
        ],
        isClickable: true
      };
      chrome.notifications.create(
          cesp.NOTIFICATION_TAG,
          opt,
          function(id) {
            chrome.alarms.create(
                cesp.NOTIFICATION_ALARM_NAME,
                {delayInMinutes: cesp.NOTIFICATION_DEFAULT_TIMEOUT});
          });
      chrome.notifications.onClicked.addListener(clickHandler);
      chrome.notifications.onButtonClicked.addListener(clickHandler);
      setSurveysShownStorageValue(items[cesp.SURVEYS_SHOWN_TODAY] + 1);
    });
  });
}

/**
 * Creates a new tab with the experience sampling survey page.
 * @param {object} element The browser element of interest.
 * @param {object} decision The decision the participant made.
 * @param {object} timePromptShown Date object of when the survey prompt
 *     notification was shown to the participant.
 * @param {object} timePromptClicked Date object of when the participant
 *     clicked the survey prompt notification.
 */
function loadSurvey(element, decision, timePromptShown, timePromptClicked) {
  chrome.storage.local.get(cesp.READY_FOR_SURVEYS, function(items) {
    if (!items[cesp.READY_FOR_SURVEYS]) return;
    var userDecision = decision['name'];
    if (userDecision !== constants.DecisionType.PROCEED &&
        userDecision !== constants.DecisionType.DENY) {
      return;
    }

    var surveyUrl, visitUrl;
    var eventType = constants.FindEventType(element['name']);
    switch (eventType) {
      case constants.EventType.SSL_OVERRIDABLE:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.SSL_OVERRIDABLE_PROCEED :
            constants.SurveyLocation.SSL_OVERRIDABLE_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.SSL_NONOVERRIDABLE:
        surveyUrl = constants.SurveyLocation.SSL_NONOVERRIDABLE;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.MALWARE:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.MALWARE_PROCEED :
            constants.SurveyLocation.MALWARE_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.PHISHING:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.PHISHING_PROCEED :
            constants.SurveyLocation.PHISHING_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.EXTENSION_INSTALL:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.EXTENSION_PROCEED :
            constants.SurveyLocation.EXTENSION_NOPROCEED;
        break;
      case constants.EventType.HARMFUL:
      case constants.EventType.SB_OTHER:
      case constants.EventType.DOWNLOAD_MALICIOUS:
      case constants.EventType.DOWNLOAD_DANGEROUS:
      case constants.EventType.DOWNLOAD_DANGER_PROMPT:
        // Don't survey about these.
        return;
      case constants.EventType.UNKNOWN:
        throw new Error('Unknown event type: ' + element['name']);
        break;
    }
    if ((eventType !== constants.EventType.EXTENSION_INSTALL && !visitUrl) ||
        !surveyUrl) {
      return;
    }
    visitUrl = encodeURIComponent(visitUrl);
    var openUrl = 'surveys/survey.html?js=' + surveyUrl + '&url=' + visitUrl;
    chrome.tabs.create(
        {'url': chrome.extension.getURL(openUrl)},
        function(tab) {
          try {
            chrome.tabs.remove(cesp.openTabId);
          } catch (err) { }
          cesp.openTabId = tab.id;
        });
  });
}

// Trigger the new survey prompt when the participant makes a decision about an
// experience sampling element.
chrome.experienceSamplingPrivate.onDecision.addListener(showSurveyNotification);

/**
 * A survey response (question and answer).
 * @constructor
 * @param {string} question The question being answered.
 * @param {string} answer The answer to that question.
 */
function Response(question, answer) {
  this.question = question;
  this.answer = answer;
}

/**
 * A completed survey.
 * @constructor
 * @param {string} type The type of survey.
 * @param {int} participantId The participant ID.
 * @param {Date} dateTaken The date and time when the survey was taken.
 * @param {Array.Response} responses An array of Response objects.
*/
function Survey(type, participantId, dateTaken, responses) {
  this.type = type;
  this.participantId = participantId;
  this.dateTaken = dateTaken;
  this.responses = responses;
}

/**
 * Sends a survey to the CESP backend via XHR.
 * @param {Survey} survey The completed survey to send to the backend.
 * @param {function(string)} successCallback A function to call on receiving a
 *     successful response (HTTP 204). It should look like
 *     "function(response) {...};" where "response" is the text of the response
 *     (if there is any).
 * @param {function(!number=)} errorCallback A function to call on receiving an
 *     error from the server, or on timing out. It should look like
 *     "function(status) {...};" where "status" is an HTTP status code integer,
 *     if there is one. For a timeout, there is no status.
 */
function sendSurvey(survey, successCallback, errorCallback) {
  var url = cesp.SERVER_URL + cesp.SUBMIT_SURVEY_ACTION;
  var method = "POST";
  var dateTaken = survey.dateTaken.toISOString();
  // Get rid of timezone "Z" on end of ISO String for AppEngine compatibility.
  if (dateTaken.slice(-1) === "Z") {
    dateTaken = dateTaken.slice(0, -1);
  }
  var data = {
    "date_taken": dateTaken,
    "participant_id": survey.participantId,
    "responses": [],
    "survey_type": survey.type
  };
  for (var i = 0; i < survey.responses.length; i++) {
    data.responses.push(survey.responses[i]);
  }
  var xhr = new XMLHttpRequest();
  function onLoadHandler(event) {
    if (xhr.readyState === 4) {
      if (xhr.status === 204) {
        successCallback(xhr.response);
      } else {
        errorCallback(xhr.status);
      }
    }
  }
  function onErrorHandler(event) {
    errorCallback(xhr.status);
  }
  function onTimeoutHandler(event) {
    errorCallback();
  }
  xhr.open(method, url, true);
  xhr.setRequestHeader('Content-Type', 'application/JSON');
  xhr.timeout = cesp.XHR_TIMEOUT;
  xhr.onload = onLoadHandler;
  xhr.onerror = onErrorHandler;
  xhr.ontimeout = onTimeoutHandler;
  xhr.send(JSON.stringify(data));
}
<commit_msg>max surveys per day<commit_after>/**
 * Experience Sampling event page.
 *
 * This background page handles the various events for registering participants
 * and showing new surveys in response to API events.
 *
 * Participants must fill out both a consent form and a startup survey (with
 * demographics) before they can begin to answer real survey questions.
 */

var cesp = {};  // namespace variable

cesp.operatingSystem = '';
cesp.openTabId = -1;

// Settings.
cesp.SERVER_URL = 'https://chrome-experience-sampling.appspot.com';
cesp.SUBMIT_SURVEY_ACTION = '/_ah/api/cesp/v1/submitsurvey';
cesp.XHR_TIMEOUT = 4000;
cesp.NOTIFICATION_TITLE = 'Take a Chrome user experience survey!';
cesp.NOTIFICATION_BODY = 'Your feedback makes Chrome better.';
cesp.NOTIFICATION_BUTTON = 'Take survey!';
cesp.NOTIFICATION_CONSENT_LINK = 'What is this?';
cesp.MAX_SURVEYS_PER_DAY = 2;
cesp.ICON_FILE = 'icons/cues_85.png';
cesp.NOTIFICATION_DEFAULT_TIMEOUT = 10;  // minutes
cesp.NOTIFICATION_TAG = 'chromeSurvey';
cesp.SURVEY_COUNT_RESET_ALARM_NAME = 'surveyCountReset';
cesp.NOTIFICATION_ALARM_NAME = 'notificationTimeout';
cesp.UNINSTALL_ALARM_NAME = 'uninstallAlarm';
cesp.READY_FOR_SURVEYS = 'readyForSurveys';

// SETUP

/**
 * A helper method for updating the value in local storage.
 * @param {bool} newState The desired new state for the ready for surveys flag.
 */
function setReadyForSurveysStorageValue(newState) {
  var items = {};
  items[cesp.READY_FOR_SURVEYS] = newState;
  chrome.storage.local.set(items);
}

/**
 * A helper method for updating the value in local storage.
 * @param {int} newCount The desired new survey count value.
 */
function setSurveysShownStorageValue(newCount) {
  var items = {};
  items[cesp.SURVEYS_SHOWN_TODAY] = newCount;
  chrome.storage.local.set(items);
}

/**
 * Sets up basic state for the extension. Called when extension is installed.
 * @param {object} details The details of the chrome.runtime.onInstalled event.
 */
function setupState(details) {
  // We check the event reason because onInstalled can trigger for other
  // reasons (extension or browser update).
  if (details.reason === 'install') {
    setReadyForSurveysStorageValue(false);
    chrome.storage.local.set({'pending_responses': []});
    chrome.runtime.getPlatformInfo(function(platformInfo) {
      cesp.operatingSystem = platformInfo.os;
    });
    // Automatically uninstall the extension after 120 days.
    chrome.alarms.create(cesp.UNINSTALL_ALARM_NAME, {delayInMinutes: 172800});
    // Set the count of surveys shown to 0, and reset it each day at midnight.
    setSurveysShownStorageValue(0);
    var midnight = new Date();
    midnight.setHours(0, 0, 0, 0);
    // Midnight is the last midnight, so we set the alarm for one day from it.
    chrome.alarms.create(cesp.SURVEY_THROTTLE_RESET_ALARM,
        {when: midnight.getTime() + 86400000, periodInMinutes: 1440});
  }
}

/**
 * Handles the uninstall alarm.
 * @param {Alarm} alarm The alarm object from the onAlarm event.
*/
function handleUninstallAlarm(alarm) {
  if (alarm.name === cesp.UNINSTALL_ALARM_NAME)
    chrome.management.uninstallSelf();
}
chrome.alarms.onAlarm.addListener(handleUninstallAlarm);

/**
 * Resets the count of surveys shown to 0.
 * @param {Alarm} alarm The alarm object from the onAlarm event.
 */
function resetSurveyCount(alarm) {
  if (alarm.name === cesp.SURVEY_THROTTLE_RESET_ALARM)
    setSurveysShownStorageValue(0);
}
chrome.alarms.onAlarm.addListener(resetSurveyCount);

/**
 * Retrieves the registration status from Local Storage.
 */
function getConsentStatus() {
  chrome.storage.local.get(constants.CONSENT_KEY, maybeShowConsentForm);
}

/**
 * Checks whether consent has been granted yet; if not, opens the consent form.
 * @param {object} consentLookup Object containing consent status (or empty).
 */
function maybeShowConsentForm(consentLookup) {
  if (!consentLookup || consentLookup[constants.CONSENT_KEY] == null ||
      consentLookup[constants.CONSENT_KEY] == constants.CONSENT_PENDING) {
    chrome.storage.onChanged.addListener(storageUpdated);
    chrome.tabs.create({'url': chrome.extension.getURL('consent.html')});
  } else if (consentLookup[constants.CONSENT_KEY] ==
             constants.CONSENT_REJECTED) {
    chrome.management.uninstallSelf();
  } else if (consentLookup[constants.CONSENT_KEY] ==
             constants.CONSENT_GRANTED) {
    // Someone might have filled out the consent form previously but not
    // filled out the setup survey. Check to see if that's the case.
    chrome.storage.local.get(constants.SETUP_KEY, maybeShowSetupSurvey);
  }
}

/**
 * Checks whether the setup survey has been completed yet. If it has been, we
 * are now ready to start showing surveys. If not, we need to listen for
 * when it's completed.
 * @param {object} setupLookup Object containing setup survey status (or empty).
 */
function maybeShowSetupSurvey(setupLookup) {
  if (!setupLookup || setupLookup[constants.SETUP_KEY] == null ||
      setupLookup[constants.SETUP_KEY] == constants.SETUP_PENDING) {
    chrome.tabs.create({'url': chrome.extension.getURL('surveys/setup.html')});
  } else if (setupLookup[constants.SETUP_KEY] == constants.SETUP_COMPLETED) {
    setReadyForSurveysStorageValue(true);
  }
}

/**
 * Listens for the setup survey submission. When that happens, signals that
 * the experience sampling is now ready to begin.
 * @param {object} changes The changed portions of the database.
 * @param {string} areaName The name of the storage area.
 */
function storageUpdated(changes, areaName) {
  if (changes && changes[constants.SETUP_KEY] &&
      changes[constants.SETUP_KEY].newValue == constants.SETUP_COMPLETED) {
    setReadyForSurveysStorageValue(true);
  }
}

// Performs consent and registration checks on startup and install.
chrome.runtime.onInstalled.addListener(getConsentStatus);
chrome.runtime.onStartup.addListener(getConsentStatus);
chrome.runtime.onInstalled.addListener(setupState);

// SURVEY HANDLING

/**
 * Clears our existing notification(s).
 * @param {Alarm} alarm The alarm object from the onAlarm event (optional).
 */
function clearNotifications(alarm) {
  if (alarm && alarm.name !== cesp.NOTIFICATION_ALARM_NAME)
    return;
  chrome.notifications.clear(cesp.NOTIFICATION_TAG, function(unused) {});
  chrome.alarms.clear(cesp.NOTIFICATION_ALARM_NAME);
}
// Clear the notification state when the survey times out.
chrome.alarms.onAlarm.addListener(clearNotifications);

/**
 * Creates a new notification to prompt the participant to take an experience
 * sampling survey.
 * @param {object} element The browser element of interest.
 * @param {object} decision The decision the participant made.
 */
function showSurveyNotification(element, decision) {
  chrome.storage.local.get(cesp.READY_FOR_SURVEYS, function(items) {
    if (!items[cesp.READY_FOR_SURVEYS]) return;

    chrome.storage.local.get(cesp.SURVEYS_SHOWN_TODAY, function(items) {
      if (items[cesp.SURVEYS_SHOWN_TODAY] >= cesp.MAX_SURVEYS_PER_DAY) {
        return;
      }

      clearNotifications();

      var timePromptShown = new Date();
      var clickHandler = function(notificationId, buttonIndex) {
        if (buttonIndex === 1) {
          chrome.tabs.create({'url': chrome.extension.getURL('consent.html')});
        } else {
          var timePromptClicked = new Date();
          loadSurvey(element, decision, timePromptShown, timePromptClicked);
          clearNotifications();
        }
      };
      var opt = {
        type: 'basic',
        iconUrl: cesp.ICON_FILE,
        title: cesp.NOTIFICATION_TITLE,
        message: cesp.NOTIFICATION_BODY,
        eventTime: Date.now(),
        buttons: [
          {title: cesp.NOTIFICATION_BUTTON},
          {title: cesp.NOTIFICATION_CONSENT_LINK}
        ],
        isClickable: true
      };
      chrome.notifications.create(
          cesp.NOTIFICATION_TAG,
          opt,
          function(id) {
            chrome.alarms.create(
                cesp.NOTIFICATION_ALARM_NAME,
                {delayInMinutes: cesp.NOTIFICATION_DEFAULT_TIMEOUT});
          });
      chrome.notifications.onClicked.addListener(clickHandler);
      chrome.notifications.onButtonClicked.addListener(clickHandler);
      setSurveysShownStorageValue(items[cesp.SURVEYS_SHOWN_TODAY] + 1);
    });
  });
}

/**
 * Creates a new tab with the experience sampling survey page.
 * @param {object} element The browser element of interest.
 * @param {object} decision The decision the participant made.
 * @param {object} timePromptShown Date object of when the survey prompt
 *     notification was shown to the participant.
 * @param {object} timePromptClicked Date object of when the participant
 *     clicked the survey prompt notification.
 */
function loadSurvey(element, decision, timePromptShown, timePromptClicked) {
  chrome.storage.local.get(cesp.READY_FOR_SURVEYS, function(items) {
    if (!items[cesp.READY_FOR_SURVEYS]) return;
    var userDecision = decision['name'];
    if (userDecision !== constants.DecisionType.PROCEED &&
        userDecision !== constants.DecisionType.DENY) {
      return;
    }

    var surveyUrl, visitUrl;
    var eventType = constants.FindEventType(element['name']);
    switch (eventType) {
      case constants.EventType.SSL_OVERRIDABLE:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.SSL_OVERRIDABLE_PROCEED :
            constants.SurveyLocation.SSL_OVERRIDABLE_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.SSL_NONOVERRIDABLE:
        surveyUrl = constants.SurveyLocation.SSL_NONOVERRIDABLE;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.MALWARE:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.MALWARE_PROCEED :
            constants.SurveyLocation.MALWARE_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.PHISHING:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.PHISHING_PROCEED :
            constants.SurveyLocation.PHISHING_NOPROCEED;
        visitUrl = urlHandler.GetMinimalUrl(element['destination']);
        break;
      case constants.EventType.EXTENSION_INSTALL:
        surveyUrl = userDecision === constants.DecisionType.PROCEED ?
            constants.SurveyLocation.EXTENSION_PROCEED :
            constants.SurveyLocation.EXTENSION_NOPROCEED;
        break;
      case constants.EventType.HARMFUL:
      case constants.EventType.SB_OTHER:
      case constants.EventType.DOWNLOAD_MALICIOUS:
      case constants.EventType.DOWNLOAD_DANGEROUS:
      case constants.EventType.DOWNLOAD_DANGER_PROMPT:
        // Don't survey about these.
        return;
      case constants.EventType.UNKNOWN:
        throw new Error('Unknown event type: ' + element['name']);
        break;
    }
    if ((eventType !== constants.EventType.EXTENSION_INSTALL && !visitUrl) ||
        !surveyUrl) {
      return;
    }
    visitUrl = encodeURIComponent(visitUrl);
    var openUrl = 'surveys/survey.html?js=' + surveyUrl + '&url=' + visitUrl;
    chrome.tabs.create(
        {'url': chrome.extension.getURL(openUrl)},
        function(tab) {
          try {
            chrome.tabs.remove(cesp.openTabId);
          } catch (err) { }
          cesp.openTabId = tab.id;
        });
  });
}

// Trigger the new survey prompt when the participant makes a decision about an
// experience sampling element.
chrome.experienceSamplingPrivate.onDecision.addListener(showSurveyNotification);

/**
 * A survey response (question and answer).
 * @constructor
 * @param {string} question The question being answered.
 * @param {string} answer The answer to that question.
 */
function Response(question, answer) {
  this.question = question;
  this.answer = answer;
}

/**
 * A completed survey.
 * @constructor
 * @param {string} type The type of survey.
 * @param {int} participantId The participant ID.
 * @param {Date} dateTaken The date and time when the survey was taken.
 * @param {Array.Response} responses An array of Response objects.
*/
function Survey(type, participantId, dateTaken, responses) {
  this.type = type;
  this.participantId = participantId;
  this.dateTaken = dateTaken;
  this.responses = responses;
}

/**
 * Sends a survey to the CESP backend via XHR.
 * @param {Survey} survey The completed survey to send to the backend.
 * @param {function(string)} successCallback A function to call on receiving a
 *     successful response (HTTP 204). It should look like
 *     "function(response) {...};" where "response" is the text of the response
 *     (if there is any).
 * @param {function(!number=)} errorCallback A function to call on receiving an
 *     error from the server, or on timing out. It should look like
 *     "function(status) {...};" where "status" is an HTTP status code integer,
 *     if there is one. For a timeout, there is no status.
 */
function sendSurvey(survey, successCallback, errorCallback) {
  var url = cesp.SERVER_URL + cesp.SUBMIT_SURVEY_ACTION;
  var method = "POST";
  var dateTaken = survey.dateTaken.toISOString();
  // Get rid of timezone "Z" on end of ISO String for AppEngine compatibility.
  if (dateTaken.slice(-1) === "Z") {
    dateTaken = dateTaken.slice(0, -1);
  }
  var data = {
    "date_taken": dateTaken,
    "participant_id": survey.participantId,
    "responses": [],
    "survey_type": survey.type
  };
  for (var i = 0; i < survey.responses.length; i++) {
    data.responses.push(survey.responses[i]);
  }
  var xhr = new XMLHttpRequest();
  function onLoadHandler(event) {
    if (xhr.readyState === 4) {
      if (xhr.status === 204) {
        successCallback(xhr.response);
      } else {
        errorCallback(xhr.status);
      }
    }
  }
  function onErrorHandler(event) {
    errorCallback(xhr.status);
  }
  function onTimeoutHandler(event) {
    errorCallback();
  }
  xhr.open(method, url, true);
  xhr.setRequestHeader('Content-Type', 'application/JSON');
  xhr.timeout = cesp.XHR_TIMEOUT;
  xhr.onload = onLoadHandler;
  xhr.onerror = onErrorHandler;
  xhr.ontimeout = onTimeoutHandler;
  xhr.send(JSON.stringify(data));
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Module dependencies.
 */

var program = require('commander');
var settings = require('./lib/settings').settings;
var spawn = require('child_process').spawn;
var path = require('path');
var fs = require('fs');

var component;

program
  .description('Builds or serves the current content')
  .option('--serve', 'Serves')
  .option('--watch', 'Serves and watches')
  .option('--reload', 'Live reload')
  .option('--validate', 'Validate links')
  .option('--config <config>', 'Specify configuration file')
  .option('--destination <directory>', 'Specify build destination')
  .arguments('[component] [options]')
  .action(function(component) {
    component = component;
  })
  .parse(process.argv);

if (!settings.generate) {
  console.log('You do not have a static site generator installed.')
}

if (settings.generate.metalsmith) {
  var config;
  if (program.config && fileExists(path.join(process.cwd(),program.config))) {
    var config = require(path.join(process.cwd(),program.config));
  } else if (fileExists(path.join(process.cwd(),'metalsmith.json'))) {
    var config = require(path.join(process.cwd(),'metalsmith.json'));
  } else {
    console.log("Could not find a metalsmith configuration file.")
    process.exit(1)
  }

  if (program.destination) {
    config.destination = program.destination
  }

//  var metalsmith = spawn('metalsmith', [''], { env: process.env, stdio: "inherit"});

  // provide a formatting function for use in templates
  config.metadata.formatDate = function(date, format) {
      return require("moment")(date).format(format || "MMMM Do, YYYY");
  }
  // only enable live-reloading when requested
  if (program.serve || program.watch) {
    var redirects = Object.assign({
        "/" : settings.publish.baseurl + "/"
      }, settings.publish.redirects)

    config.plugins.push( { 'metalsmith-serve' : {
      "document_root": "_site",
      "redirects": redirects,
      "http_error_files": {
        "404": settings.publish.baseurl + "/404.html"
      }
    } } );
  }

  if (program.watch) {
    config.plugins.push( { 'metalsmith-watch': {
      "paths": {
        "${source}/**/*.md": true,
        "_layouts/**": "**/*.html",
        "_includes/**": "**/*.html"
      }
    } } );
  }
  if (program.watch && program.reload) {
    config.plugins.push( { 'metalsmith-watch' : { livereload: 35729 } } );
    config.metadata.customHTML = '<script src="http://localhost:35729/livereload.js?snipver=1"></script>';
  }
  if (program.validate) {
    config.plugins.push( { 'metalsmith-broken-link-checker': true } );
  }
  config.plugins.find( x => { for (k in x) { return (k == 'metalsmith-ignore') } } )['metalsmith-ignore'].push("metalsmith.tmp.json");
  // Swallow the --watch option
  process.argv = []

  fs.writeFile("metalsmith.tmp.json", JSON.stringify(config, null, 2), 'utf8', function (err) {
    if (err) {
      console.log(err)
      process.exit(1)
    }
    process.argv.push("/usr/local/bin/node", "/usr/local/bin/content-build", "--config", "metalsmith.tmp.json")
    require('metalsmith/bin/metalsmith');

  })
} else if (settings.generate.jekyll) {
  var config = program.config ? ['--config', program.config] : []
  spawn('bundle', ['exec', 'jekyll', 'build'].concat(config), { env: process.env, stdio: "inherit"});
}

function fileExists(filePath)
{
    try
    {
        return fs.statSync(filePath).isFile();
    }
    catch (err)
    {
        return false;
    }
}<commit_msg>Added short flags and tried to fix watch/reload<commit_after>#!/usr/bin/env node

/**
 * Module dependencies.
 */

var program = require('commander');
var settings = require('./lib/settings').settings;
var spawn = require('child_process').spawn;
var path = require('path');
var fs = require('fs');

var component;

program
  .description('Builds or serves the current content')
  .option('-s, --serve', 'Serves')
  .option('-w, --watch', 'Serves and watches')
  .option('-r, --reload', 'Live reload')
  .option('-v, --validate', 'Validate links')
  .option('-c, --config <config>', 'Specify configuration file')
  .option('-d, --destination <directory>', 'Specify build destination')
  .arguments('[component] [options]')
  .action(function(component) {
    component = component;
  })
  .parse(process.argv);

if (!settings.generate) {
  console.log('You do not have a static site generator installed.')
}

if (settings.generate.metalsmith) {
  var config;
  if (program.config && fileExists(path.join(process.cwd(),program.config))) {
    var config = require(path.join(process.cwd(),program.config));
  } else if (fileExists(path.join(process.cwd(),'metalsmith.json'))) {
    var config = require(path.join(process.cwd(),'metalsmith.json'));
  } else {
    console.log("Could not find a metalsmith configuration file.")
    process.exit(1)
  }

  if (program.destination) {
    config.destination = program.destination
  }

//  var metalsmith = spawn('metalsmith', [''], { env: process.env, stdio: "inherit"});

  // provide a formatting function for use in templates
  config.metadata.formatDate = function(date, format) {
      return require("moment")(date).format(format || "MMMM Do, YYYY");
  }
  // only enable live-reloading when requested
  if (program.serve || program.watch) {
    var redirects = Object.assign({
        "/" : settings.publish.baseurl + "/"
      }, settings.publish.redirects)

    config.plugins.push( { 'metalsmith-serve' : {
      "document_root": "_site",
      "redirects": redirects,
      "http_error_files": {
        "404": settings.publish.baseurl + "/404.html"
      }
    } } );
  }

  if (program.watch) {
    var watch = { 'metalsmith-watch': {
      "paths": {
        "${source}/**/*.md": true,
        "_layouts/**": "**/*.md",
        "_includes/**": "**/*.md"
      }
    } }
    if (program.reload) {
      watch['metalsmith-watch'].livereload = 35729;
      config.metadata.customHTML = '<script src="http://localhost:35729/livereload.js?snipver=1"></script>';
    }
    config.plugins.push( watch );
  }
  // if (program.watch && program.reload) {
  //   config.plugins.push( { 'metalsmith-watch' : { livereload: 35729 } } );
  //   config.metadata.customHTML = '<script src="http://localhost:35729/livereload.js?snipver=1"></script>';
  // }
  if (program.validate) {
    config.plugins.push( { 'metalsmith-broken-link-checker': true } );
  }
  config.plugins.find( x => { for (k in x) { return (k == 'metalsmith-ignore') } } )['metalsmith-ignore'].push("metalsmith.tmp.json");
  // Swallow the --watch option
  process.argv = []

  fs.writeFile("metalsmith.tmp.json", JSON.stringify(config, null, 2), 'utf8', function (err) {
    if (err) {
      console.log(err)
      process.exit(1)
    }
    process.argv.push("/usr/local/bin/node", "/usr/local/bin/content-build", "--config", "metalsmith.tmp.json")
    require('metalsmith/bin/metalsmith');

  })
} else if (settings.generate.jekyll) {
  var config = program.config ? ['--config', program.config] : []
  spawn('bundle', ['exec', 'jekyll', 'build'].concat(config), { env: process.env, stdio: "inherit"});
}

function fileExists(filePath)
{
    try
    {
        return fs.statSync(filePath).isFile();
    }
    catch (err)
    {
        return false;
    }
}<|endoftext|>


<commit_before>var loki = require('lokijs');
var jsgraph = require('jsgraph');
var irc  = require('tmi.js');
var uuid = require('uuid');
var express = require('express');

//var collections = require('./collections');
function Event(type, opts) {  //var e = new Event(type, args);
  this.id = uuid.v4();
  this.type = type;
  this.created = new Date();
  for (var key in opts) {
    this[key] = opts[key];
  }
  return this;
}

function DirectedGraph(name) {
  var res = jsgraph.directed.create();
  if (res.error) return console.error(res.error);
  var graph = res.result;
  graph.name = name;
  return graph;
}

module.exports = function(emitter, username, secret, config) {

  this.started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var nodes = socialdb.addCollection('nodes');
  var edges = socialdb.addCollection('edges');

  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel= function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        channel = JSON.parse(body);
        channels.insert(channel);
        var node = {
          type: "channel",
          id: channel._id;
          name: channel.name;
        };
        vres = socialGraph.addVertex(u: channel._id, p: node);
        if (vres.error) return console.error(vres.error);
        nodes.insert(vres.response);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        user = JSON.parse(body);
        users.insert(user);
      });
    }
  }

  function onConnected(address, port) {
    var ts = new Date();
    var event = {
      created: ts.toUTCString(),
      type: "connected",
      address: address,
      port: port
    };
    events.insert(event);
    return true;
  }

  function onJoin(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> join", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "join",
      channel: channel,
      username: user
    };
    events.insert(event);
    cacheUser(user);
  }

  function onPart(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> part", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "part",
      channel: channel,
      username: user
    };
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = {
      created: ts.toUTCString(),
      type: "hosted",
      channel: channel,
      username: user,
      viewers: viewers
    };
    events.insert(event);
    cacheUser(user.username);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "chat",
      channel: channel,
      username: user.username,
      message: message
    });
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "action",
      channel: channel,
      user: user.username,
      message: message
    });
    cacheUser(user.username);
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });

  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  this.disconnect = function() {
    this.client.disconnect();
  };

  return this;
};
<commit_msg>syntax error<commit_after>var loki = require('lokijs');
var jsgraph = require('jsgraph');
var irc  = require('tmi.js');
var uuid = require('uuid');
var express = require('express');

//var collections = require('./collections');
function Event(type, opts) {  //var e = new Event(type, args);
  this.id = uuid.v4();
  this.type = type;
  this.created = new Date();
  for (var key in opts) {
    this[key] = opts[key];
  }
  return this;
}

function DirectedGraph(name) {
  var res = jsgraph.directed.create();
  if (res.error) return console.error(res.error);
  var graph = res.result;
  graph.name = name;
  return graph;
}

module.exports = function(emitter, username, secret, config) {

  this.started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var nodes = socialdb.addCollection('nodes');
  var edges = socialdb.addCollection('edges');

  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel= function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        channel = JSON.parse(body);
        channels.insert(channel);
        var node = {
          type: "channel",
          id: channel._id,
          name: channel.name
        };
        vres = socialGraph.addVertex(u: channel._id, p: node);
        if (vres.error) return console.error(vres.error);
        nodes.insert(vres.response);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        user = JSON.parse(body);
        users.insert(user);
      });
    }
  }

  function onConnected(address, port) {
    var ts = new Date();
    var event = {
      created: ts.toUTCString(),
      type: "connected",
      address: address,
      port: port
    };
    events.insert(event);
    return true;
  }

  function onJoin(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> join", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "join",
      channel: channel,
      username: user
    };
    events.insert(event);
    cacheUser(user);
  }

  function onPart(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> part", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "part",
      channel: channel,
      username: user
    };
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = {
      created: ts.toUTCString(),
      type: "hosted",
      channel: channel,
      username: user,
      viewers: viewers
    };
    events.insert(event);
    cacheUser(user.username);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "chat",
      channel: channel,
      username: user.username,
      message: message
    });
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "action",
      channel: channel,
      user: user.username,
      message: message
    });
    cacheUser(user.username);
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });

  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  this.disconnect = function() {
    this.client.disconnect();
  };

  return this;
};
<|endoftext|>


<commit_before>// This file is part of MLDB. Copyright 2016 mldb.ai inc. All rights reserved.

function assertEqual(expr, val)
{
    if (expr == val)
        return;
    if (JSON.stringify(expr) == JSON.stringify(val))
        return;

    mldb.log(expr, 'IS NOT EQUAL TO', val);

    throw "Assertion failure";
}

var res1 = mldb.query("SELECT tf_Cos(1.23, {T: { type: 'DT_DOUBLE'}}) AS res");
var res2 = mldb.query("SELECT cos(1.23) AS res");

mldb.log(res1[0].columns[0]);
mldb.log(res2[0].columns[0]);

assertEqual(res1[0].columns[0][1],
            res2[0].columns[0][1]);

var res = mldb.query("SELECT tf_MatrixInverse([[1,2],[3, 4]], { T: { type: 'DT_DOUBLE' } }) AS res");

mldb.log(res);

var expected = [
   {
      "columns" : [
         [ "res.0.0", -1.9999999999999998, "NaD" ],
         [ "res.0.1", 1.0, "NaD" ],
         [ "res.1.0", 1.4999999999999998, "NaD" ],
         [ "res.1.1", -0.49999999999999994, "NaD" ]
      ],
      "rowName" : "result"
   }
];

assertEqual(res, expected);

var ops = mldb.get("/v1/plugins/tensorflow/routes/ops");

mldb.log(ops);

var op = mldb.get("/v1/plugins/tensorflow/routes/ops/EncodePng");

mldb.log(op);

var png = mldb.query("SELECT tf_EncodePng([[[0,0,0,0]]], {T: { type: 'DT_UINT8'}}) AS png NAMED 'png'");
mldb.log(png);

var png = mldb.query("SELECT tf_EncodePng([[[1,1,1,0]]], {}) AS png NAMED 'png'");
mldb.log(png);

var png = mldb.query("SELECT tf_EncodePng([[[1,1,1,0]]]) AS png NAMED 'png'");
mldb.log(png);

var conv_output = mldb.query("SELECT tf_Conv2D({input: [[[[1], [1], [1]], [[1], [1], [1]], [[1], [1], [1]]]], filter: [[[[1]], [[2]]], [[[2]], [[1]]]]}, {T: { type: 'DT_FLOAT'}, padding: 'SAME', strides: [ 1, 1, 1, 1] }) AS res")
mldb.log(conv_output)

//check that we get the name of the node in the error message
var jpeg = mldb.get('/v1/query', {q: "SELECT tf_EncodeJpeg([[]]) AS *", format: 'table'});
assertEqual(jpeg.json.error, "Fetching tensor for input node `image`: Can't convert value '' of type 'EMPTY' to unsigned integer")

"success"
<commit_msg>Update MLDB-1736-tensorflow-builtins.js<commit_after>// This file is part of MLDB. Copyright 2016 mldb.ai inc. All rights reserved.

function assertEqual(expr, val)
{
    if (expr == val)
        return;
    if (JSON.stringify(expr) == JSON.stringify(val))
        return;

    mldb.log(expr, 'IS NOT EQUAL TO', val);

    throw "Assertion failure";
}

var res1 = mldb.query("SELECT tf_Cos(1.23, {T: { type: 'DT_DOUBLE'}}) AS res");
var res2 = mldb.query("SELECT cos(1.23) AS res");

mldb.log(res1[0].columns[0]);
mldb.log(res2[0].columns[0]);

assertEqual(res1[0].columns[0][1],
            res2[0].columns[0][1]);

var res = mldb.query("SELECT tf_MatrixInverse([[1,2],[3, 4]], { T: { type: 'DT_DOUBLE' } }) AS res");

mldb.log(res);

var expected = [
   {
      "columns" : [
         [ "res.0.0", -1.9999999999999998, "NaD" ],
         [ "res.0.1", 1.0, "NaD" ],
         [ "res.1.0", 1.4999999999999998, "NaD" ],
         [ "res.1.1", -0.49999999999999994, "NaD" ]
      ],
      "rowName" : "result"
   }
];

assertEqual(res, expected);

var ops = mldb.get("/v1/plugins/tensorflow/routes/ops");

mldb.log(ops);

var op = mldb.get("/v1/plugins/tensorflow/routes/ops/EncodePng");

mldb.log(op);

var png = mldb.query("SELECT tf_EncodePng([[[0,0,0,0]]], {T: { type: 'DT_UINT8'}}) AS png NAMED 'png'");
mldb.log(png);

var png = mldb.query("SELECT tf_EncodePng([[[1,1,1,0]]], {}) AS png NAMED 'png'");
mldb.log(png);

var png = mldb.query("SELECT tf_EncodePng([[[1,1,1,0]]]) AS png NAMED 'png'");
mldb.log(png);

var conv_output = mldb.query("SELECT tf_Conv2D({input: [[[[1], [1], [1]], [[1], [1], [1]], [[1], [1], [1]]]], filter: [[[[1]], [[2]]], [[[2]], [[1]]]]}, {T: { type: 'DT_FLOAT'}, padding: 'SAME', strides: [ 1, 1, 1, 1] }) AS res")
mldb.log(conv_output)

//check that we get the name of the node in the error message
var jpeg = mldb.get('/v1/query', {q: "SELECT tf_EncodeJpeg([[]]) AS *", format: 'table'});
assertEqual(jpeg.json.error, "Fetching tensor for input node 'image': Can't convert value '' of type 'EMPTY' to unsigned integer")

"success"
<|endoftext|>


<commit_before>var store = storage('local');

function mingleAPI(path) {
  return store.get('options').then(function (options) {
    var settings = {
      url: `${ options.host }/api/v2${ path }`,
      dataType: 'xml'
    };
    if (options.username && options.password) {
      settings.headers = {
        'Authorization': 'Basic ' + btoa(options.username + ':' + options.password)
      };
    }
    return Promise.resolve($.ajax(settings));
  });
}

function getMingleCardInfo(card) {
  return store.get('options').then(function (options) {
    return mingleAPI(`/projects/${ card.project }/cards/${ card.number }.xml`).then(function (xml) {
      var $xml = $(xml);

      $.extend(card, {
        name: $xml.find('card > name').text(),
        type: $xml.find('card > card_type name').text(),
        url: `${ options.host }/projects/${ card.project }/cards/${ card.number }`,
        labels: []
      });

      return Promise.all([
        addCardLabel(card),
        addCardTransitions(card),
        addPropertyLabels(card, $xml, options)
      ])
      .then(function () {
        return card;
      });
    });
  });
}

function addCardTransitions(card) {
  // TODO: support user input for transitions
  return mingleAPI(`/projects/${ card.project }/cards/${ card.number }/transitions.xml`).then(function (xml) {
    var $xml = $(xml);
    card.transitions = $xml
      .find(`transitions > transition`)
      .map(function () {
        var $node = $(this);
        return {
          id: $node.children('id').text(),
          name: $node.children('name').text()
        };
      })
      .get();
  });
}

function getPropertyValue($xml, name) {
  var $value = $xml
    .find('card > properties > property name')
    .filter(function () { return $(this).text() === name; })
    .parent()
    .find('value');

  if ($value.find('name').size()) {
    return $value.find('name');
  } else {
    return $value;
  }
}

function getCardColors(card) {
  var key = card.project + '_typeColorMap';

  return store.get(key).then(function (colors) {
    if (colors) return colors;

    return mingleAPI(`/projects/${ card.project }/card_types.xml`).then(function (xml) {
      var $xml = $(xml);
      var colors = $xml
        .find(`card_types > card_type`)
        .map(function () {
          var $node = $(this);
          return {
            name: $node.children('name').text(),
            color: $node.children('color').text() || '#5bb2ef'
          };
        })
        .get()
        .reduce(function (colors, type) {
          colors[type.name] = type.color;
          return colors;
        }, {});

      return store.set(key, colors);
    });
  });
}

function getCardPropertyColors(card) {
  var key = card.project + '_propertyColorMap';

  return store.get(key).then(function (colors) {
    if (colors) return colors;

    var colors = {};

    return mingleAPI(`/projects/${ card.project }/property_definitions.xml`).then(function (xml) {
      $(xml)
        .find('property_definitions > property_definition name').get()
        .forEach(function (node) {
          var $prop = $(node);
          var name = $prop.text();
          var values = $prop.parent().find('property_value_details > property_value').get();
          colors[name] = {};

          values.forEach(function (node) {
            var $node = $(node);
            var value = $node.children('value').text();
            var color = $node.children('color').text();
            colors[name][value] = color || '#5bb2ef';
          });
        });

      return store.set(key, colors);
    });
  });
}

function addCardLabel(card) {
  return getCardColors(card).then(function (colors) {
    var label = {
      name: 'Mingle',
      value: card.type + ' #' + card.number,
      color: colors[card.type]
    };
    card.labels.push(label);
  });
}

function addPropertyLabel(card, $xml, colors, property) {
  if (!property) return;
  var value = getPropertyValue($xml, property).text();
  var label = {
    name: property,
    value: value,
    color: colors[property][value]
  };
  card.labels.push(label);
}

function addPropertyLabels(card, $xml, options) {
  var properties = options.properties.split(',');
  return getCardPropertyColors(card).then(function (colors) {
    properties.forEach(function (property) {
      addPropertyLabel(card, $xml, colors, $.trim(property));
    });
  });
}

chrome.runtime.onMessage.addListener(function (request, sender, callback) {
  if (request.mingle) {
    getMingleCardInfo(request.mingle).then(callback);
    return true; // make sure we wait for asynchronously executed callback
  }
});
<commit_msg>add optional method parameter for API calls<commit_after>var store = storage('local');

function mingleAPI(path, method) {
  return store.get('options').then(function (options) {
    var settings = {
      url: `${ options.host }/api/v2${ path }`,
      dataType: 'xml',
      method: method || 'GET'
    };
    if (options.username && options.password) {
      settings.headers = {
        'Authorization': 'Basic ' + btoa(options.username + ':' + options.password)
      };
    }
    return Promise.resolve($.ajax(settings));
  });
}

function getMingleCardInfo(card) {
  return store.get('options').then(function (options) {
    return mingleAPI(`/projects/${ card.project }/cards/${ card.number }.xml`).then(function (xml) {
      var $xml = $(xml);

      $.extend(card, {
        name: $xml.find('card > name').text(),
        type: $xml.find('card > card_type name').text(),
        url: `${ options.host }/projects/${ card.project }/cards/${ card.number }`,
        labels: []
      });

      return Promise.all([
        addCardLabel(card),
        addCardTransitions(card),
        addPropertyLabels(card, $xml, options)
      ])
      .then(function () {
        return card;
      });
    });
  });
}

function addCardTransitions(card) {
  // TODO: support user input for transitions
  return mingleAPI(`/projects/${ card.project }/cards/${ card.number }/transitions.xml`).then(function (xml) {
    var $xml = $(xml);
    card.transitions = $xml
      .find(`transitions > transition`)
      .map(function () {
        var $node = $(this);
        return {
          id: $node.children('id').text(),
          name: $node.children('name').text()
        };
      })
      .get();
  });
}

function getPropertyValue($xml, name) {
  var $value = $xml
    .find('card > properties > property name')
    .filter(function () { return $(this).text() === name; })
    .parent()
    .find('value');

  if ($value.find('name').size()) {
    return $value.find('name');
  } else {
    return $value;
  }
}

function getCardColors(card) {
  var key = card.project + '_typeColorMap';

  return store.get(key).then(function (colors) {
    if (colors) return colors;

    return mingleAPI(`/projects/${ card.project }/card_types.xml`).then(function (xml) {
      var $xml = $(xml);
      var colors = $xml
        .find(`card_types > card_type`)
        .map(function () {
          var $node = $(this);
          return {
            name: $node.children('name').text(),
            color: $node.children('color').text() || '#5bb2ef'
          };
        })
        .get()
        .reduce(function (colors, type) {
          colors[type.name] = type.color;
          return colors;
        }, {});

      return store.set(key, colors);
    });
  });
}

function getCardPropertyColors(card) {
  var key = card.project + '_propertyColorMap';

  return store.get(key).then(function (colors) {
    if (colors) return colors;

    var colors = {};

    return mingleAPI(`/projects/${ card.project }/property_definitions.xml`).then(function (xml) {
      $(xml)
        .find('property_definitions > property_definition name').get()
        .forEach(function (node) {
          var $prop = $(node);
          var name = $prop.text();
          var values = $prop.parent().find('property_value_details > property_value').get();
          colors[name] = {};

          values.forEach(function (node) {
            var $node = $(node);
            var value = $node.children('value').text();
            var color = $node.children('color').text();
            colors[name][value] = color || '#5bb2ef';
          });
        });

      return store.set(key, colors);
    });
  });
}

function addCardLabel(card) {
  return getCardColors(card).then(function (colors) {
    var label = {
      name: 'Mingle',
      value: card.type + ' #' + card.number,
      color: colors[card.type]
    };
    card.labels.push(label);
  });
}

function addPropertyLabel(card, $xml, colors, property) {
  if (!property) return;
  var value = getPropertyValue($xml, property).text();
  var label = {
    name: property,
    value: value,
    color: colors[property][value]
  };
  card.labels.push(label);
}

function addPropertyLabels(card, $xml, options) {
  var properties = options.properties.split(',');
  return getCardPropertyColors(card).then(function (colors) {
    properties.forEach(function (property) {
      addPropertyLabel(card, $xml, colors, $.trim(property));
    });
  });
}

chrome.runtime.onMessage.addListener(function (request, sender, callback) {
  if (request.mingle) {
    getMingleCardInfo(request.mingle).then(callback);
    return true; // make sure we wait for asynchronously executed callback
  }
});
<|endoftext|>