<commit_before>/* eslint import/namespace: [2, { allowComputed: true }]*/

import assert from 'assert';
import * as REGEX from './index.js';

const fixtures = {
  IMPORT: {
    valid: [
      'import foo from "foo"',
      'import _ from "foo"',
      'import $ from "foo"',
      'import * as bar from "foo"',
      'import { bar } from "foo"',
      'import { foo as bar } from "foo"',
      'import { foo, bar } from "foo"',
      'import { foo, bar as baz } from "foo"',
      'import foo, {bar} from "foo"',
      'import foo, * as bar from "foo"',
      'import "foo"',
      'import \nfoo \nfrom \n"foo"',
      'import {\nbar } from "foo"',
      'import { bar\n } from "foo"',
      'import { \nbar\n } from "foo"',
      'import foo\n, {bar} from "foo"',
      'import foo, {bar} \nfrom "foo"',
      'import foo, {\nbar} from "foo"',
      'import foo, {bar\n} from "foo"',
    ],
    invalid: [
      'import "fo o"',
      // TODO tweak IMPORT regexp so that invalid statements are not matched
      // 'import foo "foo"',
      // 'import from "foo"',
    ],
  },
  EXPORT: {
    valid: [
      'export * from "foo"',
      'export { foo, bar } from "foo"',
      'export { foo as bar} from "foo"',
      'export { foo, bar as baz } from "foo"',
      'export {\nbar } from "foo"',
      'export { bar\n } from "foo"',
      'export { \nbar\n } from "foo"',
    ],
    invalid: [
      'export * from "fo o"',
    ],
  },
  REQUIRE: {
    valid: [
      'require("foo")',
      'require( "foo" )',
      'require(  "foo"  )',
      'require(\t"foo"\t)',
      'require ("foo")',
      'var foo = require("foo")',
      'var $ = require("foo")',
      'var _ = require("foo")',
      ['var foo = require("foo")var bar = require("bar")', ['foo', 'bar']],
      ['require("foo")require("bar");', ['foo', 'bar']],
      ['require("foo")require("bar");', ['foo', 'bar']],
      ['var foo = require("foo")require("bar");', ['foo', 'bar']],
      ['var foo = require("foo")require("bar")', ['foo', 'bar']],
      ['foo = require("foo")require("bar")', ['foo', 'bar']],
      ['foo = require("foo")bar = require("bar")', ['foo', 'bar']],
      ['foo = require("a-b")bar = require("c-d-e")', ['a-b', 'c-d-e']],
      ['foo = require("./foo")bar = require("./bar")', ['./foo', './bar']],
      ['const foo = require("./foo")bar = require("./bar")', ['./foo', './bar']],
      'require "foo"',
      'require_relative "foo"',
      // require.resolve
      'require.resolve "foo"',
      'require.resolve("foo")',
      'require.resolve( "foo" )',
      'require.resolve(  "foo"  )',
      'require.resolve(\t"foo"\t)',
      'require.resolve ("foo")',
      'var foo = require.resolve("foo")',
      ['var foo = require.resolve("foo")var bar = require.resolve("bar")', ['foo', 'bar']],
      // JavaScript Dynamic Imports import("./module")
      'import("foo")',
      'import( "foo" )',
      'import(  "foo"  )',
      'import(\t"foo"\t)',
      'import ("foo")',
      'var foo = import("foo")',
      'var $ = import("foo")',
      'var _ = import("foo")',
      ['var foo = import("foo")var bar = import("bar")', ['foo', 'bar']],
      ['import("foo")import("bar");', ['foo', 'bar']],
      ['import("foo")import("bar");', ['foo', 'bar']],
      ['var foo = import("foo")import("bar");', ['foo', 'bar']],
      ['var foo = import("foo")import("bar")', ['foo', 'bar']],
      ['foo = import("foo")import("bar")', ['foo', 'bar']],
      ['foo = import("foo")bar = import("bar")', ['foo', 'bar']],
      ['foo = import("a-b")bar = import("c-d-e")', ['a-b', 'c-d-e']],
      ['foo = import("./foo")bar = import("./bar")', ['./foo', './bar']],
      ['const foo = import("./foo")bar = import("./bar")', ['./foo', './bar']],
      'import "foo"',
    ],
    invalid: [
      'require(foo)',
      'require"foo"',
      'require (foo)',
      'require("fo o")',
      'require_relative(foo)',
      'require_relative"foo"',
      'require_relative (foo)',
      'require_relative("fo o")',
      // require.resolve
      'require.resolve(foo)',
      'require.resolve"foo"',
      'require.resolve (foo)',
      'require.resolve("fo o")',
      'requireDresolve("foo")',
      // JavaScript Dynamic Imports import("./module")
      'import(foo)',
      'import"foo"',
      'import (foo)',
      'import("fo o")',
      // import.resolve
      'import.resolve(foo)',
      'import.resolve"foo"',
      'import.resolve ("foo")',
      'import.resolve (foo)',
      'import.resolve("fo o")',
      'import.resolve "foo"',
      'import.resolve("foo")',
      'import.resolve( "foo" )',
      'import.resolve(  "foo"  )',
      'import.resolve(\t"foo"\t)',
      'var foo = import.resolve("foo")',
      'var foo = import.resolve("foo")var bar = import.resolve("bar")',
    ],
  },
  GEM: {
    valid: [
      'gem "foo"',
      ['gem \'foo\'', ['foo']],
    ],
    invalid: [
      'gem     "foo"',
    ],
  },
  HASKELL_IMPORT: {
    valid: [
      ['import Foo', ['Foo']],
      ['import Foo.Bar', ['Foo.Bar']],
      ['import qualified Foo', ['Foo']],
      ['import qualified Foo.Bar', ['Foo.Bar']],
      ['import Foo.Bar.Baz', ['Foo.Bar.Baz']],
      ['import Foo Bar', ['Foo']],
      ['import Foo', ['Foo']],
      ['import Foo as Bar', ['Foo']],
      ['import Foo ()', ['Foo']],
      ['import Foo', ['Foo']],
    ],
    invalid: [
      ['limport Foo'],
      ['imports Foo'],
      ['import "Foo"'],
      ['import /Foo'],
      ['import .foo'],
      ['import foo'],
    ],
  },
  HOMEBREW: {
    valid: [
      'depends_on "foo"',
      ['depends_on \'foo\'', ['foo']],
      'conflicts_with "foo"',
      ['conflicts_with \'foo\'', ['foo']],
      'depends_on cask: "foo"',
      ['depends_on cask: \'foo\'', ['foo']],
      'conflicts_with cask: "foo"',
      ['conflicts_with cask: \'foo\'', ['foo']],
      'depends_on formula: "foo"',
      ['depends_on formula: \'foo\'', ['foo']],
      'conflicts_with formula: "foo"',
      ['conflicts_with formula: \'foo\'', ['foo']],
    ],
    // These probably aren't actually invalid, but
    // https://github.com/Homebrew/homebrew-core/ has no occurences of multiple
    // spaces after depends_on/conflicts_with, and I'm guessing their lint
    // prohibits them anyway.
    invalid: [
      'depends_on     "foo"',
      'conflicts_with     "foo"',
    ],
  },
  TYPESCRIPT_REFERENCE: {
    valid: [
      '/// <reference path="foo" />',
      '///<reference path="foo" />',
    ],
    invalid: [
      '// <reference path="foo" />',
    ],
  },
  DOCKER_FROM: {
    valid: [
      ['FROM foo', ['foo']],
      ['FROM foo:1.2.3', ['foo:1.2.3']],
      ['FROM foo:1.2.3-alpha', ['foo:1.2.3-alpha']],
      ['FROM foo/bar', ['foo/bar']],
    ],
    invalid: [
      'FROMfoo',
      // 'FROM\nfoo',
    ],
  },
  VIM_PLUGIN: {
    valid: [
      ["Plugin 'VundleVim/Vundle.vim'", ['VundleVim/Vundle.vim']],
      ['Plugin "VundleVim/Vundle.vim"', ['VundleVim/Vundle.vim']],
      ["Plugin 'ctrlp.vim'", ['ctrlp.vim']],
      ['Plugin "ctrlp.vim"', ['ctrlp.vim']],
    ],
    invalid: [
      "Plugin'ctrlp.vim'",
    ],
  },
  RUST_CRATE: {
    valid: [
      ['extern crate pcre;', ['pcre']],
      ['extern crate std as ruststd;', ['std']],
      ['use std::option::Option::{Some, None};', ['std']],
    ],
    invalid: [
      "extern create 'pcre'",
      'because it',
    ],
  },
  PYTHON_IMPORT: {
    valid: [
      ['import foo', ['foo']],
      ['\nimport foo', ['foo']],
      ['import fo_o', ['fo_o']],
      ['import .foo', ['.foo']],
      ['import foo as bar', ['foo']],
      ['from foo import bar', ['foo']],
      ['from foo import bar, baz', ['foo']],
      ['from foo.bar import baz', ['foo.bar']],
    ],
    invalid: [
      '\simport foo',
      '\simport\nfoo',
      '# from the',
    ],
  },
  REQUIREMENTS_TXT: {
    valid: [
      ['wheel==0.23.0', ['wheel']],
      ['Yarg==0.1.9', ['Yarg']],
      ['docopt==0.6.2', ['docopt']],
      ['Flask-Cache==0.13.1', ['Flask-Cache']],
      ['flake8', ['flake8']],
    ],
    invalid: [
      '-r requirements.txt',
    ],
  },
  CSS_IMPORT: {
    valid: [
      '@import \'foo\'',
      '@import "foo"',
      '@import "foo" bar',
      '@import URL("foo")',
      '@import url("foo")',
      '@import url("foo") bar',
    ],
    invalid: [
      '@import foo',
      '@import url(foo)',
    ],
  },
  LESS_IMPORT: {
    valid: [
      '@import (less) "foo";',
      '@import (optional, reference) "foo";',
      // below copied from CSS_IMPORT
      '@import \'foo\'',
      '@import "foo"',
      '@import "foo" bar',
      '@import URL("foo")',
      '@import url("foo")',
      '@import url("foo") bar',
      // above copied from CSS_IMPORT
    ],
    invalid: [
      // below copied from CSS_IMPORT
      '@import foo',
      '@import url(foo)',
      // above copied from CSS_IMPORT
    ],
  },
  HTML_IMPORT: {
    valid: [
      '<link rel="import" href="foo">',
    ],
    invalid: [
      '<link href="foo">',
    ],
  },
};

const goFixtures = {
  valid: [
    'import "foo"',
    'import _ "foo"',
    'import . "foo"',
    'import bar "foo"',
    ['import "./foo"', ['./foo']],
    ['import "./foo/bar"', ['./foo/bar']],
    '\nimport "foo"',
    ['import "fo_o"', ['fo_o']],
    ['import "github.com/foo/bar"', ['github.com/foo/bar']],
    ['import "bitbucket.org/foo/bar"', ['bitbucket.org/foo/bar']],
    ['import "launchpad.net/foo/bar"', ['launchpad.net/foo/bar']],
    ['import "hub.jazz.net/foo/bar"', ['hub.jazz.net/foo/bar']],
    ['import "gopkg.in/foo/bar"', ['gopkg.in/foo/bar']],
    'import (\n"foo"\n)',
    'import (\n_ "foo"\n)',
    'import (\n. "foo"\n)',
    'import (\nbar "foo"\n)',
    'import (\n    "foo"\n)',
    ['import (\n"./foo"\n"./bar"\n)', ['./foo', './bar']],
    ['import (\n"github.com/foo/bar"\n)', ['github.com/foo/bar']],
    ['import (\n"bitbucket.org/foo/bar"\n)', ['bitbucket.org/foo/bar']],
    ['import (\n"launchpad.net/foo/bar"\n)', ['launchpad.net/foo/bar']],
    ['import (\n"hub.jazz.net/foo/bar"\n)', ['hub.jazz.net/foo/bar']],
    ['import (\n"gopkg.in/foo/bar"\n)', ['gopkg.in/foo/bar']],
    ['import (\n"github.com/foo"\n"github.com/bar"\n)', ['github.com/foo', 'github.com/bar']],
    ['import (\n"github.com/foo"\n\n"github.com/bar"\n)', ['github.com/foo', 'github.com/bar']],
    ['import (\nbar "github.com/foo/bar"\n)', ['github.com/foo/bar']],
  ],
  invalid: [
    '\simport foo',
    '\simport\nfoo',
    'import "octo.com/foo/bar"',
    'import (\n"octo.com/foo/bar"\n)',
  ],
};

function fixturesIterator(fixturesList, next) {
  fixturesList.forEach((statement) => {
    const text = Array.isArray(statement) ? statement[0] : statement;
    const expected = Array.isArray(statement) ? statement[1] : ['foo'];

    next(text, expected);
  });
}

describe('helper-grammar-regex-collection', () => {
  Object.keys(fixtures).forEach((grammar) => {
    const { valid, invalid } = fixtures[grammar];
    const regex = REGEX[grammar];

    beforeEach(() => {
      regex.lastIndex = 0;
    });

    describe(grammar, () => {
      describe('valid', () => {
        fixturesIterator(valid, (text, expected) => {
          it(text, () => {
            let match;
            let result = [];

            while (match = regex.exec(text)) { // eslint-disable-line no-cond-assign
              result = result.concat(match.slice(1));
            }

            assert.deepEqual(result, expected);
          });
        });
      });

      describe('invalid', () => {
        fixturesIterator(invalid, (text) => {
          it(text, () => {
            assert.equal(regex.exec(text), null);
          });
        });
      });
    });
  });

  describe('go()', () => {
    const { valid, invalid } = goFixtures;

    describe('valid', () => {
      fixturesIterator(valid, (text, expected) => {
        it(text, () => {
          let match;
          let result = [];

          REGEX.go(text).forEach((regex) => {
            while (match = regex.exec(text)) { // eslint-disable-line no-cond-assign
              result = result.concat(match.slice(1));
            }
          });

          assert.deepEqual(result, expected);
        });
      });
    });

    describe('invalid', () => {
      fixturesIterator(invalid, (text) => {
        it(text, () => {
          REGEX.go(text).forEach((regex) => {
            assert.equal(regex.exec(text), null);
          });
        });
      });
    });
  });
});
<commit_msg>test: Dedupe grammar tests (#340)<commit_after>/* eslint import/namespace: [2, { allowComputed: true }]*/

import assert from 'assert';
import * as REGEX from './index.js';

const fixtures = {
  IMPORT: {
    valid: [
      'import foo from "foo"',
      'import _ from "foo"',
      'import $ from "foo"',
      'import * as bar from "foo"',
      'import { bar } from "foo"',
      'import { foo as bar } from "foo"',
      'import { foo, bar } from "foo"',
      'import { foo, bar as baz } from "foo"',
      'import foo, {bar} from "foo"',
      'import foo, * as bar from "foo"',
      'import "foo"',
      'import \nfoo \nfrom \n"foo"',
      'import {\nbar } from "foo"',
      'import { bar\n } from "foo"',
      'import { \nbar\n } from "foo"',
      'import foo\n, {bar} from "foo"',
      'import foo, {bar} \nfrom "foo"',
      'import foo, {\nbar} from "foo"',
      'import foo, {bar\n} from "foo"',
    ],
    invalid: [
      'import "fo o"',
      // TODO tweak IMPORT regexp so that invalid statements are not matched
      // 'import foo "foo"',
      // 'import from "foo"',
    ],
  },
  EXPORT: {
    valid: [
      'export * from "foo"',
      'export { foo, bar } from "foo"',
      'export { foo as bar} from "foo"',
      'export { foo, bar as baz } from "foo"',
      'export {\nbar } from "foo"',
      'export { bar\n } from "foo"',
      'export { \nbar\n } from "foo"',
    ],
    invalid: [
      'export * from "fo o"',
    ],
  },
  REQUIRE: {
    valid: [
      'require("foo")',
      'require( "foo" )',
      'require(  "foo"  )',
      'require(\t"foo"\t)',
      'require ("foo")',
      'var foo = require("foo")',
      'var $ = require("foo")',
      'var _ = require("foo")',
      ['var foo = require("foo")var bar = require("bar")', ['foo', 'bar']],
      ['require("foo")require("bar");', ['foo', 'bar']],
      ['require("foo")require("bar");', ['foo', 'bar']],
      ['var foo = require("foo")require("bar");', ['foo', 'bar']],
      ['var foo = require("foo")require("bar")', ['foo', 'bar']],
      ['foo = require("foo")require("bar")', ['foo', 'bar']],
      ['foo = require("foo")bar = require("bar")', ['foo', 'bar']],
      ['foo = require("a-b")bar = require("c-d-e")', ['a-b', 'c-d-e']],
      ['foo = require("./foo")bar = require("./bar")', ['./foo', './bar']],
      ['const foo = require("./foo")bar = require("./bar")', ['./foo', './bar']],
      'require "foo"',
      'require_relative "foo"',
      // require.resolve
      'require.resolve "foo"',
      'require.resolve("foo")',
      'require.resolve( "foo" )',
      'require.resolve(  "foo"  )',
      'require.resolve(\t"foo"\t)',
      'require.resolve ("foo")',
      'var foo = require.resolve("foo")',
      ['var foo = require.resolve("foo")var bar = require.resolve("bar")', ['foo', 'bar']],
      // JavaScript Dynamic Imports import("./module")
      'import("foo")',
      'import( "foo" )',
      'import(  "foo"  )',
      'import(\t"foo"\t)',
      'import ("foo")',
      'var foo = import("foo")',
      'var $ = import("foo")',
      'var _ = import("foo")',
      ['var foo = import("foo")var bar = import("bar")', ['foo', 'bar']],
      ['import("foo")import("bar");', ['foo', 'bar']],
      ['import("foo")import("bar");', ['foo', 'bar']],
      ['var foo = import("foo")import("bar");', ['foo', 'bar']],
      ['var foo = import("foo")import("bar")', ['foo', 'bar']],
      ['foo = import("foo")import("bar")', ['foo', 'bar']],
      ['foo = import("foo")bar = import("bar")', ['foo', 'bar']],
      ['foo = import("a-b")bar = import("c-d-e")', ['a-b', 'c-d-e']],
      ['foo = import("./foo")bar = import("./bar")', ['./foo', './bar']],
      ['const foo = import("./foo")bar = import("./bar")', ['./foo', './bar']],
      'import "foo"',
    ],
    invalid: [
      'require(foo)',
      'require"foo"',
      'require (foo)',
      'require("fo o")',
      'require_relative(foo)',
      'require_relative"foo"',
      'require_relative (foo)',
      'require_relative("fo o")',
      // require.resolve
      'require.resolve(foo)',
      'require.resolve"foo"',
      'require.resolve (foo)',
      'require.resolve("fo o")',
      'requireDresolve("foo")',
      // JavaScript Dynamic Imports import("./module")
      'import(foo)',
      'import"foo"',
      'import (foo)',
      'import("fo o")',
      // import.resolve
      'import.resolve(foo)',
      'import.resolve"foo"',
      'import.resolve ("foo")',
      'import.resolve (foo)',
      'import.resolve("fo o")',
      'import.resolve "foo"',
      'import.resolve("foo")',
      'import.resolve( "foo" )',
      'import.resolve(  "foo"  )',
      'import.resolve(\t"foo"\t)',
      'var foo = import.resolve("foo")',
      'var foo = import.resolve("foo")var bar = import.resolve("bar")',
    ],
  },
  GEM: {
    valid: [
      'gem "foo"',
      ['gem \'foo\'', ['foo']],
    ],
    invalid: [
      'gem     "foo"',
    ],
  },
  HASKELL_IMPORT: {
    valid: [
      ['import Foo', ['Foo']],
      ['import Foo.Bar', ['Foo.Bar']],
      ['import qualified Foo', ['Foo']],
      ['import qualified Foo.Bar', ['Foo.Bar']],
      ['import Foo.Bar.Baz', ['Foo.Bar.Baz']],
      ['import Foo Bar', ['Foo']],
      ['import Foo', ['Foo']],
      ['import Foo as Bar', ['Foo']],
      ['import Foo ()', ['Foo']],
      ['import Foo', ['Foo']],
    ],
    invalid: [
      ['limport Foo'],
      ['imports Foo'],
      ['import "Foo"'],
      ['import /Foo'],
      ['import .foo'],
      ['import foo'],
    ],
  },
  HOMEBREW: {
    valid: [
      'depends_on "foo"',
      ['depends_on \'foo\'', ['foo']],
      'conflicts_with "foo"',
      ['conflicts_with \'foo\'', ['foo']],
      'depends_on cask: "foo"',
      ['depends_on cask: \'foo\'', ['foo']],
      'conflicts_with cask: "foo"',
      ['conflicts_with cask: \'foo\'', ['foo']],
      'depends_on formula: "foo"',
      ['depends_on formula: \'foo\'', ['foo']],
      'conflicts_with formula: "foo"',
      ['conflicts_with formula: \'foo\'', ['foo']],
    ],
    // These probably aren't actually invalid, but
    // https://github.com/Homebrew/homebrew-core/ has no occurences of multiple
    // spaces after depends_on/conflicts_with, and I'm guessing their lint
    // prohibits them anyway.
    invalid: [
      'depends_on     "foo"',
      'conflicts_with     "foo"',
    ],
  },
  TYPESCRIPT_REFERENCE: {
    valid: [
      '/// <reference path="foo" />',
      '///<reference path="foo" />',
    ],
    invalid: [
      '// <reference path="foo" />',
    ],
  },
  DOCKER_FROM: {
    valid: [
      ['FROM foo', ['foo']],
      ['FROM foo:1.2.3', ['foo:1.2.3']],
      ['FROM foo:1.2.3-alpha', ['foo:1.2.3-alpha']],
      ['FROM foo/bar', ['foo/bar']],
    ],
    invalid: [
      'FROMfoo',
      // 'FROM\nfoo',
    ],
  },
  VIM_PLUGIN: {
    valid: [
      ["Plugin 'VundleVim/Vundle.vim'", ['VundleVim/Vundle.vim']],
      ['Plugin "VundleVim/Vundle.vim"', ['VundleVim/Vundle.vim']],
      ["Plugin 'ctrlp.vim'", ['ctrlp.vim']],
      ['Plugin "ctrlp.vim"', ['ctrlp.vim']],
    ],
    invalid: [
      "Plugin'ctrlp.vim'",
    ],
  },
  RUST_CRATE: {
    valid: [
      ['extern crate pcre;', ['pcre']],
      ['extern crate std as ruststd;', ['std']],
      ['use std::option::Option::{Some, None};', ['std']],
    ],
    invalid: [
      "extern create 'pcre'",
      'because it',
    ],
  },
  PYTHON_IMPORT: {
    valid: [
      ['import foo', ['foo']],
      ['\nimport foo', ['foo']],
      ['import fo_o', ['fo_o']],
      ['import .foo', ['.foo']],
      ['import foo as bar', ['foo']],
      ['from foo import bar', ['foo']],
      ['from foo import bar, baz', ['foo']],
      ['from foo.bar import baz', ['foo.bar']],
    ],
    invalid: [
      '\simport foo',
      '\simport\nfoo',
      '# from the',
    ],
  },
  REQUIREMENTS_TXT: {
    valid: [
      ['wheel==0.23.0', ['wheel']],
      ['Yarg==0.1.9', ['Yarg']],
      ['docopt==0.6.2', ['docopt']],
      ['Flask-Cache==0.13.1', ['Flask-Cache']],
      ['flake8', ['flake8']],
    ],
    invalid: [
      '-r requirements.txt',
    ],
  },
  CSS_IMPORT: {
    valid: [
      '@import \'foo\'',
      '@import "foo"',
      '@import "foo" bar',
      '@import URL("foo")',
      '@import url("foo")',
      '@import url("foo") bar',
    ],
    invalid: [
      '@import foo',
      '@import url(foo)',
    ],
  },
  LESS_IMPORT: {
    valid: [
      '@import (less) "foo";',
      '@import (optional, reference) "foo";',
      // below copied from CSS_IMPORT
      '@import \'foo\'',
      '@import "foo"',
      '@import "foo" bar',
      '@import URL("foo")',
      '@import url("foo")',
      '@import url("foo") bar',
      // above copied from CSS_IMPORT
    ],
    invalid: [
      // below copied from CSS_IMPORT
      '@import foo',
      '@import url(foo)',
      // above copied from CSS_IMPORT
    ],
  },
  HTML_IMPORT: {
    valid: [
      '<link rel="import" href="foo">',
    ],
    invalid: [
      '<link href="foo">',
    ],
  },
  go: {
    valid: [
      'import "foo"',
      'import _ "foo"',
      'import . "foo"',
      'import bar "foo"',
      ['import "./foo"', ['./foo']],
      ['import "./foo/bar"', ['./foo/bar']],
      '\nimport "foo"',
      ['import "fo_o"', ['fo_o']],
      ['import "github.com/foo/bar"', ['github.com/foo/bar']],
      ['import "bitbucket.org/foo/bar"', ['bitbucket.org/foo/bar']],
      ['import "launchpad.net/foo/bar"', ['launchpad.net/foo/bar']],
      ['import "hub.jazz.net/foo/bar"', ['hub.jazz.net/foo/bar']],
      ['import "gopkg.in/foo/bar"', ['gopkg.in/foo/bar']],
      'import (\n"foo"\n)',
      'import (\n_ "foo"\n)',
      'import (\n. "foo"\n)',
      'import (\nbar "foo"\n)',
      'import (\n    "foo"\n)',
      ['import (\n"./foo"\n"./bar"\n)', ['./foo', './bar']],
      ['import (\n"github.com/foo/bar"\n)', ['github.com/foo/bar']],
      ['import (\n"bitbucket.org/foo/bar"\n)', ['bitbucket.org/foo/bar']],
      ['import (\n"launchpad.net/foo/bar"\n)', ['launchpad.net/foo/bar']],
      ['import (\n"hub.jazz.net/foo/bar"\n)', ['hub.jazz.net/foo/bar']],
      ['import (\n"gopkg.in/foo/bar"\n)', ['gopkg.in/foo/bar']],
      ['import (\n"github.com/foo"\n"github.com/bar"\n)', ['github.com/foo', 'github.com/bar']],
      ['import (\n"github.com/foo"\n\n"github.com/bar"\n)', ['github.com/foo', 'github.com/bar']],
      ['import (\nbar "github.com/foo/bar"\n)', ['github.com/foo/bar']],
    ],
    invalid: [
      '\simport foo',
      '\simport\nfoo',
      'import "octo.com/foo/bar"',
      'import (\n"octo.com/foo/bar"\n)',
    ],
  },
};

function fixturesIterator(fixturesList, next) {
  fixturesList.forEach((statement) => {
    const text = Array.isArray(statement) ? statement[0] : statement;
    const expected = Array.isArray(statement) ? statement[1] : ['foo'];

    next(text, expected);
  });
}

describe('helper-grammar-regex-collection', () => {
  Object.keys(fixtures).forEach((grammar) => {
    const { valid, invalid } = fixtures[grammar];
    let regexes = REGEX[grammar];

    // Help ensure that `regexes` is a function that returns an array of RegExp
    if (regexes instanceof RegExp) {
      regexes = [regexes];
    }
    if (Array.isArray(regexes)) {
      const oldRegexes = regexes;
      regexes = () => oldRegexes;
    }

    describe(grammar, () => {
      describe('valid', () => {
        fixturesIterator(valid, (text, expected) => {
          it(text, () => {
            let match;
            let result = [];

            regexes(text).forEach((regex) => {
              while (match = regex.exec(text)) { // eslint-disable-line no-cond-assign
                result = result.concat(match.slice(1));
              }
            });

            assert.deepEqual(result, expected);
          });
        });
      });

      describe('invalid', () => {
        fixturesIterator(invalid, (text) => {
          it(text, () => {
            regexes(text).forEach((regex) => {
              assert.equal(regex.exec(text), null);
            });
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>// Package metadata for Meteor.js web platform (https://www.meteor.com/)
// This file is defined within the Meteor documentation at
//
//   http://docs.meteor.com/#/full/packagejs
//
// and it is needed to define a Meteor package
'use strict';

Package.describe({
  name: 'useraccounts:flow-routing',
  summary: 'UserAccounts package providing routes configuration capability via kadira:flow-router.',
  version: '1.11.1',
  git: 'https://github.com/meteor-useraccounts/flow-routing.git'
});

Package.onUse(function(api) {
  api.versionsFrom('METEOR@1.0.3');

  api.use([
    'check',
    'kadira:blaze-layout',
    'kadira:flow-router',
    'underscore',
    'useraccounts:core',
  ], ['client', 'server']);

  api.imply([
    'kadira:blaze-layout@2.0.0',
    'kadira:flow-router@2.0.1',
    'useraccounts:core@1.11.1',
  ], ['client', 'server']);

  api.addFiles([
    'lib/core.js',
  ], ['server']);

  api.addFiles([
    'lib/core.js',
    'lib/client.js',
    'lib/templates_helpers/at_input.js',
  ], ['client']);
});
<commit_msg>update package<commit_after>// Package metadata for Meteor.js web platform (https://www.meteor.com/)
// This file is defined within the Meteor documentation at
//
//   http://docs.meteor.com/#/full/packagejs
//
// and it is needed to define a Meteor package
'use strict';

Package.describe({
  name: 'useraccounts:flow-routing',
  summary: 'UserAccounts package providing routes configuration capability via kadira:flow-router.',
  version: '1.11.1',
  git: 'https://github.com/meteor-useraccounts/flow-routing.git'
});

Package.onUse(function(api) {
  api.versionsFrom('METEOR@1.0.3');

  api.use([
    'check',
    'kadira:blaze-layout',
    'kadira:flow-router',
    'underscore',
    'useraccounts:core',
  ], ['client', 'server']);

  api.imply([
    'kadira:blaze-layout@2.0.0',
    'kadira:flow-router@2.0.2',
    'useraccounts:core@1.11.1',
  ], ['client', 'server']);

  api.addFiles([
    'lib/core.js',
  ], ['client', 'server']);

  api.addFiles([
    'lib/client.js',
    'lib/templates_helpers/at_input.js',
  ], ['client']);
});
<|endoftext|>


<commit_before>/**
# Tutorial 1. Quick Start

**Caution**: work in progress.

## Setting up a bindinator project

Right now you need the bindinator source and lib directories,
and you probably want components.

Assuming you have copied the lib/ and source/ directories,
your web app's page needs to look like this:

    <head>
      ...
    </head>
    <body>
      ...
      <script src="lib/require.js"></script>
      <script>
        require.autoPreload('source/b8r.js').then(b8r => {
          ...
        });
      </script>
    </body>

b8r lets you register objects -- bind objects to a name -- which allows
the binding of those objects to DOM elements (i.e. view components).

The following example simply registers an object which the other
examples use, and displays the state of the data every second.

You can try playing with the other examples and then coming back to
see what the data looks like OR you can change the example code and
run it again, which will overwrite the registered data.

```
<pre id="tutorial1-data" data-bind="json=tutorial1">
</pre>
<script>
  b8r.register('tutorial1', {
    range: 5,
    input: 'hello, world',
    hello: () => alert(b8r.get('tutorial1.input')),
    list: [
      {id: 1, first: 'Ernest', last: 'Hemingway'},
      {id: 2, first: 'Arundhati', last: 'Roy'},
      {id: 3, first: 'Ursula', last: 'Le Guin'}
    ]
  });

  const domInterval = require('lib/domInterval.js');
  domInterval(
    component,
    () => b8r.bindAll(b8r.id('tutorial1-data')),
    1000
  );
</script>
```

**Aside** you can ignore the domInterval code if you like. domInterval is
simply a wrapper for setInterval that kills the interval after the specified
DOM element leaves the document body.

Once you have b8r loaded, you can bind registered data to the DOM using
`data-bind` attributes, such as this:

```
<h3 data-bind="text=tutorial1.input">
  Placeholder
</h3>
<label>
  A bound input field
  <input
    data-bind="value=tutorial1.input"
  >
</label><br>
<label>
  A bound range input
  <input
    type="range"
    min=0
    max=10
    data-bind="value=tutorial1.range"
  >
</label><br>
<label>
  A numeric input bound to the same value as above
  <input
    type="number"
    data-bind="value=tutorial1.range"
  >
</label>
```

Notice that the object registered in the previous example is seen in this example.

You can also bind events to registered methods using the `data-event` attribute:

```
<button
  data-event="click:tutorial1.hello"
>
  Click Me
</button>
```

Finally, b8r allows you to handle lists of objects using the `data-list` attribute,
`data-list="path.to.list"`.

b8r will create a clone of the element for every item in the list. If you are using id
paths, updating the list will cause minimal changes to the DOM. Within a list item you
can bind elements using relative paths:

```
  <ul>
    <li
      data-list="tutorial1.list:id"
    >
      <span data-bind="text=.last">Last</span>, <span data-bind="text=.first">First</span>
    </li>
  </ul>
  <h3>There's nothing magical about lists!</h3>
  <div
    data-list="tutorial1.list:id"
  >
    <label>
      <span
        data-bind="text=.id"
      >
        0
      </span>
      <input
        style="width: 80px"
        data-bind="value=.first"
      >
      <input
        style="width: 80px"
        data-bind="value=.last"
      >
    </label>
  </div>
```

To allow for efficient list updates (if you expect the list to change frequently)
you can specify an **id-path** to identify individual items: `data-list="path.to.list:path.to.id"`.

Typically, the id path will simply be a property of the list item such as id
(as in the example).

You can try turning on "paint flashing" in the debugging tools to see what happens when you edit the
names in the second list.
*/<commit_msg>Added simpler example<commit_after>/**
# Tutorial 1. Quick Start

**Caution**: work in progress.

## Setting up a bindinator project

Right now you need the bindinator source and lib directories,
and you probably want components.

Assuming you have copied the lib/ and source/ directories,
your web app's page needs to look like this:

    <head>
      ...
    </head>
    <body>
      ...
      <script src="lib/require.js"></script>
      <script>
        require.autoPreload('source/b8r.js').then(b8r => {
          ...
        });
      </script>
    </body>

b8r lets you register objects -- bind objects to a name -- which allows
the *binding* of those objects to DOM elements (i.e. view components).

```
<input width="12" data-bind="value=example.data">
<button data-event="click:example.method">Alert</button>
<script>
b8r.register('example', {
  data: 'hello world',
  method: () => alert(b8r.get('example.data'))
});
</script>
```

Try clicking the **Alert** button before and after editing the text.
In the console you can type b8r.get('example') to see how the
registered data is automatically kept up to date.

The following example simply registers an object which the other
examples use, and displays the state of the data every second.

You can try playing with the other examples and then coming back to
see what the data looks like OR you can change the example code and
run it again, which will overwrite the registered data.

```
<pre id="tutorial1-data" data-bind="json=tutorial1">
</pre>
<script>
  b8r.register('tutorial1', {
    range: 5,
    input: 'hello, world',
    hello: () => alert(b8r.get('tutorial1.input')),
    list: [
      {id: 1, first: 'Ernest', last: 'Hemingway'},
      {id: 2, first: 'Arundhati', last: 'Roy'},
      {id: 3, first: 'Ursula', last: 'Le Guin'}
    ]
  });

  const domInterval = require('lib/domInterval.js');
  domInterval(
    component,
    () => b8r.bindAll(b8r.id('tutorial1-data')),
    1000
  );
</script>
```

**Aside** you can ignore the domInterval code if you like. domInterval is
simply a wrapper for setInterval that kills the interval after the specified
DOM element leaves the document body.

Once you have b8r loaded, you can bind registered data to the DOM using
`data-bind` attributes, such as this:

```
<h3 data-bind="text=tutorial1.input">
  Placeholder
</h3>
<label>
  A bound input field
  <input
    data-bind="value=tutorial1.input"
  >
</label><br>
<label>
  A bound range input
  <input
    type="range"
    min=0
    max=10
    data-bind="value=tutorial1.range"
  >
</label><br>
<label>
  A numeric input bound to the same value as above
  <input
    type="number"
    data-bind="value=tutorial1.range"
  >
</label>
```

Notice that the object registered in the previous example is seen in this example.

You can also bind events to registered methods using the `data-event` attribute:

```
<button
  data-event="click:tutorial1.hello"
>
  Click Me
</button>
```

Finally, b8r allows you to handle lists of objects using the `data-list` attribute,
`data-list="path.to.list"`.

b8r will create a clone of the element for every item in the list. If you are using id
paths, updating the list will cause minimal changes to the DOM. Within a list item you
can bind elements using relative paths:

```
  <ul>
    <li
      data-list="tutorial1.list:id"
    >
      <span data-bind="text=.last">Last</span>, <span data-bind="text=.first">First</span>
    </li>
  </ul>
  <h3>There's nothing magical about lists!</h3>
  <div
    data-list="tutorial1.list:id"
  >
    <label>
      <span
        data-bind="text=.id"
      >
        0
      </span>
      <input
        style="width: 80px"
        data-bind="value=.first"
      >
      <input
        style="width: 80px"
        data-bind="value=.last"
      >
    </label>
  </div>
```

To allow for efficient list updates (if you expect the list to change frequently)
you can specify an **id-path** to identify individual items: `data-list="path.to.list:path.to.id"`.

Typically, the id path will simply be a property of the list item such as id
(as in the example).

You can try turning on "paint flashing" in the debugging tools to see what happens when you edit the
names in the second list.
*/<|endoftext|>


<commit_before>/* eslint no-restricted-syntax: 0 */

require('promise.prototype.finally')

const _ = require('underscore')

global.Promise.prototype.thenReturn = function thenReturn(value) {
  return this.then(function then() { return Promise.resolve(value); })
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

function each(iterable, iterator) {
  return Promise.resolve(iterable).then((array) => {
    return new Promise((resolve, reject) => {
      Array.from(array).reduce((prevPromise, item, idx, len) => (
        prevPromise.then(() => Promise.resolve(iterator(item, idx, len)))
      ), Promise.resolve())
      .then(() => resolve(iterable))
      .catch((err) => reject(err))
    })
  })
}

function promisify(nodeFn) {
  return function wrapper(...fnArgs) {
    return new Promise((resolve, reject) => {
      nodeFn.call(this, ...fnArgs, (err, ...results) => {
        if (err) {
          reject(err)
          return
        }
        resolve(...results)
      });
    })
  }
}

function promisifyAll(obj) {
  for (const key in obj) {
    if (!key.endsWith('Async') && _.isFunction(obj[key])) {
      obj[`${key}Async`] = promisify(obj[key])
    }
  }
  return obj
}

module.exports = {
  each,
  sleep,
  promisify,
  promisifyAll,
  props: require('promise-props'),
}
<commit_msg>Remove unused PromiseUtils in cloud-core<commit_after><|endoftext|>


<commit_before>Package.describe({
    summary: "Form bot proteciton for Meteor."
});

Package.on_use(function(api) {
    api.use([
        'templating',
        'handlebars',
    ], 'client');

    api.add_files(['server/server.js'], 'server');
    api.add_files(['client/client.js', 'client/client.html'], 'client');
    api.export && api.export('reCAPTCHA', ['client', 'server']);
});<commit_msg>fixed add files order<commit_after>Package.describe({
    summary: "Form bot proteciton for Meteor."
});

Package.on_use(function(api) {
    api.use([
        'templating',
        'handlebars',
    ], 'client');

    api.add_files(['server/server.js'], 'server');
    api.add_files(['client/client.html', 'client/client.js'], 'client');
    api.export && api.export('reCAPTCHA', ['client', 'server']);
});<|endoftext|>


<commit_before>/**
 * @module
 * @private
 */
// FIXME: A place for code that used to live under the `LocusZoom` namespace
// Eventually this should be moved into classes or some other mechanism for code sharing. No external uses should
//  depend on any items in this module.

import * as d3 from 'd3';

/**
 * Generate a curtain object for a plot, panel, or any other subdivision of a layout
 * The panel curtain, like the plot curtain is an HTML overlay that obscures the entire panel. It can be styled
 *   arbitrarily and display arbitrary messages. It is useful for reporting error messages visually to an end user
 *   when the error renders the panel unusable.
 *   TODO: Improve type doc here
 * @returns {object}
 */
function generateCurtain() {
    return {
        showing: false,
        selector: null,
        content_selector: null,
        hide_delay: null,

        /**
         * Generate the curtain. Any content (string) argument passed will be displayed in the curtain as raw HTML.
         *   CSS (object) can be passed which will apply styles to the curtain and its content.
         * @param {string} content Content to be displayed on the curtain (as raw HTML)
         * @param {object} css Apply the specified styles to the curtain and its contents
         */
        show: (content, css) => {
            if (!this.curtain.showing) {
                this.curtain.selector = d3.select(this.parent_plot.svg.node().parentNode).insert('div')
                    .attr('class', 'lz-curtain')
                    .attr('id', `${this.id}.curtain`);
                this.curtain.content_selector = this.curtain.selector.append('div')
                    .attr('class', 'lz-curtain-content');
                this.curtain.selector.append('div')
                    .attr('class', 'lz-curtain-dismiss').html('Dismiss')
                    .on('click', () => this.curtain.hide());
                this.curtain.showing = true;
            }
            return this.curtain.update(content, css);
        },

        /**
         * Update the content and css of the curtain that's currently being shown. This method also adjusts the size
         *   and positioning of the curtain to ensure it still covers the entire panel with no overlap.
         * @param {string} content Content to be displayed on the curtain (as raw HTML)
         * @param {object} css Apply the specified styles to the curtain and its contents
         */
        update: (content, css) => {
            if (!this.curtain.showing) {
                return this.curtain;
            }
            clearTimeout(this.curtain.hide_delay);
            // Apply CSS if provided
            if (typeof css == 'object') {
                applyStyles(this.curtain.selector, css);
            }
            // Update size and position
            const page_origin = this._getPageOrigin();
            this.curtain.selector
                .style('top', `${page_origin.y}px`)
                .style('left', `${page_origin.x}px`)
                .style('width', `${this.parent_plot.layout.width}px`)
                .style('height', `${this.layout.height}px`);
            this.curtain.content_selector
                .style('max-width', `${this.parent_plot.layout.width - 40}px`)
                .style('max-height', `${this.layout.height - 40}px`);
            // Apply content if provided
            if (typeof content == 'string') {
                this.curtain.content_selector.html(content);
            }
            return this.curtain;
        },

        /**
         * Remove the curtain
         * @param {number} delay Time to wait (in ms)
         */
        hide: (delay) => {
            if (!this.curtain.showing) {
                return this.curtain;
            }
            // If a delay was passed then defer to a timeout
            if (typeof delay == 'number') {
                clearTimeout(this.curtain.hide_delay);
                this.curtain.hide_delay = setTimeout(this.curtain.hide, delay);
                return this.curtain;
            }
            // Remove curtain
            this.curtain.selector.remove();
            this.curtain.selector = null;
            this.curtain.content_selector = null;
            this.curtain.showing = false;
            return this.curtain;
        },
    };
}

/**
 * Generate a loader object for a plot, panel, or any other subdivision of a layout
 *
 * The panel loader is a small HTML overlay that appears in the lower left corner of the panel. It cannot be styled
 *   arbitrarily, but can show a custom message and show a minimalist loading bar that can be updated to specific
 *   completion percentages or be animated.
 * TODO Improve type documentation
 * @returns {object}
 */
function generateLoader() {
    return {
        showing: false,
        selector: null,
        content_selector: null,
        progress_selector: null,
        cancel_selector: null,

        /**
         * Show a loading indicator
         * @param {string} [content='Loading...'] Loading message (displayed as raw HTML)
         */
        show: (content) => {
            // Generate loader
            if (!this.loader.showing) {
                this.loader.selector = d3.select(this.parent_plot.svg.node().parentNode).insert('div')
                    .attr('class', 'lz-loader')
                    .attr('id', `${this.id}.loader`);
                this.loader.content_selector = this.loader.selector.append('div')
                    .attr('class', 'lz-loader-content');
                this.loader.progress_selector = this.loader.selector
                    .append('div')
                    .attr('class', 'lz-loader-progress-container')
                    .append('div')
                    .attr('class', 'lz-loader-progress');

                this.loader.showing = true;
                if (typeof content == 'undefined') {
                    content = 'Loading...';
                }
            }
            return this.loader.update(content);
        },

        /**
         * Update the currently displayed loader and ensure the new content is positioned correctly.
         * @param {string} content The text to display (as raw HTML). If not a string, will be ignored.
         * @param {number} [percent] A number from 1-100. If a value is specified, it will stop all animations
         *   in progress.
         */
        update: (content, percent) => {
            if (!this.loader.showing) {
                return this.loader;
            }
            clearTimeout(this.loader.hide_delay);
            // Apply content if provided
            if (typeof content == 'string') {
                this.loader.content_selector.html(content);
            }
            // Update size and position
            const padding = 6; // is there a better place to store/define this?
            const page_origin = this._getPageOrigin();
            const loader_boundrect = this.loader.selector.node().getBoundingClientRect();
            this.loader.selector
                .style('top', `${page_origin.y + this.layout.height - loader_boundrect.height - padding}px`)
                .style('left', `${page_origin.x + padding  }px`);

            // Apply percent if provided
            if (typeof percent == 'number') {
                this.loader.progress_selector
                    .style('width', `${Math.min(Math.max(percent, 1), 100)}%`);
            }
            return this.loader;
        },

        /**
         * Adds a class to the loading bar that makes it loop infinitely in a loading animation. Useful when exact
         *   percent progress is not available.
         */
        animate: () => {
            this.loader.progress_selector.classed('lz-loader-progress-animated', true);
            return this.loader;
        },

        /**
         *  Sets the loading bar in the loader to percentage width equal to the percent (number) value passed. Percents
         *    will automatically be limited to a range of 1 to 100. Will stop all animations in progress.
         */
        setPercentCompleted: (percent) => {
            this.loader.progress_selector.classed('lz-loader-progress-animated', false);
            return this.loader.update(null, percent);
        },

        /**
         * Remove the loader
         * @param {number} delay Time to wait (in ms)
         */
        hide: (delay) => {
            if (!this.loader.showing) {
                return this.loader;
            }
            // If a delay was passed then defer to a timeout
            if (typeof delay == 'number') {
                clearTimeout(this.loader.hide_delay);
                this.loader.hide_delay = setTimeout(this.loader.hide, delay);
                return this.loader;
            }
            // Remove loader
            this.loader.selector.remove();
            this.loader.selector = null;
            this.loader.content_selector = null;
            this.loader.progress_selector = null;
            this.loader.cancel_selector = null;
            this.loader.showing = false;
            return this.loader;
        },
    };
}

/**
 * Modern d3 removed the ability to set many styles at once (object syntax). This is a helper so that layouts with
 *  config-objects can set styles all at once
 * @private
 * @param {d3.selection} selection
 * @param {Object} styles
 */
function applyStyles(selection, styles) {
    styles = styles || {};
    for (let [prop, value] of Object.entries(styles)) {
        selection.style(prop, value);
    }
}

/**
 * Prevent a UI function from being called more than once in a given interval. This allows, eg, search boxes to delay
 *   expensive operations until the user is done typing
 * @param {function} func The function to debounce. Returns a wrapper.
 * @param {number} delay Time to wait after last call (in ms)
 */
function debounce(func, delay = 500) {
    let timer;
    return () => {
        clearTimeout(timer);
        timer = setTimeout(
            () => func.apply(this, arguments),
            delay
        );
    };
}

export { applyStyles, debounce, generateCurtain, generateLoader };
<commit_msg>Fix an issue where plot-level "curtain" errors weren't shown in the right place<commit_after>/**
 * @module
 * @private
 */
// FIXME: A place for code that used to live under the `LocusZoom` namespace
// Eventually this should be moved into classes or some other mechanism for code sharing. No external uses should
//  depend on any items in this module.

import * as d3 from 'd3';

/**
 * Generate a curtain object for a plot, panel, or any other subdivision of a layout
 * The panel curtain, like the plot curtain is an HTML overlay that obscures the entire panel. It can be styled
 *   arbitrarily and display arbitrary messages. It is useful for reporting error messages visually to an end user
 *   when the error renders the panel unusable.
 *   TODO: Improve type doc here
 * @returns {object}
 */
function generateCurtain() {
    return {
        showing: false,
        selector: null,
        content_selector: null,
        hide_delay: null,

        /**
         * Generate the curtain. Any content (string) argument passed will be displayed in the curtain as raw HTML.
         *   CSS (object) can be passed which will apply styles to the curtain and its content.
         * @param {string} content Content to be displayed on the curtain (as raw HTML)
         * @param {object} css Apply the specified styles to the curtain and its contents
         */
        show: (content, css) => {
            if (!this.curtain.showing) {
                this.curtain.selector = d3.select(this.parent_plot.svg.node().parentNode).insert('div')
                    .attr('class', 'lz-curtain')
                    .attr('id', `${this.id}.curtain`);
                this.curtain.content_selector = this.curtain.selector.append('div')
                    .attr('class', 'lz-curtain-content');
                this.curtain.selector.append('div')
                    .attr('class', 'lz-curtain-dismiss').html('Dismiss')
                    .on('click', () => this.curtain.hide());
                this.curtain.showing = true;
            }
            return this.curtain.update(content, css);
        },

        /**
         * Update the content and css of the curtain that's currently being shown. This method also adjusts the size
         *   and positioning of the curtain to ensure it still covers the entire panel with no overlap.
         * @param {string} content Content to be displayed on the curtain (as raw HTML)
         * @param {object} css Apply the specified styles to the curtain and its contents
         */
        update: (content, css) => {
            if (!this.curtain.showing) {
                return this.curtain;
            }
            clearTimeout(this.curtain.hide_delay);
            // Apply CSS if provided
            if (typeof css == 'object') {
                applyStyles(this.curtain.selector, css);
            }
            // Update size and position
            const page_origin = this._getPageOrigin();

            // Panel layouts have a height; plot layouts don't
            const height = this.layout.height || this._total_height;
            this.curtain.selector
                .style('top', `${page_origin.y}px`)
                .style('left', `${page_origin.x}px`)
                .style('width', `${this.parent_plot.layout.width}px`)
                .style('height', `${height}px`);
            this.curtain.content_selector
                .style('max-width', `${this.parent_plot.layout.width - 40}px`)
                .style('max-height', `${height - 40}px`);
            // Apply content if provided
            if (typeof content == 'string') {
                this.curtain.content_selector.html(content);
            }
            return this.curtain;
        },

        /**
         * Remove the curtain
         * @param {number} delay Time to wait (in ms)
         */
        hide: (delay) => {
            if (!this.curtain.showing) {
                return this.curtain;
            }
            // If a delay was passed then defer to a timeout
            if (typeof delay == 'number') {
                clearTimeout(this.curtain.hide_delay);
                this.curtain.hide_delay = setTimeout(this.curtain.hide, delay);
                return this.curtain;
            }
            // Remove curtain
            this.curtain.selector.remove();
            this.curtain.selector = null;
            this.curtain.content_selector = null;
            this.curtain.showing = false;
            return this.curtain;
        },
    };
}

/**
 * Generate a loader object for a plot, panel, or any other subdivision of a layout
 *
 * The panel loader is a small HTML overlay that appears in the lower left corner of the panel. It cannot be styled
 *   arbitrarily, but can show a custom message and show a minimalist loading bar that can be updated to specific
 *   completion percentages or be animated.
 * TODO Improve type documentation
 * @returns {object}
 */
function generateLoader() {
    return {
        showing: false,
        selector: null,
        content_selector: null,
        progress_selector: null,
        cancel_selector: null,

        /**
         * Show a loading indicator
         * @param {string} [content='Loading...'] Loading message (displayed as raw HTML)
         */
        show: (content) => {
            // Generate loader
            if (!this.loader.showing) {
                this.loader.selector = d3.select(this.parent_plot.svg.node().parentNode).insert('div')
                    .attr('class', 'lz-loader')
                    .attr('id', `${this.id}.loader`);
                this.loader.content_selector = this.loader.selector.append('div')
                    .attr('class', 'lz-loader-content');
                this.loader.progress_selector = this.loader.selector
                    .append('div')
                    .attr('class', 'lz-loader-progress-container')
                    .append('div')
                    .attr('class', 'lz-loader-progress');

                this.loader.showing = true;
                if (typeof content == 'undefined') {
                    content = 'Loading...';
                }
            }
            return this.loader.update(content);
        },

        /**
         * Update the currently displayed loader and ensure the new content is positioned correctly.
         * @param {string} content The text to display (as raw HTML). If not a string, will be ignored.
         * @param {number} [percent] A number from 1-100. If a value is specified, it will stop all animations
         *   in progress.
         */
        update: (content, percent) => {
            if (!this.loader.showing) {
                return this.loader;
            }
            clearTimeout(this.loader.hide_delay);
            // Apply content if provided
            if (typeof content == 'string') {
                this.loader.content_selector.html(content);
            }
            // Update size and position
            const padding = 6; // is there a better place to store/define this?
            const page_origin = this._getPageOrigin();
            const loader_boundrect = this.loader.selector.node().getBoundingClientRect();
            this.loader.selector
                .style('top', `${page_origin.y + this.layout.height - loader_boundrect.height - padding}px`)
                .style('left', `${page_origin.x + padding  }px`);

            // Apply percent if provided
            if (typeof percent == 'number') {
                this.loader.progress_selector
                    .style('width', `${Math.min(Math.max(percent, 1), 100)}%`);
            }
            return this.loader;
        },

        /**
         * Adds a class to the loading bar that makes it loop infinitely in a loading animation. Useful when exact
         *   percent progress is not available.
         */
        animate: () => {
            this.loader.progress_selector.classed('lz-loader-progress-animated', true);
            return this.loader;
        },

        /**
         *  Sets the loading bar in the loader to percentage width equal to the percent (number) value passed. Percents
         *    will automatically be limited to a range of 1 to 100. Will stop all animations in progress.
         */
        setPercentCompleted: (percent) => {
            this.loader.progress_selector.classed('lz-loader-progress-animated', false);
            return this.loader.update(null, percent);
        },

        /**
         * Remove the loader
         * @param {number} delay Time to wait (in ms)
         */
        hide: (delay) => {
            if (!this.loader.showing) {
                return this.loader;
            }
            // If a delay was passed then defer to a timeout
            if (typeof delay == 'number') {
                clearTimeout(this.loader.hide_delay);
                this.loader.hide_delay = setTimeout(this.loader.hide, delay);
                return this.loader;
            }
            // Remove loader
            this.loader.selector.remove();
            this.loader.selector = null;
            this.loader.content_selector = null;
            this.loader.progress_selector = null;
            this.loader.cancel_selector = null;
            this.loader.showing = false;
            return this.loader;
        },
    };
}

/**
 * Modern d3 removed the ability to set many styles at once (object syntax). This is a helper so that layouts with
 *  config-objects can set styles all at once
 * @private
 * @param {d3.selection} selection
 * @param {Object} styles
 */
function applyStyles(selection, styles) {
    styles = styles || {};
    for (let [prop, value] of Object.entries(styles)) {
        selection.style(prop, value);
    }
}

/**
 * Prevent a UI function from being called more than once in a given interval. This allows, eg, search boxes to delay
 *   expensive operations until the user is done typing
 * @param {function} func The function to debounce. Returns a wrapper.
 * @param {number} delay Time to wait after last call (in ms)
 */
function debounce(func, delay = 500) {
    let timer;
    return () => {
        clearTimeout(timer);
        timer = setTimeout(
            () => func.apply(this, arguments),
            delay
        );
    };
}

export { applyStyles, debounce, generateCurtain, generateLoader };
<|endoftext|>


<commit_before>/*!
 * Drone MVCI Framework for Javascript
 * https://github.com/andremendonca/dronejs
 *
 * Depends on jQuery
 *
 * Version 0.0.2-alpha
 */

var Drone = {
  methods: function (classInstance) {
    return {
      proxy: function (fn) {
        return function () {
          return fn.apply(classInstance, arguments);
        };
      }
    };
  },

  helpers: {
    views: {
      createEventsBinds: function (events) {
        var binds = {};

        $.each(events, function (i, b) {
          $.each(events[i], function(j) {
            binds[events[i][j]] = function (handler) {
              $(i)[j](handler);
            };
          });
        });

        return binds;
      }
    },
    controllers: {
      bindEventHandlers: function(handlers){
        $.each(handlers, this.proxy(function (i, handler) {
          this.view[handler](this[handler + "Handler"]);
        }));
      },
    }
  }
};

Drone.Base = function (classObject) {
  return function (attributes) {
      var F = function () {
        $.extend(this, Drone.methods(this));
      };

      var newClassObject = $.extend(true, {}, classObject, attributes);

      F.prototype = newClassObject;

      var instance = new F();

      if (instance.init) {
        instance.init();
      };

      return instance;
  };
};

Drone.View = function (classObject) {
  var events = classObject.events;
  var binds = Drone.helpers.views.createEventsBinds(events);

  $.extend(classObject, binds);

  delete classObject.events;
  return Drone.Base(classObject);
};

Drone.Controller = function (classObject) {
  var handlers = classObject.eventHandlers;

  classObject.init = function () {
    Drone.helpers.controllers.bindEventHandlers.call(this, handlers);
  };

  delete classObject.eventHandlers;
  return Drone.Base(classObject);
}

Drone.Initializer = function (classObject, autoexec) {
  classObject.exec = function () {
    for (var key in classObject) {
      if (classObject.hasOwnProperty(key) && key != "exec") {
        classObject[key]();
      }
    }
  };

  if (autoexec === true) {
    $().ready(function () {
      classObject.exec();
    });
  }

  return classObject;
};
<commit_msg>Views events and event handlers are optional<commit_after>/*!
 * Drone MVCI Framework for Javascript
 * https://github.com/andremendonca/dronejs
 *
 * Depends on jQuery
 *
 * Version 0.0.2-alpha
 */

var Drone = {
  methods: function (classInstance) {
    return {
      proxy: function (fn) {
        return function () {
          return fn.apply(classInstance, arguments);
        };
      }
    };
  },

  helpers: {
    views: {
      createEventsBinds: function (events) {
        var binds = {};

        $.each(events, function (i, b) {
          $.each(events[i], function(j) {
            binds[events[i][j]] = function (handler) {
              $(i)[j](handler);
            };
          });
        });

        return binds;
      }
    },
    controllers: {
      bindEventHandlers: function(handlers){
        $.each(handlers, this.proxy(function (i, handler) {
          this.view[handler](this[handler + "Handler"]);
        }));
      },
    }
  }
};

Drone.Base = function (classObject) {
  return function (attributes) {
      var F = function () {
        $.extend(this, Drone.methods(this));
      };

      var newClassObject = $.extend(true, {}, classObject, attributes);

      F.prototype = newClassObject;

      var instance = new F();

      if (instance.init) {
        instance.init();
      };

      return instance;
  };
};

Drone.View = function (classObject) {
  var events = classObject.events || [];
  var binds = Drone.helpers.views.createEventsBinds(events);

  $.extend(classObject, binds);

  delete classObject.events;
  return Drone.Base(classObject);
};

Drone.Controller = function (classObject) {
  var handlers = classObject.eventHandlers || [];

  classObject.init = function () {
    Drone.helpers.controllers.bindEventHandlers.call(this, handlers);
  };

  delete classObject.eventHandlers;
  return Drone.Base(classObject);
}

Drone.Initializer = function (classObject, autoexec) {
  classObject.exec = function () {
    for (var key in classObject) {
      if (classObject.hasOwnProperty(key) && key != "exec") {
        classObject[key]();
      }
    }
  };

  if (autoexec === true) {
    $().ready(function () {
      classObject.exec();
    });
  }

  return classObject;
};
<|endoftext|>


<commit_before>Package.describe({
  name: 'lookback:body-class',
  version: '0.0.1',
  summary: 'Automatically add classes for Iron Router templates and layouts for scoping with CSS.'
});

var where = 'client';

Package.onUse(function(api) {
  api.versionsFrom('0.9.3');

  api.use(['coffeescript', 'check', 'underscore', 'jquery', 'tracker'], where);
  api.use('iron:router', where, {weak: true});

  api.addFiles('lookback:body-class.coffee', where);

  api.export('BodyClass', where);
});
<commit_msg>Version constraint for IR<commit_after>Package.describe({
  name: 'lookback:body-class',
  version: '0.0.1',
  summary: 'Automatically add classes for Iron Router templates and layouts for scoping with CSS.'
});

var where = 'client';

Package.onUse(function(api) {
  api.versionsFrom('0.9.3');

  api.use(['coffeescript', 'check', 'underscore', 'jquery', 'tracker'], where);
  api.use('iron:router@1.0.0', where, {weak: true});

  api.addFiles('lookback:body-class.coffee', where);

  api.export('BodyClass', where);
});
<|endoftext|>


<commit_before>'use strict';

angular.module('mean').controller('MessageController', ['$scope','$upload', '$stateParams','$http', '$location', 'Global','dialogs', 'Messages', 'Socket',
    function($scope, $upload, $stateParams, $http, $location, Global, dialogs, Messages, Socket) {
        $scope.global = Global;
        $scope.limit = 10;
        $scope.fromOffset = 0;
        $scope.toOffset = 0;
        $scope.pageNumber = 1;
        $scope.totals = 0;
        $scope.toalsPage = 0;
        $scope.messages = [];
        $scope.showLoading = false;
        $scope.isInbox = 1;
        $scope.isTrash = 0;
        $scope.isSchedule = 0;
        $scope.heading = "Inbox";
        $scope.fileName = "";
        $scope.repeatTime = "";
        // Incoming
        Socket.on('onMessageCreated', function(data) {
            // check if current user in array recipients
            var msg = data.message;
            if (msg.to.indexOf($scope.global.user.username) !== -1) {
                $scope.find();
            };
        });

        // private function

        $scope.getMessages = function(page){

            $http.get('/api/unread').success(function(response){
                $scope.global.unreadInbox = response.totals;
            });

            Messages.query({limit : $scope.limit , page : $scope.pageNumber , inbox : $scope.isInbox, trash : $scope.isTrash , schedule : $scope.isSchedule },function(messages) {
                $scope.fromOffset = ( $scope.pageNumber - 1 ) * $scope.limit + 1;
                $scope.toOffset = $scope.pageNumber * $scope.limit;
                $scope.totals = messages[messages.length - 1].totals;

                if ($scope.totals == 0) {
                    $scope.fromOffset = 0;
                };

                $scope.totalsPage = messages[messages.length - 1].pages;
                if($scope.toOffset > $scope.totals)
                    $scope.toOffset = $scope.totals;

                var finalResult = [];
                for (var i = messages.length - 2; i >= 0; i--) {
                    var tmp = messages[i];

                    if (tmp.file != "") {
                        tmp.hasAttachment = true;
                        tmp.attachment = '/public/upload/' + tmp.file;
                    }
                    else
                    {
                       tmp.hasAttachment = false;
                    }

                    if ($scope.isSchedule) {
                        // switch to receiver
                        tmp.fromName = tmp.to.join(',');
                    };

                    if ($scope.isInbox) {
                        if( tmp.isRead.indexOf($scope.global.user.username) !== -1 )
                            tmp.status = 'read';
                        else
                            tmp.status = 'unread';
                    }
                    else
                    {
                        tmp.status = 'read';
                    }

                    finalResult.push(tmp);
                };

                $scope.messages = finalResult;
                $scope.showLoading = false;
            });
        }

        // find all

        $scope.find = function() {
            $scope.showLoading = true;
            $scope.getMessages($scope.pageNumber);
        };

        // sent email
        $scope.sent = function() {
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.heading = "Sent email";
            $scope.isInbox = 0;
            $scope.isTrash = 0;
            $scope.isSchedule = 0;
            $scope.getMessages($scope.pageNumber);
        };

        // inbox
        $scope.inbox = function() {
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 1;
            $scope.isTrash = 0;
            $scope.isSchedule = 0;
            $scope.heading = "Inbox";
            $scope.getMessages($scope.pageNumber);
        };

        // trash
        $scope.trash = function(){
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 0;
            $scope.isTrash = 1;
            $scope.isSchedule = 0;
            $scope.heading = "Trash";
            $scope.getMessages($scope.pageNumber);
        }

        // trash
        $scope.schedule = function(){
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 0;
            $scope.isTrash = 0;
            $scope.isSchedule = 1;
            $scope.heading = "Schedule";
            $scope.getMessages($scope.pageNumber);
        }

        // Show modal
        $scope.showModal = function (message) {
            $('#myModalDetail h4').text('From ' + message.fromName);

            $('#myModalDetail .modal-body').html(message.message);

            if ( (typeof message.file != 'undefined') &&  message.file != "") {
                $('#myModalDetail .modal-body').prepend('
 Your attachment: ' + message.file + '

');
            }

            $('input[rel=' + message._id + ']').parent().parent().removeClass('unread').addClass('read');
            $('#myModalDetail').modal('show');

            // mark as read right now
            if ($scope.isInbox) {
                message.$update(function() {
                   $http.get('/api/unread').success(function(response){
                        $scope.global.unreadInbox = response.totals;
                    });
                });
            };

        };

        // Check all msg
        $scope.checkAll = function () {
            if($('#checkAll').is(':checked')){
                $('input.mail-checkbox').each(function(){
                    $(this).prop('checked',true);
                });
            }
            else
            {
                $('input.mail-checkbox').each(function(){
                    $(this).prop('checked',false);
                });
            }
        };

        $scope.showPicker = function(){

            $('#ontime').datetimepicker({
                  datepicker:false,
                  format:'H:i',
                   mask:true,
                  onChangeDateTime:function(dp,$input){
                    $scope.repeatTime = $input.val();
                  }
              });
             $('#ontime').datetimepicker('show');
             $('#ondatetime').datetimepicker({
                       mask:true,
                      onChangeDateTime:function(dp,$input){
                        $scope.repeatTime = $input.val();
                      }
                  });
             $('#ondatetime').datetimepicker('show');
        };

        // utility function
        $scope.$on('LoadTimePicker', function() {

            $('#ontime').datetimepicker({
                  datepicker:false,
                  format:'H:i',
                   mask:true,
                  onChangeDateTime:function(dp,$input){
                    $scope.repeatTime = $input.val();
                  }
              });

        });

        $scope.$on('LoadDateTimePicker', function() {

            $('#ondatetime').datetimepicker({
                       mask:true,
                      onChangeDateTime:function(dp,$input){
                        $scope.repeatTime = $input.val();
                      }
                  });

        });

        $scope.$on('LoadJs', function() {
                var ajaxUrl = '';
                if ($stateParams.classId != undefined) {
                    ajaxUrl = "/api/members/" + $stateParams.classId + '/';
                }
                else
                {
                    ajaxUrl = "/api/users";
                }

                $("#selectRecipient").select2({
                    placeholder: "Search a recipient",
                    multiple: true,
                    ajax: {
                        url: ajaxUrl,
                        dataType: 'jsonp',
                        data: function (term, page) {
                            return {
                                q: term, // search term
                                page_limit: 10,
                            };
                        },
                        results: function (data, page) {
                            var classData = [];
                            for (var i = 0; i < data.length; i++) {
                                classData[i] = { id : data[i].username , text : data[i].name , owner : data[i]._id, file : data[i].avatar };
                            };
                            return {results: classData};
                        }
                    },
                    initSelection: function(element, callback) {
                        var id=$(element).val();
                    },
                    formatResult: userFormatResult,
                    formatSelection: userFormatSelection,
                    escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
                });
        });

        $scope.selectRead = function () {
            $('input.mail-checkbox').each(function(){

                    if ($(this).parent().parent().hasClass('read')) {
                        $(this).prop('checked',true);
                    }
                    else
                        $(this).prop('checked',false);
            });
        };

         $scope.selectUnread = function () {
            $('input.mail-checkbox').each(function(){

                    if ($(this).parent().parent().hasClass('unread')) {
                        $(this).prop('checked',true);
                    }
                    else
                        $(this).prop('checked',false);
            });
        };

        $scope.selectNone = function () {
            $('input.mail-checkbox').each(function(){
                   $(this).prop('checked',false);

            });
            $('#checkAll').prop('checked',false);
        };

        // mark all as read
        $scope.massRead = function () {
            var idArr = [];
            $('input.mail-checkbox').each(function(){
                    if ($(this).is(':checked')) {
                        idArr.push($(this).attr('rel'));
                    };
            });
            for (var i = 0; i < $scope.messages.length; i++) {
                if( $scope.messages[i].status == 'unread' && idArr.indexOf($scope.messages[i]._id) !== -1)
                    $scope.messages[i].$update();
            };
        };

        $scope.massUnread = function () {
            // body...
        };

        // remove msg
        $scope.massDelete = function () {
            $scope.showLoading = true;
            var idArr = [];
            $('input.mail-checkbox').each(function(){
                    if ($(this).is(':checked')) {
                        idArr.push($(this).attr('rel'));
                    };
            });
            Messages.clean({ids : idArr.join(',') , inbox : $scope.isInbox, trash : $scope.isTrash , schedule : $scope.isSchedule},function(resp){
                // reset pagination
                $scope.totals -= idArr.length;

                $scope.totalsPage = Math.ceil($scope.totals / $scope.limit);

                if($scope.totalsPage < 1)
                    $scope.totalsPage = 1;

                if ($scope.pageNumber > $scope.totalsPage) {
                    $scope.pageNumber = $scope.totalsPage;
                };
                if ($scope.isSchedule) {
                        $scope.schedule();
                }
                else
                {
                    $scope.find();
                }

                $('#checkAll').prop('checked',false);
            });

        };

        // pagination

        $scope.nextPage = function(){
            if($scope.pageNumber < $scope.totalsPage)
            {
                $('#checkAll').prop('checked',false);
                $scope.showLoading = true;
                $scope.pageNumber += 1;
                $scope.getMessages($scope.pageNumber);
            }
        };

        $scope.previousPage = function(){
            if($scope.pageNumber > 1 )
            {
                $('#checkAll').prop('checked',false);
                $scope.showLoading = true;
                $scope.pageNumber -= 1;
                $scope.getMessages($scope.pageNumber);
            }
        }

        // send msg
         $scope.send = function() {
            this.recipient = $('#selectRecipient').select2('val');

            if (!this.recipient.length) {
                    alert('Please select at least on recipient!');
                    return;
            }

            if ($scope.repeatChecked) {
               // find the select weekly
               var selectWeeklyArr = [];
               $('#selectWeekly').find('input').each(function(){
                    if ($(this).is(':checked')) {
                        selectWeeklyArr.push($(this).attr('name'));
                    };
               });
               if (!selectWeeklyArr.length) {
                    alert('Please select at least on day!');
                    return;
               } else {
                    selectWeeklyArr.push($scope.repeatTime);
                    $scope.repeatTime = selectWeeklyArr.join(',');
               }
            }

            var message = new Messages({
                receiver : this.recipient,
                message: this.message,
                file : $scope.fileName,
                repeatChecked : $scope.repeatChecked,
                occurChecked : $scope.occurChecked,
                repeat : $scope.repeatTime
            });

            message.$save(function(response) {
                $scope.find();
                Socket.emit('createMessage', {message : message , user : $scope.global.user});
                $('#uploadfile').val('');
                $('#exampleInputEmail3').tokenfield('createToken', '');

            });
            $('#myModal').modal('hide');
            $('#myModalMessage').modal('hide');
            this.recipient = '';
            this.message = '';
            // reset upload file
            $scope.fileName = "";
            $scope.repeatChecked = false;
            $scope.occurChecked = false;
        };

        // upload file
        $scope.onFileSelect = function($files) {

        for (var i = 0; i < $files.length; i++) {
          var file = $files[i];
          $scope.upload = $upload.upload({
            url: '/upload',
            method: 'POST',
            withCredentials: true,
            file: file
          }).success(function(data, status, headers, config) {
            // file is uploaded successfully
            $scope.fileName = data.files.file.name;
          });
        }

      };
    }
]);
<commit_msg>Upload progress for message module<commit_after>'use strict';

angular.module('mean').controller('MessageController', ['$scope','$upload', '$stateParams','$http', '$location', 'Global','dialogs', 'Messages', 'Socket',
    function($scope, $upload, $stateParams, $http, $location, Global, dialogs, Messages, Socket) {
        $scope.global = Global;
        $scope.limit = 10;
        $scope.fromOffset = 0;
        $scope.toOffset = 0;
        $scope.pageNumber = 1;
        $scope.totals = 0;
        $scope.toalsPage = 0;
        $scope.messages = [];
        $scope.showLoading = false;
        $scope.isInbox = 1;
        $scope.isTrash = 0;
        $scope.isSchedule = 0;
        $scope.heading = "Inbox";
        $scope.fileName = "";
        $scope.repeatTime = "";
        $scope.uploadProgress = 0;
        // Incoming
        Socket.on('onMessageCreated', function(data) {
            // check if current user in array recipients
            var msg = data.message;
            if (msg.to.indexOf($scope.global.user.username) !== -1) {
                $scope.find();
            };
        });

        // private function

        $scope.getMessages = function(page){

            $http.get('/api/unread').success(function(response){
                $scope.global.unreadInbox = response.totals;
            });

            Messages.query({limit : $scope.limit , page : $scope.pageNumber , inbox : $scope.isInbox, trash : $scope.isTrash , schedule : $scope.isSchedule },function(messages) {
                $scope.fromOffset = ( $scope.pageNumber - 1 ) * $scope.limit + 1;
                $scope.toOffset = $scope.pageNumber * $scope.limit;
                $scope.totals = messages[messages.length - 1].totals;

                if ($scope.totals == 0) {
                    $scope.fromOffset = 0;
                };

                $scope.totalsPage = messages[messages.length - 1].pages;
                if($scope.toOffset > $scope.totals)
                    $scope.toOffset = $scope.totals;

                var finalResult = [];
                for (var i = messages.length - 2; i >= 0; i--) {
                    var tmp = messages[i];

                    if (tmp.file != "") {
                        tmp.hasAttachment = true;
                        tmp.attachment = '/public/upload/' + tmp.file;
                    }
                    else
                    {
                       tmp.hasAttachment = false;
                    }

                    if ($scope.isSchedule) {
                        // switch to receiver
                        tmp.fromName = tmp.to.join(',');
                    };

                    if ($scope.isInbox) {
                        if( tmp.isRead.indexOf($scope.global.user.username) !== -1 )
                            tmp.status = 'read';
                        else
                            tmp.status = 'unread';
                    }
                    else
                    {
                        tmp.status = 'read';
                    }

                    finalResult.push(tmp);
                };

                $scope.messages = finalResult;
                $scope.showLoading = false;
            });
        }

        // find all

        $scope.find = function() {
            $scope.showLoading = true;
            $scope.getMessages($scope.pageNumber);
        };

        // sent email
        $scope.sent = function() {
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.heading = "Sent email";
            $scope.isInbox = 0;
            $scope.isTrash = 0;
            $scope.isSchedule = 0;
            $scope.getMessages($scope.pageNumber);
        };

        // inbox
        $scope.inbox = function() {
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 1;
            $scope.isTrash = 0;
            $scope.isSchedule = 0;
            $scope.heading = "Inbox";
            $scope.getMessages($scope.pageNumber);
        };

        // trash
        $scope.trash = function(){
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 0;
            $scope.isTrash = 1;
            $scope.isSchedule = 0;
            $scope.heading = "Trash";
            $scope.getMessages($scope.pageNumber);
        }

        // trash
        $scope.schedule = function(){
            $scope.showLoading = true;
            $scope.pageNumber = 1;
            $scope.isInbox = 0;
            $scope.isTrash = 0;
            $scope.isSchedule = 1;
            $scope.heading = "Schedule";
            $scope.getMessages($scope.pageNumber);
        }

        // Show modal
        $scope.showModal = function (message) {
            $('#myModalDetail h4').text('From ' + message.fromName);

            $('#myModalDetail .modal-body').html(message.message);

            if ( (typeof message.file != 'undefined') &&  message.file != "") {
                $('#myModalDetail .modal-body').prepend('
 Your attachment: ' + message.file + '

');
            }

            $('input[rel=' + message._id + ']').parent().parent().removeClass('unread').addClass('read');
            $('#myModalDetail').modal('show');

            // mark as read right now
            if ($scope.isInbox) {
                message.$update(function() {
                   $http.get('/api/unread').success(function(response){
                        $scope.global.unreadInbox = response.totals;
                    });
                });
            };

        };

        // Check all msg
        $scope.checkAll = function () {
            if($('#checkAll').is(':checked')){
                $('input.mail-checkbox').each(function(){
                    $(this).prop('checked',true);
                });
            }
            else
            {
                $('input.mail-checkbox').each(function(){
                    $(this).prop('checked',false);
                });
            }
        };

        $scope.showPicker = function(){

            $('#ontime').datetimepicker({
                  datepicker:false,
                  format:'H:i',
                   mask:true,
                  onChangeDateTime:function(dp,$input){
                    $scope.repeatTime = $input.val();
                  }
              });
             $('#ontime').datetimepicker('show');
             $('#ondatetime').datetimepicker({
                       mask:true,
                      onChangeDateTime:function(dp,$input){
                        $scope.repeatTime = $input.val();
                      }
                  });
             $('#ondatetime').datetimepicker('show');
        };

        // utility function
        $scope.$on('LoadTimePicker', function() {

            $('#ontime').datetimepicker({
                  datepicker:false,
                  format:'H:i',
                   mask:true,
                  onChangeDateTime:function(dp,$input){
                    $scope.repeatTime = $input.val();
                  }
              });

        });

        $scope.$on('LoadDateTimePicker', function() {

            $('#ondatetime').datetimepicker({
                       mask:true,
                      onChangeDateTime:function(dp,$input){
                        $scope.repeatTime = $input.val();
                      }
                  });

        });

        $scope.$on('LoadJs', function() {
                var ajaxUrl = '';
                if ($stateParams.classId != undefined) {
                    ajaxUrl = "/api/members/" + $stateParams.classId + '/';
                }
                else
                {
                    ajaxUrl = "/api/users";
                }

                $("#selectRecipient").select2({
                    placeholder: "Search a recipient",
                    multiple: true,
                    ajax: {
                        url: ajaxUrl,
                        dataType: 'jsonp',
                        data: function (term, page) {
                            return {
                                q: term, // search term
                                page_limit: 10,
                            };
                        },
                        results: function (data, page) {
                            var classData = [];
                            for (var i = 0; i < data.length; i++) {
                                classData[i] = { id : data[i].username , text : data[i].name , owner : data[i]._id, file : data[i].avatar };
                            };
                            return {results: classData};
                        }
                    },
                    initSelection: function(element, callback) {
                        var id=$(element).val();
                    },
                    formatResult: userFormatResult,
                    formatSelection: userFormatSelection,
                    escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
                });
        });

        $scope.selectRead = function () {
            $('input.mail-checkbox').each(function(){

                    if ($(this).parent().parent().hasClass('read')) {
                        $(this).prop('checked',true);
                    }
                    else
                        $(this).prop('checked',false);
            });
        };

         $scope.selectUnread = function () {
            $('input.mail-checkbox').each(function(){

                    if ($(this).parent().parent().hasClass('unread')) {
                        $(this).prop('checked',true);
                    }
                    else
                        $(this).prop('checked',false);
            });
        };

        $scope.selectNone = function () {
            $('input.mail-checkbox').each(function(){
                   $(this).prop('checked',false);

            });
            $('#checkAll').prop('checked',false);
        };

        // mark all as read
        $scope.massRead = function () {
            var idArr = [];
            $('input.mail-checkbox').each(function(){
                    if ($(this).is(':checked')) {
                        idArr.push($(this).attr('rel'));
                    };
            });
            for (var i = 0; i < $scope.messages.length; i++) {
                if( $scope.messages[i].status == 'unread' && idArr.indexOf($scope.messages[i]._id) !== -1)
                    $scope.messages[i].$update();
            };
        };

        $scope.massUnread = function () {
            // body...
        };

        // remove msg
        $scope.massDelete = function () {
            $scope.showLoading = true;
            var idArr = [];
            $('input.mail-checkbox').each(function(){
                    if ($(this).is(':checked')) {
                        idArr.push($(this).attr('rel'));
                    };
            });
            Messages.clean({ids : idArr.join(',') , inbox : $scope.isInbox, trash : $scope.isTrash , schedule : $scope.isSchedule},function(resp){
                // reset pagination
                $scope.totals -= idArr.length;

                $scope.totalsPage = Math.ceil($scope.totals / $scope.limit);

                if($scope.totalsPage < 1)
                    $scope.totalsPage = 1;

                if ($scope.pageNumber > $scope.totalsPage) {
                    $scope.pageNumber = $scope.totalsPage;
                };
                if ($scope.isSchedule) {
                        $scope.schedule();
                }
                else
                {
                    $scope.find();
                }

                $('#checkAll').prop('checked',false);
            });

        };

        // pagination

        $scope.nextPage = function(){
            if($scope.pageNumber < $scope.totalsPage)
            {
                $('#checkAll').prop('checked',false);
                $scope.showLoading = true;
                $scope.pageNumber += 1;
                $scope.getMessages($scope.pageNumber);
            }
        };

        $scope.previousPage = function(){
            if($scope.pageNumber > 1 )
            {
                $('#checkAll').prop('checked',false);
                $scope.showLoading = true;
                $scope.pageNumber -= 1;
                $scope.getMessages($scope.pageNumber);
            }
        }

        // send msg
         $scope.send = function() {
            this.recipient = $('#selectRecipient').select2('val');

            if (!this.recipient.length) {
                    alert('Please select at least on recipient!');
                    return;
            }

            if ($scope.repeatChecked) {
               // find the select weekly
               var selectWeeklyArr = [];
               $('#selectWeekly').find('input').each(function(){
                    if ($(this).is(':checked')) {
                        selectWeeklyArr.push($(this).attr('name'));
                    };
               });
               if (!selectWeeklyArr.length) {
                    alert('Please select at least on day!');
                    return;
               } else {
                    selectWeeklyArr.push($scope.repeatTime);
                    $scope.repeatTime = selectWeeklyArr.join(',');
               }
            }

            var message = new Messages({
                receiver : this.recipient,
                message: this.message,
                file : $scope.fileName,
                repeatChecked : $scope.repeatChecked,
                occurChecked : $scope.occurChecked,
                repeat : $scope.repeatTime
            });

            message.$save(function(response) {
                $scope.find();
                Socket.emit('createMessage', {message : message , user : $scope.global.user});
                $('#uploadfile').val('');
                $('#exampleInputEmail3').tokenfield('createToken', '');

            });
            $('#myModal').modal('hide');
            $('#myModalMessage').modal('hide');
            this.recipient = '';
            this.message = '';
            // reset upload file
            $scope.fileName = "";
            $scope.repeatChecked = false;
            $scope.occurChecked = false;
        };

        // upload file
        $scope.onFileSelect = function($files) {
        var dlg = dialogs.wait('Upload your file',undefined,$scope.uploadProgress);
        for (var i = 0; i < $files.length; i++) {
          var file = $files[i];
          $scope.upload = $upload.upload({
            url: '/upload',
            method: 'POST',
            withCredentials: true,
            file: file
          }).progress(function(evt) {
             $scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total);
             if ($scope.uploadProgress == 100) {
                $scope.$broadcast('dialogs.wait.complete');
                $scope.uploadProgress = 0;
             }
             else
                $scope.$broadcast('dialogs.wait.progress',{'progress' : $scope.uploadProgress});
          })
          .success(function(data, status, headers, config) {
            // file is uploaded successfully
            $scope.fileName = data.files.file.name;
          });
        }

      };
    }
]);
<|endoftext|>


<commit_before>var d3 = require('./d3.format')

/* o looks like :
	{
		value: 567.456,
		unitLabel: 'km'
	}
	*/
function format(fmt, o, appendUnit) {
  /*
		We're using d3's value formatter https://github.com/mbostock/d3/wiki/Formatting
		*/
  if (o.value === Infinity || o.value === -Infinity) {
    o.value = NaN // These are correctly handled by the formatter
  }
  var unit = appendUnit && o.unitLabel ? ' ' + o.unitLabel : ''
  var number = o.value === 0 ? "0" : d3.format(fmt)(o.value)
  return number + unit
}

/* Outputs for example 2.5k, 26k, or 245k. */
function shortFormat(o, sign) {

  sign = sign || ''

  var v = Math.abs(o.value)

  if (v < 0.1) {
    return format(sign + ".2f", o)
  } else if (v < 1) {
    return format(sign + ".1f", o)
  } else if (v >= 1) {
    var reste = d3.formatPrefix(v).scale(v) // e.g. 17 892 -> 17.892
    if (reste < 10 && d3.round(reste) === reste) {
      return format(sign + ".1s", o)
    } else if (reste < 100) {
      return format(sign + ".2s", o)
    } else {
      return format(sign + ".3s", o)
    }
  }
}

/* A more precise, common (US) representation of the value, e.g. 1,234,567.78 */
function altValue(o, sign) {

  sign = sign || ''
  var p = o.precision

  /* Do not add a .00 when value is a round number (thanks d3 for not including this... or did I miss something ?) */
  if (d3.round(o.value) === o.value) {
    p = 0
  } else if (p == undefined) {
    /* default precision */
    p = 2
  }

  return format(sign + ",." + p + "f", o, true)
}

function rounded(o, sign) {

  sign = sign || ''

  /* Do not add a .00 when value is a round number (thanks d3 for not including this... or did I miss something ?) */

  return format(sign + ",.0f", o, true)
}

module.exports = {

  //TODO test this
  'regular': function(o) {
    return {
      regular: shortFormat(o),
      alt: altValue(o),
      rounded: rounded(o),
      unitLabel: o.unitLabel
    }
  },
  'evolution': function(o) {
    return {
      regular: shortFormat(o, '+'),
      alt: altValue(o, '+'),
      unitLabel: o.unitLabel
    }
  },
  'percentage': function(o) {
    return {
      regular: format(".0%", o),
      alt: format(".2%", o),
      unitLabel: o.unitLabel
    }
  },
  'evolutionPercentage': function(o) {
    return {
      regular: format("+.0%", o),
      alt: format("+.2%", o),
      unitLabel: o.unitLabel
    }
  },
  'humanPercentage': function(o) {
    /* Get a human version of a percentage : 1/3, half, about 20%, ... */
    var human = '1/3' //TODO
    return {
      regular: human,
      alt: format(".0%", o),
      unitLabel: o.unitLabel
    }
  },
  /* Format 185 seconds to 3:5 minutes (alt : 3 minutes and 5 seconds) (TODO)*/
  'minutesSeconds': function(o) {
    return {
      regular: D.formatDuration(o.value),
      alt: D.formatDurationFull(o.value)
    }
  },
  'superShort': function(o) {
    return {
      regular: format(".1s", o),
      alt: format(",.2f", o),
      unitLabel: o.unitLabel
    }
  },

};
<commit_msg>d3.format was overriding d3<commit_after>require('./d3.format')

/* o looks like :
	{
		value: 567.456,
		unitLabel: 'km'
	}
	*/
function format(fmt, o, appendUnit) {
  /*
		We're using d3's value formatter https://github.com/mbostock/d3/wiki/Formatting
		*/
  if (o.value === Infinity || o.value === -Infinity) {
    o.value = NaN // These are correctly handled by the formatter
  }
  var unit = appendUnit && o.unitLabel ? ' ' + o.unitLabel : ''
  var number = o.value === 0 ? "0" : d3.format(fmt)(o.value)
  return number + unit
}

/* Outputs for example 2.5k, 26k, or 245k. */
function shortFormat(o, sign) {

  sign = sign || ''

  var v = Math.abs(o.value)

  if (v < 0.1) {
    return format(sign + ".2f", o)
  } else if (v < 1) {
    return format(sign + ".1f", o)
  } else if (v >= 1) {
    var reste = d3.formatPrefix(v).scale(v) // e.g. 17 892 -> 17.892
    if (reste < 10 && d3.round(reste) === reste) {
      return format(sign + ".1s", o)
    } else if (reste < 100) {
      return format(sign + ".2s", o)
    } else {
      return format(sign + ".3s", o)
    }
  }
}

/* A more precise, common (US) representation of the value, e.g. 1,234,567.78 */
function altValue(o, sign) {

  sign = sign || ''
  var p = o.precision

  /* Do not add a .00 when value is a round number (thanks d3 for not including this... or did I miss something ?) */
  if (d3.round(o.value) === o.value) {
    p = 0
  } else if (p == undefined) {
    /* default precision */
    p = 2
  }

  return format(sign + ",." + p + "f", o, true)
}

function rounded(o, sign) {

  sign = sign || ''

  /* Do not add a .00 when value is a round number (thanks d3 for not including this... or did I miss something ?) */

  return format(sign + ",.0f", o, true)
}

module.exports = {

  //TODO test this
  'regular': function(o) {
    return {
      regular: shortFormat(o),
      alt: altValue(o),
      rounded: rounded(o),
      unitLabel: o.unitLabel
    }
  },
  'evolution': function(o) {
    return {
      regular: shortFormat(o, '+'),
      alt: altValue(o, '+'),
      unitLabel: o.unitLabel
    }
  },
  'percentage': function(o) {
    return {
      regular: format(".0%", o),
      alt: format(".2%", o),
      unitLabel: o.unitLabel
    }
  },
  'evolutionPercentage': function(o) {
    return {
      regular: format("+.0%", o),
      alt: format("+.2%", o),
      unitLabel: o.unitLabel
    }
  },
  'humanPercentage': function(o) {
    /* Get a human version of a percentage : 1/3, half, about 20%, ... */
    var human = '1/3' //TODO
    return {
      regular: human,
      alt: format(".0%", o),
      unitLabel: o.unitLabel
    }
  },
  /* Format 185 seconds to 3:5 minutes (alt : 3 minutes and 5 seconds) (TODO)*/
  'minutesSeconds': function(o) {
    return {
      regular: D.formatDuration(o.value),
      alt: D.formatDurationFull(o.value)
    }
  },
  'superShort': function(o) {
    return {
      regular: format(".1s", o),
      alt: format(",.2f", o),
      unitLabel: o.unitLabel
    }
  },

};
<|endoftext|>