<commit_before>var _ = require('underscore');
var fs = require('fs');
var path = require('path');

exports.docsFilename = function (name) {
	return name.replace(/ /g, "_")
		.replace(/./g, ".")
		.replace(/>/g, "_gt_")
		.replace(/\*/g, "_star_")
		.replace(/\//g, "|") + '.html';
}
exports.name = function (file) {
	return file.substring(0, file.lastIndexOf('.'));
}

exports.handlebarsHelpers = function (helpers, Handlebars) {
	if (helpers && Handlebars) {
		_.each(helpers, function (helper, name) {
			Handlebars.registerHelper(name, helper);
		});
	}
	return Handlebars;
}

exports.handlebarsPartials = function (folder, Handlebars) {
	var files = fs.readdirSync(folder);
	files.forEach(function (filename) {
		var template = fs.readFileSync(path.join(folder, filename));
		Handlebars.registerPartial(filename, template.toString());
	});
	return Handlebars;
}

exports.activateItems = function (root, name) {
	var traverse = function (children) {
		var anyActive = false;
		_.each(children, function (child) {
			var active = false;
			if (child.children) {
				active = traverse(child.children);
			}
			child.active = active || child.name == name;
			if (child.active) {
				anyActive = true;
			}
		});

		return anyActive;
	}

	traverse(root.children);

	return root;
}

exports.menuTree = function (data, root) {
	var copies = _.map(data, function (value) {
		return _.extend({
			active: false
		}, value);
	});

	_.each(copies, function (current, index, arr) {
		var parent = _.find(arr, function (value) {
			return value.name === current.parent;
		});

		if (parent) {
			parent.children = parent.children || [];
			parent.children.push(current);
			parent.children = _.sortBy(parent.children, function (child) {
				return child.order || child.name;
			});
		}
	});

	if (root) {
		return _.find(copies, function (current) {
			return current.name == root;
		});
	}

	return copies;
}

exports.replaceLinks = function (text, data) {
	var replacer = function (match, content) {
		var parts = content.match(/^(\S+)\s(.*)/);
		var link = parts ? parts[1].replace('::', '.prototype.') : content;
		var description = parts && parts[2] ? parts[2] : link;

		if (data[link]) {
			return '' + description + '';
		}

		return match;
	};

	return text.replace(/[\[](.*?)\]/g, replacer);
}

exports.helpers = {
	activePage: function(current, expected) {
		return current == this.page ? 'active' : '';
	},

	url: function(url) {
		return path.join(this.root, url);
	},

	makeHref: function (name) {
		return exports.docsFilename(name);
	}
};
<commit_msg>Fixed an issue where links like [a::b] weren't getting linkified.<commit_after>var _ = require('underscore');
var fs = require('fs');
var path = require('path');

exports.docsFilename = function (name) {
	return name.replace(/ /g, "_")
		.replace(/./g, ".")
		.replace(/>/g, "_gt_")
		.replace(/\*/g, "_star_")
		.replace(/\//g, "|") + '.html';
}
exports.name = function (file) {
	return file.substring(0, file.lastIndexOf('.'));
}

exports.handlebarsHelpers = function (helpers, Handlebars) {
	if (helpers && Handlebars) {
		_.each(helpers, function (helper, name) {
			Handlebars.registerHelper(name, helper);
		});
	}
	return Handlebars;
}

exports.handlebarsPartials = function (folder, Handlebars) {
	var files = fs.readdirSync(folder);
	files.forEach(function (filename) {
		var template = fs.readFileSync(path.join(folder, filename));
		Handlebars.registerPartial(filename, template.toString());
	});
	return Handlebars;
}

exports.activateItems = function (root, name) {
	var traverse = function (children) {
		var anyActive = false;
		_.each(children, function (child) {
			var active = false;
			if (child.children) {
				active = traverse(child.children);
			}
			child.active = active || child.name == name;
			if (child.active) {
				anyActive = true;
			}
		});

		return anyActive;
	}

	traverse(root.children);

	return root;
}

exports.menuTree = function (data, root) {
	var copies = _.map(data, function (value) {
		return _.extend({
			active: false
		}, value);
	});

	_.each(copies, function (current, index, arr) {
		var parent = _.find(arr, function (value) {
			return value.name === current.parent;
		});

		if (parent) {
			parent.children = parent.children || [];
			parent.children.push(current);
			parent.children = _.sortBy(parent.children, function (child) {
				return child.order || child.name;
			});
		}
	});

	if (root) {
		return _.find(copies, function (current) {
			return current.name == root;
		});
	}

	return copies;
}

exports.replaceLinks = function (text, data) {
	var replacer = function (match, content) {
		content = content.replace('::', '.prototype.');
		var parts = content.match(/^(\S+)\s(.*)/);
		var link = parts ? parts[1] : content;
		var description = parts && parts[2] ? parts[2] : link;

		if (data[link]) {
			return '' + description + '';
		}

		return match;
	};

	return text.replace(/[\[](.*?)\]/g, replacer);
}

exports.helpers = {
	activePage: function(current, expected) {
		return current == this.page ? 'active' : '';
	},

	url: function(url) {
		return path.join(this.root, url);
	},

	makeHref: function (name) {
		return exports.docsFilename(name);
	}
};
<|endoftext|>


<commit_before>/* jshint node: true */
module.exports = {
    clean: require("./clean"),
    compress: require("./compress"),
    concat: require("./concat"),
    copy: require("./copy"),
    cssmin: require("./cssmin"),
    jshint: require("./jshint"),
    shell: require("./shell"),
    static: require("./static"),
    stripcode: require("./stripcode"),
    uglify: require("./uglify"),
    version: require("./version"),
    watch: require("./watch"),
};

<commit_msg>chore($grunt/configs): Ensure banner task is loaded<commit_after>/* jshint node: true */
module.exports = {
    banner: require("./banner"),
    clean: require("./clean"),
    compress: require("./compress"),
    concat: require("./concat"),
    copy: require("./copy"),
    cssmin: require("./cssmin"),
    jshint: require("./jshint"),
    shell: require("./shell"),
    static: require("./static"),
    stripcode: require("./stripcode"),
    uglify: require("./uglify"),
    version: require("./version"),
    watch: require("./watch"),
};

<|endoftext|>


<commit_before>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Kadira.connected) {
      //only add kadiraInfo if it is connected
      var kadiraInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        kadiraInfo.trace = Kadira.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        //use JSON stringify to save the CPU
        var startData = { userId: this.userId, params: JSON.stringify(msg.params) };
        Kadira.tracer.event(kadiraInfo.trace, 'start', startData);
        var waitEventId = Kadira.tracer.event(kadiraInfo.trace, 'wait', {waitOn: waitOnMessages}, kadiraInfo);
        msg._waitEventId = waitEventId;
        msg.__kadiraInfo = kadiraInfo;

        if(msg.msg == 'sub') {
          // start tracking inside processMessage allows us to indicate
          // wait time as well
          Kadira.models.pubsub._trackSub(this, msg);
        }
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    var self = this;
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return Kadira.env.kadiraInfo.withValue(kadiraInfo, function () {
      return originalMethodHandler.call(self, msg, unblock);
    });
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    var self = this;
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return Kadira.env.kadiraInfo.withValue(kadiraInfo, function () {
      return orginalSubHandler.call(self, msg, unblock);
    });
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var kadiraInfo = Kadira._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __kadiraInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(kadiraInfo && kadiraInfo.currentError) {
          error = kadiraInfo.currentError;
        }

        error = _.pick(error, ['message', 'stack']);

        Kadira.tracer.endLastEvent(kadiraInfo.trace);
        Kadira.tracer.event(kadiraInfo.trace, 'error', {error: error});
      } else {
        var isForced = Kadira.tracer.endLastEvent(kadiraInfo.trace);
        if (isForced) {
          console.warn('Kadira endevent forced complete', JSON.stringify(kadiraInfo.trace.events));
        };
        Kadira.tracer.event(kadiraInfo.trace, 'complete');
      }

      if(kadiraInfo) {
        //processing the message
        var trace = Kadira.tracer.buildTrace(kadiraInfo.trace);
        Kadira.models.methods.processMethod(trace);

        // error may or may not exist and error tracking can be disabled
        if(error && Kadira.options.enableErrorTracking) {
          Kadira.models.error.trackError(error, trace);
        }

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Kadira._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Kadira.env.currentSub.get();

      if(subscription) {
        var session = this;
        Kadira.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

// wrap existing method handlers for capturing errors
_.each(Meteor.default_server.method_handlers, function(handler, name) {
  wrapMethodHanderForErrors(name, handler, Meteor.default_server.method_handlers);
});

// wrap future method handlers for capturing errors
var originalMeteorMethods = Meteor.methods;
Meteor.methods = function(methodMap) {
  _.each(methodMap, function(handler, name) {
    wrapMethodHanderForErrors(name, handler, methodMap);
  });
  originalMeteorMethods(methodMap);
};

function wrapMethodHanderForErrors(name, originalHandler, methodMap) {
  methodMap[name] = function() {
    try{
      return originalHandler.apply(this, arguments);
    } catch(ex) {

      if(Kadira._getInfo()) {
        // wrap error stack so Meteor._debug can identify and ignore it
        ex.stack = {stack: ex.stack, source: 'method'};
        Kadira._getInfo().currentError = ex;
      }
      throw ex;
    }
  }
}
<commit_msg>Fix method error stack<commit_after>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Kadira.connected) {
      //only add kadiraInfo if it is connected
      var kadiraInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        kadiraInfo.trace = Kadira.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        //use JSON stringify to save the CPU
        var startData = { userId: this.userId, params: JSON.stringify(msg.params) };
        Kadira.tracer.event(kadiraInfo.trace, 'start', startData);
        var waitEventId = Kadira.tracer.event(kadiraInfo.trace, 'wait', {waitOn: waitOnMessages}, kadiraInfo);
        msg._waitEventId = waitEventId;
        msg.__kadiraInfo = kadiraInfo;

        if(msg.msg == 'sub') {
          // start tracking inside processMessage allows us to indicate
          // wait time as well
          Kadira.models.pubsub._trackSub(this, msg);
        }
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    var self = this;
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return Kadira.env.kadiraInfo.withValue(kadiraInfo, function () {
      return originalMethodHandler.call(self, msg, unblock);
    });
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    var self = this;
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return Kadira.env.kadiraInfo.withValue(kadiraInfo, function () {
      return orginalSubHandler.call(self, msg, unblock);
    });
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var kadiraInfo = Kadira._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __kadiraInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(kadiraInfo && kadiraInfo.currentError) {
          // the error stack is wrapped so Meteor._debug can identify
          // this as a method error.
          error = {
            message: kadiraInfo.currentError.message,
            stack: kadiraInfo.currentError.stack.stack
          };
        }

        error = _.pick(error, ['message', 'stack']);

        Kadira.tracer.endLastEvent(kadiraInfo.trace);
        Kadira.tracer.event(kadiraInfo.trace, 'error', {error: error});
      } else {
        var isForced = Kadira.tracer.endLastEvent(kadiraInfo.trace);
        if (isForced) {
          console.warn('Kadira endevent forced complete', JSON.stringify(kadiraInfo.trace.events));
        };
        Kadira.tracer.event(kadiraInfo.trace, 'complete');
      }

      if(kadiraInfo) {
        //processing the message
        var trace = Kadira.tracer.buildTrace(kadiraInfo.trace);
        Kadira.models.methods.processMethod(trace);

        // error may or may not exist and error tracking can be disabled
        if(error && Kadira.options.enableErrorTracking) {
          Kadira.models.error.trackError(error, trace);
        }

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Kadira._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Kadira.env.currentSub.get();

      if(subscription) {
        var session = this;
        Kadira.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

// wrap existing method handlers for capturing errors
_.each(Meteor.default_server.method_handlers, function(handler, name) {
  wrapMethodHanderForErrors(name, handler, Meteor.default_server.method_handlers);
});

// wrap future method handlers for capturing errors
var originalMeteorMethods = Meteor.methods;
Meteor.methods = function(methodMap) {
  _.each(methodMap, function(handler, name) {
    wrapMethodHanderForErrors(name, handler, methodMap);
  });
  originalMeteorMethods(methodMap);
};

function wrapMethodHanderForErrors(name, originalHandler, methodMap) {
  methodMap[name] = function() {
    try{
      return originalHandler.apply(this, arguments);
    } catch(ex) {

      if(Kadira._getInfo()) {
        // wrap error stack so Meteor._debug can identify and ignore it
        ex.stack = {stack: ex.stack, source: 'method'};
        Kadira._getInfo().currentError = ex;
      }
      throw ex;
    }
  }
}
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2011 Cello Software, LLC.
 * All rights reserved.
 * Available via the new BSD License.
 */
/*jshint
	bitwise: false, curly: true, eqeqeq: true, forin: true, immed: true, indent: 4, maxlen: 100,
	newcap: true, noarg: true, noempty: true, onevar: true, passfail: false, strict: true,
	undef: true, white: true
*/
/*global define: false, require: false */

define(['compose', 'promise'], function (compose, promise) {
	'use strict';
	return compose(function Singleton(model) {
		this.model = model;
	}, {
		_released: true,

		resolve: function (args) {
			var lifestyle = this;

			// reuse a single instance
			return this._instance || promise.when(this.model.construct(args), function (instance) {
				lifestyle._instance = instance;
				lifestyle._released = false;
				return instance;
			});
		},

		release: function (instance) {
			// do nothing
			this._released = true;
		},

		destroy: function () {
			var model = this.model,
				instance = this._instance;

			if (!this._released) {
				model.release(instance);
			}
			model.deconstruct(instance);
		}
	});
});
<commit_msg>fix problem where singleton was creating multiple instances<commit_after>/**
 * @license Copyright (c) 2011 Cello Software, LLC.
 * All rights reserved.
 * Available via the new BSD License.
 */
/*jshint
	bitwise: false, curly: true, eqeqeq: true, forin: true, immed: true, indent: 4, maxlen: 100,
	newcap: true, noarg: true, noempty: true, onevar: true, passfail: false, strict: true,
	undef: true, white: true
*/
/*global define: false, require: false */

define(['compose', 'promise'], function (compose, promise) {
	'use strict';
	return compose(function Singleton(model) {
		this.model = model;
	}, {
		_released: true,

		resolve: function (args) {
			var lStyle = this,
				inst = lStyle._instance = lStyle._instance || lStyle.model.construct(args);

			lStyle._released = false;

			// reuse a single instance
			return promise.when(inst, function (instance) {
				lStyle._instance = instance;
				return instance;
			});
		},

		release: function (instance) {
			// do nothing
			this._released = true;
		},

		destroy: function () {
			var model = this.model,
				instance = this._instance;

			if (!this._released) {
				model.release(instance);
			}
			model.deconstruct(instance);
		}
	});
});
<|endoftext|>


<commit_before>$(function() {

    var app = {
        init: function() {
            this.user = {};
            $('.menu-crud').addClass('hidden');
            $('.menu-user').addClass('hidden');
            $('.menu-loading').removeClass('hidden');
            $('.menu-user').addClass('hidden');
            $('.btn-login').addClass('hidden');

            $('.btn-login').attr('href', '/api/login?url=/');
            $('.btn-logout').attr('href','/api/logout?url=/');

            this.router = new Router();
            this.setEventListeners();
            this.getUser();

            Backbone.history.start({pushState: true});
        },

        setEventListeners: function() {
            var self = this;
            $('.menu-crud .item a').click(function(ev) {
                var $el = $(ev.target).closest('.item');

                $('.menu-crud .item').removeClass('active');
                $el.addClass("active");

                if ($el.hasClass('menu-list')) {
                    self.router.navigate('list', {trigger: true});
                }

                if ($el.hasClass('menu-create')) {
                    self.router.navigate('new', {trigger: true});
                }
            });

            $('.navbar-brand').click(function() {
                self.router.navigate('', {trigger: true});
				$('.menu-create').removeClass('active');
				$('.menu-list').removeClass('active');
            });
        },

        getUser: function() {
            var self = this;
            $.ajax({
                method: 'GET',
                url: '/api/users/me',
                success: function(me) {
                    // user is already signed in
                    console.log(me);
                    self.user = me;
                    self.showLogout();
                },

                error: function(err) {
                    console.log('you have not authenticated');
                    self.showLogin();
                }
            });
        },
        showLogin: function() {
           $('.menu-loading').addClass('hidden');
           $('.menu-user').addClass('hidden');
           $('.btn-login').removeClass('hidden');
        },
        showLogout: function() {
           $('.menu-crud').removeClass('hidden');
           $('.user-email').text(this.user.email);
           $('.menu-loading').addClass('hidden');
           $('.btn-login').addClass('hidden');
           $('.menu-user').removeClass('hidden');
        },
        showHome: function() {
            $('.app-content').html('');
        },
        showList: function() {
            var $listTemplate = getTemplate('tpl-thesis-list');
            $('.app-content').html($listTemplate);
            this.loadAllThesis();
        },
        showForm: function(object) {
            if (!object) {
                object = {};
            }
            var self = this;
            var $formTemplate = getTemplate('tpl-thesis-form', object);
            $('.app-content').html($formTemplate);

            $('form').unbind('submit').submit(function(ev) {
				$.get('/api/thesis', self.save);

                return false;
            });
        },
		showView: function(object) {
			$('.app-content').html(getTemplate('tpl-thesis-view-item', object));
			if (typeof(FB) !== 'undefined') {
				FB.XFBML.parse();
			}else{
				fb(document, 'script', 'facebook-jssdk');
			}
        },
        loadAllThesis: function() {
            $.get('/api/thesis', this.displayLoadedList);
        },
        displayLoadedList: function(list) {
            console.log('response', list);
            //  use tpl-thesis-list-item to render each loaded list and attach it
			for (var i = 0; i < list.length; i++){
				$('.thesis-list').append(getTemplate('tpl-thesis-list-item', list[i]));
			}

			$('.table tbody tr').click(function (event) {
				app.router.navigate('thesis-' + $(this).attr('data-id'), {trigger: true});
				$('.menu-create').removeClass('active');
				$('.menu-list').removeClass('active');
			});
        },
        save: function(allThesis) {
            var self = this;

			var thesisObject = {};
			var inputs = $('form').serializeArray();
			for (var i = 0; i < inputs.length; i++) {
				thesisObject[inputs[i].name] = inputs[i].value;
			}

			if (thesisObject.Title.length != 0){
				var sameThesis = false;

				for (var i = 0; i < allThesis.length; i++){
					if (thesisObject.Title == allThesis[i].Title){
						sameThesis = true;
						break;
					}
				}

				if (sameThesis){
					alert("Thesis with this title was already created.");
				}else{
					$.post('/api/thesis', thesisObject);
					alert("Thesis \"" + thesisObject.Title + "\", saved.");
				}
			}else{
				alert("Thesis title entry is blank.");
			}
        }

    };

    function getTemplate(template_id, context) {
        var template, $template, markup;
        template = $('#' + template_id);
        $template = Handlebars.compile(template.html());
        markup = $template(context);
        return markup;

    }

    var Router = Backbone.Router.extend({
        routes: {
            '': 'onHome',
            'thesis-:id': 'onView',
            'new': 'onCreate',
            'edit': 'onEdit',
            'list': 'onList'
        },

       onHome: function() {
            app.showHome();
       },

       onView: function(id) {
           console.log('thesis id', id);
		   $.get('api/thesis/' + id, app.showView);
       },

       onCreate: function() {
            app.showForm();
       },

       onEdit: function() {

       },

       onList: function() {
            app.showList();
       }

    });

	function fb(d, s, id) {
		var js, fjs = d.getElementsByTagName(s)[0];
		if (d.getElementById(id)) return;
		js = d.createElement(s); js.id = id;
		js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=219985108164393";
		fjs.parentNode.insertBefore(js, fjs);
	}

    app.init();
});
<commit_msg>Update app.js<commit_after>$(function() {

    var app = {
        init: function() {
            this.user = {};
            $('.menu-crud').addClass('hidden');
            $('.menu-user').addClass('hidden');
            $('.menu-loading').removeClass('hidden');
            $('.menu-user').addClass('hidden');
            $('.btn-login').addClass('hidden');

            $('.btn-login').attr('href', '/api/login?url=/');
            $('.btn-logout').attr('href','/api/logout?url=/');

            this.router = new Router();
            this.setEventListeners();
            this.getUser();

            Backbone.history.start({pushState: true});
        },

        setEventListeners: function() {
            var self = this;
            $('.menu-crud .item a').click(function(ev) {
                var $el = $(ev.target).closest('.item');

                $('.menu-crud .item').removeClass('active');
                $el.addClass("active");

                if ($el.hasClass('menu-list')) {
                    self.router.navigate('list', {trigger: true});
                }

                if ($el.hasClass('menu-create')) {
                    self.router.navigate('new', {trigger: true});
                }

            });

            $('.navbar-brand').click(function() {
                self.router.navigate('', {trigger: true});
            });
            $('.form-search').unbind('submit').submit(function(ev) {
                self.router.navigate('search?=' + $('.search-input').val(), {trigger: true});
                return false;
            });
        },

        getUser: function() {
            var self = this;
            $.ajax({
                method: 'GET',
                url: '/api/users/me',
                success: function(me) {
                    // user is already signed in
                    console.log(me);
                    self.user = me;
                    self.showLogout();
                },

                error: function(err) {
                    console.log('you have not authenticated');
                    self.showLogin();
                }
            });
        },
        showLogin: function() {
           $('.menu-loading').addClass('hidden');
           $('.menu-user').addClass('hidden');
           $('.btn-login').removeClass('hidden');
        },
        showLogout: function() {
           $('.menu-crud').removeClass('hidden');
           $('.user-email').text(this.user.email);
           $('.menu-loading').addClass('hidden');
           $('.btn-login').addClass('hidden');
           $('.menu-user').removeClass('hidden');
        },
        showHome: function() {
            $('.app-content').html('');
        },
        showList: function() {
            var $listTemplate = getTemplate('tpl-thesis-list');
            $('.app-content').html($listTemplate);
        },
        search: function(query, callback) {
            $.get('/api/search/?q=' + query, {
                returned_fields: JSON.stringify(['Title', 'Year'])
            }, function(list) {
                callback(list);
            });
        },
        getThesisByID: function(id, callback) {
            var object = {};
            $.get('/api/thesis/' + id, function(item) {
                callback(item);
            });
        },
        showThesis: function(thesis) {
            var self = this;
            var $viewTemplate = getTemplate('tpl-thesis-detail', thesis);
            $('.app-content').html($viewTemplate);
        },
        showForm: function(object) {
            var self = this;
            if (!object) {
                object = {};
            }
            var $formTemplate = getTemplate('tpl-thesis-form', object);
            $('.app-content').html($formTemplate);
            $('form').unbind('submit').submit(function(ev) {
                var thesisObject = {};
                var inputs = $('form').serializeArray();
                for (var i = 0; i < inputs.length; i++) {
                    thesisObject[inputs[i].name] = inputs[i].value;
                }
                self.save(thesisObject);
                return false;
            });

            $('.btn-cancel').click(function() {
                self.router.navigate('list', {trigger: true});
                return false;
            });

            self.setEventListeners();
        },
        loadAllThesis: function() {
            var self = this;
            setTimeout(function() {
                $.get('/api/thesis', function(res) {
                    self.displayLoadedList(res);
                });
            }, 200);
        },
        displayLoadedList: function(list) {
            var self = this;
            // iterate thru the list to display each item
            _.each(list, function(item) {
                var $thesisItem = $(getTemplate('tpl-thesis-list-item', item));
                var id = item.Id
                if (item.Key) {
                    id = item.Key;
                }
                $thesisItem.find('.btn-edit').click(function() {
                    self.router.navigate('edit-' + id, {trigger: true});
                });
                $thesisItem.find('.btn-view').click(function() {
                    self.router.navigate('thesis-' + id, {trigger: true});
                });
                $thesisItem.find('.btn-delete').click(function() {
                    self.router.navigate('delete-' + id, {trigger: true});
                });
                $('.thesis-list').append($thesisItem);
            });

            // add event handlers for the edit button

        },
        save: function(object) {
            var self = this;

            var thesisObject = {};
            var inputs = $('form').serializeArray();
            for (var i = 0; i < inputs.length; i++)
            {
                thesisObject[inputs[i].name] = inputs[i].value;
            }

            if (thesisObject.Title.length != 0 && thesisObject.Description.length != 0)
            {

                $.post('api/thesis', object, function(res) {
                self.router.navigate('list', {trigger: true});
                });
                alert("A new thesis entry was saved");
                return false;
            }

            else
            {
                alert("Forbidden: Thesis title and description are required!");
            }

        },
        deleteThesis: function(id){
            var self = this;
            $.ajax({
                type: 'DELETE',
                url: '/api/thesis/' + id,
                success: function() {
                    self.router.navigate('list', {trigger: true});
                }
            });
        },
        searchThesis: function(keyword){
            var self = this;
            var regexStatement = new RegExp(".*(" + keyword + ").*", "i");
            $.get('/api/thesis', function(obj){
               var sorted_list = $.grep(obj, function(thesis, index){
                    return regexStatement.test(thesis.Title);
               });
               if(sorted_list.length == 0){
                    alert('Sorry. Zero results');
               }
               else{
                    var $listTemplate = getTemplate('tpl-thesis-list');
                    $('.app-content').html($listTemplate);
                    _.each(sorted_list, function(item) {
                        var $thesisItem = $(getTemplate('tpl-thesis-list-item', item));
                        $('.thesis-list').append($thesisItem);
                        var id = item.Id
                        if (item.Key) {
                            id = item.Key;
                        }
                        $thesisItem.find('.btn-edit').click(function() {
                            self.router.navigate('edit-' + id, {trigger: true});
                        });
                        $thesisItem.find('.btn-view').click(function() {
                            self.router.navigate('thesis-' + id, {trigger: true});
                        });
                        $thesisItem.find('.btn-delete').click(function() {
                            self.router.navigate('delete-' + id, {trigger: true});
                        });
                    });
               }
               $('.search-input').val('');
            });
        }

    };

    function getTemplate(template_id, context) {
        var template, $template, markup;
        template = $('#' + template_id);
        $template = Handlebars.compile(template.html());
        markup = $template(context);
        return markup;

    }

    var Router = Backbone.Router.extend({
        routes: {
            '': 'onHome',
            'search?=:query': 'onSearch',
            'thesis-:id': 'onView',
            'new': 'onCreate',
            'edit-:id': 'onEdit',
            'list': 'onList',
            'delete-:id': 'onDelete'
       },

       onHome: function() {
            app.showHome();
       },
       onSearch: function(query) {
            app.searchThesis(query);
       },
       onView: function(id) {
           console.log('thesis id', id);
            app.getThesisByID(id, function(item) {
                app.showThesis(item);
                FB.XFBML.parse();
            });
       },
       onCreate: function() {
            app.showForm();
       },
       onEdit: function(id) {
            app.getThesisByID(id, function(item) {
                app.showForm(item);
            });
       },
       onList: function() {
            app.showList();
            app.loadAllThesis();
       },
       onDelete: function(id) {
            app.deleteThesis(id);
       },

    });
    app.init();

});
<|endoftext|>


<commit_before>(function () { // to wrap use strict
    'use strict';

    // This module manages the warning page that comes up when a book is first downloaded.
    // its url looks like downloadBook/bookId.
    angular.module('BloomLibraryApp.download', ['ui.router'])
    // Its Continue button will attempt to download the specified book.
        .config(function config($urlRouterProvider, $stateProvider) {

            // We have an abstract parent state
            // and then a child for 'preflighting' new users
            // and another for telling them it is coming.
            $stateProvider.state('browse.detail.downloadBook', {
                url: "/downloadBook",
                abstract: true,
                views: {
                    '@': {
                        template: '<ui-view/>'
                    }
                }
            });
            // Give the user a few last words about what they need for a successful download.
            $stateProvider.state('browse.detail.downloadBook.preflight', {
                url: "/preflight",
                templateUrl: 'modules/download/preflight.tpl.html',
                controller: 'PreflightCtrl'
            });
            // Downloading happens in this state
            $stateProvider.state('browse.detail.downloadBook.hereItComes', {
                url: "/hereItComes",
                templateUrl: 'modules/download/hereItComes.tpl.html',
                controller: 'DownloadCtrl'
            });
        });

    angular.module('BloomLibraryApp.download')
        .controller('PreflightCtrl', ['$scope', '$state', '$stateParams', 'bookService', '$location', 'localStorageService',

        function ($scope, $state, $stateParams, bookService, $location, localStorageService) {
            // Used to display the book title
            bookService.getBookById($stateParams.bookId).then(function (book) {
                $scope.book = book;
            });

            //when the user clicks this checkbox, store their preference
            $scope.$watch('skipDownloadPreflight', function() {
                localStorageService.set('skipDownloadPreflight',$scope.skipDownloadPreflight);
            });

            $scope.skipDownloadPreflight = localStorageService.get('skipDownloadPreflight');

            $scope.cancel = function () {
                $state.go('^.^'); //we're done here. Go back to the detail.
            };

            // Our state hierarchy has "download", with two children: preflight and hereItComes.
            // Preflight is used only until users know what they're doing, then they click on a checkbox
            // that makes it so we don't show that anymore. There must be a more elegant way, with ui-router,
            // to skip the preflight if we need to. But this it the way I've gotten it to work thusfar:
            // we intercept the state change to preflight, and if we don't want to go there, then we
            // instead go straight to the 'hereItComes' screen.
            $scope.$on('$viewContentLoaded',
                function(event, toState, toParams, fromState, fromParams){
                    if($state.current.name.indexOf('preflight') > -1 &&
                        localStorageService.get('skipDownloadPreflight')==='true') {
                        event.preventDefault();
                        $state.go("browse.detail.downloadBook.hereItComes");
                    }
                });
        } ])
        .controller('DownloadCtrl', ['$stateParams', 'bookService', '$timeout', '$analytics', function($stateParams, bookService, $timeout, $analytics) {
            //get the book for which we're going to show the details asynchronously, then start the download
            bookService.getBookById($stateParams.bookId).then(function (book) {
                // Without $timeout, setting the href will cancel the analytics request
                $timeout(function() {
                    $analytics.eventTrack('Download Book', {book: book.objectId, href: book.bookOrder});
                });
                window.location.href = book.bookOrder + '&title=' + encodeURIComponent(book.title);
            });
        }]);
} ());  // end wrap-everything function<commit_msg>Add title of book to the downloadBook analytics event.<commit_after>(function () { // to wrap use strict
    'use strict';

    // This module manages the warning page that comes up when a book is first downloaded.
    // its url looks like downloadBook/bookId.
    angular.module('BloomLibraryApp.download', ['ui.router'])
    // Its Continue button will attempt to download the specified book.
        .config(function config($urlRouterProvider, $stateProvider) {

            // We have an abstract parent state
            // and then a child for 'preflighting' new users
            // and another for telling them it is coming.
            $stateProvider.state('browse.detail.downloadBook', {
                url: "/downloadBook",
                abstract: true,
                views: {
                    '@': {
                        template: '<ui-view/>'
                    }
                }
            });
            // Give the user a few last words about what they need for a successful download.
            $stateProvider.state('browse.detail.downloadBook.preflight', {
                url: "/preflight",
                templateUrl: 'modules/download/preflight.tpl.html',
                controller: 'PreflightCtrl'
            });
            // Downloading happens in this state
            $stateProvider.state('browse.detail.downloadBook.hereItComes', {
                url: "/hereItComes",
                templateUrl: 'modules/download/hereItComes.tpl.html',
                controller: 'DownloadCtrl'
            });
        });

    angular.module('BloomLibraryApp.download')
        .controller('PreflightCtrl', ['$scope', '$state', '$stateParams', 'bookService', '$location', 'localStorageService',

        function ($scope, $state, $stateParams, bookService, $location, localStorageService) {
            // Used to display the book title
            bookService.getBookById($stateParams.bookId).then(function (book) {
                $scope.book = book;
            });

            //when the user clicks this checkbox, store their preference
            $scope.$watch('skipDownloadPreflight', function() {
                localStorageService.set('skipDownloadPreflight',$scope.skipDownloadPreflight);
            });

            $scope.skipDownloadPreflight = localStorageService.get('skipDownloadPreflight');

            $scope.cancel = function () {
                $state.go('^.^'); //we're done here. Go back to the detail.
            };

            // Our state hierarchy has "download", with two children: preflight and hereItComes.
            // Preflight is used only until users know what they're doing, then they click on a checkbox
            // that makes it so we don't show that anymore. There must be a more elegant way, with ui-router,
            // to skip the preflight if we need to. But this it the way I've gotten it to work thusfar:
            // we intercept the state change to preflight, and if we don't want to go there, then we
            // instead go straight to the 'hereItComes' screen.
            $scope.$on('$viewContentLoaded',
                function(event, toState, toParams, fromState, fromParams){
                    if($state.current.name.indexOf('preflight') > -1 &&
                        localStorageService.get('skipDownloadPreflight')==='true') {
                        event.preventDefault();
                        $state.go("browse.detail.downloadBook.hereItComes");
                    }
                });
        } ])
        .controller('DownloadCtrl', ['$stateParams', 'bookService', '$timeout', '$analytics', function($stateParams, bookService, $timeout, $analytics) {
            //get the book for which we're going to show the details asynchronously, then start the download
            bookService.getBookById($stateParams.bookId).then(function (book) {
                // Without $timeout, setting the href will cancel the analytics request
                $timeout(function() {
                    $analytics.eventTrack('Download Book', {book: book.objectId, href: book.bookOrder, bookTitle:book.title});
                });
                window.location.href = book.bookOrder + '&title=' + encodeURIComponent(book.title);
            });
        }]);
} ());  // end wrap-everything function<|endoftext|>


<commit_before>
var chart_h = 40;
var chart_w = 80;
var stepX = 77 / 14;

var chart_1_y = [
  70, 25, 40, 30, 45, 40, 35, 55, 37, 50, 60, 45,70, 78
];
var chart_2_y = [
  20, 65, 65, 40, 55, 34, 54, 50, 60, 64, 55, 27, 24, 30
];

function point(x, y) {
    x: 0;
    y: 0;
}
/* DRAW GRID */
function drawGrid(graph) {
    var graph = Snap(graph);
    var g = graph.g();
    g.attr('id', 'grid');
    for (i = 0; i <= stepX + 2; i++) {
        var horizontalLine = graph.path(
            "M" + 0 + "," + stepX * i + " " +
            "L" + 77 + "," + stepX * i);
        horizontalLine.attr('class', 'horizontal');
        g.add(horizontalLine);
    };
    for (i = 0; i <= 14; i++) {
        var horizontalLine = graph.path(
            "M" + stepX * i + "," + 38.7 + " " +
            "L" + stepX * i + "," + 0)
        horizontalLine.attr('class', 'vertical');
        g.add(horizontalLine);
    };
}
drawGrid('#chart-2');
drawGrid('#chart-1');

function drawLineGraph(graph, points, container, id) {

    var graph = Snap(graph);

    /*END DRAW GRID*/

    /* PARSE POINTS */
    var myPoints = [];
    var shadowPoints = [];

    function parseData(points) {
        for (i = 0; i < points.length; i++) {
            var p = new point();
            var pv = points[i] / 100 * 40;
            p.x = 83.7 / points.length * i + 1;
            p.y = 40 - pv;
            if (p.x > 78) {
                p.x = 78;
            }
            myPoints.push(p);
        }
    }

    var segments = [];

    function createSegments(p_array) {
        for (i = 0; i < p_array.length; i++) {
            var seg = "L" + p_array[i].x + "," + p_array[i].y;
            if (i === 0) {
                seg = "M" + p_array[i].x + "," + p_array[i].y;
            }
            segments.push(seg);
        }
    }

    function joinLine(segments_array, id) {
        var line = segments_array.join(" ");
        var line = graph.path(line);
        line.attr('id', 'graph-' + id);
        var lineLength = line.getTotalLength();

        line.attr({
            'stroke-dasharray': lineLength,
                'stroke-dashoffset': lineLength
        });
    }

    function calculatePercentage(points, graph) {
        var initValue = points[0];
        var endValue = points[points.length - 1];
        var sum = endValue - initValue;
        var prefix;
        var percentageGain;
        var stepCount = 1300 / sum;

        function findPrefix() {
            if (sum > 0) {
                prefix = "+";
            } else {
                prefix = "";
            }
        }

        var percentagePrefix = "";

        function percentageChange() {
            percentageGain = initValue / endValue * 100;

            if(percentageGain > 100){
              console.log('over100');
              percentageGain = Math.round(percentageGain * 100*10) / 100;
            }else if(percentageGain < 100){
              console.log('under100');
              percentageGain = Math.round(percentageGain * 10) / 10;
            }
            if (initValue > endValue) {

                percentageGain = endValue/initValue*100-100;
                percentageGain = percentageGain.toFixed(2);

                percentagePrefix = "";
                $(graph).find('.percentage-value').addClass('negative');
            } else {
                percentagePrefix = "+";
            }
          if(endValue > initValue){
              percentageGain = endValue/initValue*100;
              percentageGain = Math.round(percentageGain);
          }
        };
        percentageChange();
        findPrefix();

        var percentage = $(graph).find('.percentage-value');
        var totalGain = $(graph).find('.total-gain');
        var hVal = $(graph).find('.h-value');

        function count(graph, sum) {
            var totalGain = $(graph).find('.total-gain');
            var i = 0;
            var time = 1300;
            var intervalTime = Math.abs(time / sum);
            var timerID = 0;
            if (sum > 0) {
                var timerID = setInterval(function () {
                    i++;
                    totalGain.text(percentagePrefix + i);
                    if (i === sum) clearInterval(timerID);
                }, intervalTime);
            } else if (sum < 0) {
                var timerID = setInterval(function () {
                    i--;
                    totalGain.text(percentagePrefix + i);
                    if (i === sum) clearInterval(timerID);
                }, intervalTime);
            }
        }
        count(graph, sum);

        percentage.text(percentagePrefix + percentageGain + "%");
        totalGain.text("0%");
        setTimeout(function () {
            percentage.addClass('visible');
            hVal.addClass('visible');
        }, 1300);

    }

    function showValues() {
        var val1 = $(graph).find('.h-value');
        var val2 = $(graph).find('.percentage-value');
        val1.addClass('visible');
        val2.addClass('visible');
    }

    function drawPolygon(segments, id) {
        var lastel = segments[segments.length - 1];
        var polySeg = segments.slice();
        polySeg.push([78, 38.4], [1, 38.4]);
        var polyLine = polySeg.join(' ').toString();
        var replacedString = polyLine.replace(/L/g, '').replace(/M/g, "");

        var poly = graph.polygon(replacedString);
        var clip = graph.rect(-80, 0, 80, 40);
        poly.attr({
            'id': 'poly-' + id,
            /*'clipPath':'url(#clip)'*/
                'clipPath': clip
        });
        clip.animate({
            transform: 't80,0'
        }, 1300, mina.linear);
    }

      parseData(points);

      createSegments(myPoints);
      calculatePercentage(points, container);
      joinLine(segments,id);

      drawPolygon(segments, id);

    /*$('#poly-'+id).attr('class','show');*/

    /* function drawPolygon(segments,id){
      var polySeg = segments;
      polySeg.push([80,40],[0,40]);
      var polyLine = segments.join(' ').toString();
      var replacedString = polyLine.replace(/L/g,'').replace(/M/g,"");
      var poly = graph.polygon(replacedString);
      poly.attr('id','poly-'+id)
    }
    drawPolygon(segments,id);*/
}
function drawCircle(container,id,progress,parent){
  var paper = Snap(container);
  var prog = paper.path("M5,50 A45,45,0 1 1 95,50 A45,45,0 1 1 5,50");
  var lineL = prog.getTotalLength();
  var oneUnit = lineL/100;
  var toOffset = lineL - oneUnit * progress;
  var myID = 'circle-graph-'+id;
  prog.attr({
    'stroke-dashoffset':lineL,
    'stroke-dasharray':lineL,
    'id':myID
  });

  var animTime = 1300/*progress / 100*/

  prog.animate({
    'stroke-dashoffset':toOffset
  },animTime,mina.easein);

  function countCircle(animtime,parent,progress){
    var textContainer = $(parent).find('.circle-percentage');
    var i = 0;
    var time = 1300;
    var intervalTime = Math.abs(time / progress);
    var timerID = setInterval(function () {
      i++;
      textContainer.text(i+"%");
      if (i === progress) clearInterval(timerID);
    }, intervalTime);
  }
  countCircle(animTime,parent,progress);
}

$(window).on('load',function(){
    drawCircle('#chart-3',1,77,'#circle-1');
    drawCircle('#chart-4',2,53,'#circle-2');
    drawLineGraph('#chart-1', chart_1_y, '#graph-1-container', 1);
    drawLineGraph('#chart-2', chart_2_y, '#graph-2-container', 2);
});
<commit_msg>Remove unused js-file<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    browserNoActivityTimeout: 100, // TODO: increase this
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    // This custom launcher is used by Travis (see .travis.yml). It launches the
    // version of Chrome installed in Travis' docker images. Without it, Travis
    // can't start the default Chrome.
    customLaunchers: {
      ChromeNoSandbox: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: config.angularCli && config.angularCli.codeCoverage
              ? ['progress', 'coverage-istanbul']
              : ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
<commit_msg>Increase timeout for Karma to wait for Chrome activity<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    // The default timeout, 10000, was sometimes causing Karma to time out with:
    // "Disconnected (1 times), because no message in 10000 ms."
    browserNoActivityTimeout: 60000,
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    // This custom launcher is used by Travis (see .travis.yml). It launches the
    // version of Chrome installed in Travis' docker images. Without it, Travis
    // can't start the default Chrome.
    customLaunchers: {
      ChromeNoSandbox: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: config.angularCli && config.angularCli.codeCoverage
              ? ['progress', 'coverage-istanbul']
              : ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
<|endoftext|>


<commit_before>Template.nearbyLocs.helpers({
	nearbyname:function() {
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});
		if (loc.name) {
			if (loc.nickname != undefined) {
				return loc.nickname + " ("+loc.name+")";
			}
			else {
				return loc.name;
			}

		}
		else {
			return "N/A";
		}
	},
	nearbyindex:function() {
		return "nearbydist"+this;
	},
	dist:function() {
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});
		if (loc.entrances != null && loc.entrances != undefined) {
			Meteor.call("distance",
				Session.get("currentLocation"),
				Intersections.findOne({"id":loc.entrances[0]}).coordinate,
				function(error,data) {
					if (error) {
						console.log(error);
					}
					else {
						var classname = ".nearbydist"+loc.id;
						if (Session.get("unit") == "m") {
							$(classname).html("about "+ Math.floor(data)+"m");
						}
						else {
							$(classname).html("about "+Math.round(Math.floor(data*3.28))+"ft");
						}
					}
				});
		}
		else {
			var classname = ".nearbydist"+loc.id;
			$(classname).append("N/A");
			return "N/A";
		}
	}
});

Template.nearbyLocs.events({
	'click .visitnearby':function(event) {
		event.preventDefault();
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});

		var currentPage = Router.current().route.path();
		if (currentPage == "/selfguide") {
			Session.set("prev","/selfguide");
		}
		else {
			Session.set("prev","/locationList");
		}

		Session.set("viewLocation",loc._id);

		console.log(Session.get("viewLocation"));
		console.log(Session.get("prev"));

		Router.go('/viewLocation/'+loc._id);

		Session.set("pageTitle","Viewing " + loc.nickname);
	}
});<commit_msg>fix for nearby places links and hopefully distances<commit_after>Template.nearbyLocs.helpers({
	nearbyname:function() {
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});
		if (loc.name) {
			if (loc.nickname != undefined) {
				return loc.nickname + " ("+loc.name+")";
			}
			else {
				return loc.name;
			}

		}
		else {
			return "N/A";
		}
	},
	nearbyindex:function() {
		return "nearbydist"+this;
	},
	dist:function() {
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});
		if (loc.entrances != null && loc.entrances != undefined) {
			Meteor.call("distance",
				Session.get("currentLocation"),
				Intersections.findOne({"id":loc.entrances[0]}).coordinate.coordinates,
				function(error,data) {
					if (error) {
						console.log(error);
					}
					else {
						var classname = ".nearbydist"+loc.id;
						if (Session.get("unit") == "m") {
							$(classname).html("about "+ Math.floor(data)+"m");
						}
						else {
							$(classname).html("about "+Math.round(Math.floor(data*3.28))+"ft");
						}
					}
				});
		}
		else {
			var classname = ".nearbydist"+loc.id;
			$(classname).append("N/A");
			return "N/A";
		}
	}
});

Template.nearbyLocs.events({
	'click .visitnearby':function(event) {
		event.preventDefault();
		var loc = Locations.findOne({"id":JSON.stringify(this).replace(/"([^"]+(?="))"/g, '$1')});

		var currentPage = Router.current().route.path();
		if (currentPage == "/selfguide") {
			Session.set("prev","/selfguide");
		}
		else {
			Session.set("prev","/locationList");
		}

		Session.set("viewLocation",loc.id);

		console.log(Session.get("viewLocation"));
		console.log(Session.get("prev"));

		Router.go('/viewLocation/'+loc.id);

		Session.set("pageTitle","Viewing " + loc.nickname);
	}
});<|endoftext|>


<commit_before>app.Idea = React.createClass({
  getInitialState: function() {
    // set initial editing state to false
    return {
      displaying: true,
      editing: false
    };
  },

  componentDidMount: function() {
    // add a change listener on the IdeaStore
    // this is needed when the edit comes back and emits a change
    // that will force the component to re-render
    app.IdeaStore.addChangeListener(function() {
      if(this.isMounted()) {
        this.setState({editing: false});
      }
    }.bind(this));
  },

  show: function () {
    if (this.isMounted()) {
      this.setState({ displaying: !this.state.displaying });
    }
  },

  render: function() {
    var ideaContent;
    var editForm;
    var userName = document.cookie.split("user=")[1].split('%20').join(' ');

    // if editing render edit form otherwise render "Edit Idea" button
    if (this.state.editing) {
      editForm = <app.IdeaForm editing="true" name={this.props.name} key={this.props._id} _id={this.props._id} />
    }

    //if displaying and the idea is the user's original idea, allow them to edit/delete
    if (this.state.displaying && (userName === this.props.ownerName)) {
      ideaContent = (

          <form className="pure-form pure-g">

{this.props.ownerName}: {this.props.name}

              {editForm}

              <app.Interest idea_id={this.props._id} />

              <button className="button-small pure-button pure-button-primary" onClick={this.edit}>{ this.state.editing ? 'Cancel' : 'Edit Idea'}</button>
              <button className="button-small pure-button pure-button-primary" onClick={this.delete}>Delete Idea</button>

              <app.Comments idea_id={this.props._id} />

          </form>

      );
    } //otherwise if the idea wasn't the user's, allow them to comment but not edit/delete it
    else if (this.state.displaying && (userName !== this.props.ownerName)) {
      ideaContent = (

          <form className="pure-form pure-g">

{this.props.ownerName}: {this.props.name}

              {editForm}

              <app.Interest idea_id={this.props._id} />

              <app.Comments idea_id={this.props._id} />

          </form>

      );
    }

    return (

        {ideaContent}

    );
  },

  edit: function(e) {
    e.preventDefault();
    if (this.isMounted()) {
      this.setState({ editing: !this.state.editing });
    }
  },

  delete: function(e) {
    e.preventDefault();
    if (this.isMounted()) {
      app.IdeaActions.delete({ id: this.props._id });
    }
  }
});

//COMMAND TO POST IDEA ON BEHALF OF FAKE USER (DEVEVELOPMENT PURPOSES ONLY)
// curl -H "Content-Type: application/json" -d '{"name":"theyll never catch me in this fox hole!","ownerName":"Saddam Hussein","room_id":"548a38cebcf20d5101e0e13c"}' http://localhost:3000/ideas/548a38cebcf20d510

<commit_msg>adds getcurrentuser function to getInitialState of idea<commit_after>app.Idea = React.createClass({
  getInitialState: function() {
    // set initial editing state to false
    return {
      displaying: true,
      editing: false,
      currentUser: app.UserStore.get()
    };
  },

  componentDidMount: function() {
    // add a change listener on the IdeaStore
    // this is needed when the edit comes back and emits a change
    // that will force the component to re-render
    app.IdeaStore.addChangeListener(function() {
      if(this.isMounted()) {
        this.setState({editing: false});
      }
    }.bind(this));
  },

  show: function () {
    if (this.isMounted()) {
      this.setState({ displaying: !this.state.displaying });
    }
  },

  render: function() {
    var ideaContent;
    var editForm;
    var userName = document.cookie.split("user=")[1].split('%20').join(' ');

    // if editing render edit form otherwise render "Edit Idea" button
    if (this.state.editing) {
      editForm = <app.IdeaForm editing="true" name={this.props.name} key={this.props._id} _id={this.props._id} />
    }

    //if displaying and the idea is the user's original idea, allow them to edit/delete
    if (this.state.displaying && (userName === this.props.ownerName)) {
      ideaContent = (

          <form className="pure-form pure-g">

{this.props.ownerName}: {this.props.name}

              {editForm}

              <app.Interest idea_id={this.props._id} />

              <button className="button-small pure-button pure-button-primary" onClick={this.edit}>{ this.state.editing ? 'Cancel' : 'Edit Idea'}</button>
              <button className="button-small pure-button pure-button-primary" onClick={this.delete}>Delete Idea</button>

              <app.Comments idea_id={this.props._id} />

          </form>

      );
    } //otherwise if the idea wasn't the user's, allow them to comment but not edit/delete it
    else if (this.state.displaying && (userName !== this.props.ownerName)) {
      ideaContent = (

          <form className="pure-form pure-g">

{this.props.ownerName}: {this.props.name}

              {editForm}

              <app.Interest idea_id={this.props._id} />

              <app.Comments idea_id={this.props._id} />

          </form>

      );
    }

    return (

        {ideaContent}

    );
  },

  edit: function(e) {
    e.preventDefault();
    if (this.isMounted()) {
      this.setState({ editing: !this.state.editing });
    }
  },

  delete: function(e) {
    e.preventDefault();
    if (this.isMounted()) {
      app.IdeaActions.delete({ id: this.props._id });
    }
  }
});

//COMMAND TO POST IDEA ON BEHALF OF FAKE USER (DEVEVELOPMENT PURPOSES ONLY)
// curl -H "Content-Type: application/json" -d '{"name":"theyll never catch me in this fox hole!","ownerName":"Saddam Hussein","room_id":"548a38cebcf20d5101e0e13c"}' http://localhost:3000/ideas/548a38cebcf20d510

<|endoftext|>


<commit_before>
import EntityManager from "idb-entity/es2015/EntityManager"
import createPrivate from "namespace-proxy"
import Account from "../../model/Account"
import Channel from "../../model/Channel"
import Playlist from "../../model/Playlist"
import Video from "../../model/Video"
import Subscription from "../../model/Subscription"
import Database from "../storage/Database"
import ClientFactory from "../youtube-api/ClientFactory"

const PRIVATE = createPrivate()

/**
 * Utility for fetching and updating the list of subscribed channels of a
 * single user's Google account.
 */
export default class SubscriptionsFetcher {
  /**
   * Initializes the subscription fetcher.
   *
   * @param database The current database connection provider.
   * @param clientFactory YouTube API client factory.
   */
  constructor(database: Database, clientFactory: ClientFactory) {
    /**
     * The current database connection provider.
     *
     * @type {Database}
     */
    PRIVATE(this).database = database

    /**
     * YouTube API client factory.
     *
     * @type {ClientFactory}
     */
    PRIVATE(this).clientFactory = clientFactory
  }

  async updateSubscriptions(account: Account): void {
    if (!account.channelId) {
      throw new Error("Cannot read the subscription of a foreign account, " +
          "because the channel ID is not known")
    }

    let allSubscriptions = await this[PRIVATE.fetchCurrentSubscriptions](
      account
    )

    let entityManager = PRIVATE(this).database.createEntityManager()
    entityManager.runTransaction(async () => {
      let knownSubscriptions = await this[PRIVATE.fetchSubscriptions](
        entityManager,
        account
      )

      for (let [subscription, channel] of allSubscriptions) {
        // save new subscriptions
        let knownSubscription = knownSubscriptions.get(channel.id)
        if (!knownSubscription) {
          knownSubscription = await entityManager.persist(subscription)
        }

        let knownChannel = await entityManager.find(Channel, channel.id)
        if (!knownChannel) {
          knownChannel = await entityManager.persist(channel)
        }
        let playlist = await entityManager.find(
            Playlist,
            knownChannel.uploadsPlaylistId
        )
        if (!playlist) {
          playlist = await this[PRIVATE.fetchUploadsPlaylist](channel)
          playlist = await entityManager.persist(playlist)
        }

        // update existing subscriptions
        if (!knownChannel.accountIds.includes(account.id)) {
          knownChannel.accountIds.push(account.id)
        }
        if (!playlist.accountIds.includes(account.id)) {
          playlist.accountIds.push(account.id)
          let videos = await entityManager.query(Video, {
            channelId: knownChannel.id
          })
          for (let video of videos) {
            if (!video.accountIds.includes(account.id)) {
              video.accountIds.push(account.id)
            }
          }
        }

        knownSubscriptions.delete(channel.id)
      }

      // delete subscriptions that were removed from the account
      for (let [channelId, subscription] of knownSubscriptions) {
        let channel = await entityManager.find(Channel, channelId)
        channel.accountIds = channel.accountIds.filter(
          otherAccountId => otherAccountId !== account.id
        )
        let shouldDeleteChannel =
            !channel.accountIds.length &&
            !channel.incognitoSubscriptionIds.length
        if (shouldDeleteChannel) {
          entityManager.remove(Channel, channel.id)
        }
        let playlist = await entityManager.find(
            Playlist,
            channel.uploadsPlaylistId
        )
        playlist.accountIds = playlist.accountIds.filter(
          otherAccountId => otherAccountId !== account.id
        )
        let shouldDeletePlaylist =
            !playlist.accountIds.length &&
            !playlist.incognitoSubscriptionIds.length
        if (shouldDeletePlaylist) {
          entityManager.remove(Playlist, playlist.id)
        }
        let videos = await entityManager.query(Video, {
          channelId: channelId
        })
        for (let video of videos) {
          video.accountIds = video.accountIds.filter(
            otherAccountId => otherAccountId !== account.id
          )
          let shouldDeleteVideo =
              !video.accountIds.length &&
              !video.incognitoSubscriptionIds.length
          if (shouldDeleteVideo) {
            entityManager.remove(Video, video.id)
          }
        }
      }
    })
  }

  /**
   * Fetches the uploads playlist entity from the YouTube API for the specified
   * channel.
   *
   * @param channel The YouTube channel for which the uploads playlist should
   *        be fetched.
   * @return The fetched playlist entity.
   */
  async [PRIVATE.fetchUploadsPlaylist](channel: Channel): Playlist {
    let client = PRIVATE(this).clientFactory.getClientForUser(channel.id)
    let playlists = await client.getUploadsPlaylists([channel])
    return playlists[0]
  }

  /**
   * Fetches the currently known subscriptions of the specified Google account
   * from the local Indexed DB database.
   *
   * @param entityManager The entity manager representing the connection to the
   *        database.
   * @param account The Google account.
   * @return The map of subscribed channel IDs to the subscription entities.
   */
  async [PRIVATE.fetchSubscriptions](entityManager: EntityManager,
      account: Account): Map<string, Subscription> {
    let currentSubscriptionsList = await entityManager.query(
      Subscription,
      {
        isIncognito: 0,
        accountId: account.id
      }
    )
    return new Map(currentSubscriptionsList.map(
      subscription => [subscription.channelId, subscription]
    ))
  }

  /**
   * Fetches the current list of the specified Google account's subscriptions
   * from the YouTube API.
   *
   * The method will attempt to fetch the list without the user's authorization
   * if possible first, and use the user's authorization if the unauthorized
   * attempt fails.
   *
   * @param account The Google account for which to fetch the subscriptions.
   * @return The current list of account's subscriptions and the subscribed
   *         channels as returned by the YouTube API.
   */
  async [PRIVATE.fetchCurrentSubscriptions](account: Account):
      Array<[Subscription, Channel]> {
    let client = PRIVATE(this).clientFactory.getClientForUser(account.id)
    try {
      return await client.getSubscriptions(account)
    } catch (error) {
      console.warn("Failed to fetch the subscriptions list without user's " +
          "authorization", error)
      return await client.getSubscriptions(account, true)
    }
  }
}
<commit_msg>refactoring, fixed deleting an unsubscribed subscription<commit_after>
import EntityManager from "idb-entity/es2015/EntityManager"
import createPrivate from "namespace-proxy"
import Account from "../../model/Account"
import Channel from "../../model/Channel"
import Playlist from "../../model/Playlist"
import Video from "../../model/Video"
import Subscription from "../../model/Subscription"
import Database from "../storage/Database"
import ClientFactory from "../youtube-api/ClientFactory"

const PRIVATE = createPrivate()

/**
 * Utility for fetching and updating the list of subscribed channels of a
 * single user's Google account.
 */
export default class SubscriptionsFetcher {
  /**
   * Initializes the subscription fetcher.
   *
   * @param database The current database connection provider.
   * @param clientFactory YouTube API client factory.
   */
  constructor(database: Database, clientFactory: ClientFactory) {
    /**
     * The current database connection provider.
     *
     * @type {Database}
     */
    PRIVATE(this).database = database

    /**
     * YouTube API client factory.
     *
     * @type {ClientFactory}
     */
    PRIVATE(this).clientFactory = clientFactory
  }

  async updateSubscriptions(account: Account): void {
    if (!account.channelId) {
      throw new Error("Cannot read the subscription of a foreign account, " +
          "because the channel ID is not known")
    }

    let allSubscriptions = await this[PRIVATE.fetchCurrentSubscriptions](
      account
    )

    let entityManager = PRIVATE(this).database.createEntityManager()
    entityManager.runTransaction(async () => {
      let knownSubscriptions = await this[PRIVATE.fetchSubscriptions](
        entityManager,
        account
      )

      for (let [subscription, channel] of allSubscriptions) {
        // save new subscriptions
        let knownSubscription = knownSubscriptions.get(channel.id)
        if (!knownSubscription) {
          knownSubscription = await entityManager.persist(subscription)
        }

        let knownChannel = await entityManager.find(Channel, channel.id)
        if (!knownChannel) {
          knownChannel = await entityManager.persist(channel)
        }
        let playlist = await entityManager.find(
            Playlist,
            knownChannel.uploadsPlaylistId
        )
        if (!playlist) {
          playlist = await this[PRIVATE.fetchUploadsPlaylist](channel)
          playlist = await entityManager.persist(playlist)
        }

        // update existing subscriptions
        if (!knownChannel.accountIds.includes(account.id)) {
          knownChannel.accountIds.push(account.id)
        }
        if (!playlist.accountIds.includes(account.id)) {
          playlist.accountIds.push(account.id)
          let videos = await entityManager.query(Video, {
            channelId: knownChannel.id
          })
          for (let video of videos) {
            if (!video.accountIds.includes(account.id)) {
              video.accountIds.push(account.id)
            }
          }
        }

        knownSubscriptions.delete(channel.id)
      }

      // delete subscriptions that were removed from the account
      for (let [channelId, subscription] of knownSubscriptions) {
        await this[PRIVATE.deleteUnsubsribedSubscription](
          entityManager,
          channelId,
          subscription
        )
      }
    })
  }

  /**
   * Deletes the provided subscription, which the user is no longer subscribed
   * to.
   *
   * The method also updates the account IDs of the subscribed channel, uploads
   * playlist and videos. If any of those will no longer be subscribed by any
   * account or incognito subscription, they will be deleted.
   *
   * @param entityManager The current entity manager in a transaction used to
   *        update the subscriptions.
   * @param channelId The ID of the channel that has been unsubscribed
   * @param subscriptions The subscription that should be deleted.
   */
  async [PRIVATE.deleteUnsubsribedSubscription](
    entityManager: EntityManager,
    channelId: string,
    subscriptions: Subscription
  ): void {
    // update and delete (if necessary) the channel
    let channel = await entityManager.find(Channel, channelId)
    channel.accountIds = channel.accountIds.filter(
      otherAccountId => otherAccountId !== account.id
    )
    let shouldDeleteChannel =
        !channel.accountIds.length &&
        !channel.incognitoSubscriptionIds.length
    if (shouldDeleteChannel) {
      entityManager.remove(Channel, channel.id)
    }

    // update and delete (if necessary) the playlist
    let playlist = await entityManager.find(
      Playlist,
      channel.uploadsPlaylistId
    )
    playlist.accountIds = playlist.accountIds.filter(
      otherAccountId => otherAccountId !== account.id
    )
    let shouldDeletePlaylist =
        !playlist.accountIds.length &&
        !playlist.incognitoSubscriptionIds.length
    if (shouldDeletePlaylist) {
      entityManager.remove(Playlist, playlist.id)
    }

    // updated and delete (if necessary) the videos
    let videos = await entityManager.query(Video, {
      channelId: channelId
    })
    for (let video of videos) {
      video.accountIds = video.accountIds.filter(
        otherAccountId => otherAccountId !== account.id
      )
      let shouldDeleteVideo =
          !video.accountIds.length &&
          !video.incognitoSubscriptionIds.length
      if (shouldDeleteVideo) {
        entityManager.remove(Video, video.id)
      }
    }

    // delete the subscription
    entityManager.remove(Subscription, subscriptions.id)
  }

  /**
   * Fetches the uploads playlist entity from the YouTube API for the specified
   * channel.
   *
   * @param channel The YouTube channel for which the uploads playlist should
   *        be fetched.
   * @return The fetched playlist entity.
   */
  async [PRIVATE.fetchUploadsPlaylist](channel: Channel): Playlist {
    let client = PRIVATE(this).clientFactory.getClientForUser(channel.id)
    let playlists = await client.getUploadsPlaylists([channel])
    return playlists[0]
  }

  /**
   * Fetches the currently known subscriptions of the specified Google account
   * from the local Indexed DB database.
   *
   * @param entityManager The entity manager representing the connection to the
   *        database.
   * @param account The Google account.
   * @return The map of subscribed channel IDs to the subscription entities.
   */
  async [PRIVATE.fetchSubscriptions](entityManager: EntityManager,
      account: Account): Map<string, Subscription> {
    let currentSubscriptionsList = await entityManager.query(
      Subscription,
      {
        isIncognito: 0,
        accountId: account.id
      }
    )
    return new Map(currentSubscriptionsList.map(
      subscription => [subscription.channelId, subscription]
    ))
  }

  /**
   * Fetches the current list of the specified Google account's subscriptions
   * from the YouTube API.
   *
   * The method will attempt to fetch the list without the user's authorization
   * if possible first, and use the user's authorization if the unauthorized
   * attempt fails.
   *
   * @param account The Google account for which to fetch the subscriptions.
   * @return The current list of account's subscriptions and the subscribed
   *         channels as returned by the YouTube API.
   */
  async [PRIVATE.fetchCurrentSubscriptions](account: Account):
      Array<[Subscription, Channel]> {
    let client = PRIVATE(this).clientFactory.getClientForUser(account.id)
    try {
      return await client.getSubscriptions(account)
    } catch (error) {
      console.warn("Failed to fetch the subscriptions list without user's " +
          "authorization", error)
      return await client.getSubscriptions(account, true)
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

angular.module('app')
    .factory('rgiUserMethodSrvc', function (
        $http,
        $q,
        rgiIdentitySrvc,
        rgiUserSrvc
    ) {
        return {
            createUser: function (new_user_data) {
                var new_user = new rgiUserSrvc(new_user_data),
                    dfd = $q.defer(),
                    error_message;

                new_user.$save().then(function () {
                    dfd.resolve();
                }, function (response) {
                    error_message = response.data.reason.replace('ValidationError: ', '');
                    dfd.reject(error_message.charAt(0).toUpperCase() + error_message.slice(1));
                });
                return dfd.promise;
            },
            deleteUser: function (userDeletion) {
                var dfd = $q.defer(),
                    deleteID = new rgiUserSrvc();

                deleteID.id = userDeletion;

                deleteID.$delete().then(function () {
                    dfd.resolve();
                }, function (response) {
                    dfd.reject(response.data.reason);
                });
                return dfd.promise;
            },
            updateUser: function (new_user_data) {
                var dfd = $q.defer(),
                error_message;

                new_user_data.$update().then(function () {
                    dfd.resolve();
                }, function (response) {
                    error_message = response.data.reason.replace('ValidationError: ', '');
                    dfd.reject(error_message.charAt(0).toUpperCase() + error_message.slice(1));
                });
                return dfd.promise;
            }
        };
    });<commit_msg>refactor the user method service<commit_after>'use strict';

angular.module('app')
    .factory('rgiUserMethodSrvc', function (
        $q,
        rgiUserSrvc
    ) {
        var
            getFormattedErrorMessage = function(errorMessage) {
                errorMessage = errorMessage.replace('ValidationError: ', '');
                return errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1);
            },
            manipulateUser = function(user, action, formatErrorMessage) {
                var dfd = $q.defer();

                user[action]().then(function () {
                    dfd.resolve();
                }, function (response) {
                    var errorMessage = response.data.reason;
                    dfd.reject(formatErrorMessage ? getFormattedErrorMessage(errorMessage) : errorMessage);
                });

                return dfd.promise;
            };

        return {
            createUser: function (user) {
                return manipulateUser(new rgiUserSrvc(user), '$save', true);
            },
            deleteUser: function (userId) {
                var user = new rgiUserSrvc();
                user.id = userId;
                return manipulateUser(user, '$delete', false);
            },
            updateUser: function (user) {
                return manipulateUser(user, '$update', true);
            }
        };
    });
<|endoftext|>


<commit_before>timesSuite = (getData) => {
  return () => {

  var data;

  before(() => {
    data = getData();
  });

  var range = (count) => Array.from(Array(count).keys());

  beforeEach((done) => {
    var rows = range(16).map((i) => ({ id: i, value: i % 4, time: new Date(Math.floor(i / 4)) }));
    data.store(rows).then((res) => {
      assert.isArray(res);
      assert.lengthOf(res, 16);
      done();
    });
  });

  it("#.find(time: X)", (done) => {
    data.find({ time: new Date(0) }).value().then((res) => {
      assert.deepEqual({ id: 0, time: new Date(0), value: 0 }, res);
      done();
    }).catch(done);
  });

  it("#.find(value: X, time: Y)", (done) => {
    data.find({ value: 1, time: new Date(3) }).value().then((res) => {
      assert.deepEqual({ id: 13, value: 1, time: new Date(3) }, res);
      done();
    }).catch(done);
  });

  it("#.findAll(time: X)", (done) => {
    data.findAll({ time: new Date(2) }).value().then((res) => {
      assert.deepEqual(range(4).map((i) => ({ id: i + 8, value: i, time: new Date(2) })), res);
      done();
    }).catch(done);
  });

  it("#.findAll(value: X, time: Y)", (done) => {
    data.findAll({ value: 2, time: new Date(3) }).value().then((res) => {
      assert.deepEqual([{ id: 14, value: 2, time: new Date(3) }], res);
      done();
    }).catch(done);
  });

  it("#.findAll(value: X).above(time: Y)", (done) => {
    data.findAll({ value: 3 }).above({ time: new Date(1) }).value().then((res) => {
      assert.deepEqual(range(3).map((i) => ({ id: 3 + (i + 1) * 4, value: 3, time: new Date(i + 1) })), res);
      done();
    }).catch(done);
  });

  it("#.findAll(value: X).above(time: Y).below(time: Z)", (done) => {
    data.findAll({ value: 2 }).above({ time: new Date(1) }).below({ time: new Date(3) }).value().then((res) => {
      assert.deepEqual([{ id: 6, value: 2, time: new Date(1) }, { id: 10, value: 2, time: new Date(2) }], res);
      done();
    }).catch(done);
  });
  } // Testing `find`
}
<commit_msg>Times tests pass<commit_after>'use strict'
const timesSuite = getData => () => {
  let data

  before(() => {
    data = getData()
  })

  let range = count => Array.from(Array(count).keys())

  beforeEach(done => {
    const rows = range(16).map(i => (
      {
        id: i,
        value: i % 4,
        time: new Date(Math.floor(i / 4)),
      }
    ))
    data.store(rows).toArray()
      .do(res => {
        assert.isArray(res)
        assert.lengthOf(res, 16)
      })
      .subscribe(doneObserver(done))
  })

  it('finds a document by a field with a time value', done => {
    data.find({ time: new Date(0) }).fetch()
      .do(res => assert.deepEqual(res, { id: 0, time: new Date(0), value: 0 }))
      .subscribe(doneObserver(done))
  })

  it('finds a document by a time field and another field', done => {
    data.find({ value: 1, time: new Date(3) }).fetch()
      .do(res => assert.deepEqual(res, { id: 13, value: 1, time: new Date(3) }))
      .subscribe(doneObserver(done))
  })

  it('finds all documents by a field with a time value', done => {
    data.findAll({ time: new Date(2) }).fetch({ asCursor: false })
      .do(res => assert.deepEqual(res, range(4).map(i => ({
        id: i + 8,
        value: i,
        time: new Date(2)
      }))))
      .subscribe(doneObserver(done))
  })

  it('finds all documents by a time field and another field', done => {
    data.findAll({ value: 2, time: new Date(3) }).fetch({ asCursor: false })
      .do(res =>
          assert.deepEqual(res, [ { id: 14, value: 2, time: new Date(3) } ]))
      .subscribe(doneObserver(done))
  })

  it('finds all documents bounded above by a time', done => {
    data.findAll({ value: 3 })
      .above({ time: new Date(1) })
      .fetch({ asCursor: false })
      .do(res => assert.deepEqual(res, range(3).map(i => ({
        id: 3 + (i + 1) * 4,
        value: 3,
        time: new Date(i + 1),
      }))))
      .subscribe(doneObserver(done))
  })

  it('finds all documents between two times', done => {
    data.findAll({ value: 2 })
      .above({ time: new Date(1) })
      .below({ time: new Date(3) })
      .fetch({ asCursor: false })
      .do(res => assert.deepEqual(res, [
        { id: 6, value: 2, time: new Date(1) },
        { id: 10, value: 2, time: new Date(2) },
      ]))
      .subscribe(doneObserver(done))
  })
} // Testing `find`
<|endoftext|>


<commit_before>
  cleanMarkers = function () {
    if (markersArray) {
      for (i in markersArray) {
        markersArray[i].setMap(null);
      }
    markersArray.length = 0;
    }
  }

  placeMarkers = function () {
  // add geolocalized messages
    if (Session.get('activeGeohashes')) {
      _(Session.get('activeGeohashes')).each(function (gh) {
        var geo = geohash.decode(gh);
        var el = {
          lat: geo[0], lon:geo[1], ghash:gh };

        // FIX: bad coords
        //console.log('a-'+el.lon+" "+el.lat);
        var marker = new google.maps.Marker({
          position: new google.maps.LatLng(el.lat, el.lon),
          draggable: false,
          map: map
        });

        // infowindow
        var infowindow = new google.maps.InfoWindow({
            content: '
 Chat Area
Go to this chat!

'
        });
        google.maps.event.addListener(marker, 'click', function() {
          infowindow.open(map,marker);
        });

        markersArray.push(marker);
      });
     var markerCluster = new MarkerClusterer(map, markersArray);
    }
  }

  Meteor.autorun(function() {
  });

  // just to ensure gmap is loaded
  Meteor.setTimeout(function () {
    // body...
    Meteor.call('activeGeohashes',function (err,data) {
      console.log(data);
      Session.set('activeGeohashes', data);
      cleanMarkers();
      placeMarkers();
    });
  }, 5000);

Template.home.rendered = function() {
  markersArray = [];

    var mapCenter = new google.maps.LatLng(45.0,7.5);
    var myOptions = {
      zoom: 2,
      center: mapCenter,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    // FIXME: error in 1st render & duplication with localize
    map = new google.maps.Map(document.getElementById("maphome"), myOptions);
}<commit_msg>better clustering<commit_after>
  cleanMarkers = function () {
    if (markersArrayh) {
      for (i in markersArrayh) {
        markersArrayh[i].setMap(null);
      }
    markersArrayh.length = 0;
    }
  }

  placeMarkers = function () {
  // add geolocalized messages
    if (Session.get('activeGeohashes')) {
      _(Session.get('activeGeohashes')).each(function (gh) {
        var geo = geohash.decode(gh);
        var el = {
          lat: geo[0], lon:geo[1], ghash:gh };

        // FIX: bad coords
        //console.log('a-'+el.lon+" "+el.lat);
        var marker = new google.maps.Marker({
          position: new google.maps.LatLng(el.lat, el.lon),
          draggable: false,
          map: map
        });

        // infowindow
        var infowindow = new google.maps.InfoWindow({
            content: '
 Chat Area
Go to this chat!

'
        });
        google.maps.event.addListener(marker, 'click', function() {
          infowindow.open(map,marker);
        });

        markersArrayh.push(marker);
      });
     var markerCluster = new MarkerClusterer(map, markersArrayh);
    }
  }

  Meteor.autorun(function() {
  });

Template.home.rendered = function() {
  markersArrayh = [];

    var mapCenter = new google.maps.LatLng(45.0,7.5);
    var myOptions = {
      zoom: 2,
      center: mapCenter,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    // FIXME: error in 1st render & duplication with localize
    map = new google.maps.Map(document.getElementById("maphome"), myOptions);

    // just to ensure gmap is loaded
    Meteor.setTimeout(function () {
      // body...
      Meteor.call('activeGeohashes',function (err,data) {
        // console.log(data);
        Session.set('activeGeohashes', data);
        // cleanMarkers();
        placeMarkers();
      });
    }, 3000);

}<|endoftext|>


<commit_before>var debug = require('debug')('webtorrent-www:home')
var fs = require('fs')
var moment = require('moment')
var path = require('path')
var prettyBytes = require('pretty-bytes')
var throttle = require('throttleit')
var TorrentGraph = require('../lib/torrent-graph')
var WebTorrent = require('webtorrent')
var xhr = require('xhr')

var TORRENT = fs.readFileSync(
  path.join(__dirname, '../../static/torrents/sintel.torrent')
)

module.exports = function () {
  var graph = window.graph = new TorrentGraph('#svgWrap')

  getRtcConfig('https://instant.io/rtcConfig', function (err, rtcConfig) {
    if (err) onError(err)
    createClient(rtcConfig)
  })

  var torrent
  function createClient (rtcConfig) {
    var client = window.client = new WebTorrent({ rtcConfig: rtcConfig })
    client.on('warning', onWarning)
    client.on('error', onError)

    torrent = client.add(TORRENT, onTorrent)
    graph.add({ id: 'You', me: true })
  }

  var $body = document.body
  var $progressBar = document.querySelector('#progressBar')
  var $numPeers = document.querySelector('#numPeers')
  var $downloaded = document.querySelector('#downloaded')
  var $total = document.querySelector('#total')
  var $remaining = document.querySelector('#remaining')

  function onTorrent () {
    torrent.files[0].appendTo('#videoWrap .video', onError)
    torrent.on('wire', onWire)
    torrent.on('done', onDone)

    torrent.on('download', throttle(onProgress, 500))
    torrent.on('upload', throttle(onProgress, 500))
    setInterval(onProgress, 5000)
    onProgress()
  }

  function onWire (wire) {
    var id = wire.peerId.toString()
    graph.add({ id: id, ip: wire.remoteAddress || 'Unknown' })
    graph.connect('You', id)
    wire.once('close', function () {
      graph.disconnect('You', id)
      graph.remove(id)
    })
  }

  function onProgress () {
    var percent = Math.round(torrent.progress * 100 * 100) / 100
    $progressBar.style.width = percent + '%'
    $numPeers.innerHTML = torrent.numPeers + (torrent.numPeers === 1 ? ' peer' : ' peers')

    $downloaded.innerHTML = prettyBytes(torrent.downloaded)
    $total.innerHTML = prettyBytes(torrent.length)

    var remaining
    if (torrent.done) {
      remaining = 'Done.'
    } else {
      remaining = moment.duration(torrent.timeRemaining / 1000, 'seconds').humanize()
      remaining = remaining[0].toUpperCase() + remaining.substring(1) + ' remaining.'
    }
    $remaining.innerHTML = remaining
  }

  function onDone () {
    $body.className += ' is-seed'
    onProgress()
  }

  function onError (err) {
    if (err) {
      window.alert(err)
      console.error(err)
    }
  }

  function onWarning (err) {
    if (err) {
      console.error(err)
    }
  }

  function getRtcConfig (url, cb) {
    xhr(url, function (err, res) {
      if (err || res.statusCode !== 200) {
        cb(new Error('Could not get WebRTC config from server. Using default (without TURN).'))
      } else {
        var rtcConfig
        try {
          rtcConfig = JSON.parse(res.body)
        } catch (err) {
          return cb(new Error('Got invalid WebRTC config from server: ' + res.body))
        }
        debug('got rtc config: %o', rtcConfig)
        cb(null, rtcConfig)
      }
    })
  }
}
<commit_msg>throttle to 250ms<commit_after>var debug = require('debug')('webtorrent-www:home')
var fs = require('fs')
var moment = require('moment')
var path = require('path')
var prettyBytes = require('pretty-bytes')
var throttle = require('throttleit')
var TorrentGraph = require('../lib/torrent-graph')
var WebTorrent = require('webtorrent')
var xhr = require('xhr')

var TORRENT = fs.readFileSync(
  path.join(__dirname, '../../static/torrents/sintel.torrent')
)

module.exports = function () {
  var graph = window.graph = new TorrentGraph('#svgWrap')

  getRtcConfig('https://instant.io/rtcConfig', function (err, rtcConfig) {
    if (err) onError(err)
    createClient(rtcConfig)
  })

  var torrent
  function createClient (rtcConfig) {
    var client = window.client = new WebTorrent({ rtcConfig: rtcConfig })
    client.on('warning', onWarning)
    client.on('error', onError)

    torrent = client.add(TORRENT, onTorrent)
    graph.add({ id: 'You', me: true })
  }

  var $body = document.body
  var $progressBar = document.querySelector('#progressBar')
  var $numPeers = document.querySelector('#numPeers')
  var $downloaded = document.querySelector('#downloaded')
  var $total = document.querySelector('#total')
  var $remaining = document.querySelector('#remaining')

  function onTorrent () {
    torrent.files[0].appendTo('#videoWrap .video', onError)
    torrent.on('wire', onWire)
    torrent.on('done', onDone)

    torrent.on('download', throttle(onProgress, 250))
    torrent.on('upload', throttle(onProgress, 250))
    setInterval(onProgress, 5000)
    onProgress()
  }

  function onWire (wire) {
    var id = wire.peerId.toString()
    graph.add({ id: id, ip: wire.remoteAddress || 'Unknown' })
    graph.connect('You', id)
    wire.once('close', function () {
      graph.disconnect('You', id)
      graph.remove(id)
    })
  }

  function onProgress () {
    var percent = Math.round(torrent.progress * 100 * 100) / 100
    $progressBar.style.width = percent + '%'
    $numPeers.innerHTML = torrent.numPeers + (torrent.numPeers === 1 ? ' peer' : ' peers')

    $downloaded.innerHTML = prettyBytes(torrent.downloaded)
    $total.innerHTML = prettyBytes(torrent.length)

    var remaining
    if (torrent.done) {
      remaining = 'Done.'
    } else {
      remaining = moment.duration(torrent.timeRemaining / 1000, 'seconds').humanize()
      remaining = remaining[0].toUpperCase() + remaining.substring(1) + ' remaining.'
    }
    $remaining.innerHTML = remaining
  }

  function onDone () {
    $body.className += ' is-seed'
    onProgress()
  }

  function onError (err) {
    if (err) {
      window.alert(err)
      console.error(err)
    }
  }

  function onWarning (err) {
    if (err) {
      console.error(err)
    }
  }

  function getRtcConfig (url, cb) {
    xhr(url, function (err, res) {
      if (err || res.statusCode !== 200) {
        cb(new Error('Could not get WebRTC config from server. Using default (without TURN).'))
      } else {
        var rtcConfig
        try {
          rtcConfig = JSON.parse(res.body)
        } catch (err) {
          return cb(new Error('Got invalid WebRTC config from server: ' + res.body))
        }
        debug('got rtc config: %o', rtcConfig)
        cb(null, rtcConfig)
      }
    })
  }
}
<|endoftext|>