<commit_before>import h from 'spec/spec_helper';
import { _, path, lazy_require, fsAsync } from 'azk';
import { defer, async, all } from 'azk/utils/promises';

var lazy = lazy_require({
  Sync         : ['azk/sync'],
  Worker       : ['azk/sync/worker'],
  EventEmitter : ['events'],
  semver       : 'semver'
});

describe("Azk sync, Worker module", function() {
  var worker;
  var example_fixtures = h.fixture_path('sync/test_1/');
  var invalid_fixtures = path.join(h.fixture_path('sync/test_1/'), 'invalid');

  function make_copy() {
    return all([
      h.copyToTmp(example_fixtures),
      h.tmp_dir()
    ]);
  }

  function create_worker() {
    class FakeProcess extends lazy.EventEmitter {
      send(...args) {
        return this.emit('sending', ...args);
      }
    }
    var bus = new FakeProcess();
    worker  = new lazy.Worker(bus);
    return [bus, worker];
  }

  function run_and_wait_msg(bus, filter, block = null) {
    return async(function* () {
      if (_.isFunction(filter)) {
        [filter, block] = [null, filter];
      }
      var wait = defer((resolve) => {
        bus.on('sending', (msg) => {
          msg = JSON.parse(msg);
          if (_.isEmpty(filter)) {
            return resolve(msg);
          } else if (msg.op === filter) {
            return resolve(msg);
          }
        });
      });

      yield block();

      return wait;
    });
  }

  afterEach(() => worker.unwatch());

  describe("with a watch to sync a two folders", function() {
    var origin, dest, bus;
    beforeEach(() => {
      return async(function* () {
        [origin, dest] = yield make_copy();
        [bus] = create_worker();
        yield fsAsync.remove(path.join(origin, ".syncignore"));

        var msg = yield run_and_wait_msg(bus, "watch", () => {
          return bus.emit("message", { origin, destination: dest });
        });

        h.expect(msg).to.have.property('op', 'watch');
        h.expect(msg).to.have.property('status', 'ready');
      });
    });

    it("should have done initial sync", function() {
      var result = h.diff(origin, dest);
      return h.expect(result).to.eventually.have.property('deviation', 0);
    });

    it("should sync a added file", function() {
      return async(function* () {
        var file = "bar/foo.bar.txt";
        var origin_file = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.writeFile(origin_file, "foobar");
        });

        h.expect(msg).to.have.property('op', 'add');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var result = yield h.diff(origin, dest);
        return h.expect(result).to.have.property('deviation', 0);
      });
    });

    it("should sync a changed file", function() {
      return async(function* () {
        var file = "foo/Moe.txt";
        var origin_file = path.join(origin, file);
        var dest_file   = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.writeFile(origin_file, "foobar");
        });

        h.expect(msg).to.have.property('op', 'change');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var content = yield fsAsync.readFile(dest_file);
        h.expect(content.toString()).to.equal("foobar");
      });
    });

    it("should sync a removed file", function() {
      return async(function* () {
        var file = "foo/Moe.txt";
        var origin_file = path.join(origin, file);
        var dest_file   = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.remove(origin_file);
        });
        h.expect(msg).to.have.property('op', 'unlink');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var exists = yield fsAsync.exists(dest_file);
        h.expect(exists).to.fail;
      });
    });

    it("should sync a removed folder", function() {
      return async(function* () {
        var folder = "foo";
        var origin_folder = path.join(origin, folder);
        var dest_folder   = path.join(origin, folder);

        // Save all msgs
        var wait_msgs = defer((resolve) => {
          var msgs = [];
          bus.on('sending', (msg) => {
            msgs.push(JSON.parse(msg));
            if (msgs.length >= 3) { resolve(msgs); }
          });
        });

        var msg = yield run_and_wait_msg(bus, 'unlinkDir', () => {
          return fsAsync.remove(origin_folder);
        });
        h.expect(msg).to.have.property('op', 'unlinkDir');
        h.expect(msg).to.have.property('filepath', folder);
        h.expect(msg).to.have.property('status', 'done');

        // Check unlink partials
        var msgs = yield wait_msgs;
        h.expect(msgs).to.include.something.that.deep.eql(
          {"op": "unlink", "status":"done", "filepath":"foo/Barney.txt"}
        );
        h.expect(msgs).to.include.something.that.deep.eql(
          {"op": "unlink", "status":"done", "filepath":"foo/Moe.txt"}
        );

        var exists = yield fsAsync.exists(dest_folder);
        h.expect(exists).to.fail;
      });
    });
  });

  it("should forward sync options", function* () {
    var [origin, dest]  = yield make_copy();
    var [bus] = create_worker();
    var opts  = { except: ["foo/"] };

    var msg = yield run_and_wait_msg(bus, () => {
      return bus.emit("message", { origin, destination: dest, opts });
    });

    h.expect(msg).to.have.property('op', 'sync');
    h.expect(msg).to.have.property('status', 'done');

    var exists = yield fsAsync.exists(path.join(dest, "foo"));
    h.expect(exists).to.fail;

    exists = yield fsAsync.exists(path.join(dest, "bar"));
    h.expect(exists).to.ok;
  });

  it("should not include content patterns files from except_from option", function* () {
    var [origin, dest]  = yield make_copy();
    worker = create_worker()[1];
    yield worker.watch(origin, dest, {
      except_from: h.fixture_path("sync/rsyncignore.txt"),
      except: [ "ignored" ]
    });

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "bar/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.not.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.not.match(/bar\/Fred.txt/);
    h.expect(msgs).to.match(/foo\/Moe.txt/);
  });

  it("should exclude the .gitignore content for default", function* () {
    var [origin, dest]  = yield make_copy();
    yield fsAsync.writeFile(path.join(origin, ".gitignore"), "ignored/");
    yield fsAsync.remove(path.join(origin, ".syncignore"));

    worker = create_worker()[1];
    yield worker.watch(origin, dest, {});

    var exists = yield fsAsync.exists(path.join(dest, "ignored"));
    h.expect(exists).to.be.not.ok;

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.not.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.match(/foo\/Moe.txt/);
  });

  it("should exclude the .syncignore content for default in preference to .gitignore", function* () {
    var [origin, dest]  = yield make_copy();

    worker = create_worker()[1];
    yield worker.watch(origin, dest, {});

    var exists = yield fsAsync.exists(path.join(dest, "ignored"));
    h.expect(exists).to.be.ok;

    exists = yield fsAsync.exists(path.join(dest, "foo"));
    h.expect(exists).to.be.not.ok;

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.not.match(/foo\/Moe.txt/);
  });

  it("should not override a worker", function() {
    return async(function* () {
      var [origin, dest]  = yield make_copy();
      var [bus] = create_worker();
      var opts  = { except: ["foo/"] };

      var msg = yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest, opts });
      });

      h.expect(msg).to.have.property('op', 'sync');
      h.expect(msg).to.have.property('status', 'done');

      var wait_msgs = defer((resolve) => {
        var msgs = [];
        bus.on('sending', (msg) => {
          msgs.push(JSON.parse(msg));
          if (msgs.length >= 2) { resolve(msgs); }
        });
      });

      yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest, opts });
      });

      var msgs = yield wait_msgs;
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "sync", "status":"close"}
      );
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "sync", "status":"done"}
      );
    });
  });

  it("should return a error if initial sync fails", function() {
    return async(function* () {
      var origin = yield h.tmp_dir();
      var dest   = path.join(yield h.tmp_dir(), "foo", "bar");
      var [bus]  = create_worker();
      var rsync_version = yield lazy.Sync.version();

      var msg = yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest });
      });

      h.expect(msg).to.have.property('op', 'sync');
      h.expect(msg).to.have.property('status', 'fail');

      if (lazy.semver.cmp(rsync_version, '>=', '3.1.0')) {
        h.expect(msg).to.have.deep.property('err.code', 3);
        h.expect(msg).to.have.deep.property('err.err').and.match(/Error: rsync.*3/);
      } else {
        h.expect(msg).to.have.deep.property('err.code', 12);
        h.expect(msg).to.have.deep.property('err.err').and.match(/Error: rsync.*12/);
      }
    });
  });

  it("should return a error if origin not exists", function() {
    return async(function* () {
      var origin = invalid_fixtures;
      var dest   = yield h.tmp_dir();
      var [bus]  = create_worker();

      var msg = yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest });
      });

      h.expect(msg).to.have.property('op', 'sync');
      h.expect(msg).to.have.property('status', 'fail');
      h.expect(msg).to.have.deep.property('err.code', 101);
      h.expect(msg).to.have.deep.property('err.err').and.match(/Sync: origin path not exist/);
    });
  });
});
<commit_msg>[better promises] Fixing test regard sync worker<commit_after>import h from 'spec/spec_helper';
import { _, path, lazy_require, fsAsync } from 'azk';
import { defer, async, all } from 'azk/utils/promises';

var lazy = lazy_require({
  Sync         : ['azk/sync'],
  Worker       : ['azk/sync/worker'],
  EventEmitter : ['events'],
  semver       : 'semver'
});

describe("Azk sync, Worker module", function() {
  var worker;
  var example_fixtures = h.fixture_path('sync/test_1/');
  var invalid_fixtures = path.join(h.fixture_path('sync/test_1/'), 'invalid');

  function make_copy() {
    return all([
      h.copyToTmp(example_fixtures),
      h.tmp_dir()
    ]);
  }

  function create_worker() {
    class FakeProcess extends lazy.EventEmitter {
      send(...args) {
        return this.emit('sending', ...args);
      }
    }
    var bus = new FakeProcess();
    worker  = new lazy.Worker(bus);
    return [bus, worker];
  }

  function run_and_wait_msg(bus, filter, block = null) {
    return async(function* () {
      if (_.isFunction(filter)) {
        [filter, block] = [null, filter];
      }
      var wait = defer((resolve) => {
        bus.on('sending', (msg) => {
          msg = JSON.parse(msg);
          if (_.isEmpty(filter)) {
            return resolve(msg);
          } else if (msg.op === filter) {
            return resolve(msg);
          }
        });
      });

      yield block();

      return wait;
    });
  }

  afterEach(() => worker.unwatch());

  describe("with a watch to sync a two folders", function() {
    var origin, dest, bus;
    beforeEach(() => {
      return async(function* () {
        [origin, dest] = yield make_copy();
        [bus] = create_worker();
        yield fsAsync.remove(path.join(origin, ".syncignore"));

        var msg = yield run_and_wait_msg(bus, "watch", () => {
          return bus.emit("message", { origin, destination: dest });
        });

        h.expect(msg).to.have.property('op', 'watch');
        h.expect(msg).to.have.property('status', 'ready');
      });
    });

    it("should have done initial sync", function() {
      var result = h.diff(origin, dest);
      return h.expect(result).to.eventually.have.property('deviation', 0);
    });

    it("should sync a added file", function() {
      return async(function* () {
        var file = "bar/foo.bar.txt";
        var origin_file = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.writeFile(origin_file, "foobar");
        });

        h.expect(msg).to.have.property('op', 'add');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var result = yield h.diff(origin, dest);
        return h.expect(result).to.have.property('deviation', 0);
      });
    });

    it("should sync a changed file", function() {
      return async(function* () {
        var file = "foo/Moe.txt";
        var origin_file = path.join(origin, file);
        var dest_file   = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.writeFile(origin_file, "foobar");
        });

        h.expect(msg).to.have.property('op', 'change');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var content = yield fsAsync.readFile(dest_file);
        h.expect(content.toString()).to.equal("foobar");
      });
    });

    it("should sync a removed file", function() {
      return async(function* () {
        var file = "foo/Moe.txt";
        var origin_file = path.join(origin, file);
        var dest_file   = path.join(origin, file);

        var msg = yield run_and_wait_msg(bus, () => {
          return fsAsync.remove(origin_file);
        });
        h.expect(msg).to.have.property('op', 'unlink');
        h.expect(msg).to.have.property('filepath', file);
        h.expect(msg).to.have.property('status', 'done');

        var exists = yield fsAsync.exists(dest_file);
        h.expect(exists).to.fail;
      });
    });

    it("should sync a removed folder", function() {
      return async(function* () {
        var folder = "foo";
        var origin_folder = path.join(origin, folder);
        var dest_folder   = path.join(origin, folder);

        // Save all messages and call check via event emitter
        var msgs = [];
        bus.on('sending', (msg) => {
          msgs.push(JSON.parse(msg));
          bus.emit('received');
        });

        var wait_msgs = defer((resolve) => {
          bus.on('received', (msg) => {
            if (msgs.length >= 3) { resolve(msgs); }
          });
        });

        var msg = yield run_and_wait_msg(bus, 'unlinkDir', () => {
          return fsAsync.remove(origin_folder);
        });
        h.expect(msg).to.have.property('op', 'unlinkDir');
        h.expect(msg).to.have.property('filepath', folder);
        h.expect(msg).to.have.property('status', 'done');

        // Check unlink partials
        var msgs = yield wait_msgs;
        h.expect(msgs).to.include.something.that.deep.eql(
          {"op": "unlink", "status":"done", "filepath":"foo/Barney.txt"}
        );
        h.expect(msgs).to.include.something.that.deep.eql(
          {"op": "unlink", "status":"done", "filepath":"foo/Moe.txt"}
        );

        var exists = yield fsAsync.exists(dest_folder);
        h.expect(exists).to.fail;
      });
    });
  });

  it("should forward sync options", function* () {
    var [origin, dest]  = yield make_copy();
    var [bus] = create_worker();
    var opts  = { except: ["foo/"] };

    var msg = yield run_and_wait_msg(bus, () => {
      return bus.emit("message", { origin, destination: dest, opts });
    });

    h.expect(msg).to.have.property('op', 'sync');
    h.expect(msg).to.have.property('status', 'done');

    var exists = yield fsAsync.exists(path.join(dest, "foo"));
    h.expect(exists).to.fail;

    exists = yield fsAsync.exists(path.join(dest, "bar"));
    h.expect(exists).to.ok;
  });

  it("should not include content patterns files from except_from option", function* () {
    var [origin, dest]  = yield make_copy();
    worker = create_worker()[1];
    yield worker.watch(origin, dest, {
      except_from: h.fixture_path("sync/rsyncignore.txt"),
      except: [ "ignored" ]
    });

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "bar/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.not.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.not.match(/bar\/Fred.txt/);
    h.expect(msgs).to.match(/foo\/Moe.txt/);
  });

  it("should exclude the .gitignore content for default", function* () {
    var [origin, dest]  = yield make_copy();
    yield fsAsync.writeFile(path.join(origin, ".gitignore"), "ignored/");
    yield fsAsync.remove(path.join(origin, ".syncignore"));

    worker = create_worker()[1];
    yield worker.watch(origin, dest, {});

    var exists = yield fsAsync.exists(path.join(dest, "ignored"));
    h.expect(exists).to.be.not.ok;

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.not.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.match(/foo\/Moe.txt/);
  });

  it("should exclude the .syncignore content for default in preference to .gitignore", function* () {
    var [origin, dest]  = yield make_copy();

    worker = create_worker()[1];
    yield worker.watch(origin, dest, {});

    var exists = yield fsAsync.exists(path.join(dest, "ignored"));
    h.expect(exists).to.be.ok;

    exists = yield fsAsync.exists(path.join(dest, "foo"));
    h.expect(exists).to.be.not.ok;

    var wait = defer((resolve) => {
      worker.chok.on('all', (event, filepath) => {
        resolve(filepath);
      });
    });

    yield fsAsync.writeFile(path.join(origin, "ignored/Fred.txt"), "foobar");
    yield fsAsync.writeFile(path.join(origin, "foo/Moe.txt" ), "foobar");

    var msgs = yield wait;
    h.expect(msgs).to.match(/ignored\/Fred.txt/);
    h.expect(msgs).to.not.match(/foo\/Moe.txt/);
  });

  it("should not override a worker", function() {
    return async(function* () {
      var [origin, dest]  = yield make_copy();
      var [bus] = create_worker();
      var opts  = { except: ["foo/"] };

      var msg = yield run_and_wait_msg(bus,'watch', () => {
        return bus.emit("message", { origin, destination: dest, opts });
      });

      h.expect(msg).to.have.property('op', 'watch');
      h.expect(msg).to.have.property('status', 'ready');

      // Save all messages and call check via event emitter
      msgs = [];
      bus.on('sending', (msg) => {
        msgs.push(JSON.parse(msg));
        bus.emit('received');
      });

      var wait_msgs = defer((resolve) => {
        bus.on('received', () => {
          if (msgs.length >= 3) { resolve(msgs); }
        });
      });

      yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest, opts });
      });

      var msgs = yield wait_msgs;
      h.expect(msgs).to.containSubset([
        {"op": "sync", "status":"close"},
        {"op": "sync", "status":"done"},
        {"op": "watch", "status":"ready"},
      ]);
    });
  });

  it("should return a error if initial sync fails", function() {
    return async(function* () {
      var origin = yield h.tmp_dir();
      var dest   = path.join(yield h.tmp_dir(), "foo", "bar");
      var [bus]  = create_worker();
      var rsync_version = yield lazy.Sync.version();

      var msg = yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest });
      });

      h.expect(msg).to.have.property('op', 'sync');
      h.expect(msg).to.have.property('status', 'fail');

      if (lazy.semver.cmp(rsync_version, '>=', '3.1.0')) {
        h.expect(msg).to.have.deep.property('err.code', 3);
        h.expect(msg).to.have.deep.property('err.err').and.match(/Error: rsync.*3/);
      } else {
        h.expect(msg).to.have.deep.property('err.code', 12);
        h.expect(msg).to.have.deep.property('err.err').and.match(/Error: rsync.*12/);
      }
    });
  });

  it("should return a error if origin not exists", function() {
    return async(function* () {
      var origin = invalid_fixtures;
      var dest   = yield h.tmp_dir();
      var [bus]  = create_worker();

      var msg = yield run_and_wait_msg(bus, () => {
        return bus.emit("message", { origin, destination: dest });
      });

      h.expect(msg).to.have.property('op', 'sync');
      h.expect(msg).to.have.property('status', 'fail');
      h.expect(msg).to.have.deep.property('err.code', 101);
      h.expect(msg).to.have.deep.property('err.err').and.match(/Sync: origin path not exist/);
    });
  });
});
<|endoftext|>


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

var path = require( "path" ),
    normalize = function( p ){ return '"' + path.normalize( p ) + '"'; },
    join = path.join,
    // Make Windows happy, use `node <path>`
    nodeExec = function( p ){ return 'node "' + p + '"'; },
    SLICE = Array.prototype.slice,

    JSLINT = nodeExec( normalize( "./node_modules/jshint/bin/hint" ) ),
    CSSLINT = nodeExec( normalize( "./node_modules/csslint/cli.js" ) ),
    UGLIFY = nodeExec( normalize( "./node_modules/uglify-js/bin/uglifyjs" ) ),
    RJS = nodeExec( normalize( "./node_modules/requirejs/bin/r.js" ) ),
    LESS = nodeExec( normalize( "./node_modules/less/bin/lessc" ) ),
    DOX = normalize( "./tools/dox.py" ),

    SRC_DIR = 'src',

    TEMPLATES_DIR = 'templates',

    DIST_DIR = 'dist',

    DOCS_DIR = 'docs',

    TEST_DIR = 'test',

    DEFAULT_CONFIG = './src/default-config',

    CSS_DIR = 'css',

    BUTTER_LESS_FILE = join( CSS_DIR, "butter.ui.less" ),
    BUTTER_CSS_FILE_COMMENT = "/* THIS FILE WAS GENERATED BY A TOOL, DO NOT EDIT. SEE " +
                              BUTTER_LESS_FILE + " */",
    BUTTER_CSS_FILE = join( CSS_DIR, "/butter.ui.css" ),

    BUTTERED_POPCORN = join( DIST_DIR, '/buttered-popcorn.js' ),
    BUTTERED_POPCORN_MIN = join( DIST_DIR, '/buttered-popcorn.min.js' ),

    PACKAGE_NAME = "butter";

require('shelljs/make');

function checkCSS() {
  echo('### Linting CSS files');

  var dirs = SLICE.call( arguments ).join( ' ' );

  // see cli.js --list-rules.
  var warnings = [
//    "important",
//    "adjoining-classes",
//    "duplicate-background-images",
//    "qualified-headings",
//    "fallback-colors",
//    "empty-rules",
//    "shorthand",
//    "overqualified-elements",
//    "import",
//    "regex-selectors",
//    "rules-count",
//    "font-sizes",
//    "universal-selector",
//    "unqualified-attributes",
    "zero-units"
  ].join(",");

  var errors = [
    "known-properties",
    "compatible-vendor-prefixes",
    "display-property-grouping",
    "duplicate-properties",
    "errors",
    "gradients",
    "font-faces",
    "floats",
    "vendor-prefix"
  ].join(",");

  exec(CSSLINT + ' --warnings=' + warnings +
                 ' --errors=' + errors +
                 ' --quiet --format=compact' +
                 ' ' + dirs);
}

function checkJS(){
  // Takes a string or an array of strings referring to directories.
  echo('### Linting JS files');

  var dirs = SLICE.call( arguments );

  // Get all js and json files in dirs
  var files = "";
  [ /\.js$/, /\.json$/ ].forEach( function( regexp ){
    files += find( dirs ).filter( function( file ) {
        return file.match( regexp );
      }).join(' ') + ' ';
  });

  // jshint with non-errors plus linting of json files
  exec(JSLINT + ' ' + files + ' --show-non-errors --extra-ext json');
}

target.all = function() {
  target.submodules();
  target.check();
  target.build();
};

target.clean = function() {
  rm('-fr', DIST_DIR);
};

target.dist = function() {
  mkdir('-p', DIST_DIR);
};

target.submodules = function() {
  echo('### Updating git submodules');

  exec('git submodule update --init --recursive');
};

target.docs = function() {
  echo('### Creating documentation from src...');
  mkdir('-p', DOCS_DIR);

  var files = find( SRC_DIR ).filter( function( file ) {
    return file.match(/\.js$/);
  });

  var docTypes = [
    'md'
  ];

  for (var i = files.length - 1; i >= 0; i--) {
    echo('### Processing documentation for ' + files[i]);
    for (var j = docTypes.length - 1; j >= 0; j--) {
      var newFileName = DOCS_DIR + '/' + files[i].substring(4).replace(/\//g, '-').replace(/\.js$/, '.' + docTypes[j]),
          command = 'python ' + DOX + ' -t ' + docTypes[j] + ' -o '+ newFileName + ' -i ' + files[i];
      exec(command);
    };
  };
};

target.check = function() {
  checkJS( SRC_DIR );
  checkCSS( CSS_DIR );
};

target['check-templates'] = function() {
  checkJS( TEMPLATES_DIR );
  checkCSS( TEMPLATES_DIR );
};

target['check-css'] = function( dirs ) {
  checkCSS( CSS_DIR );
};

target['check-lint'] = function( dir ) {
  checkJS( SRC_DIR );
};

target['check-tests'] = function( dir ) {
  checkJS( TEST_DIR );
};

// If compress is true, crush CSS down, otherwise leave expanded.
function lessToCSS( compress ){
  echo( "### Building CSS using LESS (" +
        ( compress ? "with" : "without" ) +
        " compression)" );

  var args = compress ? " --yui-compress " : " ",
  result = exec(LESS + args + BUTTER_LESS_FILE, {silent:true});

  if( result.code === 0 ){
    var css = BUTTER_CSS_FILE_COMMENT + "\n\n" + result.output;
    css.to( BUTTER_CSS_FILE );
    target['check-css']();
  } else {
    echo( result.output );
  }
}

target.css = function() {
  // Leave CSS expanded if building in tree (for debugging)
  lessToCSS( false );
};

function build( version ){
  echo('### Building butter');

  target.clean();
  target.dist();

  exec(RJS + ' -o tools/build.js');
  exec(RJS + ' -o tools/build.optimized.js');

  // Stamp Butter.version with supplied version, or git info
  if( !version ){
    version = exec('git describe',
                   {silent:true}).output.replace(/\r?\n/m, "");
  }

  sed('-i', '@VERSION@', version, 'dist/butter.js');
  sed('-i', '@VERSION@', version, 'dist/butter.min.js');

  // Compress CSS for deployment
  lessToCSS( true );

  cp( BUTTER_CSS_FILE, DIST_DIR );
}

target.build = function(){
  build();
};

target.server = function() {
  echo('### Serving butter');

  cd('cornfield');
  exec('node app.js', { async: true });
};

target.package = function() {
  echo('### Making Butter Package');

  target.build();

  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);

  echo('### Creating butter.zip');
  cd(DIST_DIR);
  exec('zip -r ' + PACKAGE_NAME + '.zip ' + ls('.').join(' '));
};

target['buttered-popcorn'] = function(){
  echo('### Making Combined Popcorn for Butter: Buttered Popcorn');

  var defaultConfig = require( DEFAULT_CONFIG ),
      popcornDir = defaultConfig.dirs['popcorn-js'].replace( '{{baseDir}}', './' ),
      players = defaultConfig.player.players,
      plugins = defaultConfig.plugin.plugins,
      popcornFiles = [];

  // Popcorn License Header
  popcornFiles.push( popcornDir + '/LICENSE_HEADER' );

  // classList shim
  popcornFiles.push( './tools/classlist-shim.js' );

  // popcorn IE8 shim
  popcornFiles.push( popcornDir + '/ie8/popcorn.ie8.js' );

  // popcorn.js
  popcornFiles.push( popcornDir + '/popcorn.js' );

  // plugins
  plugins.forEach( function( plugin ){
    popcornFiles.push( plugin.path.replace( '{{baseDir}}', './' ) );
  });

  // module for baseplayer
  popcornFiles.push( popcornDir + '/modules/player/popcorn.player.js' );

  // players
  players.forEach( function( player ){
    popcornFiles.push( player.path.replace( '{{baseDir}}', './' ) );
  });

  // Stamp Popcorn.version with the git commit sha we are using
  var cwd = pwd();
  cd( popcornDir );
  var popcornVersion = exec('git describe',
                       {silent:true}).output.replace(/\r?\n/m, "");
  cd( cwd );

  // Write out dist/buttered-popcorn.js
  cat( popcornFiles ).to( BUTTERED_POPCORN );
  sed('-i', '@VERSION', popcornVersion, BUTTERED_POPCORN);

  // Write out dist/buttered-popcorn.min.js
  exec( UGLIFY + ' --output ' + BUTTERED_POPCORN_MIN + ' ' + BUTTERED_POPCORN );
};

target.release = function() {
  echo('### Making Butter Release');

  // To pass a release version number, use:
  // $ VERSION=0.5 node make release
  var version = env['VERSION'];

  if( !version ){
    console.log( "ERROR: Must provide a version when building a release: VERSION=XXX node make release" );
    return;
  }

  build( version );

  // Build buttered-popcorn.js
  target['buttered-popcorn']();

  // Copy over templates and other resources
  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);

  echo('### Creating butter.zip');
  cd(DIST_DIR);
  exec('zip -r ' + PACKAGE_NAME + '.zip ' + ls('.').join(' '));
};

target.beautify = function( a ) {
  echo('### Beautifying butter');
  cd('tools');
  exec('./beautify.sh');
};

target.test = function() {
  var unbeautified = [ "if.js", "for.js", "while.js", "array.js", "function.js", "object.js", "comments.js", "eolspace.js" ],
      beautified = [ "if.expected.js", "for.expected.js", "while.expected.js", "array.expected.js", "function.expected.js", "object.expected.js", "comments.expected.js", "eolspace.expected.js" ],
      result;

  echo('### Testing Beautifier');
  for( var i = 0, l = unbeautified.length; i < l; i++ ) {
    result = exec('bash test/beautifier/test.sh ' + unbeautified[ i ] + ' ' + beautified[ i ]);
    rm('tmp.txt');
    // checking against a length of 1 because if the output is empty a newline character gets returned
    if( result.output.length === 1 ) {
      echo(unbeautified[ i ] + ' was beautified correctly');
    } else {
      echo(unbeautified[ i ] + ' did not beautify correctly');
    }
  }
};

target.storycamp = function(){
  echo('### Making single file version of Butter + Popcorn (use UNMINIFIED=1 for unminified)');

  // To get unminified butter.js, use the UNMINIFIED env variable:
  // $ UNMINIFIED=1 node make storycamp
  var unminified = env['UNMINIFIED'] === "1";

  build( 'storycamp' );
  target['buttered-popcorn']();

  var storyCamp = 'butter.js',
      storyCampMin = 'butter.min.js';

  function makeButterJS( keepMe, deleteMe ){
    echo( '### Cleaning temp files' );
    cd( DIST_DIR );
    rm( '-f', deleteMe );

    // Mirror layout in butter/ so templates are happy, renaming to src/butter.js
    mkdir( 'src' );
    mv( keepMe, './src/butter.js' );
  }

  var cwd = pwd();

  // Depending on whether we want minified source, keep one, delete one.
  if( unminified ){
    makeButterJS( storyCamp, storyCampMin );
  } else {
    // Write out dist/storycamp-butter.min.js
    exec( UGLIFY + ' --output ' + DIST_DIR + '/' + storyCampMin + ' ' + DIST_DIR + '/' + storyCamp );
    makeButterJS( storyCampMin, storyCamp );
  }

  // Move css files into dist/css
  mkdir('css');
  mv('*.css', './css');

  // Copy other assets over
  cd(cwd);
  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);
  cp('-R', 'cornfield', DIST_DIR);

  // Copy the popcorn test videos over
  mkdir('-p', './dist/external/popcorn-js/test');
  cp('external/popcorn-js/test/trailer.*', './dist/external/popcorn-js/test');

  // Copy the rest of the popcorn plugins over in case templates look for them
  mkdir('-p', './dist/external/popcorn-js/plugins');
  cp('-R', 'external/popcorn-js/plugins/*', './dist/external/popcorn-js/plugins');

  // Export will need a version of popcorn.js where the templates expect it
  // at dist/external/popcorn-js/popcorn.js
  cd( DIST_DIR );
  if( unminified ){
    mv( 'buttered-popcorn.js', './external/popcorn-js/popcorn.js' );
    rm( '-f', 'buttered-popcorn.min.js' );
  } else {
    mv( 'buttered-popcorn.min.js', './external/popcorn-js/popcorn.js' );
    rm( '-f', 'buttered-popcorn.js' );
  }
};
<commit_msg>[t1874] Have CSS Lint ignore -moz-appearance rule<commit_after>#!/usr/bin/env node

var path = require( "path" ),
    normalize = function( p ){ return '"' + path.normalize( p ) + '"'; },
    join = path.join,
    // Make Windows happy, use `node <path>`
    nodeExec = function( p ){ return 'node "' + p + '"'; },
    SLICE = Array.prototype.slice,

    JSLINT = nodeExec( normalize( "./node_modules/jshint/bin/hint" ) ),
    CSSLINT = nodeExec( normalize( "./node_modules/csslint/cli.js" ) ),
    UGLIFY = nodeExec( normalize( "./node_modules/uglify-js/bin/uglifyjs" ) ),
    RJS = nodeExec( normalize( "./node_modules/requirejs/bin/r.js" ) ),
    LESS = nodeExec( normalize( "./node_modules/less/bin/lessc" ) ),
    DOX = normalize( "./tools/dox.py" ),

    SRC_DIR = 'src',

    TEMPLATES_DIR = 'templates',

    DIST_DIR = 'dist',

    DOCS_DIR = 'docs',

    TEST_DIR = 'test',

    DEFAULT_CONFIG = './src/default-config',

    CSS_DIR = 'css',

    BUTTER_LESS_FILE = join( CSS_DIR, "butter.ui.less" ),
    BUTTER_CSS_FILE_COMMENT = "/* THIS FILE WAS GENERATED BY A TOOL, DO NOT EDIT. SEE " +
                              BUTTER_LESS_FILE + " */",
    BUTTER_CSS_FILE = join( CSS_DIR, "/butter.ui.css" ),

    BUTTERED_POPCORN = join( DIST_DIR, '/buttered-popcorn.js' ),
    BUTTERED_POPCORN_MIN = join( DIST_DIR, '/buttered-popcorn.min.js' ),

    PACKAGE_NAME = "butter";

require('shelljs/make');

// To supress CSS warnings/errors for a particular line, end the line
// with a comment indicating you want CSS Lint to ignore this line's
// error(s).  Here are some examples:
//
//   -webkit-appearance: button; /* csslint-ignore */
//   -webkit-appearance: button; /*csslint-ignore*/
//   -webkit-appearance: button; /* csslint-ignore: This is being done because of iOS ... */
function checkCSSFile( filename, warnings, errors ) {
  var fileLines = cat( filename ).split( /\r?\n/ ),
    ignoreLines = "",
    // Look for: "blah blah blah /* csslint-ignore */" or
    //           "blah blah /*csslint-ignore: this is my reason*/"
    ignoreRegex = /\/\*\s*csslint-ignore[^*]*\*\/$/,
    // Errors look like: "css/butter.ui.css: line 186, col 3, Error..."
    lineRegex = /\: line (\d+),/;

  // Build a map of lines to ignore: "|14||27|" means ignore lines 14 and 27
  for( var i=0; i < fileLines.length; i++ ){
    if( ignoreRegex.test( fileLines[ i ] ) ) {
      ignoreLines += "|" + i + "|";
    }
  }

  // Run CSSLint across the file, check for errors/warnings and ignore if
  // they are ones we know about from above.
  var outputLines = exec(CSSLINT +
    ' --warnings=' + warnings +
    ' --errors=' + errors +
    ' --quiet --format=compact' +
    ' ' + filename, { silent: true } ).output.split( /\r?\n/ )
    .forEach( function( line ) {
      if( !line ) {
        return;
      }
      var number = "|" + lineRegex.exec( line )[1] + "|";
      if( ignoreLines.indexOf( number ) === -1 ) {
        echo( line );
      }
  });
}

function checkCSS( dir ) {
  echo('### Linting CSS files');

  // see cli.js --list-rules.
  var warnings = [
//    "important",
//    "adjoining-classes",
//    "duplicate-background-images",
//    "qualified-headings",
//    "fallback-colors",
//    "empty-rules",
//    "shorthand",
//    "overqualified-elements",
//    "import",
//    "regex-selectors",
//    "rules-count",
//    "font-sizes",
//    "universal-selector",
//    "unqualified-attributes",
    "zero-units"
  ].join(",");

  var errors = [
    "known-properties",
    "compatible-vendor-prefixes",
    "display-property-grouping",
    "duplicate-properties",
    "errors",
    "gradients",
    "font-faces",
    "floats",
    "vendor-prefix"
  ].join(",");

  var files = ls( dir );
  files.forEach( function( filename ) {
    filename = join( dir, filename );
    if( /\.css$/.test( filename ) ) {
      checkCSSFile( filename, warnings, errors );
    }
  });

  // CSS Lint seems to want to print 2 blank lines when done.
  echo('\n\n');
}

function checkJS(){
  // Takes a string or an array of strings referring to directories.
  echo('### Linting JS files');

  var dirs = SLICE.call( arguments );

  // Get all js and json files in dirs
  var files = "";
  [ /\.js$/, /\.json$/ ].forEach( function( regexp ){
    files += find( dirs ).filter( function( file ) {
        return file.match( regexp );
      }).join(' ') + ' ';
  });

  // jshint with non-errors plus linting of json files
  exec(JSLINT + ' ' + files + ' --show-non-errors --extra-ext json');
}

target.all = function() {
  target.submodules();
  target.check();
  target.build();
};

target.clean = function() {
  rm('-fr', DIST_DIR);
};

target.dist = function() {
  mkdir('-p', DIST_DIR);
};

target.submodules = function() {
  echo('### Updating git submodules');

  exec('git submodule update --init --recursive');
};

target.docs = function() {
  echo('### Creating documentation from src...');
  mkdir('-p', DOCS_DIR);

  var files = find( SRC_DIR ).filter( function( file ) {
    return file.match(/\.js$/);
  });

  var docTypes = [
    'md'
  ];

  for (var i = files.length - 1; i >= 0; i--) {
    echo('### Processing documentation for ' + files[i]);
    for (var j = docTypes.length - 1; j >= 0; j--) {
      var newFileName = DOCS_DIR + '/' + files[i].substring(4).replace(/\//g, '-').replace(/\.js$/, '.' + docTypes[j]),
          command = 'python ' + DOX + ' -t ' + docTypes[j] + ' -o '+ newFileName + ' -i ' + files[i];
      exec(command);
    };
  };
};

target.check = function() {
  checkJS( SRC_DIR );
  checkCSS( CSS_DIR );
};

target['check-templates'] = function() {
  checkJS( TEMPLATES_DIR );
  checkCSS( TEMPLATES_DIR );
};

target['check-css'] = function( dirs ) {
  checkCSS( CSS_DIR );
};

target['check-lint'] = function( dir ) {
  checkJS( SRC_DIR );
};

target['check-tests'] = function( dir ) {
  checkJS( TEST_DIR );
};

// If compress is true, crush CSS down, otherwise leave expanded.
function lessToCSS( compress ){
  echo( "### Building CSS using LESS (" +
        ( compress ? "with" : "without" ) +
        " compression)" );

  var args = compress ? " --yui-compress " : " ",
  result = exec(LESS + args + BUTTER_LESS_FILE, {silent:true});

  if( result.code === 0 ){
    var css = BUTTER_CSS_FILE_COMMENT + "\n\n" + result.output;
    css.to( BUTTER_CSS_FILE );
    // Our /* csslint-ignore */ override can't work when compressed.
    // People should lint on their own separate to that.
    if( !compress ) {
      target['check-css']();
    }
  } else {
    echo( result.output );
  }
}

target.css = function() {
  // Leave CSS expanded if building in tree (for debugging)
  lessToCSS( false );
};

function build( version ){
  echo('### Building butter');

  target.clean();
  target.dist();

  exec(RJS + ' -o tools/build.js');
  exec(RJS + ' -o tools/build.optimized.js');

  // Stamp Butter.version with supplied version, or git info
  if( !version ){
    version = exec('git describe',
                   {silent:true}).output.replace(/\r?\n/m, "");
  }

  sed('-i', '@VERSION@', version, 'dist/butter.js');
  sed('-i', '@VERSION@', version, 'dist/butter.min.js');

  // Compress CSS for deployment
  lessToCSS( true );

  cp( BUTTER_CSS_FILE, DIST_DIR );
}

target.build = function(){
  build();
};

target.server = function() {
  echo('### Serving butter');

  cd('cornfield');
  exec('node app.js', { async: true });
};

target.package = function() {
  echo('### Making Butter Package');

  target.build();

  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);

  echo('### Creating butter.zip');
  cd(DIST_DIR);
  exec('zip -r ' + PACKAGE_NAME + '.zip ' + ls('.').join(' '));
};

target['buttered-popcorn'] = function(){
  echo('### Making Combined Popcorn for Butter: Buttered Popcorn');

  var defaultConfig = require( DEFAULT_CONFIG ),
      popcornDir = defaultConfig.dirs['popcorn-js'].replace( '{{baseDir}}', './' ),
      players = defaultConfig.player.players,
      plugins = defaultConfig.plugin.plugins,
      popcornFiles = [];

  // Popcorn License Header
  popcornFiles.push( popcornDir + '/LICENSE_HEADER' );

  // classList shim
  popcornFiles.push( './tools/classlist-shim.js' );

  // popcorn IE8 shim
  popcornFiles.push( popcornDir + '/ie8/popcorn.ie8.js' );

  // popcorn.js
  popcornFiles.push( popcornDir + '/popcorn.js' );

  // plugins
  plugins.forEach( function( plugin ){
    popcornFiles.push( plugin.path.replace( '{{baseDir}}', './' ) );
  });

  // module for baseplayer
  popcornFiles.push( popcornDir + '/modules/player/popcorn.player.js' );

  // players
  players.forEach( function( player ){
    popcornFiles.push( player.path.replace( '{{baseDir}}', './' ) );
  });

  // Stamp Popcorn.version with the git commit sha we are using
  var cwd = pwd();
  cd( popcornDir );
  var popcornVersion = exec('git describe',
                       {silent:true}).output.replace(/\r?\n/m, "");
  cd( cwd );

  // Write out dist/buttered-popcorn.js
  cat( popcornFiles ).to( BUTTERED_POPCORN );
  sed('-i', '@VERSION', popcornVersion, BUTTERED_POPCORN);

  // Write out dist/buttered-popcorn.min.js
  exec( UGLIFY + ' --output ' + BUTTERED_POPCORN_MIN + ' ' + BUTTERED_POPCORN );
};

target.release = function() {
  echo('### Making Butter Release');

  // To pass a release version number, use:
  // $ VERSION=0.5 node make release
  var version = env['VERSION'];

  if( !version ){
    echo( "ERROR: Must provide a version when building a release: VERSION=XXX node make release" );
    return;
  }

  build( version );

  // Build buttered-popcorn.js
  target['buttered-popcorn']();

  // Copy over templates and other resources
  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);

  echo('### Creating butter.zip');
  cd(DIST_DIR);
  exec('zip -r ' + PACKAGE_NAME + '.zip ' + ls('.').join(' '));
};

target.beautify = function( a ) {
  echo('### Beautifying butter');
  cd('tools');
  exec('./beautify.sh');
};

target.test = function() {
  var unbeautified = [ "if.js", "for.js", "while.js", "array.js", "function.js", "object.js", "comments.js", "eolspace.js" ],
      beautified = [ "if.expected.js", "for.expected.js", "while.expected.js", "array.expected.js", "function.expected.js", "object.expected.js", "comments.expected.js", "eolspace.expected.js" ],
      result;

  echo('### Testing Beautifier');
  for( var i = 0, l = unbeautified.length; i < l; i++ ) {
    result = exec('bash test/beautifier/test.sh ' + unbeautified[ i ] + ' ' + beautified[ i ]);
    rm('tmp.txt');
    // checking against a length of 1 because if the output is empty a newline character gets returned
    if( result.output.length === 1 ) {
      echo(unbeautified[ i ] + ' was beautified correctly');
    } else {
      echo(unbeautified[ i ] + ' did not beautify correctly');
    }
  }
};

target.storycamp = function(){
  echo('### Making single file version of Butter + Popcorn (use UNMINIFIED=1 for unminified)');

  // To get unminified butter.js, use the UNMINIFIED env variable:
  // $ UNMINIFIED=1 node make storycamp
  var unminified = env['UNMINIFIED'] === "1";

  build( 'storycamp' );
  target['buttered-popcorn']();

  var storyCamp = 'butter.js',
      storyCampMin = 'butter.min.js';

  function makeButterJS( keepMe, deleteMe ){
    echo( '### Cleaning temp files' );
    cd( DIST_DIR );
    rm( '-f', deleteMe );

    // Mirror layout in butter/ so templates are happy, renaming to src/butter.js
    mkdir( 'src' );
    mv( keepMe, './src/butter.js' );
  }

  var cwd = pwd();

  // Depending on whether we want minified source, keep one, delete one.
  if( unminified ){
    makeButterJS( storyCamp, storyCampMin );
  } else {
    // Write out dist/storycamp-butter.min.js
    exec( UGLIFY + ' --output ' + DIST_DIR + '/' + storyCampMin + ' ' + DIST_DIR + '/' + storyCamp );
    makeButterJS( storyCampMin, storyCamp );
  }

  // Move css files into dist/css
  mkdir('css');
  mv('*.css', './css');

  // Copy other assets over
  cd(cwd);
  cp('-R', 'resources', DIST_DIR);
  cp('-R', 'templates', DIST_DIR);
  cp('-R', 'cornfield', DIST_DIR);

  // Copy the popcorn test videos over
  mkdir('-p', './dist/external/popcorn-js/test');
  cp('external/popcorn-js/test/trailer.*', './dist/external/popcorn-js/test');

  // Copy the rest of the popcorn plugins over in case templates look for them
  mkdir('-p', './dist/external/popcorn-js/plugins');
  cp('-R', 'external/popcorn-js/plugins/*', './dist/external/popcorn-js/plugins');

  // Export will need a version of popcorn.js where the templates expect it
  // at dist/external/popcorn-js/popcorn.js
  cd( DIST_DIR );
  if( unminified ){
    mv( 'buttered-popcorn.js', './external/popcorn-js/popcorn.js' );
    rm( '-f', 'buttered-popcorn.min.js' );
  } else {
    mv( 'buttered-popcorn.min.js', './external/popcorn-js/popcorn.js' );
    rm( '-f', 'buttered-popcorn.js' );
  }
};
<|endoftext|>


<commit_before>var v = require( '../../src/validation' )();

describe( 'validation/index.js', function () {
    describe( 'validateField', function () {
        var fldID   = 'test';
        var fldMeta = { renderer: 'text', validators: [] };

        it( 'should return `null` instead of errors array if `validators` are unset', function () {
            expect( v.validateField( fldID, fldMeta, null ) )
                .toEqual({ 'test': null });
        });

        it( 'should return error array if `validators` are set and value is not valid', function () {
            var validators = [{ rule: 'required', message: 'err' }];
            fldMeta.validators = validators;
            expect( v.validateField( fldID, fldMeta, null ) )
                .toEqual({ 'test': validators });
        });
    });
});
<commit_msg>Update unit-tests for validation<commit_after>var v = require( '../../src/validation' )();

describe( 'validation/index.js', function () {
    describe( 'validateField', function () {
        var fldID   = 'test';
        var fldMeta = { renderer: 'text', validators: [] };

        it( 'should return `null` instead of errors array if `validators` are unset', function () {
            expect( v.validateField( fldID, fldMeta, null ) )
                .toEqual({ 'test': null });
        });

        it( 'should return array with errors if `validators` are set and value is not valid', function () {
            var validators = [{ rule: 'required', message: 'err' }];
            fldMeta.validators = validators;

            expect( v.validateField( fldID, fldMeta, null ) )
                .toEqual({ 'test': validators });
        });

        it2( 'should return array with only those errors, that if `validators` are set ',
             'and value is not valid', function () {
            var validators = [{ rule: 'required', message: 'err1' },
                              { rule: 'length', value: 5, message: 'err2' }];
            fldMeta.validators = validators;

            expect( v.validateField( fldID, fldMeta, 'test' ) )
                .toEqual({ 'test': [ validators[1] ] });

            expect( v.validateField( fldID, fldMeta, null ) )
                .toEqual({ 'test': validators });
        });
    });
});

function ms () {
    return Array.prototype.join.call( arguments, '' );
}

function it2 () {
    var ss = Array.prototype.slice.call( arguments, 0, -1 );
    var fn = Array.prototype.slice.call( arguments, -1 )[ 0 ];

    return it(ms.apply( null, ss ), fn );
}
<|endoftext|>


<commit_before>function Transaction(attributes){
    this.user_id = attributes.user_id;
    this.coin_name = attributes.coin_name;
    this.id = attributes.id;
    this.money_in = attributes.money_in.toFixed(2);
    this.price_per_coin = attributes.price_per_coin.toFixed(2);
    this.quantity = attributes.quantity.toFixed(4);
}

Transaction.prototype.renderTxItem = function(){
    const src = $("#newly-created-transaction-template").html();
    const template = Handlebars.compile(src);
    const txItem = template(this);
    $("#preprend-new-transaction").prepend(txItem);
    if($("#no-transactions").text()){
        $("#no-transactions").empty();
        $("#no-transactions").removeClass();
        $("[id^='insert-show-more']").parent().removeClass("bb").removeClass("b--light-silver");
    }
}

$(function() {
   $("#list_txs_in_wallet").on('click', function(){
       const coin_id = $(this).data("coinid");
       const user_id = $(this).data("userid");
       const coin_name = $(this).data("coinname");
        $.getJSON(`/users/${user_id}/transactions?coin_id=${coin_id}`,function(APIresponse){
            showTxsWithTemplate(APIresponse);
            changeListLink(coin_name);
        });
   });
   $("ol").on('click',"i", function(){
        const tx_id = $(this).data("txid");
        const user_id = $(this).data("userid");
        const image = $(this).data("image");
        const $thisIcon = $(this)
        $.getJSON(`/users/${user_id}/transactions/${tx_id}`,function(APIresponse){
            changeView(image, $thisIcon, APIresponse, tx_id, user_id);
        });
   });

   $("fieldset#link_field_set").on('click',"#create-a-transaction-form", function(event){
       event.preventDefault();
       const user_id = $(this).data("userId");
       showNewTransactionForm({user_id: user_id});

       $("#add_comment").on('click', "i", function(){
            $("#add_comment i").remove();
            $("#add_comment").removeClass("mt3")
            $("#add_comment").append('Comment:');
            $("#add_comment").append('<textarea class="db pa2 input-reset ba bg-transparent hover-bg-black hover-white" type="text" name="transaction[note_attributes][comments][]" id="transaction_note_attributes_comment" rows="4" cols="30"></textarea>');
            $("#add_comment").append('Add another comment (optional)')
       });

       $("form#new_transaction.new_transaction").on('submit', function(event){
        //Code below is if want to explore caching typed in coin names so drop down options are fresh
        // const typedCoinNames = localStorage['typedCoinNames'];
            event.preventDefault();
            console.log("click");
                   //Code below is if want to explore caching typed in coin names so drop down options are fresh
            // if ($(this)["0"][4].value !== ""){
           //     typedCoinNames.push($(this)["0"][4].value);
           //     localStorage['typedCoinNames'] = typedCoinNames;
           // }
            const params = $(this).serialize();
            const address = $(this).attr("action");
            const user_id =  /\d+/.exec(address)[0]
            const posting = $.ajax({
                type: "POST",
                url: address,
                data: params,
                dataType: "json"
            });
            posting.done(function(APIresponse) {
                console.log("called back")

                // TODO: handle response

                //If APIresponse gives back HTML (meaning error-filled form):
                //.failure
 // if (APIresponse.includes("error")) {
    // console.log("APIresponse has error")
    // debugger;
                     //for error(s) in a form, clear

 and fill in with APIresponse:

    // $('#new_transaction_form').empty();
    //$('#new_transaction_form').html(APIresponse);
                     //potential fix for bug listed below:
                         //take API response and eliminate HTML starting right below div id="new_transaction_form"
                         // until div class="tl_measure_center"?
                                 //the issue is that a class=mesaure_center already exists prior to injection
                     //BUG alert: multiple error form submissions eliminates transaction history at bottom
                     // of the page.  Fix!
                //.failure
              //  } else {
                    console.log("APIresponse is good!")
                    const transaction = new Transaction(APIresponse);
                    transaction.renderTxItem();
                    restoreCreateATransaction(user_id);

                //end

             //   }
                    //delete from index. can you remove completely in js??
            });
        });
    });
});

function checkIfListed(selection) {
    if (selection.value == "-1"){
        $("#another-coin").show();
    } else{
        $("#another-coin").hide();
    }
}
function restoreCreateATransaction(user_id) {
    $("#new_transaction_form").empty();
    $("#create-a-transaction-form").replaceWith(`Create a transaction`)
    $("#link_field_set").addClass("pb4");
}

function showNewTransactionForm(userIdObj) {
    const src = $("#new-transaction-form-template").html();
    const template = Handlebars.compile(src);
    const newForm = template(userIdObj);
    $("#new_transaction_form").html(newForm);
    $("#create-a-transaction-form").replaceWith('
Create a transaction:
')
    $("#link_field_set").removeClass("pb4");
}

function showTxsWithTemplate(txs) {
    const src = $("#transaction-template").html();
    const template = Handlebars.compile(src);
    const txList = template(txs.reverse());
    $("#transactions_in_wallet").html(txList);
}
function showExtraTxDataTemplate(txAdditionalInfo, tx_id, user_id) {
    const src = $("#transaction-show-template").html();

    const template = Handlebars.compile(src);
    const txExtra = template(txAdditionalInfo);
    $(`#insert-show-more-${tx_id}`).html(txExtra);

    const srcForMod = $("#edit-delete-icons-template").html();
    const templateForMod = Handlebars.compile(srcForMod);
    const deleteURL = `/users/${user_id}/transactions/${tx_id}`
    const editURL = `/users/${user_id}/transactions/${tx_id}/edit`
    const modFeature = templateForMod({deleteURL: deleteURL, editURL: editURL });

    $(`#insert-show-more-${tx_id}`).append(modFeature);
    $(`#insert-show-more-${tx_id}`).addClass("pb4");
}
function changeListLink(coinName){
    $("#list_txs_in_wallet").replaceWith(`<p id="list_txs_in_wallet" class="mt0 black-80 b ph3 pv2 ba f6 dib">${coinName} Transaction History</p>`)
}

function changeView(displayedImage, $icon, APIresponse, tx_id, user_id) {
    if (displayedImage === "ellipses"){
        showExtraTxDataTemplate(APIresponse, tx_id, user_id);
        changeToDownArrowIcon($icon);

    } else if (displayedImage === "downArrow") {
        $(`#insert-show-more-${tx_id}`).empty();
        $(`#insert-show-more-${tx_id}`).removeClass("pb4");
        changetoEllipsesIcon($icon);
    }
}

function changeToDownArrowIcon($icon) {
    $icon.data('image', "downArrow");
    $icon.removeClass("fa-ellipsis-h")
    $icon.addClass("fa-caret-down")
}
function changetoEllipsesIcon($icon) {
     $icon.data('image', "ellipses");
     $icon.removeClass("fa-caret-down");
     $icon.addClass("fa-ellipsis-h");
}
<commit_msg>elimnate border around text to make it not look like a button<commit_after>function Transaction(attributes){
    this.user_id = attributes.user_id;
    this.coin_name = attributes.coin_name;
    this.id = attributes.id;
    this.money_in = attributes.money_in.toFixed(2);
    this.price_per_coin = attributes.price_per_coin.toFixed(2);
    this.quantity = attributes.quantity.toFixed(4);
}

Transaction.prototype.renderTxItem = function(){
    const src = $("#newly-created-transaction-template").html();
    const template = Handlebars.compile(src);
    const txItem = template(this);
    $("#preprend-new-transaction").prepend(txItem);
    if($("#no-transactions").text()){
        $("#no-transactions").empty();
        $("#no-transactions").removeClass();
        $("[id^='insert-show-more']").parent().removeClass("bb").removeClass("b--light-silver");
    }
}

$(function() {
   $("#list_txs_in_wallet").on('click', function(){
       const coin_id = $(this).data("coinid");
       const user_id = $(this).data("userid");
       const coin_name = $(this).data("coinname");
        $.getJSON(`/users/${user_id}/transactions?coin_id=${coin_id}`,function(APIresponse){
            showTxsWithTemplate(APIresponse);
            changeListLink(coin_name);
        });
   });
   $("ol").on('click',"i", function(){
        const tx_id = $(this).data("txid");
        const user_id = $(this).data("userid");
        const image = $(this).data("image");
        const $thisIcon = $(this)
        $.getJSON(`/users/${user_id}/transactions/${tx_id}`,function(APIresponse){
            changeView(image, $thisIcon, APIresponse, tx_id, user_id);
        });
   });

   $("fieldset#link_field_set").on('click',"#create-a-transaction-form", function(event){
       event.preventDefault();
       const user_id = $(this).data("userId");
       showNewTransactionForm({user_id: user_id});

       $("#add_comment").on('click', "i", function(){
            $("#add_comment i").remove();
            $("#add_comment").removeClass("mt3")
            $("#add_comment").append('Comment:');
            $("#add_comment").append('<textarea class="db pa2 input-reset ba bg-transparent hover-bg-black hover-white" type="text" name="transaction[note_attributes][comments][]" id="transaction_note_attributes_comment" rows="4" cols="30"></textarea>');
            $("#add_comment").append('Add another comment (optional)')
       });

       $("form#new_transaction.new_transaction").on('submit', function(event){
        //Code below is if want to explore caching typed in coin names so drop down options are fresh
        // const typedCoinNames = localStorage['typedCoinNames'];
            event.preventDefault();
            console.log("click");
                   //Code below is if want to explore caching typed in coin names so drop down options are fresh
            // if ($(this)["0"][4].value !== ""){
           //     typedCoinNames.push($(this)["0"][4].value);
           //     localStorage['typedCoinNames'] = typedCoinNames;
           // }
            const params = $(this).serialize();
            const address = $(this).attr("action");
            const user_id =  /\d+/.exec(address)[0]
            const posting = $.ajax({
                type: "POST",
                url: address,
                data: params,
                dataType: "json"
            });
            posting.done(function(APIresponse) {
                console.log("called back")

                // TODO: handle response

                //If APIresponse gives back HTML (meaning error-filled form):
                //.failure
 // if (APIresponse.includes("error")) {
    // console.log("APIresponse has error")
    // debugger;
                     //for error(s) in a form, clear

 and fill in with APIresponse:

    // $('#new_transaction_form').empty();
    //$('#new_transaction_form').html(APIresponse);
                     //potential fix for bug listed below:
                         //take API response and eliminate HTML starting right below div id="new_transaction_form"
                         // until div class="tl_measure_center"?
                                 //the issue is that a class=mesaure_center already exists prior to injection
                     //BUG alert: multiple error form submissions eliminates transaction history at bottom
                     // of the page.  Fix!
                //.failure
              //  } else {
                    console.log("APIresponse is good!")
                    const transaction = new Transaction(APIresponse);
                    transaction.renderTxItem();
                    restoreCreateATransaction(user_id);

                //end

             //   }
                    //delete from index. can you remove completely in js??
            });
        });
    });
});

function checkIfListed(selection) {
    if (selection.value == "-1"){
        $("#another-coin").show();
    } else{
        $("#another-coin").hide();
    }
}
function restoreCreateATransaction(user_id) {
    $("#new_transaction_form").empty();
    $("#create-a-transaction-form").replaceWith(`Create a transaction`)
    $("#link_field_set").addClass("pb4");
}

function showNewTransactionForm(userIdObj) {
    const src = $("#new-transaction-form-template").html();
    const template = Handlebars.compile(src);
    const newForm = template(userIdObj);
    $("#new_transaction_form").html(newForm);
    $("#create-a-transaction-form").replaceWith('
Create a transaction:
')
    $("#link_field_set").removeClass("pb4");
}

function showTxsWithTemplate(txs) {
    const src = $("#transaction-template").html();
    const template = Handlebars.compile(src);
    const txList = template(txs.reverse());
    $("#transactions_in_wallet").html(txList);
}
function showExtraTxDataTemplate(txAdditionalInfo, tx_id, user_id) {
    const src = $("#transaction-show-template").html();

    const template = Handlebars.compile(src);
    const txExtra = template(txAdditionalInfo);
    $(`#insert-show-more-${tx_id}`).html(txExtra);

    const srcForMod = $("#edit-delete-icons-template").html();
    const templateForMod = Handlebars.compile(srcForMod);
    const deleteURL = `/users/${user_id}/transactions/${tx_id}`
    const editURL = `/users/${user_id}/transactions/${tx_id}/edit`
    const modFeature = templateForMod({deleteURL: deleteURL, editURL: editURL });

    $(`#insert-show-more-${tx_id}`).append(modFeature);
    $(`#insert-show-more-${tx_id}`).addClass("pb4");
}
function changeListLink(coinName){
    $("#list_txs_in_wallet").replaceWith(`<p id="list_txs_in_wallet" class="mt0 black-80 b ph3 pv2 f6 dib">${coinName} Transaction History:</p>`)
}

function changeView(displayedImage, $icon, APIresponse, tx_id, user_id) {
    if (displayedImage === "ellipses"){
        showExtraTxDataTemplate(APIresponse, tx_id, user_id);
        changeToDownArrowIcon($icon);

    } else if (displayedImage === "downArrow") {
        $(`#insert-show-more-${tx_id}`).empty();
        $(`#insert-show-more-${tx_id}`).removeClass("pb4");
        changetoEllipsesIcon($icon);
    }
}

function changeToDownArrowIcon($icon) {
    $icon.data('image', "downArrow");
    $icon.removeClass("fa-ellipsis-h")
    $icon.addClass("fa-caret-down")
}
function changetoEllipsesIcon($icon) {
     $icon.data('image', "ellipses");
     $icon.removeClass("fa-caret-down");
     $icon.addClass("fa-ellipsis-h");
}
<|endoftext|>


<commit_before>/**
 * @file DatePicker component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import _ from 'lodash';

import MaterialDatePickerTextField from '../MaterialDatePickerTextField';
import DayPicker from '../_DayPicker';
import MonthPicker from '../_MonthPicker';
import YearPicker from '../_YearPicker';
import Popup from '../Popup';

import Theme from '../Theme';

export default class MaterialDatePicker extends Component {
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.validValue = true;
        this.state = {
            value: props.value,
            popupVisible: false,
            year: moment(props.value).format('YYYY'),
            month: moment(props.value).format('MM'),
            day: moment(props.value).format('DD'),
            datePickerLevel: 'day',
            marginLeft: 0
        };

        this.textFieldChangeHandle = ::this.textFieldChangeHandle;
        this.togglePopup = ::this.togglePopup;
        this.closePopup = ::this.closePopup;
        this.datePickerChangeHandle = ::this.datePickerChangeHandle;
        this.yearPickerChangeHandle = ::this.yearPickerChangeHandle;
        this.monthPickerChangeHandle = ::this.monthPickerChangeHandle;
        this.dayPickerChangeHandle = ::this.dayPickerChangeHandle;
        this.todayHandle = ::this.todayHandle;

    }

    datePickerChangeHandle(selectLevel) {
        this.setState({
            datePickerLevel: selectLevel
        });
    }

    textFieldChangeHandle(text) {
        const {minValue, maxValue, dateFormat} = this.props;
        if (text && text.length) {
            const flag = moment(text, this.props.dateFormat, true).isValid();
            if (flag) {
                if (minValue && moment(text).isBefore(minValue) || maxValue && moment(text).isAfter(maxValue)) {

                } else {
                    const year = moment(text).format('YYYY'),
                        month = moment(text).format('MM'),
                        day = moment(text).format('DD');
                    this.setState({
                        value: moment(text, dateFormat),
                        year: year,
                        month: month,
                        day: day
                    });
                }
            } else {

            }
        } else {
            this.setState({
                value: ''
            });
        }
    }

    dayPickerChangeHandle(date) {
        const {autoClose, dateFormat} = this.props;
        let state = _.cloneDeep(this.state);
        state.value = moment(date.time, dateFormat);
        state.year = date.year;
        state.month = date.month;
        state.day = date.day;
        state.popupVisible = !autoClose;
        if (state.popupVisible) {
            this.setState(state);
        } else {
            !this.props.disabled && this.setState(state, () => {
                this.props.onChange && this.props.onChange(moment(date.time).format(dateFormat));
            });
        }
    }

    monthPickerChangeHandle(date) {
        this.setState({
            datePickerLevel: 'day',
            year: date.year,
            month: date.month
        });
    }

    yearPickerChangeHandle(year) {
        this.setState({
            datePickerLevel: 'month',
            year: year
        });
    }

    todayHandle() {
        const {dateFormat} = this.props;
        const year = moment().format('YYYY'),
            month = moment().format('MM'),
            day = moment().format('DD');
        let timer = moment(moment(), dateFormat);
        this.setState({
            value: timer,
            year: year,
            month: month,
            day: day
        });
    }

    togglePopup(e) {
        if (this.validValue) {
            this.setState({
                popupVisible: !this.state.popupVisible,
                triggerEl: e.target
            });
        }
    }

    closePopup() {
        const {value} = this.state;
        !this.props.disabled && this.setState({
            popupVisible: false
        }, () => {
            this.props.onChange && this.props.onChange(value && moment(value).format(this.props.dateFormat));
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value || nextProps.dateFormat !== this.props.dateFormat) {
            this.setState({
                value: moment(nextProps.value, nextProps.dateFormat),
                dateFormat: nextProps.dateFormat,
                year: moment(nextProps.value).format('YYYY'),
                month: moment(nextProps.value).format('MM'),
                day: moment(nextProps.value).format('DD')
            });
        }
    }

    componentDidMount() {
        // debugger
        const {value, dateFormat} = this.props;
        let state = _.cloneDeep(this.state);
        if (value) {
            if (moment(value, dateFormat).isValid()) {
                const year = moment(value).format('YYYY'),
                    month = moment(value).format('MM'),
                    day = moment(value).format('DD');
                state.value = moment(value, dateFormat);
                state.year = year;
                state.month = month;
                state.day = day;
                this.setState(state);
            } else {
                this.validValue = false;
                console.error('Invalid date');
            }
        }
    }

    render() {

        const {className, name, placeholder, dateFormat, maxValue, minValue, label, isLabelAnimate, isFooter} = this.props,
            {value, popupVisible, datePickerLevel, year, month, day, triggerEl, isHover, isFocus} = this.state;
        let textValue = value && moment(value).format(dateFormat);

        return (
            <div className={`date-picker ${className}`}
                 ref="datePicker">
                <MaterialDatePickerTextField
                    ref="datePickerInput"
                    name={name}
                    placeholder={placeholder}
                    value={textValue}
                    readOnly={!popupVisible}
                    clearButtonVisible={false}
                    isFocusedSelectAll={false}
                    popupVisible={popupVisible}
                    label={label}
                    isLabelAnimate={isLabelAnimate}
                    onChange={this.textFieldChangeHandle}
                    onTouchTap={e => {
                        this.togglePopup(e);
                    }}/>
                <Popup className={`material-date-picker-popup`}
                       visible={popupVisible}
                       triggerEl={triggerEl}
                       hasTriangle={false}
                       onRequestClose={() => {
                           this.closePopup(3);
                       }}>
                    {
                        datePickerLevel == 'day' ?
                            <DayPicker
                                value={value}
                                dateFormat={dateFormat}
                                year={year}
                                month={month}
                                day={day}
                                maxValue={maxValue}
                                minValue={minValue}
                                isFooter={isFooter}
                                onChange={this.dayPickerChangeHandle}
                                previousClick={this.datePickerChangeHandle}/>
                            : (
                            datePickerLevel == 'month' ?
                                <MonthPicker
                                    value={value}
                                    year={year}
                                    month={month}
                                    day={day}
                                    maxValue={maxValue}
                                    minValue={minValue}
                                    onChange={this.monthPickerChangeHandle}
                                    previousClick={this.datePickerChangeHandle}/>
                                :
                                <YearPicker
                                    value={value}
                                    year={year}
                                    month={month}
                                    day={day}
                                    maxValue={maxValue}
                                    minValue={minValue}
                                    onChange={this.yearPickerChangeHandle}/>
                        )
                    }
                    {
                        isFooter && datePickerLevel == 'day' ?

                                {
                                    (minValue && moment(this.props.value).isBefore(minValue)) || (maxValue && moment(maxValue).isBefore(this.props.value)) ?

                                            Today

                                        :

                                            Today

                                }

                            :
                            null
                    }
                </Popup>

        );
    }
};

MaterialDatePicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Date picker input name.
     */
    name: PropTypes.string,

    /**
     * This is the initial date value of the component.
     */
    value: PropTypes.any,

    /**
     * The label of the date picker.
     */
    label: PropTypes.any,

    /**
     * The animate of the date picker.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The ending of a range of valid dates. The range includes the endDate.
     */
    maxValue: PropTypes.any,

    /**
     * The beginning of a range of valid dates. The range includes the startDate.
     */
    minValue: PropTypes.any,

    /**
     * DatePicker textField element placeholder.
     */
    placeholder: PropTypes.string,

    /**
     * Date format.
     */
    dateFormat: PropTypes.string,

    /**
     * If true,hide date display.
     */
    autoClose: PropTypes.bool,

    /**
     * Callback function that is fired when the date value changes.
     */
    onChange: PropTypes.func

};

MaterialDatePicker.defaultProps = {
    className: '',
    style: null,
    name: '',
    maxValue: '',
    minValue: '',
    placeholder: 'Date',
    dateFormat: 'YYYY-MM-DD',
    isLabelAnimate:true,
    autoClose: true,
    isFooter: true
};<commit_msg>update MaterialDatePicker<commit_after>/**
 * @file DatePicker component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import _ from 'lodash';

import MaterialDatePickerTextField from '../MaterialDatePickerTextField';
import DayPicker from '../_DayPicker';
import MonthPicker from '../_MonthPicker';
import YearPicker from '../_YearPicker';
import Popup from '../Popup';

import Theme from '../Theme';

class MaterialDatePicker extends Component {

    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.validValue = true;
        this.state = {
            value: props.value,
            popupVisible: false,
            year: moment(props.value).format('YYYY'),
            month: moment(props.value).format('MM'),
            day: moment(props.value).format('DD'),
            datePickerLevel: 'day',
            marginLeft: 0
        };

        this.textFieldChangeHandle = ::this.textFieldChangeHandle;
        this.togglePopup = ::this.togglePopup;
        this.closePopup = ::this.closePopup;
        this.datePickerChangeHandle = ::this.datePickerChangeHandle;
        this.yearPickerChangeHandle = ::this.yearPickerChangeHandle;
        this.monthPickerChangeHandle = ::this.monthPickerChangeHandle;
        this.dayPickerChangeHandle = ::this.dayPickerChangeHandle;
        this.todayHandle = ::this.todayHandle;

    }

    datePickerChangeHandle(selectLevel) {
        this.setState({
            datePickerLevel: selectLevel
        });
    }

    textFieldChangeHandle(text) {
        const {minValue, maxValue, dateFormat} = this.props;
        if (text && text.length) {
            const flag = moment(text, this.props.dateFormat, true).isValid();
            if (flag) {
                if (minValue && moment(text).isBefore(minValue) || maxValue && moment(text).isAfter(maxValue)) {

                } else {
                    const year = moment(text).format('YYYY'),
                        month = moment(text).format('MM'),
                        day = moment(text).format('DD');
                    this.setState({
                        value: moment(text, dateFormat),
                        year: year,
                        month: month,
                        day: day
                    });
                }
            } else {

            }
        } else {
            this.setState({
                value: ''
            });
        }
    }

    dayPickerChangeHandle(date) {
        const {autoClose, dateFormat} = this.props;
        let state = _.cloneDeep(this.state);
        state.value = moment(date.time, dateFormat);
        state.year = date.year;
        state.month = date.month;
        state.day = date.day;
        state.popupVisible = !autoClose;
        if (state.popupVisible) {
            this.setState(state);
        } else {
            !this.props.disabled && this.setState(state, () => {
                this.props.onChange && this.props.onChange(moment(date.time).format(dateFormat));
            });
        }
    }

    monthPickerChangeHandle(date) {
        this.setState({
            datePickerLevel: 'day',
            year: date.year,
            month: date.month
        });
    }

    yearPickerChangeHandle(year) {
        this.setState({
            datePickerLevel: 'month',
            year: year
        });
    }

    todayHandle() {
        const {dateFormat} = this.props;
        const year = moment().format('YYYY'),
            month = moment().format('MM'),
            day = moment().format('DD');
        let timer = moment(moment(), dateFormat);
        this.setState({
            value: timer,
            year: year,
            month: month,
            day: day
        });
    }

    togglePopup(e) {
        if (this.validValue) {
            this.setState({
                popupVisible: !this.state.popupVisible,
                triggerEl: e.target
            });
        }
    }

    closePopup() {
        const {value} = this.state;
        !this.props.disabled && this.setState({
            popupVisible: false
        }, () => {
            this.props.onChange && this.props.onChange(value && moment(value).format(this.props.dateFormat));
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value || nextProps.dateFormat !== this.props.dateFormat) {
            this.setState({
                value: moment(nextProps.value, nextProps.dateFormat),
                dateFormat: nextProps.dateFormat,
                year: moment(nextProps.value).format('YYYY'),
                month: moment(nextProps.value).format('MM'),
                day: moment(nextProps.value).format('DD')
            });
        }
    }

    componentDidMount() {
        // debugger
        const {value, dateFormat} = this.props;
        let state = _.cloneDeep(this.state);
        if (value) {
            if (moment(value, dateFormat).isValid()) {
                const year = moment(value).format('YYYY'),
                    month = moment(value).format('MM'),
                    day = moment(value).format('DD');
                state.value = moment(value, dateFormat);
                state.year = year;
                state.month = month;
                state.day = day;
                this.setState(state);
            } else {
                this.validValue = false;
                console.error('Invalid date');
            }
        }
    }

    render() {

        const {className, name, placeholder, dateFormat, maxValue, minValue, label, isLabelAnimate, isFooter} = this.props,
            {value, popupVisible, datePickerLevel, year, month, day, triggerEl, isHover, isFocus} = this.state;
        let textValue = value && moment(value).format(dateFormat);

        return (
            <div className={`date-picker ${className}`}
                 ref="datePicker">
                <MaterialDatePickerTextField
                    ref="datePickerInput"
                    name={name}
                    placeholder={placeholder}
                    value={textValue}
                    readOnly={!popupVisible}
                    clearButtonVisible={false}
                    isFocusedSelectAll={false}
                    popupVisible={popupVisible}
                    label={label}
                    isLabelAnimate={isLabelAnimate}
                    onChange={this.textFieldChangeHandle}
                    onTouchTap={e => {
                        this.togglePopup(e);
                    }}/>
                <Popup className={`material-date-picker-popup`}
                       visible={popupVisible}
                       triggerEl={triggerEl}
                       hasTriangle={false}
                       onRequestClose={() => {
                           this.closePopup(3);
                       }}>
                    {
                        datePickerLevel == 'day' ?
                            <DayPicker
                                value={value}
                                dateFormat={dateFormat}
                                year={year}
                                month={month}
                                day={day}
                                maxValue={maxValue}
                                minValue={minValue}
                                isFooter={isFooter}
                                onChange={this.dayPickerChangeHandle}
                                previousClick={this.datePickerChangeHandle}/>
                            : (
                                datePickerLevel == 'month' ?
                                    <MonthPicker
                                        value={value}
                                        year={year}
                                        month={month}
                                        day={day}
                                        maxValue={maxValue}
                                        minValue={minValue}
                                        onChange={this.monthPickerChangeHandle}
                                        previousClick={this.datePickerChangeHandle}/>
                                    :
                                    <YearPicker
                                        value={value}
                                        year={year}
                                        month={month}
                                        day={day}
                                        maxValue={maxValue}
                                        minValue={minValue}
                                        onChange={this.yearPickerChangeHandle}/>
                            )
                    }
                    {
                        isFooter && datePickerLevel == 'day' ?

                                {
                                    (minValue && moment(this.props.value).isBefore(minValue)) || (maxValue && moment(maxValue).isBefore(this.props.value)) ?

                                            Today

                                        :

                                            Today

                                }

                            :
                            null
                    }
                </Popup>

        );
    }
};

MaterialDatePicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Date picker input name.
     */
    name: PropTypes.string,

    /**
     * This is the initial date value of the component.
     */
    value: PropTypes.any,

    /**
     * The label of the date picker.
     */
    label: PropTypes.any,

    /**
     * The animate of the date picker.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The ending of a range of valid dates. The range includes the endDate.
     */
    maxValue: PropTypes.any,

    /**
     * The beginning of a range of valid dates. The range includes the startDate.
     */
    minValue: PropTypes.any,

    /**
     * DatePicker textField element placeholder.
     */
    placeholder: PropTypes.string,

    /**
     * Date format.
     */
    dateFormat: PropTypes.string,

    /**
     * If true,hide date display.
     */
    autoClose: PropTypes.bool,

    /**
     * Callback function that is fired when the date value changes.
     */
    onChange: PropTypes.func

};

MaterialDatePicker.defaultProps = {
    className: '',
    style: null,
    name: '',
    maxValue: '',
    minValue: '',
    placeholder: 'Date',
    dateFormat: 'YYYY-MM-DD',
    isLabelAnimate: true,
    autoClose: true,
    isFooter: true
};

export default MaterialDatePicker;<|endoftext|>


<commit_before>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var map = BateeqModels.map;

var Storage = BateeqModels.inventory.Storage;

module.exports = class StorageManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.storageCollection = this.db.use(map.inventory.Storage);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var filterName = {
                    'name': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode, filterName]
                };

                query['$and'].push($or);
            }

            this.storageCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(storages => {
                    resolve(storages);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            if (id === '')
                resolve(null);
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getByIdOrDefault(id) {
        return new Promise((resolve, reject) => {
            if (id === '')
                resolve(null);
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleOrDefaultByQuery(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.storageCollection
                .single(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    getSingleOrDefaultByQuery(query) {
        return new Promise((resolve, reject) => {
            this.storageCollection
                .singleOrDefault(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {

                    this.storageCollection.insert(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    update(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {
                    this.storageCollection.update(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {
                    validStorage._deleted = true;
                    this.storageCollection.update(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(storage) {
        var errors = {};
        return new Promise((resolve, reject) => {
            var valid = new Storage(storage);
            // 1. begin: Declare promises.
            var getStorage = this.storageCollection.singleOrDefault({
                "$and": [{
                    _id: {
                        '$ne': new ObjectId(valid._id)
                    }
                }, {
                        code: valid.code
                    }]
            });
            // 1. end: Declare promises.

            // 2. begin: Validation.
            Promise.all([getStorage])
                .then(results => {
                    var _storage = results[0];

                    if (!valid.code || valid.code == '')
                        errors["code"] = "code is required";
                    else if (_storage) {
                        errors["code"] = "code already exists";
                    }

                    if (!valid.name || valid.name == '')
                        errors["name"] = "name is required";

                    // 2c. begin: check if data has any error, reject if it has.
                    for (var prop in errors) {
                        var ValidationError = require('../../validation-error');
                        reject(new ValidationError('data does not pass validation', errors));
                    }

                    valid.stamp(this.user.username, 'manager');
                    resolve(valid);
                })
                .catch(e => {
                    reject(e);
                })
        });
    }
};<commit_msg>add method getByCode in storage-manager<commit_after>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var map = BateeqModels.map;

var Storage = BateeqModels.inventory.Storage;

module.exports = class StorageManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.storageCollection = this.db.use(map.inventory.Storage);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var filterName = {
                    'name': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode, filterName]
                };

                query['$and'].push($or);
            }

            this.storageCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(storages => {
                    resolve(storages);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            if (id === '')
                resolve(null);
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getByIdOrDefault(id) {
        return new Promise((resolve, reject) => {
            if (id === '')
                resolve(null);
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleOrDefaultByQuery(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

     getByCode(code) {
        return new Promise((resolve, reject) => {
            var query = {
                code: code,
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.storageCollection
                .single(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    getSingleOrDefaultByQuery(query) {
        return new Promise((resolve, reject) => {
            this.storageCollection
                .singleOrDefault(query)
                .then(storage => {
                    resolve(storage);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {

                    this.storageCollection.insert(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    update(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {
                    this.storageCollection.update(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(storage) {
        return new Promise((resolve, reject) => {
            this._validate(storage)
                .then(validStorage => {
                    validStorage._deleted = true;
                    this.storageCollection.update(validStorage)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(storage) {
        var errors = {};
        return new Promise((resolve, reject) => {
            var valid = new Storage(storage);
            // 1. begin: Declare promises.
            var getStorage = this.storageCollection.singleOrDefault({
                "$and": [{
                    _id: {
                        '$ne': new ObjectId(valid._id)
                    }
                }, {
                        code: valid.code
                    }]
            });
            // 1. end: Declare promises.

            // 2. begin: Validation.
            Promise.all([getStorage])
                .then(results => {
                    var _storage = results[0];

                    if (!valid.code || valid.code == '')
                        errors["code"] = "code is required";
                    else if (_storage) {
                        errors["code"] = "code already exists";
                    }

                    if (!valid.name || valid.name == '')
                        errors["name"] = "name is required";

                    // 2c. begin: check if data has any error, reject if it has.
                    for (var prop in errors) {
                        var ValidationError = require('../../validation-error');
                        reject(new ValidationError('data does not pass validation', errors));
                    }

                    valid.stamp(this.user.username, 'manager');
                    resolve(valid);
                })
                .catch(e => {
                    reject(e);
                })
        });
    }
};<|endoftext|>