<commit_before>import resolve from 'eslint-module-utils/resolve'
import docsUrl from '../docsUrl'

function checkImports(imported, context) {
  for (const [module, nodes] of imported.entries()) {
    if (nodes.length > 1) {
      const message = `'${module}' imported multiple times.`
      const [first, ...rest] = nodes
      const sourceCode = context.getSourceCode()
      const fix = getFix(first, rest, sourceCode)

      context.report({
        node: first.source,
        message,
        fix, // Attach the autofix (if any) to the first import.
      })

      for (const node of rest) {
        context.report({
          node: node.source,
          message,
        })
      }
    }
  }
}

function getFix(first, rest, sourceCode) {
  // Sorry ESLint <= 3 users, no autofix for you.
  if (typeof sourceCode.getCommentsBefore !== 'function') {
    return undefined
  }

  const defaultImportNames = new Set(
    [first, ...rest].map(getDefaultImportName).filter(Boolean)
  )

  // Bail if there are multiple different default import names – it's up to the
  // user to choose which one to keep.
  if (defaultImportNames.size > 1) {
    return undefined
  }

  // It's not obvious what the user wants to do with comments associated with
  // duplicate imports, so skip imports with comments when autofixing.
  const restWithoutComments = rest.filter(node => !(
      hasCommentBefore(node, sourceCode) ||
      hasCommentAfter(node, sourceCode) ||
      hasCommentInsideNonSpecifiers(node, sourceCode)
  ))

  const specifiers = restWithoutComments
    .map(node => {
      const tokens = sourceCode.getTokens(node)
      const openBrace = tokens.find(token => isPunctuator(token, '{'))
      const closeBrace = tokens.find(token => isPunctuator(token, '}'))

      if (openBrace == null || closeBrace == null) {
        return undefined
      }

      return {
        importNode: node,
        text: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]),
        hasTrailingComma: isPunctuator(sourceCode.getTokenBefore(closeBrace), ','),
        isEmpty: !hasSpecifiers(node),
      }
    })
    .filter(Boolean)

  const unnecessaryImports = restWithoutComments.filter(node =>
    !hasSpecifiers(node) &&
    !specifiers.some(specifier => specifier.importNode === node)
  )

  const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1
  const shouldAddSpecifiers = specifiers.length > 0
  const shouldRemoveUnnecessary = unnecessaryImports.length > 0

  if (!(shouldAddDefault || shouldAddSpecifiers || shouldRemoveUnnecessary)) {
    return undefined
  }

  return function* (fixer) {
    const tokens = sourceCode.getTokens(first)
    const openBrace = tokens.find(token => isPunctuator(token, '{'))
    const closeBrace = tokens.find(token => isPunctuator(token, '}'))
    const firstToken = sourceCode.getFirstToken(first)
    const [defaultImportName] = defaultImportNames

    const firstHasTrailingComma =
      closeBrace != null &&
      isPunctuator(sourceCode.getTokenBefore(closeBrace), ',')
    const firstIsEmpty = !hasSpecifiers(first)

    const [specifiersText] = specifiers.reduce(
      ([result, needsComma], specifier) => {
        return [
          needsComma && !specifier.isEmpty
            ? `${result},${specifier.text}`
            : `${result}${specifier.text}`,
          specifier.isEmpty ? needsComma : true,
        ]
      },
      ['', !firstHasTrailingComma && !firstIsEmpty]
    )

    if (shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
      // `import './foo'` → `import def, {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName}, {${specifiersText}} from`)
    } else if (shouldAddDefault && openBrace == null && !shouldAddSpecifiers) {
      // `import './foo'` → `import def from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName} from`)
    } else if (shouldAddDefault && openBrace != null && closeBrace != null) {
      // `import {...} from './foo'` → `import def, {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName},`)
      if (shouldAddSpecifiers) {
        // `import def, {...} from './foo'` → `import def, {..., ...} from './foo'`
        yield fixer.insertTextBefore(closeBrace, specifiersText)
      }
    } else if (!shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
      // `import './foo'` → `import {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`)
    } else if (!shouldAddDefault && openBrace != null && closeBrace != null) {
      // `import {...} './foo'` → `import {..., ...} from './foo'`
      yield fixer.insertTextBefore(closeBrace, specifiersText)
    }

    // Remove imports whose specifiers have been moved into the first import.
    for (const specifier of specifiers) {
      yield fixer.remove(specifier.importNode)
    }

    // Remove imports whose default import has been moved to the first import,
    // and side-effect-only imports that are unnecessary due to the first
    // import.
    for (const node of unnecessaryImports) {
      yield fixer.remove(node)
    }
  }
}

function isPunctuator(node, value) {
  return node.type === 'Punctuator' && node.value === value
}

// Get the name of the default import of `node`, if any.
function getDefaultImportName(node) {
  const defaultSpecifier = node.specifiers
    .find(specifier => specifier.type === 'ImportDefaultSpecifier')
  return defaultSpecifier != null ? defaultSpecifier.local.name : undefined
}

// Checks whether `node` has any non-default specifiers.
function hasSpecifiers(node) {
  const specifiers = node.specifiers
    .filter(specifier => specifier.type === 'ImportSpecifier')
  return specifiers.length > 0
}

// Checks whether `node` has a comment (that ends) on the previous line or on
// the same line as `node` (starts).
function hasCommentBefore(node, sourceCode) {
  return sourceCode.getCommentsBefore(node)
    .some(comment => comment.loc.end.line >= node.loc.start.line - 1)
}

// Checks whether `node` has a comment (that starts) on the same line as `node`
// (ends).
function hasCommentAfter(node, sourceCode) {
  return sourceCode.getCommentsAfter(node)
    .some(comment => comment.loc.start.line === node.loc.end.line)
}

// Checks whether `node` has any comments _inside,_ except inside the `{...}`
// part (if any).
function hasCommentInsideNonSpecifiers(node, sourceCode) {
  const tokens = sourceCode.getTokens(node)
  const openBraceIndex = tokens.findIndex(token => isPunctuator(token, '{'))
  const closeBraceIndex = tokens.findIndex(token => isPunctuator(token, '}'))
  // Slice away the first token, since we're no looking for comments _before_
  // `node` (only inside). If there's a `{...}` part, look for comments before
  // the `{`, but not before the `}` (hence the `+1`s).
  const someTokens = openBraceIndex >= 0 && closeBraceIndex >= 0
    ? tokens.slice(1, openBraceIndex + 1).concat(tokens.slice(closeBraceIndex + 1))
    : tokens.slice(1)
  return someTokens.some(token => sourceCode.getCommentsBefore(token).length > 0)
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      url: docsUrl('no-duplicates'),
    },
    fixable: 'code',
  },

  create: function (context) {
    const imported = new Map()
    const typesImported = new Map()
    return {
      'ImportDeclaration': function (n) {
        // resolved path will cover aliased duplicates
        const resolvedPath = resolve(n.source.value, context) || n.source.value
        const importMap = n.importKind === 'type' ? typesImported : imported

        if (importMap.has(resolvedPath)) {
          importMap.get(resolvedPath).push(n)
        } else {
          importMap.set(resolvedPath, [n])
        }
      },

      'Program:exit': function () {
        checkImports(imported, context)
        checkImports(typesImported, context)
      },
    }
  },
}
<commit_msg>no-duplicates: Add comment explaining ESLint <= 3 autofix disabling<commit_after>import resolve from 'eslint-module-utils/resolve'
import docsUrl from '../docsUrl'

function checkImports(imported, context) {
  for (const [module, nodes] of imported.entries()) {
    if (nodes.length > 1) {
      const message = `'${module}' imported multiple times.`
      const [first, ...rest] = nodes
      const sourceCode = context.getSourceCode()
      const fix = getFix(first, rest, sourceCode)

      context.report({
        node: first.source,
        message,
        fix, // Attach the autofix (if any) to the first import.
      })

      for (const node of rest) {
        context.report({
          node: node.source,
          message,
        })
      }
    }
  }
}

function getFix(first, rest, sourceCode) {
  // Sorry ESLint <= 3 users, no autofix for you. Autofixing duplicate imports
  // requires multiple `fixer.whatever()` calls in the `fix`: We both need to
  // update the first one, and remove the rest. Support for multiple
  // `fixer.whatever()` in a single `fix` was added in ESLint 4.1.
  // `sourceCode.getCommentsBefore` was added in 4.0, so that's an easy thing to
  // check for.
  if (typeof sourceCode.getCommentsBefore !== 'function') {
    return undefined
  }

  const defaultImportNames = new Set(
    [first, ...rest].map(getDefaultImportName).filter(Boolean)
  )

  // Bail if there are multiple different default import names – it's up to the
  // user to choose which one to keep.
  if (defaultImportNames.size > 1) {
    return undefined
  }

  // It's not obvious what the user wants to do with comments associated with
  // duplicate imports, so skip imports with comments when autofixing.
  const restWithoutComments = rest.filter(node => !(
      hasCommentBefore(node, sourceCode) ||
      hasCommentAfter(node, sourceCode) ||
      hasCommentInsideNonSpecifiers(node, sourceCode)
  ))

  const specifiers = restWithoutComments
    .map(node => {
      const tokens = sourceCode.getTokens(node)
      const openBrace = tokens.find(token => isPunctuator(token, '{'))
      const closeBrace = tokens.find(token => isPunctuator(token, '}'))

      if (openBrace == null || closeBrace == null) {
        return undefined
      }

      return {
        importNode: node,
        text: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]),
        hasTrailingComma: isPunctuator(sourceCode.getTokenBefore(closeBrace), ','),
        isEmpty: !hasSpecifiers(node),
      }
    })
    .filter(Boolean)

  const unnecessaryImports = restWithoutComments.filter(node =>
    !hasSpecifiers(node) &&
    !specifiers.some(specifier => specifier.importNode === node)
  )

  const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1
  const shouldAddSpecifiers = specifiers.length > 0
  const shouldRemoveUnnecessary = unnecessaryImports.length > 0

  if (!(shouldAddDefault || shouldAddSpecifiers || shouldRemoveUnnecessary)) {
    return undefined
  }

  return function* (fixer) {
    const tokens = sourceCode.getTokens(first)
    const openBrace = tokens.find(token => isPunctuator(token, '{'))
    const closeBrace = tokens.find(token => isPunctuator(token, '}'))
    const firstToken = sourceCode.getFirstToken(first)
    const [defaultImportName] = defaultImportNames

    const firstHasTrailingComma =
      closeBrace != null &&
      isPunctuator(sourceCode.getTokenBefore(closeBrace), ',')
    const firstIsEmpty = !hasSpecifiers(first)

    const [specifiersText] = specifiers.reduce(
      ([result, needsComma], specifier) => {
        return [
          needsComma && !specifier.isEmpty
            ? `${result},${specifier.text}`
            : `${result}${specifier.text}`,
          specifier.isEmpty ? needsComma : true,
        ]
      },
      ['', !firstHasTrailingComma && !firstIsEmpty]
    )

    if (shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
      // `import './foo'` → `import def, {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName}, {${specifiersText}} from`)
    } else if (shouldAddDefault && openBrace == null && !shouldAddSpecifiers) {
      // `import './foo'` → `import def from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName} from`)
    } else if (shouldAddDefault && openBrace != null && closeBrace != null) {
      // `import {...} from './foo'` → `import def, {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` ${defaultImportName},`)
      if (shouldAddSpecifiers) {
        // `import def, {...} from './foo'` → `import def, {..., ...} from './foo'`
        yield fixer.insertTextBefore(closeBrace, specifiersText)
      }
    } else if (!shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
      // `import './foo'` → `import {...} from './foo'`
      yield fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`)
    } else if (!shouldAddDefault && openBrace != null && closeBrace != null) {
      // `import {...} './foo'` → `import {..., ...} from './foo'`
      yield fixer.insertTextBefore(closeBrace, specifiersText)
    }

    // Remove imports whose specifiers have been moved into the first import.
    for (const specifier of specifiers) {
      yield fixer.remove(specifier.importNode)
    }

    // Remove imports whose default import has been moved to the first import,
    // and side-effect-only imports that are unnecessary due to the first
    // import.
    for (const node of unnecessaryImports) {
      yield fixer.remove(node)
    }
  }
}

function isPunctuator(node, value) {
  return node.type === 'Punctuator' && node.value === value
}

// Get the name of the default import of `node`, if any.
function getDefaultImportName(node) {
  const defaultSpecifier = node.specifiers
    .find(specifier => specifier.type === 'ImportDefaultSpecifier')
  return defaultSpecifier != null ? defaultSpecifier.local.name : undefined
}

// Checks whether `node` has any non-default specifiers.
function hasSpecifiers(node) {
  const specifiers = node.specifiers
    .filter(specifier => specifier.type === 'ImportSpecifier')
  return specifiers.length > 0
}

// Checks whether `node` has a comment (that ends) on the previous line or on
// the same line as `node` (starts).
function hasCommentBefore(node, sourceCode) {
  return sourceCode.getCommentsBefore(node)
    .some(comment => comment.loc.end.line >= node.loc.start.line - 1)
}

// Checks whether `node` has a comment (that starts) on the same line as `node`
// (ends).
function hasCommentAfter(node, sourceCode) {
  return sourceCode.getCommentsAfter(node)
    .some(comment => comment.loc.start.line === node.loc.end.line)
}

// Checks whether `node` has any comments _inside,_ except inside the `{...}`
// part (if any).
function hasCommentInsideNonSpecifiers(node, sourceCode) {
  const tokens = sourceCode.getTokens(node)
  const openBraceIndex = tokens.findIndex(token => isPunctuator(token, '{'))
  const closeBraceIndex = tokens.findIndex(token => isPunctuator(token, '}'))
  // Slice away the first token, since we're no looking for comments _before_
  // `node` (only inside). If there's a `{...}` part, look for comments before
  // the `{`, but not before the `}` (hence the `+1`s).
  const someTokens = openBraceIndex >= 0 && closeBraceIndex >= 0
    ? tokens.slice(1, openBraceIndex + 1).concat(tokens.slice(closeBraceIndex + 1))
    : tokens.slice(1)
  return someTokens.some(token => sourceCode.getCommentsBefore(token).length > 0)
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      url: docsUrl('no-duplicates'),
    },
    fixable: 'code',
  },

  create: function (context) {
    const imported = new Map()
    const typesImported = new Map()
    return {
      'ImportDeclaration': function (n) {
        // resolved path will cover aliased duplicates
        const resolvedPath = resolve(n.source.value, context) || n.source.value
        const importMap = n.importKind === 'type' ? typesImported : imported

        if (importMap.has(resolvedPath)) {
          importMap.get(resolvedPath).push(n)
        } else {
          importMap.set(resolvedPath, [n])
        }
      },

      'Program:exit': function () {
        checkImports(imported, context)
        checkImports(typesImported, context)
      },
    }
  },
}
<|endoftext|>


<commit_before>import { isUndefined, filter, flatten, chunk, times } from 'lodash';
import Promise from 'pinkie';
import Compiler from '../compiler';
import BrowserConnection from '../browser/connection';
import { GeneralError } from '../errors/runtime';
import browserProviderPool from '../browser/provider/pool';
import MESSAGE from '../errors/runtime/message';
import BrowserSet from './browser-set';
import TestedApp from './tested-app';
import parseFileList from '../utils/parse-file-list';

const DEFAULT_APP_INIT_DELAY = 1000;

export default class Bootstrapper {
    constructor (browserConnectionGateway) {
        this.browserConnectionGateway = browserConnectionGateway;

        this.concurrency                 = 1;
        this.sources                     = [];
        this.browsers                    = [];
        this.reporters                   = [];
        this.filter                      = null;
        this.appCommand                  = null;
        this.appInitDelay                = DEFAULT_APP_INIT_DELAY;
        this.disableTestSyntaxValidation = false;
    }

    static _splitBrowserInfo (browserInfo) {
        const remotes   = [];
        const automated = [];

        browserInfo.forEach(browser => {
            if (browser instanceof BrowserConnection)
                remotes.push(browser);
            else
                automated.push(browser);
        });

        return { remotes, automated };
    }

    async _getBrowserInfo () {
        if (!this.browsers.length)
            throw new GeneralError(MESSAGE.browserNotSet);

        const browserInfo = await Promise.all(this.browsers.map(browser => browserProviderPool.getBrowserInfo(browser)));

        return flatten(browserInfo);
    }

    _createAutomatedConnections (browserInfo) {
        if (!browserInfo)
            return [];

        return browserInfo
            .map(browser => times(this.concurrency, () => new BrowserConnection(this.browserConnectionGateway, browser)));
    }

    async _getBrowserConnections (browserInfo) {
        const { automated, remotes } = Bootstrapper._splitBrowserInfo(browserInfo);

        if (remotes && remotes.length % this.concurrency)
            throw new GeneralError(MESSAGE.cannotDivideRemotesCountByConcurrency);

        let browserConnections = this._createAutomatedConnections(automated);

        browserConnections = browserConnections.concat(chunk(remotes, this.concurrency));

        return await BrowserSet.from(browserConnections);
    }

    async _getTests () {
        if (!this.sources.length)
            throw new GeneralError(MESSAGE.testSourcesNotSet);

        const parsedFileList = await parseFileList(this.sources, process.cwd());
        const compiler       = new Compiler(parsedFileList, this.disableTestSyntaxValidation);
        let tests            = await compiler.getTests();

        const testsWithOnlyFlag = tests.filter(test => test.only);

        if (testsWithOnlyFlag.length)
            tests = testsWithOnlyFlag;

        if (this.filter)
            tests = tests.filter(test => this.filter(test.name, test.fixture.name, test.fixture.path, test.meta, test.fixture.meta));

        if (!tests.length)
            throw new GeneralError(MESSAGE.noTestsToRun);

        return tests;
    }

    _getReporterPlugins () {
        const stdoutReporters = filter(this.reporters, r => isUndefined(r.outStream) || r.outStream === process.stdout);

        if (stdoutReporters.length > 1)
            throw new GeneralError(MESSAGE.multipleStdoutReporters, stdoutReporters.map(r => r.name).join(', '));

        if (!this.reporters.length) {
            this.reporters.push({
                name:      'spec',
                outStream: process.stdout
            });
        }

        return this.reporters.map(({ name, outStream }) => {
            let pluginFactory = name;

            if (typeof pluginFactory !== 'function') {
                try {
                    pluginFactory = require('testcafe-reporter-' + name);
                }
                catch (err) {
                    throw new GeneralError(MESSAGE.cantFindReporterForAlias, name);
                }
            }

            return {
                plugin: pluginFactory(),
                outStream
            };
        });
    }

    async _startTestedApp () {
        if (this.appCommand) {
            const testedApp = new TestedApp();

            await testedApp.start(this.appCommand, this.appInitDelay);

            return testedApp;
        }

        return null;
    }

    _canUseParallelBootstrapping (browserInfo) {
        return browserInfo.every(browser => browser.provider.isLocalBrowser(null, browserInfo.browserName));
    }

    async _bootstrapSequence (browserInfo) {
        const tests       = await this._getTests();
        const testedApp   = await this._startTestedApp();
        const browserSet  = await this._getBrowserConnections(browserInfo);

        return { tests, testedApp, browserSet };
    }

    _wrapBootstrappingPromise (promise) {
        return promise
            .then(result => ({ error: null, result }))
            .catch(error => ({ result: null, error }));
    }

    async _handleBootstrappingError ([browserSetStatus, testsStatus, testedAppStatus]) {
        if (!browserSetStatus.error)
            await browserSetStatus.result.dispose();

        if (!testedAppStatus.error && testedAppStatus.result)
            await testedAppStatus.result.kill();

        if (testsStatus.error)
            throw testsStatus.error;
        else if (testedAppStatus.error)
            throw testedAppStatus.error;
        else
            throw browserSetStatus.error;
    }

    async _bootstrapParallel (browserInfo) {
        let bootstrappingPromises = [
            this._getBrowserConnections(browserInfo),
            this._getTests(),
            this._startTestedApp()
        ];

        bootstrappingPromises = bootstrappingPromises.map(promise => this._wrapBootstrappingPromise(promise));

        const bootstrappingStatuses = await Promise.all(bootstrappingPromises);

        if (bootstrappingStatuses.some(status => status.error))
            await this._handleBootstrappingError(bootstrappingStatuses);

        const [browserSet, tests, testedApp] = bootstrappingStatuses.map(status => status.result);

        return { browserSet, tests, testedApp };
    }

    // API
    async createRunnableConfiguration () {
        const reporterPlugins = this._getReporterPlugins();

        // NOTE: If a user forgot to specify a browser, but has specified a path to tests, the specified path will be
        // considered as the browser argument, and the tests path argument will have the predefined default value.
        // It's very ambiguous for the user, who might be confused by compilation errors from an unexpected test.
        // So, we need to retrieve the browser aliases and paths before tests compilation.
        const browserInfo = await this._getBrowserInfo();

        if (this._canUseParallelBootstrapping(browserInfo))
            return { reporterPlugins, ...await this._bootstrapParallel(browserInfo) };

        return { reporterPlugins, ...await this._bootstrapSequence(browserInfo) };
    }
}
<commit_msg>Fix parallel bootstrapping condition check (closes #3219) (#3221)<commit_after>import { isUndefined, filter, flatten, chunk, times } from 'lodash';
import Promise from 'pinkie';
import Compiler from '../compiler';
import BrowserConnection from '../browser/connection';
import { GeneralError } from '../errors/runtime';
import browserProviderPool from '../browser/provider/pool';
import MESSAGE from '../errors/runtime/message';
import BrowserSet from './browser-set';
import TestedApp from './tested-app';
import parseFileList from '../utils/parse-file-list';

const DEFAULT_APP_INIT_DELAY = 1000;

export default class Bootstrapper {
    constructor (browserConnectionGateway) {
        this.browserConnectionGateway = browserConnectionGateway;

        this.concurrency                 = 1;
        this.sources                     = [];
        this.browsers                    = [];
        this.reporters                   = [];
        this.filter                      = null;
        this.appCommand                  = null;
        this.appInitDelay                = DEFAULT_APP_INIT_DELAY;
        this.disableTestSyntaxValidation = false;
    }

    static _splitBrowserInfo (browserInfo) {
        const remotes   = [];
        const automated = [];

        browserInfo.forEach(browser => {
            if (browser instanceof BrowserConnection)
                remotes.push(browser);
            else
                automated.push(browser);
        });

        return { remotes, automated };
    }

    async _getBrowserInfo () {
        if (!this.browsers.length)
            throw new GeneralError(MESSAGE.browserNotSet);

        const browserInfo = await Promise.all(this.browsers.map(browser => browserProviderPool.getBrowserInfo(browser)));

        return flatten(browserInfo);
    }

    _createAutomatedConnections (browserInfo) {
        if (!browserInfo)
            return [];

        return browserInfo
            .map(browser => times(this.concurrency, () => new BrowserConnection(this.browserConnectionGateway, browser)));
    }

    async _getBrowserConnections (browserInfo) {
        const { automated, remotes } = Bootstrapper._splitBrowserInfo(browserInfo);

        if (remotes && remotes.length % this.concurrency)
            throw new GeneralError(MESSAGE.cannotDivideRemotesCountByConcurrency);

        let browserConnections = this._createAutomatedConnections(automated);

        browserConnections = browserConnections.concat(chunk(remotes, this.concurrency));

        return await BrowserSet.from(browserConnections);
    }

    async _getTests () {
        if (!this.sources.length)
            throw new GeneralError(MESSAGE.testSourcesNotSet);

        const parsedFileList = await parseFileList(this.sources, process.cwd());
        const compiler       = new Compiler(parsedFileList, this.disableTestSyntaxValidation);
        let tests            = await compiler.getTests();

        const testsWithOnlyFlag = tests.filter(test => test.only);

        if (testsWithOnlyFlag.length)
            tests = testsWithOnlyFlag;

        if (this.filter)
            tests = tests.filter(test => this.filter(test.name, test.fixture.name, test.fixture.path, test.meta, test.fixture.meta));

        if (!tests.length)
            throw new GeneralError(MESSAGE.noTestsToRun);

        return tests;
    }

    _getReporterPlugins () {
        const stdoutReporters = filter(this.reporters, r => isUndefined(r.outStream) || r.outStream === process.stdout);

        if (stdoutReporters.length > 1)
            throw new GeneralError(MESSAGE.multipleStdoutReporters, stdoutReporters.map(r => r.name).join(', '));

        if (!this.reporters.length) {
            this.reporters.push({
                name:      'spec',
                outStream: process.stdout
            });
        }

        return this.reporters.map(({ name, outStream }) => {
            let pluginFactory = name;

            if (typeof pluginFactory !== 'function') {
                try {
                    pluginFactory = require('testcafe-reporter-' + name);
                }
                catch (err) {
                    throw new GeneralError(MESSAGE.cantFindReporterForAlias, name);
                }
            }

            return {
                plugin: pluginFactory(),
                outStream
            };
        });
    }

    async _startTestedApp () {
        if (this.appCommand) {
            const testedApp = new TestedApp();

            await testedApp.start(this.appCommand, this.appInitDelay);

            return testedApp;
        }

        return null;
    }

    async _canUseParallelBootstrapping (browserInfo) {
        const isLocalPromises = browserInfo.map(browser => browser.provider.isLocalBrowser(null, browserInfo.browserName));
        const isLocalBrowsers = await Promise.all(isLocalPromises);

        return isLocalBrowsers.every(result => result);
    }

    async _bootstrapSequence (browserInfo) {
        const tests       = await this._getTests();
        const testedApp   = await this._startTestedApp();
        const browserSet  = await this._getBrowserConnections(browserInfo);

        return { tests, testedApp, browserSet };
    }

    _wrapBootstrappingPromise (promise) {
        return promise
            .then(result => ({ error: null, result }))
            .catch(error => ({ result: null, error }));
    }

    async _handleBootstrappingError ([browserSetStatus, testsStatus, testedAppStatus]) {
        if (!browserSetStatus.error)
            await browserSetStatus.result.dispose();

        if (!testedAppStatus.error && testedAppStatus.result)
            await testedAppStatus.result.kill();

        if (testsStatus.error)
            throw testsStatus.error;
        else if (testedAppStatus.error)
            throw testedAppStatus.error;
        else
            throw browserSetStatus.error;
    }

    async _bootstrapParallel (browserInfo) {
        let bootstrappingPromises = [
            this._getBrowserConnections(browserInfo),
            this._getTests(),
            this._startTestedApp()
        ];

        bootstrappingPromises = bootstrappingPromises.map(promise => this._wrapBootstrappingPromise(promise));

        const bootstrappingStatuses = await Promise.all(bootstrappingPromises);

        if (bootstrappingStatuses.some(status => status.error))
            await this._handleBootstrappingError(bootstrappingStatuses);

        const [browserSet, tests, testedApp] = bootstrappingStatuses.map(status => status.result);

        return { browserSet, tests, testedApp };
    }

    // API
    async createRunnableConfiguration () {
        const reporterPlugins = this._getReporterPlugins();

        // NOTE: If a user forgot to specify a browser, but has specified a path to tests, the specified path will be
        // considered as the browser argument, and the tests path argument will have the predefined default value.
        // It's very ambiguous for the user, who might be confused by compilation errors from an unexpected test.
        // So, we need to retrieve the browser aliases and paths before tests compilation.
        const browserInfo = await this._getBrowserInfo();

        if (await this._canUseParallelBootstrapping(browserInfo))
            return { reporterPlugins, ...await this._bootstrapParallel(browserInfo) };

        return { reporterPlugins, ...await this._bootstrapSequence(browserInfo) };
    }
}
<|endoftext|>


<commit_before>'use babel'

import {Emitter, CompositeDisposable} from 'sb-event-kit'
import {createServer} from 'ws'
import websocketPort from './lib/wport'
import Cache from './cache'
import getOptions from './opts'
import Log from './lib/log'

const debug = Log.bind(null, { name: 'bridge', icon: '🚃' })

export default new class Bridge {
  constructor() {
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()
    this.connections = new Set()
    this.server = null
    this.queue = []

    this.subscriptions.add(this.emitter)
  }
  activate() {
    this.server = createServer({
      port: websocketPort()
    }, connection => {
      this.connections.add(connection)
      connection.on('close', () => {
        this.connections.delete(connection)
      })
      connection.on('message', (data, flags) => {
        if (flags.binary) {
          // Ignore binary
          debug('Ignoring message because its binary')
          return
        }
        try {
          const message = JSON.parse(data)
          this.emitter.emit(`message:${message._type}`, message)
        } catch (_) {
          debug('Error parsing bridge message')
        }
      })
      this.welcomeConnection(connection)
    })
  }
  welcomeConnection(connection) {
    connection.send(this.encodeMessage('flint:baseDir', {
      dir: Cache.baseDir()
    }))
    connection.send(this.encodeMessage('flint:opts', getOptions()))
    if (this.queue) {
      this.queue.forEach(function(contents) {
        connection.send(contents)
      })
      this.queue = []
    }
  }
  broadcast(type, message) {
    this.broadcastRaw(this.encodeMessage(type, message))
  }
  encodeMessage(type, message = {}) {
    return JSON.stringify(Object.assign({
      _type: type,
      timestamp: Date.now()
    }, message))
  }
  broadcastRaw(message) {
    const encodedMessage = message && typeof message === 'object' ? JSON.stringify(message) : message

    if (this.connections.size) {
      this.connections.forEach(function(connection) {
        connection.send(encodedMessage)
      })
    } else this.queue.push(encodedMessage)
  }
  onMessage(type, callback) {
    return this.emitter.on(`message:${type}`, callback)
  }
  dispose() {
    if (this.server) {
      this.server.close()
    }
    this.connections.clear()
    this.subscriptions.dispose()
  }
}
<commit_msg>:art: Add some debugging info<commit_after>'use babel'

import {Emitter, CompositeDisposable} from 'sb-event-kit'
import {createServer} from 'ws'
import websocketPort from './lib/wport'
import Cache from './cache'
import getOptions from './opts'
import Log from './lib/log'

const debug = Log.bind(null, { name: 'bridge', icon: '🚃' })

export default new class Bridge {
  constructor() {
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()
    this.connections = new Set()
    this.server = null
    this.queue = []

    this.subscriptions.add(this.emitter)
  }
  activate() {
    this.server = createServer({
      port: websocketPort()
    }, connection => {
      this.connections.add(connection)
      connection.on('close', () => {
        this.connections.delete(connection)
      })
      connection.on('message', (data, flags) => {
        if (flags.binary) {
          // Ignore binary
          debug('Ignoring message because its binary')
          return
        }
        try {
          const message = JSON.parse(data)
          debug('IN', message._type)
          this.emitter.emit(`message:${message._type}`, message)
        } catch (_) {
          debug('Error parsing bridge message')
        }
      })
      this.welcomeConnection(connection)
    })
  }
  welcomeConnection(connection) {
    connection.send(this.encodeMessage('flint:baseDir', {
      dir: Cache.baseDir()
    }))
    connection.send(this.encodeMessage('flint:opts', getOptions()))
    if (this.queue.length) {
      this.queue.forEach(function(contents) {
        connection.send(contents)
      })
      this.queue = []
    }
  }
  broadcast(type, message) {
    this.broadcastRaw(this.encodeMessage(type, message))
  }
  encodeMessage(type, message = {}) {
    debug('OUT', type)
    return JSON.stringify(Object.assign({
      _type: type,
      timestamp: Date.now()
    }, message))
  }
  broadcastRaw(message) {
    if (typeof message !== 'string') {
      throw new Error('Malformed message given')
    }

    if (this.connections.size) {
      this.connections.forEach(function(connection) {
        connection.send(message)
      })
    } else this.queue.push(message)
  }
  onMessage(type, callback) {
    return this.emitter.on(`message:${type}`, callback)
  }
  dispose() {
    if (this.server) {
      this.server.close()
    }
    this.connections.clear()
    this.subscriptions.dispose()
  }
}
<|endoftext|>


<commit_before>/* jshint node: true */
"use strict";
module.exports = function (config) {
  var buildName = process.env.TRAVIS_BUILD_NUMBER || process.env.USERNAME + "-" + Date.now();

  config.set({
    reporters: ["dots"],

    browserDisconnectTimeout : 10000, // default 2000
    browserDisconnectTolerance : 1, // default 0
    browserNoActivityTimeout : 4*60*1000, //default 10000
    captureTimeout : 4*60*1000, //default 60000

    browserStack: {
      project: "gurps.js",
      build: buildName,
      startTunnel: true,
      username: process.env.BROWSERSTACK_USERNAME,
      accessKey: process.env.BROWSERSTACK_KEY
    },
    customLaunchers: {
      bs_android: {
        base: "BrowserStack",
        os: "android",
        os_version: "4.2",
        device: "LG Nexus 4"
      },
      bs_ios: {
        base: "BrowserStack",
        os: "ios",
        os_version: "7.0",
        device: "iPhone 5S"
      },
      bs_opera_mobile: {
        base: "BrowserStack",
        os: "opera",
        os_version: "1280x800",
        device: "Samsung Galaxy Tab 10.1"
      },
      bs_opera_mac: {
        base: "BrowserStack",
        browser: "opera",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_safari_mac: {
        base: "BrowserStack",
        browser: "safari",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_chrome_mac: {
        base: "BrowserStack",
        browser: "chrome",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_firefox_mac: {
        base: "BrowserStack",
        browser: "firefox",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_ie7_winXP: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "7.0",
        os: "Windows",
        os_version: "XP"
      },
      bs_ie8_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "8.0",
        os: "Windows",
        os_version: "7"
      },
      bs_ie9_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "9.0",
        os: "Windows",
        os_version: "7"
      },
      bs_ie10_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "10.0",
        os: "Windows",
        os_version: "7"
      },
      "bs_ie11_win7": {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "11.0",
        os: "Windows",
        os_version: "7"
      },
    },
    browsers: [
      "bs_android",
      "bs_ios",
      "bs_opera_mobile",
      "bs_opera_mac",
      "bs_safari_mac",
      "bs_chrome_mac",
      "bs_firefox_mac",
      "bs_ie8_win7",
      "bs_ie9_win7",
      "bs_ie10_win7",
      "bs_ie11_win7"
    ]
  });
};
<commit_msg>removing ie8 support<commit_after>/* jshint node: true */
"use strict";
module.exports = function (config) {
  var buildName = process.env.TRAVIS_BUILD_NUMBER || process.env.USERNAME + "-" + Date.now();

  config.set({
    reporters: ["dots"],

    browserDisconnectTimeout : 10000, // default 2000
    browserDisconnectTolerance : 1, // default 0
    browserNoActivityTimeout : 4*60*1000, //default 10000
    captureTimeout : 4*60*1000, //default 60000

    browserStack: {
      project: "gurps.js",
      build: buildName,
      startTunnel: true,
      username: process.env.BROWSERSTACK_USERNAME,
      accessKey: process.env.BROWSERSTACK_KEY
    },
    customLaunchers: {
      bs_android: {
        base: "BrowserStack",
        os: "android",
        os_version: "4.2",
        device: "LG Nexus 4"
      },
      bs_ios: {
        base: "BrowserStack",
        os: "ios",
        os_version: "7.0",
        device: "iPhone 5S"
      },
      bs_opera_mobile: {
        base: "BrowserStack",
        os: "opera",
        os_version: "1280x800",
        device: "Samsung Galaxy Tab 10.1"
      },
      bs_opera_mac: {
        base: "BrowserStack",
        browser: "opera",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_safari_mac: {
        base: "BrowserStack",
        browser: "safari",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_chrome_mac: {
        base: "BrowserStack",
        browser: "chrome",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_firefox_mac: {
        base: "BrowserStack",
        browser: "firefox",
        os: "OS X",
        os_version: "Mavericks"
      },
      bs_ie7_winXP: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "7.0",
        os: "Windows",
        os_version: "XP"
      },
      bs_ie8_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "8.0",
        os: "Windows",
        os_version: "7"
      },
      bs_ie9_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "9.0",
        os: "Windows",
        os_version: "7"
      },
      bs_ie10_win7: {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "10.0",
        os: "Windows",
        os_version: "7"
      },
      "bs_ie11_win7": {
        base: "BrowserStack",
        browser: "ie",
        browser_version: "11.0",
        os: "Windows",
        os_version: "7"
      },
    },
    browsers: [
      "bs_android",
      "bs_ios",
      "bs_opera_mobile",
      "bs_opera_mac",
      "bs_safari_mac",
      "bs_chrome_mac",
      "bs_firefox_mac",
      "bs_ie9_win7",
      "bs_ie10_win7",
      "bs_ie11_win7"
    ]
  });
};
<|endoftext|>


<commit_before>'use strict';

(function poll() {
  if (typeof ynabToolKit !== 'undefined' && ynabToolKit.pageReady === true) {
    ynabToolKit.runningBalance = (function () {
      var currentlyRunning = false;

      function updateRunningBalanceCalculation(arrangedContent) {
        var i;
        var transaction;
        var runningBalance = 0;

        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');
        if (accountController.get('sortAscending')) {
          for (i = 0; i < arrangedContent.length; i++) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        } else {
          for (i = arrangedContent.length - 1; i >= 0; i--) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        }
      }

      function updateRunningBalanceColumn() {
        insertHeader();

        $('.ynab-grid-container .ynab-grid-body .ynab-grid-body-row').each(function (index, element) {
          var $element = $(element);
          if ($element.hasClass('ynab-grid-body-empty')) return;

          insertValue(element);
        });
      }

      function insertHeader() {
        if ($('.ynab-grid-header .ynab-toolkit-grid-cell-running-balance').length) return;

        var $headerRow = $('.ynab-grid-header');
        var runningBalanceHeader = $('.ynab-grid-cell-inflow', $headerRow).clone();
        runningBalanceHeader.removeClass('ynab-grid-cell-inflow');
        runningBalanceHeader.addClass('ynab-toolkit-grid-cell-running-balance');
        runningBalanceHeader.text('RUNNING BALANCE');
        runningBalanceHeader.insertAfter($('.ynab-grid-cell-inflow', $headerRow));

        if ($('.ynab-grid-body .ynab-grid-body-row-top .ynab-toolkit-grid-cell-running-balance').length) return;
        var $topRow = $('.ynab-grid-body-row-top');
        var topRowRunningBalance = $('.ynab-grid-cell-inflow', $topRow).clone();
        topRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        topRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');
        topRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $topRow));
      }

      function insertValue(element) {
        var $currentRow = $(element);
        var currentRowRunningBalance = $('.ynab-grid-cell-inflow', $currentRow).clone();
        currentRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        currentRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');

        var emberView = ynabToolKit.shared.getEmberView($currentRow.attr('id'));
        var transaction = emberView.get('content');
        var runningBalance = transaction.get('__ynabToolKitRunningBalance');
        var currencySpan = $('.user-data', currentRowRunningBalance);
        var currencySymbol = ynab.YNABSharedLib.currencyFormatter.getCurrency().currency_symbol;

        if (runningBalance < 0) {
          currencySpan.addClass('user-data currency negative');
        } else if (runningBalance > 0) {
          currencySpan.addClass('user-data currency positive');
        } else {
          currencySpan.addClass('user-data currency zero');
        }

        if (transaction.get('parentEntityId') !== null) {
          currencySpan.text('');
        } else {
          currencySpan.text(currencySymbol + ynab.formatCurrency(runningBalance));
        }

        currentRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $currentRow));
      }

      function onYnabGridyBodyChanged() {
        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');

        if (accountController.get('sortProperties').indexOf('date') !== -1) {
          updateRunningBalanceCalculation(accountController.get('arrangedContent'));
          updateRunningBalanceColumn();
        } else {
          $('.ynab-toolkit-grid-cell-running-balance').remove();
        }
      }

      function addDeadColumnToAddRows() {
        var $ynabGridAddRows = $('.ynab-grid-add-rows');

        if ($ynabGridAddRows.children().length) {
          if ($('.ynab-toolkit-grid-cell-running-balance', $ynabGridAddRows).length) return;
          $('
').insertAfter($('.ynab-grid-cell-inflow', $ynabGridAddRows));
        }
      }

      return {
        invoke: function () {
          currentlyRunning = true;

          Ember.run.next(function () {
            var applicationController = ynabToolKit.shared.containerLookup('controller:application');

            if (applicationController.get('currentPath').indexOf('accounts') > -1 && applicationController.get('selectedAccountId')) {
              onYnabGridyBodyChanged();
            }

            currentlyRunning = false;
          }, 50);
        },

        observe: function invoke(changedNodes) {
          if (changedNodes.has('ynab-grid-body') && !currentlyRunning) {
            ynabToolKit.runningBalance.invoke();
          }

          if (changedNodes.has('ynab-grid-cell ynab-grid-cell-accountName user-data') &&
              changedNodes.has('ynab-grid-cell ynab-grid-cell-date user-data')
          ) {
            addDeadColumnToAddRows();
          }
        }
      };
    }());

    ynabToolKit.runningBalance.invoke();
  } else {
    setTimeout(poll, 250);
  }
}());
<commit_msg>use shared currencyFormatter<commit_after>'use strict';

(function poll() {
  if (typeof ynabToolKit !== 'undefined' && ynabToolKit.pageReady === true) {
    ynabToolKit.runningBalance = (function () {
      var currentlyRunning = false;

      function updateRunningBalanceCalculation(arrangedContent) {
        var i;
        var transaction;
        var runningBalance = 0;

        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');
        if (accountController.get('sortAscending')) {
          for (i = 0; i < arrangedContent.length; i++) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        } else {
          for (i = arrangedContent.length - 1; i >= 0; i--) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        }
      }

      function updateRunningBalanceColumn() {
        insertHeader();

        $('.ynab-grid-container .ynab-grid-body .ynab-grid-body-row').each(function (index, element) {
          var $element = $(element);
          if ($element.hasClass('ynab-grid-body-empty')) return;

          insertValue(element);
        });
      }

      function insertHeader() {
        if ($('.ynab-grid-header .ynab-toolkit-grid-cell-running-balance').length) return;

        var $headerRow = $('.ynab-grid-header');
        var runningBalanceHeader = $('.ynab-grid-cell-inflow', $headerRow).clone();
        runningBalanceHeader.removeClass('ynab-grid-cell-inflow');
        runningBalanceHeader.addClass('ynab-toolkit-grid-cell-running-balance');
        runningBalanceHeader.text('RUNNING BALANCE');
        runningBalanceHeader.insertAfter($('.ynab-grid-cell-inflow', $headerRow));

        if ($('.ynab-grid-body .ynab-grid-body-row-top .ynab-toolkit-grid-cell-running-balance').length) return;
        var $topRow = $('.ynab-grid-body-row-top');
        var topRowRunningBalance = $('.ynab-grid-cell-inflow', $topRow).clone();
        topRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        topRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');
        topRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $topRow));
      }

      function insertValue(element) {
        var $currentRow = $(element);
        var currentRowRunningBalance = $('.ynab-grid-cell-inflow', $currentRow).clone();
        currentRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        currentRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');

        var emberView = ynabToolKit.shared.getEmberView($currentRow.attr('id'));
        var transaction = emberView.get('content');
        var runningBalance = transaction.get('__ynabToolKitRunningBalance');
        var currencySpan = $('.user-data', currentRowRunningBalance);

        if (runningBalance < 0) {
          currencySpan.addClass('user-data currency negative');
        } else if (runningBalance > 0) {
          currencySpan.addClass('user-data currency positive');
        } else {
          currencySpan.addClass('user-data currency zero');
        }

        if (transaction.get('parentEntityId') !== null) {
          currencySpan.text('');
        } else {
          currencySpan.text(ynabToolKit.shared.formatCurrency(runningBalance));
        }

        currentRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $currentRow));
      }

      function onYnabGridyBodyChanged() {
        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');

        if (accountController.get('sortProperties').indexOf('date') !== -1) {
          updateRunningBalanceCalculation(accountController.get('arrangedContent'));
          updateRunningBalanceColumn();
        } else {
          $('.ynab-toolkit-grid-cell-running-balance').remove();
        }
      }

      function addDeadColumnToAddRows() {
        var $ynabGridAddRows = $('.ynab-grid-add-rows');

        if ($ynabGridAddRows.children().length) {
          if ($('.ynab-toolkit-grid-cell-running-balance', $ynabGridAddRows).length) return;
          $('
').insertAfter($('.ynab-grid-cell-inflow', $ynabGridAddRows));
        }
      }

      return {
        invoke: function () {
          currentlyRunning = true;

          Ember.run.next(function () {
            var applicationController = ynabToolKit.shared.containerLookup('controller:application');

            if (applicationController.get('currentPath').indexOf('accounts') > -1 && applicationController.get('selectedAccountId')) {
              onYnabGridyBodyChanged();
            }

            currentlyRunning = false;
          }, 50);
        },

        observe: function invoke(changedNodes) {
          if (changedNodes.has('ynab-grid-body') && !currentlyRunning) {
            ynabToolKit.runningBalance.invoke();
          }

          if (changedNodes.has('ynab-grid-cell ynab-grid-cell-accountName user-data') &&
              changedNodes.has('ynab-grid-cell ynab-grid-cell-date user-data')
          ) {
            addDeadColumnToAddRows();
          }
        }
      };
    }());

    ynabToolKit.runningBalance.invoke();
  } else {
    setTimeout(poll, 250);
  }
}());
<|endoftext|>


<commit_before>import {getFaucet, getTestFaucet} from "../branding";

export const blockTradesAPIs = {
    BASE: "https://api.blocktrades.us/v2",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const openledgerAPIs = {
    BASE: "https://ol-api1.openledger.info/api/v0/ol/support",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount",
    RPC_URL: "https://openledger.info/api/"
};

export const rudexAPIs = {
    BASE: "https://gateway.rudex.org/api/rudex",
    COINS_LIST: "/coins",
    NEW_DEPOSIT_ADDRESS: "/simple-api/initiate-trade"
};

export const bitsparkAPIs = {
    BASE: "https://dex-api.bitspark.io/api/v1",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const cryptoBridgeAPIs = {
    BASE: "https://api.crypto-bridge.org/api/v1",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/wallets",
    MARKETS: "/markets",
    TRADING_PAIRS: "/trading-pairs"
};

export const citadelAPIs = {
    BASE: "https://citadel.li/trade",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const gdex2APIs = {
    BASE: "https://api.52bts.net/adjust",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs"
};

// Legacy Deposit/Withdraw
export const gdexAPIs = {
    BASE: "https://api.52bts.net",
    ASSET_LIST: "/gateway/asset/assetList",
    ASSET_DETAIL: "/gateway/asset/assetDetail",
    GET_DEPOSIT_ADDRESS: "/gateway/address/getAddress",
    CHECK_WITHDRAY_ADDRESS: "/gateway/address/checkAddress",
    DEPOSIT_RECORD_LIST: "/gateway/deposit/recordList",
    DEPOSIT_RECORD_DETAIL: "/gateway/deposit/recordDetail",
    WITHDRAW_RECORD_LIST: "/gateway/withdraw/recordList",
    WITHDRAW_RECORD_DETAIL: "/gateway/withdraw/recordDetail",
    GET_USER_INFO: "/gateway/user/getUserInfo",
    USER_AGREEMENT: "/gateway/user/isAgree",
    WITHDRAW_RULE: "/gateway/withdraw/rule"
};

export const xbtsxAPIs = {
    BASE: "https://apis.xbts.io/api/v1",
    COINS_LIST: "/coin"
};

export const nodeRegions = [
    // region of the node follows roughly https://en.wikipedia.org/wiki/Subregion#/media/File:United_Nations_geographical_subregions.png
    "Northern Europe",
    "Western Europe",
    "Southern Europe",
    "Eastern Europe",
    "Northern Asia",
    "Western Asia",
    "Southern Asia",
    "Eastern Asia",
    "Central Asia",
    "Southeastern Asia",
    "Australia and New Zealand",
    "Melanesia",
    "Polynesia",
    "Micronesia",
    "Northern Africa",
    "Western Africa",
    "Middle Africa",
    "Eastern Africa",
    "Southern Africa",
    "Northern America",
    "Central America",
    "Caribbean",
    "South America"
];

export const settingsAPIs = {
    // If you want a location to be translated, add the translation to settings in locale-xx.js
    // and use an object {translate: key} in WS_NODE_LIST
    DEFAULT_WS_NODE: "wss://fake.automatic-selection.com",
    WS_NODE_LIST: [
        {
            url: "wss://fake.automatic-selection.com",
            location: {translate: "settings.api_closest"}
        },
        {
            url: "ws://127.0.0.1:8090",
            location: "Locally hosted"
        },
        {
            url: "wss://nexus01.co.uk/ws",
            region: "Northern Europe",
            country: "England",
            location: "Gloucester",
            operator: "Witness: nexus01",
            contact: "telegram:rosswlkr"
        },
        {
            url: "wss://dex.iobanker.com/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Frankfurt",
            operator: "Witness: iobanker-core",
            contact: "email:admin@iobanker.com"
        },
        {
            url: "wss://api.bitshares.rocks/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "AZ, Phoenix",
            operator: "Vagnavs",
            contact: "telegram:Maximat00"
        },
        {
            url: "wss://ws.gdex.top",
            region: "Eastern Asia",
            country: "China",
            location: "Shanghai",
            operator: "Witness: gdex-witness",
            contact: "telegram:BrianZhang"
        },
        {
            url: "wss://api.bts.btspp.io:10100",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: btspp-witness",
            contact: "telegram:btsplusplus"
        },
        {
            url: "wss://api.bts.mobi/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "Virginia",
            operator: "Witness: in.abit",
            contact: "telegram:abitmore"
        },
        {
            url: "wss://btsws.roelandp.nl/ws",
            region: "Northern Europe",
            country: "Finland",
            location: "Helsinki",
            operator: "Witness: roelandp",
            contact: "telegram:roelandp"
        },
        {
            url: "wss://api.bitshares.bhuz.info/ws",
            region: "Northern America",
            country: "Canada",
            operator: "Witness: bhuz",
            contact: "telegram:bhuzor"
        },
        {
            url: "wss://api.btsgo.net/ws",
            region: "Southeastern Asia",
            location: "Singapore",
            operator: "Witness: xn-delegate",
            contact: "wechat:Necklace"
        },
        {
            url: "wss://bts.open.icowallet.net/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: magicwallet.witness",
            contact: "telegram:plus_wave"
        },
        {
            url: "wss://freedom.bts123.cc:15138/",
            region: "Eastern Asia",
            country: "China",
            location: "Changsha",
            operator: "Witness: delegate.freedom",
            contact: "telegram:eggplant"
        },
        {
            url: "wss://bitshares.bts123.cc:15138/",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: delegate.freedom",
            contact: "telegram:eggplant"
        },
        // TODO the owner said it's temporarily closed. Recheck later.
        //{
        //    url: "wss://api.bts.ai",
        //    region: "Eastern Asia",
        //    country: "China",
        //    location: "Beijing",
        //    operator: "Witness: witness.hiblockchain",
        //    contact: "telegram:vianull;wechat:strugglingl"
        //},
        {
            url: "wss://api.61bts.com",
            region: "Eastern Asia",
            country: "China",
            location: "Shandong",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api-us.61bts.com",
            region: "Northern America",
            country: "USA",
            location: "St. Louis",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api.dex.trading/",
            region: "Western Europe",
            country: "France",
            location: "Paris",
            operator: "Witness: zapata42-witness",
            contact: "telegram:Zapata_42"
        },
        {
            url: "wss://api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        {
            url: "wss://us.api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        {
            url: "wss://asia.api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        // TODO node is out of sync, recheck later
        //{
        //    url: "wss://citadel.li/node",
        //    region: "Western Europe",
        //    country: "Iceland",
        //    location: "Reykjavik",
        //    operator: "CITADEL",
        //    contact: "email:citadel.li;support"
        //},
        {
            url: "wss://api-bts.liondani.com/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Falkenstein",
            operator: "Witness: liondani",
            contact: "telegram:liondani"
        },
        {
            url: "wss://public.xbts.io/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://cloud.xbts.io/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "VG, Ashburn",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://node.xbts.io/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Falkenstein",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://api.pindd.club",
            region: "Northern America",
            country: "U.S.A.",
            location: "Seattle, CA",
            operator: "Witness: gbac-ety001",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://hongkong.bitshares.im/ws",
            region: "East Asia",
            country: "China",
            location: "Hong Kong",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://singapore.bitshares.im/ws",
            region: "Southeast Asia",
            country: "Singapore",
            location: "Singapore",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://newyork.bitshares.im/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "New York",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://api.btslebin.com/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Hong Kong",
            operator: "Witness: lebin-witness",
            contact: "telegram: lebinbit"
        },
        // Testnet
        {
            url: "wss://node.testnet.bitshares.eu",
            region: "TESTNET - Western Europe",
            country: "Germany",
            location: "Frankfurt",
            operator: "BitShares Europe",
            contact: "telegram:xeroc"
        },
        {
            url: "wss://testnet.dex.trading/",
            region: "TESTNET - Western Europe",
            country: "France",
            location: "Paris",
            operator: "Witness: zapata42-witness",
            contact: "telegram:Zapata_42"
        },
        {
            url: "wss://testnet.xbts.io/ws",
            region: "TESTNET - Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://testnet.bitshares.im/ws",
            region: "Eastern Asia",
            country: "Japan",
            location: "Tokyo",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://api-testnet.61bts.com/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Shandong",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api-us-testnet.61bts.com/ws",
            region: "Northern America",
            country: "USA",
            location: "St. Louis",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        }
    ],
    ES_WRAPPER_LIST: [
        {
            url: "https://api.bitshares.ws/openexplorer",
            region: "Western Europe",
            country: "Germany",
            operator: "blocksights.info",
            contact: "bitshares:blocksights"
        }
    ],
    DEFAULT_FAUCET: getFaucet().url,
    TESTNET_FAUCET: getTestFaucet().url
};
<commit_msg>add blocksights nodes<commit_after>import {getFaucet, getTestFaucet} from "../branding";

export const blockTradesAPIs = {
    BASE: "https://api.blocktrades.us/v2",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const openledgerAPIs = {
    BASE: "https://ol-api1.openledger.info/api/v0/ol/support",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount",
    RPC_URL: "https://openledger.info/api/"
};

export const rudexAPIs = {
    BASE: "https://gateway.rudex.org/api/rudex",
    COINS_LIST: "/coins",
    NEW_DEPOSIT_ADDRESS: "/simple-api/initiate-trade"
};

export const bitsparkAPIs = {
    BASE: "https://dex-api.bitspark.io/api/v1",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const cryptoBridgeAPIs = {
    BASE: "https://api.crypto-bridge.org/api/v1",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/wallets",
    MARKETS: "/markets",
    TRADING_PAIRS: "/trading-pairs"
};

export const citadelAPIs = {
    BASE: "https://citadel.li/trade",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs",
    DEPOSIT_LIMIT: "/deposit-limits",
    ESTIMATE_OUTPUT: "/estimate-output-amount",
    ESTIMATE_INPUT: "/estimate-input-amount"
};

export const gdex2APIs = {
    BASE: "https://api.52bts.net/adjust",
    COINS_LIST: "/coins",
    ACTIVE_WALLETS: "/active-wallets",
    TRADING_PAIRS: "/trading-pairs"
};

// Legacy Deposit/Withdraw
export const gdexAPIs = {
    BASE: "https://api.52bts.net",
    ASSET_LIST: "/gateway/asset/assetList",
    ASSET_DETAIL: "/gateway/asset/assetDetail",
    GET_DEPOSIT_ADDRESS: "/gateway/address/getAddress",
    CHECK_WITHDRAY_ADDRESS: "/gateway/address/checkAddress",
    DEPOSIT_RECORD_LIST: "/gateway/deposit/recordList",
    DEPOSIT_RECORD_DETAIL: "/gateway/deposit/recordDetail",
    WITHDRAW_RECORD_LIST: "/gateway/withdraw/recordList",
    WITHDRAW_RECORD_DETAIL: "/gateway/withdraw/recordDetail",
    GET_USER_INFO: "/gateway/user/getUserInfo",
    USER_AGREEMENT: "/gateway/user/isAgree",
    WITHDRAW_RULE: "/gateway/withdraw/rule"
};

export const xbtsxAPIs = {
    BASE: "https://apis.xbts.io/api/v1",
    COINS_LIST: "/coin"
};

export const nodeRegions = [
    // region of the node follows roughly https://en.wikipedia.org/wiki/Subregion#/media/File:United_Nations_geographical_subregions.png
    "Northern Europe",
    "Western Europe",
    "Southern Europe",
    "Eastern Europe",
    "Northern Asia",
    "Western Asia",
    "Southern Asia",
    "Eastern Asia",
    "Central Asia",
    "Southeastern Asia",
    "Australia and New Zealand",
    "Melanesia",
    "Polynesia",
    "Micronesia",
    "Northern Africa",
    "Western Africa",
    "Middle Africa",
    "Eastern Africa",
    "Southern Africa",
    "Northern America",
    "Central America",
    "Caribbean",
    "South America"
];

export const settingsAPIs = {
    // If you want a location to be translated, add the translation to settings in locale-xx.js
    // and use an object {translate: key} in WS_NODE_LIST
    DEFAULT_WS_NODE: "wss://fake.automatic-selection.com",
    WS_NODE_LIST: [
        {
            url: "wss://fake.automatic-selection.com",
            location: {translate: "settings.api_closest"}
        },
        {
            url: "ws://127.0.0.1:8090",
            location: "Locally hosted"
        },
        {
            url: "wss://nexus01.co.uk/ws",
            region: "Northern Europe",
            country: "England",
            location: "Gloucester",
            operator: "Witness: nexus01",
            contact: "telegram:rosswlkr"
        },
        {
            url: "wss://dex.iobanker.com/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Frankfurt",
            operator: "Witness: iobanker-core",
            contact: "email:admin@iobanker.com"
        },
        {
            url: "wss://api.bitshares.rocks/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "AZ, Phoenix",
            operator: "Vagnavs",
            contact: "telegram:Maximat00"
        },
        {
            url: "wss://ws.gdex.top",
            region: "Eastern Asia",
            country: "China",
            location: "Shanghai",
            operator: "Witness: gdex-witness",
            contact: "telegram:BrianZhang"
        },
        {
            url: "wss://api.bts.btspp.io:10100",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: btspp-witness",
            contact: "telegram:btsplusplus"
        },
        {
            url: "wss://api.bts.mobi/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "Virginia",
            operator: "Witness: in.abit",
            contact: "telegram:abitmore"
        },
        {
            url: "wss://btsws.roelandp.nl/ws",
            region: "Northern Europe",
            country: "Finland",
            location: "Helsinki",
            operator: "Witness: roelandp",
            contact: "telegram:roelandp"
        },
        {
            url: "wss://api.bitshares.bhuz.info/ws",
            region: "Northern America",
            country: "Canada",
            operator: "Witness: bhuz",
            contact: "telegram:bhuzor"
        },
        {
            url: "wss://api.btsgo.net/ws",
            region: "Southeastern Asia",
            location: "Singapore",
            operator: "Witness: xn-delegate",
            contact: "wechat:Necklace"
        },
        {
            url: "wss://bts.open.icowallet.net/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: magicwallet.witness",
            contact: "telegram:plus_wave"
        },
        {
            url: "wss://freedom.bts123.cc:15138/",
            region: "Eastern Asia",
            country: "China",
            location: "Changsha",
            operator: "Witness: delegate.freedom",
            contact: "telegram:eggplant"
        },
        {
            url: "wss://bitshares.bts123.cc:15138/",
            region: "Eastern Asia",
            country: "China",
            location: "Hangzhou",
            operator: "Witness: delegate.freedom",
            contact: "telegram:eggplant"
        },
        // TODO the owner said it's temporarily closed. Recheck later.
        //{
        //    url: "wss://api.bts.ai",
        //    region: "Eastern Asia",
        //    country: "China",
        //    location: "Beijing",
        //    operator: "Witness: witness.hiblockchain",
        //    contact: "telegram:vianull;wechat:strugglingl"
        //},
        {
            url: "wss://api.61bts.com",
            region: "Eastern Asia",
            country: "China",
            location: "Shandong",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api-us.61bts.com",
            region: "Northern America",
            country: "USA",
            location: "St. Louis",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api.dex.trading/",
            region: "Western Europe",
            country: "France",
            location: "Paris",
            operator: "Witness: zapata42-witness",
            contact: "telegram:Zapata_42"
        },
        {
            url: "wss://api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        {
            url: "wss://us.api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        {
            url: "wss://asia.api.bitshares.org/ws",
            region: "Western Europe",
            country: "France",
            location: "",
            operator: "bitshares.org",
            contact: ""
        },
        {
            url: "wss://eu.nodes.bitshares.ws",
            region: "Western Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: blocksights",
            contact: "telegram:blocksights"
        },
        {
            url: "wss://api-bts.liondani.com/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Falkenstein",
            operator: "Witness: liondani",
            contact: "telegram:liondani"
        },
        {
            url: "wss://public.xbts.io/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://cloud.xbts.io/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "VG, Ashburn",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://node.xbts.io/ws",
            region: "Western Europe",
            country: "Germany",
            location: "Falkenstein",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://api.pindd.club",
            region: "Northern America",
            country: "U.S.A.",
            location: "Seattle, CA",
            operator: "Witness: gbac-ety001",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://hongkong.bitshares.im/ws",
            region: "East Asia",
            country: "China",
            location: "Hong Kong",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://singapore.bitshares.im/ws",
            region: "Southeast Asia",
            country: "Singapore",
            location: "Singapore",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://newyork.bitshares.im/ws",
            region: "Northern America",
            country: "U.S.A.",
            location: "New York",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://api.btslebin.com/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Hong Kong",
            operator: "Witness: lebin-witness",
            contact: "telegram: lebinbit"
        },
        // Testnet
        {
            url: "wss://eu.nodes.testnet.bitshares.ws",
            region: "TESTNET - Western Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: blocksights",
            contact: "telegram:blocksights"
        },
        {
            url: "wss://testnet.dex.trading/",
            region: "TESTNET - Western Europe",
            country: "France",
            location: "Paris",
            operator: "Witness: zapata42-witness",
            contact: "telegram:Zapata_42"
        },
        {
            url: "wss://testnet.xbts.io/ws",
            region: "TESTNET - Europe",
            country: "Germany",
            location: "Nuremberg",
            operator: "Witness: xbtsio-wallet",
            contact: "telegram: xbtsio"
        },
        {
            url: "wss://testnet.bitshares.im/ws",
            region: "Eastern Asia",
            country: "Japan",
            location: "Tokyo",
            operator: "Witness: clone",
            contact: "telegram: yexiao"
        },
        {
            url: "wss://api-testnet.61bts.com/ws",
            region: "Eastern Asia",
            country: "China",
            location: "Shandong",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        },
        {
            url: "wss://api-us-testnet.61bts.com/ws",
            region: "Northern America",
            country: "USA",
            location: "St. Louis",
            operator: "Witness: liuye",
            contact: "email:work@akawa.ink"
        }
    ],
    ES_WRAPPER_LIST: [
        {
            url: "https://api.bitshares.ws/openexplorer",
            region: "Western Europe",
            country: "Germany",
            operator: "blocksights.info",
            contact: "telegram:blocksights"
        }
    ],
    DEFAULT_FAUCET: getFaucet().url,
    TESTNET_FAUCET: getTestFaucet().url
};
<|endoftext|>


<commit_before>videojs.addLanguage("de",{
 "Play": "Wiedergabe",
 "Pause": "Pause",
 "Current Time": "Aktueller Zeitpunkt",
 "Duration Time": "Dauer",
 "Remaining Time": "Verbleibende Zeit",
 "Stream Type": "Streamtyp",
 "LIVE": "LIVE",
 "Loaded": "Geladen",
 "Progress": "Status",
 "Fullscreen": "Vollbild",
 "Non-Fullscreen": "Kein Vollbild",
 "Mute": "Ton aus",
 "Unmuted": "Ton ein",
 "Playback Rate": "Wiedergabegeschwindigkeit",
 "Subtitles": "Untertitel",
 "subtitles off": "Untertitel aus",
 "Captions": "Untertitel",
 "captions off": "Untertitel aus",
 "Chapters": "Kapitel",
 "You aborted the video playback": "Sie haben die Videowiedergabe abgebrochen.",
 "A network error caused the video download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.",
 "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.",
 "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.",
 "No compatible source was found for this video.": "Für dieses Video wurde keine kompatible Quelle gefunden."
});<commit_msg>Delete de.js<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const JspFilterNodeRenderer = require('entoj-export-jsp').export.renderer.JspFilterNodeRenderer;
const co = require('co');

/**
 * Renders |settings filters
 */
class CmSettingsFilterRenderer extends JspFilterNodeRenderer
{
    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'export.renderer/CmSettingsFilterRenderer';
    }

    /**
     * @type {Boolean|Array}
     */
    get filterName()
    {
        return ['settings'];
    }

    /**
     * @return {Promise<String>}
     */
    render(node, configuration)
    {
        if (!node || !configuration)
        {
            return Promise.resolve('');
        }
        const scope = this;
        const promise = co(function*()
        {
            let result = '';
            const filter = node.find('FilterNode', { name: scope.filterName });
            if (!filter)
            {
                throw new Error('Could not locate settings filter in ' + node.type);
            }

            // Get data
            const key = yield configuration.renderer.renderNode(filter.value, configuration);
            const context = (filter.arguments.length)
                ? yield configuration.renderer.renderNode(filter.arguments[0].value, configuration)
                : 'self';

            // Render
            result+= 'bp:setting(' + context + ', ' + key + ')';

            return result;
        });
        return promise;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.CmSettingsFilterRenderer = CmSettingsFilterRenderer;
<commit_msg>fix(export.renderer.CmSettingsFilterRenderer): adds settingsService to support CM9<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const JspFilterNodeRenderer = require('entoj-export-jsp').export.renderer.JspFilterNodeRenderer;
const co = require('co');

/**
 * Renders |settings filters
 */
class CmSettingsFilterRenderer extends JspFilterNodeRenderer
{
    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'export.renderer/CmSettingsFilterRenderer';
    }

    /**
     * @type {Boolean|Array}
     */
    get filterName()
    {
        return ['settings'];
    }

    /**
     * @return {Promise<String>}
     */
    render(node, configuration)
    {
        if (!node || !configuration)
        {
            return Promise.resolve('');
        }
        const scope = this;
        const promise = co(function*()
        {
            let result = '';
            const filter = node.find('FilterNode', { name: scope.filterName });
            if (!filter)
            {
                throw new Error('Could not locate settings filter in ' + node.type);
            }

            // Get data
            const key = yield configuration.renderer.renderNode(filter.value, configuration);
            const context = (filter.arguments.length)
                ? yield configuration.renderer.renderNode(filter.arguments[0].value, configuration)
                : 'self';

            // Render
            result+= 'bp:setting(settingsService, ' + context + ', ' + key + ')';

            return result;
        });
        return promise;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.CmSettingsFilterRenderer = CmSettingsFilterRenderer;
<|endoftext|>


<commit_before>describe('Auth Token Module', function() {
  beforeEach(module('lk.authentication.token'));

  var $window,
      AuthTokenFactory;

  var key = 'auth-token';

  beforeEach(inject(function(_$window_, _AuthTokenFactory_){
    $window = _$window_;
    AuthTokenFactory = _AuthTokenFactory_;
  }));

  // because our test will be testing if we save a token, we want to make sure there
  // is no token already stored.  That way our test are more accurate.
  afterEach(function () {
    $window.localStorage.removeItem(key);
  });

  describe('AuthTokenFactory', function () {

    it('should store an authtoken in local storage when setToken function  is called', function () {
      AuthTokenFactory.setToken('thisWouldBeOurAuthToken');
      expect($window.localStorage.getItem(key)).toEqual('thisWouldBeOurAuthToken');
    });

    it('should remove token from local storage if setToken() is called with no params', function () {
      $window.localStorage.setItem(key, 'thisWouldBeOurAuthToken');
      AuthTokenFactory.setToken();
      expect($window.localStorage.getItem(key)).toEqual(null);
    });

    it('should return the token in local storage when calling getTokenif one is present', function () {
      $window.localStorage.setItem(key, 'thisWouldBeOurAuthToken');
      expect(AuthTokenFactory.getToken()).toEqual('thisWouldBeOurAuthToken');
    });

    it('should return nothing if token is absent', function () {
      AuthTokenFactory.setToken();
      expect(AuthTokenFactory.getToken()).toEqual(null);
    });
  });
});
<commit_msg>Add some tests to lk.authentication.token, regarding setting headers.<commit_after>describe('Auth Token Module', function() {
  beforeEach(module('lk.authentication.token'));

  var $window,
      $http,
      AuthTokenFactory;

  var key = 'auth-token';

  beforeEach(inject(function(_$window_, _$http_, _AuthTokenFactory_){
    $window = _$window_;
    $http = _$http_;
    AuthTokenFactory = _AuthTokenFactory_;
  }));

  // because our test will be testing if we save a token, we want to make sure there
  // is no token already stored.  That way our test are more accurate.
  afterEach(function () {
    $window.localStorage.removeItem(key);
  });

  describe('AuthTokenFactory', function () {

    it('should store an authtoken in local storage when setToken function is called', function () {
      AuthTokenFactory.setToken('thisWouldBeOurAuthToken');
      expect($window.localStorage.getItem(key)).toEqual('thisWouldBeOurAuthToken');
    });

    it('should set JWT header authorization when setToken() is called', function () {
      $window.localStorage.setItem(key, 'thisWouldBeOurAuthToken');
      AuthTokenFactory.setToken('thisWouldBeOurAuthToken');
      expect($http.defaults.headers.common.Authorization).toEqual('JWT thisWouldBeOurAuthToken');
    });

    it('should remove token from local storage if setToken is called with no params', function () {
      $window.localStorage.setItem(key, 'thisWouldBeOurAuthToken');
      AuthTokenFactory.setToken();
      expect($window.localStorage.getItem(key)).toEqual(null);
    });

    it('should delete JWT header authorization when setToken is called with no params', function () {
      $http.defaults.headers.common.Authorization = 'JWT ' + 'thisWouldBeOurAuthToken';
      AuthTokenFactory.setToken();
      expect($http.defaults.headers.common.Authorization).toEqual(undefined);
    });

    it('should return the token in local storage when calling getTokenif one is present', function () {
      $window.localStorage.setItem(key, 'thisWouldBeOurAuthToken');
      expect(AuthTokenFactory.getToken()).toEqual('thisWouldBeOurAuthToken');
    });

    it('should return nothing if token is absent', function () {
      AuthTokenFactory.setToken();
      expect(AuthTokenFactory.getToken()).toEqual(null);
    });
  });
});

<|endoftext|>


<commit_before>define([
	"Ember",
	"hbs!templates/components/list/InfiniteScrollComponent"
], function(
	Ember,
	layout
) {

	var get = Ember.get;
	var set = Ember.set;
	var alias = Ember.computed.alias;
	var or = Ember.computed.or;

	var $window = Ember.$( window );

	return Ember.Component.extend({
		layout: layout,
		tagName: "button",
		classNameBindings: [
			":btn",
			":btn-with-icon",
			":infinite-scroll-component",
			"hasFetchedAll:hidden"
		],
		attributeBindings: [
			"type",
			"locked:disabled"
		],

		scrollThreshold: 2 / 3,
		scrollListener : null,

		type: "button",
		locked: or( "isFetching", "hasFetchedAll" ),
		error: alias( "targetObject.fetchError" ),

		isFetching: alias( "targetObject.isFetching" ),
		hasFetchedAll: alias( "targetObject.hasFetchedAll" ),

		click: function() {
			var targetObject = get( this, "targetObject" );
			targetObject.send( "willFetchContent", true );
		},

		didInsertElement: function() {
			this._super();

			var $elem     = this.$().parent();
			var threshold = get( this, "scrollThreshold" );
			var target    = get( this, "targetObject" );
			var listener  = this.infiniteScroll.bind( this, $elem[ 0 ], threshold, target );

			set( this, "scrollListener", listener );

			$elem.on( "scroll", listener );
			$window.on( "resize", listener );
		},

		willDestroyElement: function() {
			this._super();

			var scrollListener = get( this, "scrollListener" );
			var $elem = this.$();
			$elem.parent().off( "scroll", scrollListener );
			$window.off( "resize", scrollListener );

			set( this, "scrollListener", null );
		},

		infiniteScroll: function( elem, percentage, target ) {
			var threshold = percentage * elem.clientHeight;
			var remaining = elem.scrollHeight - elem.clientHeight - elem.scrollTop;
			if ( remaining <= threshold ) {
				target.send( "willFetchContent" );
			}
		}
	});

});
<commit_msg>Fix parent elem logic in InfiniteScrollComponent<commit_after>define([
	"Ember",
	"hbs!templates/components/list/InfiniteScrollComponent"
], function(
	Ember,
	layout
) {

	var get = Ember.get;
	var set = Ember.set;
	var alias = Ember.computed.alias;
	var or = Ember.computed.or;

	var $ = Ember.$;
	var $window = $( window );

	var document = window.document;
	var documentElement = document.documentElement;
	var getComputedStyle = window.getComputedStyle;

	return Ember.Component.extend({
		layout: layout,
		tagName: "button",
		classNameBindings: [
			":btn",
			":btn-with-icon",
			":infinite-scroll-component",
			"hasFetchedAll:hidden"
		],
		attributeBindings: [
			"type",
			"locked:disabled"
		],

		scrollThreshold: 2 / 3,
		scrollListener : null,

		type: "button",
		locked: or( "isFetching", "hasFetchedAll" ),
		error: alias( "targetObject.fetchError" ),

		isFetching: alias( "targetObject.isFetching" ),
		hasFetchedAll: alias( "targetObject.hasFetchedAll" ),

		click: function() {
			var targetObject = get( this, "targetObject" );
			targetObject.send( "willFetchContent", true );
		},

		didInsertElement: function() {
			this._super.apply( this, arguments );

			// find first parent node which has a scroll bar
			var overflow;
			var parent = get( this, "element" );
			while ( ( parent = parent.parentNode ) ) {
				if ( parent.clientHeight >= parent.scrollHeight ) {
					continue;
				}
				overflow = getComputedStyle( parent, "" ).getPropertyValue( "overflow-y" );
				if ( overflow === "scroll" || overflow === "auto" ) {
					break;
				}
			}
			// fallback: use the main content element or the document body
			if ( parent === documentElement ) {
				parent = documentElement.querySelector( "main.content" ) || document.body;
			}

			var $parent   = $( parent );
			var threshold = get( this, "scrollThreshold" );
			var target    = get( this, "targetObject" );
			var listener  = this.infiniteScroll.bind( this, parent, threshold, target );

			set( this, "$parent", $parent );
			set( this, "scrollListener", listener );

			$parent.on( "scroll", listener );
			$window.on( "resize", listener );
		},

		willDestroyElement: function() {
			this._super.apply( this, arguments );

			var $parent = get( this, "$parent" );
			var scrollListener = get( this, "scrollListener" );
			$parent.off( "scroll", scrollListener );
			$window.off( "resize", scrollListener );
			set( this, "$parent", null );
			set( this, "scrollListener", null );
		},

		infiniteScroll: function( elem, percentage, target ) {
			var threshold = percentage * elem.clientHeight;
			var remaining = elem.scrollHeight - elem.clientHeight - elem.scrollTop;
			if ( remaining <= threshold ) {
				target.send( "willFetchContent" );
			}
		}
	});

});
<|endoftext|>