<commit_before>(function () {
  'use strict';

  var _ = require('underscore');
  var glob = require('glob');
  var async = require('async');

  var deepExtend = require('./deep-extend');
  var noop = require('./noop');

  // Makes `require()` parse YAML files
  require('js-yaml');

  function getFilenames(path, callback) {
    if (typeof path !== 'string' && path.length) {
      path = _.flatten(path);

      var tasks = _.map(path, function (p) {
        return function (done) {
          getFilenames(p, function (err, r) {
            done(err, r);
          });
        };
      });

      async.series(tasks, function (err, filenames) {
        callback(err, _.flatten(filenames));
      });

      return;
    }

    if (typeof path === 'string') {
      glob(path, callback);
      return;
    }

    callback(null, []);
  }

  function load(path) {
    var callback = arguments[arguments.length - 1];

    if (typeof callback === 'function') {
      path = Array.prototype.slice.call(arguments, 0, -1);
    } else {
      callback = noop;
      path = Array.prototype.slice.call(arguments, 0);
    }

    getFilenames(path, function (err, filenames) {
      if (err) { return callback(err, null); }

      var formats = {};
      var len, i;

      for (i = 0, len = filenames.length; i < len; i += 1) {
        deepExtend(formats, require(filenames[i]));
      }

      callback(null, formats);
    });
  }

  exports = module.exports = load;
})();
<commit_msg>Dropped support for YAML files for now<commit_after>(function () {
  'use strict';

  var _ = require('underscore');
  var glob = require('glob');
  var async = require('async');

  var deepExtend = require('./deep-extend');
  var noop = require('./noop');

  function getFilenames(path, callback) {
    if (typeof path !== 'string' && path.length) {
      path = _.flatten(path);

      var tasks = _.map(path, function (p) {
        return function (done) {
          getFilenames(p, function (err, r) {
            done(err, r);
          });
        };
      });

      async.series(tasks, function (err, filenames) {
        callback(err, _.flatten(filenames));
      });

      return;
    }

    if (typeof path === 'string') {
      glob(path, callback);
      return;
    }

    callback(null, []);
  }

  function load(path) {
    var callback = arguments[arguments.length - 1];

    if (typeof callback === 'function') {
      path = Array.prototype.slice.call(arguments, 0, -1);
    } else {
      callback = noop;
      path = Array.prototype.slice.call(arguments, 0);
    }

    getFilenames(path, function (err, filenames) {
      if (err) { return callback(err, null); }

      var formats = {};
      var len, i;

      for (i = 0, len = filenames.length; i < len; i += 1) {
        deepExtend(formats, require(filenames[i]));
      }

      callback(null, formats);
    });
  }

  exports = module.exports = load;
})();
<|endoftext|>


<commit_before>'use strict';
var fs = require("fs");
var async = require("async");
var request = require("request");
var hostModel = require("../../models/host.js");
const utils = require("../../utils/utils.js");

function checkReturnError(soajs, mainCb, data, cb) {
	utils.checkErrorReturn(soajs, mainCb, data, cb);
}

var BL = {
	model: null,

	"list": function (config, soajs, cbMain) {
		hostModel.getEnvironment(soajs, BL.model, soajs.inputmaskData.env, function (err, envRecord) {
			checkReturnError(soajs, cbMain, { config: config, error: err || !envRecord, code: 600 }, function () {
				hostModel.getHosts(soajs, BL.model, envRecord.code, function (err, hosts) {
					checkReturnError(soajs, cbMain, { config: config, error: err, code: 600 }, function () {
						return cbMain(null, {
							'hosts': hosts,
							'deployer': envRecord.deployer,
							'profile': envRecord.profile
						});
					});
				});
			});
		});
	},

	/*
	 * This function will return all the environments where a service is deployed.
	 * it takes the service name and renders an object having the following form :
	 * "env_code: apiPrefix.domain"
	 * @ param serviceName
	 * @ param getHostEnv
	 * @ param response
	 */
	"listHostEnv": function (config, soajs, deployer, helpers, cbMain) {
		BL.listHAhostEnv(config, soajs, deployer, helpers, (error, response) => {
			if(error){
				return cb(error);
			}
			else if(response){
				return cbMain(null, response);
			}
			else {
					hostModel.getHostEnv(soajs, BL.model, function (error, envs) {
						checkReturnError(soajs, cbMain, {config: config, error: error, code: 600}, function () {
							checkReturnError(soajs, cbMain, {
								config: config,
								error: envs.length === 0,
								code: 621
							}, function () {
								async.map(envs, function (oneEnv, callback) {
									var opts = {
										"collection": "environment",
										"conditions": {
											"$and": [
												{
													"deployer.type": "manual"
												},
												{
													"$or": [
														{
															"sensitive": {"$exists": false}
														},
														{
															"sensitive": false
														}
													]
												}
											]
										}
									};
									BL.model.findEntry(soajs, opts, function (error, environment) {
										if (error) {
											return callback(error);
										}
										if (environment) {
											return callback(null, oneEnv.toUpperCase());
										}

										return callback(null, null);
									});
								}, function (error, envsList) {

									for (var i = envsList.length - 1; i >= 0; i--) {
										if (!envsList[i]) {
											envsList.splice(i, 1);
										}
									}

									hostModel.getEnvInfo(soajs, BL.model, {envList: envsList}, function (error, result) {
										checkReturnError(soajs, cbMain, {
											config: config,
											error: error,
											code: 600
										}, function () {
											var response = {};
											result.forEach(function (oneEnvRec) {
												if (!response[oneEnvRec.code.toLowerCase()]) {
													response[oneEnvRec.code.toLowerCase()] = {};
												}
												if (oneEnvRec.domain === '') {
													output[oneEnvRec.code.toLowerCase()]['domain'] = oneEnvRec.deployer.manual.nodes;
												}
												else {
													response[oneEnvRec.code.toLowerCase()]['domain'] = oneEnvRec.apiPrefix + "." + oneEnvRec.domain;
												}
											});
											//grab the list of tenant that have ext key in requested env and return tenant code, package nad key as part of response
											helpers.getTenants(soajs, response, BL.model, function (error) {
												checkReturnError(soajs, cbMain, {
													config: config,
													error: error
												}, function () {
													return cbMain(null, response);
												});
											});
										});
									});
								});
							});
						});
					});
			}
		});
	},

	"listHAhostEnv": function (config, soajs, deployer, helpers, cbMain) {
		var output = {};

		//fetch the non sensitive environments only
		var opts = {
			"collection": "environment",
			"conditions": {
				"$and": [
					{
						"deployer.type": "container"
					},
					{
						"$or": [
							{
								"sensitive": { "$exists": false }
							},
							{
								"sensitive": false
							},
							{
								"sensitive": null
							}
						]
					}
				]
			}
		};
		BL.model.findEntries(soajs, opts, function (error, environments) {
			checkReturnError(soajs, cbMain, { config: config, error: error, code: 600 }, function () {
				if (!environments || environments.length === 0) {
					return cbMain(null, output);
				}

				async.eachSeries(environments, function (oneEnv, mCb) {
					//NOTE: need to get registry for environment, deployer depends on registry not environment record
					//NOTE: can't get registries without listing environments, no other way to get the list of available registries
					var options = helpers.buildDeployerOptions(oneEnv, soajs, BL.model);
					checkReturnError(soajs, cbMain, { config: config, error: !options, code: 825 }, function () {
						options.params = {
							env: oneEnv.code.toLowerCase()
						};
						let port = oneEnv.port;

						deployer.execute({'type': 'container', 'driver': options.strategy}, 'listServices', options, (error, services) =>{
							checkReturnError(soajs, cbMain, { config: config, error: error }, function () {

								//loop in services and see if you find the one you want only
								for (var i = 0; i < services.length; i++) {
									var oneService = services[i];
									if (soajs.inputmaskData.service === oneService.labels["soajs.service.name"]) {
										if (!output[oneEnv.code.toLowerCase()]) {
											output[oneEnv.code.toLowerCase()] = {};
										}

										oneService.ports.forEach(function (onePort) {
											if (onePort.published) {
												port = onePort.published;
											}
										});

										if(oneEnv.domain === ''){
											let deployerInfo = oneEnv.deployer.selected.split(".");
											if(deployerInfo[1] === 'docker' && deployerInfo[2] === 'local'){
												output[oneEnv.code.toLowerCase()]['domain'] = "localhost";
											}
											else{
												if(oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]]){
													if(oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]].nodes && oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]].nodes !== ''){
														output[oneEnv.code.toLowerCase()]['domain'] = oneEnv.nodes;
													}
												}
											}
										}
										else{
											output[oneEnv.code.toLowerCase()]['domain'] = oneEnv.apiPrefix + "." + oneEnv.domain;
										}
										break;
									}
								}

								if(!port){
									services.forEach(function (oneService) {
										if (output[oneEnv.code.toLowerCase()] && oneService.labels['soajs.service.name'] === 'controller' && oneService.labels['soajs.env.code'] === oneEnv.code.toLowerCase()) {
											//todo: map domain to inner property
											oneService.ports.forEach(function (onePort) {
												if (onePort.published) {
													port = onePort.published;
												}
											});
										}
									});

									if(!port){
										if(process.env.SOAJS_SAAS && oneEnv.deployer && oneEnv.deployer.selected === 'container.kubernetes.remote'){
											if(oneEnv.deployer.container && oneEnv.deployer.container.kubernetes && oneEnv.deployer.container.kubernetes.remote && oneEnv.deployer.container.kubernetes.remote.nginxDeployType === 'LoadBalancer'){
												port = oneEnv.port;
											}
										}
										else {
											services.forEach(function (oneService) {
												if (output[oneEnv.code.toLowerCase()] && oneService.labels['soajs.service.group'] === 'soajs-nginx' && oneService.labels['soajs.env.code'] === oneEnv.code.toLowerCase()) {
													//todo: map domain to inner property
													oneService.ports.forEach(function (onePort) {
														if (onePort.published) {
															port = onePort.published;
														}
													});
												}
											});
										}
									}
								}

								if(output[oneEnv.code.toLowerCase()]){
									output[oneEnv.code.toLowerCase()]['domain'] += ":" + port;
								}
								return mCb(null, true);
							});
						});
					});
				}, function (error) {
					checkReturnError(soajs, cbMain, { config: config, error: error, code: 600 }, function () {
						//grab the list of tenant that have ext key in requested env and return tenant code, package nad key as part of response
						helpers.getTenants(soajs, output, BL.model, function (error) {
							checkReturnError(soajs, cbMain, { config: config, error: error }, function () {
								return cbMain(null, output);
							});
						});
					});
				});
			});
		});
	},

	"awareness": function (config, soajs, cbMain) {
		hostModel.getEnvironment(soajs, BL.model, soajs.inputmaskData.env, function (err, envRecord) {
			checkReturnError(soajs, cbMain, { config: config, error: err || !envRecord, code: 600 }, function () {
				hostModel.getControllers(soajs, BL.model, envRecord.code, function (err, controllers) {
					checkReturnError(soajs, cbMain, { config: config, error: err, code: 600 }, function () {
						return cbMain(null, controllers);
					});
				});
			});
		});
	}
};

module.exports = {
	"init": function (modelName, cb) {
		var modelPath;

		if (!modelName) {
			return cb(new Error("No Model Requested!"));
		}

		modelPath = __dirname + "/../../models/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				BL.model = require(filePath);
				return cb(null, BL);
			});
		}
	}
};
<commit_msg>updates in api logic to return the port when a load balancer was used.<commit_after>'use strict';
var fs = require("fs");
var async = require("async");
var request = require("request");
var hostModel = require("../../models/host.js");
const utils = require("../../utils/utils.js");

function checkReturnError(soajs, mainCb, data, cb) {
	utils.checkErrorReturn(soajs, mainCb, data, cb);
}

var BL = {
	model: null,

	"list": function (config, soajs, cbMain) {
		hostModel.getEnvironment(soajs, BL.model, soajs.inputmaskData.env, function (err, envRecord) {
			checkReturnError(soajs, cbMain, {config: config, error: err || !envRecord, code: 600}, function () {
				hostModel.getHosts(soajs, BL.model, envRecord.code, function (err, hosts) {
					checkReturnError(soajs, cbMain, {config: config, error: err, code: 600}, function () {
						return cbMain(null, {
							'hosts': hosts,
							'deployer': envRecord.deployer,
							'profile': envRecord.profile
						});
					});
				});
			});
		});
	},

	/*
	 * This function will return all the environments where a service is deployed.
	 * it takes the service name and renders an object having the following form :
	 * "env_code: apiPrefix.domain"
	 * @ param serviceName
	 * @ param getHostEnv
	 * @ param response
	 */
	"listHostEnv": function (config, soajs, deployer, helpers, cbMain) {
		BL.listHAhostEnv(config, soajs, deployer, helpers, (error, response) => {
			if (error) {
				return cb(error);
			}
			else if (response) {
				return cbMain(null, response);
			}
			else {
				hostModel.getHostEnv(soajs, BL.model, function (error, envs) {
					checkReturnError(soajs, cbMain, {config: config, error: error, code: 600}, function () {
						checkReturnError(soajs, cbMain, {
							config: config,
							error: envs.length === 0,
							code: 621
						}, function () {
							async.map(envs, function (oneEnv, callback) {
								var opts = {
									"collection": "environment",
									"conditions": {
										"$and": [
											{
												"deployer.type": "manual"
											},
											{
												"$or": [
													{
														"sensitive": {"$exists": false}
													},
													{
														"sensitive": false
													}
												]
											}
										]
									}
								};
								BL.model.findEntry(soajs, opts, function (error, environment) {
									if (error) {
										return callback(error);
									}
									if (environment) {
										return callback(null, oneEnv.toUpperCase());
									}

									return callback(null, null);
								});
							}, function (error, envsList) {

								for (var i = envsList.length - 1; i >= 0; i--) {
									if (!envsList[i]) {
										envsList.splice(i, 1);
									}
								}

								hostModel.getEnvInfo(soajs, BL.model, {envList: envsList}, function (error, result) {
									checkReturnError(soajs, cbMain, {
										config: config,
										error: error,
										code: 600
									}, function () {
										var response = {};
										result.forEach(function (oneEnvRec) {
											if (!response[oneEnvRec.code.toLowerCase()]) {
												response[oneEnvRec.code.toLowerCase()] = {};
											}
											if (oneEnvRec.domain === '') {
												output[oneEnvRec.code.toLowerCase()]['domain'] = oneEnvRec.deployer.manual.nodes;
											}
											else {
												response[oneEnvRec.code.toLowerCase()]['domain'] = oneEnvRec.apiPrefix + "." + oneEnvRec.domain;
											}
										});
										//grab the list of tenant that have ext key in requested env and return tenant code, package nad key as part of response
										helpers.getTenants(soajs, response, BL.model, function (error) {
											checkReturnError(soajs, cbMain, {
												config: config,
												error: error
											}, function () {
												return cbMain(null, response);
											});
										});
									});
								});
							});
						});
					});
				});
			}
		});
	},

	"listHAhostEnv": function (config, soajs, deployer, helpers, cbMain) {
		var output = {};

		//fetch the non sensitive environments only
		var opts = {
			"collection": "environment",
			"conditions": {
				"$and": [
					{
						"deployer.type": "container"
					},
					{
						"$or": [
							{
								"sensitive": {"$exists": false}
							},
							{
								"sensitive": false
							},
							{
								"sensitive": null
							}
						]
					}
				]
			}
		};
		BL.model.findEntries(soajs, opts, function (error, environments) {
			checkReturnError(soajs, cbMain, {config: config, error: error, code: 600}, function () {
				if (!environments || environments.length === 0) {
					return cbMain(null, output);
				}

				async.eachSeries(environments, function (oneEnv, mCb) {
					//NOTE: need to get registry for environment, deployer depends on registry not environment record
					//NOTE: can't get registries without listing environments, no other way to get the list of available registries
					var options = helpers.buildDeployerOptions(oneEnv, soajs, BL.model);
					checkReturnError(soajs, cbMain, {config: config, error: !options, code: 825}, function () {
						options.params = {
							env: oneEnv.code.toLowerCase()
						};
						let port;

						deployer.execute({
							'type': 'container',
							'driver': options.strategy
						}, 'listServices', options, (error, services) => {
							checkReturnError(soajs, cbMain, {config: config, error: error}, function () {

								BL.model.findEntry(soajs, {
									collection: "infra",
									conditions: { "deployments.environments": {"$in": [ oneEnv.code.toUpperCase() ] } }
								}, (error, infraProvider) => {
									checkReturnError(soajs, cbMain, {config: config, error: error}, function () {

										let detectedDeployment;
										infraProvider.deployments.forEach((oneDeployment) => {
											if (oneDeployment.environments.indexOf(oneEnv.code.toUpperCase()) !== -1) {
												detectedDeployment = oneDeployment;
											}
										});

										//loop in services and see if you find the one you want only
										for (var i = 0; i < services.length; i++) {
											var oneService = services[i];
											if (soajs.inputmaskData.service === oneService.labels["soajs.service.name"]) {
												if (!output[oneEnv.code.toLowerCase()]) {
													output[oneEnv.code.toLowerCase()] = {};
												}

												oneService.ports.forEach(function (onePort) {
													if (onePort.published) {
														port = onePort.published;
													}
												});

												if (oneEnv.domain === '') {
													let deployerInfo = oneEnv.deployer.selected.split(".");
													if (deployerInfo[1] === 'docker' && deployerInfo[2] === 'local') {
														output[oneEnv.code.toLowerCase()]['domain'] = "localhost";
													}
													else {
														if (oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]]) {
															if (oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]].nodes && oneEnv.deployer[deployerInfo[0]][deployerInfo[1]][deployerInfo[2]].nodes !== '') {
																output[oneEnv.code.toLowerCase()]['domain'] = oneEnv.nodes;
															}
														}
													}
												}
												else {
													output[oneEnv.code.toLowerCase()]['domain'] = oneEnv.apiPrefix + "." + oneEnv.domain;
												}
												break;
											}
										}

										if (!port) {
											services.forEach(function (oneService) {
												if (output[oneEnv.code.toLowerCase()] && oneService.labels['soajs.service.name'] === 'controller' && oneService.labels['soajs.env.code'] === oneEnv.code.toLowerCase()) {
													//todo: map domain to inner property
													oneService.ports.forEach(function (onePort) {
														if (onePort.published) {
															port = onePort.published;
														}
													});
												}
											});

											if (!port) {
												if (process.env.SOAJS_SAAS && oneEnv.deployer && oneEnv.deployer.selected === 'container.kubernetes.remote') {
													if (oneEnv.deployer.container && oneEnv.deployer.container.kubernetes && oneEnv.deployer.container.kubernetes.remote && oneEnv.deployer.container.kubernetes.remote.nginxDeployType === 'LoadBalancer') {
														port = oneEnv.port;
													}
												}
												else {
													services.forEach(function (oneService) {
														if (output[oneEnv.code.toLowerCase()] && oneService.labels['soajs.service.group'] === 'soajs-nginx' && oneService.labels['soajs.env.code'] === oneEnv.code.toLowerCase()) {
															if(detectedDeployment){
																oneService.ports.forEach(function (onePort) {
																	detectedDeployment.loadBalancers[oneEnv.code.toUpperCase()][oneService.labels['soajs.service.name']].ports.forEach((lbPorts) => {
																		if(lbPorts.published === onePort.published){
																			onePort.published = lbPorts.target
																		}
																	});
																});
															}
															//todo: map domain to inner property
															oneService.ports.forEach(function (onePort) {
																if (!port && onePort.published) {
																	port = onePort.published;
																}
															});
														}
													});
												}
											}
										}

										if(!port && oneEnv.port){
											port = oneEnv.port;
										}

										if (output[oneEnv.code.toLowerCase()]) {
											output[oneEnv.code.toLowerCase()]['domain'] += ":" + port;
										}
										return mCb(null, true);
									});
								});
							});
						});
					});
				}, function (error) {
					checkReturnError(soajs, cbMain, {config: config, error: error, code: 600}, function () {
						//grab the list of tenant that have ext key in requested env and return tenant code, package nad key as part of response
						helpers.getTenants(soajs, output, BL.model, function (error) {
							checkReturnError(soajs, cbMain, {config: config, error: error}, function () {
								return cbMain(null, output);
							});
						});
					});
				});
			});
		});
	},

	"awareness": function (config, soajs, cbMain) {
		hostModel.getEnvironment(soajs, BL.model, soajs.inputmaskData.env, function (err, envRecord) {
			checkReturnError(soajs, cbMain, {config: config, error: err || !envRecord, code: 600}, function () {
				hostModel.getControllers(soajs, BL.model, envRecord.code, function (err, controllers) {
					checkReturnError(soajs, cbMain, {config: config, error: err, code: 600}, function () {
						return cbMain(null, controllers);
					});
				});
			});
		});
	}
};

module.exports = {
	"init": function (modelName, cb) {
		var modelPath;

		if (!modelName) {
			return cb(new Error("No Model Requested!"));
		}

		modelPath = __dirname + "/../../models/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				BL.model = require(filePath);
				return cb(null, BL);
			});
		}
	}
};
<|endoftext|>


<commit_before>(function() {

    var concatPattern = /\s*[\s,|]+\s*/g;

    module.exports = {
        active: false,
        neverClose: [],
        forceInline: [],
        forceBlock: [],
        makeNeverCloseSelfClosing: false,
        ignoreGrammar: false,
        configDefaults: {
            closeOnEndOfOpeningTag: false,
            neverClose: 'br, hr, img, input, link, meta, area, base, col, command, embed, keygen, param, source, track, wbr',
            makeNeverCloseElementsSelfClosing: false,
            forceInline: 'title, h1, h2, h3, h4, h5, h6',
            forceBlock: '',
            ignoreGrammar: false
        },
        activate: function() {
            var self = this;
            atom.workspaceView.command( 'html-helper:closeElement', self.closeElement );
        },
        closeElement: function() {
            var self = this,
                lines = atom.workspace.activePaneItem.buffer.lines,
                lineIndex = atom.workspace.activePaneItem.buffer.markers.markers['1'].range.end.row,
                line = lines[lineIndex],
                open = '<',
                close = '>',
                indexStart = -1,
                indexEnd = -1,
                linePos = 0,
                el,
                element,
                elementRegex;

            while( linePos < line.length ){
                indexStart = line.indexOf(open, linePos)+1;
                indexEnd = line.indexOf(close, indexStart)
                el = line.substring(indexStart, indexEnd);
                elementRegex = '<('+el+')\\b[^>]*>(.*)</\\1>';
                element = line.match(elementRegex); //output ["
TEST
", "h1", "TEST", index: 12, input: "
TEST

"]
                if( element === null ){
                    //close element
                    elCloseStr = '</' + el + '>';
                    //stop loop
                    linePos = line.length;
                    //insert closing tag
                    atom.workspace.activePaneItem.insertText( elCloseStr );
                    //move cursor
                    atom.workspace.activePaneItem.setCursorBufferPosition( [lineIndex, linePos] );
                }else{
                    //element closed, add length to linePos, try again
                    linePos = element.index + String(element[0]).length;
                }
            }
        }
    };

})();
<commit_msg>remove unused vars<commit_after>(function() {

    var concatPattern = /\s*[\s,|]+\s*/g;

    module.exports = {
        active: false,
        activate: function() {
            var self = this;
            atom.workspaceView.command( 'html-helper:closeElement', self.closeElement );
        },
        closeElement: function() {
            var self = this,
                lines = atom.workspace.activePaneItem.buffer.lines,
                lineIndex = atom.workspace.activePaneItem.buffer.markers.markers['1'].range.end.row,
                line = lines[lineIndex],
                open = '<',
                close = '>',
                indexStart = -1,
                indexEnd = -1,
                linePos = 0,
                el,
                element,
                elementRegex;

            while( linePos < line.length ){
                indexStart = line.indexOf(open, linePos)+1;
                indexEnd = line.indexOf(close, indexStart)
                el = line.substring(indexStart, indexEnd);
                elementRegex = '<('+el+')\\b[^>]*>(.*)</\\1>';
                element = line.match(elementRegex); //output ["
TEST
", "h1", "TEST", index: 12, input: "
TEST

"]
                if( element === null ){
                    //close element
                    elCloseStr = '</' + el + '>';
                    //stop loop
                    linePos = line.length;
                    //insert closing tag
                    atom.workspace.activePaneItem.insertText( elCloseStr );
                    //move cursor
                    atom.workspace.activePaneItem.setCursorBufferPosition( [lineIndex, linePos] );
                }else{
                    //element closed, add length to linePos, try again
                    linePos = element.index + String(element[0]).length;
                }
            }
        }
    };

})();
<|endoftext|>


<commit_before>(function ($, $$) {

    "use strict";

    var action = "share", shipment;

    var shipments = {
            123:
            {id : 123, status : "Waiting",  description : "Cellular Accessories and Phones",  date: "12/20/2013",
                trucks : [
                    {truck : "Box Truck 304", driverNumber : "D4702",
                        departs : {city : "Denver, CO", day : "12/18/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/20/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 512", driverNumber : "D0123",
                        departs : {city : "Los Angeles, CA", day : "12/19/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "12/19/2013", time : "5:00 PM"}}
                ],
                destination : {name : "The Tech Store", address : {street : "55 Fourth Street", city : "San Francisco", state : "CA", zip : "94103"},
                total : "$23500.00"
            },
            124:
            {id : 124, status : "Approved", description : "Laptop Supplies",  date: "09/10/2013",
                trucks : [
                    {truck : "Bus 005", driverNumber : "D2702",
                        departs : {city : "Austin, TX", day : "09/5/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "09:30 AM"}},
                    {truck : "Lowboy 201", driverNumber : "UA123",
                        departs : {city : "Las Vegas, NV", day : "09/8/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "5:00 PM"}}
                ],
                destination : {name : "DigiMedia", address : {street : "150 Van Ness St", city : "San Francisco", state : "CA", zip : "94134"},
                total : "$17750.00"
            },
            125:
            {id : 125, status : "Complete", description : "Tablets",  date: "06/18/2013",
                trucks : [
                    {truck : "Box Truck 117", driverNumber : "D6702",
                        departs : {city : "Salt Lake City, UT", day : "06/14/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "06/18/2013", time : "09:30 AM"}}
                ],
                destination : {name : "Munder Difflin Paperless Company", address : {street : "160 Brannan St", city : "San Francisco", state : "CA", zip : "94103"},
                total : "$13000.00"
            },
            126:
            {id : 126, status : "Complete", description : "TVs, Projectors, and Desktops, oh my!",  date: "04/02/2013",
                trucks : [
                    {truck : "Bus 006", driverNumber : "D2202",
                        departs : {city : "Portland, OR", day : "04/02/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "04/05/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 215", driverNumber : "UA123",
                        departs : {city : "San Diego, CA", day : "04/10/2013", time : "03:00 PM"},
                        arrives : {city : "San Francisco, CA", day : "04/12/2013", time : "5:30 PM"}}
                ],
                destination : {name : "Salesforce", address : {street : "1 Market St", city : "San Francisco", state : "CA", zip : "94105"},
                total : "$3300.00"
            }
        };

    var module = {

        instance : function (sr) {
            var payload;

            function refresh(id) {
                var v, $tbody;
                if (!$$.isNil(id)) {
                    $tbody = $("#reservation tbody");
                    v = shipments[id];
                    $tbody.append(
                        "
" + v.status  +
                            "	" + v.description + "	" + v.total + "	" + v.date + "
");
                }
                else {
                    $tbody = $("#reservations tbody");
                    $.each(shipments, function(i, v) {
                        $tbody.append(
                            "
" + v.status  +
                                "	" + v.description + "	" + v.date + "
");
                    });
                }
            }

            function prettyPrint(id) {

                var it = shipments[id];

                var text = "My Shipment For: " + it.description + "\n";
                	text += "Status: " + it.status + "\n";
                	text += "Date: " + it.date + "\n\n";
                    for (var i = 0; i < it.trucks.length; i++) {
                        var f = it.trucks[i];
                        text += "Truck: " + f.truck + " " + f.driverNumber + "- " + f.departs.city +"\n";
                        text += "Departs: " + f.departs.city + ": " + f.departs.day + " " + f.departs.time + "\n";
                        text += "Arrives: " + f.arrives.city + ": " + f.departs.day + " " + f.arrives.time + "\n\n";
                    }
                    text += "Hotel: " + it.destination.name + ", " + it.destination.address.street + ", " + it.destination.address.city + ", " + it.destination.address.state + "\n";
                    text += "Room " + it.destination.rooms + ", Days " + it.destination.days + ", Guests 1\n";
                    text += "Daily Rate: " + it.destination.rate + "\n";
                    text += "Check In: " + it.destination.checkin;

                return text;
            }

            function approve(id) {

                $(function() {
                    $("button" )
                        .button()
                        .click(function( event ) {
                            event.preventDefault();
                            action = $(this).attr("id");
                            $("#status").empty().append(("approve" === action) ? "Approved" : "Denied");
                        });
                });

                console.log("Approve...." + id);
                refresh(id);
            }

            function draw() {

                $("#reservations").chromatable({
                    width: "100%",
                    height: "100%",
                    scrolling: "yes"
                });

                $("#reservations").selectable({
                    filter:'tr',
                    selected: function(event, ui){
                        var s = $(this).find('.ui-selected');
                        shipment = s[0].id;
                        console.log("Shipment: " + shipment);

                        // @review - generate named methods instead (fireEnable())
                        console.log("Fire event from client");
                        $$.client.publish(sr.client, {name : 'publisher.setValidForSubmit', payload : true});
                    }
                });

                $( "#radio" ).buttonset();
                $("#radio :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                $( "#switch" ).buttonset();
                $("#switch :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    var tempAction = $(this).attr("id");
                  	action = "share";
                    $('#reservations .ui-selected').removeClass('ui-selected')
                    console.log("Action: " + action);
                });

                $( "#radio2" ).buttonset();
				$("#radio2 :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                refresh();
            }

            var handlers = {
                onSetupPanel : function(payload) {
                	console.log("EH Module setupPanel..", payload);
                },
                onShowPanel : function(payload) {
                    console.log("EH Module showPanel", payload);
                },
                onClearPanelState : function(payload) {
                    console.log("EH Module clearPanelState");
                    // Clear the selected reservation
                    $('#reservations .ui-selected').removeClass('ui-selected')

                    // Clear the selected radio buttons
                    $('input[name="radio"]').prop('checked', false);

                    // Re enable the default selection
                    action = "share";
                    $('#share').prop('checked', true);
                    $("#radio :radio").button( "refresh");
                    $('#itin').prop('checked', true);
                },
                onSuccess : function() {
                    console.log("EH Module onSuccess");
                },
                onFailure : function () {
                    console.log("EH Module onFailure");
                },
                onGetPayload : function () {
                    var p = {};
                    console.log("EH Module getPayload");
                    if ("share" === action) {
                        p.feedItemType = "TextPost";
                        p.auxText = prettyPrint(shipment);
                    }
                    else if ("link" === action) {
                        p.feedItemType = "LinkPost";
                        p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                        p.url = "https://canvas-publisher-feed.herokuapp.com/signed-request.jsp?intinerary=" + shipment;
                        p.urlName = shipments[shipment].description;
                    }
                    else if ("approval" === action) {
                         p.feedItemType = "CanvasPost";
                         p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                         p.namespace =  sr.context.application.namespace;
                         p.developerName =  sr.context.application.developerName;
                         p.thumbnailUrl = "https://icons.iconarchive.com/icons/aha-soft/perfect-transport/48/Airplane-icon.png";
                         p.parameters =  "{\"shipment\":\"" + shipment + "\"}";
                         p.title =  "Shipment - " +shipments[shipment].description;
                         p.description = "This is a travel shipment for Shipment - " + shipments[shipment].description + ".  Click the link to open the Canvas App.";
                    }

                    // Note: we can extend the payload here to include more then just value.
                    $$.client.publish(sr.client, {name : 'publisher.setPayload', payload : p});
                }
            };

            var that = {

                draw : draw,
                refresh : refresh,
                approve : approve,

                // Subscriptions to callbacks from publisher...
                subscriptions : [
                    {name : 'publisher.setupPanel', onData : handlers.onSetupPanel},
                    {name : 'publisher.showPanel', onData : handlers.onShowPanel},
                    {name : 'publisher.clearPanelState',  onData : handlers.onClearPanelState},
                    {name : 'publisher.failure', onData : handlers.onFailure},
                    {name : 'publisher.success', onData : handlers.onSuccess},
                    {name : 'publisher.getPayload', onData : handlers.onGetPayload}
                ]
            };
            return that;
        }
    };

    // Replace with module pattern
    window.shipment = module;

}(jQuery, Sfdc.canvas));<commit_msg>second commit<commit_after>(function ($, $$) {

    "use strict";

    var action = "share", shipment;

    var shipments = {
            123:
            {id : 123, status : "Waiting",  description : "Cellular Accessories and Phones",  date: "12/20/2013",
                trucks : [
                    {truck : "Box Truck 304", driverNumber : "D4702",
                        departs : {city : "Denver, CO", day : "12/18/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/20/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 512", driverNumber : "D0123",
                        departs : {city : "Los Angeles, CA", day : "12/19/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "12/19/2013", time : "5:00 PM"}}
                ],
                destination : {name : "The Tech Store", address : {street : "55 Fourth Street", city : "San Francisco", state : "CA", zip : "94103"}},
                total : "$23500.00"
            },
            124:
            {id : 124, status : "Approved", description : "Laptop Supplies",  date: "09/10/2013",
                trucks : [
                    {truck : "Bus 005", driverNumber : "D2702",
                        departs : {city : "Austin, TX", day : "09/5/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "09:30 AM"}},
                    {truck : "Lowboy 201", driverNumber : "UA123",
                        departs : {city : "Las Vegas, NV", day : "09/8/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "5:00 PM"}}
                ],
                destination : {name : "DigiMedia", address : {street : "150 Van Ness St", city : "San Francisco", state : "CA", zip : "94134"}},
                total : "$17750.00"
            },
            125:
            {id : 125, status : "Complete", description : "Tablets",  date: "06/18/2013",
                trucks : [
                    {truck : "Box Truck 117", driverNumber : "D6702",
                        departs : {city : "Salt Lake City, UT", day : "06/14/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "06/18/2013", time : "09:30 AM"}}
                ],
                destination : {name : "Munder Difflin Paperless Company", address : {street : "160 Brannan St", city : "San Francisco", state : "CA", zip : "94103"}},
                total : "$13000.00"
            },
            126:
            {id : 126, status : "Complete", description : "TVs, Projectors, and Desktops, oh my!",  date: "04/02/2013",
                trucks : [
                    {truck : "Bus 006", driverNumber : "D2202",
                        departs : {city : "Portland, OR", day : "04/02/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "04/05/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 215", driverNumber : "UA123",
                        departs : {city : "San Diego, CA", day : "04/10/2013", time : "03:00 PM"},
                        arrives : {city : "San Francisco, CA", day : "04/12/2013", time : "5:30 PM"}}
                ],
                destination : {name : "Salesforce", address : {street : "1 Market St", city : "San Francisco", state : "CA", zip : "94105"}},
                total : "$3300.00"
            }
        };

    var module = {

        instance : function (sr) {
            var payload;

            function refresh(id) {
                var v, $tbody;
                if (!$$.isNil(id)) {
                    $tbody = $("#reservation tbody");
                    v = shipments[id];
                    $tbody.append(
                        "
" + v.status  +
                            "	" + v.description + "	" + v.total + "	" + v.date + "
");
                }
                else {
                    $tbody = $("#reservations tbody");
                    $.each(shipments, function(i, v) {
                        $tbody.append(
                            "
" + v.status  +
                                "	" + v.description + "	" + v.date + "
");
                    });
                }
            }

            function prettyPrint(id) {

                var it = shipments[id];

                var text = "My Shipment For: " + it.description + "\n";
                	text += "Status: " + it.status + "\n";
                	text += "Date: " + it.date + "\n\n";
                    for (var i = 0; i < it.trucks.length; i++) {
                        var f = it.trucks[i];
                        text += "Truck: " + f.truck + " " + f.driverNumber + "- " + f.departs.city +"\n";
                        text += "Departs: " + f.departs.city + ": " + f.departs.day + " " + f.departs.time + "\n";
                        text += "Arrives: " + f.arrives.city + ": " + f.departs.day + " " + f.arrives.time + "\n\n";
                    }
                    text += "Hotel: " + it.destination.name + ", " + it.destination.address.street + ", " + it.destination.address.city + ", " + it.destination.address.state + "\n";
                    text += "Room " + it.destination.rooms + ", Days " + it.destination.days + ", Guests 1\n";
                    text += "Daily Rate: " + it.destination.rate + "\n";
                    text += "Check In: " + it.destination.checkin;

                return text;
            }

            function approve(id) {

                $(function() {
                    $("button" )
                        .button()
                        .click(function( event ) {
                            event.preventDefault();
                            action = $(this).attr("id");
                            $("#status").empty().append(("approve" === action) ? "Approved" : "Denied");
                        });
                });

                console.log("Approve...." + id);
                refresh(id);
            }

            function draw() {

                $("#reservations").chromatable({
                    width: "100%",
                    height: "100%",
                    scrolling: "yes"
                });

                $("#reservations").selectable({
                    filter:'tr',
                    selected: function(event, ui){
                        var s = $(this).find('.ui-selected');
                        shipment = s[0].id;
                        console.log("Shipment: " + shipment);

                        // @review - generate named methods instead (fireEnable())
                        console.log("Fire event from client");
                        $$.client.publish(sr.client, {name : 'publisher.setValidForSubmit', payload : true});
                    }
                });

                $( "#radio" ).buttonset();
                $("#radio :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                $( "#switch" ).buttonset();
                $("#switch :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    var tempAction = $(this).attr("id");
                  	action = "share";
                    $('#reservations .ui-selected').removeClass('ui-selected')
                    console.log("Action: " + action);
                });

                $( "#radio2" ).buttonset();
				$("#radio2 :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                refresh();
            }

            var handlers = {
                onSetupPanel : function(payload) {
                	console.log("EH Module setupPanel..", payload);
                },
                onShowPanel : function(payload) {
                    console.log("EH Module showPanel", payload);
                },
                onClearPanelState : function(payload) {
                    console.log("EH Module clearPanelState");
                    // Clear the selected reservation
                    $('#reservations .ui-selected').removeClass('ui-selected')

                    // Clear the selected radio buttons
                    $('input[name="radio"]').prop('checked', false);

                    // Re enable the default selection
                    action = "share";
                    $('#share').prop('checked', true);
                    $("#radio :radio").button( "refresh");
                    $('#itin').prop('checked', true);
                },
                onSuccess : function() {
                    console.log("EH Module onSuccess");
                },
                onFailure : function () {
                    console.log("EH Module onFailure");
                },
                onGetPayload : function () {
                    var p = {};
                    console.log("EH Module getPayload");
                    if ("share" === action) {
                        p.feedItemType = "TextPost";
                        p.auxText = prettyPrint(shipment);
                    }
                    else if ("link" === action) {
                        p.feedItemType = "LinkPost";
                        p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                        p.url = "https://canvas-publisher-feed.herokuapp.com/signed-request.jsp?intinerary=" + shipment;
                        p.urlName = shipments[shipment].description;
                    }
                    else if ("approval" === action) {
                         p.feedItemType = "CanvasPost";
                         p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                         p.namespace =  sr.context.application.namespace;
                         p.developerName =  sr.context.application.developerName;
                         p.thumbnailUrl = "https://icons.iconarchive.com/icons/aha-soft/perfect-transport/48/Airplane-icon.png";
                         p.parameters =  "{\"shipment\":\"" + shipment + "\"}";
                         p.title =  "Shipment - " +shipments[shipment].description;
                         p.description = "This is a travel shipment for Shipment - " + shipments[shipment].description + ".  Click the link to open the Canvas App.";
                    }

                    // Note: we can extend the payload here to include more then just value.
                    $$.client.publish(sr.client, {name : 'publisher.setPayload', payload : p});
                }
            };

            var that = {

                draw : draw,
                refresh : refresh,
                approve : approve,

                // Subscriptions to callbacks from publisher...
                subscriptions : [
                    {name : 'publisher.setupPanel', onData : handlers.onSetupPanel},
                    {name : 'publisher.showPanel', onData : handlers.onShowPanel},
                    {name : 'publisher.clearPanelState',  onData : handlers.onClearPanelState},
                    {name : 'publisher.failure', onData : handlers.onFailure},
                    {name : 'publisher.success', onData : handlers.onSuccess},
                    {name : 'publisher.getPayload', onData : handlers.onGetPayload}
                ]
            };
            return that;
        }
    };

    // Replace with module pattern
    window.shipment = module;

}(jQuery, Sfdc.canvas));<|endoftext|>


<commit_before>(function() {
  'use strict';

  // 1 2 5 10 25 50 100 250 500 etc
  function monkeyNumber(i) {
    var adjust = [1, 2.5, 5];
    return Math.floor(Math.pow(10, Math.floor(i/3)) * adjust[i % 3]);
  }

  // the j such that monkeyNumber(j) is closest to i
  function monkeyIndex(i) {
    if(i < 1) { return 0; }
    var group = Math.floor(Math.log(i) / Math.LN10),
        offset = i/(2.5 * Math.pow(10, group));
    if(offset >= 3) {
      group++;
      offset = 0;
    }
    return 3*group + Math.round(Math.min(2, offset));
  }

  function closestMonkey(i) {
    if(i === Infinity) {
      return Infinity;
    }
    return monkeyNumber(monkeyIndex(i));
  }

  angular.module('begriffs.paginate-anything', []).

    directive('begriffs.pagination', function () {
      return {
        restrict: 'E',
        scope: {
          url: '=',
          headers: '&',
          collection: '=',

          page: '=?',
          perPage: '=?',
          perPagePresets: '=?',
          clientLimit: '=?',
          linkGroupSize: '=?',
          reloadPage: '=?',

          // directive -> app communication only
          numPages: '=?',
          numItems: '=?',
          serverLimit: '=?'
        },
        templateUrl: function(element, attr) {
          return attr.templateUrl || 'tpl/paginate-anything.html';
        },
        replace: true,
        controller: ['$scope', '$http', function($scope, $http) {

          $scope.reloadPage     = false;
          $scope.paginated      = false;
          $scope.serverLimit    = Infinity; // it's not known yet
          $scope.Math           = window.Math; // Math for the template

          var lgs = $scope.linkGroupSize, cl = $scope.clientLimit;
          $scope.linkGroupSize  = typeof(lgs) === 'number' ? lgs : 3;
          $scope.clientLimit    = closestMonkey(typeof(cl) === 'number' ? cl : 250);
          $scope.updatePresets  = function () {
            var presets = [];
            for(var i = Math.min(3, monkeyIndex($scope.perPage || 250));
                i <= monkeyIndex(Math.min($scope.clientLimit, $scope.serverLimit));
                i++) {
              presets.push(monkeyNumber(i));
            }
            $scope.perPagePresets = presets;
          };

          $scope.gotoPage = function (i) {
            if(i < 0 || i*$scope.perPage >= $scope.numItems) {
              return;
            }

            var pp = closestMonkey($scope.perPage || 100);
            $scope.page = i;
            requestRange({
              from: i * pp,
              to: (i+1) * pp - 1
            });
          };

          $scope.linkGroupFirst = function() {
            var rightDebt = Math.max( 0,
              $scope.linkGroupSize - ($scope.numPages - 1 - ($scope.page + 2))
            );
            return Math.max( 0,
              $scope.page - ($scope.linkGroupSize + rightDebt)
            );
          };

          $scope.linkGroupLast = function() {
            var leftDebt = Math.max( 0,
              $scope.linkGroupSize - ($scope.page - 2)
            );
            return Math.min( $scope.numPages-1,
              $scope.page + ($scope.linkGroupSize + leftDebt)
            );
          };

          $scope.isFinite = function() {
            return $scope.numPages < Infinity;
          };

          function requestRange(request) {
            $http({
              method: 'GET',
              url: $scope.url,
              headers: angular.extend(
                {}, $scope.headers,
                { 'Range-Unit': 'items', Range: [request.from, request.to].join('-') }
              )
            }).success(function (data, status, headers) {
              $scope.collection = data;

              var response = parseRange(headers('Content-Range'));

              $scope.numItems = response ? response.total : data.length;

              if(response) {
                if(length(response) < response.total) {
                  $scope.paginated = true;

                  if(
                    ( request.to < response.total - 1) ||
                    (response.to < response.total - 1 && response.total < request.to)
                  ) {
                    if(!$scope.perPage || length(response) < $scope.perPage) {
                      var idx = monkeyIndex(length(response));
                      if(monkeyNumber(idx) > length(response)) {
                        idx--;
                      }
                      $scope.serverLimit = monkeyNumber(idx);
                      $scope.perPage = $scope.Math.min(
                        $scope.serverLimit,
                        $scope.clientLimit
                      );
                    }
                  }
                }
                $scope.numPages = Math.ceil(response.total / ($scope.perPage || 1));
              }
            });
          }

          $scope.gotoPage($scope.page || 0);
          $scope.updatePresets();

          $scope.$watch('page', function(newPage, oldPage) {
            if(newPage !== oldPage) {
              $scope.gotoPage(newPage);
            }
          });

          $scope.$watch('perPage', function(newPp, oldPp) {
            if(typeof(oldPp) === 'number' && newPp !== oldPp) {
              var first = $scope.page * oldPp;
              var newPage = Math.floor(first / newPp);

              if($scope.page !== newPage) {
                $scope.page = newPage; // $digest() will trigger gotoPage
              } else { // sometimes upping perPage stays on a page (e.g. page 0)
                $scope.gotoPage($scope.page);
              }
            }
          });

          $scope.$watch('serverLimit', function(newLimit, oldLimit) {
            if(newLimit !== oldLimit) {
              $scope.updatePresets();
            }
          });

          $scope.$watch('url', function(newUrl, oldUrl) {
            if(newUrl !== oldUrl) {
              $scope.gotoPage(0);
            }
          });

          $scope.$watch('reloadPage', function(newVal, oldVal) {
            if(newVal === true && oldVal === false) {
              $scope.reloadPage = false;
              $scope.gotoPage($scope.page);
            }
          });

        }],
      };
    }).

    filter('makeRange', function() {
      // http://stackoverflow.com/a/14932395/3102996
      return function(input) {
        var lowBound, highBound;
        switch (input.length) {
          case 1:
            lowBound = 0;
            highBound = parseInt(input[0]) - 1;
            break;
          case 2:
            lowBound = parseInt(input[0]);
            highBound = parseInt(input[1]);
            break;
          default:
            return input;
        }
        var result = [];
        for (var i = lowBound; i <= highBound; i++) { result.push(i); }
        return result;
      };
    });

  function parseRange(hdr) {
    var m = hdr && hdr.match(/^(\d+)-(\d+)\/(\d+|\*)$/);
    if(!m) { return null; }
    return {
      from: +m[1],
      to: +m[2],
      total: m[3] === '*' ? Infinity : +m[3]
    };
  }

  function length(range) {
    return range.to - range.from + 1;
  }
}());
<commit_msg>Simplify gotoPage() to prevent duplicate Firefox queries<commit_after>(function() {
  'use strict';

  // 1 2 5 10 25 50 100 250 500 etc
  function monkeyNumber(i) {
    var adjust = [1, 2.5, 5];
    return Math.floor(Math.pow(10, Math.floor(i/3)) * adjust[i % 3]);
  }

  // the j such that monkeyNumber(j) is closest to i
  function monkeyIndex(i) {
    if(i < 1) { return 0; }
    var group = Math.floor(Math.log(i) / Math.LN10),
        offset = i/(2.5 * Math.pow(10, group));
    if(offset >= 3) {
      group++;
      offset = 0;
    }
    return 3*group + Math.round(Math.min(2, offset));
  }

  function closestMonkey(i) {
    if(i === Infinity) {
      return Infinity;
    }
    return monkeyNumber(monkeyIndex(i));
  }

  angular.module('begriffs.paginate-anything', []).

    directive('begriffs.pagination', function () {
      return {
        restrict: 'E',
        scope: {
          url: '=',
          headers: '&',
          collection: '=',

          page: '=?',
          perPage: '=?',
          perPagePresets: '=?',
          clientLimit: '=?',
          linkGroupSize: '=?',
          reloadPage: '=?',

          // directive -> app communication only
          numPages: '=?',
          numItems: '=?',
          serverLimit: '=?'
        },
        templateUrl: function(element, attr) {
          return attr.templateUrl || 'tpl/paginate-anything.html';
        },
        replace: true,
        controller: ['$scope', '$http', function($scope, $http) {

          $scope.reloadPage     = false;
          $scope.paginated      = false;
          $scope.serverLimit    = Infinity; // it's not known yet
          $scope.Math           = window.Math; // Math for the template

          var lgs = $scope.linkGroupSize, cl = $scope.clientLimit;
          $scope.linkGroupSize  = typeof(lgs) === 'number' ? lgs : 3;
          $scope.clientLimit    = closestMonkey(typeof(cl) === 'number' ? cl : 250);
          $scope.updatePresets  = function () {
            var presets = [];
            for(var i = Math.min(3, monkeyIndex($scope.perPage || 250));
                i <= monkeyIndex(Math.min($scope.clientLimit, $scope.serverLimit));
                i++) {
              presets.push(monkeyNumber(i));
            }
            $scope.perPagePresets = presets;
          };

          $scope.gotoPage = function (i) {
            $scope.page = i;
          };

          $scope.linkGroupFirst = function() {
            var rightDebt = Math.max( 0,
              $scope.linkGroupSize - ($scope.numPages - 1 - ($scope.page + 2))
            );
            return Math.max( 0,
              $scope.page - ($scope.linkGroupSize + rightDebt)
            );
          };

          $scope.linkGroupLast = function() {
            var leftDebt = Math.max( 0,
              $scope.linkGroupSize - ($scope.page - 2)
            );
            return Math.min( $scope.numPages-1,
              $scope.page + ($scope.linkGroupSize + leftDebt)
            );
          };

          $scope.isFinite = function() {
            return $scope.numPages < Infinity;
          };

          function requestRange(request) {
            $http({
              method: 'GET',
              url: $scope.url,
              headers: angular.extend(
                {}, $scope.headers,
                { 'Range-Unit': 'items', Range: [request.from, request.to].join('-') }
              )
            }).success(function (data, status, headers) {
              $scope.collection = data;

              var response = parseRange(headers('Content-Range'));

              $scope.numItems = response ? response.total : data.length;

              if(response) {
                if(length(response) < response.total) {
                  $scope.paginated = true;

                  if(
                    ( request.to < response.total - 1) ||
                    (response.to < response.total - 1 && response.total < request.to)
                  ) {
                    if(!$scope.perPage || length(response) < $scope.perPage) {
                      var idx = monkeyIndex(length(response));
                      if(monkeyNumber(idx) > length(response)) {
                        idx--;
                      }
                      $scope.serverLimit = monkeyNumber(idx);
                      $scope.perPage = $scope.Math.min(
                        $scope.serverLimit,
                        $scope.clientLimit
                      );
                    }
                  }
                }
                $scope.numPages = Math.ceil(response.total / ($scope.perPage || 1));
              }
            });
          }

          $scope.page = $scope.page || 0;
          $scope.updatePresets();

          $scope.$watch('page', function(newPage, oldPage) {
            if(newPage !== oldPage) {
              if(newPage < 0 || newPage*$scope.perPage >= $scope.numItems) {
                return;
              }

              var pp = closestMonkey($scope.perPage || 100);
              requestRange({
                from: newPage * pp,
                to: (newPage+1) * pp - 1
              });
            }
          });

          $scope.$watch('perPage', function(newPp, oldPp) {
            if(typeof(oldPp) === 'number' && newPp !== oldPp) {
              var first = $scope.page * oldPp;
              var newPage = Math.floor(first / newPp);

              if($scope.page !== newPage) {
                $scope.page = newPage;
              } else {
                requestRange({
                  from: $scope.page * newPp,
                  to: ($scope.page+1) * newPp - 1
                });
              }
            }
          });

          $scope.$watch('serverLimit', function(newLimit, oldLimit) {
            if(newLimit !== oldLimit) {
              $scope.updatePresets();
            }
          });

          $scope.$watch('url', function(newUrl, oldUrl) {
            if(newUrl !== oldUrl) {
              $scope.page = 0;
            }
          });

          $scope.$watch('reloadPage', function(newVal, oldVal) {
            if(newVal === true && oldVal === false) {
              $scope.reloadPage = false;
              requestRange({
                from: $scope.page * $scope.perPage,
                to: ($scope.page+1) * $scope.perPage - 1
              });
            }
          });

          var pp = closestMonkey($scope.perPage || 100);
          requestRange({
            from: $scope.page * pp,
            to: ($scope.page+1) * pp - 1
          });

        }],
      };
    }).

    filter('makeRange', function() {
      // http://stackoverflow.com/a/14932395/3102996
      return function(input) {
        var lowBound, highBound;
        switch (input.length) {
          case 1:
            lowBound = 0;
            highBound = parseInt(input[0]) - 1;
            break;
          case 2:
            lowBound = parseInt(input[0]);
            highBound = parseInt(input[1]);
            break;
          default:
            return input;
        }
        var result = [];
        for (var i = lowBound; i <= highBound; i++) { result.push(i); }
        return result;
      };
    });

  function parseRange(hdr) {
    var m = hdr && hdr.match(/^(\d+)-(\d+)\/(\d+|\*)$/);
    if(!m) { return null; }
    return {
      from: +m[1],
      to: +m[2],
      total: m[3] === '*' ? Infinity : +m[3]
    };
  }

  function length(range) {
    return range.to - range.from + 1;
  }
}());
<|endoftext|>


<commit_before>// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// 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
//
//      http://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.

/**
 * @fileoverview This event wrapper will dispatch an event when the user uses
 * the mouse wheel to scroll an element. You can get the direction by checking
 * the deltaX and deltaY properties of the event.
 *
 * This class aims to smooth out inconsistencies between browser platforms with
 * regards to mousewheel events, but we do not cover every possible
 * software/hardware combination out there, some of which occasionally produce
 * very large deltas in mousewheel events. If your application wants to guard
 * against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
 * to set maximum values that make sense for your application.
 *
 * @author arv@google.com (Erik Arvidsson)
 * @see ../demos/mousewheelhandler.html
 */

goog.provide('goog.events.MouseWheelEvent');
goog.provide('goog.events.MouseWheelHandler');
goog.provide('goog.events.MouseWheelHandler.EventType');

goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.math');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * This event handler allows you to catch mouse wheel events in a consistent
 * manner.
 * @param {Element|Document} element The element to listen to the mouse wheel
 *     event on.
 * @param {boolean=} opt_capture Whether to handle the mouse wheel event in
 *     capture phase.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.events.MouseWheelHandler = function(element, opt_capture) {
  goog.events.EventTarget.call(this);

  /**
   * This is the element that we will listen to the real mouse wheel events on.
   * @type {Element|Document}
   * @private
   */
  this.element_ = element;

  var rtlElement = goog.dom.isElement(this.element_) ?
      /** @type {Element} */ (this.element_) :
                             (this.element_ ?
                                  /** @type {Document} */ (this.element_).body :
                                  null);

  /**
   * True if the element exists and is RTL, false otherwise.
   * @type {boolean}
   * @private
   */
  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);

  var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';

  /**
   * The key returned from the goog.events.listen.
   * @type {goog.events.Key}
   * @private
   */
  this.listenKey_ = goog.events.listen(this.element_, type, this, opt_capture);
};
goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);

/**
 * Enum type for the events fired by the mouse wheel handler.
 * @enum {string}
 */
goog.events.MouseWheelHandler.EventType = {
  MOUSEWHEEL: 'mousewheel'
};

/**
 * Optional maximum magnitude for x delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaX_;

/**
 * Optional maximum magnitude for y delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaY_;

/**
 * @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
  this.maxDeltaX_ = maxDeltaX;
};

/**
 * @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
  this.maxDeltaY_ = maxDeltaY;
};

/**
 * Handles the events on the element.
 * @param {goog.events.BrowserEvent} e The underlying browser event.
 */
goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
  var deltaX = 0;
  var deltaY = 0;
  var detail = 0;
  var be = e.getBrowserEvent();
  if (be.type == 'mousewheel') {
    // In IE we get a multiple of 120; we adjust to a multiple of 3 to
    // represent number of lines scrolled (like Gecko).
    // Newer versions of Webkit match IE behavior, and WebKit on
    // Windows also matches IE behavior.
    // See bug https://bugs.webkit.org/show_bug.cgi?id=24368
    var wheelDeltaScaleFactor = 40;

    detail = goog.events.MouseWheelHandler.smartScale_(
        -be.wheelDelta, wheelDeltaScaleFactor);
    if (goog.isDef(be.wheelDeltaX)) {
      // Webkit has two properties to indicate directional scroll, and
      // can scroll both directions at once.
      deltaX = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaX, wheelDeltaScaleFactor);
      deltaY = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaY, wheelDeltaScaleFactor);
    } else {
      deltaY = detail;
    }

    // Historical note: Opera (pre 9.5) used to negate the detail value.
  } else {  // Gecko
    // Gecko returns multiple of 3 (representing the number of lines scrolled)
    detail = be.detail;

    // Gecko sometimes returns really big values if the user changes settings to
    // scroll a whole page per scroll
    if (detail > 100) {
      detail = 3;
    } else if (detail < -100) {
      detail = -3;
    }

    // Firefox 3.1 adds an axis field to the event to indicate direction of
    // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
      deltaX = detail;
    } else {
      deltaY = detail;
    }
  }

  if (goog.isNumber(this.maxDeltaX_)) {
    deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
  }
  if (goog.isNumber(this.maxDeltaY_)) {
    deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
  }
  // Don't clamp 'detail', since it could be ambiguous which axis it refers to
  // and because it's informally deprecated anyways.

  // For horizontal scrolling we need to flip the value for RTL grids.
  if (this.isRtl_) {
    deltaX = -deltaX;
  }
  var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
  this.dispatchEvent(newEvent);
};

/**
 * Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
 * @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
 *     be an integer.
 * @param {number} scaleFactor Factor to scale the delta down by. Expected to
 *     be an integer.
 * @return {number} Scaled-down delta value, or the original delta if the
 *     scaleFactor does not appear to be applicable.
 * @private
 */
goog.events.MouseWheelHandler.smartScale_ = function(
    mouseWheelDelta, scaleFactor) {
  // The basic problem here is that in Webkit on Mac and Linux, we can get two
  // very different types of mousewheel events: from continuous devices
  // (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
  //
  // Non-continuous devices in Webkit get their wheel deltas scaled up to
  // behave like IE. Continuous devices return much smaller unscaled values
  // (which most of the time will not be cleanly divisible by the IE scale
  // factor), so we should not try to normalize them down.
  //
  // Detailed discussion:
  //   https://bugs.webkit.org/show_bug.cgi?id=29601
  //   http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
  if (goog.userAgent.WEBKIT && (goog.userAgent.MAC || goog.userAgent.LINUX) &&
      (mouseWheelDelta % scaleFactor) != 0) {
    return mouseWheelDelta;
  } else {
    return mouseWheelDelta / scaleFactor;
  }
};

/** @override */
goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
  goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
  goog.events.unlistenByKey(this.listenKey_);
  this.listenKey_ = null;
};

/**
 * A base class for mouse wheel events. This is used with the
 * MouseWheelHandler.
 *
 * @param {number} detail The number of rows the user scrolled.
 * @param {Event} browserEvent Browser event object.
 * @param {number} deltaX The number of rows the user scrolled in the X
 *     direction.
 * @param {number} deltaY The number of rows the user scrolled in the Y
 *     direction.
 * @constructor
 * @extends {goog.events.BrowserEvent}
 * @final
 */
goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
  goog.events.BrowserEvent.call(this, browserEvent);

  this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;

  /**
   * The number of lines the user scrolled
   * @type {number}
   * NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
   * more information.
   */
  this.detail = detail;

  /**
   * The number of "lines" scrolled in the X direction.
   *
   * Note that not all browsers provide enough information to distinguish
   * horizontal and vertical scroll events, so for these unsupported browsers,
   * we will always have a deltaX of 0, even if the user scrolled their mouse
   * wheel or trackpad sideways.
   *
   * Currently supported browsers are Webkit and Firefox 3.1 or later.
   *
   * @type {number}
   */
  this.deltaX = deltaX;

  /**
   * The number of lines scrolled in the Y direction.
   * @type {number}
   */
  this.deltaY = deltaY;
};
goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
<commit_msg>добавил в wheelhandler возможность передавать объект<commit_after>// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// 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
//
//      http://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.

/**
 * @fileoverview This event wrapper will dispatch an event when the user uses
 * the mouse wheel to scroll an element. You can get the direction by checking
 * the deltaX and deltaY properties of the event.
 *
 * This class aims to smooth out inconsistencies between browser platforms with
 * regards to mousewheel events, but we do not cover every possible
 * software/hardware combination out there, some of which occasionally produce
 * very large deltas in mousewheel events. If your application wants to guard
 * against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
 * to set maximum values that make sense for your application.
 *
 * @author arv@google.com (Erik Arvidsson)
 * @see ../demos/mousewheelhandler.html
 */

goog.provide('goog.events.MouseWheelEvent');
goog.provide('goog.events.MouseWheelHandler');
goog.provide('goog.events.MouseWheelHandler.EventType');

goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.math');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * This event handler allows you to catch mouse wheel events in a consistent
 * manner.
 * @param {Element|Document} element The element to listen to the mouse wheel
 *     event on.
 * @param {(boolean|!AddEventListenerOptions)=} opt_capture Whether to handle the mouse wheel event in
 *     capture phase.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.events.MouseWheelHandler = function(element, opt_capture) {
  goog.events.EventTarget.call(this);

  /**
   * This is the element that we will listen to the real mouse wheel events on.
   * @type {Element|Document}
   * @private
   */
  this.element_ = element;

  var rtlElement = goog.dom.isElement(this.element_) ?
      /** @type {Element} */ (this.element_) :
                             (this.element_ ?
                                  /** @type {Document} */ (this.element_).body :
                                  null);

  /**
   * True if the element exists and is RTL, false otherwise.
   * @type {boolean}
   * @private
   */
  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);

  var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';

  /**
   * The key returned from the goog.events.listen.
   * @type {goog.events.Key}
   * @private
   */
  this.listenKey_ = goog.events.listen(this.element_, type, this, opt_capture);
};
goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);

/**
 * Enum type for the events fired by the mouse wheel handler.
 * @enum {string}
 */
goog.events.MouseWheelHandler.EventType = {
  MOUSEWHEEL: 'mousewheel'
};

/**
 * Optional maximum magnitude for x delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaX_;

/**
 * Optional maximum magnitude for y delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaY_;

/**
 * @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
  this.maxDeltaX_ = maxDeltaX;
};

/**
 * @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
  this.maxDeltaY_ = maxDeltaY;
};

/**
 * Handles the events on the element.
 * @param {goog.events.BrowserEvent} e The underlying browser event.
 */
goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
  var deltaX = 0;
  var deltaY = 0;
  var detail = 0;
  var be = e.getBrowserEvent();
  if (be.type == 'mousewheel') {
    // In IE we get a multiple of 120; we adjust to a multiple of 3 to
    // represent number of lines scrolled (like Gecko).
    // Newer versions of Webkit match IE behavior, and WebKit on
    // Windows also matches IE behavior.
    // See bug https://bugs.webkit.org/show_bug.cgi?id=24368
    var wheelDeltaScaleFactor = 40;

    detail = goog.events.MouseWheelHandler.smartScale_(
        -be.wheelDelta, wheelDeltaScaleFactor);
    if (goog.isDef(be.wheelDeltaX)) {
      // Webkit has two properties to indicate directional scroll, and
      // can scroll both directions at once.
      deltaX = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaX, wheelDeltaScaleFactor);
      deltaY = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaY, wheelDeltaScaleFactor);
    } else {
      deltaY = detail;
    }

    // Historical note: Opera (pre 9.5) used to negate the detail value.
  } else {  // Gecko
    // Gecko returns multiple of 3 (representing the number of lines scrolled)
    detail = be.detail;

    // Gecko sometimes returns really big values if the user changes settings to
    // scroll a whole page per scroll
    if (detail > 100) {
      detail = 3;
    } else if (detail < -100) {
      detail = -3;
    }

    // Firefox 3.1 adds an axis field to the event to indicate direction of
    // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
      deltaX = detail;
    } else {
      deltaY = detail;
    }
  }

  if (goog.isNumber(this.maxDeltaX_)) {
    deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
  }
  if (goog.isNumber(this.maxDeltaY_)) {
    deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
  }
  // Don't clamp 'detail', since it could be ambiguous which axis it refers to
  // and because it's informally deprecated anyways.

  // For horizontal scrolling we need to flip the value for RTL grids.
  if (this.isRtl_) {
    deltaX = -deltaX;
  }
  var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
  this.dispatchEvent(newEvent);
};

/**
 * Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
 * @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
 *     be an integer.
 * @param {number} scaleFactor Factor to scale the delta down by. Expected to
 *     be an integer.
 * @return {number} Scaled-down delta value, or the original delta if the
 *     scaleFactor does not appear to be applicable.
 * @private
 */
goog.events.MouseWheelHandler.smartScale_ = function(
    mouseWheelDelta, scaleFactor) {
  // The basic problem here is that in Webkit on Mac and Linux, we can get two
  // very different types of mousewheel events: from continuous devices
  // (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
  //
  // Non-continuous devices in Webkit get their wheel deltas scaled up to
  // behave like IE. Continuous devices return much smaller unscaled values
  // (which most of the time will not be cleanly divisible by the IE scale
  // factor), so we should not try to normalize them down.
  //
  // Detailed discussion:
  //   https://bugs.webkit.org/show_bug.cgi?id=29601
  //   http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
  if (goog.userAgent.WEBKIT && (goog.userAgent.MAC || goog.userAgent.LINUX) &&
      (mouseWheelDelta % scaleFactor) != 0) {
    return mouseWheelDelta;
  } else {
    return mouseWheelDelta / scaleFactor;
  }
};

/** @override */
goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
  goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
  goog.events.unlistenByKey(this.listenKey_);
  this.listenKey_ = null;
};

/**
 * A base class for mouse wheel events. This is used with the
 * MouseWheelHandler.
 *
 * @param {number} detail The number of rows the user scrolled.
 * @param {Event} browserEvent Browser event object.
 * @param {number} deltaX The number of rows the user scrolled in the X
 *     direction.
 * @param {number} deltaY The number of rows the user scrolled in the Y
 *     direction.
 * @constructor
 * @extends {goog.events.BrowserEvent}
 * @final
 */
goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
  goog.events.BrowserEvent.call(this, browserEvent);

  this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;

  /**
   * The number of lines the user scrolled
   * @type {number}
   * NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
   * more information.
   */
  this.detail = detail;

  /**
   * The number of "lines" scrolled in the X direction.
   *
   * Note that not all browsers provide enough information to distinguish
   * horizontal and vertical scroll events, so for these unsupported browsers,
   * we will always have a deltaX of 0, even if the user scrolled their mouse
   * wheel or trackpad sideways.
   *
   * Currently supported browsers are Webkit and Firefox 3.1 or later.
   *
   * @type {number}
   */
  this.deltaX = deltaX;

  /**
   * The number of lines scrolled in the Y direction.
   * @type {number}
   */
  this.deltaY = deltaY;
};
goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
<|endoftext|>


<commit_before>var ngMeteorCollections = angular.module('ngMeteor.collections', []);

ngMeteorCollections.factory('$collection', ['$window', '$rootScope', 'HashKeyCopier', '$q',
	function($window, $rootScope, HashKeyCopier, $q){
		return function (name, scope, selector, options, publisher) {
			var collection = $window[name];
			if(!selector) selector = {};
			if(collection instanceof Meteor.Collection){
				Deps.autorun(function(){
					var subscription = Meteor.subscribe(name, selector, options, publisher);
					scope[name] = HashKeyCopier.copyHashKeys(scope[name],collection.find(selector, options).fetch(),["_id"]);
					if(!scope.$$phase){scope.$apply()} // I think this bit is redundant now.

					// Temporary Fix
					//===================================================================
					scope[name].ready = function(fn){
						var deferred = $q.defer();

						isReady = setInterval(function() {
					        if ( subscription.ready() ) {
					        	deferred.resolve('ready');
					        	clearInterval(isReady);
					        } else {
					        	deferred.reject('not ready');
					        }
					    }, 100);

					    deferred.promise.then(function(){
					    	fn();
					    });
					}
					scope[name].add = function(data){
						data = angular.copy(data);
						upsert = function(item){
							if(!item._id){
								collection.insert(item);
							}else{
								var item_id = item._id;
								delete item._id;
								collection.update(item_id, {$set: item});
							}
						}
						if(!data){
							console.error("Cannot add null");
						} else{
							if(angular.isArray(data)){
								angular.forEach(data, function(value, key){
									upsert(value);
								});
							}else{
								upsert(data);
							}
						}
						Deps.flush();
					}
					scope[name].delete = function(data){
						if(!data){
							console.error("Cannot delete null");
						} else{
							if(!data._id){
								console.error("Cannot delete an object without an _id")
							} else{
								if(angular.isArray(data)){
									angular.forEach(data, function(item, key){
										collection.remove(item._id);
									});
								}else{
									collection.remove(data._id);
								}
							}
						}
						Deps.flush();
					}
					//===================================================================

				});
				return scope[name];
			} else{
				console.error('There is no Meteor Collection called "' + name + '"')
			}
		}
	}
]);<commit_msg>Reworked the $collection service<commit_after>var ngMeteorCollections = angular.module('ngMeteor.collections', []);

ngMeteorCollections.factory('$collection', ['$rootScope', '$q',
	function($rootScope, $q) {
		return function (collection, selector, options) {
			if (!(collection instanceof Meteor.Collection)) {
				throw new TypeError("The first argument of $collection must be a Meteor.Collection object.");
			}
			return new ngMeteorCollection(collection, $rootScope, $q, selector, options);
		}
	}
]);

ngMeteorCollection = function(collection, $rootScope, $q, selector, options) {
	var self = [];

	self.__proto__ = ngMeteorCollection.prototype;
	self.__proto__.$q = $q;
	self.$$collection = collection;

	if (!selector) selector = {}; // Set default selector if none is passed in the arguments.

	// Run whenever dependency changes.
	Deps.autorun(function() {
		var items = collection.find(selector, options).fetch(); // Get updated collection objects.

		// Check if the collection has changed.
		if (!angular.equals(self, items)) {
			var oldSelf = self.slice(0); // Makes a deep copy with $$hashKeys

			self.length = 0; // Empty the array so objects can be pushed to it.

			// Check for existing objects to update.
			if (oldSelf.length > 0) {
				angular.forEach(oldSelf, function(obj){
					if (obj._id){
						// Get an array of _ids in the collection.
						var index = items.map(function(item) { return item._id; }).indexOf(obj._id);

						// Check if an object with that _id still exists in the collection.
						// If it doesn't exist, the object must've been removed
						// so there would be no need to push the object again.
						if (index >= 0) {
							var item = items[index]; // Get the updated object from the collection.
							items.splice(index,1); // Remove the object from the collection so it isn't pushed as a new object later.
							if (obj.$$hashKey) item["$$hashKey"] = obj.$$hashKey; // Transfer existing $$hashKey.
							self.push(item); // Push updated object.
						}
					} else {
						// Simply push the object if it isn't part of the collection
						// because it is part of the local model yet to be saved.
						self.push(obj);
					}
				});
			}

			// Push all the new objects in the collection.
			angular.forEach(items, function(item) {
				self.push(item);
			});

			// Update bindings from the $rootScope down.
			if(!$rootScope.$$phase) $rootScope.$apply();
		}
	});

	return self;
}

ngMeteorCollection.prototype = new Array;

ngMeteorCollection.prototype.save = function save (docs) {
	var self = this,
		collection = self.$$collection,
		$q = self.$q,
		promises = []; // To store all promises.

	/*
	* The upsert function will either update an object if the _id exists
	* or insert an object if the _id is not set in the collection.
	* Returns a promise.
	*/
	upsert = function (item, $q) {
		var deferred = $q.defer();

		item = angular.copy(item); // Makes a deep copy without the $$hashKeys.

		if (item._id) { // Performs an update if the _id property is set.
			var item_id = item._id; // Store the _id in temporary variable
			delete item._id; // Remove the _id property so that it can be $set using update.
			collection.update(item_id, {$set: item}, function(error, result) {
				if(error) {
					deferred.reject(error);
				}else{
					deferred.resolve({_id: item_id, action: "updated"});
				}
			});
		} else { // Performs an insert if the _id property isn't set.
			collection.insert(item, function(error, result) {
				if(error) {
					deferred.reject(error);
				}else{
					deferred.resolve({_id: result, action: "inserted"});
				}
			});
		}

		return deferred.promise;
	}

	/*
	* How to update the collection depending on the 'docs' argument passed.
	*/
	if (docs) { // Checks if a 'docs' argument was passed.
		if(angular.isArray(docs)) { // If an array of objects were passed.
			angular.forEach(docs, function(doc) {
				this.push(upsert(doc, $q));
			}, promises);
		}else{ // If a single object was passed.
			promises.push(upsert(docs, $q));
		}
	} else { // If no 'docs' argument was passed, save the entire collection.
		angular.forEach(self, function(doc) {
			this.push(upsert(doc, $q));
		}, promises);
	}

	return $q.all(promises); // Returns all promises when they're resolved.
}

ngMeteorCollection.prototype.remove = function remove (keys) {
	var self = this,
		collection = self.$$collection,
		$q = self.$q,
		promises = []; // To store all promises.

	/*
	* The remove function will delete an object with the _id property
	* equal to the specified key.
	* Returns a promise.
	*/
	remove = function (key, $q) {
		var deferred = $q.defer();

		if (key) { // Checks if 'key' argument is set.
			collection.remove(key, function(error, result) {
				if(error) {
					deferred.reject(error);
				}else{
					deferred.resolve({_id: key, action: "removed"});
				}
			});
		} else {
			deferred.reject("key cannot be null");
		}

		return deferred.promise;
	}

	/*
	* What to remove from collection depending on the 'keys' argument passed.
	*/
	if (docs) { // Checks if a 'docs' argument was passed.
		if(angular.isArray(keys)) { // If an array of keys were passed.
			angular.forEach(keys, function(key) {
				this.push(remove(key, $q));
			}, promises);
		}else{ // If a single key was passed.
			promises.push(remove(keys, $q));
		}
	} else { // If no 'keys' argument was passed, save the entire collection.
		angular.forEach(self, function(doc) {
			this.push(remove(doc._id, $q));
		}, promises);
	}

	return $q.all(promises); // Returns all promises when they're resolved.
}

ngMeteorCollection.prototype.bind = function bind () {
	// All changes to the object will be automatically synced to the database.
}<|endoftext|>