<commit_before>import $ from "jquery";
import { Grid } from "ag-grid/main";
import { formatDate } from "../../utilities/date-utilities";

import "ag-grid/dist/styles/ag-grid.css";
import "ag-grid/dist/styles/ag-theme-balham.css";

/**
 * Internationalized text from div#messages.hidden
 * @type {Object} map of data attribute key name to i18n text
 */
let MESSAGES = {
  sampleName: "SAMPLE NAME",
  file: "FILE",
  analysisType: "ANALYSIS TYPE",
  pipeline: "PIPELINE",
  analysisSubmissionName: "ANALYSIS SUBMISSION",
  createdDate: "CREATED",
  download: "DOWNLOAD",
  submitter: "SUBMITTER",
  statusCode: "STATUS CODE",
  requestUrl: "REQUEST URL",
  statusText: "STATUS TEXT",
  reqError: "REQUEST ERROR",
  error: "!!!ERROR!!!",
  preparing: "PREPARING DOWNLOAD",
  automatedAnalyses: "AUTOMATED ANALYSES",
  sharedAnalyses: "SHARED ANALYSES"
};
MESSAGES = Object.assign(MESSAGES, $("#js-messages").data());

/**
 * Analysis output file path regex to capture filename with extension
 * @type {RegExp}
 */
const FILENAME_REGEX = /.*\/(.+\.\w+)/;

/**
 * Base URL for AJAX requests.
 * @type {string}
 */
const BASE_URL = window.PAGE.URLS.base;

/**
 * Get the project id if on Project Analysis Outputs page; null if on User Analysis Outputs page
 * @return {?number} Project id if on Project Analysis Outputs page; null if on User Analysis Outputs page
 */
function getProjectId() {
  try {
    return window.project.id;
  } catch (e) {
    return null;
  }
}

/**
 * URL to get analysis output file info via AJAX for a project or user
 * @param {boolean} isShared If true get shared analysis output, else false get automated
 * @return {string} AJAX URL to get analysis output file info
 */
function getAjaxUrl(isShared = true) {
  const projectId = getProjectId();
  if (projectId) {
    return isShared
      ? window.PAGE.URLS.sharedAnalyses
      : window.PAGE.URLS.automatedAnalyses;
  } else {
    return `${BASE_URL}analysis/ajax/user/analysis-outputs`;
  }
}

/**
 * HTML container for dynamically generated table and other UI elements
 * @type {jQuery|HTMLElement}
 */
const $app = $("#app");

/**
 * ag-grid Grid instance object
 * @type {Grid}
 */
let grid;

/**
 * Get filename from path
 * @param path File path
 */
function getFilename(path) {
  return path.replace(FILENAME_REGEX, "$1");
}

/**
 * Download a file with a temporary hidden  element.
 *
 * @param {string} url URL for download
 * @param {string} downloadName Download filename
 */
function downloadFile(url, downloadName) {
  /**
   * Hidden  element for downloading each AOF
   * @type {HTMLAnchorElement}
   */
  const $a = document.createElement("a");
  $a.style.display = "none";
  document.body.appendChild($a);
  $a.setAttribute("href", url);
  $a.setAttribute("download", downloadName);
  $a.click();
  document.body.removeChild($a);
}

/**
 * Download analysis output files (AOFs) for selected rows in ag-grid table
 *
 * A single selected AOF will be downloaded unzipped.
 * Multiple files are downloaded in the same request as a Zipped response
 * stream.
 *
 * @param {Object} api ag-grid grid options API object
 * @param {jQuery|HTMLElement} $dlButton
 */
function downloadSelected($dlButton, api) {
  /**
   * Selected rows in the ag-grid Grid corresponding to AOFs
   * @type {*|RowNode[]}
   */
  const selectedNodes = api.getSelectedNodes();
  setDownloadButtonHtml($dlButton, selectedNodes.length, true, true);
  if (selectedNodes.length === 1) {
    const {
      analysisSubmissionId,
      analysisOutputFileId,
      sampleName,
      sampleId,
      filePath
    } = selectedNodes[0].data;
    let url = `${BASE_URL}analysis/ajax/download/${analysisSubmissionId}/file/${analysisOutputFileId}`;
    const downloadName = `${sampleName}-sampleId-${sampleId}-analysisSubmissionId-${analysisSubmissionId}-${getFilename(
      filePath
    )}`;
    url += "?" + $.param({ filename: downloadName });
    downloadFile(url, downloadName);
    setDownloadButtonHtml($dlButton, selectedNodes.length);
  } else if (selectedNodes.length > 1) {
    const outputs = selectedNodes.map(node => node.data);
    let url = `${BASE_URL}analysis/ajax/download/prepare`;
    let downloadUrl = `${BASE_URL}analysis/ajax/download/selection`;
    $.ajax({
      url,
      type: "POST",
      data: JSON.stringify(outputs),
      contentType: "application/json",
      dataType: "json",
      success: ({ selectionSize }) => {
        const projectId = getProjectId();
        const projectOrUser = projectId ? `projectId-${projectId}` : `user`;
        downloadUrl += `?filename=${projectOrUser}-batch-download-${selectionSize}-analysis-output-files`;
        downloadFile(downloadUrl, "batch-download.zip");
        setDownloadButtonHtml($dlButton, selectedNodes.length);
      },
      error: (jqXHR, textStatus, errorThrown) => {
        console.error(jqXHR, textStatus, errorThrown);
        setDownloadButtonHtml($dlButton, selectedNodes.length);
      }
    });
  } else {
    $dlButton.attr("disabled", null);
  }
}

/**
 * Set analysis output file download button inner HTML
 * @param {jQuery|HTMLElement} $dlButton Download button element
 * @param {number} selectionLength Number of rows selected for download
 * @param {boolean} isDisabled Disable the download button?
 * @param {boolean} isPreparing Is the download being prepared? It's necessary to prepare downloads for large collections of analysis output files.
 */
function setDownloadButtonHtml(
  $dlButton,
  selectionLength,
  isDisabled = false,
  isPreparing = false
) {
  $dlButton.attr("disabled", isDisabled ? "disabled" : null);
  const badge = selectionLength
    ? `<span class="badge">${selectionLength}</span>`
    : "";
  $dlButton.html(
    ` ${
      isPreparing ? MESSAGES.preparing : MESSAGES.download
    } ${badge}`
  );
}

/**
 * Initialize ag-grid Grid
 * @param {HTMLElement} $grid Element to create Grid in
 * @param {Array<Object<string>>} headers Table fields
 * @param {Array<Object>} rows Table data
 * @param {jQuery|HTMLElement} $dlButton Download button
 * @return {Grid} ag-grid object
 */
function initAgGrid($grid, headers, rows, $dlButton) {
  const gridOptions = {
    enableColResize: true,
    columnDefs: headers,
    rowData: rows,
    rowDeselection: true,
    enableSorting: true,
    enableFilter: true,
    rowSelection: "multiple",
    onSelectionChanged: e => {
      const selectedNodes = e.api.getSelectedNodes();
      const selectionLength = selectedNodes.length;
      setDownloadButtonHtml($dlButton, selectionLength, selectionLength === 0);
    }
  };
  const grid = new Grid($grid, gridOptions);
  gridOptions.api.sizeColumnsToFit();
  $dlButton.on("click", e => {
    e.preventDefault();
    downloadSelected($dlButton, gridOptions.api);
  });
  return grid;
}

/**
 * Filter for single sample analysis outputs.
 * @param data
 * @returns {Array<Object>}
 */
function filterSingleSampleOutputs(data) {
  // group analysis output file (AOF) info by AOF id
  const groupedDataByAofId = data.reduce((acc, x) => {
    const { analysisOutputFileId } = x;
    if (acc.hasOwnProperty(analysisOutputFileId)) {
      acc[analysisOutputFileId].push(x);
    } else {
      acc[analysisOutputFileId] = [x];
    }
    return acc;
  }, {});
  // return AOF info that only has one Sample assoc with the AOF id, i.e. single
  // sample AOFs
  return Object.keys(groupedDataByAofId)
    .filter(x => groupedDataByAofId[x].length === 1)
    .map(x => groupedDataByAofId[x][0]);
}

/**
 * Get workflow/pipeline info and save to `workflowIds` map.
 * @param {Array<Object>} singleSampleOutputs Single sample analysis output file infos
 * @returns {Object<Object>} Map of workflow id to workflow info map.
 */
function getWorkflowInfo(singleSampleOutputs) {
  const workflowIds = singleSampleOutputs.reduce(
    (acc, x) => Object.assign(acc, { [x.workflowId]: null }),
    {}
  );
  Object.keys(workflowIds).forEach(workflowId => {
    $.get(`${BASE_URL}pipelines/ajax/${workflowId}`).done(wfInfo => {
      workflowIds[workflowId] = wfInfo;
      if (grid) {
        grid.context.beans.gridApi.beanInstance.redrawRows();
      }
    });
  });
  return workflowIds;
}

/**
 * Get analysis output file (AOF) table information and create table.
 * @param {boolean} isShared If project analyses to be shown, show outputs shared with project, otherwise show automated analyses.
 */
function getTableData(isShared = true) {
  $.get(getAjaxUrl(isShared))
    .done(data => {
      $app.html("");
      const singleSampleOutputs = filterSingleSampleOutputs(data);
      const workflowIds = getWorkflowInfo(singleSampleOutputs);
      /**
       * ag-grid Grid header definitions
       * @type {*[]}
       */
      const HEADERS = [
        {
          field: "sampleName",
          headerName: MESSAGES.sampleName,
          checkboxSelection: true,
          headerCheckboxSelection: true,
          headerCheckboxSelectionFilteredOnly: true,
          cellRenderer: p => {
            const { sampleId, sampleName } = p.data;
            const projectId = getProjectId();
            const projectUrlPrefix = projectId ? `projects/${projectId}/` : "";
            return `<a href="${BASE_URL}${projectUrlPrefix}samples/${sampleId}" target="_blank">${sampleName}</a>`;
          }
        },
        {
          field: "filePath",
          headerName: MESSAGES.file,
          cellRenderer: p => {
            const {
              filePath,
              analysisOutputFileKey,
              analysisOutputFileId
            } = p.data;
            const REGEX = /^\d+\/\d+\/(.+)$/;
            const groups = REGEX.exec(filePath);
            if (groups === null) return filePath;
            const filename = groups[1];

            return `${filename} <small>(${analysisOutputFileKey}, id=${analysisOutputFileId})</small>`;
          }
        },
        {
          field: "analysisType",
          headerName: MESSAGES.analysisType
        },
        {
          field: "workflowId",
          headerName: MESSAGES.pipeline,
          cellRenderer: p => {
            const wfInfo = workflowIds[p.data.workflowId];
            if (wfInfo === null) return p.data.workflowId;
            return `${wfInfo.name} (v${wfInfo.version})`;
          }
        },
        {
          field: "analysisSubmissionName",
          headerName: MESSAGES.analysisSubmissionName,
          cellRenderer: p =>
            `${p.data.analysisSubmissionName}`
        },
        getProjectId()
          ? {
              field: "userId",
              headerName: MESSAGES.submitter,
              cellRenderer: p =>
                `${p.data.userFirstName} ${p.data.userLastName}`
            }
          : null,
        {
          field: "createdDate",
          headerName: MESSAGES.createdDate,
          cellRenderer: p => formatDate({ date: p.data.createdDate })
        }
      ].filter(header => header !== null);
      const gridId = `grid-outputs`;
      const $grid = $(
        `<div id="${gridId}" class="ag-theme-balham" style="height: 600px; width: 100%; resize: both;"/>`
      );
      const $dlButton = $(
        `<button type="button" class="btn" disabled="disabled"></button>`
      );
      setDownloadButtonHtml($dlButton, 0, true);
      $app.prepend($dlButton);
      $app.append($grid);

      /**
       * Set `grid` to initialized ag-grid Grid for access to Grid API.
       * @type {Grid}
       */
      grid = initAgGrid(
        document.getElementById(gridId),
        HEADERS,
        singleSampleOutputs,
        $dlButton
      );
    })
    .fail((xhr, error, exception) => {
      const $alert = $(
        `<div class="alert alert-danger"><h4>${MESSAGES.reqError}</h4></div>`
      );
      if (xhr !== null) {
        $alert.append($(`<p>${MESSAGES.statusCode}: ${xhr.status}</p>`));
        $alert.append($(`<p>${MESSAGES.requestUrl}: ${AJAX_URL}</p>`));
      }
      if (exception !== null) {
        $alert.append($(`<p>${MESSAGES.statusText}: ${exception}</p>`));
      }
      if (error !== null) {
        $alert.append($(`<p>${MESSAGES.error}: "${error}"</p>`));
      }
      $app.append($alert);
    });
}

// init table on page load with outputs shared to project
getTableData(window.PAGE.page === "shared");
<commit_msg>Added CSS class `spaced-bottom` to download button for outputs download table<commit_after>import $ from "jquery";
import { Grid } from "ag-grid/main";
import { formatDate } from "../../utilities/date-utilities";

import "ag-grid/dist/styles/ag-grid.css";
import "ag-grid/dist/styles/ag-theme-balham.css";

/**
 * Internationalized text from div#messages.hidden
 * @type {Object} map of data attribute key name to i18n text
 */
let MESSAGES = {
  sampleName: "SAMPLE NAME",
  file: "FILE",
  analysisType: "ANALYSIS TYPE",
  pipeline: "PIPELINE",
  analysisSubmissionName: "ANALYSIS SUBMISSION",
  createdDate: "CREATED",
  download: "DOWNLOAD",
  submitter: "SUBMITTER",
  statusCode: "STATUS CODE",
  requestUrl: "REQUEST URL",
  statusText: "STATUS TEXT",
  reqError: "REQUEST ERROR",
  error: "!!!ERROR!!!",
  preparing: "PREPARING DOWNLOAD",
  automatedAnalyses: "AUTOMATED ANALYSES",
  sharedAnalyses: "SHARED ANALYSES"
};
MESSAGES = Object.assign(MESSAGES, $("#js-messages").data());

/**
 * Analysis output file path regex to capture filename with extension
 * @type {RegExp}
 */
const FILENAME_REGEX = /.*\/(.+\.\w+)/;

/**
 * Base URL for AJAX requests.
 * @type {string}
 */
const BASE_URL = window.PAGE.URLS.base;

/**
 * Get the project id if on Project Analysis Outputs page; null if on User Analysis Outputs page
 * @return {?number} Project id if on Project Analysis Outputs page; null if on User Analysis Outputs page
 */
function getProjectId() {
  try {
    return window.project.id;
  } catch (e) {
    return null;
  }
}

/**
 * URL to get analysis output file info via AJAX for a project or user
 * @param {boolean} isShared If true get shared analysis output, else false get automated
 * @return {string} AJAX URL to get analysis output file info
 */
function getAjaxUrl(isShared = true) {
  const projectId = getProjectId();
  if (projectId) {
    return isShared
      ? window.PAGE.URLS.sharedAnalyses
      : window.PAGE.URLS.automatedAnalyses;
  } else {
    return `${BASE_URL}analysis/ajax/user/analysis-outputs`;
  }
}

/**
 * HTML container for dynamically generated table and other UI elements
 * @type {jQuery|HTMLElement}
 */
const $app = $("#app");

/**
 * ag-grid Grid instance object
 * @type {Grid}
 */
let grid;

/**
 * Get filename from path
 * @param path File path
 */
function getFilename(path) {
  return path.replace(FILENAME_REGEX, "$1");
}

/**
 * Download a file with a temporary hidden  element.
 *
 * @param {string} url URL for download
 * @param {string} downloadName Download filename
 */
function downloadFile(url, downloadName) {
  /**
   * Hidden  element for downloading each AOF
   * @type {HTMLAnchorElement}
   */
  const $a = document.createElement("a");
  $a.style.display = "none";
  document.body.appendChild($a);
  $a.setAttribute("href", url);
  $a.setAttribute("download", downloadName);
  $a.click();
  document.body.removeChild($a);
}

/**
 * Download analysis output files (AOFs) for selected rows in ag-grid table
 *
 * A single selected AOF will be downloaded unzipped.
 * Multiple files are downloaded in the same request as a Zipped response
 * stream.
 *
 * @param {Object} api ag-grid grid options API object
 * @param {jQuery|HTMLElement} $dlButton
 */
function downloadSelected($dlButton, api) {
  /**
   * Selected rows in the ag-grid Grid corresponding to AOFs
   * @type {*|RowNode[]}
   */
  const selectedNodes = api.getSelectedNodes();
  setDownloadButtonHtml($dlButton, selectedNodes.length, true, true);
  if (selectedNodes.length === 1) {
    const {
      analysisSubmissionId,
      analysisOutputFileId,
      sampleName,
      sampleId,
      filePath
    } = selectedNodes[0].data;
    let url = `${BASE_URL}analysis/ajax/download/${analysisSubmissionId}/file/${analysisOutputFileId}`;
    const downloadName = `${sampleName}-sampleId-${sampleId}-analysisSubmissionId-${analysisSubmissionId}-${getFilename(
      filePath
    )}`;
    url += "?" + $.param({ filename: downloadName });
    downloadFile(url, downloadName);
    setDownloadButtonHtml($dlButton, selectedNodes.length);
  } else if (selectedNodes.length > 1) {
    const outputs = selectedNodes.map(node => node.data);
    let url = `${BASE_URL}analysis/ajax/download/prepare`;
    let downloadUrl = `${BASE_URL}analysis/ajax/download/selection`;
    $.ajax({
      url,
      type: "POST",
      data: JSON.stringify(outputs),
      contentType: "application/json",
      dataType: "json",
      success: ({ selectionSize }) => {
        const projectId = getProjectId();
        const projectOrUser = projectId ? `projectId-${projectId}` : `user`;
        downloadUrl += `?filename=${projectOrUser}-batch-download-${selectionSize}-analysis-output-files`;
        downloadFile(downloadUrl, "batch-download.zip");
        setDownloadButtonHtml($dlButton, selectedNodes.length);
      },
      error: (jqXHR, textStatus, errorThrown) => {
        console.error(jqXHR, textStatus, errorThrown);
        setDownloadButtonHtml($dlButton, selectedNodes.length);
      }
    });
  } else {
    $dlButton.attr("disabled", null);
  }
}

/**
 * Set analysis output file download button inner HTML
 * @param {jQuery|HTMLElement} $dlButton Download button element
 * @param {number} selectionLength Number of rows selected for download
 * @param {boolean} isDisabled Disable the download button?
 * @param {boolean} isPreparing Is the download being prepared? It's necessary to prepare downloads for large collections of analysis output files.
 */
function setDownloadButtonHtml(
  $dlButton,
  selectionLength,
  isDisabled = false,
  isPreparing = false
) {
  $dlButton.attr("disabled", isDisabled ? "disabled" : null);
  const badge = selectionLength
    ? `<span class="badge">${selectionLength}</span>`
    : "";
  $dlButton.html(
    ` ${
      isPreparing ? MESSAGES.preparing : MESSAGES.download
    } ${badge}`
  );
}

/**
 * Initialize ag-grid Grid
 * @param {HTMLElement} $grid Element to create Grid in
 * @param {Array<Object<string>>} headers Table fields
 * @param {Array<Object>} rows Table data
 * @param {jQuery|HTMLElement} $dlButton Download button
 * @return {Grid} ag-grid object
 */
function initAgGrid($grid, headers, rows, $dlButton) {
  const gridOptions = {
    enableColResize: true,
    columnDefs: headers,
    rowData: rows,
    rowDeselection: true,
    enableSorting: true,
    enableFilter: true,
    rowSelection: "multiple",
    onSelectionChanged: e => {
      const selectedNodes = e.api.getSelectedNodes();
      const selectionLength = selectedNodes.length;
      setDownloadButtonHtml($dlButton, selectionLength, selectionLength === 0);
    }
  };
  const grid = new Grid($grid, gridOptions);
  gridOptions.api.sizeColumnsToFit();
  $dlButton.on("click", e => {
    e.preventDefault();
    downloadSelected($dlButton, gridOptions.api);
  });
  return grid;
}

/**
 * Filter for single sample analysis outputs.
 * @param data
 * @returns {Array<Object>}
 */
function filterSingleSampleOutputs(data) {
  // group analysis output file (AOF) info by AOF id
  const groupedDataByAofId = data.reduce((acc, x) => {
    const { analysisOutputFileId } = x;
    if (acc.hasOwnProperty(analysisOutputFileId)) {
      acc[analysisOutputFileId].push(x);
    } else {
      acc[analysisOutputFileId] = [x];
    }
    return acc;
  }, {});
  // return AOF info that only has one Sample assoc with the AOF id, i.e. single
  // sample AOFs
  return Object.keys(groupedDataByAofId)
    .filter(x => groupedDataByAofId[x].length === 1)
    .map(x => groupedDataByAofId[x][0]);
}

/**
 * Get workflow/pipeline info and save to `workflowIds` map.
 * @param {Array<Object>} singleSampleOutputs Single sample analysis output file infos
 * @returns {Object<Object>} Map of workflow id to workflow info map.
 */
function getWorkflowInfo(singleSampleOutputs) {
  const workflowIds = singleSampleOutputs.reduce(
    (acc, x) => Object.assign(acc, { [x.workflowId]: null }),
    {}
  );
  Object.keys(workflowIds).forEach(workflowId => {
    $.get(`${BASE_URL}pipelines/ajax/${workflowId}`).done(wfInfo => {
      workflowIds[workflowId] = wfInfo;
      if (grid) {
        grid.context.beans.gridApi.beanInstance.redrawRows();
      }
    });
  });
  return workflowIds;
}

/**
 * Get analysis output file (AOF) table information and create table.
 * @param {boolean} isShared If project analyses to be shown, show outputs shared with project, otherwise show automated analyses.
 */
function getTableData(isShared = true) {
  $.get(getAjaxUrl(isShared))
    .done(data => {
      $app.html("");
      const singleSampleOutputs = filterSingleSampleOutputs(data);
      const workflowIds = getWorkflowInfo(singleSampleOutputs);
      /**
       * ag-grid Grid header definitions
       * @type {*[]}
       */
      const HEADERS = [
        {
          field: "sampleName",
          headerName: MESSAGES.sampleName,
          checkboxSelection: true,
          headerCheckboxSelection: true,
          headerCheckboxSelectionFilteredOnly: true,
          cellRenderer: p => {
            const { sampleId, sampleName } = p.data;
            const projectId = getProjectId();
            const projectUrlPrefix = projectId ? `projects/${projectId}/` : "";
            return `<a href="${BASE_URL}${projectUrlPrefix}samples/${sampleId}" target="_blank">${sampleName}</a>`;
          }
        },
        {
          field: "filePath",
          headerName: MESSAGES.file,
          cellRenderer: p => {
            const {
              filePath,
              analysisOutputFileKey,
              analysisOutputFileId
            } = p.data;
            const REGEX = /^\d+\/\d+\/(.+)$/;
            const groups = REGEX.exec(filePath);
            if (groups === null) return filePath;
            const filename = groups[1];

            return `${filename} <small>(${analysisOutputFileKey}, id=${analysisOutputFileId})</small>`;
          }
        },
        {
          field: "analysisType",
          headerName: MESSAGES.analysisType
        },
        {
          field: "workflowId",
          headerName: MESSAGES.pipeline,
          cellRenderer: p => {
            const wfInfo = workflowIds[p.data.workflowId];
            if (wfInfo === null) return p.data.workflowId;
            return `${wfInfo.name} (v${wfInfo.version})`;
          }
        },
        {
          field: "analysisSubmissionName",
          headerName: MESSAGES.analysisSubmissionName,
          cellRenderer: p =>
            `${p.data.analysisSubmissionName}`
        },
        getProjectId()
          ? {
              field: "userId",
              headerName: MESSAGES.submitter,
              cellRenderer: p =>
                `${p.data.userFirstName} ${p.data.userLastName}`
            }
          : null,
        {
          field: "createdDate",
          headerName: MESSAGES.createdDate,
          cellRenderer: p => formatDate({ date: p.data.createdDate })
        }
      ].filter(header => header !== null);
      const gridId = `grid-outputs`;
      const $grid = $(
        `<div id="${gridId}" class="ag-theme-balham" style="height: 600px; width: 100%; resize: both;"/>`
      );
      const $dlButton = $(
        `<button type="button" class="btn spaced-bottom" disabled="disabled"></button>`
      );
      setDownloadButtonHtml($dlButton, 0, true);
      $app.prepend($dlButton);
      $app.append($grid);

      /**
       * Set `grid` to initialized ag-grid Grid for access to Grid API.
       * @type {Grid}
       */
      grid = initAgGrid(
        document.getElementById(gridId),
        HEADERS,
        singleSampleOutputs,
        $dlButton
      );
    })
    .fail((xhr, error, exception) => {
      const $alert = $(
        `<div class="alert alert-danger"><h4>${MESSAGES.reqError}</h4></div>`
      );
      if (xhr !== null) {
        $alert.append($(`<p>${MESSAGES.statusCode}: ${xhr.status}</p>`));
        $alert.append($(`<p>${MESSAGES.requestUrl}: ${AJAX_URL}</p>`));
      }
      if (exception !== null) {
        $alert.append($(`<p>${MESSAGES.statusText}: ${exception}</p>`));
      }
      if (error !== null) {
        $alert.append($(`<p>${MESSAGES.error}: "${error}"</p>`));
      }
      $app.append($alert);
    });
}

// init table on page load with outputs shared to project
getTableData(window.PAGE.page === "shared");
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');

var adjacency_list = require('./adjacency_list.js');
var operations = require('./operations.js');

function testBreadthFirstSearch() {
    testSearch(operations.breadthFirstSearch, [0, 1, 4, 5, 2, 3]);
}

function testDepthFirstSearch() {
    testSearch(operations.depthFirstSearch, [0, 1, 2, 3, 4, 5]);
}

function testSearch(searchFunction, expectedVertices) {
    var list = new adjacency_list.construct({
        vertexCount: 6,
        edges: [
            [0, 1],
            [0, 4],
            [0, 5],
            [1, 2],
            [1, 4],
            [2, 3],
            [3, 4]
        ]
    });

    adjacency_list.log(list);

    assert.strictEqual(list.length, 6);

    var vertices = [];
    var processVertex = function(x) {
        console.log('Processing vertex: ' + x);
        vertices.push(x);
    };

    searchFunction(list, 0, processVertex);

    assert.deepEqual(vertices, expectedVertices);
}

testBreadthFirstSearch();
testDepthFirstSearch();

console.log('All tests OK.');
<commit_msg>[adjacency_list/javascript] Remove logs<commit_after>'use strict';

var assert = require('assert');

var adjacency_list = require('./adjacency_list.js');
var operations = require('./operations.js');

function testBreadthFirstSearch() {
    testSearch(operations.breadthFirstSearch, [0, 1, 4, 5, 2, 3]);
}

function testDepthFirstSearch() {
    testSearch(operations.depthFirstSearch, [0, 1, 2, 3, 4, 5]);
}

function testSearch(searchFunction, expectedVertices) {
    var list = new adjacency_list.construct({
        vertexCount: 6,
        edges: [
            [0, 1],
            [0, 4],
            [0, 5],
            [1, 2],
            [1, 4],
            [2, 3],
            [3, 4]
        ]
    });

    assert.strictEqual(list.length, 6);

    var vertices = [];
    var processVertex = function(x) {
        vertices.push(x);
    };

    searchFunction(list, 0, processVertex);

    assert.deepEqual(vertices, expectedVertices);
}

testBreadthFirstSearch();
testDepthFirstSearch();

console.log('All tests OK.');
<|endoftext|>


<commit_before>export default {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "formula": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string"
                },
                "start": {
                    "type": "string",
                    "format": "date-time"
                },
                "end": {
                    "type": "string",
                    "format": "date-time"
                },
                "variables": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "measurementType": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            },
            "required": ["formula", "start", "end", "variables", "measurementType"]
        }
    },
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": ["co2", "db", "pod", "pod-anz", "scada", "site", "thl"]
        },
        "description": {
            "type": "string"
        },
        "shortDescription": {
            "type": "string"
        },
        "sampleDeltaInMS": {
            "type": "integer",
            "minimum": 10000
        },
        "unitOfMeasurement": {
            "type": "string"
        },
        "virtual": {
            "type": "boolean"
        },
        "formulas": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/formula"
            }
        },
        "tags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "primaryTags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "siteId": {
            "type": "string"
        },
        "userId": {
            "type": "string"
        },
        "parentSensorId": {
            "type": "string"
        }
    },
    "additionalProperties": false,
    "required": [
        "id",
        "name",
        "type"
    ]
};
<commit_msg>add custom-monitoring type to sensors schema<commit_after>export default {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "formula": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string"
                },
                "start": {
                    "type": "string",
                    "format": "date-time"
                },
                "end": {
                    "type": "string",
                    "format": "date-time"
                },
                "variables": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "measurementType": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            },
            "required": ["formula", "start", "end", "variables", "measurementType"]
        }
    },
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": ["co2", "custom-monitoring", "db", "pod", "pod-anz", "scada", "site", "thl"]
        },
        "description": {
            "type": "string"
        },
        "shortDescription": {
            "type": "string"
        },
        "sampleDeltaInMS": {
            "type": "integer",
            "minimum": 10000
        },
        "unitOfMeasurement": {
            "type": "string"
        },
        "virtual": {
            "type": "boolean"
        },
        "formulas": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/formula"
            }
        },
        "tags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "primaryTags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "siteId": {
            "type": "string"
        },
        "userId": {
            "type": "string"
        },
        "parentSensorId": {
            "type": "string"
        }
    },
    "additionalProperties": false,
    "required": [
        "id",
        "name",
        "type"
    ]
};
<|endoftext|>


<commit_before>/**
 * COMMON WEBPACK CONFIGURATION
 */

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin({
  filename: '[name]-[contenthash].css',
  allChunks: true,
});
const AssetsPlugin = require('assets-webpack-plugin');
const assetsPluginInstance = new AssetsPlugin({
  path: path.join(process.cwd(), 'server', 'middlewares'),
  filename: 'generated.assets.json',
});
const isBuildingDll = Boolean(process.env.BUILDING_DLL);
module.exports = (options) => ({
  entry: options.entry,
  output: Object.assign({ // Compile into js/build.js
    path: path.resolve(process.cwd(), 'build'),
    publicPath: '/',
  }, options.output), // Merge with env dependent settings
  module: {
    loaders: [{
      test: /\.js$/, // Transform all .js files required somewhere with Babel
      loader: 'babel-loader',
      exclude: /node_modules/,
      query: options.babelQuery,
    }, {
      // Do not transform vendor's CSS with CSS-modules
      // The point is that they remain in global scope.
      // Since we require these CSS files in our JS or CSS files,
      // they will be a part of our compilation either way.
      // So, no need for ExtractTextPlugin here.
      test: /\.css$/,
      include: /node_modules/,
      use: extractCSS.extract({
        fallback: 'style-loader',
        use: 'css-loader',
      }),
      // loaders: ['style-loader', 'css-loader'],
    }, {
      test: /\.scss$/,
      // exclude: /node_modules/, ExtractTextPlugin.extract({
      use: extractCSS.extract({
        fallback: 'style-loader',
        use: ['css-loader', 'sass-loader'],
      }),
    }, {
      test: /\.(eot|svg|ttf|woff|woff2)$/,
      loader: 'file-loader',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        {
          loader: 'file-loader',
        },
        {
          loader: 'img-loader',
          options: {},
        },
      ],
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.json$/,
      loader: 'json-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader',
      query: {
        limit: 10000,
      },
    }],
  },
  plugins: options.plugins.concat([
    extractCSS,
    new webpack.ProvidePlugin({
      // make fetch available
      fetch: 'exports-loader?self.fetch!whatwg-fetch',
    }),

    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
    // inside your code for any environment checks; UglifyJS will automatically
    // drop any unreachable code.
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV),
        browser: true,
      },
    }),
    new webpack.NamedModulesPlugin(),
  ]).concat(
    isBuildingDll ? [] : [assetsPluginInstance]
  ),
  resolve: {
    modules: ['app', 'node_modules'],
    extensions: [
      '.js',
      '.jsx',
      '.react.js',
    ],
    mainFields: [
      'browser',
      'main',
      'jsnext:main',
    ],
  },
  devtool: options.devtool,
  target: 'web', // Make web variables accessible to webpack, e.g. window
  performance: options.performance || {},
});
<commit_msg>public path<commit_after>/**
 * COMMON WEBPACK CONFIGURATION
 */

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin({
  filename: '[name]-[contenthash].css',
  allChunks: true,
});
const AssetsPlugin = require('assets-webpack-plugin');
const assetsPluginInstance = new AssetsPlugin({
  path: path.join(process.cwd(), 'server', 'middlewares'),
  filename: 'generated.assets.json',
});
const isBuildingDll = Boolean(process.env.BUILDING_DLL);
module.exports = (options) => ({
  entry: options.entry,
  output: Object.assign({ // Compile into js/build.js
    path: path.resolve(process.cwd(), 'build'),
    publicPath: '/assets/',
  }, options.output), // Merge with env dependent settings
  module: {
    loaders: [{
      test: /\.js$/, // Transform all .js files required somewhere with Babel
      loader: 'babel-loader',
      exclude: /node_modules/,
      query: options.babelQuery,
    }, {
      // Do not transform vendor's CSS with CSS-modules
      // The point is that they remain in global scope.
      // Since we require these CSS files in our JS or CSS files,
      // they will be a part of our compilation either way.
      // So, no need for ExtractTextPlugin here.
      test: /\.css$/,
      include: /node_modules/,
      use: extractCSS.extract({
        fallback: 'style-loader',
        use: 'css-loader',
      }),
      // loaders: ['style-loader', 'css-loader'],
    }, {
      test: /\.scss$/,
      // exclude: /node_modules/, ExtractTextPlugin.extract({
      use: extractCSS.extract({
        fallback: 'style-loader',
        use: ['css-loader', 'sass-loader'],
      }),
    }, {
      test: /\.(eot|svg|ttf|woff|woff2)$/,
      loader: 'file-loader',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        {
          loader: 'file-loader',
        },
        {
          loader: 'img-loader',
          options: {},
        },
      ],
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.json$/,
      loader: 'json-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader',
      query: {
        limit: 10000,
      },
    }],
  },
  plugins: options.plugins.concat([
    extractCSS,
    new webpack.ProvidePlugin({
      // make fetch available
      fetch: 'exports-loader?self.fetch!whatwg-fetch',
    }),

    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
    // inside your code for any environment checks; UglifyJS will automatically
    // drop any unreachable code.
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV),
        browser: true,
      },
    }),
    new webpack.NamedModulesPlugin(),
  ]).concat(
    isBuildingDll ? [] : [assetsPluginInstance]
  ),
  resolve: {
    modules: ['app', 'node_modules'],
    extensions: [
      '.js',
      '.jsx',
      '.react.js',
    ],
    mainFields: [
      'browser',
      'main',
      'jsnext:main',
    ],
  },
  devtool: options.devtool,
  target: 'web', // Make web variables accessible to webpack, e.g. window
  performance: options.performance || {},
});
<|endoftext|>


<commit_before>function AppRun(AppConstants, $rootScope, $timeout, Wallet, Alert, $transitions) {
    'ngInject';

     // Change page title based on state
    $transitions.onSuccess({ to: true }, (transition) => {
        $rootScope.setPageTitle(transition.router.globals.current.title);
        // Enable tooltips globally
        $timeout( function() {
            $('[data-toggle="tooltip"]').tooltip()
        });
    });

    // Check if a wallet is loaded before accessing private states
    $transitions.onStart({
        to: (state) => {
            return (state.name !== 'app.home') && (state.name !== 'app.login') && (state.name !== 'app.signup') && (state.name !== 'app.faq') && (state.name !== 'app.trezor');
        }
    }, (transition) => {
        if (!Wallet.current) {
            Alert.noWalletLoaded();
            return transition.router.stateService.target('app.home');
        }
    });

    // Helper method for setting the page's title
    $rootScope.setPageTitle = (title) => {
        $rootScope.pageTitle = '';
        if (title) {
            $rootScope.pageTitle += title;
            $rootScope.pageTitle += ' \u2014 ';
        }
        $rootScope.pageTitle += AppConstants.appName;
    };
}

export default AppRun;
<commit_msg>Improve wallet check in state transitions<commit_after>function AppRun(AppConstants, $rootScope, $timeout, Wallet, Alert, $transitions) {
    'ngInject';

    const publicStates = [
        "app.home",
        "app.login",
        "app.signup",
        "app.faq",
        "app.trezor",
        "app.offlineTransactionHome",
        "app.offlineTransactionCreate",
        "app.offlineTransactionSend"
    ];

     // Change page title based on state
    $transitions.onSuccess({ to: true }, (transition) => {
        $rootScope.setPageTitle(transition.router.globals.current.title);
        // Enable tooltips globally
        $timeout( function() {
            $('[data-toggle="tooltip"]').tooltip()
        });
    });

    // Check if a wallet is loaded before accessing private states
    $transitions.onStart({
        to: (state) => {
            for (let i = 0; i < publicStates.length; i++) {
                if (publicStates[i] === state.name) return false;
            }
            return true;
        }
    }, (transition) => {
        if (!Wallet.current) {
            Alert.noWalletLoaded();
            return transition.router.stateService.target('app.home');
        }
    });

    // Helper method for setting the page's title
    $rootScope.setPageTitle = (title) => {
        $rootScope.pageTitle = '';
        if (title) {
            $rootScope.pageTitle += title;
            $rootScope.pageTitle += ' \u2014 ';
        }
        $rootScope.pageTitle += AppConstants.appName;
    };
}

export default AppRun;
<|endoftext|>


<commit_before>//new intro<commit_msg>solution<commit_after>var Hapi = require('hapi');
var server = Hapi.createServer('localhost', 8081);
server.route({
	method: 'GET',
	path: '/',
	handler: function (request, reply) {
		reply('Hello Hapi');
	}
});
server.start();<|endoftext|>


<commit_before>/**
 * Mongoose Schema for MongoDB
 */
var mongoosastic = require('mongoosastic'),
    mongoose = require('mongoose'),
    config = require('./config');

// File schema
var File = new mongoose.Schema({
  name: { type: String, required: true },
  extension: String,
  type: String,
  size: Number,
  friendlySize: String,
  path: String,
  lastModified: Date,
  isFolder: Boolean,

  keywords: Array,
  category: String,
  concepts: Array,
  entities: Array,
  summary: String,
  user: String,

  genre: Array,
  title: String,
  artist: Array,
  albumArtist: Array,
  year: Date,
  album: String,
  albumCover: Buffer
});

File.plugin(mongoosastic);

exports.File = File;

// User schema
exports.User = mongoose.model('User', new mongoose.Schema({
  googleId: { type: String, index: { unique: true } },
  accessToken: String,
  displayName: String,
  link: String,
  picture: String,
  gender: String,
  email: String,
  locale: String,
  verified: Boolean
}));<commit_msg>schema changes<commit_after>/**
 * Mongoose Schema for MongoDB
 */
var mongoosastic = require('mongoosastic'),
    mongoose = require('mongoose'),
    config = require('./config');

// File schema
//
// 1-to-1 onto function between user and files
var File = new mongoose.Schema({
  // general
  name: { type: String, required: true },
  extension: String,
  type: String,
  size: Number,
  friendlySize: String,
  path: String,
  lastModified: Date,
  isFolder: Boolean,

  // txt
  keywords: Array,
  category: String,
  concepts: Array,
  entities: Array,
  preview: String,
  user: String,

  // mp3
  genre: Array,
  title: String,
  artist: Array,
  albumArtist: Array,
  year: Date,
  album: String,
  albumCover: Buffer
});

File.plugin(mongoosastic);

exports.File = File;

// User schema
exports.User = mongoose.model('User', new mongoose.Schema({
  googleId: { type: String, index: { unique: true } },
  accessToken: String,
  displayName: String,
  link: String,
  picture: String,
  gender: String,
  email: String,
  locale: String,
  verified: Boolean
}));<|endoftext|>


<commit_before>
"use strict";

// deps

	const	path = require('path'),
			fs = require('simplefs'),

			Phones = require(path.join(__dirname, 'database', 'phones.js'));

// private

	// sql

		function _runSQLFile(Container, SQLFile) {

			return new Promise(function(resolve, reject) {

				try {

					fs.readFileProm(SQLFile, 'utf8').then(function(sql) {

						let queries = [];

						sql.split(';').forEach(function(query) {

							query = query.trim()
										.replace(/--(.*)\s/g, "")
										.replace(/\s/g, " ")
										.replace(/  /g, " ");

							if ('' != query) {
								queries.push(query + ';');
							}

						});

						function executeQueries(i) {

							return new Promise(function(resolve, reject) {

								if (i >= queries.length) {
									resolve();
								}
								else {

									Container.get('db').run(queries[i], [], function(err) {

										if (err) {
											reject((err.message) ? err.message : err);
										}
										else {
											executeQueries(i + 1).then(resolve).catch(reject);
										}

									});

								}

							});

						}

						executeQueries(0).then(resolve).catch(reject);

					}).catch(reject);

				}
				catch(e) {
					reject((e.message) ? e.message : e);
				}

			});

		}

	function _freeSocket(socket) {

		socket.removeAllListeners('plugins.freesms.phone.add');
		socket.removeAllListeners('plugins.freesms.phone.edit');
		socket.removeAllListeners('plugins.freesms.phone.delete');

		socket.removeAllListeners('plugins.freesms.send');

	}

// module

module.exports = class MIAPluginVideosManager extends require('simplepluginsmanager').SimplePlugin {

	constructor () {

		super();

		this.phones = null;
		this.loaded = false;

	}

	load (Container) {

		let that = this;

		return new Promise(function(resolve, reject) {

			try {

				Container.get('users').lastInserted().then(function(user) {

					that.phones = new Categories(Container.get('db'));
					that.loaded = true;

					Container.get('websockets').onDisconnect(_freeSocket).onLog(function(socket) {

						if (that.loaded) {

							that.phones.searchByUser(user).then(function(phones) {
								socket.emit('plugins.freesms.phones', phones);
							}).catch(function(err) {
								Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUser] : ' + ((err.message) ? err.message : err));
								socket.emit('plugins.freesms.error', (err.message) ? err.message : err);
							});

							socket.on('plugins.freesms.phone.add', function (data) {

								if (Container.get('conf').get('debug')) {
									Container.get('logs').log('plugins.freesms.phone.add');
								}

								try {

									that.phones.searchByUserByNameOrCode(user, data.name, data.code).then(function(phone) {

										if (phone) {
											socket.emit('plugins.freesms.error', 'Ce téléphone existe déjà.');
										}
										else {

											that.phones.add({
												user: user,
												name : data.name
											}).then(function(phone) {

												Container.get('websockets').emit('plugins.freesms.phone.added', phone);

											}).catch(function(err) {
												Container.get('logs').err('-- [plugins/FreeSMS/phones/add] : ' + err);
												socket.emit('plugins.freesms.error', err);
											})

										}

									}).catch(function(err) {
										Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUserByNameOrCode] : ' + err);
										socket.emit('plugins.freesms.error', err);
									});

								}
								catch (e) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/add] : ' + ((e.message) ? e.message : e));
									Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
								}

							})
							.on('plugins.freesms.phone.edit', function (data) {

								if (Container.get('conf').get('debug')) {
									Container.get('logs').log('plugins.freesms.phone.edit');
								}

								try {

									that.phones.searchById(data.id).then(function(phone) {

										if (!phone) {
											socket.emit('plugins.freesms.error', 'Impossible de trouver ce téléphone.');
										}
										else {

											that.phones.searchByUserByNameOrCode(user, data.name, data.code).then(function(_category) {

												if (_category) {
													socket.emit('plugins.freesms.error', 'Ce nom ou ce numéro existe déjà.');
												}
												else {

													phone.name = data.name;
													phone.code = data.code;
													phone.freeapi = data.freeapi;

													that.phones.edit(phone).then(function(phone) {
														Container.get('websockets').emit('plugins.freesms.phone.edited', phone);
													}).catch(function(err) {
														Container.get('logs').err('-- [plugins/FreeSMS/phones/edit] : ' + err);
														socket.emit('plugins.freesms.error', err);
													});

												}

											}).catch(function(err) {
												Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUserByNameOrCode] : ' + err);
												socket.emit('plugins.freesms.error', err);
											});

										}

									}).catch(function(err) {
										Container.get('logs').err('-- [plugins/FreeSMS/phones/searchById] : ' + err);
										socket.emit('plugins.freesms.error', err);
									});

								}
								catch (e) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/edit] : ' + ((e.message) ? e.message : e));
									Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
								}

							})
							.on('plugins.freesms.phone.delete', function (data) {

								if (Container.get('conf').get('debug')) {
									Container.get('logs').log('plugins.freesms.phone.delete');
								}

								try {

									that.phones.searchById(data.id).then(function(phone) {

										if (!phone) {
											socket.emit('plugins.freesms.error', 'Impossible de trouver ce téléphone.');
										}
										else {

											that.phones.delete(phone).then(function() {

												that.phones.searchByUser(user).then(function(phones) {
													socket.emit('plugins.freesms.phones', phones);
												}).catch(function(err) {

													Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUser] : ' + ((err.message) ? err.message : err));
													socket.emit('plugins.freesms.error', (err.message) ? err.message : err);

												});

											}).catch(function(err) {
												Container.get('logs').err('-- [plugins/FreeSMS/phones/delete] : ' + err);
												socket.emit('plugins.freesms.error', err);
											});

										}

									}).catch(function(err) {
										Container.get('logs').err('-- [plugins/FreeSMS/phones/searchById] : ' + err);
										socket.emit('plugins.freesms.error', err);
									});

								}
								catch (e) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/delete] : ' + ((e.message) ? e.message : e));
									Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
								}

							});

						}

					});

					resolve();

				}).catch(reject);

			}
			catch(e) {
				reject((e.message) ? e.message : e);
			}

		});

	}

	unload (Container) {

		super.unload();

		let that = this;

		return new Promise(function(resolve, reject) {

			try {

				that.phones = null;
				that.loaded = false;

				Container.get('websockets').getSockets().forEach(_freeSocket);

				resolve();

			}
			catch(e) {
				reject((e.message) ? e.message : e);
			}

		});

	}

	install (Container) {
		return _runSQLFile(Container, path.join(__dirname, 'database', 'create.sql'));
	}

	update (Container) {

		let that = this;

		return fs.unlinkProm(path.join(__dirname, 'backup.json')).then(function() {
			return that.install(Container);
		});

	}

	uninstall (Container) {

		return fs.unlinkProm(path.join(__dirname, 'backup.json')).then(function() {
			return _runSQLFile(Container, path.join(__dirname, 'database', 'delete.sql'));
		});

	}

};
<commit_msg>réécriture du main<commit_after>
"use strict";

// deps

	const	path = require('path'),
			fs = require('simplefs'),

			Phones = require(path.join(__dirname, 'database', 'phones.js'));

// private

	// sql

		function _runSQLFile(Container, SQLFile) {

			return new Promise(function(resolve, reject) {

				try {

					fs.readFileProm(SQLFile, 'utf8').then(function(sql) {

						let queries = [];

						sql.split(';').forEach(function(query) {

							query = query.trim()
										.replace(/--(.*)\s/g, "")
										.replace(/\s/g, " ")
										.replace(/  /g, " ");

							if ('' != query) {
								queries.push(query + ';');
							}

						});

						function executeQueries(i) {

							return new Promise(function(resolve, reject) {

								if (i >= queries.length) {
									resolve();
								}
								else {

									Container.get('db').run(queries[i], [], function(err) {

										if (err) {
											reject((err.message) ? err.message : err);
										}
										else {
											executeQueries(i + 1).then(resolve).catch(reject);
										}

									});

								}

							});

						}

						executeQueries(0).then(resolve).catch(reject);

					}).catch(reject);

				}
				catch(e) {
					reject((e.message) ? e.message : e);
				}

			});

		}

	function _freeSocket(socket) {

		socket.removeAllListeners('plugins.freesms.phone.add');
		socket.removeAllListeners('plugins.freesms.phone.edit');
		socket.removeAllListeners('plugins.freesms.phone.delete');

		socket.removeAllListeners('plugins.freesms.send');

	}

// module

module.exports = class MIAPluginVideosManager extends require('simplepluginsmanager').SimplePlugin {

	constructor () {

		super();

		this.phones = null;
		this.loaded = false;

	}

	load (Container) {

		let that = this, user;
		return Container.get('users').lastInserted().then(function(_user) {

			user = _user;

			return Container.get('actions').bindExecuter('sms.freeapi.send', function(action) {

				if (action.params) {
					console.log(action);
				}

			});

		}).then(function() {

			return new Promise(function(resolve, reject) {

				that.phones = new Phones(Container.get('db'));
				that.loaded = true;

				Container.get('websockets').onDisconnect(_freeSocket).onLog(function(socket) {

					if (that.loaded) {

						that.phones.searchByUser(user).then(function(phones) {
							socket.emit('plugins.freesms.phones', phones);
						}).catch(function(err) {
							Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUser] : ' + ((err.message) ? err.message : err));
							socket.emit('plugins.freesms.error', (err.message) ? err.message : err);
						});

						socket.on('plugins.freesms.phone.add', function (data) {

							if (Container.get('conf').get('debug')) {
								Container.get('logs').log('plugins.freesms.phone.add');
							}

							try {

								that.phones.searchByUserByNameOrCode(user, data.name, data.code).then(function(phone) {

									if (phone) {
										socket.emit('plugins.freesms.error', 'Ce téléphone existe déjà.');
									}
									else {

										that.phones.add({
											user: user,
											name : data.name
										}).then(function(phone) {

											Container.get('websockets').emit('plugins.freesms.phone.added', phone);

										}).catch(function(err) {
											Container.get('logs').err('-- [plugins/FreeSMS/phones/add] : ' + err);
											socket.emit('plugins.freesms.error', err);
										})

									}

								}).catch(function(err) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUserByNameOrCode] : ' + err);
									socket.emit('plugins.freesms.error', err);
								});

							}
							catch (e) {
								Container.get('logs').err('-- [plugins/FreeSMS/phones/add] : ' + ((e.message) ? e.message : e));
								Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
							}

						})
						.on('plugins.freesms.phone.edit', function (data) {

							if (Container.get('conf').get('debug')) {
								Container.get('logs').log('plugins.freesms.phone.edit');
							}

							try {

								that.phones.searchById(data.id).then(function(phone) {

									if (!phone) {
										socket.emit('plugins.freesms.error', 'Impossible de trouver ce téléphone.');
									}
									else {

										that.phones.searchByUserByNameOrCode(user, data.name, data.code).then(function(_category) {

											if (_category) {
												socket.emit('plugins.freesms.error', 'Ce nom ou ce numéro existe déjà.');
											}
											else {

												phone.name = data.name;
												phone.code = data.code;
												phone.freeapi = data.freeapi;

												that.phones.edit(phone).then(function(phone) {
													Container.get('websockets').emit('plugins.freesms.phone.edited', phone);
												}).catch(function(err) {
													Container.get('logs').err('-- [plugins/FreeSMS/phones/edit] : ' + err);
													socket.emit('plugins.freesms.error', err);
												});

											}

										}).catch(function(err) {
											Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUserByNameOrCode] : ' + err);
											socket.emit('plugins.freesms.error', err);
										});

									}

								}).catch(function(err) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/searchById] : ' + err);
									socket.emit('plugins.freesms.error', err);
								});

							}
							catch (e) {
								Container.get('logs').err('-- [plugins/FreeSMS/phones/edit] : ' + ((e.message) ? e.message : e));
								Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
							}

						})
						.on('plugins.freesms.phone.delete', function (data) {

							if (Container.get('conf').get('debug')) {
								Container.get('logs').log('plugins.freesms.phone.delete');
							}

							try {

								that.phones.searchById(data.id).then(function(phone) {

									if (!phone) {
										socket.emit('plugins.freesms.error', 'Impossible de trouver ce téléphone.');
									}
									else {

										that.phones.delete(phone).then(function() {

											that.phones.searchByUser(user).then(function(phones) {
												socket.emit('plugins.freesms.phones', phones);
											}).catch(function(err) {

												Container.get('logs').err('-- [plugins/FreeSMS/phones/searchByUser] : ' + ((err.message) ? err.message : err));
												socket.emit('plugins.freesms.error', (err.message) ? err.message : err);

											});

										}).catch(function(err) {
											Container.get('logs').err('-- [plugins/FreeSMS/phones/delete] : ' + err);
											socket.emit('plugins.freesms.error', err);
										});

									}

								}).catch(function(err) {
									Container.get('logs').err('-- [plugins/FreeSMS/phones/searchById] : ' + err);
									socket.emit('plugins.freesms.error', err);
								});

							}
							catch (e) {
								Container.get('logs').err('-- [plugins/FreeSMS/phones/delete] : ' + ((e.message) ? e.message : e));
								Container.get('websockets').emit('plugins.freesms.error', ((e.message) ? e.message : e));
							}

						});

					}

				});

				resolve();

			});

		});

	}

	unload (Container) {

		super.unload();

		let that = this;
		return new Promise(function(resolve, reject) {

			try {

				that.phones = null;
				that.loaded = false;

				Container.get('websockets').getSockets().forEach(_freeSocket);

				resolve();

			}
			catch(e) {
				reject((e.message) ? e.message : e);
			}

		});

	}

	install (Container) {

		return _runSQLFile(Container, path.join(__dirname, 'database', 'create.sql')).then(function() {

			return Container.get('actionstypes').add({
				name: "Envoyer un SMS avec l'API de Free",
				command: "sms.freeapi.send"
			});

		});
	}

	update (Container) {

		let that = this;
		return fs.unlinkProm(path.join(__dirname, 'backup.json')).then(function() {
			return that.install(Container);
		});

	}

	uninstall (Container) {

		return fs.unlinkProm(path.join(__dirname, 'backup.json')).then(function() {
			return _runSQLFile(Container, path.join(__dirname, 'database', 'delete.sql'));
		}).then(function() {
			return Container.get('actionstypes').getOneByCommand('sms.freeapi.send');
		}).then(function(actionstype) {

			if (actionstype) {
				return Container.delete(actionstype);
			}
			else {
				return new Promise(function(resolve, reject) { resolve(); });
			}

		});

	}

};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * 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 expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var SettingsPageView = Backbone.View.extend({

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.el = options.el;
        this.render();
        this.getUserPayload();
        this.addHandlers();
    },

    addHandlers: function() {
        var self = this;

        $('.settings-form').on('submit', function(e) {
            e.preventDefault();
            self.trimInputField('[name="username"]');
            self.trimInputField('[name="first_name"]');
            self.trimInputField('[name="last_name"]');

            // ('[name="email"]') seems to have native trim() support
            // based on the type="email"

            self.submitRequest($(this).serialize());
        });
    },

    trimInputField: function(selector) {
        var trimmedContent = $(selector).val().trim();
        $(selector).val(trimmedContent);
    },

    getUserPayload: function() {
        $.get('/user')
            .done(function(result) {
                console.log('getUserPayload succeeded');
                console.log(result);
                $('[name="username"]').val(result.username);
                $('[name="first_name"]').val(result.first_name);
                $('[name="last_name"]').val(result.last_name);
                $('[name="email"]').val(result.email);
            })
            .fail(function() {
                console.log('getUserPayload failed');
            });
    },

    submitRequest: function(input) {
        console.log('submitting request with ' + input);
        var self = this;

        // Upon clicking the submit button, the serialized user input is sent
        // via $.post to check the credentials. If successful, invoke "done"
        // if not, invoke "fail"

        $.ajax({
            type: "PUT",
            url: '/user',
            data: input,
        }).done(function(success) {
            goldstone.raiseInfo('Update successful', true);
            console.log('success: ', success);
        })
            .fail(function(fail) {
                try {
                    goldstone.raiseInfo(fail.responseJSON.non_field_errors[0], true);
                } catch (e) {
                    goldstone.raiseInfo(e, true);
                }
            });
    },

    storeAuthToken: function(token) {
        localStorage.setItem('userToken', token);
    },

    redirectPostSuccessfulAuth: function() {

        // if there was a previously visited page that
        // had redirected to the login page due to lack
        // of credentials, redirect back to that page
        if (location.hash && location.hash.length > 0) {
            locationhref = '/' + location.hash.slice(1);
        } else {

            // or just redirect to /discover
            locationhref = '/';
        }

        location.href = locationhref;

    },

    render: function() {
        this.$el.html(this.template());
        return this;
    },

    template: _.template('' +
        '
' +
        '
' +
        '
' +
        '<form class="settings-form">' +
        '
Update Personal Settings
' +
        'Username' +
        '<input name="username" type="text" class="form-control" placeholder="username">' +
        'First name' +
        '<input name="first_name" type="text" class="form-control" placeholder="First name" autofocus>' +
        'Last name' +
        '<input name="last_name" type="text" class="form-control" placeholder="Last name">
' +
        'Email' +
        '<input name="email" type="email" class="form-control" placeholder="Email">
' +
        '<button name="submit" class="btn btn-lg btn-primary btn-block" type="submit">Update</button>' +
        '</form>' +
        '
' +
        '
' +
        '
'
    )

});
<commit_msg>Sets username form field in settinsPageView.js to required, as username is required by api endpoint.<commit_after>/**
 * Copyright 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * 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 expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var SettingsPageView = Backbone.View.extend({

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.el = options.el;
        this.render();
        this.getUserPayload();
        this.addHandlers();
    },

    addHandlers: function() {
        var self = this;

        $('.settings-form').on('submit', function(e) {
            e.preventDefault();
            self.trimInputField('[name="username"]');
            self.trimInputField('[name="first_name"]');
            self.trimInputField('[name="last_name"]');

            // ('[name="email"]') seems to have native trim() support
            // based on the type="email"

            self.submitRequest($(this).serialize());
        });
    },

    trimInputField: function(selector) {
        var trimmedContent = $(selector).val().trim();
        $(selector).val(trimmedContent);
    },

    getUserPayload: function() {
        $.get('/user')
            .done(function(result) {
                console.log('getUserPayload succeeded');
                console.log(result);
                $('[name="username"]').val(result.username);
                $('[name="first_name"]').val(result.first_name);
                $('[name="last_name"]').val(result.last_name);
                $('[name="email"]').val(result.email);
            })
            .fail(function() {
                console.log('getUserPayload failed');
            });
    },

    submitRequest: function(input) {
        console.log('submitting request with ' + input);
        var self = this;

        // Upon clicking the submit button, the serialized user input is sent
        // via $.post to check the credentials. If successful, invoke "done"
        // if not, invoke "fail"

        $.ajax({
            type: "PUT",
            url: '/user',
            data: input,
        }).done(function(success) {
            goldstone.raiseInfo('Update successful', true);
            console.log('success: ', success);
        })
            .fail(function(fail) {
                try {
                    goldstone.raiseInfo(fail.responseJSON.non_field_errors[0], true);
                } catch (e) {
                    goldstone.raiseInfo(fail.responseText, true);
                }
            });
    },

    storeAuthToken: function(token) {
        localStorage.setItem('userToken', token);
    },

    redirectPostSuccessfulAuth: function() {

        // if there was a previously visited page that
        // had redirected to the login page due to lack
        // of credentials, redirect back to that page
        if (location.hash && location.hash.length > 0) {
            locationhref = '/' + location.hash.slice(1);
        } else {

            // or just redirect to /discover
            locationhref = '/';
        }

        location.href = locationhref;

    },

    render: function() {
        this.$el.html(this.template());
        return this;
    },

    template: _.template('' +
        '
' +
        '
' +
        '
' +
        '<form class="settings-form">' +
        '
Update Personal Settings
' +
        'Username' +
        '<input name="username" type="text" class="form-control" placeholder="username" required>' +
        'First name' +
        '<input name="first_name" type="text" class="form-control" placeholder="First name" autofocus>' +
        'Last name' +
        '<input name="last_name" type="text" class="form-control" placeholder="Last name">
' +
        'Email' +
        '<input name="email" type="email" class="form-control" placeholder="Email">
' +
        '<button name="submit" class="btn btn-lg btn-primary btn-block" type="submit">Update</button>' +
        '</form>' +
        '
' +
        '
' +
        '
'
    )

});
<|endoftext|>


<commit_before>// (c) 2011 Richard Carter
// This code is licensed under the MIT license; see LICENSE.txt for details.

require('./config.js');

var util = require('util');
var net = require('net');
var fs = require('fs');
var vm = require('vm');
var repl = require('repl');

var irc = global.nodebot = (function(){
	var socket = new net.Socket();
	socket.setNoDelay(true);
	socket.setEncoding('ascii');

	var buffer = {
		b: new Buffer(4096),
		size: 0
	};

	var listeners = [];
	var ignorelist;
	try {
		ignorelist = fs.readFileSync('data/ignore.txt', 'ascii');
		if(!ignorelist) ignorelist = "";
		ignorelist = ignorelist.split('\n');
	} catch(err) {ignorelist = [];}

	function send(data) {
		if(data.length > 510) {
			console.log("ERROR tried to send data > 510 chars in length: "+data);
			return;
		}
		socket.write(data+'\r\n', 'ascii', function() {
			console.log("-> "+data);
		});
	}

	socket.on('data', function(data) {
		data = data.replace('\r','');

		var newlineIdx;
		while((newlineIdx = data.indexOf('\n')) > -1) {
			if(buffer.size > 0) {
				data = buffer.b.toString('ascii', 0, buffer.size) + data;
				newlineIdx += buffer.size;
				buffer.size = 0;
			}

			handle(data.substr(0, newlineIdx));
			data = data.slice(newlineIdx+1);
		}

		if(data.length > 0) {
			buffer.b.write(data, buffer.size, 'ascii');
			buffer.size += data.length;
		}
	});

	function handle(data) {
		console.log("<- "+data);

		var user = (/^:([^!]+)!/i).exec(data);
		if(user) {
			user = user[1];
			for(var i=0; i<ignorelist.length; i++) {
				if(ignorelist[i].toUpperCase() == user.toUpperCase()) {
					// user is ignored
					return;
				}
			}
		}

		var replyTo = null;
		if(data.indexOf('PRIVMSG') > -1) {
			var dest = (/^:([^!]+)!.*PRIVMSG ([^ ]+) /i).exec(data);
			if(dest) {
				if(dest[2].toUpperCase() == nodebot_prefs.nickname.toUpperCase()) {
					replyTo = dest[1];
				} else {
					replyTo = dest[2];
				}
			}
		}

		for(var i=0; i<listeners.length; i++) {
			match = listeners[i][0].exec(data);
			if(match) {
				try {
					listeners[i][1](match, data, replyTo);
				} catch(err) {
					console.log("caught error in script "+listeners[i][3]+": "+err);
				}
				if(listeners[i][2]) {
					listeners.splice(i,1);
					i--;
				}
			}
		}
	}

	return {
		/*raw: function(stuff) {
			send(stuff);
		},*/
		connect: function(host, port, nickname, username, realname) {
			var port = port || 6667;
			socket.connect(port, host, function () {
				send('NICK '+nickname);
				send('USER '+username+' localhost * '+realname);
			});

		},
		loadScripts: function() {
			socket.pause();

			listeners = [];

			var scripts = fs.readdirSync('scripts');
			if(scripts) {
				for(var i=0; i<scripts.length; i++) {
					if(scripts[i].substr(-3) == '.js') {
						console.log("Loading script "+scripts[i]+"...");
						var script = fs.readFileSync('scripts/'+scripts[i]);
						if(script) {
							var scriptName = scripts[i];
							var sandbox = {
								irc: irc,
								nodebot_prefs: nodebot_prefs,
								console: console,
								setTimeout: setTimeout,
								vm: vm, fs: fs, require: require, util: util,
								listen: function(dataRegex, callback, once) {
									once = once || false;
									listeners.push([dataRegex, callback, once, scriptName]);
								}
							};
							try {
								vm.runInNewContext(script, sandbox, scripts[i]);
							} catch(err) {
								console.log("Error in script "+scripts[i]+": "+err);
								for(var i=0; i<listeners.length; i++) {
									if(listeners[i][3] == scriptName) {
										listeners.splice(i,1);
										i--;
									}
								}
							}

						}
					}
				}
			}
			try {
				socket.resume();
			} catch(err) {}
			console.log("Scripts loaded.");
		},

		/* IRC COMMANDS: */
		pong: function(server) {
			send("PONG :"+server);
		},
		join: function(channel, key) {
			var cmd = "JOIN :"+channel;
			if(key) cmd += " "+key;
			send(cmd);
		},
		part: function(channel) {
			send("PART :"+channel);
		},
		privmsg: function(user, message) {
			// TODO split message into chunks so it doesn't exceed max length
			send("PRIVMSG "+user+" :"+message);
		},
		action: function(channel, action) {
			irc.privmsg(channel, '\x01ACTION '+action+'\x01');
		},

		/* ADDITIONAL GLOBAL IRC FUNCTIONALITY */
		ignore: function(user) {
			ignorelist.push(user);
			fs.writeFileSync('data/ignore.txt', ignorelist.join('\n'), 'ascii');
		},
		unignore: function(user) {
			for(var i=0; i<ignorelist.length; i++) {
				if(ignorelist[i].toUpperCase() == user.toUpperCase()) {
					ignorelist.splice(i, 1);
					i--;
				}
			}
			fs.writeFileSync('data/ignore.txt', ignorelist.join('\n'), 'ascii');
		},
		chatignorelist: function(channel) {
			irc.chatmsg(channel, "Ignore list: "+ignorelist.join(","));
		}
	}
})();

irc.loadScripts();
irc.connect(nodebot_prefs.server, nodebot_prefs.port, nodebot_prefs.nickname, nodebot_prefs.nickname, nodebot_prefs.realname);

repl.start();<commit_msg>Added sanitize function for IRC commands<commit_after>// (c) 2011 Richard Carter
// This code is licensed under the MIT license; see LICENSE.txt for details.

require('./config.js');

var util = require('util');
var net = require('net');
var fs = require('fs');
var vm = require('vm');
var repl = require('repl');

var irc = global.nodebot = (function(){
	var socket = new net.Socket();
	socket.setNoDelay(true);
	socket.setEncoding('ascii');

	var buffer = {
		b: new Buffer(4096),
		size: 0
	};

	var listeners = [];
	var ignorelist;
	try {
		ignorelist = fs.readFileSync('data/ignore.txt', 'ascii');
		if(!ignorelist) ignorelist = "";
		ignorelist = ignorelist.split('\n');
	} catch(err) {ignorelist = [];}

	function send(data) {
		if(data.length > 510) {
			console.log("ERROR tried to send data > 510 chars in length: "+data);
			return;
		}
		socket.write(data+'\r\n', 'ascii', function() {
			console.log("-> "+data);
		});
	}

	socket.on('data', function(data) {
		data = data.replace('\r','');

		var newlineIdx;
		while((newlineIdx = data.indexOf('\n')) > -1) {
			if(buffer.size > 0) {
				data = buffer.b.toString('ascii', 0, buffer.size) + data;
				newlineIdx += buffer.size;
				buffer.size = 0;
			}

			handle(data.substr(0, newlineIdx));
			data = data.slice(newlineIdx+1);
		}

		if(data.length > 0) {
			buffer.b.write(data, buffer.size, 'ascii');
			buffer.size += data.length;
		}
	});

	function handle(data) {
		console.log("<- "+data);

		var user = (/^:([^!]+)!/i).exec(data);
		if(user) {
			user = user[1];
			for(var i=0; i<ignorelist.length; i++) {
				if(ignorelist[i].toUpperCase() == user.toUpperCase()) {
					// user is ignored
					return;
				}
			}
		}

		var replyTo = null;
		if(data.indexOf('PRIVMSG') > -1) {
			var dest = (/^:([^!]+)!.*PRIVMSG ([^ ]+) /i).exec(data);
			if(dest) {
				if(dest[2].toUpperCase() == nodebot_prefs.nickname.toUpperCase()) {
					replyTo = dest[1];
				} else {
					replyTo = dest[2];
				}
			}
		}

		for(var i=0; i<listeners.length; i++) {
			match = listeners[i][0].exec(data);
			if(match) {
				try {
					listeners[i][1](match, data, replyTo);
				} catch(err) {
					console.log("caught error in script "+listeners[i][3]+": "+err);
				}
				if(listeners[i][2]) {
					listeners.splice(i,1);
					i--;
				}
			}
		}
	}

	function sanitize(data) {
		return data.replace(/\n/g, "\\n");
	}

	return {
		/*raw: function(stuff) {
			send(stuff);
		},*/
		connect: function(host, port, nickname, username, realname) {
			var port = port || 6667;
			socket.connect(port, host, function () {
				send('NICK '+sanitize(nickname));
				send('USER '+sanitize(username)+' localhost * '+sanitize(realname));
			});

		},
		loadScripts: function() {
			socket.pause();

			listeners = [];

			var scripts = fs.readdirSync('scripts');
			if(scripts) {
				for(var i=0; i<scripts.length; i++) {
					if(scripts[i].substr(-3) == '.js') {
						console.log("Loading script "+scripts[i]+"...");
						var script = fs.readFileSync('scripts/'+scripts[i]);
						if(script) {
							var scriptName = scripts[i];
							var sandbox = {
								irc: irc,
								nodebot_prefs: nodebot_prefs,
								console: console,
								setTimeout: setTimeout,
								vm: vm, fs: fs, require: require, util: util,
								listen: function(dataRegex, callback, once) {
									once = once || false;
									listeners.push([dataRegex, callback, once, scriptName]);
								}
							};
							try {
								vm.runInNewContext(script, sandbox, scripts[i]);
							} catch(err) {
								console.log("Error in script "+scripts[i]+": "+err);
								for(var i=0; i<listeners.length; i++) {
									if(listeners[i][3] == scriptName) {
										listeners.splice(i,1);
										i--;
									}
								}
							}

						}
					}
				}
			}
			try {
				socket.resume();
			} catch(err) {}
			console.log("Scripts loaded.");
		},

		/* IRC COMMANDS: */
		pong: function(server) {
			send("PONG :"+server);
		},
		join: function(channel, key) {
			var cmd = "JOIN :"+sanitize(channel);
			if(key) {
				cmd += " "+sanitize(key);
			}
			send(cmd);
		},
		part: function(channel) {
			send("PART :"+sanitize(channel));
		},
		privmsg: function(user, message) {
			// TODO split message into chunks so it doesn't exceed max length
			send("PRIVMSG "+sanitize(user)+" :"+sanitize(message));
		},
		action: function(channel, action) {
			irc.privmsg(channel, '\x01ACTION '+action+'\x01');
		},

		/* ADDITIONAL GLOBAL IRC FUNCTIONALITY */
		ignore: function(user) {
			ignorelist.push(user);
			fs.writeFileSync('data/ignore.txt', ignorelist.join('\n'), 'ascii');
		},
		unignore: function(user) {
			for(var i=0; i<ignorelist.length; i++) {
				if(ignorelist[i].toUpperCase() == user.toUpperCase()) {
					ignorelist.splice(i, 1);
					i--;
				}
			}
			fs.writeFileSync('data/ignore.txt', ignorelist.join('\n'), 'ascii');
		},
		chatignorelist: function(channel) {
			irc.chatmsg(channel, "Ignore list: "+sanitize(ignorelist.join(",")));
		}
	}
})();

irc.loadScripts();
irc.connect(nodebot_prefs.server, nodebot_prefs.port, nodebot_prefs.nickname, nodebot_prefs.nickname, nodebot_prefs.realname);

repl.start();<|endoftext|>


<commit_before>var searcher = function() {
	var data = {
		"name": "start",
		"children": [{
			"name": "first",
			"children": [{
				"name": "build-A"
			}, {
				"name": "build-B"
			}]
		}]
	};

	var when = function(deferreds) {
		if (deferreds.length == 0) {
			return $.Deferred().resolve([]);
		} else if (deferreds.length == 1) {
			return deferreds[0].pipe(function(deferred) {
				return [deferred]
			})
		} else {
			return $.when.apply($, deferreds)
		}
	}

	var width = 960,
		height = 2000;

	var cluster = d3.layout.tree().nodeSize([200, 200]);
	var diagonal = d3.svg.diagonal()
		.projection(function(d) {
			return [d.x, d.y];
		});
	var svg = d3.select("body").append("svg")
		.attr("width", width)
		.attr("height", height)
		.append("g")
		.attr("transform", "translate(500,300)");

	var searchedRev = 1234;

	var renderData = function() {
		var nodes = cluster.nodes(data),
			links = cluster.links(nodes);

		var link = svg.selectAll(".link")
			.data(links, function(d) {
				return d.source.name + d.target.name
			})

		link.enter().append("path")
			.attr("class", "link");

		link.transition().attr("d", diagonal)

		link.exit().remove();

		var node = svg.selectAll(".node")
			.data(nodes, function(d) {
				return d.name
			});

		var parentNode = node.enter().append("g")
			.attr("class", "node");
		parentNode.append("circle")
			.attr("r", 4.5);
		parentNode.append("a")
			.attr("xlink:href", function(d) {
				return d.url
			})
			.append("text")
			.attr("dy", 3);

		node.selectAll("a text").transition()
			.attr("dx", function(d) {
				return d.children ? -8 : 8;
			})
			.attr("dy", 3)
			.style("text-anchor", function(d) {
				return d.children ? "end" : "start";
			})
			.text(function(d) {
				return d.name;
			});

		node.transition().attr("transform", function(d) {
			return "translate(" + d.x + "," + d.y + ")";
		})

		node.exit().remove();
	}

	var buildData = function(jobName) {
		var resultDef = $.Deferred();
		var findRevision = function(envVars) {
			for (var i = 0; i < envVars.length; i++) {
				var env = envVars[i];
				if (env.name == "REV") {
					return env.value
				}
			}
		}
		var jobRequest = $.getJSON(
			"http://localhost:8080/job/" + jobName +
			"/api/json?tree=lastBuild[number,url,actions[triggeredProjects[name]]]")
		var envVarsRequest = jobRequest.pipe(
			function(job) {
				return $.getJSON(job.lastBuild.url + "injectedEnvVars/export");
			})

		var getTriggeredProjects = function(build) {
			var actions = build.actions;
			var triggeredProjects = [];
			var i;
			var action;
			for (i = 0; i < actions.length; i++) {
				action = actions[i];
				if (action.triggeredProjects) {
					triggeredProjects = triggeredProjects.concat(action.triggeredProjects);
				}
			}
			return triggeredProjects;
		}

		var triggeredProjectRequest = jobRequest.pipe(
			function(job) {
				var triggeredProjects = getTriggeredProjects(job.lastBuild)
				var deferreds = $.map(triggeredProjects, function(job) {
					return buildData(job.name);
				})
				return when(deferreds);
			}
		)

		$.when(jobRequest, envVarsRequest, triggeredProjectRequest).done(
			function(jobResult, envVarsResult, triggeredProjects) {
				var job = jobResult[0];
				var envVars = envVarsResult[0];
				var rev = findRevision(envVars.envVars.envVar)
					// if (!$.isArray(triggeredProjects)) {
					// 	triggeredProjects = [triggeredProjects]
					// }

				var result = {
					revision: rev,
					name: jobName + " - " + rev,
					jobName: jobName,
					url: job.lastBuild.url,
					children: triggeredProjects
				}
				resultDef.resolve(result)
			}
		)

		return resultDef;
	}

	var init = function() {
		d3.select(self.frameElement).style("height", height + "px");

		// renderData()

		data.children[0].children[0].children = [{
			"name": "build-C"
		}]

		buildData("chain-start").done(function(result) {
			data = result;
			renderData();
		})

		// setTimeout(renderData, 1000);

		// d3.json(
		// 	"http://localhost:8080/view/my-chain/job/chain-step-1/4/injectedEnvVars/export",
		// 	function(error, json) {
		// 		var envVars = json.envVars.envVar;
		// 		var rev
		// 		for (var i = 0; i < envVars.length; i++) {
		// 			var env = envVars[i];
		// 			if (env.name == "REV") {
		// 				rev = env.value
		// 			}
		// 		}
		// 		d3.select("body").append("div").text(rev);
		// 	}).on("beforesend", function(request) {
		// 	request.withCredentials = true;
		// });
	}

	return {
		init: init
	}

}().init();
<commit_msg>Multiline Node descriptions<commit_after>var whereIsMyBuild = function() {
	var data = {};
	var when = function(deferreds) {
		if (deferreds.length == 0) {
			return $.Deferred().resolve([]);
		} else if (deferreds.length == 1) {
			return deferreds[0].pipe(function(deferred) {
				return [deferred]
			})
		} else {
			return $.when.apply($, deferreds)
		}
	}

	var width = 960,
		height = 2000;

	var cluster = d3.layout.tree().nodeSize([200, 200]);
	var diagonal = d3.svg.diagonal()
		.projection(function(d) {
			return [d.x, d.y];
		});
	var svg = d3.select("body").append("svg")
		.attr("width", width)
		.attr("height", height)
		.append("g")
		.attr("transform", "translate(500,300)");

	var searchedRev = 1234;

	var renderData = function() {
		var nodes = cluster.nodes(data),
			links = cluster.links(nodes);

		var link = svg.selectAll(".link")
			.data(links, function(d) {
				return d.source.name + d.target.name
			})

		link.enter().append("path")
			.attr("class", "link");

		link.transition().attr("d", diagonal)

		link.exit().remove();

		var node = svg.selectAll(".node")
			.data(nodes, function(d) {
				return d.name
			});

		var parentNode = node.enter().append("g")
			.attr("class", "node");
		parentNode.append("circle")
			.attr("r", 4.5);
		var textNode = parentNode.append("a")
			.attr("xlink:href", function(d) {
				return d.url
			})
			.append("text");

		var dxChildren = function(d) {
			return 8;
		}

		textNode
			.append("tspan")
			.text(function(d) {
				return d.jobName;
			});

		textNode
			.append("tspan")
			.attr("dy", "1.2em")
			.text(function(d) {
				return d.revision
			})

		node.selectAll("a text tspan")
			.attr("x", "0")
			.attr("dx", dxChildren)

		node.selectAll("a text").transition()
			.attr("dy", 0)
			.style("text-anchor", "start");

		node.transition().attr("transform", function(d) {
			return "translate(" + d.x + "," + d.y + ")";
		})

		node.exit().remove();
	}

	var buildData = function(jobName) {
		var resultDef = $.Deferred();
		var findRevision = function(envVars) {
			for (var i = 0; i < envVars.length; i++) {
				var env = envVars[i];
				if (env.name == "REV") {
					return env.value
				}
			}
		}
		var jobRequest = $.getJSON(
			"http://localhost:8080/job/" + jobName +
			"/api/json?tree=lastBuild[number,url,actions[triggeredProjects[name]]]")
		var envVarsRequest = jobRequest.pipe(
			function(job) {
				return $.getJSON(job.lastBuild.url + "injectedEnvVars/export");
			})

		var getTriggeredProjects = function(build) {
			var actions = build.actions;
			var triggeredProjects = [];
			var i;
			var action;
			for (i = 0; i < actions.length; i++) {
				action = actions[i];
				if (action.triggeredProjects) {
					triggeredProjects = triggeredProjects.concat(action.triggeredProjects);
				}
			}
			return triggeredProjects;
		}

		var triggeredProjectRequest = jobRequest.pipe(
			function(job) {
				var triggeredProjects = getTriggeredProjects(job.lastBuild)
				var deferreds = $.map(triggeredProjects, function(job) {
					return buildData(job.name);
				})
				return when(deferreds);
			}
		)

		$.when(jobRequest, envVarsRequest, triggeredProjectRequest).done(
			function(jobResult, envVarsResult, triggeredProjects) {
				var job = jobResult[0];
				var envVars = envVarsResult[0];
				var rev = findRevision(envVars.envVars.envVar)
					// if (!$.isArray(triggeredProjects)) {
					// 	triggeredProjects = [triggeredProjects]
					// }

				var result = {
					revision: rev,
					name: jobName + " - " + rev,
					jobName: jobName,
					url: job.lastBuild.url,
					children: triggeredProjects
				}
				resultDef.resolve(result)
			}
		)

		return resultDef;
	}

	var init = function() {
		d3.select(self.frameElement).style("height", height + "px");

		buildData("chain-start").done(function(result) {
			data = result;
			renderData();
		})
	}

	return {
		init: init
	}

}().init();
<|endoftext|>


<commit_before>/**
 * RegisteredAlgorithms
 */
class RegisteredAlgorithms extends Array {

  /**
   * Constructor
   *
   * @param {Array} collection
   */
  constructor (collection) {
    super()

    collection.forEach(item => {
      this.push(item)
    })
  }

  /**
   * getCaseInsensitive
   *
   * @param {string} algName
   * @returns {string}
   */
  getCaseInsensitive (algName) {
    return this.find(item => {
      return item.match(new RegExp(algName, 'i'))
    })
  }
}

/**
 * Export
 */
module.exports = RegisteredAlgorithms
<commit_msg>refactor(RegisteredAlgorithms): convert from array to object mapping<commit_after>/**
 * RegisteredAlgorithms
 */
class RegisteredAlgorithms {

  /**
   * Constructor
   *
   * @param {Object} mapping
   */
  constructor (mapping) {
    Object.assign(this, mapping)
  }

  /**
   * getCaseInsensitive
   *
   * @param {string} algName
   * @returns {string}
   */
  getCaseInsensitive (algName) {
    for (let key in this) {
      if (key.match(new RegExp(`^${algName}$`, 'i'))) {
        return key
      }
    }
  }
}

/**
 * Export
 */
module.exports = RegisteredAlgorithms
<|endoftext|>


<commit_before>// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
var app = angular.module('careWheels', ['ionic', 'ngCordova'])

app.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.cordova && window.cordova.plugins.Keyboard) {
      // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
      // for form inputs)
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);

      // Don't remove this line unless you know what you are doing. It stops the viewport
      // from snapping when text inputs are focused. Ionic handles this internally for
      // a much nicer keyboard experience.
      cordova.plugins.Keyboard.disableScroll(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})

app.controller("NotificationController", function($scope, $log, $cordovaLocalNotification){
  function Time() {this.hours=0; this.minutes=0; this.seconds=0;};
  $scope.data = angular.fromJson(window.localStorage['Reminders']);
  $scope.Init_Notifs = function() {
    if($scope.data){
      $scope.data;
      $scope.data[0] = new Time();
      $scope.data[1] = new Time();
      $scope.data[2] = new Time();
      $scope.Create_Notif(10,0,0,1);
      $scope.Create_Notif(14,0,0,2);
      $scope.Create_Notif(19,0,0,3);
    } else {
      $log.warn("in else");
      if($scope.data[0]) $scope.Create_Notif($scope.data[0].hours,$scope.data[0].minutes,$scope.data[0].seconds,1);
      if($scope.data[1]) $scope.Create_Notif($scope.data[1].hours,$scope.data[1].minutes,$scope.data[1].seconds,2);
      if($scope.data[2]) $scope.Create_Notif($scope.data[2].hours,$scope.data[2].minutes,$scope.data[2].seconds,3);
    }
    $log.warn($scope.data[0]);
  }

  $scope.Create_Notif = function(hours=0, minutes=0, seconds=0, reminderNum=0){
    if(reminderNum==0){
      $cordovaLocalNotification.schedule({
        id: reminderNum,
        message: "There are red alert(s) on your CareWheel!",
        title: "CareWheels",
        sound: null
      }).then(function() {
        $log.warn("Alert notification has been set");
      });
    } else if(reminderNum <4){
      var time = new Date();
      time.setHours(hours);
      $scope.data[reminderNum-1].hours = hours;
      time.setMinutes(minutes);
      $scope.data[reminderNum-1].minutes = minutes;
      time.setSeconds(seconds);
      $scope.data[reminderNum-1].seconds = seconds;

      window.localStorage['Reminders'] = angular.toJson($scope.data);
      // $cordovaLocalNotification.schedule({
      //   id: reminderNum,
      //   firstAt: time,
      //   every: "day",
      //   message: "Reminder " + reminderNum + ": Please check in with your CareWheel!",
      //   title: "CareWheels",
      //   sound: null
      // }).then(function() {
      //   $log.warn("Notification" + reminderNum + "has been set to " + time.getUTCTime());
      // });
    } else {
      $log.warn("Incorrect attempt to create notification for id #" + reminderNum);
    }
  };

  $scope.Delete_Notif = function(id){
    $cordovaLocalNotification.clear(id, function() {
        $log.warn(id + " is deleted");
    });
  }
});<commit_msg>Issue was actually a minor typo (if statement with if true when it should've been if false). Definitely want to do additional testing but I think it's working now!<commit_after>// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
var app = angular.module('careWheels', ['ionic', 'ngCordova'])

app.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.cordova && window.cordova.plugins.Keyboard) {
      // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
      // for form inputs)
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);

      // Don't remove this line unless you know what you are doing. It stops the viewport
      // from snapping when text inputs are focused. Ionic handles this internally for
      // a much nicer keyboard experience.
      cordova.plugins.Keyboard.disableScroll(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})

app.controller("NotificationController", function($scope, $log, $cordovaLocalNotification){
  function Time() {this.hours=0; this.minutes=0; this.seconds=0;};
  $scope.data = angular.fromJson(window.localStorage['Reminders']);
  $scope.Init_Notifs = function() {
    if(!$scope.data){
      $log.warn("in if");
      $scope.data;
      $scope.data[0] = new Time();
      $scope.data[1] = new Time();
      $scope.data[2] = new Time();
      $scope.Create_Notif(10,0,0,1);
      $scope.Create_Notif(14,0,0,2);
      $scope.Create_Notif(19,0,0,3);
    } else {
      $log.warn("in else");
      if($scope.data[0]) $scope.Create_Notif($scope.data[0].hours,$scope.data[0].minutes,$scope.data[0].seconds,1);
      if($scope.data[1]) $scope.Create_Notif($scope.data[1].hours,$scope.data[1].minutes,$scope.data[1].seconds,2);
      if($scope.data[2]) $scope.Create_Notif($scope.data[2].hours,$scope.data[2].minutes,$scope.data[2].seconds,3);
    }
    $log.warn($scope.data[1]);
  }

  $scope.Create_Notif = function(hours=0, minutes=0, seconds=0, reminderNum=0){
    if(reminderNum==0){
      $cordovaLocalNotification.schedule({
        id: reminderNum,
        message: "There are red alert(s) on your CareWheel!",
        title: "CareWheels",
        sound: null
      }).then(function() {
        $log.warn("Alert notification has been set");
      });
    } else if(reminderNum <4){
      var time = new Date();
      time.setHours(hours);
      $scope.data[reminderNum-1].hours = hours;
      time.setMinutes(minutes);
      $scope.data[reminderNum-1].minutes = minutes;
      time.setSeconds(seconds);
      $scope.data[reminderNum-1].seconds = seconds;
      window.localStorage['Reminders'] = angular.toJson($scope.data);

      $cordovaLocalNotification.schedule({
        id: reminderNum,
        firstAt: time,
        every: "day",
        message: "Reminder " + reminderNum + ": Please check in with your CareWheel!",
        title: "CareWheels",
        sound: null
      }).then(function() {
        $log.warn("Notification" + reminderNum + "has been set to " + time.getUTCTime());
      });
    } else {
      $log.warn("Incorrect attempt to create notification for id #" + reminderNum);
    }
  };

  $scope.Delete_Notif = function(id){
    $cordovaLocalNotification.clear(id, function() {
        $log.warn(id + " is deleted");
    });
  }
});
<|endoftext|>


<commit_before>let parseJson = (str) => {
  try {
    return JSON.parse(str);
  } catch (e) {
    log('invalid json :( ', str, e);
    return false;
  }
};

Template.DocumentsFilter.onCreated(function() {
  this.invalidSelection = new ReactiveVar(null);
  this.invalidOptions = new ReactiveVar(null);
});

Template.DocumentsFilter.helpers({
  invalidSelection() {
    return Template.instance().invalidSelection.get();
  },

  invalidOptions() {
    return Template.instance().invalidOptions.get();
  }
});

Template.DocumentsFilter.events({
  'submit form.document-filter'(e, i) {
    e.preventDefault();
    const selection = e.currentTarget.selection.value;
    const options = e.currentTarget.options.value;

    let selectionJson = {};
    if(!!selection) {
      selectionJson = parseJson(selection);
      if(!selectionJson) {
        i.invalidSelection.set('Invalid json format');
        return false;
      }
    } else {
      i.invalidSelection.set(false);
    }

    let optionsJson = {};
    if(!!options) {
      optionsJson = parseJson(options);
      if(!optionsJson) {
        i.invalidOptions.set('Invalid json format');
        return false;
      }
    } else {
      i.invalidOptions.set(false);
    }

    i.data.onSubmit(selectionJson, optionsJson);
  }
});
<commit_msg>json parser tuneup<commit_after>let parseJson = (str) => {
  // http://stackoverflow.com/questions/24175802/missing-quotation-marks-on-keys-in-json
  str = str.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:([^\/])/g, '"$2":$4');
  try {
    return JSON.parse(str);
  } catch (e) {
    log('invalid json :( ', str, e);
    return false;
  }
};

Template.DocumentsFilter.onCreated(function() {
  this.invalidSelection = new ReactiveVar(null);
  this.invalidOptions = new ReactiveVar(null);
});

Template.DocumentsFilter.helpers({
  invalidSelection() {
    return Template.instance().invalidSelection.get();
  },

  invalidOptions() {
    return Template.instance().invalidOptions.get();
  }
});

Template.DocumentsFilter.events({
  'submit form.document-filter'(e, i) {
    e.preventDefault();
    const selection = e.currentTarget.selection.value;
    const options = e.currentTarget.options.value;

    let selectionJson = {};
    if(!!selection) {
      selectionJson = parseJson(selection);
      if(!selectionJson) {
        i.invalidSelection.set('Invalid json format');
        return false;
      }
    } else {
      i.invalidSelection.set(false);
    }

    let optionsJson = {};
    if(!!options) {
      optionsJson = parseJson(options);
      if(!optionsJson) {
        i.invalidOptions.set('Invalid json format');
        return false;
      }
    } else {
      i.invalidOptions.set(false);
    }

    i.data.onSubmit(selectionJson, optionsJson);
  }
});
<|endoftext|>


<commit_before>$(document).ready(function() {
  Sortable.init();

  //Initialize Tables
  for(var i = 0; i < $('table').length; i++){
    $($('table')[i]).attr("data-sortable-initialized", false);
    Sortable.initTable($('table')[i]);
    $($('table')[i]).attr("data-sortable-initialized", true);
  }
})

$('#expand-all').on('click', function () {
    $('.accordion-toggle.collapsed.has-data').parent().siblings('.panel-collapse').collapse('show');
});

$('#collapse-all').on('click', function () {
    $('.accordion-toggle.has-data').parent().siblings('.panel-collapse').collapse('hide');
});
<commit_msg>AQCU-1196: Fix for arrows collapse panel arrows not updating once the TSS report has been saved.<commit_after>$(document).ready(function() {
  Sortable.init();

  //Initialize Tables
  for(var i = 0; i < $('table').length; i++){
    $($('table')[i]).attr("data-sortable-initialized", false);
    Sortable.initTable($('table')[i]);
    $($('table')[i]).attr("data-sortable-initialized", true);
  }
});

$('#expand-all').on('click', function () {
    $('.accordion-toggle.collapsed.has-data').parent().siblings('.panel-collapse').collapse('show');
});

$('#collapse-all').on('click', function () {
    $('.accordion-toggle.has-data').parent().siblings('.panel-collapse').collapse('hide');
});

$('.accordion-toggle').on('click', function (event){
  if($(event.target).hasClass('collapsed')){
    $(event.target).removeClass('collapsed')
  } else {
    $(event.target).addClass('collapsed')
  }
})
<|endoftext|>