<commit_before>/* @flow */

import EventEmitter from 'events';

import {assert} from 'chai';
import {describe, it} from 'mocha';
import deepcopy from 'deepcopy';
import fs from 'mz/fs';
import sinon from 'sinon';
import WebSocket from 'ws';

import getValidatedManifest from '../../../src/util/manifest';
import {
  basicManifest,
  StubChildProcess,
} from '../helpers';
import {
  ChromiumExtensionRunner,
  DEFAULT_CHROME_FLAGS,
} from '../../../src/extension-runners/chromium';
import type {
  ChromiumExtensionRunnerParams,
} from '../../../src/extension-runners/chromium';
import {
  consoleStream, // instance is imported to inspect logged messages
} from '../../../src/util/logger';

function prepareExtensionRunnerParams({params} = {}) {
  const fakeChromeInstance = {
    process: new StubChildProcess(),
    kill: sinon.spy(async () => {}),
  };
  const runnerParams: ChromiumExtensionRunnerParams = {
    extensions: [{
      sourceDir: '/fake/sourceDir',
      manifestData: deepcopy(basicManifest),
    }],
    keepProfileChanges: false,
    startUrl: undefined,
    chromiumLaunch: sinon.spy(async () => {
      return fakeChromeInstance;
    }),
    desktopNotifications: sinon.spy(() => {}),
    ...(params || {}),
  };

  return {params: runnerParams, fakeChromeInstance};
}

describe('util/extension-runners/chromium', async () => {

  it('uses the expected chrome flags', () => {
    const expectedFlags = [ // Flags from 11.2
      '--disable-translate',
      '--disable-background-networking',
      '--disable-sync',
      '--metrics-recording-only',
      '--disable-default-apps',
      '--no-first-run',
    ];

    assert.deepEqual(DEFAULT_CHROME_FLAGS, expectedFlags);
  });

  it('installs and runs the extension', async () => {
    const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    assert.equal(runnerInstance.getName(), 'Chromium');

    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
    sinon.assert.calledOnce(fakeChromeInstance.kill);
  });

  it('installs a "reload manager" companion extension', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    assert.equal(await fs.exists(reloadManagerExtension), true);
    const managerExtManifest = await getValidatedManifest(
      reloadManagerExtension);
    assert.deepEqual(managerExtManifest.permissions, ['management', 'tabs']);

    await runnerInstance.exit();
  });

  it('controls the "reload manager" from a websocket server', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const wssInfo = runnerInstance.wss.address();
    const wsURL = `ws://${wssInfo.address}:${wssInfo.port}`;
    const wsClient = new WebSocket(wsURL);

    await new Promise((resolve) => wsClient.on('open', resolve));

    // Clear console stream from previous messages and start recording
    consoleStream.stopCapturing();
    consoleStream.flushCapturedLogs();
    consoleStream.startCapturing();
    // Make verbose to capture debug logs.
    consoleStream.makeVerbose();

    // Emit a fake socket object as a new wss connection.

    const fakeSocket = new EventEmitter();
    sinon.spy(fakeSocket, 'on');
    runnerInstance.wss.emit('connection', fakeSocket);
    sinon.assert.calledOnce(fakeSocket.on);

    fakeSocket.emit('error', new Error('Fake wss socket ERROR'));

    // Retrieve captures logs and stop capturing.
    const {capturedMessages} = consoleStream;
    consoleStream.stopCapturing();

    assert.ok(capturedMessages.some(
      (message) => (
        message.match('[debug]') &&
        message.match('Fake wss socket ERROR')
      )));

    const waitForReloadAll = new Promise((resolve) =>
      wsClient.on('message', resolve));
    await runnerInstance.reloadAllExtensions();
    assert.deepEqual(JSON.parse(await waitForReloadAll),
                     {type: 'webExtReloadAllExtensions'});

    // TODO(rpl): change this once we improve the manager extension to be able
    // to reload a single extension.
    const waitForReloadOne = new Promise((resolve) =>
      wsClient.on('message', resolve));
    await runnerInstance.reloadExtensionBySourceDir('/fake/sourceDir');
    assert.deepEqual(JSON.parse(await waitForReloadOne),
                     {type: 'webExtReloadAllExtensions'});

    // Verify that if one websocket connection gets closed, a second websocket
    // connection still receives the control messages.
    const wsClient2 = new WebSocket(wsURL);
    await new Promise((resolve) => wsClient2.on('open', resolve));
    wsClient.close();

    await runnerInstance.reloadAllExtensions();
    const waitForReloadAllAgain = new Promise((resolve) =>
      wsClient2.on('message', resolve));
    await runnerInstance.reloadAllExtensions();
    assert.deepEqual(JSON.parse(await waitForReloadAllAgain),
                     {type: 'webExtReloadAllExtensions'});

    await runnerInstance.exit();
  });

  it('exits if the chrome instance is shutting down', async () => {
    const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const onceExiting = new Promise((resolve) =>
      runnerInstance.registerCleanup(resolve));

    fakeChromeInstance.process.emit('close');

    await onceExiting;
  });

  it('calls all cleanup callbacks on exit', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    runnerInstance.registerCleanup(function fnThrowsError() {
      throw new Error('fake cleanup exception');
    });

    const onceExiting = new Promise((resolve) =>
      runnerInstance.registerCleanup(resolve));

    await runnerInstance.exit();
    await onceExiting;
  });

  it('does not call exit if chrome instance exits while shutting down',
     async () => {
       const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
       const runnerInstance = new ChromiumExtensionRunner(params);
       await runnerInstance.run();

       sinon.spy(runnerInstance, 'exit');

       const exitDone = runnerInstance.exit();
       fakeChromeInstance.process.emit('close');

       await exitDone;

       sinon.assert.calledOnce(runnerInstance.exit);
     });

  it('awaits for the async setup to complete before exiting', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        chromiumLaunch: sinon.spy(async () => {
          throw new Error('Fake chromiumLaunch ERROR');
        }),
      },
    });
    const runnerInstance = new ChromiumExtensionRunner(params);
    assert.equal(runnerInstance.getName(), 'Chromium');

    await assert.isRejected(
      runnerInstance.run(),
      /Fake chromiumLaunch ERROR/
    );

    // Clear console stream from previous messages and start recording
    consoleStream.stopCapturing();
    consoleStream.flushCapturedLogs();
    consoleStream.startCapturing();
    // Make verbose to capture debug logs.
    consoleStream.makeVerbose();

    // Call exit and then verify that it caught the chromiumLaunch rejection
    // and logged it as a debug log.
    await runnerInstance.exit();

    // Retrieve captures logs and stop capturing.
    const {capturedMessages} = consoleStream;
    consoleStream.stopCapturing();

    assert.ok(capturedMessages.some(
      (message) => (
        message.match('[debug]') &&
        message.match('ignored setup error on chromium runner shutdown') &&
        message.match('Fake chromiumLaunch ERROR')
      )));
  });

  it('does use a custom chromium binary when passed', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {chromiumBinary: '/my/custom/chrome-bin'},
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: '/my/custom/chrome-bin',
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
  });

  it('does pass multiple starting urls to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {startUrl: ['url1', 'url2', 'url3']},
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        'url2',
        'url3',
      ],
      startingUrl: 'url1',
    });

    await runnerInstance.exit();
  });

  it('does pass additional args to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        args: ['--arg1', 'arg2', '--arg3'],
        startUrl: ['url1', 'url2', 'url3'],
      },
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        '--arg1',
        'arg2',
        '--arg3',
        'url2',
        'url3',
      ],
      startingUrl: 'url1',
    });

    await runnerInstance.exit();
  });

  it('does pass a user-data-dir flag to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        chromiumProfile: '/fake/chrome/profile',
      },
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        '--user-data-dir=/fake/chrome/profile',
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
  });

});
<commit_msg>chore: Updated expected chrome-launcher v0.12.0 default flags<commit_after>/* @flow */

import EventEmitter from 'events';

import {assert} from 'chai';
import {describe, it} from 'mocha';
import deepcopy from 'deepcopy';
import fs from 'mz/fs';
import sinon from 'sinon';
import WebSocket from 'ws';

import getValidatedManifest from '../../../src/util/manifest';
import {
  basicManifest,
  StubChildProcess,
} from '../helpers';
import {
  ChromiumExtensionRunner,
  DEFAULT_CHROME_FLAGS,
} from '../../../src/extension-runners/chromium';
import type {
  ChromiumExtensionRunnerParams,
} from '../../../src/extension-runners/chromium';
import {
  consoleStream, // instance is imported to inspect logged messages
} from '../../../src/util/logger';

function prepareExtensionRunnerParams({params} = {}) {
  const fakeChromeInstance = {
    process: new StubChildProcess(),
    kill: sinon.spy(async () => {}),
  };
  const runnerParams: ChromiumExtensionRunnerParams = {
    extensions: [{
      sourceDir: '/fake/sourceDir',
      manifestData: deepcopy(basicManifest),
    }],
    keepProfileChanges: false,
    startUrl: undefined,
    chromiumLaunch: sinon.spy(async () => {
      return fakeChromeInstance;
    }),
    desktopNotifications: sinon.spy(() => {}),
    ...(params || {}),
  };

  return {params: runnerParams, fakeChromeInstance};
}

describe('util/extension-runners/chromium', async () => {

  it('uses the expected chrome flags', () => {
    // Flags from chrome-launcher v0.12.0
    const expectedFlags = [
      '--disable-features=TranslateUI',
      '--disable-component-extensions-with-background-pages',
      '--disable-background-networking',
      '--disable-sync',
      '--metrics-recording-only',
      '--disable-default-apps',
      '--no-first-run',
      '--disable-backgrounding-occluded-windows',
      '--disable-renderer-backgrounding',
      '--disable-background-timer-throttling',
    ];

    assert.deepEqual(DEFAULT_CHROME_FLAGS, expectedFlags);
  });

  it('installs and runs the extension', async () => {
    const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    assert.equal(runnerInstance.getName(), 'Chromium');

    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
    sinon.assert.calledOnce(fakeChromeInstance.kill);
  });

  it('installs a "reload manager" companion extension', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    assert.equal(await fs.exists(reloadManagerExtension), true);
    const managerExtManifest = await getValidatedManifest(
      reloadManagerExtension);
    assert.deepEqual(managerExtManifest.permissions, ['management', 'tabs']);

    await runnerInstance.exit();
  });

  it('controls the "reload manager" from a websocket server', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const wssInfo = runnerInstance.wss.address();
    const wsURL = `ws://${wssInfo.address}:${wssInfo.port}`;
    const wsClient = new WebSocket(wsURL);

    await new Promise((resolve) => wsClient.on('open', resolve));

    // Clear console stream from previous messages and start recording
    consoleStream.stopCapturing();
    consoleStream.flushCapturedLogs();
    consoleStream.startCapturing();
    // Make verbose to capture debug logs.
    consoleStream.makeVerbose();

    // Emit a fake socket object as a new wss connection.

    const fakeSocket = new EventEmitter();
    sinon.spy(fakeSocket, 'on');
    runnerInstance.wss.emit('connection', fakeSocket);
    sinon.assert.calledOnce(fakeSocket.on);

    fakeSocket.emit('error', new Error('Fake wss socket ERROR'));

    // Retrieve captures logs and stop capturing.
    const {capturedMessages} = consoleStream;
    consoleStream.stopCapturing();

    assert.ok(capturedMessages.some(
      (message) => (
        message.match('[debug]') &&
        message.match('Fake wss socket ERROR')
      )));

    const waitForReloadAll = new Promise((resolve) =>
      wsClient.on('message', resolve));
    await runnerInstance.reloadAllExtensions();
    assert.deepEqual(JSON.parse(await waitForReloadAll),
                     {type: 'webExtReloadAllExtensions'});

    // TODO(rpl): change this once we improve the manager extension to be able
    // to reload a single extension.
    const waitForReloadOne = new Promise((resolve) =>
      wsClient.on('message', resolve));
    await runnerInstance.reloadExtensionBySourceDir('/fake/sourceDir');
    assert.deepEqual(JSON.parse(await waitForReloadOne),
                     {type: 'webExtReloadAllExtensions'});

    // Verify that if one websocket connection gets closed, a second websocket
    // connection still receives the control messages.
    const wsClient2 = new WebSocket(wsURL);
    await new Promise((resolve) => wsClient2.on('open', resolve));
    wsClient.close();

    await runnerInstance.reloadAllExtensions();
    const waitForReloadAllAgain = new Promise((resolve) =>
      wsClient2.on('message', resolve));
    await runnerInstance.reloadAllExtensions();
    assert.deepEqual(JSON.parse(await waitForReloadAllAgain),
                     {type: 'webExtReloadAllExtensions'});

    await runnerInstance.exit();
  });

  it('exits if the chrome instance is shutting down', async () => {
    const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const onceExiting = new Promise((resolve) =>
      runnerInstance.registerCleanup(resolve));

    fakeChromeInstance.process.emit('close');

    await onceExiting;
  });

  it('calls all cleanup callbacks on exit', async () => {
    const {params} = prepareExtensionRunnerParams();
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    runnerInstance.registerCleanup(function fnThrowsError() {
      throw new Error('fake cleanup exception');
    });

    const onceExiting = new Promise((resolve) =>
      runnerInstance.registerCleanup(resolve));

    await runnerInstance.exit();
    await onceExiting;
  });

  it('does not call exit if chrome instance exits while shutting down',
     async () => {
       const {params, fakeChromeInstance} = prepareExtensionRunnerParams();
       const runnerInstance = new ChromiumExtensionRunner(params);
       await runnerInstance.run();

       sinon.spy(runnerInstance, 'exit');

       const exitDone = runnerInstance.exit();
       fakeChromeInstance.process.emit('close');

       await exitDone;

       sinon.assert.calledOnce(runnerInstance.exit);
     });

  it('awaits for the async setup to complete before exiting', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        chromiumLaunch: sinon.spy(async () => {
          throw new Error('Fake chromiumLaunch ERROR');
        }),
      },
    });
    const runnerInstance = new ChromiumExtensionRunner(params);
    assert.equal(runnerInstance.getName(), 'Chromium');

    await assert.isRejected(
      runnerInstance.run(),
      /Fake chromiumLaunch ERROR/
    );

    // Clear console stream from previous messages and start recording
    consoleStream.stopCapturing();
    consoleStream.flushCapturedLogs();
    consoleStream.startCapturing();
    // Make verbose to capture debug logs.
    consoleStream.makeVerbose();

    // Call exit and then verify that it caught the chromiumLaunch rejection
    // and logged it as a debug log.
    await runnerInstance.exit();

    // Retrieve captures logs and stop capturing.
    const {capturedMessages} = consoleStream;
    consoleStream.stopCapturing();

    assert.ok(capturedMessages.some(
      (message) => (
        message.match('[debug]') &&
        message.match('ignored setup error on chromium runner shutdown') &&
        message.match('Fake chromiumLaunch ERROR')
      )));
  });

  it('does use a custom chromium binary when passed', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {chromiumBinary: '/my/custom/chrome-bin'},
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: '/my/custom/chrome-bin',
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
  });

  it('does pass multiple starting urls to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {startUrl: ['url1', 'url2', 'url3']},
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        'url2',
        'url3',
      ],
      startingUrl: 'url1',
    });

    await runnerInstance.exit();
  });

  it('does pass additional args to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        args: ['--arg1', 'arg2', '--arg3'],
        startUrl: ['url1', 'url2', 'url3'],
      },
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        '--arg1',
        'arg2',
        '--arg3',
        'url2',
        'url3',
      ],
      startingUrl: 'url1',
    });

    await runnerInstance.exit();
  });

  it('does pass a user-data-dir flag to chrome', async () => {
    const {params} = prepareExtensionRunnerParams({
      params: {
        chromiumProfile: '/fake/chrome/profile',
      },
    });

    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        `--load-extension=${reloadManagerExtension},/fake/sourceDir`,
        '--user-data-dir=/fake/chrome/profile',
      ],
      startingUrl: undefined,
    });

    await runnerInstance.exit();
  });

});
<|endoftext|>


<commit_before>angular.module('codebrag.commits')

    .controller('CommitsCtrl', function ($scope, $http, commitsListService, commitLoadFilter) {

        $scope.syncCommits = commitsListService.syncCommits;

        $scope.loadAllCommits = function() {
            $scope.commits = commitsListService.loadAllCommits();
        };

        $scope.loadPendingCommits = function() {
            $scope.commits = commitsListService.loadCommitsPendingReview();
        };

        $scope.canLoadMore = function() {
            return !commitLoadFilter.isAll();
        };

        $scope.loadMoreCommits = function() {
            $scope.commits = commitsListService.loadMoreCommits();
        };

        $scope.loadPendingCommits();

    });<commit_msg>Make current diff disappear when switching between pending / all<commit_after>angular.module('codebrag.commits')

    .controller('CommitsCtrl', function ($scope, $http, commitsListService, commitLoadFilter, $state) {

        $scope.syncCommits = commitsListService.syncCommits;

        $scope.loadAllCommits = function() {
            $scope.commits = commitsListService.loadAllCommits();
            $state.transitionTo('commits.list');
        };

        $scope.loadPendingCommits = function() {
            $scope.commits = commitsListService.loadCommitsPendingReview();
            $state.transitionTo('commits.list');
        };

        $scope.canLoadMore = function() {
            return !commitLoadFilter.isAll();
        };

        $scope.loadMoreCommits = function() {
            $scope.commits = commitsListService.loadMoreCommits();
        };

        $scope.loadPendingCommits();

    });<|endoftext|>


<commit_before>var plotdata = [];
var timeWindowChanged = 0;
var ajaxAsyncXdr = [];
var event_vis_feed_data;

function convert_to_plotlist(multigraph_feedlist) {
  var plotlist = [];
  var showtag = (multigraph_feedlist[0]['showtag'] != undefined ? multigraph_feedlist[0]['showtag'] : true);
  for (z in multigraph_feedlist) {
    var tag = (showtag && multigraph_feedlist[z]['tag']!=undefined && multigraph_feedlist[z]['tag']!="" ? multigraph_feedlist[z]['tag']+": " : "");
    if (multigraph_feedlist[z]['datatype']==1) {
      plotlist[z] = {
        id: multigraph_feedlist[z]['id'],
        selected: 1,
        plot: {
          data: null,
          label: tag + multigraph_feedlist[z]['name'],
          points: {
            show: true,
            radius: 0,
            lineWidth: 1, // in pixels
            fill: false
          },
          lines: {
            show: true,
            fill: multigraph_feedlist[z]['fill']
          }
        }
      };
    }

    else if (multigraph_feedlist[z]['datatype']==2) {
      plotlist[z] = {
        id: multigraph_feedlist[z]['id'],
        selected: 1,
        plot: {
          data: null,
          label: tag + multigraph_feedlist[z]['name'],
          bars: {
            show: true,
            align: "left", barWidth: 3600*24*1000, fill: multigraph_feedlist[z]['fill']
          }
        }
      };
    } else {
      console.log("ERROR: Unknown plot datatype! Datatype: ", multigraph_feedlist[z]['datatype']);
    }

    if (multigraph_feedlist[z]['left']==true) {
      plotlist[z].plot.yaxis = 1;
    } else if (multigraph_feedlist[z]['right']==true) {
      plotlist[z].plot.yaxis = 2;
    } else {
      console.log("ERROR: Unknown plot alignment! Alignment setting: ", multigraph_feedlist[z]['right']);
    }

    // Only set the plotcolour variable if we have a value to set it with
    if (multigraph_feedlist[z]["lineColour"]) {
      // Some browsers really want the leading "#". It works without in chrome, not in IE and opera.
      // What the hell, people?
      if (multigraph_feedlist[z]["lineColour"].indexOf("#") == -1) {
        plotlist[z].plot.color = "#" + multigraph_feedlist[z]["lineColour"];
      } else {
        plotlist[z].plot.color = multigraph_feedlist[z]["lineColour"];
      }
    }

    if (multigraph_feedlist[z]['left']==false && multigraph_feedlist[z]['right']==false) {
      plotlist[z].selected = 0;
    }
  }
  return plotlist;
}

/*
 Handle Feeds
*/

// Ignore load request spurts
function vis_feed_data() {
   clearTimeout(event_vis_feed_data); // Cancel any pending events
   event_vis_feed_data = setTimeout(function() { vis_feed_data_delayed(); }, 500);
   if (multigraph_feedlist !== undefined && multigraph_feedlist.length != plotdata.length) plotdata = [];
   plot();
}

// Load relevant feed data asynchronously
function vis_feed_data_delayed() {
  var plotlist = convert_to_plotlist(multigraph_feedlist);
  for(var i in plotlist) {
    if (plotlist[i].selected) {
      if (!plotlist[i].plot.data) {

        var skipmissing = 0; if (multigraph_feedlist[i]['skipmissing']) skipmissing = 1;

        if (plotdata[i] === undefined) plotdata[i] = [];

        if (typeof ajaxAsyncXdr[i] !== 'undefined') {
          ajaxAsyncXdr[i].abort(); // Abort pending loads
          ajaxAsyncXdr[i]=undefined;
        }
        var context = {index:i, plotlist:plotlist[i]};

        var npoints = 800;
        interval = Math.round(((view.end - view.start)/npoints)/1000);

        // Round to more common and useful intervals:
        interval = view.round_interval(interval);

        var intervalms = interval*1000;
        view.start = Math.round(view.start/intervalms)*intervalms;
        view.end = Math.round(view.end/intervalms)*intervalms;

        ajaxAsyncXdr[i] = get_feed_data_async(vis_feed_data_callback,context,plotlist[i].id,view.start,view.end,interval,skipmissing,1);
      }
    }
  }
}

//load feed data to multigraph plot
function vis_feed_data_callback(context,data) {
  var i = context['index'];
  context['plotlist'].plot.data = data;
  if (context['plotlist'].plot.data) {
    plotdata[i] = context['plotlist'].plot;
  }
  plot();
}

function plot() {
  $.plot($("#graph"), plotdata, {
    grid: { show: true, hoverable: true, clickable: true },
    xaxis: { mode: "time", timezone: "browser", min: view.start, max: view.end },
    selection: { mode: "x" },
    legend: { position: "nw", toggle: true },
    toggle: { scale: "visible" },
    touch: { pan: "x", scale: "x" }
  });
}

function multigraph_init(element) {
  // Get start and end time of multigraph view
  // end time and timewindow is stored in the first multigraph_feedlist item.
  // start time is calculated from end - timewindow
  plotdata = [];
  var timeWindow = (3600000*24.0*7);
  var now = new Date().getTime();
  view.start = now - timeWindow;
  view.end = now;

  if (multigraph_feedlist !== undefined && multigraph_feedlist[0] != undefined) {
    view.end = multigraph_feedlist[0].end;
    if (view.end==0) view.end = now;
    if (multigraph_feedlist[0].timeWindow) {
        view.start = view.end - multigraph_feedlist[0].timeWindow;
    }
  }

  var out =
    "
"+
      "

"+
      "
"+
        "
"+
        "Tooltip:"+
        "<input id='enableTooltip' type='checkbox' checked >"+
        "
 "+

        "
"+
        "<button class='btn graph-time' type='button' time='1'>D</button>"+
        "<button class='btn graph-time' type='button' time='7'>W</button>"+
        "<button class='btn graph-time' type='button' time='30'>M</button>"+
        "<button class='btn graph-time' type='button' time='365'>Y</button>
"+

        "
"+
        "<button class='btn graph-nav' id='zoomin'>+</button>"+
        "<button class='btn graph-nav' id='zoomout'>-</button>"+
        "<button class='btn graph-nav' id='left'><</button>"+
        "<button class='btn graph-nav' id='right'>></button>
"+
      "
"+
    "
"
  ;
  $(element).html(out);

  // Tool tip
  var previousPoint = null;
  $(element).bind("plothover", function (event, pos, item) {
    //$("#x").text(pos.x.toFixed(2));
    //$("#y").text(pos.y.toFixed(2));

    if ($("#enableTooltip:checked").length > 0) {
      if (item) {
        if (previousPoint != item.dataIndex) {
          previousPoint = item.dataIndex;

          $("#tooltip").remove();
          var x = item.datapoint[0].toFixed(2),
          y = item.datapoint[1].toFixed(2);

          // create a new javascript Date object based on the timestamp
          // This implementation is clumsy, but the js native date.toTimeString() returns
          // strings like "08:53:35 GMT-0800", and there is no easy way to turn off the "GMT-xxxx" segment
          // blargh
          var date = new Date(parseInt(x));
          var hours = date.getHours();
          var minutes = date.getMinutes();
          var seconds = date.getSeconds();
          if (hours < 10)
            hours = "0"+hours;
          if (minutes < 10)
            minutes = "0"+minutes;
          if (seconds < 10)
            seconds = "0"+seconds;

          // will display time in 10:30:23 format
          var formattedTime = hours + ':' + minutes + ':' + seconds;

          // I'd like to eventually add colour hinting to the background of the tooltop.
          // This is why showTooltip has the bgColour parameter.
          tooltip(item.pageX, item.pageY, item.series.label + " at " + formattedTime   + " = " + y, "#DDDDDD");
        }
      } else {
        $("#tooltip").remove();
        previousPoint = null;
      }
    }
  });

  $('#graph').width($('#graph_bound').width());
  $('#graph').height($('#graph_bound').height());
  if (embed) $('#graph').height($(window).height());

  $(window).resize(function() {
    $('#graph').width($('#graph_bound').width());
    if (embed) $('#graph').height($(window).height());
    plot();
  });

  // Graph selections
  $("#graph").bind("plotselected", function (event, ranges) {
     view.start = ranges.xaxis.from;
     view.end = ranges.xaxis.to;
     vis_feed_data();
  });

  // Navigation actions
  $("#zoomout").click(function () {view.zoomout(); vis_feed_data();});
  $("#zoomin").click(function () {view.zoomin(); vis_feed_data();});
  $('#right').click(function () {view.panright(); vis_feed_data();});
  $('#left').click(function () {view.panleft(); vis_feed_data();});
  $('.graph-time').click(function () {view.timewindow($(this).attr("time")); vis_feed_data();});

  // Navigation and zooming buttons for mouse and touch
  $("#graph").mouseenter(function() {
    $("#graph-navbar").show();
    $("#graph-tooltip").show();
    $("#graph-buttons").stop().fadeIn();
    $("#stats").stop().fadeIn();
  });
  $("#graph_bound").mouseleave(function() {
    $("#graph-buttons").stop().fadeOut();
    $("#stats").stop().fadeOut();
  });
  $("#graph").bind("touchstarted", function (event, pos) {
    $("#graph-navbar").hide();
    $("#graph-tooltip").hide();
    $("#graph-buttons").stop().fadeOut();
    $("#stats").stop().fadeOut();
  });
  $("#graph").bind("touchended", function (event, ranges) {
    $("#graph-buttons").stop().fadeIn();
    $("#stats").stop().fadeIn();
    view.start = ranges.xaxis.from;
    view.end = ranges.xaxis.to;
    vis_feed_data();
  });
}
<commit_msg>Update multigraph.js<commit_after>var plotdata = [];
var timeWindowChanged = 0;
var ajaxAsyncXdr = [];
var event_vis_feed_data;
var showlegend = true;

function convert_to_plotlist(multigraph_feedlist) {
  var plotlist = [];
  var showtag = (multigraph_feedlist[0]['showtag'] != undefined ? multigraph_feedlist[0]['showtag'] : true);
  showlegend = (multigraph_feedlist[0]['showlegend']==undefined || multigraph_feedlist[0]['showlegend']);
  for (z in multigraph_feedlist) {
    var tag = (showtag && multigraph_feedlist[z]['tag']!=undefined && multigraph_feedlist[z]['tag']!="" ? multigraph_feedlist[z]['tag']+": " : "");
    if (multigraph_feedlist[z]['datatype']==1) {
      plotlist[z] = {
        id: multigraph_feedlist[z]['id'],
        selected: 1,
        plot: {
          data: null,
          label: tag + multigraph_feedlist[z]['name'],
 		  stack: stacked,
          points: {
            show: true,
            radius: 0,
            lineWidth: 1, // in pixels
            fill: false
          },
          lines: {
            show: true,
            fill: multigraph_feedlist[z]['fill'] ? (stacked ? 1.0 : 0.5) : 0.0
          }
        }
      };
    }

    else if (multigraph_feedlist[z]['datatype']==2) {
      plotlist[z] = {
        id: multigraph_feedlist[z]['id'],
        selected: 1,
        plot: {
          data: null,
          label: tag + multigraph_feedlist[z]['name'],
		  stack: stacked,
          bars: {
            show: true,
            align: "left", barWidth: 3600*24*1000,
			fill: multigraph_feedlist[z]['fill'] ? (stacked ? 1.0 : 0.5) : 0.0
          }
        }
      };
    } else {
      console.log("ERROR: Unknown plot datatype! Datatype: ", multigraph_feedlist[z]['datatype']);
    }

    if (multigraph_feedlist[z]['left']==true) {
      plotlist[z].plot.yaxis = 1;
    } else if (multigraph_feedlist[z]['right']==true) {
      plotlist[z].plot.yaxis = 2;
    } else {
      console.log("ERROR: Unknown plot alignment! Alignment setting: ", multigraph_feedlist[z]['right']);
    }

    // Only set the plotcolour variable if we have a value to set it with
    if (multigraph_feedlist[z]["lineColour"]) {
      // Some browsers really want the leading "#". It works without in chrome, not in IE and opera.
      // What the hell, people?
      if (multigraph_feedlist[z]["lineColour"].indexOf("#") == -1) {
        plotlist[z].plot.color = "#" + multigraph_feedlist[z]["lineColour"];
      } else {
        plotlist[z].plot.color = multigraph_feedlist[z]["lineColour"];
      }
    }

    if (multigraph_feedlist[z]['left']==false && multigraph_feedlist[z]['right']==false) {
      plotlist[z].selected = 0;
    }
  }
  return plotlist;
}

/*
 Handle Feeds
*/

function vis_feed_data() {
	if (multigraph_feedlist !== undefined && multigraph_feedlist[0] != undefined && multigraph_feedlist[0]['autorefresh'] != undefined) {
	  var now = new Date().getTime();
	  var timeWindow = view.end - view.start;
	  if (now - view.end < 2000 * multigraph_feedlist[0]['autorefresh']) {
	    view.end = now;
	    view.start = view.end - timeWindow;
        vis_feed_data1();
	    setTimeout(vis_feed_data, 1000 * multigraph_feedlist[0]['autorefresh']);
      } else{
        vis_feed_data1();
	  }
    } else{
      vis_feed_data1();
	}
 }
// Ignore load request spurts
function vis_feed_data1() {
   clearTimeout(event_vis_feed_data); // Cancel any pending events
   event_vis_feed_data = setTimeout(function() { vis_feed_data_delayed(); }, 500);
   if (multigraph_feedlist !== undefined && multigraph_feedlist.length != plotdata.length) plotdata = [];
   plot();
}

// Load relevant feed data asynchronously
function vis_feed_data_delayed() {
  var plotlist = convert_to_plotlist(multigraph_feedlist);
  for(var i in plotlist) {
    if (plotlist[i].selected) {
      if (!plotlist[i].plot.data) {

        var skipmissing = 0; if (multigraph_feedlist[i]['skipmissing']) skipmissing = 1;

        if (plotdata[i] === undefined) plotdata[i] = [];

        if (typeof ajaxAsyncXdr[i] !== 'undefined') {
          ajaxAsyncXdr[i].abort(); // Abort pending loads
          ajaxAsyncXdr[i]=undefined;
        }
        var context = {index:i, plotlist:plotlist[i]};

        var npoints = 800;
        interval = Math.round(((view.end - view.start)/npoints)/1000);

        // Round to more common and useful intervals:
        interval = view.round_interval(interval);

        var intervalms = interval*1000;
        view.start = Math.round(view.start/intervalms)*intervalms;
        view.end = Math.round(view.end/intervalms)*intervalms;

        ajaxAsyncXdr[i] = get_feed_data_async(vis_feed_data_callback,context,plotlist[i].id,view.start,view.end,interval,skipmissing,1);
      }
    }
  }
}

//load feed data to multigraph plot
function vis_feed_data_callback(context,data) {
  var i = context['index'];
  context['plotlist'].plot.data = data;
  if (context['plotlist'].plot.data) {
    plotdata[i] = context['plotlist'].plot;
  }
  plot();
}

function plot() {
  $.plot($("#graph"), plotdata, {
    grid: { show: true, hoverable: true, clickable: true },
    xaxis: { mode: "time", timezone: "browser", min: view.start, max: view.end },
    selection: { mode: "x" },
    legend: { show: showlegend, position: "nw", toggle: true },
    toggle: { scale: "visible" },
    touch: { pan: "x", scale: "x" }
  });
}

function multigraph_init(element) {
  // Get start and end time of multigraph view
  // end time and timewindow is stored in the first multigraph_feedlist item.
  // start time is calculated from end - timewindow
  plotdata = [];
  var timeWindow = (3600000*24.0*7);
  var now = new Date().getTime();
  view.start = now - timeWindow;
  view.end = now;

  if (multigraph_feedlist !== undefined && multigraph_feedlist[0] != undefined) {
    view.end = multigraph_feedlist[0].end;
    if (view.end==0) view.end = now;
    if (multigraph_feedlist[0].timeWindow) {
        view.start = view.end - multigraph_feedlist[0].timeWindow;
    }
  }

  var out =
    "
"+
      "

"+
      "
"+
        "
"+
        "Tooltip:"+
        "<input id='enableTooltip' type='checkbox' checked >"+
        "
 "+

        "
"+
        "<button class='btn graph-time' type='button' time='1'>D</button>"+
        "<button class='btn graph-time' type='button' time='7'>W</button>"+
        "<button class='btn graph-time' type='button' time='30'>M</button>"+
        "<button class='btn graph-time' type='button' time='365'>Y</button>
"+

        "
"+
        "<button class='btn graph-nav' id='zoomin'>+</button>"+
        "<button class='btn graph-nav' id='zoomout'>-</button>"+
        "<button class='btn graph-nav' id='left'><</button>"+
        "<button class='btn graph-nav' id='right'>></button>
"+
      "
"+
    "
"
  ;
  $(element).html(out);

  // Tool tip
  var previousPoint = null;
  $(element).bind("plothover", function (event, pos, item) {
    //$("#x").text(pos.x.toFixed(2));
    //$("#y").text(pos.y.toFixed(2));

    if ($("#enableTooltip:checked").length > 0) {
      if (item) {
        if (previousPoint != item.dataIndex) {
          previousPoint = item.dataIndex;

          $("#tooltip").remove();
          var x = item.datapoint[0].toFixed(2),
          y = item.datapoint[1].toFixed(2);

          // create a new javascript Date object based on the timestamp
          // This implementation is clumsy, but the js native date.toTimeString() returns
          // strings like "08:53:35 GMT-0800", and there is no easy way to turn off the "GMT-xxxx" segment
          // blargh
          var date = new Date(parseInt(x));
          var hours = date.getHours();
          var minutes = date.getMinutes();
          var seconds = date.getSeconds();
          if (hours < 10)
            hours = "0"+hours;
          if (minutes < 10)
            minutes = "0"+minutes;
          if (seconds < 10)
            seconds = "0"+seconds;

          // will display time in 10:30:23 format
          var formattedTime = hours + ':' + minutes + ':' + seconds;

          // I'd like to eventually add colour hinting to the background of the tooltop.
          // This is why showTooltip has the bgColour parameter.
          tooltip(item.pageX, item.pageY, item.series.label + " at " + formattedTime   + " = " + y, "#DDDDDD");
        }
      } else {
        $("#tooltip").remove();
        previousPoint = null;
      }
    }
  });

  $('#graph').width($('#graph_bound').width());
  $('#graph').height($('#graph_bound').height());
  if (embed) $('#graph').height($(window).height());

  $(window).resize(function() {
    $('#graph').width($('#graph_bound').width());
    if (embed) $('#graph').height($(window).height());
    plot();
  });

  // Graph selections
  $("#graph").bind("plotselected", function (event, ranges) {
     view.start = ranges.xaxis.from;
     view.end = ranges.xaxis.to;
     vis_feed_data();
  });

  // Navigation actions
  $("#zoomout").click(function () {view.zoomout(); vis_feed_data();});
  $("#zoomin").click(function () {view.zoomin(); vis_feed_data();});
  $('#right').click(function () {view.panright(); vis_feed_data();});
  $('#left').click(function () {view.panleft(); vis_feed_data();});
  $('.graph-time').click(function () {view.timewindow($(this).attr("time")); vis_feed_data();});

  // Navigation and zooming buttons for mouse and touch
  $("#graph").mouseenter(function() {
    $("#graph-navbar").show();
    $("#graph-tooltip").show();
    $("#graph-buttons").stop().fadeIn();
    $("#stats").stop().fadeIn();
  });
  $("#graph_bound").mouseleave(function() {
    $("#graph-buttons").stop().fadeOut();
    $("#stats").stop().fadeOut();
  });
  $("#graph").bind("touchstarted", function (event, pos) {
    $("#graph-navbar").hide();
    $("#graph-tooltip").hide();
    $("#graph-buttons").stop().fadeOut();
    $("#stats").stop().fadeOut();
  });
  $("#graph").bind("touchended", function (event, ranges) {
    $("#graph-buttons").stop().fadeIn();
    $("#stats").stop().fadeIn();
    view.start = ranges.xaxis.from;
    view.end = ranges.xaxis.to;
    vis_feed_data();
  });
}
<|endoftext|>


<commit_before>// @flow
/* eslint-env browser */
import ImagePicker from 'react-native-image-picker'
import React, {Component} from 'react'
import {Box, Icon, Input, Text, ClickableBox} from '../../../common-adapters'
import {globalMargins, globalStyles, globalColors} from '../../../styles'
import {isIOS} from '../../../constants/platform'

import type {AttachmentInput} from '../../../constants/chat'
import type {Props} from '.'

class ConversationInput extends Component<void, Props, void> {
  componentWillReceiveProps (nextProps: Props) {
    if (this.props.editingMessage !== nextProps.editingMessage) {
      if (nextProps.editingMessage && nextProps.editingMessage.type === 'Text') {
        this.props.setText(nextProps.editingMessage.message.stringValue())
        this.props.inputFocus()
      }
    }
  }

  _onBlur = () => {
    if (this.props.editingMessage) {
      this.props.onShowEditor(null)
      this.props.setText('')
    }
  }

  _onSubmit = () => {
    const text = this.props.text
    if (!text) {
      return
    }

    if (this.props.isLoading) {
      console.log('Ignoring chat submit while still loading')
      return
    }

    this.props.setText('')
    this.props.inputClear()
    if (this.props.editingMessage) {
      this.props.onEditMessage(this.props.editingMessage, text)
    } else {
      this.props.onPostMessage(text)
    }
  }

  _openFilePicker = () => {
    ImagePicker.showImagePicker({}, response => {
      if (response.didCancel) {
        return
      }
      const filename = isIOS ? response.uri.replace('file://', '') : response.path
      const conversationIDKey = this.props.selectedConversationIDKey
      if (!response.didCancel && conversationIDKey) {
        const input: AttachmentInput = {
          conversationIDKey,
          filename,
          title: response.fileName,
          type: 'Image',
        }
        this.props.onAttach([input])
      }
    })
  }

  render () {
    // Auto-growing multiline doesn't work smoothly on Android yet.
    const multilineOpts = isIOS ? {rowsMax: 3, rowsMin: 1} : {rowsMax: 2, rowsMin: 2}

    return (
      <Box style={styleContainer}>
        <Input
          autoCorrect={true}
          autoCapitalize='sentences'
          autoFocus={false}
          hideUnderline={true}
          hintText='Write a message'
          inputStyle={styleInputText}
          multiline={true}
          onBlur={this._onBlur}
          onChangeText={this.props.setText}
          ref={this.props.inputSetRef}
          small={true}
          style={styleInput}
          value={this.props.text}
          {...multilineOpts}
        />
        <Action text={this.props.text} onSubmit={this._onSubmit} editingMessage={this.props.editingMessage} openFilePicker={this._openFilePicker} isLoading={this.props.isLoading} />
      </Box>
    )
  }
}

const Action = ({text, onSubmit, editingMessage, openFilePicker, isLoading}) => (
  text ? (
    <ClickableBox feedback={false} onClick={onSubmit}>
      <Box style={styleActionText}>
        <Text type='BodyBigLink' style={{...(isLoading ? {color: globalColors.grey} : {})}}>{editingMessage ? 'Save' : 'Send'}</Text>
      </Box>
    </ClickableBox>
  ) : (
    <Icon onClick={openFilePicker} type='iconfont-camera' style={styleActionButton} />
  )
)

const styleActionText = {
  ...globalStyles.flexBoxColumn,
  alignItems: 'center',
  alignSelf: isIOS ? 'flex-end' : 'center',
  justifyContent: 'center',
  paddingBottom: 5,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.small,
  paddingTop: 5,
}

const styleActionButton = {
  alignSelf: isIOS ? 'flex-end' : 'center',
  paddingBottom: 5,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.small,
}

const styleInputText = {
  ...globalStyles.fontRegular,
  fontSize: 14,
  lineHeight: 18,
}

const styleContainer = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  borderTopColor: globalColors.black_05,
  borderTopWidth: 1,
  ...(isIOS
    ? {
      paddingBottom: globalMargins.tiny,
      paddingTop: globalMargins.tiny,
    }
    : {
      minHeight: 48,
    }
  )
}

const styleInput = {
  flex: 1,
  marginLeft: globalMargins.tiny,
}

export default ConversationInput
<commit_msg>Fix trailing-comma CI break<commit_after>// @flow
/* eslint-env browser */
import ImagePicker from 'react-native-image-picker'
import React, {Component} from 'react'
import {Box, Icon, Input, Text, ClickableBox} from '../../../common-adapters'
import {globalMargins, globalStyles, globalColors} from '../../../styles'
import {isIOS} from '../../../constants/platform'

import type {AttachmentInput} from '../../../constants/chat'
import type {Props} from '.'

class ConversationInput extends Component<void, Props, void> {
  componentWillReceiveProps (nextProps: Props) {
    if (this.props.editingMessage !== nextProps.editingMessage) {
      if (nextProps.editingMessage && nextProps.editingMessage.type === 'Text') {
        this.props.setText(nextProps.editingMessage.message.stringValue())
        this.props.inputFocus()
      }
    }
  }

  _onBlur = () => {
    if (this.props.editingMessage) {
      this.props.onShowEditor(null)
      this.props.setText('')
    }
  }

  _onSubmit = () => {
    const text = this.props.text
    if (!text) {
      return
    }

    if (this.props.isLoading) {
      console.log('Ignoring chat submit while still loading')
      return
    }

    this.props.setText('')
    this.props.inputClear()
    if (this.props.editingMessage) {
      this.props.onEditMessage(this.props.editingMessage, text)
    } else {
      this.props.onPostMessage(text)
    }
  }

  _openFilePicker = () => {
    ImagePicker.showImagePicker({}, response => {
      if (response.didCancel) {
        return
      }
      const filename = isIOS ? response.uri.replace('file://', '') : response.path
      const conversationIDKey = this.props.selectedConversationIDKey
      if (!response.didCancel && conversationIDKey) {
        const input: AttachmentInput = {
          conversationIDKey,
          filename,
          title: response.fileName,
          type: 'Image',
        }
        this.props.onAttach([input])
      }
    })
  }

  render () {
    // Auto-growing multiline doesn't work smoothly on Android yet.
    const multilineOpts = isIOS ? {rowsMax: 3, rowsMin: 1} : {rowsMax: 2, rowsMin: 2}

    return (
      <Box style={styleContainer}>
        <Input
          autoCorrect={true}
          autoCapitalize='sentences'
          autoFocus={false}
          hideUnderline={true}
          hintText='Write a message'
          inputStyle={styleInputText}
          multiline={true}
          onBlur={this._onBlur}
          onChangeText={this.props.setText}
          ref={this.props.inputSetRef}
          small={true}
          style={styleInput}
          value={this.props.text}
          {...multilineOpts}
        />
        <Action text={this.props.text} onSubmit={this._onSubmit} editingMessage={this.props.editingMessage} openFilePicker={this._openFilePicker} isLoading={this.props.isLoading} />
      </Box>
    )
  }
}

const Action = ({text, onSubmit, editingMessage, openFilePicker, isLoading}) => (
  text ? (
    <ClickableBox feedback={false} onClick={onSubmit}>
      <Box style={styleActionText}>
        <Text type='BodyBigLink' style={{...(isLoading ? {color: globalColors.grey} : {})}}>{editingMessage ? 'Save' : 'Send'}</Text>
      </Box>
    </ClickableBox>
  ) : (
    <Icon onClick={openFilePicker} type='iconfont-camera' style={styleActionButton} />
  )
)

const styleActionText = {
  ...globalStyles.flexBoxColumn,
  alignItems: 'center',
  alignSelf: isIOS ? 'flex-end' : 'center',
  justifyContent: 'center',
  paddingBottom: 5,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.small,
  paddingTop: 5,
}

const styleActionButton = {
  alignSelf: isIOS ? 'flex-end' : 'center',
  paddingBottom: 5,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.small,
}

const styleInputText = {
  ...globalStyles.fontRegular,
  fontSize: 14,
  lineHeight: 18,
}

const styleContainer = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  borderTopColor: globalColors.black_05,
  borderTopWidth: 1,
  ...(isIOS
    ? {
      paddingBottom: globalMargins.tiny,
      paddingTop: globalMargins.tiny,
    }
    : {
      minHeight: 48,
    }
  ),
}

const styleInput = {
  flex: 1,
  marginLeft: globalMargins.tiny,
}

export default ConversationInput
<|endoftext|>


<commit_before>define(["vendors/go-dashboard/dashboard","vendors/go-dashboard/jquery.qrcode.min"], function(Dashboard, qrcode) {

    $.fn.dashboard = function (options) {
        var el = $(this).find(".dashboard");
        if (!$(el).data("dashboard")) {
            $(el).data("dashboard", true);

            var DashboardViewModel = new Dashboard();
            var __has_loaded = false;
            // catch click tab title
            $(el).find(".dashboard-scene>ul").click(function (event) {
                if ($(event.target).is("a")) {

                    if (!__has_loaded) {
                        var base_dir = window.location.pathname;
                        base_dir = base_dir.substring(0, base_dir.lastIndexOf("/") + 1)
                        $("html script[src]").each(function (i, e) {
                            var src = $(e).attr("src");
                            if (src) {
                                src = src.replace(/\\/g,"/");
                                src = src.replace("//","/");
                                src = src.replace("//","/");
                                if (src.substr(0, 1) != "/") {
                                    src = base_dir + src;
                                }
                                DashboardViewModel.availableScripts.push(src);
                            }
                        });
                        $("html link[href]").each(function (i, e) {
                            if ($(e).attr("rel") == "stylesheet" || $(e).attr("type") == "text/css") {
                                var src = $(e).attr("href");
                                if (src) {
                                    src = src.replace(/\\/g,"/");
                                    src = src.replace("//","/");
                                    src = src.replace("//","/");
                                    if (src.substr(0, 1) != "/") {
                                        src = base_dir + src;
                                    }
                                    DashboardViewModel.availableStyles.push(src);
                                }
                            }
                        });

                        var clean_html = function(_content){
                            var re = /").appendTo(el);
        }
        return $._delay(function (dfd) {
            /*
             //     anon       true, if the space may be omitted in anonymous function declarations
             //     bitwise    true, if bitwise operators should be allowed
             //     browser    true, if the standard browser globals should be predefined
             //     'continue' true, if the continuation statement should be tolerated
             //     css        true, if CSS workarounds should be tolerated
             //     debug      true, if debugger statements should be allowed
             //     devel      true, if logging should be allowed (console, alert, etc.)
             //     eqeq       true, if == should be allowed
             //     es5        true, if ES5 syntax should be allowed
             //     evil       true, if eval should be allowed
             //     forin      true, if for in statements need not filter
             //     fragment   true, if HTML fragments should be allowed
             //     indent     the indentation factor
             //     maxerr     the maximum number of errors to allow
             //     maxlen     the maximum length of a source line
             //     newcap     true, if constructor names capitalization is ignored
             //     node       true, if Node.js globals should be predefined
             //     nomen      true, if names may have dangling _
             //     on         true, if HTML event handlers should be allowed
             //     passfail   true, if the scan should stop on first error
             //     plusplus   true, if increment/decrement should be allowed
             //     properties true, if all property names must be declared with / *properties* /
             //     regexp     true, if the . should be allowed in regexp literals
             //     rhino      true, if the Rhino environment globals should be predefined
             //     undef      true, if variables can be declared out of order
             //     unparam    true, if unused parameters should be tolerated
             //     sloppy     true, if the 'use strict'; pragma is optional
             //     stupid     true, if really stupid practices are tolerated
             //     sub        true, if all forms of subscript notation are tolerated
             //     todo       true, if TODO comments are tolerated
             //     vars       true, if multiple var statements per function should be allowed
             //     white      true, if sloppy whitespace is tolerated
             //     windows    true, if MS Windows-specific globals should be predefined

             //      to be configured
             */

            var myResult = JSLINT(script_content, {"predef": []});
            if (myResult == false) {
                var myReport = JSLINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                dfd.resolve(messages);
            } else {
                dfd.resolve([]);
            }
        });
    };
    var create_jshint_report = function(el, script_content, callback){
        require(["vendors/go-jshint/jshint-1.1.0"], function(  ){
            var options = {};
            var globals = {};
            var success = JSHINT(script_content, options, globals);
            if (success == false) {
                var myReport = JSHINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                callback(messages);
            }
        });
    }
    var create_csslint_report = function (el, css_content) {

        var lib_src = "/js/vendors/go-csslint/csslint.js";
        if ($("body").find("script[src='" + lib_src + "']").length == 0) {
            $("").appendTo(el);
        }
        return $._delay(function (dfd) {

            var results = CSSLint.verify(css_content);
            var messages = new Array();

            for (i = 0, len = results.messages.length; i < len; i++) {
                var m = results.messages[i];
                messages.push({
                    "message": m.message + " (line " + m.line + ", col " + m.col + ")",
                    "level": m.type
                });
            }

            dfd.resolve(messages);
        });
    };

    return $
});
<commit_msg>It happens it was a bad idea...<commit_after>define(["vendors/go-dashboard/dashboard","vendors/go-dashboard/jquery.qrcode.min"], function(Dashboard, qrcode) {

    $.fn.dashboard = function (options) {
        var el = $(this).find(".dashboard");
        if (!$(el).data("dashboard")) {
            $(el).data("dashboard", true);

            var DashboardViewModel = new Dashboard();
            var __has_loaded = false;
            // catch click tab title
            $(el).find(".dashboard-scene>ul").click(function (event) {
                if ($(event.target).is("a")) {

                    if (!__has_loaded) {
                        var base_dir = window.location.pathname;
                        base_dir = base_dir.substring(0, base_dir.lastIndexOf("/") + 1)
                        $("html script[src]").each(function (i, e) {
                            var src = $(e).attr("src");
                            if (src) {
                                src = src.replace(/\\/g,"/");
                                src = src.replace("//","/");
                                src = src.replace("//","/");
                                if (src.substr(0, 1) != "/") {
                                    src = base_dir + src;
                                }
                                DashboardViewModel.availableScripts.push(src);
                            }
                        });
                        $("html link[href]").each(function (i, e) {
                            if ($(e).attr("rel") == "stylesheet" || $(e).attr("type") == "text/css") {
                                var src = $(e).attr("href");
                                if (src) {
                                    src = src.replace(/\\/g,"/");
                                    src = src.replace("//","/");
                                    src = src.replace("//","/");
                                    if (src.substr(0, 1) != "/") {
                                        src = base_dir + src;
                                    }
                                    DashboardViewModel.availableStyles.push(src);
                                }
                            }
                        });

                        var clean_html = function(_content){
                            var re = /").appendTo(el);
        }
        return $._delay(function (dfd) {
            /*
             //     anon       true, if the space may be omitted in anonymous function declarations
             //     bitwise    true, if bitwise operators should be allowed
             //     browser    true, if the standard browser globals should be predefined
             //     'continue' true, if the continuation statement should be tolerated
             //     css        true, if CSS workarounds should be tolerated
             //     debug      true, if debugger statements should be allowed
             //     devel      true, if logging should be allowed (console, alert, etc.)
             //     eqeq       true, if == should be allowed
             //     es5        true, if ES5 syntax should be allowed
             //     evil       true, if eval should be allowed
             //     forin      true, if for in statements need not filter
             //     fragment   true, if HTML fragments should be allowed
             //     indent     the indentation factor
             //     maxerr     the maximum number of errors to allow
             //     maxlen     the maximum length of a source line
             //     newcap     true, if constructor names capitalization is ignored
             //     node       true, if Node.js globals should be predefined
             //     nomen      true, if names may have dangling _
             //     on         true, if HTML event handlers should be allowed
             //     passfail   true, if the scan should stop on first error
             //     plusplus   true, if increment/decrement should be allowed
             //     properties true, if all property names must be declared with / *properties* /
             //     regexp     true, if the . should be allowed in regexp literals
             //     rhino      true, if the Rhino environment globals should be predefined
             //     undef      true, if variables can be declared out of order
             //     unparam    true, if unused parameters should be tolerated
             //     sloppy     true, if the 'use strict'; pragma is optional
             //     stupid     true, if really stupid practices are tolerated
             //     sub        true, if all forms of subscript notation are tolerated
             //     todo       true, if TODO comments are tolerated
             //     vars       true, if multiple var statements per function should be allowed
             //     white      true, if sloppy whitespace is tolerated
             //     windows    true, if MS Windows-specific globals should be predefined

             //      to be configured
             */

            var myResult = JSLINT(script_content, {"predef": []});
            if (myResult == false) {
                var myReport = JSLINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                dfd.resolve(messages);
            } else {
                dfd.resolve([]);
            }
        });
    };
    var create_jshint_report = function(el, script_content, callback){
        require(["vendors/go-jshint/jshint-1.1.0"], function(  ){
            var options = {};
            var globals = {};
            var success = JSHINT(script_content, options, globals);
            if (success == false) {
                var myReport = JSHINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                callback(messages);
            }
        });
    }
    var create_csslint_report = function (el, css_content) {

        var lib_src = "/js/vendors/go-csslint/csslint.js";
        if ($("body").find("script[src='" + lib_src + "']").length == 0) {
            $("").appendTo(el);
        }
        return $._delay(function (dfd) {

            var results = CSSLint.verify(css_content);
            var messages = new Array();

            for (i = 0, len = results.messages.length; i < len; i++) {
                var m = results.messages[i];
                messages.push({
                    "message": m.message + " (line " + m.line + ", col " + m.col + ")",
                    "level": m.type
                });
            }

            dfd.resolve(messages);
        });
    };

    return $
});
<|endoftext|>


<commit_before>Vps.SubmitAction = function(form, options){
    Vps.SubmitAction.superclass.constructor.call(this, form, options);
};

Ext.extend(Vps.SubmitAction, Ext.form.Action.Submit, {
    run : function(){
        if (!this.options.params) this.options.params = {};

        //manually add date-value and checkbox-state and clear name to submit it only once
        this.form.items.each(function(field) {
            if (Ext.form.DateField && field instanceof Ext.form.DateField && field.getValue() instanceof Date) {
                this.options.params[field.getName()] = field.getValue().dateFormat("Y-m-d");
            } else if (Ext.form.Checkbox && field instanceof Ext.form.Checkbox) {
                if (field.getValue()) {
                    this.options.params[field.getName()] = 1;
                } else {
                    this.options.params[field.getName()] = 0;
                }
            } else if (field.rendered && field.el.dom.type == 'file') {
                //überspringen, file-upload muss direkt submitted werden
                return;
            } else {
                var v = field.getValue();
                if (typeof v == 'object') {
                    v = Ext.encode(v);
                }
                this.options.params[field.getName()] = v;
            }
            if (field.rendered) {
                field.realName = field.getName();
                field.el.dom.name = '';
            }
        }, this);

        Vps.SubmitAction.superclass.run.call(this);

        //restore names
        this.form.items.each(function(field) {
            if (field.realName) {
                field.el.dom.name = field.realName;
                delete field.realName;
            }
        }, this);
    }
});
Ext.form.Action.ACTION_TYPES.submit = Vps.SubmitAction; //normale submit-aktion überschreiben
<commit_msg>zeilenumbruch<commit_after>Vps.SubmitAction = function(form, options){
    Vps.SubmitAction.superclass.constructor.call(this, form, options);
};

Ext.extend(Vps.SubmitAction, Ext.form.Action.Submit, {
    run : function(){
        if (!this.options.params) this.options.params = {};

        //manually add date-value and checkbox-state and clear name to submit it only once
        this.form.items.each(function(field) {
            if (field.disabled) {
                this.options.params[field.getName()] = null;
            } else if (Ext.form.DateField &&
                field instanceof Ext.form.DateField &&
                field.getValue() instanceof Date) {
                this.options.params[field.getName()] = field.getValue().dateFormat("Y-m-d");
            } else if (Ext.form.Checkbox && field instanceof Ext.form.Checkbox) {
                if (field.getValue()) {
                    this.options.params[field.getName()] = 1;
                } else {
                    this.options.params[field.getName()] = 0;
                }
            } else if (field.rendered && field.el.dom.type == 'file') {
                //überspringen, file-upload muss direkt submitted werden
                return;
            } else {
                var v = field.getValue();
                if (typeof v == 'object') {
                    v = Ext.encode(v);
                }
                this.options.params[field.getName()] = v;
            }
            if (field.rendered) {
                field.realName = field.getName();
                field.el.dom.name = '';
            }
        }, this);

        Vps.SubmitAction.superclass.run.call(this);

        //restore names
        this.form.items.each(function(field) {
            if (field.realName) {
                field.el.dom.name = field.realName;
                delete field.realName;
            }
        }, this);
    }
});
Ext.form.Action.ACTION_TYPES.submit = Vps.SubmitAction; //normale submit-aktion überschreiben
<|endoftext|>


<commit_before>require('ember-data/serializers/rest_serializer');

/**
  @module ember-data
*/

var get = Ember.get, isNone = Ember.isNone;

DS.ActiveModelSerializer = DS.RESTSerializer.extend({
  // SERIALIZE

  /**
    Converts camelcased attributes to underscored when serializing.

    @method keyForAttribute
    @param {String} attribute
    @returns String
  */
  keyForAttribute: function(attr) {
    return Ember.String.decamelize(attr);
  },

  /**
    Underscores relationship names and appends "_id" or "_ids" when serializing
    relationship keys.

    @method keyForRelationship
    @param {String} key
    @param {String} kind
    @returns String
  */
  keyForRelationship: function(key, kind) {
    key = Ember.String.decamelize(key);
    if (kind === "belongsTo") {
      return key + "_id";
    } else if (kind === "hasMany") {
      return Ember.String.singularize(key) + "_ids";
    } else {
      return key;
    }
  },

  /**
    Does not serialize hasMany relationships
  */
  serializeHasMany: Ember.K,

  /**
    Underscores the JSON root keys when serializing.

    @method serializeIntoHash
    @param {Object} hash
    @param {subclass of DS.Model} type
    @param {DS.Model} record
    @param {Object} options
  */
  serializeIntoHash: function(data, type, record, options) {
    var root = Ember.String.decamelize(type.typeKey);
    data[root] = this.serialize(record, options);
  },

  /**
    Serializes a polymorphic type as a fully capitalized model name.

    @method serializePolymorphicType
    @param {DS.Model} record
    @param {Object} json
    @param relationship
  */
  serializePolymorphicType: function(record, json, relationship) {
    var key = relationship.key,
        belongsTo = get(record, key);
    key = this.keyForAttribute(key);
    json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
  },

  // EXTRACT

  /**
    Extracts the model typeKey from underscored root objects.

    @method typeForRoot
    @param {String} root
    @returns String the model's typeKey
  */
  typeForRoot: function(root) {
    var camelized = Ember.String.camelize(root);
    return Ember.String.singularize(camelized);
  },

  /**
    Normalize the polymorphic type from the JSON.

    Normalize:
    ```js
      {
        id: "1"
        minion: { type: "evil_minion", id: "12"}
      }
    ```

    To:
    ```js
      {
        id: "1"
        minion: { type: "evilMinion", id: "12"}
      }
    ```

    @method normalizeRelationships
    @private
  */
  normalizeRelationships: function(type, hash) {
    var payloadKey, payload, key, payloadsKey, payloads;
    var serializer = this;

    if (this.keyForRelationship) {
      type.eachRelationship(function(key, relationship) {
        if (relationship.options.polymorphic) {
          payloadKey = this.keyForAttribute(key);
          payload = hash[payloadKey];
          if (payload && payload.type) {
            payload.type = this.typeForRoot(payload.type);
          }
        } else {
          payloadKey = this.keyForRelationship(key, relationship.kind);
          payload = hash[payloadKey];
        }

        hash[key] = payload;

        if (key !== payloadKey) {
          delete hash[payloadKey];
        }
      }, this);
    }
  }
});

<commit_msg>Remove unnecessary variables<commit_after>require('ember-data/serializers/rest_serializer');

/**
  @module ember-data
*/

var get = Ember.get, isNone = Ember.isNone;

DS.ActiveModelSerializer = DS.RESTSerializer.extend({
  // SERIALIZE

  /**
    Converts camelcased attributes to underscored when serializing.

    @method keyForAttribute
    @param {String} attribute
    @returns String
  */
  keyForAttribute: function(attr) {
    return Ember.String.decamelize(attr);
  },

  /**
    Underscores relationship names and appends "_id" or "_ids" when serializing
    relationship keys.

    @method keyForRelationship
    @param {String} key
    @param {String} kind
    @returns String
  */
  keyForRelationship: function(key, kind) {
    key = Ember.String.decamelize(key);
    if (kind === "belongsTo") {
      return key + "_id";
    } else if (kind === "hasMany") {
      return Ember.String.singularize(key) + "_ids";
    } else {
      return key;
    }
  },

  /**
    Does not serialize hasMany relationships
  */
  serializeHasMany: Ember.K,

  /**
    Underscores the JSON root keys when serializing.

    @method serializeIntoHash
    @param {Object} hash
    @param {subclass of DS.Model} type
    @param {DS.Model} record
    @param {Object} options
  */
  serializeIntoHash: function(data, type, record, options) {
    var root = Ember.String.decamelize(type.typeKey);
    data[root] = this.serialize(record, options);
  },

  /**
    Serializes a polymorphic type as a fully capitalized model name.

    @method serializePolymorphicType
    @param {DS.Model} record
    @param {Object} json
    @param relationship
  */
  serializePolymorphicType: function(record, json, relationship) {
    var key = relationship.key,
        belongsTo = get(record, key);
    key = this.keyForAttribute(key);
    json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
  },

  // EXTRACT

  /**
    Extracts the model typeKey from underscored root objects.

    @method typeForRoot
    @param {String} root
    @returns String the model's typeKey
  */
  typeForRoot: function(root) {
    var camelized = Ember.String.camelize(root);
    return Ember.String.singularize(camelized);
  },

  /**
    Normalize the polymorphic type from the JSON.

    Normalize:
    ```js
      {
        id: "1"
        minion: { type: "evil_minion", id: "12"}
      }
    ```

    To:
    ```js
      {
        id: "1"
        minion: { type: "evilMinion", id: "12"}
      }
    ```

    @method normalizeRelationships
    @private
  */
  normalizeRelationships: function(type, hash) {
    var payloadKey, payload, key;

    if (this.keyForRelationship) {
      type.eachRelationship(function(key, relationship) {
        if (relationship.options.polymorphic) {
          payloadKey = this.keyForAttribute(key);
          payload = hash[payloadKey];
          if (payload && payload.type) {
            payload.type = this.typeForRoot(payload.type);
          }
        } else {
          payloadKey = this.keyForRelationship(key, relationship.kind);
          payload = hash[payloadKey];
        }

        hash[key] = payload;

        if (key !== payloadKey) {
          delete hash[payloadKey];
        }
      }, this);
    }
  }
});

<|endoftext|>


<commit_before>import React from 'react';
import {Grid} from 'react-redux-grid';
import {compose} from 'recompose';
import {withTranslation} from 'react-i18next';
import PropTypes from 'prop-types';
import './styles.scss';

const LibraryFolderTree = ({
  tracks,
  localFolders
  /* sortBy,
  direction,
  handleSort,
  // estimateItemSize,
  t*/
}) => {
  const pathToEntryMap = {};
  let lastID = 0;
  function getEntryForPath(path) {
    if (pathToEntryMap[path] === undefined) {
      const newEntry = {
        id: ++lastID,
        path,
        name: path.split('/').slice(-1)[0],
        children: []
      };

      if (localFolders.includes(path)) {
        newEntry.parentId = -1;
      } else {
        const parentPath = path.split('/').slice(0, -1).join('/');
        if (parentPath.length === 0) {
          throw new Error('Parent path cannot be empty; the folder/file path-separators must not be normalized.');
        }
        const parent = getEntryForPath(parentPath);
        newEntry.parentId = parent.id;
        parent.children.push(newEntry);
      }

      pathToEntryMap[path] = newEntry;
    }
    return pathToEntryMap[path];
  }

  for (const track of tracks) {
    const folderPath = track.path.split('/').slice(0, -1).join('/');
    const folderEntry = getEntryForPath(folderPath);
    const newEntry = {
      id: ++lastID,
      parentId: folderEntry.id,
      path: track.path,
      name: track.name,
      album: track.album,
      artist: _.isString(track.artist) ? track.artist : track.artist.name
    };
    folderEntry.children.push(newEntry);
  }

  const data = {
    root: {
      id: -1,
      name: 'Root',
      children: Object.values(pathToEntryMap).filter(entry => localFolders.includes(entry.path))
    }
  };

  const treeConfig = {
    store: {
      getState: () => window.store.getState().reactReduxGrid,
      subscribe: window.store.subscribe,
      dispatch: window.store.dispatch
    },
    stateKey: 'local-library-folder-tree',
    gridType: 'tree', // either `tree` or `grid`,
    showTreeRootNode: false, // dont display root node of tree
    // pageSize: Object.values(pathToEntryMap).length,
    infinite: true,

    plugins: {
      COLUMN_MANAGER: {
        resizable: true,
        moveable: true
        /* sortable: {
          enabled: true,
          method: 'local',
          sortingSource: pagingDataSource
        }*/
      },
      EDITOR: {
        type: 'inline',
        enabled: true
      },
      PAGER: {
        enabled: false
      },
      LOADER: {
        enabled: true
      },
      SELECTION_MODEL: {
        mode: 'single'
      },
      ERROR_HANDLER: {
        defaultErrorMessage: 'AN ERROR OCURRED',
        enabled: true
      },
      GRID_ACTIONS: null,
      BULK_ACTIONS: {
        enabled: false
      }
    },
    columns: [
      {
        name: 'Name',
        width: '30%',
        className: 'additional-class',
        dataIndex: 'name',
        sortable: false,
        expandable: true
      },
      {
        name: 'Album',
        dataIndex: 'album',
        sortable: false,
        className: 'additional-class',
        defaultSortDirection: 'descend'
      },
      {
        name: 'Artist',
        dataIndex: 'artist',
        sortable: false,
        className: 'additional-class'
      }
    ],
    data
  };
  return <Grid {...treeConfig} />;
};

LibraryFolderTree.propTypes = {
  tracks: PropTypes.array,
  localFolders: PropTypes.arrayOf(PropTypes.string),
  sortBy: PropTypes.string,
  direction: PropTypes.string,
  handleSort: PropTypes.func
};

export default compose(
  withTranslation('library')
)(LibraryFolderTree);
<commit_msg>* Got the track context menu working, for the folder-tree view.<commit_after>import React from 'react';
import {Grid} from 'react-redux-grid';
import {compose} from 'recompose';
import {withTranslation} from 'react-i18next';
import PropTypes from 'prop-types';
import { ContextPopup, getThumbnail } from '@nuclear/ui';
import TrackPopupButtons from '../../../containers/TrackPopupButtons';
import './styles.scss';

const LibraryFolderTree = ({
  tracks,
  localFolders
  /* sortBy,
  direction,
  handleSort,
  // estimateItemSize,
  t*/
}) => {
  const pathToEntryMap = {};
  let lastID = 0;
  function getEntryByID(id) {
    return Object.values(pathToEntryMap).find(entry => entry.id === id);
  }
  function getEntryForFolder(path) {
    if (pathToEntryMap[path] === undefined) {
      const newEntry = {
        id: ++lastID,
        path,
        name: path.split('/').slice(-1)[0],
        children: []
      };

      if (localFolders.includes(path)) {
        newEntry.parentId = -1;
      } else {
        const parentPath = path.split('/').slice(0, -1).join('/');
        if (parentPath.length === 0) {
          throw new Error('Parent path cannot be empty; the folder/file path-separators must not be normalized.');
        }
        const parent = getEntryForFolder(parentPath);
        newEntry.parentId = parent.id;
        parent.children.push(newEntry);
      }

      pathToEntryMap[path] = newEntry;
    }
    return pathToEntryMap[path];
  }

  for (const track of tracks) {
    const folderPath = track.path.split('/').slice(0, -1).join('/');
    const folderEntry = getEntryForFolder(folderPath);
    const newEntry = {
      id: ++lastID,
      parentId: folderEntry.id,
      track,
      path: track.path,
      name: track.name,
      album: track.album,
      artist: _.isString(track.artist) ? track.artist : track.artist.name
    };
    pathToEntryMap[track.path] = newEntry;
    folderEntry.children.push(newEntry);
  }

  const data = {
    root: {
      id: -1,
      name: 'Root',
      children: Object.values(pathToEntryMap).filter(entry => localFolders.includes(entry.path))
    }
  };

  const treeConfig = {
    store: {
      getState: () => window.store.getState().reactReduxGrid,
      subscribe: window.store.subscribe,
      dispatch: window.store.dispatch
    },
    stateKey: 'local-library-folder-tree',
    gridType: 'tree', // either `tree` or `grid`,
    showTreeRootNode: false, // dont display root node of tree
    // pageSize: Object.values(pathToEntryMap).length,
    infinite: true,

    plugins: {
      COLUMN_MANAGER: {
        resizable: true,
        moveable: true
        /* sortable: {
          enabled: true,
          method: 'local',
          sortingSource: pagingDataSource
        }*/
      },
      EDITOR: {
        type: 'inline',
        enabled: true
      },
      PAGER: {
        enabled: false
      },
      LOADER: {
        enabled: true
      },
      SELECTION_MODEL: {
        mode: 'single'
      },
      ERROR_HANDLER: {
        defaultErrorMessage: 'AN ERROR OCURRED',
        enabled: true
      },
      GRID_ACTIONS: null,
      BULK_ACTIONS: {
        enabled: false
      },
      ROW: {
        enabled: true,
        renderer: ({rowProps, cells}) => {
          const entry = getEntryByID(cells[0].props.treeData.id);
          const rowUI = (
            <tr {...rowProps}>
              {cells}
            </tr>
          );
          if (entry.track) {
            return (
              <ContextPopup
                trigger={rowUI}
                key={'library-track-' + tracks.indexOf(entry.track)}
                thumb={getThumbnail(entry.track)}
                title={_.get(entry.track, ['name'])}
                artist={_.get(entry.track, ['artist', 'name'])}
              >
                <TrackPopupButtons track={entry.track} withAddToDownloads={false}/>
              </ContextPopup>
            );
          }
          return rowUI;
        }
      }
    },
    columns: [
      {
        name: 'Name',
        width: '30%',
        className: 'additional-class',
        dataIndex: 'name',
        sortable: false,
        expandable: true
      },
      {
        name: 'Album',
        dataIndex: 'album',
        sortable: false,
        className: 'additional-class',
        defaultSortDirection: 'descend'
      },
      {
        name: 'Artist',
        dataIndex: 'artist',
        sortable: false,
        className: 'additional-class'
      }
    ],
    data
  };
  return <Grid {...treeConfig} />;
};

LibraryFolderTree.propTypes = {
  tracks: PropTypes.array,
  localFolders: PropTypes.arrayOf(PropTypes.string),
  sortBy: PropTypes.string,
  direction: PropTypes.string,
  handleSort: PropTypes.func
};

export default compose(
  withTranslation('library')
)(LibraryFolderTree);
<|endoftext|>


<commit_before>import {
  withLitHtml,
  html,
  props,
  define,
  hasNativeShadowDomSupport,
} from '@bolt/core';
import { styleMap } from 'lit-html/directives/style-map.js';
import { classMap } from 'lit-html/directives/class-map.js';
import styles from './animation-wrapper.scss';

@define
class BoltAnimationWrapper extends withLitHtml() {
  static is = 'bolt-animation-wrapper';

  static props = {
    animTriggered: {
      ...props.boolean,
    },
    boltAnimationName: {
      ...props.string,
    },
    boltAnimationDuration: {
      ...props.number,
    },
    boltAnimationDelay: {
      ...props.number,
    },
    boltAnimationFunction: {
      ...props.string,
    },
  };

  constructor(self) {
    self = super(self);
    self.useShadow = hasNativeShadowDomSupport;
    return self;
  }

  emitTransitionEnd(event) {
    const myEvent = new CustomEvent('bolt:transitionend', {
      bubbles: true,
      composed: true,
      detail: {},
    });
    this.dispatchEvent(myEvent);
  }

  connectedCallback() {
    super.connectedCallback();
  }

  render() {
    const {
      animTriggered,
      boltAnimationName = 'none',
      boltAnimationDuration = 500,
      boltAnimationDelay = 0,
      boltAnimationFunction = 'linear',
    } = this.props;

    /** @type {CSSStyleDeclaration} */
    let style = {};

    const classes = {
      'c-bolt-animation-wrapper': true,
    };

    if (animTriggered && boltAnimationName !== 'none') {
      style = Object.assign({}, style, {
        animationName: boltAnimationName,
        animationDuration: `${boltAnimationDuration}ms`,
        animationDelay: `${boltAnimationDelay}ms`,
        animationTimingFunction: boltAnimationFunction,
        animationFillMode: 'forwards',
      });
    }

    return html`
      ${this.addStyles([styles])}
      <div
        id="${this.styleId}"
        class="${classMap(classes)}"
        is="shadow-root"
        data-anim-triggered=${animTriggered ? 'yes' : 'no'}
        @animationend=${event => this.emitTransitionEnd(event)}
        style=${styleMap(style)}
      >
        ${this.slot('default')}
      </div>
    `;
  }
}

export { BoltAnimationWrapper };
<commit_msg>fix: set animation defaults<commit_after>import {
  withLitHtml,
  html,
  props,
  define,
  hasNativeShadowDomSupport,
} from '@bolt/core';
import { styleMap } from 'lit-html/directives/style-map.js';
import { classMap } from 'lit-html/directives/class-map.js';
import styles from './animation-wrapper.scss';

@define
class BoltAnimationWrapper extends withLitHtml() {
  static is = 'bolt-animation-wrapper';

  static props = {
    animTriggered: {
      ...props.boolean,
    },
    boltAnimationName: {
      ...props.string,
      ...{ default: 'none' },
    },
    boltAnimationDuration: {
      ...props.number,
      ...{ default: 500 },
    },
    boltAnimationDelay: {
      ...props.number,
      ...{ default: 0 },
    },
    boltAnimationFunction: {
      ...props.string,
      ...{ default: 'ease-in' },
    },
  };

  constructor(self) {
    self = super(self);
    self.useShadow = hasNativeShadowDomSupport;
    return self;
  }

  emitTransitionEnd(event) {
    const myEvent = new CustomEvent('bolt:transitionend', {
      bubbles: true,
      composed: true,
      detail: {},
    });
    this.dispatchEvent(myEvent);
  }

  connectedCallback() {
    super.connectedCallback();
  }

  render() {
    const {
      animTriggered,
      boltAnimationName,
      boltAnimationDuration,
      boltAnimationDelay,
      boltAnimationFunction,
    } = this.props;

    /** @type {CSSStyleDeclaration} */
    let style = {};

    const classes = {
      'c-bolt-animation-wrapper': true,
    };

    if (animTriggered && boltAnimationName !== 'none') {
      style = Object.assign({}, style, {
        animationName: boltAnimationName,
        animationDuration: `${boltAnimationDuration}ms`,
        animationDelay: `${boltAnimationDelay}ms`,
        animationTimingFunction: boltAnimationFunction,
        animationFillMode: 'forwards',
      });
    }

    return html`
      ${this.addStyles([styles])}
      <div
        class="${classMap(classes)}"
        is="shadow-root"
        data-anim-triggered=${animTriggered ? 'yes' : 'no'}
        @animationend=${event => this.emitTransitionEnd(event)}
        style=${styleMap(style)}
      >
        ${this.slot('default')}
      </div>
    `;
  }
}

export { BoltAnimationWrapper };
<|endoftext|>