<commit_before>var App = function (options){

	/*
	*	Phrases are in the Data.phrases global var
	*/

	var init = function (){
		handlers();
	};

	var handlers = function (){
		$("#btn_generate").click(function(){
			animateToThisPhrase(generateRandomPhrase());
		});
		$("#btn_better").click(function(){
			TweenLite.to(window, 0.8, {scrollTo:{y:"max"}, ease:Power2.easeOut});
		})
	};

	/** Returns a random Phrase object **/
	var generateRandomPhrase = function (){
		var phrases = Data.phrases;
		var phraseObj = {};
		var subject = phrases.subjects[Math.floor(Math.random()*phrases.subjects.length)];
		var verb    = phrases.verb[Math.floor(Math.random()*phrases.verb.length)];
		var object  = phrases.objects[Math.floor(Math.random()*phrases.objects.length)];
		var extra   = phrases.extra[Math.floor(Math.random()*phrases.extra.length)];

		var doExtra = Math.random() < 0.5;

		var phrase = subject.value + ' ' + subject.preposition + ' ' + verb + ' '  + object;
		if(doExtra){
			phrase += ' ' + extra;
		}

	    phraseObj.subject = subject;
	    phraseObj.verb = verb;
	    phraseObj.object = object;
	    phraseObj.extra = extra;
	    phraseObj.phrase = phrase;

		return phraseObj;
	};

	/** animates the html to the given Phrase object **/
	var animateToThisPhrase = function (phraseObj){
		// $("#subject").html(phraseObj.subject.value);
		// $("#preposition").html(phraseObj.subject.preposition);
		// $("#action").html(phraseObj.verb+" "+phraseObj.object);
		// $("#goal").html(phraseObj.extra);

		animateLineByIdAndText("#subject",phraseObj.subject.value,Math.random()*0.35+0.075);
		animateLineByIdAndText("#preposition",phraseObj.subject.preposition,Math.random()*0.35+0.075);
		animateLineByIdAndText("#action",phraseObj.verb+" "+phraseObj.object,Math.random()*0.35+0.075);
		animateLineByIdAndText("#goal",phraseObj.extra,Math.random()*0.35+0.075);

	};

	var animateLineByIdAndText = function (lineId,newText,speed){
		var scrnWidth = $(window).width()+100;
		TweenLite.to($(lineId),speed,{css:{x:-scrnWidth}, ease:Power2.easeOut,
			onComplete:function(){
				TweenLite.to($(lineId),0,{css:{x:scrnWidth}});
				$(lineId).html(newText);
				TweenLite.to($(lineId),speed,{css:{x:0}, ease:Power2.easeOut});
			}});
	};

	return {
		init: init
	};
};

$(function(){
	var app = new App();
	app.init();
});

<commit_msg>Phrase clickable<commit_after>var App = function (options){

	/*
	*	Phrases are in the Data.phrases global var
	*/

	var init = function (){
		handlers();
	};

	var handlers = function (){
		$("#btn_generate").click(function(){
			animateToThisPhrase(generateRandomPhrase());
		});
		$("#mediaIdea").click(function(){
			animateToThisPhrase(generateRandomPhrase());
		});
		$("#btn_better").click(function(){
			TweenLite.to(window, 0.8, {scrollTo:{y:"max"}, ease:Power2.easeOut});
		})
	};

	/** Returns a random Phrase object **/
	var generateRandomPhrase = function (){
		var phrases = Data.phrases;
		var phraseObj = {};
		var subject = phrases.subjects[Math.floor(Math.random()*phrases.subjects.length)];
		var verb    = phrases.verb[Math.floor(Math.random()*phrases.verb.length)];
		var object  = phrases.objects[Math.floor(Math.random()*phrases.objects.length)];
		var extra   = phrases.extra[Math.floor(Math.random()*phrases.extra.length)];

		var doExtra = Math.random() < 0.5;

		var phrase = subject.value + ' ' + subject.preposition + ' ' + verb + ' '  + object;
		if(doExtra){
			phrase += ' ' + extra;
		}

	    phraseObj.subject = subject;
	    phraseObj.verb = verb;
	    phraseObj.object = object;
	    phraseObj.extra = extra;
	    phraseObj.phrase = phrase;

		return phraseObj;
	};

	/** animates the html to the given Phrase object **/
	var animateToThisPhrase = function (phraseObj){
		// $("#subject").html(phraseObj.subject.value);
		// $("#preposition").html(phraseObj.subject.preposition);
		// $("#action").html(phraseObj.verb+" "+phraseObj.object);
		// $("#goal").html(phraseObj.extra);

		animateLineByIdAndText("#subject",phraseObj.subject.value,Math.random()*0.35+0.075);
		animateLineByIdAndText("#preposition",phraseObj.subject.preposition,Math.random()*0.35+0.075);
		animateLineByIdAndText("#action",phraseObj.verb+" "+phraseObj.object,Math.random()*0.35+0.075);
		animateLineByIdAndText("#goal",phraseObj.extra,Math.random()*0.35+0.075);

	};

	var animateLineByIdAndText = function (lineId,newText,speed){
		var scrnWidth = $(window).width()+100;
		TweenLite.to($(lineId),speed,{css:{x:-scrnWidth}, ease:Power2.easeOut,
			onComplete:function(){
				TweenLite.to($(lineId),0,{css:{x:scrnWidth}});
				$(lineId).html(newText);
				TweenLite.to($(lineId),speed,{css:{x:0}, ease:Power2.easeOut});
			}});
	};

	return {
		init: init
	};
};

$(function(){
	var app = new App();
	app.init();
});

<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
	res.json({
		status: "ok",
		version: "1"
	});
});

router.get('/feedback/getList', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("feedback").select("feedbackId", "msg", "name", "type", "timestamp", "username", "webpage").then(function(obj) {
		var tempArr = obj;
		tempArr.reverse();
		res.json({
			status: "ok",
			feedback: tempArr
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/feedback/get/:id', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("feedback").where({
		feedbackId: req.params.id
	}).select("*").then(function(obj) {
		res.json({
			status: "ok",
			feedback: obj[0]
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/errors/getList', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("errors").select("*").then(function(obj) {
		var tempArr = obj;
		tempArr.reverse();
		res.json({
			status: "ok",
			error: tempArr
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/errors/get/:id', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("errors").where({
		errorId: req.params.id
	}).select("*").then(function(obj) {
		res.json({
			status: "ok",
			error: obj[0]
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.post('/announcements/post/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("announcements").insert({
		days: req.body.days,
		time: req.body.time,
		msg: req.body.msg
	}).then(function() {
		res.json({
			status: "ok"
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error"
		});
	});
});

router.post('/announcements/remove/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("announcements").where({
		announcementId:req.body.id
	}).delete("*").then(function() {
		res.json({
			status: "ok"
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error"
		});
	});
});

router.get('/users/count/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("users").count('id as CNT').then(function(res) {
		res.json({
			status: "ok",
			number: res[0].CNT
		});
	}).catch(function(err) {
		res.json({
			status: "error",
			error: "Unknown database error" + err
		});
	});
});

module.exports = router;
<commit_msg>res == res<commit_after>var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
	res.json({
		status: "ok",
		version: "1"
	});
});

router.get('/feedback/getList', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("feedback").select("feedbackId", "msg", "name", "type", "timestamp", "username", "webpage").then(function(obj) {
		var tempArr = obj;
		tempArr.reverse();
		res.json({
			status: "ok",
			feedback: tempArr
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/feedback/get/:id', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("feedback").where({
		feedbackId: req.params.id
	}).select("*").then(function(obj) {
		res.json({
			status: "ok",
			feedback: obj[0]
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/errors/getList', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("errors").select("*").then(function(obj) {
		var tempArr = obj;
		tempArr.reverse();
		res.json({
			status: "ok",
			error: tempArr
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.get('/errors/get/:id', global.apiCall, global.requireUser, global.getUserRecord, global.requireViewFeedback, function(req, res, next) {
	knex("errors").where({
		errorId: req.params.id
	}).select("*").then(function(obj) {
		res.json({
			status: "ok",
			error: obj[0]
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error."
		});
	});
});

router.post('/announcements/post/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("announcements").insert({
		days: req.body.days,
		time: req.body.time,
		msg: req.body.msg
	}).then(function() {
		res.json({
			status: "ok"
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error"
		});
	});
});

router.post('/announcements/remove/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("announcements").where({
		announcementId:req.body.id
	}).delete("*").then(function() {
		res.json({
			status: "ok"
		});
	}).catch(function() {
		res.json({
			status: "error",
			error: "Unknown database error"
		});
	});
});

router.get('/users/count/', global.apiCall, global.requireUser, global.getUserRecord, global.requireNonZeroLevel, function(req, res, next) {
	knex("users").count('id as CNT').then(function(result) {
		res.json({
			status: "ok",
			number: result[0].CNT
		});
	}).catch(function(err) {
		res.json({
			status: "error",
			error: "Unknown database error"
		});
	});
});

module.exports = router;
<|endoftext|>


<commit_before>var
  KuzzleRole = require('./kuzzleRole'),
  KuzzleProfile = require('./kuzzleProfile'),
  KuzzleUser = require('./kuzzleUser');

/**
 * Kuzzle security constructor
 *
 * @param kuzzle
 * @returns {KuzzleSecurity}
 * @constructor
 */
function KuzzleSecurity(kuzzle) {

  Object.defineProperty(this, 'kuzzle', {
    value: kuzzle
  });

  Object.defineProperty(this, 'buildQueryArgs', {
    value: function (action) {
      return {
        controller: 'security',
        action: action
      };
    }
  });

  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var blacklist = ['roleFactory', 'profileFactory', 'userFactory'];

        return passes && blacklist.indexOf(name) === -1;
      }
    });
  }

  return this;
}

/**
 * Retrieve a single Role using its unique role ID.
 *
 * @param {string} id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getRole = function (id, cb) {
  var
    data = {_id: id},
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getRole', cb);

  self.kuzzle.query(this.buildQueryArgs('getRole'), data, null, function (err, response) {
    if (err) {
      return cb(err);
    }

    cb(null, new KuzzleRole(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on roles according to a filter
 *
 * /!\ There is a small delay between role creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a role that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - this object can contains an array `indexes` with a list of index id, a integer `from` and a integer `size`
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchRoles = function (filters, cb) {
  var
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchRoles', cb);

  self.kuzzle.query(this.buildQueryArgs('searchRoles'), {body: filters}, null, function (error, result) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = result.result.hits.map(function (doc) {
      return new KuzzleRole(self, doc._id, doc._source);
    });

    cb(null, { total: result.result.total, documents: documents });
  });

  return this;
};

/**
 * Create a new role in Kuzzle.
 *
 * Takes an optional argument object with the following property:
 *    - replaceIfExist (boolean, default: false):
 *        If the same role already exists: throw an error if sets to false.
 *        Replace the existing role otherwise
 *
 * @param {string} id - role identifier
 * @param {object} content - a plain javascript object representing the role
 * @param {object} [options] - (optional) arguments
 * @param {responseCallback} [cb] - (optional) Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createRole = function (id, content, options, cb) {
  var
    self = this,
    data = {},
    action = 'createRole';

  if (id && typeof id !== 'string') {
    throw new Error('KuzzleSecurity.createRole: cannot create a role without a role ID');
  }

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  data._id = id;
  data.body = content;

  if (options) {
    action = options.replaceIfExist ? 'createOrReplaceRole' : 'createRole';
  }

  if (cb) {
    self.kuzzle.query(this.buildQueryArgs(action), data, null, function (err, res) {
      var doc;

      if (err) {
        return cb(err);
      }

      doc = new KuzzleRole(self, res.result._id, res.result._source);
      cb(null, doc);
    });
  } else {
    self.kuzzle.query(this.buildQueryArgs(action), data);
  }

  return this;
};

/**
 * Delete role.
 *
 * There is a small delay between role deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a role that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Role id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteRole = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteRole'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteRole'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleRole object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - role id
 * @param {object} content - role content
 * @constructor
 */
KuzzleSecurity.prototype.roleFactory = function(id, content) {
  return new KuzzleRole(this, id, content);
};

/**
 * @param {string} id
 * @param {responseCallback} cb - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getProfile = function (id, cb) {
  var
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getProfile', cb);

  self.kuzzle.query(this.buildQueryArgs('getProfile'), {_id: id}, null, function (error, response) {
    if (error) {
      return cb(error);
    }

    cb(null, new KuzzleProfile(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on profiles according to a filter
 *
 * /!\ There is a small delay between profile creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a profile that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - this object can contains an array `roles` with a list of roles id, a integer `from` and a integer `size`
 * @param {Boolean} [hydrate] - if hydrate is true, profiles will have a list of Role object instead of just a list of role id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchProfiles = function (filters, hydrate, cb) {
  var
    self = this;

  if (!cb && typeof hydrate === 'function') {
    cb = hydrate;
    hydrate = false;
  }

  filters.hydrate = hydrate;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchProfiles', cb);

  self.kuzzle.query(this.buildQueryArgs('searchProfiles'), {body: filters}, null, function (error, response) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = response.result.hits.map(function (doc) {
      return new KuzzleProfile(self, doc._id, doc._source);
    });

    cb(null, { total: response.result.total, documents: documents });
  });

  return this;
};

/**
 * Create a new profile in Kuzzle.
 *
 * Takes an optional argument object with the following property:
 *    - replaceIfExist (boolean, default: false):
 *        If the same role already exists: throw an error if sets to false.
 *        Replace the existing role otherwise
 *
 * @param {string} id - profile identifier
 * @param {object} content
 * @param {object} [options] - (optional) arguments
 * @param {responseCallback} [cb] - (optional) Handles the query response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createProfile = function (id, content, options, cb) {
  var
    self = this,
    data = {},
    action = 'createProfile';

  if (id && typeof id !== 'string') {
    throw new Error('KuzzleSecurity.createProfile: cannot create a profile without a role ID');
  }

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  data._id = id;
  data.body = content;

  if (options) {
    action = options.updateIfExist ? 'createOrReplaceProfile' : 'createProfile';
  }

  if (cb) {
    self.kuzzle.query(this.buildQueryArgs(action), data, null, function (err, res) {
      var doc;

      if (err) {
        return cb(err);
      }

      doc = new KuzzleProfile(self, res.result._id, res.result._source);
      cb(null, doc);
    });
  } else {
    self.kuzzle.query(this.buildQueryArgs(action), data);
  }

  return this;
};

/**
 * Delete profile.
 *
 * There is a small delay between profile deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a profile that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Profile id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteProfile = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteProfile'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteProfile'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleProfile object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - profile id
 * @param {object} content - profile content
 * @constructor
 */
KuzzleSecurity.prototype.profileFactory = function(id, content) {
  return new KuzzleProfile(this, id, content);
};

/**
 * Retrieve a single User using its unique user ID.
 *
 * @param {string} id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getUser = function (id, cb) {
  var
    data = {_id: id},
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getUser', cb);

  self.kuzzle.query(this.buildQueryArgs('getUser'), data, null, function (err, response) {
    if (err) {
      return cb(err);
    }

    cb(null, new KuzzleUser(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on user according to a filter
 *
 * /!\ There is a small delay between user creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a user that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - same filters as documents filters
 * @param {Boolean} hydrate - if hydrate is true, users will have a Profile object instead of just a profile id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchUsers = function (filters, hydrate, cb) {
  var
    self = this;

  if (!cb && typeof hydrate === 'function') {
    cb = hydrate;
    hydrate = false;
  }

  filters.hydrate = hydrate;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchUsers', cb);

  self.kuzzle.query(this.buildQueryArgs('searchUsers'), {body: filters}, null, function (error, response) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = response.result.hits.map(function (doc) {
      return new KuzzleUser(self, doc._id, doc._source);
    });

    cb(null, { total: response.result.total, documents: documents });
  });

  return this;
};

/**
 *
 * @param {object} profile
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createUser = function (profile, cb) {

};

/**
 * Delete user.
 *
 * There is a small delay between user deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a user that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Profile id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteUser = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteUser'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteUser'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleUser object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - user id
 * @param {object} content - user content
 * @constructor
 */
KuzzleSecurity.prototype.userFactory = function(id, content) {
  return new KuzzleUser(this, id, content);
};

module.exports = KuzzleSecurity;<commit_msg>add createUser method<commit_after>var
  KuzzleRole = require('./kuzzleRole'),
  KuzzleProfile = require('./kuzzleProfile'),
  KuzzleUser = require('./kuzzleUser');

/**
 * Kuzzle security constructor
 *
 * @param kuzzle
 * @returns {KuzzleSecurity}
 * @constructor
 */
function KuzzleSecurity(kuzzle) {

  Object.defineProperty(this, 'kuzzle', {
    value: kuzzle
  });

  Object.defineProperty(this, 'buildQueryArgs', {
    value: function (action) {
      return {
        controller: 'security',
        action: action
      };
    }
  });

  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var blacklist = ['roleFactory', 'profileFactory', 'userFactory'];

        return passes && blacklist.indexOf(name) === -1;
      }
    });
  }

  return this;
}

/**
 * Retrieve a single Role using its unique role ID.
 *
 * @param {string} id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getRole = function (id, cb) {
  var
    data = {_id: id},
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getRole', cb);

  self.kuzzle.query(this.buildQueryArgs('getRole'), data, null, function (err, response) {
    if (err) {
      return cb(err);
    }

    cb(null, new KuzzleRole(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on roles according to a filter
 *
 * /!\ There is a small delay between role creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a role that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - this object can contains an array `indexes` with a list of index id, a integer `from` and a integer `size`
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchRoles = function (filters, cb) {
  var
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchRoles', cb);

  self.kuzzle.query(this.buildQueryArgs('searchRoles'), {body: filters}, null, function (error, result) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = result.result.hits.map(function (doc) {
      return new KuzzleRole(self, doc._id, doc._source);
    });

    cb(null, { total: result.result.total, documents: documents });
  });

  return this;
};

/**
 * Create a new role in Kuzzle.
 *
 * Takes an optional argument object with the following property:
 *    - replaceIfExist (boolean, default: false):
 *        If the same role already exists: throw an error if sets to false.
 *        Replace the existing role otherwise
 *
 * @param {string} id - role identifier
 * @param {object} content - a plain javascript object representing the role
 * @param {object} [options] - (optional) arguments
 * @param {responseCallback} [cb] - (optional) Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createRole = function (id, content, options, cb) {
  var
    self = this,
    data = {},
    action = 'createRole';

  if (id && typeof id !== 'string') {
    throw new Error('KuzzleSecurity.createRole: cannot create a role without a role ID');
  }

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  data._id = id;
  data.body = content;

  if (options) {
    action = options.replaceIfExist ? 'createOrReplaceRole' : 'createRole';
  }

  if (cb) {
    self.kuzzle.query(this.buildQueryArgs(action), data, null, function (err, res) {
      var doc;

      if (err) {
        return cb(err);
      }

      doc = new KuzzleRole(self, res.result._id, res.result._source);
      cb(null, doc);
    });
  } else {
    self.kuzzle.query(this.buildQueryArgs(action), data);
  }

  return this;
};

/**
 * Delete role.
 *
 * There is a small delay between role deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a role that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Role id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteRole = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteRole'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteRole'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleRole object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - role id
 * @param {object} content - role content
 * @constructor
 */
KuzzleSecurity.prototype.roleFactory = function(id, content) {
  return new KuzzleRole(this, id, content);
};

/**
 * @param {string} id
 * @param {responseCallback} cb - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getProfile = function (id, cb) {
  var
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getProfile', cb);

  self.kuzzle.query(this.buildQueryArgs('getProfile'), {_id: id}, null, function (error, response) {
    if (error) {
      return cb(error);
    }

    cb(null, new KuzzleProfile(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on profiles according to a filter
 *
 * /!\ There is a small delay between profile creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a profile that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - this object can contains an array `roles` with a list of roles id, a integer `from` and a integer `size`
 * @param {Boolean} [hydrate] - if hydrate is true, profiles will have a list of Role object instead of just a list of role id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchProfiles = function (filters, hydrate, cb) {
  var
    self = this;

  if (!cb && typeof hydrate === 'function') {
    cb = hydrate;
    hydrate = false;
  }

  filters.hydrate = hydrate;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchProfiles', cb);

  self.kuzzle.query(this.buildQueryArgs('searchProfiles'), {body: filters}, null, function (error, response) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = response.result.hits.map(function (doc) {
      return new KuzzleProfile(self, doc._id, doc._source);
    });

    cb(null, { total: response.result.total, documents: documents });
  });

  return this;
};

/**
 * Create a new profile in Kuzzle.
 *
 * Takes an optional argument object with the following property:
 *    - replaceIfExist (boolean, default: false):
 *        If the same profile already exists: throw an error if sets to false.
 *        Replace the existing profile otherwise
 *
 * @param {string} id - profile identifier
 * @param {object} content - attribute `roles` in `content` must only contains an array of role id
 * @param {object} [options] - (optional) arguments
 * @param {responseCallback} [cb] - (optional) Handles the query response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createProfile = function (id, content, options, cb) {
  var
    self = this,
    data = {},
    action = 'createProfile';

  if (id && typeof id !== 'string') {
    throw new Error('KuzzleSecurity.createProfile: cannot create a profile without a profile ID');
  }

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  data._id = id;
  data.body = content;

  if (options) {
    action = options.replaceIfExist ? 'createOrReplaceProfile' : 'createProfile';
  }

  if (cb) {
    self.kuzzle.query(this.buildQueryArgs(action), data, null, function (err, res) {
      var doc;

      if (err) {
        return cb(err);
      }

      doc = new KuzzleProfile(self, res.result._id, res.result._source);
      cb(null, doc);
    });
  } else {
    self.kuzzle.query(this.buildQueryArgs(action), data);
  }

  return this;
};

/**
 * Delete profile.
 *
 * There is a small delay between profile deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a profile that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Profile id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteProfile = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteProfile'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteProfile'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleProfile object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - profile id
 * @param {object} content - profile content
 * @constructor
 */
KuzzleSecurity.prototype.profileFactory = function(id, content) {
  return new KuzzleProfile(this, id, content);
};

/**
 * Retrieve a single User using its unique user ID.
 *
 * @param {string} id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.getUser = function (id, cb) {
  var
    data = {_id: id},
    self = this;

  self.kuzzle.callbackRequired('KuzzleSecurity.getUser', cb);

  self.kuzzle.query(this.buildQueryArgs('getUser'), data, null, function (err, response) {
    if (err) {
      return cb(err);
    }

    cb(null, new KuzzleUser(self, response.result._id, response.result._source));
  });

  return this;
};

/**
 * Executes a search on user according to a filter
 *
 * /!\ There is a small delay between user creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a user that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - same filters as documents filters
 * @param {Boolean} hydrate - if hydrate is true, users will have a Profile object instead of just a profile id
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchUsers = function (filters, hydrate, cb) {
  var
    self = this;

  if (!cb && typeof hydrate === 'function') {
    cb = hydrate;
    hydrate = false;
  }

  filters.hydrate = hydrate;

  self.kuzzle.callbackRequired('KuzzleSecurity.searchUsers', cb);

  self.kuzzle.query(this.buildQueryArgs('searchUsers'), {body: filters}, null, function (error, response) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = response.result.hits.map(function (doc) {
      return new KuzzleUser(self, doc._id, doc._source);
    });

    cb(null, { total: response.result.total, documents: documents });
  });

  return this;
};

/**
 * Create a new user in Kuzzle.
 *
 * Takes an optional argument object with the following property:
 *    - replaceIfExist (boolean, default: false):
 *        If the same user already exists: throw an error if sets to false.
 *        Replace the existing user otherwise
 *
 * @param {string} id - user identifier
 * @param {object} content - attribute `profile` in `content` must only contains the profile id
 * @param {object} [options] - (optional) arguments
 * @param {responseCallback} [cb] - (optional) Handles the query response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createUser = function (id, content, options, cb) {
  var
    self = this,
    data = {},
    action = 'createUser';

  if (id && typeof id !== 'string') {
    throw new Error('KuzzleSecurity.createUser: cannot create a user without a user ID');
  }

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  data._id = id;
  data.body = content;

  if (options) {
    action = options.replaceIfExist ? 'createOrReplaceUser' : 'createUser';
  }

  if (cb) {
    self.kuzzle.query(this.buildQueryArgs(action), data, null, function (err, res) {
      var doc;

      if (err) {
        return cb(err);
      }

      doc = new KuzzleUser(self, res.result._id, res.result._source);
      cb(null, doc);
    });
  } else {
    self.kuzzle.query(this.buildQueryArgs(action), data);
  }

  return this;
};

/**
 * Delete user.
 *
 * There is a small delay between user deletion and their deletion in our advanced search layer,
 * usually a couple of seconds.
 * That means that a user that was just been delete will be returned by this function
 *
 *
 * @param {string} id - Profile id to delete
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {Object} this
 */
KuzzleSecurity.prototype.deleteUser = function (id, cb) {
  var data = {_id: id};

  if (cb) {
    this.kuzzle.query(this.buildQueryArgs('deleteUser'), data, null, function (err, res) {
      if (err) {
        return cb(err);
      }

      cb(null, res.result._id);
    });
  } else {
    this.kuzzle.query(this.buildQueryArgs('deleteUser'), data);
  }

  return this;
};

/**
 * Instantiate a new KuzzleUser object. Workaround to the module.exports limitation, preventing multiple
 * constructors to be exposed without having to use a factory or a composed object.
 *
 * @param {string} id - user id
 * @param {object} content - user content
 * @constructor
 */
KuzzleSecurity.prototype.userFactory = function(id, content) {
  return new KuzzleUser(this, id, content);
};

module.exports = KuzzleSecurity;<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Thermometer node, see https://github.com/phetsims/scenery-phet/issues/43
 *
 * @author Aaron Davis
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var ShadedSphereNode = require( 'SCENERY_PHET/ShadedSphereNode' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Shape = require( 'KITE/Shape' );
  var LinearFunction = require( 'DOT/LinearFunction' );

  // constants
  // center of the bulb is at (0,0), let the client code move to the correct position
  var BULB_CENTER_X = 0;
  var BULB_CENTER_Y = 0;

  /**
   *
   * @param {number} minTemperature
   * @param {number} maxTemperature
   * @param {Property.<number>} temperatureProperty
   * @param {Object} [options]
   * @constructor
   */
  function ThermometerNode( minTemperature, maxTemperature, temperatureProperty, options ) {

    options = _.extend( {
      bulbDiameter: 50,
      tubeWidth: 30,
      tubeHeight: 100,
      lineWidth: 4,
      outlineStroke: 'black',
      tickSpacing: 15,
      majorTickLength: 15,
      minorTickLength: 7.5,
      //TODO why do we need separate options for the sphere and tube? this looks strange, eg in Friction
      fluidSphereSpacing: 2, // the empty space between the fluid sphere and the thermometer outline
      fluidRectSpacing: 2, // the empty space between the fluid in the tube and the thermometer outline

      // leave as null to have a transparent background. If a color is given, then an extra Rectangle is created for the background
      backgroundFill: null,

      // all the default colors are shades of red
      fluidMainColor: '#850e0e', // the main color of the shaded sphere and the left side of the tube gradient
      fluidHighlightColor: '#ff7575', // the highlight color of the shaded sphere and the middle of the tube gradient
      fluidRightSideColor: '#c41515' // the right side of the tube gradient, not used currently
    }, options );

    Node.call( this );

    // Create a shaded sphere to act as the bulb fill
    var fluidSphereDiameter = options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing; //TODO should this be options.lineWidth/2 ?
    var fluidSphere = new ShadedSphereNode( fluidSphereDiameter, {
      centerX: BULB_CENTER_X,
      centerY: BULB_CENTER_Y,
      mainColor: options.fluidMainColor,
      highlightColor: options.fluidHighlightColor,
      highlightXOffset: -0.2,
      highlightYOffset: 0.2,
      rotation: Math.PI / 2
    } );

    // Angles for the outline of the bulb
    var bulbStartAngle = -Math.acos( options.tubeWidth / options.bulbDiameter );
    var bulbEndAngle = Math.PI - bulbStartAngle;

    // Create the outline for the thermometer, starting with the bulb
    var outlineShape = new Shape()
      .arc( BULB_CENTER_X, BULB_CENTER_Y, options.bulbDiameter / 2, bulbStartAngle, bulbEndAngle )
      .verticalLineToRelative( -options.tubeHeight );
    var bulbUpperLeftCorner = outlineShape.getLastPoint();
    outlineShape.arc( bulbUpperLeftCorner.x + options.tubeWidth / 2, bulbUpperLeftCorner.y, options.tubeWidth / 2, Math.PI, 0 )
      .verticalLineToRelative( options.tubeHeight ).close();
    outlineShape.moveToPoint( bulbUpperLeftCorner ).moveToRelative( options.majorTickLength ).horizontalLineToRelative( -options.majorTickLength );
    // tick marks
    for ( var i = 0; i < Math.floor( options.tubeHeight / options.tickSpacing ); i++ ) {
      if ( i % 2 === 0 ) {
        outlineShape.moveToRelative( options.minorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.minorTickLength );
      }
      else {
        outlineShape.moveToRelative( options.majorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.majorTickLength );
      }
    }
    var outline = new Path( outlineShape, {
      stroke: options.outlineStroke,
      lineWidth: options.lineWidth
    } );

    var fluidWidth = options.tubeWidth - options.lineWidth - options.fluidRectSpacing; //TODO should this be options.lineWidth/2 ?
    var clipBulbRadius = ( options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing ) / 2; //TODO should this be options.lineWidth/2 ?
    var clipStartAngle = -Math.acos( ( fluidWidth / 2 ) / clipBulbRadius );
    var clipEndAngle = Math.PI - clipStartAngle;
    var fluidBottomCutoff = ( fluidSphereDiameter / 2 ) * Math.sin( clipEndAngle );
    var rectangleX = -fluidWidth / 2;  //TODO give this a more descriptive name, it tells me nothing

    // Clip area for the fluid in the tube, round at the top
    var fluidClipShape = new Shape().moveTo( rectangleX, fluidBottomCutoff )
      .verticalLineTo( bulbUpperLeftCorner.y );
    var clipUpperLeftCorner = fluidClipShape.getLastPoint();
    fluidClipShape.arc( BULB_CENTER_X, clipUpperLeftCorner.y, fluidWidth / 2, Math.PI, 0 )
      .lineTo( -rectangleX, fluidBottomCutoff ).close();

    // Clip the top of the sphere so it's flat where it connects to the tube
    var sphereClipShape = Shape.rectangle( fluidBottomCutoff, -options.bulbDiameter / 2, options.bulbDiameter, options.bulbDiameter );
    fluidSphere.setClipArea( sphereClipShape );

    // Gradient for fluid in tube
    var fluidRectangleGradient = new LinearGradient( rectangleX, 0, rectangleX + fluidWidth, 0 ).
      addColorStop( 0, options.fluidMainColor ).
      addColorStop( 0.4, options.fluidHighlightColor ).
      addColorStop( 0.5, options.fluidHighlightColor ).
      addColorStop( 1, options.fluidMainColor );

    // Fluid in the tube
    var fluidRectangle = new Rectangle( 0, 0, fluidWidth, 0, {
      fill: fluidRectangleGradient,
      clipArea: fluidClipShape
    } );

    //TODO this is buggy, does not set the background in the sphere
    // Background inside the tube, a rectangle clipped to the tube's shape
    if ( options.backgroundFill ) {
      var backgroundY = bulbUpperLeftCorner.y - options.tubeWidth / 2; //TODO this looks wrong, why is options.tubeWidth used?
      var backgroundHeight = options.tubeHeight + options.tubeWidth / 2; //TODO this looks wrong, why is options.tubeWidth used?
      var backgroundRectangle = new Rectangle( bulbUpperLeftCorner.x, backgroundY, options.tubeWidth, backgroundHeight, {
        fill: options.backgroundFill,
        clipArea: outlineShape
      } );
      this.addChild( backgroundRectangle );
    }

    // Add other nodes after optional background
    this.addChild( fluidRectangle );
    this.addChild( fluidSphere );
    this.addChild( outline );

    // Temperature determines the height of the fluid in the tube
    var height = new Path( fluidClipShape ).height;
    var maxFluidHeight = height - fluidBottomCutoff;
    var temperatureLinearFunction = new LinearFunction( minTemperature, maxTemperature, -fluidBottomCutoff, maxFluidHeight );
    temperatureProperty.link( function( temp ) {
      var fluidHeight = temperatureLinearFunction( temp );
      fluidRectangle.setRect( rectangleX, -fluidHeight, fluidWidth, fluidHeight );
    } );

    this.mutate( options );
  }

  return inherit( Node, ThermometerNode );
} );<commit_msg>#71 fix backgroundFill option so that it fills both the bulb and the tube<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Thermometer node, see https://github.com/phetsims/scenery-phet/issues/43
 *
 * @author Aaron Davis
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var ShadedSphereNode = require( 'SCENERY_PHET/ShadedSphereNode' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Shape = require( 'KITE/Shape' );
  var LinearFunction = require( 'DOT/LinearFunction' );

  // constants
  // center of the bulb is at (0,0), let the client code move to the correct position
  var BULB_CENTER_X = 0;
  var BULB_CENTER_Y = 0;

  /**
   *
   * @param {number} minTemperature
   * @param {number} maxTemperature
   * @param {Property.<number>} temperatureProperty
   * @param {Object} [options]
   * @constructor
   */
  function ThermometerNode( minTemperature, maxTemperature, temperatureProperty, options ) {

    options = _.extend( {
      bulbDiameter: 50,
      tubeWidth: 30,
      tubeHeight: 100,
      lineWidth: 4,
      outlineStroke: 'black',
      tickSpacing: 15,
      majorTickLength: 15,
      minorTickLength: 7.5,
      //TODO why do we need separate options for the sphere and tube? this looks strange, eg in Friction
      fluidSphereSpacing: 2, // the empty space between the fluid sphere and the thermometer outline
      fluidRectSpacing: 2, // the empty space between the fluid in the tube and the thermometer outline

      // leave as null to have a transparent background. If a color is given, then an extra Rectangle is created for the background
      backgroundFill: null,

      // all the default colors are shades of red
      fluidMainColor: '#850e0e', // the main color of the shaded sphere and the left side of the tube gradient
      fluidHighlightColor: '#ff7575', // the highlight color of the shaded sphere and the middle of the tube gradient
      fluidRightSideColor: '#c41515' // the right side of the tube gradient, not used currently
    }, options );

    Node.call( this );

    // Create a shaded sphere to act as the bulb fill
    var fluidSphereDiameter = options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing; //TODO should this be options.lineWidth/2 ?
    var fluidSphere = new ShadedSphereNode( fluidSphereDiameter, {
      centerX: BULB_CENTER_X,
      centerY: BULB_CENTER_Y,
      mainColor: options.fluidMainColor,
      highlightColor: options.fluidHighlightColor,
      highlightXOffset: -0.2,
      highlightYOffset: 0.2,
      rotation: Math.PI / 2
    } );

    // Angles for the outline of the bulb
    var bulbStartAngle = -Math.acos( options.tubeWidth / options.bulbDiameter );
    var bulbEndAngle = Math.PI - bulbStartAngle;

    //TODO this is buggy. The height of the tube is not options.tubeHeight, the rounded top is being added to options.tubeHeight
    // Create the outline for the thermometer, starting with the bulb
    var outlineShape = new Shape()
      .arc( BULB_CENTER_X, BULB_CENTER_Y, options.bulbDiameter / 2, bulbStartAngle, bulbEndAngle )
      .verticalLineToRelative( -options.tubeHeight );
    var bulbUpperLeftCorner = outlineShape.getLastPoint();
    outlineShape.arc( bulbUpperLeftCorner.x + options.tubeWidth / 2, bulbUpperLeftCorner.y, options.tubeWidth / 2, Math.PI, 0 )
      .verticalLineToRelative( options.tubeHeight ).close();
    outlineShape.moveToPoint( bulbUpperLeftCorner ).moveToRelative( options.majorTickLength ).horizontalLineToRelative( -options.majorTickLength );
    // tick marks
    for ( var i = 0; i < Math.floor( options.tubeHeight / options.tickSpacing ); i++ ) {
      if ( i % 2 === 0 ) {
        outlineShape.moveToRelative( options.minorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.minorTickLength );
      }
      else {
        outlineShape.moveToRelative( options.majorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.majorTickLength );
      }
    }
    var outlineNode = new Path( outlineShape, {
      stroke: options.outlineStroke,
      lineWidth: options.lineWidth
    } );

    var fluidWidth = options.tubeWidth - options.lineWidth - options.fluidRectSpacing; //TODO should this be options.lineWidth/2 ?
    var clipBulbRadius = ( options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing ) / 2; //TODO should this be options.lineWidth/2 ?
    var clipStartAngle = -Math.acos( ( fluidWidth / 2 ) / clipBulbRadius );
    var clipEndAngle = Math.PI - clipStartAngle;
    var fluidBottomCutoff = ( fluidSphereDiameter / 2 ) * Math.sin( clipEndAngle );
    var rectangleX = -fluidWidth / 2;  //TODO give this a more descriptive name, it tells me nothing

    // Clip area for the fluid in the tube, round at the top
    var fluidClipShape = new Shape().moveTo( rectangleX, fluidBottomCutoff )
      .verticalLineTo( bulbUpperLeftCorner.y );
    var clipUpperLeftCorner = fluidClipShape.getLastPoint();
    fluidClipShape.arc( BULB_CENTER_X, clipUpperLeftCorner.y, fluidWidth / 2, Math.PI, 0 )
      .lineTo( -rectangleX, fluidBottomCutoff ).close();

    // Clip the top of the sphere so it's flat where it connects to the tube
    var sphereClipShape = Shape.rectangle( fluidBottomCutoff, -options.bulbDiameter / 2, options.bulbDiameter, options.bulbDiameter );
    fluidSphere.setClipArea( sphereClipShape );

    // Gradient for fluid in tube
    var fluidRectangleGradient = new LinearGradient( rectangleX, 0, rectangleX + fluidWidth, 0 ).
      addColorStop( 0, options.fluidMainColor ).
      addColorStop( 0.4, options.fluidHighlightColor ).
      addColorStop( 0.5, options.fluidHighlightColor ).
      addColorStop( 1, options.fluidMainColor );

    // Fluid in the tube
    var fluidRectangle = new Rectangle( 0, 0, fluidWidth, 0, {
      fill: fluidRectangleGradient,
      clipArea: fluidClipShape
    } );

    // Background inside the tube
    if ( options.backgroundFill ) {
      var backgroundRectangle = new Path( outlineShape, { fill: options.backgroundFill } );
      this.addChild( backgroundRectangle );
    }

    // Add other nodes after optional background
    this.addChild( fluidRectangle );
    this.addChild( fluidSphere );
    this.addChild( outlineNode );

    // Temperature determines the height of the fluid in the tube
    var height = new Path( fluidClipShape ).height;
    var maxFluidHeight = height - fluidBottomCutoff;
    var temperatureLinearFunction = new LinearFunction( minTemperature, maxTemperature, -fluidBottomCutoff, maxFluidHeight );
    temperatureProperty.link( function( temp ) {
      var fluidHeight = temperatureLinearFunction( temp );
      fluidRectangle.setRect( rectangleX, -fluidHeight, fluidWidth, fluidHeight );
    } );

    this.mutate( options );
  }

  return inherit( Node, ThermometerNode );
} );<|endoftext|>


<commit_before>var extend = require('util')._extend;
var path = require('path');
var webpack = require('webpack');
var defaults = require('./defaults');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');

var outputPath = path.resolve(__dirname, 'dist');

var defaultsPlugin = new webpack.DefinePlugin(defaults);
var uglifyPlugin = new UglifyJsPlugin({
  sourceMap: true,
  parallel: true
});

var snippetConfig = {
  name: 'snippet',
  entry: {
    'rollbar.snippet': './src/browser/bundles/rollbar.snippet.js'
  },
  output: {
    path: outputPath,
    filename: '[name].js'
  },
  plugins: [defaultsPlugin],
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var pluginConfig = {
  name: 'plugins',
  entry: {
    'jquery': './src/browser/plugins/jquery.js'
  },
  output: {
    path: outputPath + '/plugins/',
    filename: '[name].min.js'
  },
  plugins: [defaultsPlugin],
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var vanillaConfigBase = {
  entry: {
    'rollbar': './src/browser/bundles/rollbar.js'
  },
  output: {
    path: outputPath
  },
  plugins: [defaultsPlugin],
  devtool: 'hidden-source-map',
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var UMDConfigBase = {
  entry: {
    'rollbar.umd': ['./src/browser/bundles/rollbar.js']
  },
  output: {
    path: outputPath,
    library: 'rollbar',
    libraryTarget: 'umd'
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var noConflictConfigBase = extend({}, UMDConfigBase);
noConflictConfigBase.entry = {
  'rollbar.noconflict.umd': ['./src/browser/bundles/rollbar.noconflict.js']
};

var namedAMDConfigBase = extend({}, UMDConfigBase);
namedAMDConfigBase.entry = {
  'rollbar.named-amd': namedAMDConfigBase.entry['rollbar.umd']
};
namedAMDConfigBase.output = extend({}, namedAMDConfigBase.output);
namedAMDConfigBase.output.library = 'rollbar';
namedAMDConfigBase.output.libraryTarget = 'amd';

var config = [snippetConfig, pluginConfig];

function optimizationConfig(minimizer) {
  return {
    minimize: minimizer ? true : false,
    minimizer: minimizer ? [minimizer] : []
  }
}

function addVanillaToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var vanillaConfig = extend({}, vanillaConfigBase);
  vanillaConfig.name = filename;

  var plugins = basePlugins.concat(extraPlugins);
  vanillaConfig.plugins = plugins;

  vanillaConfig.optimization = optimizationConfig(minimizer);

  vanillaConfig.output = extend({filename: filename}, vanillaConfig.output);

  webpackConfig.push(vanillaConfig);
}

function addUMDToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var UMDConfig = extend({}, UMDConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  UMDConfig.plugins = plugins;

  UMDConfig.optimization = optimizationConfig(minimizer);

  UMDConfig.output = extend({filename: filename}, UMDConfig.output);

  webpackConfig.push(UMDConfig);
}

function addNoConflictToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var noConflictConfig = extend({}, noConflictConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  noConflictConfig.plugins = plugins;

  noConflictConfig.optimization = optimizationConfig(minimizer);

  noConflictConfig.output = extend({filename: filename}, noConflictConfig.output);

  webpackConfig.push(noConflictConfig);
}

function addNamedAMDToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var AMDConfig = extend({}, namedAMDConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  AMDConfig.plugins = plugins;

  AMDConfig.optimization = optimizationConfig(minimizer);

  AMDConfig.output = extend({filename: filename}, AMDConfig.output);

  webpackConfig.push(AMDConfig);
}

function generateBuildConfig(name, plugins, minimizer) {
  addVanillaToConfig(config, name, plugins, minimizer);
  addUMDToConfig(config, name, plugins, minimizer);
  addNamedAMDToConfig(config, name, plugins, minimizer);
  addNoConflictToConfig(config, name, plugins, minimizer);
}

generateBuildConfig('[name].js', []);
generateBuildConfig('[name].min.js', [], uglifyPlugin);

module.exports = config;
<commit_msg>fix: workaround webpack/webpack issue #6642, #6677<commit_after>var extend = require('util')._extend;
var path = require('path');
var webpack = require('webpack');
var defaults = require('./defaults');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');

var outputPath = path.resolve(__dirname, 'dist');

var defaultsPlugin = new webpack.DefinePlugin(defaults);
var uglifyPlugin = new UglifyJsPlugin({
  sourceMap: true,
  parallel: true
});

var snippetConfig = {
  name: 'snippet',
  entry: {
    'rollbar.snippet': './src/browser/bundles/rollbar.snippet.js'
  },
  output: {
    path: outputPath,
    filename: '[name].js'
  },
  plugins: [defaultsPlugin],
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var pluginConfig = {
  name: 'plugins',
  entry: {
    'jquery': './src/browser/plugins/jquery.js'
  },
  output: {
    path: outputPath + '/plugins/',
    filename: '[name].min.js'
  },
  plugins: [defaultsPlugin],
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var vanillaConfigBase = {
  entry: {
    'rollbar': './src/browser/bundles/rollbar.js'
  },
  output: {
    path: outputPath
  },
  plugins: [defaultsPlugin],
  devtool: 'hidden-source-map',
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var UMDConfigBase = {
  entry: {
    'rollbar.umd': ['./src/browser/bundles/rollbar.js']
  },
  output: {
    path: outputPath,
    library: 'rollbar',
    libraryTarget: 'umd',
    globalObject: 'this'
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: 'eslint-loader',
        exclude: [/node_modules/, /vendor/],
        options: {
          failOnError: true,
          configFile: path.resolve(__dirname, '.eslintrc')
        }
      }
    ],
  }
};

var noConflictConfigBase = extend({}, UMDConfigBase);
noConflictConfigBase.entry = {
  'rollbar.noconflict.umd': ['./src/browser/bundles/rollbar.noconflict.js']
};

var namedAMDConfigBase = extend({}, UMDConfigBase);
namedAMDConfigBase.entry = {
  'rollbar.named-amd': namedAMDConfigBase.entry['rollbar.umd']
};
namedAMDConfigBase.output = extend({}, namedAMDConfigBase.output);
namedAMDConfigBase.output.library = 'rollbar';
namedAMDConfigBase.output.libraryTarget = 'amd';

var config = [snippetConfig, pluginConfig];

function optimizationConfig(minimizer) {
  return {
    minimize: minimizer ? true : false,
    minimizer: minimizer ? [minimizer] : []
  }
}

function addVanillaToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var vanillaConfig = extend({}, vanillaConfigBase);
  vanillaConfig.name = filename;

  var plugins = basePlugins.concat(extraPlugins);
  vanillaConfig.plugins = plugins;

  vanillaConfig.optimization = optimizationConfig(minimizer);

  vanillaConfig.output = extend({filename: filename}, vanillaConfig.output);

  webpackConfig.push(vanillaConfig);
}

function addUMDToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var UMDConfig = extend({}, UMDConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  UMDConfig.plugins = plugins;

  UMDConfig.optimization = optimizationConfig(minimizer);

  UMDConfig.output = extend({filename: filename}, UMDConfig.output);

  webpackConfig.push(UMDConfig);
}

function addNoConflictToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var noConflictConfig = extend({}, noConflictConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  noConflictConfig.plugins = plugins;

  noConflictConfig.optimization = optimizationConfig(minimizer);

  noConflictConfig.output = extend({filename: filename}, noConflictConfig.output);

  webpackConfig.push(noConflictConfig);
}

function addNamedAMDToConfig(webpackConfig, filename, extraPlugins, minimizer) {
  var basePlugins = [defaultsPlugin];
  var AMDConfig = extend({}, namedAMDConfigBase);

  var plugins = basePlugins.concat(extraPlugins);
  AMDConfig.plugins = plugins;

  AMDConfig.optimization = optimizationConfig(minimizer);

  AMDConfig.output = extend({filename: filename}, AMDConfig.output);

  webpackConfig.push(AMDConfig);
}

function generateBuildConfig(name, plugins, minimizer) {
  addVanillaToConfig(config, name, plugins, minimizer);
  addUMDToConfig(config, name, plugins, minimizer);
  addNamedAMDToConfig(config, name, plugins, minimizer);
  addNoConflictToConfig(config, name, plugins, minimizer);
}

generateBuildConfig('[name].js', []);
generateBuildConfig('[name].min.js', [], uglifyPlugin);

module.exports = config;
<|endoftext|>


<commit_before>// go.components.structures
// ========================
// Reusable, generic structures for Go

(function(exports) {
  var merge = go.utils.merge,
      GoError = go.errors.GoError;

  // Acts as a 'base' for class-like objects which can be extended (with the
  // prototype chain set up automatically)
  var Extendable = function () {};

  Extendable.extend = function() {
    // Backbone has an internal `extend()` function which it assigns to its
    // structures. We need this function, so we arbitrarily choose
    // `Backbone.Model`, since it has the function we are looking for.
    return Backbone.Model.extend.call(this, merge.apply(this, arguments));
  };

  // A class-like object onto which events can be bound and emitted
  var Eventable = Extendable.extend(Backbone.Events);

  // A structure that stores key-value pairs, provides helpful operations for
  // accessing the data, and emits events when items are added or removed.
  // Similar to a Backbone Collection, except the contents are key-value pairs
  // instead of models.
  //
  // Arguments:
  //   - items: the initial objects to be added
  //
  // Events emitted:
  //   - 'add' (key, value) - Emitted when an item is added
  //   - 'remove' (key, value) - Emitted when an item is removed
  var Lookup = Eventable.extend({
    addDefaults: {silent: false},
    removeDefaults: {silent: false},

    constructor: function(items) {
      this._items = {};

      items = items || {};
      for (var k in items) { this.add(k, items[k], {silent: true}); }
    },

    size: function() { return _.size(this._items); },

    keys: function() { return _.keys(this._items); },

    values: function() { return _.values(this._items); },

    items: function() { return _.clone(this._items); },

    each: function(fn, that) { return this.values().forEach(fn, that); },

    map: function(fn, that) { return this.values().map(fn, that); },

    eachItem: function(fn, that) {
      var items = this.items();
      for (var k in items) { fn.call(that, k, items[k]); }
    },

    has: function(k) { return _.has(this._items, k); },

    get: function(key) { return this._items[key]; },

    add: function(key, value, options) {
      options = _(options || {}).defaults(this.addDefaults);

      this._items[key] = value;
      if (!options.silent) { this.trigger('add', key, value); }
      return this;
    },

    remove: function(key, options) {
      options = _(options || {}).defaults(this.removeDefaults);

      var value = this._items[key];
      if (value) {
        delete this._items[key];
        if (!options.silent) { this.trigger('remove', key, value); }
      }
      return value;
    }
  });

  // A self-maintained, 'flattened' collection of all the items of lookups
  // subscribed to it.
  //
  // Arguments:
  //   - lookups: key-lookup pairs for the initial members to subscribe.
  //
  // Events emitted:
  //   - 'add' (key, value) - Emitted when an item is added
  //   - 'remove' (key, value) - Emitted when an item is removed
  var LookupGroup = Lookup.extend({
    constructor: function(lookups) {
      Lookup.prototype.constructor.call(this);
      this.members = new Lookup();

      lookups = lookups || {};
      for (var k in lookups) { this.subscribe(k, lookups[k]); }
    },

    subscribe: function(key, lookup) {
      var items = lookup.items();
      for (var k in items) { this.add(k, items[k]); }

      lookup.on('add', this.add, this);
      lookup.on('remove', this.remove, this);

      this.members.add(key, lookup);
      return this;
    },

    unsubscribe: function(key) {
      var lookup = this.members.get(key);
      lookup.keys().forEach(this.remove, this);

      lookup.off('add', this.add, this);
      lookup.off('remove', this.remove, this);

      this.members.remove(key);
      return lookup;
    }
  });

  // Accepts a collection of models and maintains a corresponding collection of
  // views. New views are created when models are added to the collection, old
  // views are removed when models are removed from the collection. Views can
  // also be looked up by the id of their corresponding models. Useful in
  // situations where views are to be created dynamically (for eg, state views
  // in a state machine diagram).
  //
  // Arguments:
  //   - collection: the collection of models to create views for
  //
  // Events emitted:
  //   - 'add' (id, view) - Emitted when a view is added
  //   - 'remove' (id, view) - Emitted when a view is removed
  var ViewCollection = Lookup.extend({
    addDefaults: {
      silent: false,
      render: true,  // render view after adding
      addModel: false  // add the model if it is not in the collection
    },

    removeDefaults: {
      silent: false,
      render: true,  // render view after adding
      removeModel: false  // remove the model if it is in the collection
    },

    constructor: function(collection) {
      Lookup.prototype.constructor.call(this);

      this.models = collection;
      this.models.each(function(m) { this.add(m, {render: false}); }, this);

      this.models.on(
        'add',
        function(m) { this.add(m, {silent: true}); },
        this);

      this.models.on(
        'remove',
        function(m) { this.remove(m, {silent: true}); },
        this);
    },

    // Override to specialise how the view is created
    create: function(options) { return new Backbone.View(options); },

    add: function(modelOrId, options) {
      options = _(options || {}).defaults(this.addDefaults, {view: {}});

      if (options.addModel) { this.models.add(modelOrId, {silent: true}); }
      var model = this.models.get(modelOrId);

      options.view.model = model;
      var view = this.create(options.view);
      Lookup.prototype.add.call(this, model.id, view, options);

      if (options.render) { view.render(); }

      return view;
    },

    _id: function(modelOrId) {
      return modelOrId.id
        ? modelOrId.id
        : modelOrId.cid
        || modelOrId;
    },

    remove: function(modelOrId, options) {
      var id = this._id(modelOrId);
      options = _(options || {}).defaults(this.removeDefaults);

      if (options.removeModel) { this.models.remove(id); }
      var view = Lookup.prototype.remove.call(this, id, options);
      if (view && typeof view.destroy === 'function') { view.destroy(); }

      return view;
    },

    render: function() {
      this.each(function(v) { v.render(); });
      return this;
    }
  });

  // A self-maintaining, 'flattened' lookup of the views in a group of view
  // collections.
  var ViewCollectionGroup = LookupGroup.extend({
    render: ViewCollection.prototype.render
  });

  // A collection of subviews mapping to an attribute on a view's model.
  //
  // Options:
  // - view: The parent view for this collection of subviews
  // - attr: The attr on the parent view's model which holds the associated
  // collection or model
  // - [type]: The view type to instantiate for each new view.
  var SubviewCollection = ViewCollection.extend({
    // Override to change the default options used on initialisation
    defaults: {type: Backbone.View},

    // Override to change the options passed to each new view
    opts: {},

    constructor: function(options) {
      this.view = options.view;
      this.attr = options.attr;

      _(options).defaults(_(this).result('defaults'));
      this.type = options.type;

      ViewCollection.prototype.constructor.call(this, this._models());
    },

    _models: function() {
      var modelOrCollection = this.view.model.get(this.attr);

      // If we were given a single model instead of a collection, create a
      // singleton collection with the model so we can work with things
      // uniformly
      return modelOrCollection instanceof Backbone.Model
        ? new Backbone.Collection([modelOrCollection])
        : modelOrCollection;
    },

    create: function(options) {
      _(options).defaults(_(this).result('opts'));
      return new this.type(options);
    }
  });

  // A self-maintaining, 'flattened' lookup of subview collections defined by a
  // schema.
  //
  // Arguments:
  // - view: The parent view of the group
  var SubviewCollectionGroup = ViewCollectionGroup.extend({
    // Override to change the subview collection type
    collectionType: SubviewCollection,

    // A list of specs/options, each a subview collection.
    // Override to change the subview collections are created.
    schema: [{attr: 'subviews', type: Backbone.View}],

    // Defaults to apply to each subview spec/option set
    defaults: {},

    constructor: function(view) {
      ViewCollectionGroup.prototype.constructor.call(this);

      this.view = view;
      this.schema = _(this).result('schema');

      // clone each collection option set so we don't modify the schema
      this.schema.forEach(
        function(options) { this.subscribe(_.clone(options)); },
        this);
    },

    subscribe: function(options) {
      _(options).defaults({view: this.view}, _(this).result('defaults'));

      var collectionType = options.collectionType || this.collectionType,
          collection = new collectionType(options);

      return ViewCollectionGroup
        .prototype
        .subscribe
        .call(this, options.attr, collection);
    }
  });

  _.extend(exports, {
    Extendable: Extendable,
    Eventable: Eventable,
    Lookup: Lookup,
    LookupGroup: LookupGroup,
    ViewCollection: ViewCollection,
    ViewCollectionGroup: ViewCollectionGroup,
    SubviewCollection: SubviewCollection,
    SubviewCollectionGroup: SubviewCollectionGroup
  });
})(go.components.structures = {});
<commit_msg>Fixed bug in ViewCollection constructor (silent adding should be applied on initialisation, not for model 'add' events)<commit_after>// go.components.structures
// ========================
// Reusable, generic structures for Go

(function(exports) {
  var merge = go.utils.merge,
      GoError = go.errors.GoError;

  // Acts as a 'base' for class-like objects which can be extended (with the
  // prototype chain set up automatically)
  var Extendable = function () {};

  Extendable.extend = function() {
    // Backbone has an internal `extend()` function which it assigns to its
    // structures. We need this function, so we arbitrarily choose
    // `Backbone.Model`, since it has the function we are looking for.
    return Backbone.Model.extend.call(this, merge.apply(this, arguments));
  };

  // A class-like object onto which events can be bound and emitted
  var Eventable = Extendable.extend(Backbone.Events);

  // A structure that stores key-value pairs, provides helpful operations for
  // accessing the data, and emits events when items are added or removed.
  // Similar to a Backbone Collection, except the contents are key-value pairs
  // instead of models.
  //
  // Arguments:
  //   - items: the initial objects to be added
  //
  // Events emitted:
  //   - 'add' (key, value) - Emitted when an item is added
  //   - 'remove' (key, value) - Emitted when an item is removed
  var Lookup = Eventable.extend({
    addDefaults: {silent: false},
    removeDefaults: {silent: false},

    constructor: function(items) {
      this._items = {};

      items = items || {};
      for (var k in items) { this.add(k, items[k], {silent: true}); }
    },

    size: function() { return _.size(this._items); },

    keys: function() { return _.keys(this._items); },

    values: function() { return _.values(this._items); },

    items: function() { return _.clone(this._items); },

    each: function(fn, that) { return this.values().forEach(fn, that); },

    map: function(fn, that) { return this.values().map(fn, that); },

    eachItem: function(fn, that) {
      var items = this.items();
      for (var k in items) { fn.call(that, k, items[k]); }
    },

    has: function(k) { return _.has(this._items, k); },

    get: function(key) { return this._items[key]; },

    add: function(key, value, options) {
      options = _(options || {}).defaults(this.addDefaults);

      this._items[key] = value;
      if (!options.silent) { this.trigger('add', key, value); }
      return this;
    },

    remove: function(key, options) {
      options = _(options || {}).defaults(this.removeDefaults);

      var value = this._items[key];
      if (value) {
        delete this._items[key];
        if (!options.silent) { this.trigger('remove', key, value); }
      }
      return value;
    }
  });

  // A self-maintained, 'flattened' collection of all the items of lookups
  // subscribed to it.
  //
  // Arguments:
  //   - lookups: key-lookup pairs for the initial members to subscribe.
  //
  // Events emitted:
  //   - 'add' (key, value) - Emitted when an item is added
  //   - 'remove' (key, value) - Emitted when an item is removed
  var LookupGroup = Lookup.extend({
    constructor: function(lookups) {
      Lookup.prototype.constructor.call(this);
      this.members = new Lookup();

      lookups = lookups || {};
      for (var k in lookups) { this.subscribe(k, lookups[k]); }
    },

    subscribe: function(key, lookup) {
      var items = lookup.items();
      for (var k in items) { this.add(k, items[k]); }

      lookup.on('add', this.add, this);
      lookup.on('remove', this.remove, this);

      this.members.add(key, lookup);
      return this;
    },

    unsubscribe: function(key) {
      var lookup = this.members.get(key);
      lookup.keys().forEach(this.remove, this);

      lookup.off('add', this.add, this);
      lookup.off('remove', this.remove, this);

      this.members.remove(key);
      return lookup;
    }
  });

  // Accepts a collection of models and maintains a corresponding collection of
  // views. New views are created when models are added to the collection, old
  // views are removed when models are removed from the collection. Views can
  // also be looked up by the id of their corresponding models. Useful in
  // situations where views are to be created dynamically (for eg, state views
  // in a state machine diagram).
  //
  // Arguments:
  //   - collection: the collection of models to create views for
  //
  // Events emitted:
  //   - 'add' (id, view) - Emitted when a view is added
  //   - 'remove' (id, view) - Emitted when a view is removed
  var ViewCollection = Lookup.extend({
    addDefaults: {
      silent: false,
      render: true,  // render view after adding
      addModel: false  // add the model if it is not in the collection
    },

    removeDefaults: {
      silent: false,
      render: true,  // render view after adding
      removeModel: false  // remove the model if it is in the collection
    },

    constructor: function(collection) {
      Lookup.prototype.constructor.call(this);

      this.models = collection;

      this.models.each(
        function(m) { this.add(m, {render: false, silent: true}); },
        this);

      this.models.on('add', function(m) { this.add(m); }, this);
      this.models.on('remove', function(m) { this.remove(m); }, this);
    },

    // Override to specialise how the view is created
    create: function(options) { return new Backbone.View(options); },

    add: function(modelOrId, options) {
      options = _(options || {}).defaults(this.addDefaults, {view: {}});

      if (options.addModel) { this.models.add(modelOrId, {silent: true}); }
      var model = this.models.get(modelOrId);

      options.view.model = model;
      var view = this.create(options.view);
      Lookup.prototype.add.call(this, model.id, view, options);

      if (options.render) { view.render(); }

      return view;
    },

    _id: function(modelOrId) {
      return modelOrId.id
        ? modelOrId.id
        : modelOrId.cid
        || modelOrId;
    },

    remove: function(modelOrId, options) {
      var id = this._id(modelOrId);
      options = _(options || {}).defaults(this.removeDefaults);

      if (options.removeModel) { this.models.remove(id); }
      var view = Lookup.prototype.remove.call(this, id, options);
      if (view && typeof view.destroy === 'function') { view.destroy(); }

      return view;
    },

    render: function() {
      this.each(function(v) { v.render(); });
      return this;
    }
  });

  // A self-maintaining, 'flattened' lookup of the views in a group of view
  // collections.
  var ViewCollectionGroup = LookupGroup.extend({
    render: ViewCollection.prototype.render
  });

  // A collection of subviews mapping to an attribute on a view's model.
  //
  // Options:
  // - view: The parent view for this collection of subviews
  // - attr: The attr on the parent view's model which holds the associated
  // collection or model
  // - [type]: The view type to instantiate for each new view.
  var SubviewCollection = ViewCollection.extend({
    // Override to change the default options used on initialisation
    defaults: {type: Backbone.View},

    // Override to change the options passed to each new view
    opts: {},

    constructor: function(options) {
      this.view = options.view;
      this.attr = options.attr;

      _(options).defaults(_(this).result('defaults'));
      this.type = options.type;

      ViewCollection.prototype.constructor.call(this, this._models());
    },

    _models: function() {
      var modelOrCollection = this.view.model.get(this.attr);

      // If we were given a single model instead of a collection, create a
      // singleton collection with the model so we can work with things
      // uniformly
      return modelOrCollection instanceof Backbone.Model
        ? new Backbone.Collection([modelOrCollection])
        : modelOrCollection;
    },

    create: function(options) {
      _(options).defaults(_(this).result('opts'));
      return new this.type(options);
    }
  });

  // A self-maintaining, 'flattened' lookup of subview collections defined by a
  // schema.
  //
  // Arguments:
  // - view: The parent view of the group
  var SubviewCollectionGroup = ViewCollectionGroup.extend({
    // Override to change the subview collection type
    collectionType: SubviewCollection,

    // A list of specs/options, each a subview collection.
    // Override to change the subview collections are created.
    schema: [{attr: 'subviews', type: Backbone.View}],

    // Defaults to apply to each subview spec/option set
    defaults: {},

    constructor: function(view) {
      ViewCollectionGroup.prototype.constructor.call(this);

      this.view = view;
      this.schema = _(this).result('schema');

      // clone each collection option set so we don't modify the schema
      this.schema.forEach(
        function(options) { this.subscribe(_.clone(options)); },
        this);
    },

    subscribe: function(options) {
      _(options).defaults({view: this.view}, _(this).result('defaults'));

      var collectionType = options.collectionType || this.collectionType,
          collection = new collectionType(options);

      return ViewCollectionGroup
        .prototype
        .subscribe
        .call(this, options.attr, collection);
    }
  });

  _.extend(exports, {
    Extendable: Extendable,
    Eventable: Eventable,
    Lookup: Lookup,
    LookupGroup: LookupGroup,
    ViewCollection: ViewCollection,
    ViewCollectionGroup: ViewCollectionGroup,
    SubviewCollection: SubviewCollection,
    SubviewCollectionGroup: SubviewCollectionGroup
  });
})(go.components.structures = {});
<|endoftext|>


<commit_before>var NumberOfSecondsBetweenReloadingData = 120;

// ============================

var boolShowMetricAndCelsiusMeasurements = true;
var boolShowPressureInMillibars = true;
var intTemperature = 0;
var intGroundTemperature = 0;
var intHumidity = 0;
var intPressure = 0;
var intPrCh1h = 0;
var intPrCh6h = 0;
var intPrCh12h = 0;
var intPrCh24h = 0;
var intPrCh48h = 0;
var intWindDirection = 0;

var chtPC1h = new chartSet();
var chtPC6h = new chartSet();
var chtPC12h = new chartSet();
var chtPC24h = new chartSet();
var chtPC48h = new chartSet();
var chtTemp = new chartSet();
var chtGroundTemp = new chartSet();
var chtHumidity = new chartSet();
var chtPressure = new chartSet();
var compass = null;

// Load the gauge from the Google Chart API. Details at
// https://developers.google.com/chart/interactive/docs/gallery/gauge
google.charts.load('current', {
    'packages': ['gauge']
});
google.charts.setOnLoadCallback(GoogleCharts_onload);

function GoogleCharts_onload() {
    if (window.console) console.log("Google charts loaded.");

    $.getScript("http://rawgit.com/JoshuaCarroll/Compass-/master/compass.js", Compass_onload);
}

function Compass_onload() {
    loadData(setupDataAndCharts);
}

function loadData(callback) {
    if (window.console) console.log("Calling loadData");
    var jsonPath = "current.php";
    $.getJSON(jsonPath, callback);
}

function setupDataAndCharts(result) {
    if (window.console) console.log("Calling setupDataAndCharts");
    setupData(result);
    setupCharts();
    setTimeout(loadData, NumberOfSecondsBetweenReloadingData * 1000, updateDataAndCharts);
}

function setupData(result) {
    if (window.console) console.log("Calling setupData");
    var obj = result.WeatherObservations.Observation1;
    intTemperature = obj.AMBIENT_TEMPERATURE;
    intGroundTemperature = obj.GROUND_TEMPERATURE;
    intHumidity = obj.HUMIDITY;
    intWindDirection = obj.WIND_DIRECTION;
    intPressure = obj.AIR_PRESSURE;
    intPrCh1h = obj.AIR_PRESSURE - result.WeatherObservations.Observation2.AIR_PRESSURE;
    intPrCh6h = obj.AIR_PRESSURE - result.WeatherObservations.Observation3.AIR_PRESSURE;
    intPrCh12h = obj.AIR_PRESSURE - result.WeatherObservations.Observation4.AIR_PRESSURE;
    intPrCh24h = obj.AIR_PRESSURE - result.WeatherObservations.Observation5.AIR_PRESSURE;
    intPrCh48h = obj.AIR_PRESSURE - result.WeatherObservations.Observation6.AIR_PRESSURE;
    boolShowMetricAndCelsiusMeasurements = result.Settings.showMetricAndCelsiusMeasurements;
    boolShowPressureInMillibars = result.Settings.showPressureInMillibars ;

    $("#rawData").empty();
    var rawList = "
";
    [result.WeatherObservations.Observation1,result.DailyStats].forEach(function (obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                rawList += "
" + property + ": " + obj[property] + "
";
            }
        }
    });
    rawList += "
";
    $("#rawData").append(rawList);
}

function setupCharts() {
    if (window.console) console.log("Calling setupCharts");

    chtTemp.options = new chartOptions();
    if (boolShowMetricAndCelsiusMeasurements) {
        chtTemp.options.yellowFrom = 35;
        chtTemp.options.yellowTo = 42;
        chtTemp.options.redFrom = 42;
        chtTemp.options.redTo = 50;
        chtTemp.options.min = -30;
        chtTemp.options.max = 50;
    }
    drawChart(chtTemp, "chart_temp", "Temperature", intTemperature);

    chtGroundTemp.options = chtTemp.options;
    drawChart(chtGroundTemp, "chart_ground_temp", "Ground temp", intGroundTemperature);

    chtHumidity.options = chartOptions();
    chtHumidity.options.redFrom = 85;
    chtHumidity.options.redTo = 100;
    chtHumidity.options.yellowFrom = 75;
    chtHumidity.options.yellowTo = 85;
    chtHumidity.options.max = 100;
    drawChart(chtHumidity, "chart_hum", "Humidity", intHumidity);

    compass = new Compass("wind_dir");
    compass.animate(intWindDirection);

    chtPressure.options = chartOptions();
    if (boolShowPressureInMillibars) {
        chtPressure.options.redFrom = 960;
        chtPressure.options.redTo = 990;
        chtPressure.options.yellowFrom = 990;
        chtPressure.options.yellowTo = 1015;
        chtPressure.options.greenFrom = 1015;
        chtPressure.options.greenTo = 1060;
        chtPressure.options.max = 1060;
        chtPressure.options.min = 920;
    }
    else {
        chtPressure.options.redFrom = 28;
        chtPressure.options.redTo = 29.2;
        chtPressure.options.yellowFrom = 29.2;
        chtPressure.options.yellowTo = 29.9;
        chtPressure.options.greenFrom = 29.9;
        chtPressure.options.greenTo = 31.3;
        chtPressure.options.max = 31.3;
        chtPressure.options.min = 27.1;
    }
    drawChart(chtPressure, "chart_pressure", "Pressure", intPressure);

    var pressureChOps = chartOptions();
    pressureChOps.height = Math.round(pressureChOps.height * 0.66);
    pressureChOps.width = Math.round(pressureChOps.width * 0.66);
    pressureChOps.max = 10;
    pressureChOps.min = -10

    chtPC1h.options = pressureChOps;
    chtPC6h.options = pressureChOps;
    chtPC12h.options = pressureChOps;
    chtPC24h.options = pressureChOps;
    chtPC48h.options = pressureChOps;

    drawChart(chtPC1h, "chart_pressure_change1h", "Δ 1 hr", intPrCh1h);
    drawChart(chtPC6h, "chart_pressure_change6h", "Δ 6 hr", intPrCh6h);
    drawChart(chtPC12h, "chart_pressure_change12h", "Δ 12 hr", intPrCh12h);
    drawChart(chtPC24h, "chart_pressure_change24h", "Δ 24 hr", intPrCh24h);
    drawChart(chtPC48h, "chart_pressure_change48h", "Δ 48 hr", intPrCh48h);
}

function updateDataAndCharts(result) {
    if (window.console) console.log("Calling updateDataAndCharts");
    setupData(result);
    updateCharts();
    setTimeout(loadData, NumberOfSecondsBetweenReloadingData * 1000, updateDataAndCharts);
}

function updateCharts() {
    if (window.console) console.log("Calling updateCharts");
    chtTemp.update(intTemperature);
    chtGroundTemp.update(intGroundTemperature);
    chtHumidity.update(intHumidity);
    chtPressure.update(intPressure);
    chtPC1h.update(intPrCh1h);
    chtPC6h.update(intPrCh6h);
    chtPC12h.update(intPrCh12h);
    chtPC24h.update(intPrCh24h);
    chtPC48h.update(intPrCh48h);
    compass.animate(intWindDirection);
}

function drawChart(chartSetObj, strChartDiv, strLabel, intValue) {
    if (window.console) console.log("Calling drawChart(" + chartSetObj + ", " + strChartDiv + ", " + strLabel + ", " + intValue + ")");
    if (!chartSetObj.options) {
        chartSetObj.options = chartOptions();
    }

    chartSetObj.data = google.visualization.arrayToDataTable([
        ['Label', 'Value'],
        [strLabel, chartSetObj.options.min]
    ]);

    chartSetObj.chart = new google.visualization.Gauge(document.getElementById(strChartDiv));

    chartSetObj.chart.draw(chartSetObj.data, chartSetObj.options);

    setTimeout(function() {
        chartSetObj.data.setValue(0, 1, intValue);
        chartSetObj.chart.draw(chartSetObj.data, chartSetObj.options);
    }, randomIntFromInterval(500, 2500));
}

function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function chartOptions() {
    var defaultChartOptions = {
        width: 300,
        height: 200,
        redFrom: 108,
        redTo: 115,
        yellowFrom: 96,
        yellowTo: 108,
        minorTicks: 5,
        min: -20,
        max: 115
    };
    return defaultChartOptions;
}

function chartSet() {
    this.chart = {};
    this.data = {};
    this.options = {};
    this.update = function (val) {
        this.data.setValue(0, 1, val);
        this.chart.draw(this.data, this.options);
    }
}<commit_msg>Changed conditional statement to expect a string.<commit_after>var NumberOfSecondsBetweenReloadingData = 120;

// ============================

var boolShowMetricAndCelsiusMeasurements = true;
var boolShowPressureInMillibars = true;
var intTemperature = 0;
var intGroundTemperature = 0;
var intHumidity = 0;
var intPressure = 0;
var intPrCh1h = 0;
var intPrCh6h = 0;
var intPrCh12h = 0;
var intPrCh24h = 0;
var intPrCh48h = 0;
var intWindDirection = 0;

var chtPC1h = new chartSet();
var chtPC6h = new chartSet();
var chtPC12h = new chartSet();
var chtPC24h = new chartSet();
var chtPC48h = new chartSet();
var chtTemp = new chartSet();
var chtGroundTemp = new chartSet();
var chtHumidity = new chartSet();
var chtPressure = new chartSet();
var compass = null;

// Load the gauge from the Google Chart API. Details at
// https://developers.google.com/chart/interactive/docs/gallery/gauge
google.charts.load('current', {
    'packages': ['gauge']
});
google.charts.setOnLoadCallback(GoogleCharts_onload);

function GoogleCharts_onload() {
    if (window.console) console.log("Google charts loaded.");

    $.getScript("http://rawgit.com/JoshuaCarroll/Compass-/master/compass.js", Compass_onload);
}

function Compass_onload() {
    loadData(setupDataAndCharts);
}

function loadData(callback) {
    if (window.console) console.log("Calling loadData");
    var jsonPath = "current.php";
    $.getJSON(jsonPath, callback);
}

function setupDataAndCharts(result) {
    if (window.console) console.log("Calling setupDataAndCharts");
    setupData(result);
    setupCharts();
    setTimeout(loadData, NumberOfSecondsBetweenReloadingData * 1000, updateDataAndCharts);
}

function setupData(result) {
    if (window.console) console.log("Calling setupData");
    var obj = result.WeatherObservations.Observation1;
    intTemperature = obj.AMBIENT_TEMPERATURE;
    intGroundTemperature = obj.GROUND_TEMPERATURE;
    intHumidity = obj.HUMIDITY;
    intWindDirection = obj.WIND_DIRECTION;
    intPressure = obj.AIR_PRESSURE;
    intPrCh1h = obj.AIR_PRESSURE - result.WeatherObservations.Observation2.AIR_PRESSURE;
    intPrCh6h = obj.AIR_PRESSURE - result.WeatherObservations.Observation3.AIR_PRESSURE;
    intPrCh12h = obj.AIR_PRESSURE - result.WeatherObservations.Observation4.AIR_PRESSURE;
    intPrCh24h = obj.AIR_PRESSURE - result.WeatherObservations.Observation5.AIR_PRESSURE;
    intPrCh48h = obj.AIR_PRESSURE - result.WeatherObservations.Observation6.AIR_PRESSURE;
    if (result.Settings.showMetricAndCelsiusMeasurements == "0") { boolShowMetricAndCelsiusMeasurements = false; }
    if (result.Settings.showPressureInMillibars == "0") { boolShowPressureInMillibars = false; }

    $("#rawData").empty();
    var rawList = "
";
    [result.WeatherObservations.Observation1,result.DailyStats].forEach(function (obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                rawList += "
" + property + ": " + obj[property] + "
";
            }
        }
    });
    rawList += "
";
    $("#rawData").append(rawList);
}

function setupCharts() {
    if (window.console) console.log("Calling setupCharts");

    chtTemp.options = new chartOptions();
    if (boolShowMetricAndCelsiusMeasurements) {
        chtTemp.options.yellowFrom = 35;
        chtTemp.options.yellowTo = 42;
        chtTemp.options.redFrom = 42;
        chtTemp.options.redTo = 50;
        chtTemp.options.min = -30;
        chtTemp.options.max = 50;
    }
    drawChart(chtTemp, "chart_temp", "Temperature", intTemperature);

    chtGroundTemp.options = chtTemp.options;
    drawChart(chtGroundTemp, "chart_ground_temp", "Ground temp", intGroundTemperature);

    chtHumidity.options = chartOptions();
    chtHumidity.options.redFrom = 85;
    chtHumidity.options.redTo = 100;
    chtHumidity.options.yellowFrom = 75;
    chtHumidity.options.yellowTo = 85;
    chtHumidity.options.max = 100;
    drawChart(chtHumidity, "chart_hum", "Humidity", intHumidity);

    compass = new Compass("wind_dir");
    compass.animate(intWindDirection);

    chtPressure.options = chartOptions();
    if (boolShowPressureInMillibars) {
        chtPressure.options.redFrom = 960;
        chtPressure.options.redTo = 990;
        chtPressure.options.yellowFrom = 990;
        chtPressure.options.yellowTo = 1015;
        chtPressure.options.greenFrom = 1015;
        chtPressure.options.greenTo = 1060;
        chtPressure.options.max = 1060;
        chtPressure.options.min = 920;
    }
    else {
        chtPressure.options.redFrom = 28;
        chtPressure.options.redTo = 29.2;
        chtPressure.options.yellowFrom = 29.2;
        chtPressure.options.yellowTo = 29.9;
        chtPressure.options.greenFrom = 29.9;
        chtPressure.options.greenTo = 31.3;
        chtPressure.options.max = 31.3;
        chtPressure.options.min = 27.1;
    }
    drawChart(chtPressure, "chart_pressure", "Pressure", intPressure);

    var pressureChOps = chartOptions();
    pressureChOps.height = Math.round(pressureChOps.height * 0.66);
    pressureChOps.width = Math.round(pressureChOps.width * 0.66);
    pressureChOps.max = 10;
    pressureChOps.min = -10

    chtPC1h.options = pressureChOps;
    chtPC6h.options = pressureChOps;
    chtPC12h.options = pressureChOps;
    chtPC24h.options = pressureChOps;
    chtPC48h.options = pressureChOps;

    drawChart(chtPC1h, "chart_pressure_change1h", "Δ 1 hr", intPrCh1h);
    drawChart(chtPC6h, "chart_pressure_change6h", "Δ 6 hr", intPrCh6h);
    drawChart(chtPC12h, "chart_pressure_change12h", "Δ 12 hr", intPrCh12h);
    drawChart(chtPC24h, "chart_pressure_change24h", "Δ 24 hr", intPrCh24h);
    drawChart(chtPC48h, "chart_pressure_change48h", "Δ 48 hr", intPrCh48h);
}

function updateDataAndCharts(result) {
    if (window.console) console.log("Calling updateDataAndCharts");
    setupData(result);
    updateCharts();
    setTimeout(loadData, NumberOfSecondsBetweenReloadingData * 1000, updateDataAndCharts);
}

function updateCharts() {
    if (window.console) console.log("Calling updateCharts");
    chtTemp.update(intTemperature);
    chtGroundTemp.update(intGroundTemperature);
    chtHumidity.update(intHumidity);
    chtPressure.update(intPressure);
    chtPC1h.update(intPrCh1h);
    chtPC6h.update(intPrCh6h);
    chtPC12h.update(intPrCh12h);
    chtPC24h.update(intPrCh24h);
    chtPC48h.update(intPrCh48h);
    compass.animate(intWindDirection);
}

function drawChart(chartSetObj, strChartDiv, strLabel, intValue) {
    if (window.console) console.log("Calling drawChart(" + chartSetObj + ", " + strChartDiv + ", " + strLabel + ", " + intValue + ")");
    if (!chartSetObj.options) {
        chartSetObj.options = chartOptions();
    }

    chartSetObj.data = google.visualization.arrayToDataTable([
        ['Label', 'Value'],
        [strLabel, chartSetObj.options.min]
    ]);

    chartSetObj.chart = new google.visualization.Gauge(document.getElementById(strChartDiv));

    chartSetObj.chart.draw(chartSetObj.data, chartSetObj.options);

    setTimeout(function() {
        chartSetObj.data.setValue(0, 1, intValue);
        chartSetObj.chart.draw(chartSetObj.data, chartSetObj.options);
    }, randomIntFromInterval(500, 2500));
}

function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function chartOptions() {
    var defaultChartOptions = {
        width: 300,
        height: 200,
        redFrom: 108,
        redTo: 115,
        yellowFrom: 96,
        yellowTo: 108,
        minorTicks: 5,
        min: -20,
        max: 115
    };
    return defaultChartOptions;
}

function chartSet() {
    this.chart = {};
    this.data = {};
    this.options = {};
    this.update = function (val) {
        this.data.setValue(0, 1, val);
        this.chart.draw(this.data, this.options);
    }
}<|endoftext|>