<commit_before>var _ = require("underscore"),
  joi = require("joi"),
  jsunity = require("jsunity");

function ModelSpec () {
  var FoxxModel, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
    },

    testWithInitialData: function () {
      instance = new FoxxModel({
        a: 1
      });

      assertEqual(instance.get("a"), 1);
      instance.set("a", 2);
      assertEqual(instance.get("a"), 2);
    },

    testWithoutInitialData: function () {
      instance = new FoxxModel();

      assertEqual(instance.get("a"), undefined);
      instance.set("a", 1);
      assertEqual(instance.get("a"), 1);
    },

    testSettingMultipleAttributes: function () {
      instance = new FoxxModel({
        a: 1,
        b: 9
      });

      instance.set({
        b: 2,
        c: 3
      });

      assertEqual(instance.get("a"), 1);
      assertEqual(instance.get("b"), 2);
      assertEqual(instance.get("c"), 3);
    },

    testAddingAMethodWithExtend: function () {
      var Model = FoxxModel.extend({
        getA: function() {
          return this.get("a");
        }
      });

      instance = new Model({
        a: 5
      });

      assertEqual(instance.getA(), 5);
    },

    testOverwritingAMethodWithExtend: function () {
      var Model = FoxxModel.extend({
        get: function() {
          return 1;
        }
      });

      instance = new Model({
        a: 5
      });

      assertEqual(instance.get(), 1);
    },

    testHas: function () {
      instance = new FoxxModel({
        a: 1,
        b: null
      });

      assertTrue(instance.has("a"));
      assertFalse(instance.has("b"));
      assertFalse(instance.has("c"));
    },

    testSerialization: function () {
      var raw = {
        a: 1,
        b: 2
      };

      instance = new FoxxModel(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      _.each(raw, function(value, key) {
        assertEqual(value, dbData[key]);
        assertEqual(value, clientData[key]);
      });
    },

    testAlwaysAllowMetadataAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer()
        }
      });

      instance = new Model({ anInteger: 1 });
      assertEqual(_.keys(instance.attributes).length, 1);
      instance.set("_key", "arango");
      assertEqual(_.keys(instance.attributes).length, 2);
      assertEqual(instance.get("_key"), "arango");
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testCoerceAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer()
        }
      });

      instance = new Model({anInteger: 1});
      instance.set("anInteger", "2");
      assertEqual(instance.get("anInteger"), 2);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testDisallowUnknownAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer().strict()
        }
      });

      instance = new Model({ anInteger: 1, nonExistant: 2 });
      assertEqual(instance.attributes.anInteger, 1);
      assertEqual(instance.attributes.nonExistant, 2);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.nonExistant._object, 2);
      assertTrue(instance.errors.nonExistant.message.indexOf("not allowed") !== -1);

      instance.set('nonExistant', undefined);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testDisallowInvalidAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer().strict()
        }
      });

      instance = new Model({ anInteger: 1 });
      instance.set("anInteger", undefined);
      assertEqual(instance.attributes.anInteger, undefined);
      instance.set("anInteger", 2);
      assertEqual(instance.attributes.anInteger, 2);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);

      instance.set("anInteger", 5.1);
      assertEqual(instance.attributes.anInteger, 5.1);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, 5.1);
      assertTrue(instance.errors.anInteger.message.indexOf("integer") !== -1);

      instance.set("anInteger", false);
      assertEqual(instance.attributes.anInteger, false);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, false);
      assertTrue(instance.errors.anInteger.message.indexOf("number") !== -1);

      instance.set("anInteger", "");
      assertEqual(instance.attributes.anInteger, "");
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, "");
      assertTrue(instance.errors.anInteger.message.indexOf("number") !== -1);

      instance.set("anInteger", 4);
      assertEqual(instance.attributes.anInteger, 4);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testFilterMetadata: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.number().integer()
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = new Model(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      assertEqual(_.keys(dbData).length, 2);
      assertEqual(_.keys(clientData).length, 1);
      assertEqual(dbData.a, raw.a);
      assertEqual(dbData._key, raw._key);
      assertEqual(clientData.a, raw.a);
    },

    testFromClient: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.number().integer()
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = Model.fromClient(raw);
      assertEqual(_.keys(instance.attributes).length, 1);
      assertEqual(instance.attributes.a, raw.a);
      assertEqual(instance.attributes._key, undefined);
    },

    testSetDefaultAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.string().default('test'),
          b: joi.string().default('test'),
          c: joi.string()
        }
      });

      instance = new Model({ a: "a", c: "c" });
      assertEqual(instance.attributes, { a: "a", c: "c", b: "test" });
    }
  };
}

function ModelAnnotationSpec () {
  var FoxxModel, toJSONSchema, jsonSchema, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
      toJSONSchema = require('org/arangodb/foxx/schema').toJSONSchema;
    },

    testGetEmptyJSONSchema: function () {
      var Model = FoxxModel.extend({});
      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties, {});
    },

    testAttributesOfAPlainModel: function () {
      var attributes = {a: 1, b: 2};
      var Model = FoxxModel.extend({});
      instance = new Model(attributes);
      assertEqual(instance.attributes, attributes);
    },

    testAddOptionalAttributeToJSONSchema: function () {
      var Model = FoxxModel.extend({
        schema: {
          x: joi.string()
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties.x.type, "string");
    },

    testAddRequiredAttributeToJSONSchema: function () {
      var Model = FoxxModel.extend({
        schema: {
          x: joi.string().required()
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.properties.x.type, "string");
      assertEqual(jsonSchema.required, ["x"]);
    }
  };
}

function ModelLegacySpec () {
  var FoxxModel, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
    },

    testFilterUnknownProperties: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          a: {type: 'integer'}
        }
      });

      var raw = {
        a: 1,
        b: 2
      };

      instance = new Model(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      assertEqual(_.keys(dbData).length, 1);
      assertEqual(_.keys(clientData).length, 1);
      assertEqual(dbData.a, raw.a);
      assertEqual(clientData.a, raw.a);
    },

    testAlwaysAllowMetdataAttributes: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          anInteger: {type: 'integer'}
        }
      });

      instance = new Model({ anInteger: 1, nonExistant: 2 });
      assertEqual(_.keys(instance.attributes).length, 1);
      instance.set("_key", "arango");
      assertEqual(_.keys(instance.attributes).length, 2);
      assertEqual(instance.get("_key"), "arango");
    },

    testFilterMetadata: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          a: {type: 'integer'}
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = new Model(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      assertEqual(_.keys(dbData).length, 2);
      assertEqual(_.keys(clientData).length, 1);
      assertEqual(dbData.a, raw.a);
      assertEqual(dbData._key, raw._key);
      assertEqual(clientData.a, raw.a);
    },

    testFromClient: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          a: {type: 'integer'}
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = Model.fromClient(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      assertEqual(_.keys(dbData).length, 1);
      assertEqual(_.keys(clientData).length, 1);
      assertEqual(dbData.a, raw.a);
      assertEqual(clientData.a, raw.a);
    },

    testWhitelistConstructorAttributes: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          a: { type: "string", required: true },
          b: { type: "string" }
        }
      });

      instance = new Model({ a: "a", b: "b", c: "c" });
      assertEqual(instance.attributes, { a: "a", b: "b" });
    },

    testSetDefaultAttributes: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          a: { type: "string", defaultValue: "test" },
          b: { type: "string", defaultValue: "test" },
          c: { type: "string" }
        }
      });

      instance = new Model({ a: "a", c: "c" });
      assertEqual(instance.attributes, { a: "a", c: "c", b: "test" });
    }
  };
}

function ModelLegacyAnnotationSpec () {
  var FoxxModel, toJSONSchema, jsonSchema, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
      toJSONSchema = require('org/arangodb/foxx/schema').toJSONSchema;
    },

    testAddOptionalAttributeToJSONSchemaInLongForm: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          x: { type: "string" }
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties.x.type, "string");
    },

    testAddOptionalAttributeToJSONSchemaInShortForm: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          x: "string"
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties.x.type, "string");
    },

    testAddRequiredAttributeToJSONSchema: function () {
      var Model = FoxxModel.extend({}, {
        attributes: {
          x: { type: "string", required: true }
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.properties.x.type, "string");
      assertEqual(jsonSchema.required, ["x"]);
    }
  };
}

jsunity.run(ModelSpec);
jsunity.run(ModelAnnotationSpec);
jsunity.run(ModelLegacySpec);
jsunity.run(ModelLegacyAnnotationSpec);

return jsunity.done();
<commit_msg>Removed Foxx Model legacy tests (non-joi schemas are no longer supported in 2.5+).<commit_after>var _ = require("underscore"),
  joi = require("joi"),
  jsunity = require("jsunity");

function ModelSpec () {
  var FoxxModel, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
    },

    testWithInitialData: function () {
      instance = new FoxxModel({
        a: 1
      });

      assertEqual(instance.get("a"), 1);
      instance.set("a", 2);
      assertEqual(instance.get("a"), 2);
    },

    testWithoutInitialData: function () {
      instance = new FoxxModel();

      assertEqual(instance.get("a"), undefined);
      instance.set("a", 1);
      assertEqual(instance.get("a"), 1);
    },

    testSettingMultipleAttributes: function () {
      instance = new FoxxModel({
        a: 1,
        b: 9
      });

      instance.set({
        b: 2,
        c: 3
      });

      assertEqual(instance.get("a"), 1);
      assertEqual(instance.get("b"), 2);
      assertEqual(instance.get("c"), 3);
    },

    testAddingAMethodWithExtend: function () {
      var Model = FoxxModel.extend({
        getA: function() {
          return this.get("a");
        }
      });

      instance = new Model({
        a: 5
      });

      assertEqual(instance.getA(), 5);
    },

    testOverwritingAMethodWithExtend: function () {
      var Model = FoxxModel.extend({
        get: function() {
          return 1;
        }
      });

      instance = new Model({
        a: 5
      });

      assertEqual(instance.get(), 1);
    },

    testHas: function () {
      instance = new FoxxModel({
        a: 1,
        b: null
      });

      assertTrue(instance.has("a"));
      assertFalse(instance.has("b"));
      assertFalse(instance.has("c"));
    },

    testSerialization: function () {
      var raw = {
        a: 1,
        b: 2
      };

      instance = new FoxxModel(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      _.each(raw, function(value, key) {
        assertEqual(value, dbData[key]);
        assertEqual(value, clientData[key]);
      });
    },

    testAlwaysAllowMetadataAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer()
        }
      });

      instance = new Model({ anInteger: 1 });
      assertEqual(_.keys(instance.attributes).length, 1);
      instance.set("_key", "arango");
      assertEqual(_.keys(instance.attributes).length, 2);
      assertEqual(instance.get("_key"), "arango");
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testCoerceAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer()
        }
      });

      instance = new Model({anInteger: 1});
      instance.set("anInteger", "2");
      assertEqual(instance.get("anInteger"), 2);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testDisallowUnknownAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer().strict()
        }
      });

      instance = new Model({ anInteger: 1, nonExistant: 2 });
      assertEqual(instance.attributes.anInteger, 1);
      assertEqual(instance.attributes.nonExistant, 2);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.nonExistant._object, 2);
      assertTrue(instance.errors.nonExistant.message.indexOf("not allowed") !== -1);

      instance.set('nonExistant', undefined);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testDisallowInvalidAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          anInteger: joi.number().integer().strict()
        }
      });

      instance = new Model({ anInteger: 1 });
      instance.set("anInteger", undefined);
      assertEqual(instance.attributes.anInteger, undefined);
      instance.set("anInteger", 2);
      assertEqual(instance.attributes.anInteger, 2);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);

      instance.set("anInteger", 5.1);
      assertEqual(instance.attributes.anInteger, 5.1);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, 5.1);
      assertTrue(instance.errors.anInteger.message.indexOf("integer") !== -1);

      instance.set("anInteger", false);
      assertEqual(instance.attributes.anInteger, false);
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, false);
      assertTrue(instance.errors.anInteger.message.indexOf("number") !== -1);

      instance.set("anInteger", "");
      assertEqual(instance.attributes.anInteger, "");
      assertEqual(_.keys(instance.errors).length, 1);
      assertFalse(instance.isValid);
      assertEqual(instance.errors.anInteger._object, "");
      assertTrue(instance.errors.anInteger.message.indexOf("number") !== -1);

      instance.set("anInteger", 4);
      assertEqual(instance.attributes.anInteger, 4);
      assertEqual(_.keys(instance.errors).length, 0);
      assertTrue(instance.isValid);
    },

    testFilterMetadata: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.number().integer()
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = new Model(raw);
      var dbData = instance.forDB();
      var clientData = instance.forClient();

      assertEqual(_.keys(dbData).length, 2);
      assertEqual(_.keys(clientData).length, 1);
      assertEqual(dbData.a, raw.a);
      assertEqual(dbData._key, raw._key);
      assertEqual(clientData.a, raw.a);
    },

    testFromClient: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.number().integer()
        }
      });

      var raw = {
        a: 1,
        _key: 2
      };

      instance = Model.fromClient(raw);
      assertEqual(_.keys(instance.attributes).length, 1);
      assertEqual(instance.attributes.a, raw.a);
      assertEqual(instance.attributes._key, undefined);
    },

    testSetDefaultAttributes: function () {
      var Model = FoxxModel.extend({
        schema: {
          a: joi.string().default('test'),
          b: joi.string().default('test'),
          c: joi.string()
        }
      });

      instance = new Model({ a: "a", c: "c" });
      assertEqual(instance.attributes, { a: "a", c: "c", b: "test" });
    }
  };
}

function ModelAnnotationSpec () {
  var FoxxModel, toJSONSchema, jsonSchema, instance;

  return {
    setUp: function () {
      FoxxModel = require('org/arangodb/foxx/model').Model;
      toJSONSchema = require('org/arangodb/foxx/schema').toJSONSchema;
    },

    testGetEmptyJSONSchema: function () {
      var Model = FoxxModel.extend({});
      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties, {});
    },

    testAttributesOfAPlainModel: function () {
      var attributes = {a: 1, b: 2};
      var Model = FoxxModel.extend({});
      instance = new Model(attributes);
      assertEqual(instance.attributes, attributes);
    },

    testAddOptionalAttributeToJSONSchema: function () {
      var Model = FoxxModel.extend({
        schema: {
          x: joi.string()
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.required, []);
      assertEqual(jsonSchema.properties.x.type, "string");
    },

    testAddRequiredAttributeToJSONSchema: function () {
      var Model = FoxxModel.extend({
        schema: {
          x: joi.string().required()
        }
      });

      jsonSchema = toJSONSchema("myname", Model);
      assertEqual(jsonSchema.id, "myname");
      assertEqual(jsonSchema.properties.x.type, "string");
      assertEqual(jsonSchema.required, ["x"]);
    }
  };
}

jsunity.run(ModelSpec);
jsunity.run(ModelAnnotationSpec);

return jsunity.done();
<|endoftext|>


<commit_before>var validate = require('./index.js');
var colors = require('colors/safe');
var fs = require('fs')

module.exports = function (dir, options) {
	if (options.gentleSummary) { options.gentleSummaryMax = 10; }
	if (fs.existsSync(dir)) {
	    validate.BIDS(dir, options, function (errors, warnings) {
	    	if (errors === 'Invalid') {
	    		console.log(colors.red("This does not appear to be a BIDS dataset. For more info go to http://bids.neuroimaging.io/"));
	    	} else if (errors.length >= 1 || warnings.length >= 1) {
		        logIssues(errors, 'red', options);
				logIssues(warnings, 'yellow', options);
			}
			else {
				console.log(colors.green("This dataset appears to be BIDS compatible."));
			}
	    });
	} else {
		console.log(colors.red(dir + " does not exits"))
	}
};

function logIssues (issues, color, options) {
	for (var i = 0; i < issues.length; i++) {
		var issue = issues[i];
    	console.log('\t' + colors[color]((i + 1) + ': ' + issue.reason + ' (code: ' + issue.code + ')'));
    	for (var j = 0; j < issue.files.length; j++) {
    		var file = issues[i].files[j];
    		if (!file) {continue;}
    		console.log('\t\t' + file.file.relativePath);
    		if (options.verbose) {console.log('\t\t\t' + file.reason);}
			if (file.line) {
				var msg = '\t\t\t@ line: ' + file.line
				if (file.character) {
					msg += ' character: ' + file.character
				}
				console.log(msg)
			}
			if (file.evidence) {
				console.log('\t\t\tEvidence: ' + file.evidence);
			}

			if (options.gentleSummary && (j+1) >= options.gentleSummaryMax) {
				var remaining = issue.files.length - (j+1);
				console.log('\t\t'+colors[color]('... and '+remaining+' more files having this issue (--GentleSummary in effect).'));
				break;
			}

    	}
		console.log();
    }
}
<commit_msg>Fixed typo in remaining files message.<commit_after>var validate = require('./index.js');
var colors = require('colors/safe');
var fs = require('fs')

module.exports = function (dir, options) {
	if (options.gentleSummary) { options.gentleSummaryMax = 10; }
	if (fs.existsSync(dir)) {
	    validate.BIDS(dir, options, function (errors, warnings) {
	    	if (errors === 'Invalid') {
	    		console.log(colors.red("This does not appear to be a BIDS dataset. For more info go to http://bids.neuroimaging.io/"));
	    	} else if (errors.length >= 1 || warnings.length >= 1) {
		        logIssues(errors, 'red', options);
				logIssues(warnings, 'yellow', options);
			}
			else {
				console.log(colors.green("This dataset appears to be BIDS compatible."));
			}
	    });
	} else {
		console.log(colors.red(dir + " does not exits"))
	}
};

function logIssues (issues, color, options) {
	for (var i = 0; i < issues.length; i++) {
		var issue = issues[i];
    	console.log('\t' + colors[color]((i + 1) + ': ' + issue.reason + ' (code: ' + issue.code + ')'));
    	for (var j = 0; j < issue.files.length; j++) {
    		var file = issues[i].files[j];
    		if (!file) {continue;}
    		console.log('\t\t' + file.file.relativePath);
    		if (options.verbose) {console.log('\t\t\t' + file.reason);}
			if (file.line) {
				var msg = '\t\t\t@ line: ' + file.line
				if (file.character) {
					msg += ' character: ' + file.character
				}
				console.log(msg)
			}
			if (file.evidence) {
				console.log('\t\t\tEvidence: ' + file.evidence);
			}

			if (options.gentleSummary && (j+1) >= options.gentleSummaryMax) {
				var remaining = issue.files.length - (j+1);
				console.log('\t\t'+colors[color]('... and '+remaining+' more files having this issue (--gentleSummary in effect).'));
				break;
			}

    	}
		console.log();
    }
}
<|endoftext|>


<commit_before>/*jslint browser: true, plusplus: true */
/*global angular */

(function () {
    'use strict';

    var CLS_VIEWKEEPER_FIXED = 'bb-viewkeeper-fixed',
        CLS_VIEWKEEPER_NO_OMNIBAR = 'bb-viewkeeper-no-omnibar',
        config = {
            viewportMarginTop: 0,
            hasOmnibar: true
        },
        ViewKeeper;

    function nextId() {
        nextId.index = nextId.index || 0;
        nextId.index++;
        return 'viewkeeper-' + nextId.index;
    }

    function getSpacerId(vk) {
        return vk.id + "-spacer";
    }

    function setElPosition(elQ, left, top, bottom, width) {
        elQ.css({
            "top": top,
            "bottom": bottom,
            "left": left
        });

        /*istanbul ignore else*/
        /* sanity check */
        if (width !== null) {
            elQ.css({ "width": width });
        }
    }

    function unfixEl(vk) {
        var elQ = angular.element(vk.el),
            width;

        angular.element("#" + getSpacerId(vk)).remove();

        elQ.removeClass(CLS_VIEWKEEPER_FIXED);

        vk.currentElFixedLeft = null;
        vk.currentElFixedTop = null;
        vk.currentElFixedBottom = null;
        vk.currentElFixedWidth = null;

        if (vk.setWidth) {
            width = "auto";
        }
        setElPosition(elQ, "", "", "", width);
    }

    function calculateVerticalOffset(vk) {
        var offset,
            verticalOffSetElTop;

        offset = vk.verticalOffSet;

        if (vk.verticalOffSetEl) {
            verticalOffSetElTop = vk.verticalOffSetEl.css('top');

            /*istanbul ignore else*/
            /* sanity check */
            if (verticalOffSetElTop) {
                verticalOffSetElTop = parseInt(verticalOffSetElTop, 10);
                if (isNaN(verticalOffSetElTop)) {
                    verticalOffSetElTop = 0;
                }
            }

            offset += (vk.verticalOffSetEl.outerHeight() + verticalOffSetElTop);
        }

        return offset;
    }

    ViewKeeper = function (options) {
        var id,
            vk = this;

        options = options || /* istanbul ignore next */ {};

        vk.fixToBottom = options.fixToBottom;
        vk.setWidth = options.setWidth;
        vk.id = id = nextId();
        vk.el = options.el;
        vk.boundaryEl = options.boundaryEl;
        vk.verticalOffSet = options.verticalOffSet || 0;
        vk.setPlaceholderHeight = (options.setPlaceholderHeight !== false);
        vk.onStateChanged = options.onStateChanged;
        vk.isFixed = false;

        if (options.verticalOffSetElId) {
            vk.verticalOffSetEl = angular.element('#' + options.verticalOffSetElId);

            vk.verticalOffSetEl.on('afterViewKeeperSync.' + id, function () {
                vk.syncElPosition();
            });
        }

        angular.element(window).on("scroll." + id + ", resize." + id + ", orientationchange." + id, function () {
            vk.syncElPosition();
        });
    };

    ViewKeeper.prototype = {

        syncElPosition: function () {
            var anchorTop,
                anchorHeight,
                isCurrentlyFixed,
                currentElFixedLeft,
                currentElFixedTop,
                currentElFixedBottom,
                currentElFixedWidth,
                documentQ,
                fixEl,
                boundaryBottom,
                boundaryOffset,
                boundaryQ,
                boundaryTop,
                elFixedLeft,
                elFixedTop,
                elFixedBottom,
                elFixedWidth,
                elHeight,
                elQ,
                needsUpdating,
                scrollLeft,
                scrollTop,
                spacerHeight,
                spacerId,
                spacerQ,
                verticalOffSet,
                vk = this,
                width;

            isCurrentlyFixed = vk.isFixed;

            verticalOffSet = calculateVerticalOffset(vk);

            elQ = angular.element(vk.el);

            // When the element isn't visible, its size can't be calculated, so don't attempt syncing position in this case.
            if (!elQ.is(':visible')) {
                return;
            }

            boundaryQ = angular.element(vk.boundaryEl);
            spacerId = getSpacerId(vk);

            spacerQ = angular.element("#" + spacerId);
            documentQ = angular.element(window.document);

            boundaryOffset = boundaryQ.offset();
            boundaryTop = boundaryOffset.top;
            boundaryBottom = boundaryTop + boundaryQ.height();

            scrollLeft = documentQ.scrollLeft();
            scrollTop = documentQ.scrollTop();

            elHeight = elQ.outerHeight(true);

            if (vk.fixToBottom) {
                elFixedBottom = 0;
            } else {
                // If the element needs to be fixed, this will calculate its position.  The position will be 0 (fully visible) unless
                // the user is scrolling the boundary out of view.  In that case, the element should begin to scroll out of view with the
                // rest of the boundary by setting its top position to a negative value.
                elFixedTop = Math.min((boundaryBottom - elHeight) - scrollTop, verticalOffSet);
            }

            elFixedWidth = boundaryQ.width();
            elFixedLeft = boundaryOffset.left - scrollLeft;

            currentElFixedLeft = vk.currentElFixedLeft;
            currentElFixedTop = vk.currentElFixedTop;
            currentElFixedBottom = vk.currentElFixedBottom;
            currentElFixedWidth = vk.currentElFixedWidth;

            if (spacerQ.length > 0) {
                anchorTop = spacerQ.offset().top;
                anchorHeight = spacerQ.outerHeight(true);
            } else {
                anchorTop = elQ.offset().top;
                anchorHeight = elHeight;
            }

            if (vk.fixToBottom) {
                //Fix el if the natural bottom of the element would not be on the screen
                fixEl = (anchorTop + anchorHeight > scrollTop + window.innerHeight);
            } else {
                fixEl = scrollTop + verticalOffSet + config.viewportMarginTop > anchorTop;
            }

            if ((fixEl && currentElFixedLeft === elFixedLeft && currentElFixedTop === elFixedTop && currentElFixedBottom === elFixedBottom && currentElFixedWidth === elFixedWidth) || (!fixEl && !(currentElFixedLeft !== undefined && currentElFixedLeft !== null))) {
                // The element is either currently fixed and its position and width do not need to change, or the element is not
                // currently fixed and does not need to be fixed.  No changes are needed.
                needsUpdating = false;
            } else {
                needsUpdating = true;
            }

            if (needsUpdating) {
                if (fixEl) {
                    vk.isFixed = true;
                    if (spacerQ.length === 0) {
                        if (vk.setPlaceholderHeight) {
                            spacerHeight = elHeight;
                        } else {
                            spacerHeight = 0;
                        }
                        elQ.after('

');
                    }

                    elQ.addClass(CLS_VIEWKEEPER_FIXED);

                    vk.currentElFixedTop = elFixedTop;
                    vk.currentElFixedBottom = elFixedBottom;
                    vk.currentElFixedLeft = elFixedLeft;
                    vk.currentElFixedWidth = elFixedWidth;

                    if (vk.setWidth) {
                        width = elFixedWidth;
                    }

                    setElPosition(elQ, elFixedLeft, elFixedTop, elFixedBottom, width);
                } else {
                    vk.isFixed = false;
                    unfixEl(vk);
                }

                //If we changed if the item is fixed, fire the callback
                if (vk.onStateChanged && isCurrentlyFixed !== vk.isFixed) {
                    vk.onStateChanged();
                }
            }
            elQ.trigger('afterViewKeeperSync');
        },

        scrollToTop: function () {
            var anchorTop,
                elQ,
                documentQ,
                spacerId,
                spacerQ,
                verticalOffset,
                vk = this;

            verticalOffset = calculateVerticalOffset(vk);

            documentQ = angular.element(window.document);
            spacerId = getSpacerId(vk);
            spacerQ = angular.element("#" + spacerId);
            elQ = angular.element(vk.el);

            if (spacerQ.length > 0) {
                anchorTop = spacerQ.offset().top;
            } else {
                anchorTop = elQ.offset().top;
            }

            documentQ.scrollTop(anchorTop - verticalOffset - config.viewportMarginTop);
        },

        destroy: function () {
            var id,
                vk = this;

            if (!vk.isDestroyed) {
                id = vk.id;

                angular.element(window).off("scroll." + id + ", resize." + id + ", orientationchange." + id);
                unfixEl(vk);

                if (vk.verticalOffSetEl) {
                    vk.verticalOffSetEl.off("afterViewKeeperSync." + vk.id);
                    vk.verticalOffSetEl = null;
                }

                vk.isDestroyed = true;
            }
        }

    };

    angular.module('sky.viewkeeper', ['sky.mediabreakpoints', 'sky.window'])
        .constant('bbViewKeeperConfig', config)
        .factory('bbViewKeeperBuilder', function () {
            return {
                create: function (options) {
                    return new ViewKeeper(options);
                }
            };
        })
        .run(['$document', '$window', 'bbMediaBreakpoints', 'bbViewKeeperConfig', function ($document, $window, bbMediaBreakpoints, bbViewKeeperConfig) {
            function mediaBreakpointHandler(breakpoints) {
                //For user agents in which the omnibar follows you down the page, the ViewKeeper needs
                //to adjust for the height of the omnibar.
                if (bbViewKeeperConfig.hasOmnibar) {
                    //Ideally these values should be driven from a more appropriate source (omnibar js?)
                    bbViewKeeperConfig.viewportMarginTop = breakpoints.xs ? 50 : 30;
                    angular.element('body').removeClass(CLS_VIEWKEEPER_NO_OMNIBAR);
                } else {
                    angular.element('body').addClass(CLS_VIEWKEEPER_NO_OMNIBAR);
                }
            }

            if (/iPad|iPod|iPhone/i.test($window.navigator.userAgent)) {
                //On iOS the omnibar doesn't scroll with you.  Need to account for this on the styling.
                angular.element('body').addClass('omnibar-not-fixed');

                //On iOS we need to have special handling when entering textboxes to correct an issue with fixed
                //elements used by view keeper when the keyboard flys out.
                angular.element(document).on('focus', 'input', function () {
                    angular.element('body').addClass('bb-viewkeeper-ignore-fixed');
                }).on('blur', 'input', function () {
                    angular.element('body').removeClass('bb-viewkeeper-ignore-fixed');
                });
            } else {
                bbMediaBreakpoints.register(mediaBreakpointHandler);
            }
        }])
        .directive('bbViewKeeper', ['bbViewKeeperBuilder', function (bbViewKeeperBuilder) {
            function link(scope, el) {
                var vk;

                function destroyVk() {
                    if (vk) {
                        vk.destroy();
                        vk = null;
                    }
                }

                el.on('$destroy', function () {
                    destroyVk();
                });

                scope.$watch('bbBoundaryElId', function () {
                    var boundaryEl,
                        bbBoundaryElId = scope.bbBoundaryElId;

                    /*istanbul ignore else */
                    if (bbBoundaryElId) {
                        boundaryEl = angular.element('#' + bbBoundaryElId);

                        /*istanbul ignore else */
                        if (boundaryEl.length === 1) {
                            destroyVk();

                            vk = bbViewKeeperBuilder.create({
                                el: el[0],
                                boundaryEl: boundaryEl[0],
                                setWidth: true
                            });
                        }
                    }
                });
            }

            return {
                link: link,
                restrict: 'A',
                scope: {
                    bbBoundaryElId: '='
                }
            };
        }])
        .directive('bbScrollingViewKeeper', ['$window', 'bbWindow', function ($window, bbWindow) {
            return {
                scope: {
                    bbScrollingViewKeeper: "="
                },
                link: function (scope, element) {
                    var elementStart,
                        scrollPos,
                        prevScroll,
                        scrollingDown = true,
                        tempTop,
                        verticalOffset,
                        id = scope.$id;

                    function scroll() {
                        if (!element.is(':visible')) {
                            return;
                        }

                        if (angular.element('.bb-omnibar>.desktop').is(':visible')) {
                            verticalOffset = angular.element('.bb-omnibar>.desktop>.bar').outerHeight();
                        } else {
                            verticalOffset = 0;
                        }

                        if (scope.bbScrollingViewKeeper && scope.bbScrollingViewKeeper.viewKeeperOffsetElId) {
                            verticalOffset += angular.element('#' + scope.bbScrollingViewKeeper.viewKeeperOffsetElId).outerHeight();
                        }

                        if (!elementStart) {
                            elementStart = element.offset().top;
                        }
                        scrollPos = $window.scrollY || $window.pageYOffset || $window.document.body.scrollTop || 0;
                        if (prevScroll > scrollPos) {
                            scrollingDown = false;
                        } else {
                            scrollingDown = true;
                        }
                        prevScroll = scrollPos;

                        if (scrollPos >= elementStart - verticalOffset && element.height() + verticalOffset <= $window.document.body.offsetHeight) {
                            if (element.height() + verticalOffset < $window.innerHeight) {
                                tempTop = 0;

                                element.removeClass('bb-grid-filters-fixed-bottom').addClass('bb-grid-filters-fixed-top');

                                element.css({
                                    top: verticalOffset + 'px'
                                });
                            } else if (scrollingDown) {
                                if (element.offset().top + element.height() >= scrollPos + $window.innerHeight) {
                                    /*istanbul ignore else*/
                                    /* sanity check */
                                    if (!tempTop) {
                                        tempTop = element.offset().top - elementStart;
                                    }

                                    element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom');

                                    element.css({
                                        top: tempTop
                                    });
                                } else {
                                    tempTop = 0;
                                    element.css({
                                        top: ''
                                    });
                                    element.removeClass('bb-grid-filters-fixed-top').addClass('bb-grid-filters-fixed-bottom');
                                }
                            } else {
                                if (element.offset().top < scrollPos + verticalOffset) {
                                    /*istanbul ignore else*/
                                    /* sanity check */
                                    if (!tempTop) {
                                        tempTop = element.offset().top - elementStart;
                                    }

                                    element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom ');

                                    element.css({
                                        top: tempTop
                                    });
                                } else {
                                    tempTop = 0;

                                    element.removeClass('bb-grid-filters-fixed-bottom').addClass('bb-grid-filters-fixed-top');

                                    element.css({
                                        top: verticalOffset + 'px'
                                    });
                                }
                            }
                        } else {
                            tempTop = 0;
                            element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom grid-filters-fixed-top grid-filters-fixed-bottom');
                            element.css({
                                top: 0
                            });
                        }
                    }

                    if (!bbWindow.isIosUserAgent()) {
                        angular.element($window).on('scroll.' + id + ', orientationchange.' + id, scroll);

                        element.on('$destroy', function () {
                            angular.element($window).off("scroll." + id + ", orientationchange." + id);
                        });
                    }
                },
                restrict: 'A'
            };
        }]);
}());
<commit_msg>white space bump<commit_after>/*jslint browser: true, plusplus: true */
/*global angular */

(function () {
    'use strict';

    var CLS_VIEWKEEPER_FIXED = 'bb-viewkeeper-fixed',
        CLS_VIEWKEEPER_NO_OMNIBAR = 'bb-viewkeeper-no-omnibar',
        config = {
            viewportMarginTop: 0,
            hasOmnibar: true
        },
        ViewKeeper;

    function nextId() {
        nextId.index = nextId.index || 0;
        nextId.index++;
        return 'viewkeeper-' + nextId.index;
    }

    function getSpacerId(vk) {
        return vk.id + "-spacer";
    }

    function setElPosition(elQ, left, top, bottom, width) {
        elQ.css({
            "top": top,
            "bottom": bottom,
            "left": left
        });

        /*istanbul ignore else*/
        /* sanity check */
        if (width !== null) {
            elQ.css({ "width": width });
        }
    }

    function unfixEl(vk) {
        var elQ = angular.element(vk.el),
            width;

        angular.element("#" + getSpacerId(vk)).remove();

        elQ.removeClass(CLS_VIEWKEEPER_FIXED);

        vk.currentElFixedLeft = null;
        vk.currentElFixedTop = null;
        vk.currentElFixedBottom = null;
        vk.currentElFixedWidth = null;

        if (vk.setWidth) {
            width = "auto";
        }
        setElPosition(elQ, "", "", "", width);
    }

    function calculateVerticalOffset(vk) {
        var offset,
            verticalOffSetElTop;

        offset = vk.verticalOffSet;

        if (vk.verticalOffSetEl) {
            verticalOffSetElTop = vk.verticalOffSetEl.css('top');

            /*istanbul ignore else*/
            /* sanity check */
            if (verticalOffSetElTop) {
                verticalOffSetElTop = parseInt(verticalOffSetElTop, 10);
                if (isNaN(verticalOffSetElTop)) {
                    verticalOffSetElTop = 0;
                }
            }

            offset += (vk.verticalOffSetEl.outerHeight() + verticalOffSetElTop);
        }

        return offset;
    }

    ViewKeeper = function (options) {
        var id,
            vk = this;

        options = options || /* istanbul ignore next */ {};

        vk.fixToBottom = options.fixToBottom;
        vk.setWidth = options.setWidth;
        vk.id = id = nextId();
        vk.el = options.el;
        vk.boundaryEl = options.boundaryEl;
        vk.verticalOffSet = options.verticalOffSet || 0;
        vk.setPlaceholderHeight = (options.setPlaceholderHeight !== false);
        vk.onStateChanged = options.onStateChanged;
        vk.isFixed = false;

        if (options.verticalOffSetElId) {
            vk.verticalOffSetEl = angular.element('#' + options.verticalOffSetElId);

            vk.verticalOffSetEl.on('afterViewKeeperSync.' + id, function () {
                vk.syncElPosition();
            });
        }

        angular.element(window).on("scroll." + id + ", resize." + id + ", orientationchange." + id, function () {
            vk.syncElPosition();
        });
    };

    ViewKeeper.prototype = {

        syncElPosition: function () {
            var anchorTop,
                anchorHeight,
                isCurrentlyFixed,
                currentElFixedLeft,
                currentElFixedTop,
                currentElFixedBottom,
                currentElFixedWidth,
                documentQ,
                fixEl,
                boundaryBottom,
                boundaryOffset,
                boundaryQ,
                boundaryTop,
                elFixedLeft,
                elFixedTop,
                elFixedBottom,
                elFixedWidth,
                elHeight,
                elQ,
                needsUpdating,
                scrollLeft,
                scrollTop,
                spacerHeight,
                spacerId,
                spacerQ,
                verticalOffSet,
                vk = this,
                width;

            isCurrentlyFixed = vk.isFixed;

            verticalOffSet = calculateVerticalOffset(vk);

            elQ = angular.element(vk.el);

            // When the element isn't visible, its size can't be calculated, so don't attempt syncing position in this case.
            if (!elQ.is(':visible')) {
                return;
            }

            boundaryQ = angular.element(vk.boundaryEl);
            spacerId = getSpacerId(vk);

            spacerQ = angular.element("#" + spacerId);
            documentQ = angular.element(window.document);

            boundaryOffset = boundaryQ.offset();
            boundaryTop = boundaryOffset.top;
            boundaryBottom = boundaryTop + boundaryQ.height();

            scrollLeft = documentQ.scrollLeft();
            scrollTop = documentQ.scrollTop();

            elHeight = elQ.outerHeight(true);

            if (vk.fixToBottom) {
                elFixedBottom = 0;
            } else {
                // If the element needs to be fixed, this will calculate its position.  The position will be 0 (fully visible) unless
                // the user is scrolling the boundary out of view.  In that case, the element should begin to scroll out of view with the
                // rest of the boundary by setting its top position to a negative value.
                elFixedTop = Math.min((boundaryBottom - elHeight) - scrollTop, verticalOffSet);
            }

            elFixedWidth = boundaryQ.width();
            elFixedLeft = boundaryOffset.left - scrollLeft;

            currentElFixedLeft = vk.currentElFixedLeft;
            currentElFixedTop = vk.currentElFixedTop;
            currentElFixedBottom = vk.currentElFixedBottom;
            currentElFixedWidth = vk.currentElFixedWidth;

            if (spacerQ.length > 0) {
                anchorTop = spacerQ.offset().top;
                anchorHeight = spacerQ.outerHeight(true);
            } else {
                anchorTop = elQ.offset().top;
                anchorHeight = elHeight;
            }

            if (vk.fixToBottom) {
                //Fix el if the natural bottom of the element would not be on the screen
                fixEl = (anchorTop + anchorHeight > scrollTop + window.innerHeight);
            } else {
                fixEl = scrollTop + verticalOffSet + config.viewportMarginTop > anchorTop;
            }

            if ((fixEl && currentElFixedLeft === elFixedLeft && currentElFixedTop === elFixedTop && currentElFixedBottom === elFixedBottom && currentElFixedWidth === elFixedWidth) || (!fixEl && !(currentElFixedLeft !== undefined && currentElFixedLeft !== null))) {
                // The element is either currently fixed and its position and width do not need to change, or the element is not
                // currently fixed and does not need to be fixed.  No changes are needed.
                needsUpdating = false;
            } else {
                needsUpdating = true;
            }

            if (needsUpdating) {
                if (fixEl) {
                    vk.isFixed = true;
                    if (spacerQ.length === 0) {
                        if (vk.setPlaceholderHeight) {
                            spacerHeight = elHeight;
                        } else {
                            spacerHeight = 0;
                        }
                        elQ.after('

');
                    }

                    elQ.addClass(CLS_VIEWKEEPER_FIXED);

                    vk.currentElFixedTop = elFixedTop;
                    vk.currentElFixedBottom = elFixedBottom;
                    vk.currentElFixedLeft = elFixedLeft;
                    vk.currentElFixedWidth = elFixedWidth;

                    if (vk.setWidth) {
                        width = elFixedWidth;
                    }

                    setElPosition(elQ, elFixedLeft, elFixedTop, elFixedBottom, width);
                } else {
                    vk.isFixed = false;
                    unfixEl(vk);
                }

                //If we changed if the item is fixed, fire the callback
                if (vk.onStateChanged && isCurrentlyFixed !== vk.isFixed) {
                    vk.onStateChanged();
                }
            }
            elQ.trigger('afterViewKeeperSync');
        },

        scrollToTop: function () {
            var anchorTop,
                elQ,
                documentQ,
                spacerId,
                spacerQ,
                verticalOffset,
                vk = this;

            verticalOffset = calculateVerticalOffset(vk);

            documentQ = angular.element(window.document);
            spacerId = getSpacerId(vk);
            spacerQ = angular.element("#" + spacerId);
            elQ = angular.element(vk.el);

            if (spacerQ.length > 0) {
                anchorTop = spacerQ.offset().top;
            } else {
                anchorTop = elQ.offset().top;
            }

            documentQ.scrollTop(anchorTop - verticalOffset - config.viewportMarginTop);
        },

        destroy: function () {
            var id,
                vk = this;

            if (!vk.isDestroyed) {
                id = vk.id;

                angular.element(window).off("scroll." + id + ", resize." + id + ", orientationchange." + id);
                unfixEl(vk);

                if (vk.verticalOffSetEl) {
                    vk.verticalOffSetEl.off("afterViewKeeperSync." + vk.id);
                    vk.verticalOffSetEl = null;
                }

                vk.isDestroyed = true;
            }
        }

    };

    angular.module('sky.viewkeeper', ['sky.mediabreakpoints', 'sky.window'])
        .constant('bbViewKeeperConfig', config)
        .factory('bbViewKeeperBuilder', function () {
            return {
                create: function (options) {
                    return new ViewKeeper(options);
                }
            };
        })
        .run(['$document', '$window', 'bbMediaBreakpoints', 'bbViewKeeperConfig', function ($document, $window, bbMediaBreakpoints, bbViewKeeperConfig) {
            function mediaBreakpointHandler(breakpoints) {
                //For user agents in which the omnibar follows you down the page, the ViewKeeper needs
                //to adjust for the height of the omnibar.
                if (bbViewKeeperConfig.hasOmnibar) {
                    //Ideally these values should be driven from a more appropriate source (omnibar js?)
                    bbViewKeeperConfig.viewportMarginTop = breakpoints.xs ? 50 : 30;
                    angular.element('body').removeClass(CLS_VIEWKEEPER_NO_OMNIBAR);
                } else {
                    angular.element('body').addClass(CLS_VIEWKEEPER_NO_OMNIBAR);
                }
            }

            if (/iPad|iPod|iPhone/i.test($window.navigator.userAgent)) {
                //On iOS the omnibar doesn't scroll with you.  Need to account for this on the styling.
                angular.element('body').addClass('omnibar-not-fixed');

                //On iOS we need to have special handling when entering textboxes to correct an issue with fixed
                //elements used by view keeper when the keyboard flys out.
                angular.element(document).on('focus', 'input', function () {
                    angular.element('body').addClass('bb-viewkeeper-ignore-fixed');
                }).on('blur', 'input', function () {
                    angular.element('body').removeClass('bb-viewkeeper-ignore-fixed');
                });
            } else {
                bbMediaBreakpoints.register(mediaBreakpointHandler);
            }
        }])
        .directive('bbViewKeeper', ['bbViewKeeperBuilder', function (bbViewKeeperBuilder) {
            function link(scope, el) {
                var vk;

                function destroyVk() {
                    if (vk) {
                        vk.destroy();
                        vk = null;
                    }
                }

                el.on('$destroy', function () {
                    destroyVk();
                });

                scope.$watch('bbBoundaryElId', function () {
                    var boundaryEl,
                        bbBoundaryElId = scope.bbBoundaryElId;

                    /*istanbul ignore else */
                    if (bbBoundaryElId) {
                        boundaryEl = angular.element('#' + bbBoundaryElId);

                        /*istanbul ignore else */
                        if (boundaryEl.length === 1) {
                            destroyVk();

                            vk = bbViewKeeperBuilder.create({
                                el: el[0],
                                boundaryEl: boundaryEl[0],
                                setWidth: true
                            });
                        }
                    }
                });
            }
            return {
                link: link,
                restrict: 'A',
                scope: {
                    bbBoundaryElId: '='
                }
            };
        }])
        .directive('bbScrollingViewKeeper', ['$window', 'bbWindow', function ($window, bbWindow) {
            return {
                scope: {
                    bbScrollingViewKeeper: "="
                },
                link: function (scope, element) {
                    var elementStart,
                        scrollPos,
                        prevScroll,
                        scrollingDown = true,
                        tempTop,
                        verticalOffset,
                        id = scope.$id;

                    function scroll() {
                        if (!element.is(':visible')) {
                            return;
                        }

                        if (angular.element('.bb-omnibar>.desktop').is(':visible')) {
                            verticalOffset = angular.element('.bb-omnibar>.desktop>.bar').outerHeight();
                        } else {
                            verticalOffset = 0;
                        }

                        if (scope.bbScrollingViewKeeper && scope.bbScrollingViewKeeper.viewKeeperOffsetElId) {
                            verticalOffset += angular.element('#' + scope.bbScrollingViewKeeper.viewKeeperOffsetElId).outerHeight();
                        }

                        if (!elementStart) {
                            elementStart = element.offset().top;
                        }
                        scrollPos = $window.scrollY || $window.pageYOffset || $window.document.body.scrollTop || 0;
                        if (prevScroll > scrollPos) {
                            scrollingDown = false;
                        } else {
                            scrollingDown = true;
                        }
                        prevScroll = scrollPos;

                        if (scrollPos >= elementStart - verticalOffset && element.height() + verticalOffset <= $window.document.body.offsetHeight) {
                            if (element.height() + verticalOffset < $window.innerHeight) {
                                tempTop = 0;

                                element.removeClass('bb-grid-filters-fixed-bottom').addClass('bb-grid-filters-fixed-top');

                                element.css({
                                    top: verticalOffset + 'px'
                                });
                            } else if (scrollingDown) {
                                if (element.offset().top + element.height() >= scrollPos + $window.innerHeight) {
                                    /*istanbul ignore else*/
                                    /* sanity check */
                                    if (!tempTop) {
                                        tempTop = element.offset().top - elementStart;
                                    }

                                    element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom');

                                    element.css({
                                        top: tempTop
                                    });
                                } else {
                                    tempTop = 0;
                                    element.css({
                                        top: ''
                                    });
                                    element.removeClass('bb-grid-filters-fixed-top').addClass('bb-grid-filters-fixed-bottom');
                                }
                            } else {
                                if (element.offset().top < scrollPos + verticalOffset) {
                                    /*istanbul ignore else*/
                                    /* sanity check */
                                    if (!tempTop) {
                                        tempTop = element.offset().top - elementStart;
                                    }

                                    element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom ');

                                    element.css({
                                        top: tempTop
                                    });
                                } else {
                                    tempTop = 0;

                                    element.removeClass('bb-grid-filters-fixed-bottom').addClass('bb-grid-filters-fixed-top');

                                    element.css({
                                        top: verticalOffset + 'px'
                                    });
                                }
                            }
                        } else {
                            tempTop = 0;
                            element.removeClass('bb-grid-filters-fixed-top bb-grid-filters-fixed-bottom grid-filters-fixed-top grid-filters-fixed-bottom');
                            element.css({
                                top: 0
                            });
                        }
                    }

                    if (!bbWindow.isIosUserAgent()) {
                        angular.element($window).on('scroll.' + id + ', orientationchange.' + id, scroll);

                        element.on('$destroy', function () {
                            angular.element($window).off("scroll." + id + ", orientationchange." + id);
                        });
                    }
                },
                restrict: 'A'
            };
        }]);
}());
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

const yargs = require('yargs');
const check = require('check-node-version');
const updateNotifier = require('update-notifier');
const pkg = require('./package.json');
const server = require('./server/server')

const wanted = {node: "6.4.0"};

updateNotifier({pkg}).notify();

const args =  yargs
  .usage("introspect [additional packages] [port]")
  .example("introspect react redux mocha -p 5000")
  .example("introspect -less")
  .option('l', {
    alias: 'less',
    describe: 'Skip visualization and output data',
    default: false
  })
  .option('p', {
    alias: 'port',
    describe: 'Specify port to use',
    nargs: 1,
    default: 8080,
  })
  .help('h')
  .alias('h', 'help')
  .argv;

check( wanted, function(error, result){
  if(error){
    throw 'While checking for correct node version there has been an error'
  }
  if (result.node.version.major !== 6 && result.node.version.major !== 7 ){
    console.log('This package requires node -v ≥ 6.4.0, please update to run')
    process.exit(1)
  }
  else{
    server.run(args)
  }
});
<commit_msg>Quick node version fix<commit_after>#!/usr/bin/env node
'use strict';

const yargs = require('yargs');
const check = require('check-node-version');
const updateNotifier = require('update-notifier');
const pkg = require('./package.json');
const server = require('./server/server')

const wanted = {node: "6.4.0"};

updateNotifier({pkg}).notify();

const args =  yargs
  .usage("introspect [additional packages] [port]")
  .example("introspect react redux mocha -p 5000")
  .example("introspect -less")
  .option('l', {
    alias: 'less',
    describe: 'Skip visualization and output data',
    default: false
  })
  .option('p', {
    alias: 'port',
    describe: 'Specify port to use',
    nargs: 1,
    default: 8080,
  })
  .help('h')
  .alias('h', 'help')
  .argv;

check( wanted, function(error, result){
  if(error){
    throw 'While checking for correct node version there has been an error'
  }
  if (result.node.version.major < 6 ){
    console.log('This package requires node -v ≥ 6.4.0, please update to run')
    process.exit(1)
  }
  else{
    server.run(args)
  }
});
<|endoftext|>


<commit_before>'use strict'

// ----------------------------------------------------------------------------

const assert = require ('assert')

//  ---------------------------------------------------------------------------

function testTradingFee (exchange = undefined, symbol, fee) {
    assert (fee, 'fee is undefined')
    assert (fee['info'], 'fee info is empty')
    assert (fee['symbol'] === symbol, 'trade symbol is not equal to requested symbol: trade: ' + fee['symbol'] + ' requested: ' + symbol)
    if (fee['maker']) {
        assert (typeof fee['maker'] === 'number', 'maker fee is not a number')
    }
    if (fee['taker']) {
        assert (typeof fee['taker'] === 'number', 'taker fee is not a number')
    }
    if (fee['percentage']) {
        assert (typeof fee['percentage'] === 'boolean', 'percentage is not a boolean')
    }
    if (fee['tierBased']) {
        assert (typeof fee['tierBased'] === 'boolean', 'tierBased is not a boolean')
    }
    return fee
}

module.exports = testTradingFee
<commit_msg>testTradingFee<commit_after>'use strict'

// ----------------------------------------------------------------------------

const assert = require ('assert')

//  ---------------------------------------------------------------------------

function testTradingFee (exchange = undefined, symbol, fee) {
    assert (fee, 'fee is undefined')
    const sampleFee = {
        'info': { 'a': 1, 'b': 2, 'c': 3 },
        'symbol': 'ETH/BTC',
        'maker': 0.002,
        'taker': 0.003,
    }
    const keys = Object.keys (sampleFee)
    for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        assert (key in fee, 'fee key ' + key + ' not found')
    }
    assert (fee['symbol'] === symbol, 'trade symbol is not equal to requested symbol: trade: ' + fee['symbol'] + ' requested: ' + symbol)
    assert (typeof fee['maker'] === 'number', 'maker fee is not a number')
    assert (typeof fee['taker'] === 'number', 'taker fee is not a number')
    if ('percentage' in fee) {
        assert (typeof fee['percentage'] === 'boolean', 'percentage is not a boolean')
    }
    if ('tierBased' in fee) {
        assert (typeof fee['tierBased'] === 'boolean', 'percentage is not a boolean')
    }
    return fee
}

module.exports = testTradingFee
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var fs = require('fs');
var path = require('path');
var meow = require('meow');
var getStdin = require('get-stdin');
var pathExists = require('path-exists');
var Imagemin = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ imagemin <file> <directory>',
		'  $ imagemin <directory> <output>',
		'  $ imagemin <file> > <output>',
		'  $ cat <file> | imagemin > <output>',
		'',
		'Example',
		'  $ imagemin images/* build',
		'  $ imagemin images build',
		'  $ imagemin foo.png > foo-optimized.png',
		'  $ cat foo.png | imagemin > foo-optimized.png',
		'',
		'Options',
		'  -i, --interlaced                    Interlace gif for progressive rendering',
		'  -o, --optimizationLevel <number>    Optimization level between 0 and 7',
		'  -p, --progressive                   Lossless conversion to progressive'
	].join('\n')
}, {
	boolean: [
		'interlaced',
		'progressive'
	],
	string: [
		'optimizationLevel'
	],
	alias: {
		i: 'interlaced',
		o: 'optimizationLevel',
		p: 'progressive'
	}
});

function isFile(path) {
	if (/^[^\s]+\.\w*$/.test(path)) {
		return true;
	}

	try {
		return fs.statSync(path).isFile();
	} catch (err) {
		return false;
	}
}

function run(src, dest) {
	var imagemin = new Imagemin()
		.src(src)
		.use(Imagemin.gifsicle(cli.flags))
		.use(Imagemin.jpegtran(cli.flags))
		.use(Imagemin.optipng(cli.flags))
		.use(Imagemin.svgo());

	if (process.stdout.isTTY) {
		imagemin.dest(dest ? dest : 'build');
	}

	imagemin.run(function (err, files) {
		if (err) {
			console.error(err.message);
			process.exit(1);
		}

		if (!process.stdout.isTTY) {
			files.forEach(function (file) {
				process.stdout.write(file.contents);
			});
		}
	});
}

if (process.stdin.isTTY) {
	var src = cli.input;
	var dest;

	if (!cli.input.length) {
		console.error([
			'Provide at least one file to optimize',
			'',
			'Example',
			'  imagemin images/* build',
			'  imagemin foo.png > foo-optimized.png',
			'  cat foo.png | imagemin > foo-optimized.png'
		].join('\n'));

		process.exit(1);
	}

	if (src.length > 1 && !isFile(src[src.length - 1])) {
		dest = src[src.length - 1];
		src.pop();
	}

	src = src.map(function (s) {
		if (!isFile(s) && pathExists.sync(s)) {
			return path.join(s, '**/*');
		}

		return s;
	});

	run(src, dest);
} else {
	getStdin.buffer(run);
}
<commit_msg>Improve TTY check<commit_after>#!/usr/bin/env node
'use strict';

var fs = require('fs');
var path = require('path');
var meow = require('meow');
var getStdin = require('get-stdin');
var pathExists = require('path-exists');
var Imagemin = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ imagemin <file> <directory>',
		'  $ imagemin <directory> <output>',
		'  $ imagemin <file> > <output>',
		'  $ cat <file> | imagemin > <output>',
		'',
		'Example',
		'  $ imagemin images/* build',
		'  $ imagemin images build',
		'  $ imagemin foo.png > foo-optimized.png',
		'  $ cat foo.png | imagemin > foo-optimized.png',
		'',
		'Options',
		'  -i, --interlaced                    Interlace gif for progressive rendering',
		'  -o, --optimizationLevel <number>    Optimization level between 0 and 7',
		'  -p, --progressive                   Lossless conversion to progressive'
	].join('\n')
}, {
	boolean: [
		'interlaced',
		'progressive'
	],
	string: [
		'optimizationLevel'
	],
	alias: {
		i: 'interlaced',
		o: 'optimizationLevel',
		p: 'progressive'
	}
});

function isFile(path) {
	if (/^[^\s]+\.\w*$/.test(path)) {
		return true;
	}

	try {
		return fs.statSync(path).isFile();
	} catch (err) {
		return false;
	}
}

function run(src, dest) {
	var imagemin = new Imagemin()
		.src(src)
		.use(Imagemin.gifsicle(cli.flags))
		.use(Imagemin.jpegtran(cli.flags))
		.use(Imagemin.optipng(cli.flags))
		.use(Imagemin.svgo());

	if (process.stdout.isTTY) {
		imagemin.dest(dest ? dest : 'build');
	}

	imagemin.run(function (err, files) {
		if (err) {
			console.error(err.message);
			process.exit(1);
		}

		if (!process.stdout.isTTY) {
			files.forEach(function (file) {
				process.stdout.write(file.contents);
			});
		}
	});
}

if (!cli.input.length && process.stdin.isTTY) {
	console.error([
		'Provide at least one file to optimize',
		'',
		'Example',
		'  imagemin images/* build',
		'  imagemin foo.png > foo-optimized.png',
		'  cat foo.png | imagemin > foo-optimized.png'
	].join('\n'));

	process.exit(1);
}

if (cli.input.length) {
	var src = cli.input;
	var dest;

	if (src.length > 1 && !isFile(src[src.length - 1])) {
		dest = src[src.length - 1];
		src.pop();
	}

	src = src.map(function (s) {
		if (!isFile(s) && pathExists.sync(s)) {
			return path.join(s, '**/*');
		}

		return s;
	});

	run(src, dest);
} else {
	getStdin.buffer(run);
}
<|endoftext|>


<commit_before>$(function () {

    module("bootstrap-carousel")

      test("should be defined on jquery object", function () {
        ok($(document.body).carousel, 'carousel method is defined')
      })

      test("should return element", function () {
        ok($(document.body).carousel()[0] == document.body, 'document.body returned')
      })

      test("should not fire sliden when slide is prevented", function () {
        $.support.transition = false
        stop()
        $('
')
          .bind('slide', function (e) {
            e.preventDefault();
            ok(true);
            start();
          })
          .bind('slid', function () {
            ok(false);
          })
          .carousel('next')
      })

      test("should fire slide event with relatedTarget", function () {
        var template = '

{{_i}}First Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

{{_i}}Second Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

{{_i}}Third Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

‹›
'
        $.support.transition = false
        stop()
        $(template)
          .on('slide', function (e) {
            e.preventDefault();
            ok(e.relatedTarget);
            ok($(e.relatedTarget).hasClass('item'));
            start();
          })
          .carousel('next')
      })

})<commit_msg>remove src's<commit_after>$(function () {

    module("bootstrap-carousel")

      test("should be defined on jquery object", function () {
        ok($(document.body).carousel, 'carousel method is defined')
      })

      test("should return element", function () {
        ok($(document.body).carousel()[0] == document.body, 'document.body returned')
      })

      test("should not fire sliden when slide is prevented", function () {
        $.support.transition = false
        stop()
        $('
')
          .bind('slide', function (e) {
            e.preventDefault();
            ok(true);
            start();
          })
          .bind('slid', function () {
            ok(false);
          })
          .carousel('next')
      })

      test("should fire slide event with relatedTarget", function () {
        var template = '

{{_i}}First Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

{{_i}}Second Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

{{_i}}Third Thumbnail label{{/i}}

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.

‹›
'
        $.support.transition = false
        stop()
        $(template)
          .on('slide', function (e) {
            e.preventDefault();
            ok(e.relatedTarget);
            ok($(e.relatedTarget).hasClass('item'));
            start();
          })
          .carousel('next')
      })

})<|endoftext|>


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

var minimist = require('minimist');
var osenv = require('osenv');
var humanSize = require('human-size');
var ini = require('ini');
var fs = require('fs');
var path = require('path');
var s3 = require('s3');
var url = require('url');
var http = require('http');
var https = require('https');
var argOptions = {
  'default': {
    'config': path.join(osenv.home(), '.s3cfg'),
    'delete-removed': false,
    'max-sockets': 20,
    'region': 'us-east-1',
    'default-mime-type': null,
    'add-header': null,
  },
  'boolean': [
    'recursive',
    'delete-removed',
    'insecure',
    'acl-public',
    'acl-private',
    'no-guess-mime-type',
  ],
  'alias': {
    'P': 'acl-public',
  },
};
var args = minimist(process.argv.slice(2), argOptions);

var fns = {
  'sync': cmdSync,
  'ls': cmdList,
  'help': cmdHelp,
  'del': cmdDelete,
  'put': cmdPut,
  'get': cmdGet,
  'cp': cmdCp,
  'mv': cmdMv,
};
var USAGE_TEXT =
  "Usage: s3-cli (command) (command arguments)\n" +
  "Commands: " + Object.keys(fns).join(" ");
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;

var s3UrlRe = /^[sS]3:\/\/(.*?)\/(.*)/;
barfOnUnexpectedArgs();

var client;
fs.readFile(args.config, {encoding: 'utf8'}, function(err, contents) {
  if (err) {
    console.error("This utility needs a config file formatted the same as for s3cmd");
    process.exit(1);
    return;
  }
  var config = ini.parse(contents);
  var accessKeyId, secretAccessKey;
  if (config && config.default) {
    accessKeyId = config.default.access_key;
    secretAccessKey = config.default.secret_key;
  }
  if (!secretAccessKey || !accessKeyId) {
    console.error("Config file missing access_key or secret_key");
    process.exit(1);
    return;
  }
  var maxSockets = parseInt(args['max-sockets'], 10);
  http.globalAgent.maxSockets = maxSockets;
  https.globalAgent.maxSockets = maxSockets;
  client = s3.createClient({
    s3Options: {
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
      sslEnabled: !args.insecure,
      region: args.region,
    },
  });
  var cmd = args._.shift();
  var fn = fns[cmd];
  if (!fn) fn = cmdHelp;
  fn();
});

function cmdSync() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];

  var sourceS3 = isS3Url(source);
  var destS3 = isS3Url(dest);

  var localDir, s3Url, method;
  var getS3Params;
  var s3Params = {};
  if (sourceS3 && !destS3) {
    localDir = dest;
    s3Url = source;
    method = client.downloadDir;
    getS3Params = downloadGetS3Params;
  } else if (!sourceS3 && destS3) {
    localDir = source;
    s3Url = dest;
    method = client.uploadDir;
    s3Params.ACL = getAcl();
    getS3Params = uploadGetS3Params;
  } else {
    console.error("one target must be from S3, the other must be from local file system.");
    process.exit(1);
  }
  var parts = parseS3Url(s3Url);
  s3Params.Prefix = parts.key;
  s3Params.Bucket = parts.bucket;

  parseAddHeaders(s3Params);

  var params = {
    deleteRemoved: args['delete-removed'],
    getS3Params: getS3Params,
    localDir: localDir,
    s3Params: s3Params,
    defaultContentType: getDefaultContentType(),
  };
  var syncer = method.call(client, params);
  setUpProgress(syncer);
}

function uploadGetS3Params(filePath, stat, callback) {
  //console.error("Uploading", filePath);
  callback(null, {
    ContentType: getContentType(),
  });
}

function downloadGetS3Params(filePath, s3Object, callback) {
  //console.error("Downloading", filePath);
  callback(null, {});
}

function cmdList() {
  expectArgCount(1);
  var recursive = args.recursive;
  var s3Url = args._[0];
  var parts = parseS3Url(s3Url);
  var params = {
    recursive: recursive,
    s3Params: {
      Bucket: parts.bucket,
      Prefix: parts.key,
      Delimiter: recursive ? null : '/',
    },
  };
  var finder = client.listObjects(params);
  finder.on('data', function(data) {
    data.CommonPrefixes.forEach(function(dirObject) {
      console.log("DIR " + dirObject.Prefix);
    });
    data.Contents.forEach(function(object) {
      console.log(object.LastModified + " " + object.Size + " " + object.Key);
    });
  });
  finder.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdDelete() {
  expectArgCount(1);
  var parts = parseS3Url(args._[0]);
  if (args.recursive) {
    doDeleteDir();
  } else {
    doDeleteObject();
  }

  function doDeleteDir() {
    var params = {
      Bucket: parts.bucket,
      Prefix: parts.key,
    };
    var deleter = client.deleteDir(params);
    setUpProgress(deleter, true);
  }

  function doDeleteObject() {
    var params = {
      Bucket: parts.bucket,
      Delete: {
        Objects: [
          {
            Key: parts.key,
          },
        ],
      }
    };
    var deleter = client.deleteObjects(params);
    deleter.on('error', function(err) {
      console.error("Error: " + err.message);
      process.exit(1);
    });
  }
}

function cmdPut() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var parts = parseS3Url(dest);
  if (/\/$/.test(parts.key)) {
    parts.key += path.basename(source);
  }
  var acl = getAcl();
  var s3Params = {
    Bucket: parts.bucket,
    Key: parts.key,
    ACL: acl,
    ContentType: getContentType(),
  };
  parseAddHeaders(s3Params);
  var params = {
    localFile: source,
    s3Params: s3Params,
    defaultContentType: getDefaultContentType(),
  };
  var uploader = client.uploadFile(params);
  var doneText;
  if (acl === 'public-read') {
    var publicUrl = args.insecure ?
      s3.getPublicUrlHttp(parts.bucket, parts.key) :
      s3.getPublicUrl(parts.bucket, parts.key, args.region);
    doneText = "Public URL: " + publicUrl;
  } else {
    doneText = "done";
  }
  setUpProgress(uploader, false, doneText);
}

function cmdGet() {
  expectArgCount(1, 2);
  var source = args._[0];
  var dest = args._[1];
  var parts = parseS3Url(source);
  if (!dest) {
    dest = unixBasename(source);
  } else if (dest[dest.length - 1] === path.sep) {
    dest = path.join(dest, unixBasename(source));
  }

  var params = {
    localFile: dest,
    s3Params: {
      Bucket: parts.bucket,
      Key: parts.key,
    },
  };
  var downloader = client.downloadFile(params);
  setUpProgress(downloader);
}

function cmdCp() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var sourceParts = parseS3Url(source);
  var destParts = parseS3Url(dest);

  var s3Params = {
    CopySource: sourceParts.bucket + '/' + sourceParts.key,
    Bucket: destParts.bucket,
    Key: destParts.key,
  };

  var copier = client.copyObject(s3Params);
  copier.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdMv() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var sourceParts = parseS3Url(source);
  var destParts = parseS3Url(dest);

  var s3Params = {
    CopySource: sourceParts.bucket + '/' + sourceParts.key,
    Bucket: destParts.bucket,
    Key: destParts.key,
  };

  var mover = client.moveObject(s3Params);
  mover.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdHelp() {
  console.log(USAGE_TEXT);
}

function parseS3Url(s3Url) {
  if (!s3Url) {
    console.error("Expected S3 URL argument");
    process.exit(1);
  }
  var match = s3Url.match(s3UrlRe);
  if (!match) {
    console.error("Not a valid S3 URL:", s3Url);
    process.exit(1);
  }
  return {
    bucket: match[1],
    key: match[2],
  };
}

function isS3Url(str) {
  return s3UrlRe.test(str);
}

function getContentType() {
  return args['no-guess-mime-type'] ? null : undefined;
}

function getDefaultContentType() {
  return args['default-mime-type'] || null;
}

function getAcl() {
  var acl = null;
  if (args['acl-public']) {
    acl = 'public-read';
  } else if (args['acl-private']) {
    acl = 'private';
  }
  return acl;
}

function setUpProgress(o, notBytes, doneText) {
  var start = null;
  doneText = doneText || "done";
  var printFn = process.stderr.isTTY ? printProgress : noop;
  printFn();
  var progressInterval = setInterval(printFn, 100);
  o.on('end', function() {
    clearInterval(progressInterval);
    process.stderr.write("\n" + doneText + "\n");
  });
  o.on('error', function(err) {
    clearInterval(progressInterval);
    process.stderr.write("\nError: " + err.message + "\n");
    process.exit(1);
  });

  function printProgress() {
    var percent = Math.floor(o.progressAmount / o.progressTotal * 100);
    var amt = notBytes ? String(o.progressAmount) : fmtBytes(o.progressAmount);
    var total = notBytes ? String(o.progressTotal) : fmtBytes(o.progressTotal);
    var parts = [];
    if (o.filesFound > 0 && !o.doneFindingFiles) {
      parts.push(o.filesFound + " files");
    }
    if (o.objectsFound > 0 && !o.doneFindingObjects) {
      parts.push(o.objectsFound + " objects");
    }
    if (o.deleteTotal > 0) {
      parts.push(o.deleteAmount + "/" + o.deleteTotal + " deleted");
    }
    if (o.progressMd5Amount > 0 && !o.doneMd5) {
      parts.push(fmtBytes(o.progressMd5Amount) + "/" + fmtBytes(o.progressMd5Total) + " MD5");
    }
    if (o.progressTotal > 0) {
      if (!start) start = new Date();
      var part = amt + "/" + total;
      if (!isNaN(percent)) part += " " + percent + "% done";
      parts.push(part);
      if (!notBytes) {
        var now = new Date();
        var seconds = (now - start) / 1000;
        var bytesPerSec = o.progressAmount / seconds;
        var humanSpeed = fmtBytes(bytesPerSec) + '/s';
        parts.push(humanSpeed);
      }
    }
    var line = parts.join(", ");
    process.stderr.clearLine();
    process.stderr.cursorTo(0);
    process.stderr.write(line);
  }
}

function parseAddHeaders(s3Params) {
  var addHeaders = args['add-header'];
  if (addHeaders) {
    if (Array.isArray(addHeaders)) {
      addHeaders.forEach(handleAddHeader);
    } else {
      handleAddHeader(addHeaders);
    }
  }
  function handleAddHeader(header) {
    var match = header.match(/^(.*):\s*(.*)$/);
    if (!match) {
      console.error("Improperly formatted header:", header);
      process.exit(1);
    }
    var headerName = match[1];
    var paramName = headerName.replace(/-/g, '');
    var paramValue = match[2];
    s3Params[paramName] = paramValue;
  }
}

function fmtBytes(byteCount) {
  if (byteCount <= 0) {
    return "0 B";
  } else {
    return humanSize(byteCount, 1);
  }
}

function noop() {}

function barfOnUnexpectedArgs() {
  var validArgs = {'_': true};
  addValid(Object.keys(argOptions.default));
  addValid(Object.keys(argOptions.alias));
  addValid(argOptions.boolean);

  var invalidArgs = [];
  for (var argName in args) {
    if (!validArgs[argName]) {
      invalidArgs.push(argName);
    }
  }

  if (invalidArgs.length) {
    console.error(USAGE_TEXT);
    console.error("Unrecognized option(s): " + invalidArgs.join(", "));
    process.exit(1);
  }

  function addValid(array) {
    array.forEach(function(name) {
      validArgs[name] = true;
    });
  }
}

function expectArgCount(min, max) {
  if (max == null) max = min;
  if (args._.length < min) {
    console.error("Expected at least " + min + " arguments, got " + args._.length);
    process.exit(1);
  }
  if (args._.length > max) {
    console.error("Expected at most " + max + " arguments, got " + args._.length);
    process.exit(1);
  }
}

// copied from Node.js path module for unix only
function unixSplitPath(filename) {
  return splitPathRe.exec(filename).slice(1);
}
function unixBasename(path) {
  return unixSplitPath(path)[2];
}
<commit_msg>Optionally use environment variables for credentials<commit_after>#!/usr/bin/env node

var minimist = require('minimist');
var osenv = require('osenv');
var humanSize = require('human-size');
var ini = require('ini');
var fs = require('fs');
var path = require('path');
var s3 = require('s3');
var url = require('url');
var http = require('http');
var https = require('https');
var argOptions = {
  'default': {
    'config': path.join(osenv.home(), '.s3cfg'),
    'delete-removed': false,
    'max-sockets': 20,
    'region': 'us-east-1',
    'default-mime-type': null,
    'add-header': null,
  },
  'boolean': [
    'recursive',
    'delete-removed',
    'insecure',
    'acl-public',
    'acl-private',
    'no-guess-mime-type',
  ],
  'alias': {
    'P': 'acl-public',
  },
};
var args = minimist(process.argv.slice(2), argOptions);

var fns = {
  'sync': cmdSync,
  'ls': cmdList,
  'help': cmdHelp,
  'del': cmdDelete,
  'put': cmdPut,
  'get': cmdGet,
  'cp': cmdCp,
  'mv': cmdMv,
};
var USAGE_TEXT =
  "Usage: s3-cli (command) (command arguments)\n" +
  "Commands: " + Object.keys(fns).join(" ");
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;

var s3UrlRe = /^[sS]3:\/\/(.*?)\/(.*)/;
barfOnUnexpectedArgs();

var client;
if (process.env.AWS_SECRET_KEY && process.env.AWS_ACCESS_KEY) {
  setup(process.env.AWS_SECRET_KEY, process.env.AWS_ACCESS_KEY);
} else {
  fs.readFile(args.config, {encoding: 'utf8'}, function(err, contents) {
    if (err) {
      console.error("This utility needs a config file formatted the same as for s3cmd");
      process.exit(1);
      return;
    }
    var config = ini.parse(contents);
    var accessKeyId, secretAccessKey;
    if (config && config.default) {
      accessKeyId = config.default.access_key;
      secretAccessKey = config.default.secret_key;
    }
    if (!secretAccessKey || !accessKeyId) {
      console.error("Config file missing access_key or secret_key");
      process.exit(1);
      return;
    }
    setup(secretAccessKey, accessKeyId);
  });
}

function setup(secretAccessKey, accessKeyId) {
  var maxSockets = parseInt(args['max-sockets'], 10);
  http.globalAgent.maxSockets = maxSockets;
  https.globalAgent.maxSockets = maxSockets;
  client = s3.createClient({
    s3Options: {
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
      sslEnabled: !args.insecure,
      region: args.region,
    },
  });
  var cmd = args._.shift();
  var fn = fns[cmd];
  if (!fn) fn = cmdHelp;
  fn();
}

function cmdSync() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];

  var sourceS3 = isS3Url(source);
  var destS3 = isS3Url(dest);

  var localDir, s3Url, method;
  var getS3Params;
  var s3Params = {};
  if (sourceS3 && !destS3) {
    localDir = dest;
    s3Url = source;
    method = client.downloadDir;
    getS3Params = downloadGetS3Params;
  } else if (!sourceS3 && destS3) {
    localDir = source;
    s3Url = dest;
    method = client.uploadDir;
    s3Params.ACL = getAcl();
    getS3Params = uploadGetS3Params;
  } else {
    console.error("one target must be from S3, the other must be from local file system.");
    process.exit(1);
  }
  var parts = parseS3Url(s3Url);
  s3Params.Prefix = parts.key;
  s3Params.Bucket = parts.bucket;

  parseAddHeaders(s3Params);

  var params = {
    deleteRemoved: args['delete-removed'],
    getS3Params: getS3Params,
    localDir: localDir,
    s3Params: s3Params,
    defaultContentType: getDefaultContentType(),
  };
  var syncer = method.call(client, params);
  setUpProgress(syncer);
}

function uploadGetS3Params(filePath, stat, callback) {
  //console.error("Uploading", filePath);
  callback(null, {
    ContentType: getContentType(),
  });
}

function downloadGetS3Params(filePath, s3Object, callback) {
  //console.error("Downloading", filePath);
  callback(null, {});
}

function cmdList() {
  expectArgCount(1);
  var recursive = args.recursive;
  var s3Url = args._[0];
  var parts = parseS3Url(s3Url);
  var params = {
    recursive: recursive,
    s3Params: {
      Bucket: parts.bucket,
      Prefix: parts.key,
      Delimiter: recursive ? null : '/',
    },
  };
  var finder = client.listObjects(params);
  finder.on('data', function(data) {
    data.CommonPrefixes.forEach(function(dirObject) {
      console.log("DIR " + dirObject.Prefix);
    });
    data.Contents.forEach(function(object) {
      console.log(object.LastModified + " " + object.Size + " " + object.Key);
    });
  });
  finder.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdDelete() {
  expectArgCount(1);
  var parts = parseS3Url(args._[0]);
  if (args.recursive) {
    doDeleteDir();
  } else {
    doDeleteObject();
  }

  function doDeleteDir() {
    var params = {
      Bucket: parts.bucket,
      Prefix: parts.key,
    };
    var deleter = client.deleteDir(params);
    setUpProgress(deleter, true);
  }

  function doDeleteObject() {
    var params = {
      Bucket: parts.bucket,
      Delete: {
        Objects: [
          {
            Key: parts.key,
          },
        ],
      }
    };
    var deleter = client.deleteObjects(params);
    deleter.on('error', function(err) {
      console.error("Error: " + err.message);
      process.exit(1);
    });
  }
}

function cmdPut() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var parts = parseS3Url(dest);
  if (/\/$/.test(parts.key)) {
    parts.key += path.basename(source);
  }
  var acl = getAcl();
  var s3Params = {
    Bucket: parts.bucket,
    Key: parts.key,
    ACL: acl,
    ContentType: getContentType(),
  };
  parseAddHeaders(s3Params);
  var params = {
    localFile: source,
    s3Params: s3Params,
    defaultContentType: getDefaultContentType(),
  };
  var uploader = client.uploadFile(params);
  var doneText;
  if (acl === 'public-read') {
    var publicUrl = args.insecure ?
      s3.getPublicUrlHttp(parts.bucket, parts.key) :
      s3.getPublicUrl(parts.bucket, parts.key, args.region);
    doneText = "Public URL: " + publicUrl;
  } else {
    doneText = "done";
  }
  setUpProgress(uploader, false, doneText);
}

function cmdGet() {
  expectArgCount(1, 2);
  var source = args._[0];
  var dest = args._[1];
  var parts = parseS3Url(source);
  if (!dest) {
    dest = unixBasename(source);
  } else if (dest[dest.length - 1] === path.sep) {
    dest = path.join(dest, unixBasename(source));
  }

  var params = {
    localFile: dest,
    s3Params: {
      Bucket: parts.bucket,
      Key: parts.key,
    },
  };
  var downloader = client.downloadFile(params);
  setUpProgress(downloader);
}

function cmdCp() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var sourceParts = parseS3Url(source);
  var destParts = parseS3Url(dest);

  var s3Params = {
    CopySource: sourceParts.bucket + '/' + sourceParts.key,
    Bucket: destParts.bucket,
    Key: destParts.key,
  };

  var copier = client.copyObject(s3Params);
  copier.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdMv() {
  expectArgCount(2);
  var source = args._[0];
  var dest = args._[1];
  var sourceParts = parseS3Url(source);
  var destParts = parseS3Url(dest);

  var s3Params = {
    CopySource: sourceParts.bucket + '/' + sourceParts.key,
    Bucket: destParts.bucket,
    Key: destParts.key,
  };

  var mover = client.moveObject(s3Params);
  mover.on('error', function(err) {
    console.error("Error: " + err.message);
    process.exit(1);
  });
}

function cmdHelp() {
  console.log(USAGE_TEXT);
}

function parseS3Url(s3Url) {
  if (!s3Url) {
    console.error("Expected S3 URL argument");
    process.exit(1);
  }
  var match = s3Url.match(s3UrlRe);
  if (!match) {
    console.error("Not a valid S3 URL:", s3Url);
    process.exit(1);
  }
  return {
    bucket: match[1],
    key: match[2],
  };
}

function isS3Url(str) {
  return s3UrlRe.test(str);
}

function getContentType() {
  return args['no-guess-mime-type'] ? null : undefined;
}

function getDefaultContentType() {
  return args['default-mime-type'] || null;
}

function getAcl() {
  var acl = null;
  if (args['acl-public']) {
    acl = 'public-read';
  } else if (args['acl-private']) {
    acl = 'private';
  }
  return acl;
}

function setUpProgress(o, notBytes, doneText) {
  var start = null;
  doneText = doneText || "done";
  var printFn = process.stderr.isTTY ? printProgress : noop;
  printFn();
  var progressInterval = setInterval(printFn, 100);
  o.on('end', function() {
    clearInterval(progressInterval);
    process.stderr.write("\n" + doneText + "\n");
  });
  o.on('error', function(err) {
    clearInterval(progressInterval);
    process.stderr.write("\nError: " + err.message + "\n");
    process.exit(1);
  });

  function printProgress() {
    var percent = Math.floor(o.progressAmount / o.progressTotal * 100);
    var amt = notBytes ? String(o.progressAmount) : fmtBytes(o.progressAmount);
    var total = notBytes ? String(o.progressTotal) : fmtBytes(o.progressTotal);
    var parts = [];
    if (o.filesFound > 0 && !o.doneFindingFiles) {
      parts.push(o.filesFound + " files");
    }
    if (o.objectsFound > 0 && !o.doneFindingObjects) {
      parts.push(o.objectsFound + " objects");
    }
    if (o.deleteTotal > 0) {
      parts.push(o.deleteAmount + "/" + o.deleteTotal + " deleted");
    }
    if (o.progressMd5Amount > 0 && !o.doneMd5) {
      parts.push(fmtBytes(o.progressMd5Amount) + "/" + fmtBytes(o.progressMd5Total) + " MD5");
    }
    if (o.progressTotal > 0) {
      if (!start) start = new Date();
      var part = amt + "/" + total;
      if (!isNaN(percent)) part += " " + percent + "% done";
      parts.push(part);
      if (!notBytes) {
        var now = new Date();
        var seconds = (now - start) / 1000;
        var bytesPerSec = o.progressAmount / seconds;
        var humanSpeed = fmtBytes(bytesPerSec) + '/s';
        parts.push(humanSpeed);
      }
    }
    var line = parts.join(", ");
    process.stderr.clearLine();
    process.stderr.cursorTo(0);
    process.stderr.write(line);
  }
}

function parseAddHeaders(s3Params) {
  var addHeaders = args['add-header'];
  if (addHeaders) {
    if (Array.isArray(addHeaders)) {
      addHeaders.forEach(handleAddHeader);
    } else {
      handleAddHeader(addHeaders);
    }
  }
  function handleAddHeader(header) {
    var match = header.match(/^(.*):\s*(.*)$/);
    if (!match) {
      console.error("Improperly formatted header:", header);
      process.exit(1);
    }
    var headerName = match[1];
    var paramName = headerName.replace(/-/g, '');
    var paramValue = match[2];
    s3Params[paramName] = paramValue;
  }
}

function fmtBytes(byteCount) {
  if (byteCount <= 0) {
    return "0 B";
  } else {
    return humanSize(byteCount, 1);
  }
}

function noop() {}

function barfOnUnexpectedArgs() {
  var validArgs = {'_': true};
  addValid(Object.keys(argOptions.default));
  addValid(Object.keys(argOptions.alias));
  addValid(argOptions.boolean);

  var invalidArgs = [];
  for (var argName in args) {
    if (!validArgs[argName]) {
      invalidArgs.push(argName);
    }
  }

  if (invalidArgs.length) {
    console.error(USAGE_TEXT);
    console.error("Unrecognized option(s): " + invalidArgs.join(", "));
    process.exit(1);
  }

  function addValid(array) {
    array.forEach(function(name) {
      validArgs[name] = true;
    });
  }
}

function expectArgCount(min, max) {
  if (max == null) max = min;
  if (args._.length < min) {
    console.error("Expected at least " + min + " arguments, got " + args._.length);
    process.exit(1);
  }
  if (args._.length > max) {
    console.error("Expected at most " + max + " arguments, got " + args._.length);
    process.exit(1);
  }
}

// copied from Node.js path module for unix only
function unixSplitPath(filename) {
  return splitPathRe.exec(filename).slice(1);
}
function unixBasename(path) {
  return unixSplitPath(path)[2];
}
<|endoftext|>


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

var mdns = require('multicast-dns')()
var path = require('path')

if (process.argv.length < 3) {
  console.error('Usage: %s <hostname>', path.basename(process.argv[1]))
  process.exit(1)
}
var hostname = process.argv[2]

mdns.on('response', function (response) {
  response.answers.forEach(function (answer) {
    if (answer.name === hostname) {
      console.log(answer.data)
      process.exit()
    }
  })
})

mdns.query(hostname, 'A')

// Give responses 3 seconds to respond
setTimeout(function () {
  console.error('Hostname not found')
  process.exit(1)
}, 3000)
<commit_msg>Make cli.js executable by default<commit_after>#!/usr/bin/env node

var mdns = require('multicast-dns')()
var path = require('path')

if (process.argv.length < 3) {
  console.error('Usage: %s <hostname>', path.basename(process.argv[1]))
  process.exit(1)
}
var hostname = process.argv[2]

mdns.on('response', function (response) {
  response.answers.forEach(function (answer) {
    if (answer.name === hostname) {
      console.log(answer.data)
      process.exit()
    }
  })
})

mdns.query(hostname, 'A')

// Give responses 3 seconds to respond
setTimeout(function () {
  console.error('Hostname not found')
  process.exit(1)
}, 3000)
<|endoftext|>