<commit_before><commit_msg>Add meatHead comparison functionality<commit_after>

function matchMaker(meatHead1, meatHead2) {
    return JSON.stringify(meatHead1) === JSON.stringify(meatHead2);
}

var joe = {'day': 'monday', 'time': '6 AM', 'location': 'anytime fitness'};
var brady = {'day': 'monday', 'time': '6 AM', 'location': 'anytime fitness'};

console.log(matchMaker(joe, brady));
<|endoftext|>


<commit_before>//Arrays

//First
_.first = function(array, n) {
  return n ? array.slice(0,n) : array[0];
}

_.initial = function(array,n) {
  return n ? array.slice(0,array.length-n) : array.slice(0,array.length-1);
}

_,last = function(array,n) {
  return n ? array.slice(array.length-n) : array[array.length-1];
}

_.rest = function(array, index) {
  return index ? array.slice(index) : array.slice(1);
}

//this is incomplete; returns the opposite of what it should.
_.compact = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if((array[i])) {
      results.push(array[i]);
    }
  }
  return results;
}

// The standard _.compact excludes false as well as falsy values. I think it would be useful to create a funciton that does something for only falsy (non-false) values. Whether it's as simple as _.compact but leaving in false, or turning those values into strings (we'd then presumably have a function that un-stringifies them too)
//I like the idea of stringifying 'falsy non-false' values, and then reconverting them after.

_.falsyString = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(!(array[i]) && typeof array[i] !== 'boolean') {
      if(typeof array[i] === 'undefined') results.push('undefined');
      else if (typeof array[i] === 'string') results.push("''");
      else if (array[i] === 0) results.push('0');
      else if (typeof array[i] === 'number') results.push('NaN');
      else if (typeof array[i] === 'object') results.push('null');
      else {
        results.push(array[i]);
        console.log('you have found an edge case');
      }
    } else results.push(array[i]);
  }
  return results;
}

_.unFalsyString = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(typeof array[i] === 'string') {
      if(array[i] === 'undefined') results.push(undefined);
      else if (array[i] === "''") results.push('');
      else if (array[i] === '0') results.push(0);
      else if (array[i] === 'NaN') results.push(NaN);
      else if (array[i] === 'null') results.push(null);
      else results.push(array[i]);
    } else results.push(array[i]);
  }
  return results;
}

//ok, let's see if i can do flatten both recursively and non-recursively.
//non-recursively: var while(countOfNonArrays !== array.length)
//basically, count the number of things in the array that are not arrays, and if this isn't equal to the length of the array, call a function on the array
//that function will iterate through the array, and flatten one level further

//the first implementation (below) works well for arrays only
_.flatten = function(array,shallow) {
  var results = [];

  //recursive flattener that only works for arrays currently
  var rFlattener = function(rInput, rKey) {
    if(Array.isArray(rInput)) {
      for (var i = 0; i < rInput.length; i++) {
        rFlattener(rInput[i]);
      }
    } else results.push(rInput);
  }

  //single-level flattener, only implemented for arrays currently
  if(arguments[1]) {
    for (var i = 0; i < array.length; i++) {
      if(Array.isArray(array[i])) {
        for (var j = 0; j < array[i].length; j++) {
          results.push(array[i][j]);
        }
      } else results.push(array[i]);
    }
  } else rFlattener(array);
  return results;
}

//supporting objects gets tricky when we have objects nested within objects: how do we display the key value pairs?
//This implementation works, though it's not prettiest solution in the world.
_.flatten = function(array,shallow) {
  var results = [];

  //recursive flattener that only works for arrays currently
  var rFlattener = function(rInput, rKey) {
    if(Array.isArray(rInput)) {
      for (var i = 0; i < rInput.length; i++) {
        rFlattener(rInput[i]);
      }
    } else if (typeof rInput === 'object') {
      for (var key in rInput) {
        results.push('key: ' + key);
        rFlattener(rInput[key],key);
      }
    } else if(rKey) {
      results.push('value: ' + rInput);
    } else results.push(rInput);
  }

  //single-level flattener, only implemented for arrays currently
  if(arguments[1]) {
    for (var i = 0; i < array.length; i++) {
      if(Array.isArray(array[i])) {
        for (var j = 0; j < array[i].length; j++) results.push(array[i][j]);
      } else results.push(array[i]);
    }
  } else rFlattener(array);
  return results;
}

//this implementation is non-recursive, just for the heck of it, and handles arrays only for simplicity
//this does not quite work yet.
pflatten = function(array) {
  var results = [];
  var notArrayCount = 0;

  for (var i = 0; i < array.length; i++) {
    results.push(array[i]);
    if(!(Array.isArray(array))) {
      notArrayCount += 1;
    }
  }

  while(notArrayCount !== results.length) {
    notArrayCount = 0;
    var tempResults = [];
    for (var j = 0; j < results.length; j++) {
      if(Array.isArray(array[j])) {
        for (var k = 0; k < array[j].length; k++) {
          tempResults.push(array[j][k]);
          if(!(Array.isArray(array[j][k]))) notArrayCount +=1;
        }
      } else {
        tempResults.push(array[j]);
        notArrayCount +=1;
      }
    }
    results = tempResults;
  }

  return results;
}

//could probably be refactored to use filter or every
_.without = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    var isUnique = true;
    for (var j = 1; j < arguments.length; j++) {
      if(arguments[j] === array[i]) {
        isUnique = false;
      }
    }
    if(isUnique) results.push(array[i]);
  }
  return results;
}

//currently this implementation checks if the number is present in at least two of the arrays. this definition does not strictly match any of the underscore functions
_.looseIntersection = function() {
  var nums = _.flatten(arguments);
  var results = [];
  for (var i = 0; i < nums.length-1; i++) {
    for (var j = i + 1; j < nums.length; j++) {
      if (nums[i] === nums[j]) {
        results.push(nums[i]);
      }
    }
  }
  return _.uniq(results);
}

_.union = function() {
  return _.uniq(_.flatten(arguments));
}

_.intersection = function() {
  //start the results array with the values of the first input array. it can never grow from here, only shrink
  var results = arguments[0];
  //iterate through each following array. check whether all elements of the results array are still present
  for (var i = 1; i < arguments.length; i++) {
    //within each of the arguments arrays, check to make sure they contain everything in the results array
    for(var j = 0; j < results.length; j++) {
       if(_.contains(arguments[i],results[j]) === false) {
         results.splice(j,1);
         j = j-1;
       }
    }
  }
  return results;
}

_.difference = function(list) {
  var results = list;
  for (var i = 1; i < arguments.length; i++) {
    for (var j = 0; j < results.length; j++) {
      if (_.contains(arguments[i],results[j]) === true) {
        results.splice(j,1);
        j = j-1;
      }
    }
  }
  return results;
}

_.uniq = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(_.contains(results,array[i]) === false) {
      results.push(array[i]);
    }
  }
  return results;
}

<commit_msg>added basic zip<commit_after>//Arrays

//First
_.first = function(array, n) {
  return n ? array.slice(0,n) : array[0];
}

_.initial = function(array,n) {
  return n ? array.slice(0,array.length-n) : array.slice(0,array.length-1);
}

_,last = function(array,n) {
  return n ? array.slice(array.length-n) : array[array.length-1];
}

_.rest = function(array, index) {
  return index ? array.slice(index) : array.slice(1);
}

//this is incomplete; returns the opposite of what it should.
_.compact = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if((array[i])) {
      results.push(array[i]);
    }
  }
  return results;
}

// The standard _.compact excludes false as well as falsy values. I think it would be useful to create a funciton that does something for only falsy (non-false) values. Whether it's as simple as _.compact but leaving in false, or turning those values into strings (we'd then presumably have a function that un-stringifies them too)
//I like the idea of stringifying 'falsy non-false' values, and then reconverting them after.

_.falsyString = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(!(array[i]) && typeof array[i] !== 'boolean') {
      if(typeof array[i] === 'undefined') results.push('undefined');
      else if (typeof array[i] === 'string') results.push("''");
      else if (array[i] === 0) results.push('0');
      else if (typeof array[i] === 'number') results.push('NaN');
      else if (typeof array[i] === 'object') results.push('null');
      else {
        results.push(array[i]);
        console.log('you have found an edge case');
      }
    } else results.push(array[i]);
  }
  return results;
}

_.unFalsyString = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(typeof array[i] === 'string') {
      if(array[i] === 'undefined') results.push(undefined);
      else if (array[i] === "''") results.push('');
      else if (array[i] === '0') results.push(0);
      else if (array[i] === 'NaN') results.push(NaN);
      else if (array[i] === 'null') results.push(null);
      else results.push(array[i]);
    } else results.push(array[i]);
  }
  return results;
}

//ok, let's see if i can do flatten both recursively and non-recursively.
//non-recursively: var while(countOfNonArrays !== array.length)
//basically, count the number of things in the array that are not arrays, and if this isn't equal to the length of the array, call a function on the array
//that function will iterate through the array, and flatten one level further

//the first implementation (below) works well for arrays only
_.flatten = function(array,shallow) {
  var results = [];

  //recursive flattener that only works for arrays currently
  var rFlattener = function(rInput, rKey) {
    if(Array.isArray(rInput)) {
      for (var i = 0; i < rInput.length; i++) {
        rFlattener(rInput[i]);
      }
    } else results.push(rInput);
  }

  //single-level flattener, only implemented for arrays currently
  if(arguments[1]) {
    for (var i = 0; i < array.length; i++) {
      if(Array.isArray(array[i])) {
        for (var j = 0; j < array[i].length; j++) {
          results.push(array[i][j]);
        }
      } else results.push(array[i]);
    }
  } else rFlattener(array);
  return results;
}

//supporting objects gets tricky when we have objects nested within objects: how do we display the key value pairs?
//This implementation works, though it's not prettiest solution in the world.
_.flatten = function(array,shallow) {
  var results = [];

  //recursive flattener that only works for arrays currently
  var rFlattener = function(rInput, rKey) {
    if(Array.isArray(rInput)) {
      for (var i = 0; i < rInput.length; i++) {
        rFlattener(rInput[i]);
      }
    } else if (typeof rInput === 'object') {
      for (var key in rInput) {
        results.push('key: ' + key);
        rFlattener(rInput[key],key);
      }
    } else if(rKey) {
      results.push('value: ' + rInput);
    } else results.push(rInput);
  }

  //single-level flattener, only implemented for arrays currently
  if(arguments[1]) {
    for (var i = 0; i < array.length; i++) {
      if(Array.isArray(array[i])) {
        for (var j = 0; j < array[i].length; j++) results.push(array[i][j]);
      } else results.push(array[i]);
    }
  } else rFlattener(array);
  return results;
}

//this implementation is non-recursive, just for the heck of it, and handles arrays only for simplicity
//this does not quite work yet.
pflatten = function(array) {
  var results = [];
  var notArrayCount = 0;

  for (var i = 0; i < array.length; i++) {
    results.push(array[i]);
    if(!(Array.isArray(array))) {
      notArrayCount += 1;
    }
  }

  while(notArrayCount !== results.length) {
    notArrayCount = 0;
    var tempResults = [];
    for (var j = 0; j < results.length; j++) {
      if(Array.isArray(array[j])) {
        for (var k = 0; k < array[j].length; k++) {
          tempResults.push(array[j][k]);
          if(!(Array.isArray(array[j][k]))) notArrayCount +=1;
        }
      } else {
        tempResults.push(array[j]);
        notArrayCount +=1;
      }
    }
    results = tempResults;
  }

  return results;
}

//could probably be refactored to use filter or every
_.without = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    var isUnique = true;
    for (var j = 1; j < arguments.length; j++) {
      if(arguments[j] === array[i]) {
        isUnique = false;
      }
    }
    if(isUnique) results.push(array[i]);
  }
  return results;
}

//currently this implementation checks if the number is present in at least two of the arrays. this definition does not strictly match any of the underscore functions
_.looseIntersection = function() {
  var nums = _.flatten(arguments);
  var results = [];
  for (var i = 0; i < nums.length-1; i++) {
    for (var j = i + 1; j < nums.length; j++) {
      if (nums[i] === nums[j]) {
        results.push(nums[i]);
      }
    }
  }
  return _.uniq(results);
}

_.union = function() {
  return _.uniq(_.flatten(arguments));
}

_.intersection = function() {
  //start the results array with the values of the first input array. it can never grow from here, only shrink
  var results = arguments[0];
  //iterate through each following array. check whether all elements of the results array are still present
  for (var i = 1; i < arguments.length; i++) {
    //within each of the arguments arrays, check to make sure they contain everything in the results array
    for(var j = 0; j < results.length; j++) {
       if(_.contains(arguments[i],results[j]) === false) {
         results.splice(j,1);
         j = j-1;
       }
    }
  }
  return results;
}

_.difference = function(list) {
  var results = list;
  for (var i = 1; i < arguments.length; i++) {
    for (var j = 0; j < results.length; j++) {
      if (_.contains(arguments[i],results[j]) === true) {
        results.splice(j,1);
        j = j-1;
      }
    }
  }
  return results;
}

_.uniq = function(array) {
  var results = [];
  for (var i = 0; i < array.length; i++) {
    if(_.contains(results,array[i]) === false) {
      results.push(array[i]);
    }
  }
  return results;
}

_.zip = function() {
  var results = [];
  for (var i = 0; i < arguments[0].length; i++) {
    var innerResult = [];
    for (var j = 0; j < arguments.length; j++) {
      innerResult.push(arguments[j][i]);
    }
    results.push(innerResult);
  }
  return results;
}

<|endoftext|>


<commit_before>'use strict';

(function () {
	var helpers = new Helpers();

	beforeEach(function () {
		this.addMatchers({
			toBePresent: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}}to Be Present');
				return this.actual.isPresent();
			},
			toBeDisplayed: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}}to Be Displayed');
				return this.actual.isDisplayed();
			},
			toHaveLengthOf: function (expectedLength) {
				helpers.createMessage(this, 'Expected request{{not}}to have length of ' + expectedLength + ' but was {{actual}}');
				return this.actual === expectedLength;
			},
			toHaveText: function (expectedText) {
				var _this = this;
				return this.actual.getText().then(function (text) {
					helpers.createMessage(_this, 'Expected {{locator}}{{not}}to have text ' + expectedText + ' but was ' + text);
					return text === expectedText;
				});
			},
			toMatchRegex: function (expectedPattern) {
				var _this = this;
				var re = new RegExp(expectedPattern);
				return this.actual.getText().then(function (text) {
					helpers.createMessage(_this, 'Expected {{locator}} with text ' + text + '{{not}}to match pattern ' + expectedPattern);
					return re.test(text);
				});
			},
			toMatchMoney: function (expectedValue, currencySymbol) {
				var _this = this;
				var regexExpectedValue = createMoneyRegexp(this.actual, expectedValue, currencySymbol, false);
				helpers.createMessage(_this, 'Expected ' + this.actual + '{{not}}to match money pattern ' + regexExpectedValue);
				return regexExpectedValue.test(this.actual);
			},
			toMatchMoneyWithFraction: function (expectedValue, currencySymbol) {
				var _this = this;
				var regexExpectedValue = createMoneyRegexp(this.actual, expectedValue, currencySymbol, true);
				helpers.createMessage(_this, 'Expected ' + this.actual + '{{not}}to match money pattern ' + regexExpectedValue);
				return regexExpectedValue.test(this.actual);
			},
			toHaveValue: function (expectedValue) {
				var _this = this;
				return this.actual.getAttribute('value').then(function (value) {
					helpers.createMessage(_this, 'Expected {{locator}}{{not}} to have value ' + expectedValue + ' but was ' + value);
					return value === expectedValue;
				});
			},
			toHaveClass: function (className) {
				var _this = this;
				helpers.createMessage(_this, 'Expected {{locator}}{{not}}to have class ' + className);
				return helpers.hasClass(this.actual, className);
			},
			toBeDisabled: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be Disabled');
				return this.actual.getAttribute('disabled').then(function (value) {
					return value === 'true';
				});
			},
			toBeChecked: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be checked');
				return this.actual.getAttribute('checked').then(function (value) {
					return value;
				});
			},
			toBeValid: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to have valid input value');
				return helpers.hasClass(this.actual, 'ng-valid');
			},
			toBeInvalid: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to have invalid input value');
				return helpers.hasClass(this.actual, 'ng-invalid');
			},
			toBeInvalidRequired: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be required and invalid (when empty)');
				return helpers.hasClass(this.actual, 'ng-invalid-required');
			},
			toMatchTranslated: function (key, values) {
				var _this = this;
				return helpers.translate(key, values).then(function (translatedStr) {
					helpers.createMessage(_this, 'Expected {{actual}}{{not}} to match ' + translatedStr + ' (translated from ' + key + ', values: ' + JSON.stringify(values) + ')');
					var re = new RegExp(translatedStr);
					return re.test(_this.actual);
				});
			}
		});
	});

	////////////////////////////////////////////////////////////////////////////////////////////////////
	//	Money Matcher Functions
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 * Gets a number and adds commas in the right place
	 * @param number
	 * @returns {string}
	 */
	var getNumberWithCommas = function (number) {
		return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	};

	/**
	 * Creates a regular expression to match money representation with or without spaces in between
	 * @param matchedValue - the number that is tested
	 * @param expectedValue - the number to match against
	 * @param currencySymbol {string}
	 * @param isFraction {boolean} - flag to add the necessary postfix to expectedValue
	 * @returns {RegExp}
	 */
	var createMoneyRegexp = function (matchedValue, expectedValue, currencySymbol, isFraction) {
		var minusSign = '';
		if (matchedValue.indexOf('-') !== -1) {
			minusSign = '-';
		}

		expectedValue = getNumberWithCommas(expectedValue);
		if (isFraction && expectedValue.indexOf('.') === -1) {
			expectedValue += '.00';
		}
		return new RegExp('^' + minusSign + '\\s*' + '\\' + currencySymbol + '\\s*' + expectedValue + '$');
	};
})();
<commit_msg>add support toMatchMoney without symbol this closes #7<commit_after>'use strict';

(function () {
	var helpers = new Helpers();

	beforeEach(function () {
		this.addMatchers({
			toBePresent: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}}to Be Present');
				return this.actual.isPresent();
			},
			toBeDisplayed: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}}to Be Displayed');
				return this.actual.isDisplayed();
			},
			toHaveLengthOf: function (expectedLength) {
				helpers.createMessage(this, 'Expected request{{not}}to have length of ' + expectedLength + ' but was {{actual}}');
				return this.actual === expectedLength;
			},
			toHaveText: function (expectedText) {
				var _this = this;
				return this.actual.getText().then(function (text) {
					helpers.createMessage(_this, 'Expected {{locator}}{{not}}to have text ' + expectedText + ' but was ' + text);
					return text === expectedText;
				});
			},
			toMatchRegex: function (expectedPattern) {
				var _this = this;
				var re = new RegExp(expectedPattern);
				return this.actual.getText().then(function (text) {
					helpers.createMessage(_this, 'Expected {{locator}} with text ' + text + '{{not}}to match pattern ' + expectedPattern);
					return re.test(text);
				});
			},
			toMatchMoney: function (expectedValue, currencySymbol) {
				var _this = this;
				var regexExpectedValue = createMoneyRegexp(this.actual, expectedValue, currencySymbol);
				helpers.createMessage(_this, 'Expected ' + this.actual + '{{not}}to match money pattern ' + regexExpectedValue);
				return regexExpectedValue.test(this.actual);
			},
			toMatchMoneyWithFraction: function (expectedValue, currencySymbol) {
				var _this = this;
				var regexExpectedValue = createMoneyRegexp(this.actual, expectedValue, currencySymbol, true);
				helpers.createMessage(_this, 'Expected ' + this.actual + '{{not}}to match money pattern ' + regexExpectedValue);
				return regexExpectedValue.test(this.actual);
			},
			toHaveValue: function (expectedValue) {
				var _this = this;
				return this.actual.getAttribute('value').then(function (value) {
					helpers.createMessage(_this, 'Expected {{locator}}{{not}} to have value ' + expectedValue + ' but was ' + value);
					return value === expectedValue;
				});
			},
			toHaveClass: function (className) {
				var _this = this;
				helpers.createMessage(_this, 'Expected {{locator}}{{not}}to have class ' + className);
				return helpers.hasClass(this.actual, className);
			},
			toBeDisabled: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be Disabled');
				return this.actual.getAttribute('disabled').then(function (value) {
					return value === 'true';
				});
			},
			toBeChecked: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be checked');
				return this.actual.getAttribute('checked').then(function (value) {
					return value;
				});
			},
			toBeValid: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to have valid input value');
				return helpers.hasClass(this.actual, 'ng-valid');
			},
			toBeInvalid: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to have invalid input value');
				return helpers.hasClass(this.actual, 'ng-invalid');
			},
			toBeInvalidRequired: function () {
				helpers.createMessage(this, 'Expected {{locator}}{{not}} to be required and invalid (when empty)');
				return helpers.hasClass(this.actual, 'ng-invalid-required');
			},
			toMatchTranslated: function (key, values) {
				var _this = this;
				return helpers.translate(key, values).then(function (translatedStr) {
					helpers.createMessage(_this, 'Expected {{actual}}{{not}} to match ' + translatedStr + ' (translated from ' + key + ', values: ' + JSON.stringify(values) + ')');
					var re = new RegExp(translatedStr);
					return re.test(_this.actual);
				});
			}
		});
	});

	////////////////////////////////////////////////////////////////////////////////////////////////////
	//	Money Matcher Functions
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 * Gets a number and adds commas in the right place
	 * @param number
	 * @returns {string}
	 */
	var getNumberWithCommas = function (number) {
		return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	};

	/**
	 * Creates a regular expression to match money representation with or without spaces in between
	 * @param matchedValue - the number that is tested
	 * @param expectedValue - the number to match against
	 * @param currencySymbol[optional] {string} - the symbol to match against.
	 *                           if not specify - validate that there is no symbol.
	 * @param isFraction[optional] {boolean} - flag to add the necessary postfix to expectedValue
	 * @returns {RegExp}
	 */
	var createMoneyRegexp = function (matchedValue, expectedValue, currencySymbol, isFraction) {
		// get value with fraction
		expectedValue = getNumberWithCommas(expectedValue);
		if (isFraction === true && expectedValue.indexOf('.') === -1) {
			expectedValue += '.00';
		}

		// add minus and symbol if needed
		var expression = '^';
		if (matchedValue.indexOf('-') !== -1) {
			expression += '-';
		}
		expression += '\\s*';
		if (typeof currencySymbol === 'string') {
			expression += '\\' + currencySymbol + '\\s*';
		}
		return new RegExp(expression + expectedValue + '$');
	};
})();
<|endoftext|>


<commit_before>/**
 * Tâmia workflow for Grunt.
 * Modernizr builder.
 *
 * @author Artem Sapegin (http://sapegin.me)
 */

module.exports = function(grunt, util, config) {
	'use strict';

	var _ = require('lodash');

	if (!util.hasScripts() && !util.hasStyles()) {
		util.skipModule();
		return config;
	}

	util.setupDirs({
		destParam: 'scriptsDest',
		destDir: 'build'
	});

	var localConfig = {
		modernizr: {
			main: {
				devFile: 'remote',
				outputFile: util.dest('modernizr.js'),
				extra: {
					load: false
				},
				files: {
					src: []
				}
			}
		}
	};

	var src = localConfig.modernizr.main.files.src;
	if (util.hasScripts()) {
		src.push(util.dest('scripts.js'));
	}
	if (util.hasStyles()) {
		src.push(util.dest('styles.css'));
	}

	config = _.merge(localConfig, config);

	util.npmDependencies([
		'grunt-modernizr'
	]);

	return config;
};
<commit_msg>Modernizr: remove html5shiv, sorry IE8.<commit_after>/**
 * Tâmia workflow for Grunt.
 * Modernizr builder.
 *
 * @author Artem Sapegin (http://sapegin.me)
 */

module.exports = function(grunt, util, config) {
	'use strict';

	var _ = require('lodash');

	if (!util.hasScripts() && !util.hasStyles()) {
		util.skipModule();
		return config;
	}

	util.setupDirs({
		destParam: 'scriptsDest',
		destDir: 'build'
	});

	var localConfig = {
		modernizr: {
			main: {
				devFile: 'remote',
				outputFile: util.dest('modernizr.js'),
				extra: {
					load: false,
					shiv: false
				},
				files: {
					src: []
				}
			}
		}
	};

	var src = localConfig.modernizr.main.files.src;
	if (util.hasScripts()) {
		src.push(util.dest('scripts.js'));
	}
	if (util.hasStyles()) {
		src.push(util.dest('styles.css'));
	}

	config = _.merge(localConfig, config);

	util.npmDependencies([
		'grunt-modernizr'
	]);

	return config;
};
<|endoftext|>


<commit_before>/**
 * Applies a Welsh soft mutation to a given string.
 */
module.exports = {
	commands: {
		soft: {
			aliases: [ 'mutate', 'treglad', 'meddal' ],
			help: 'Softly mutates a given word',
			usage: [ 'noun' ],
			command: function ( bot, msg ) {
				/* TODO: uppercase */
				var initial = msg.args.noun.charAt( 0 );
				var rest = msg.args.noun.substr( 1 );
				switch ( initial ) {
					/*  G LlMRh */
					case 'p':
						return 'b' + rest;
					case 't':
						return 'd' + rest;
					case 'c':
						return 'g' + rest;
					case 'b':
					case 'm':
						return 'f' + rest;
					case 'd':
						return 'dd' + rest;
					case 'g':
						return rest;
					case 'l':
						if ( rest.charAt( 0 ) === 'l' ) {
							return rest;
						}
						break;
					case 'r':
						if ( rest.charAt( 0 ) === 'h' ) {
							return 'r' + rest.substr( 1 );
						}
					}
				return msg.args.noun;
			}
		}
	}
};
<commit_msg>Add the other two mutations<commit_after>/**
 * Applies a Welsh soft mutation to a given string.
 */
module.exports = {
	commands: {
		soft: {
			aliases: [ 'mutate', 'treglad', 'meddal' ],
			help: 'Mutates a given word',
			usage: [ 'type', 'radical' ],
			command: function ( bot, msg ) {
				/* TODO: uppercase */
				var initial = msg.args.radical.charAt( 0 );
				var rest = msg.args.radical.substr( 1 );
				switch ( msg.args.type.toUpperCase() ) {
					case 'ASPIRATE':
					case 'LLAES':
					case 'A':
					case 'LL':
					case 'L':
					case 'AC':
						switch ( initial ) {
							case 'p':
								return 'ph' + rest;
							case 't':
								return 'th' + rest;
							case 'c':
								return 'ch' + rest;
						}
					break; /* not actually needed, but hey */

					case 'NASAL':
					case 'TRWYNOL':
					case 'N':
					case 'T':
					case 'YN':
					case 'FY':
						switch ( initial ) {
							case 'p':
								return 'mh' + rest;
							case 't':
								return 'nh' + rest;
							case 'c':
								return 'ngh' + rest;
							case 'b':
								return 'm' + rest;
							case 'd':
								return 'n' + rest;
							case 'g':
								return 'ng' + rest;
						}
					break;

					case 'SOFT':
					case 'MEDDIAL':
					case 'S':
					case 'M':
						switch ( initial ) {
							/*  G LlMRh */
							case 'p':
								return 'b' + rest;
							case 't':
								return 'd' + rest;
							case 'c':
								return 'g' + rest;
							case 'b':
							case 'm':
								return 'f' + rest;
							case 'd':
								return 'dd' + rest;
							case 'g':
								return rest;
							case 'l':
								if ( rest.charAt( 0 ) === 'l' ) {
									return rest;
								}
								break;
							case 'r':
								if ( rest.charAt( 0 ) === 'h' ) {
									return 'r' + rest.substr( 1 );
								}
							}
						break;

					default:
						return [
							'Usage: (soft|nasal|aspirate) <radical>',
							'Defnydd: (meddial|trwynol|llaes) <radical>'
						];
				}
				return msg.args.radical;
			}
		}
	}
};
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name ng.filter:orderBy
 * @function
 *
 * @description
 * Упорядочивание заданного массива `array` наосновании выражения `expression`.
 *
 * Примечание: эта функция используется для расширения типа `Array` в выражениях Angular. См. так же {@link ng.$filter}
 *
 * @param {Array} array Массив для сортировки.
 * @param {function(*)|string|Array.<(function(*)|string)>} expression Выражение, использующееся для сравнения при
 *    определении порядка элементов.
 *
 *    Может быть одним из:
 *
 *    - `function`: Функция получатель. Результат этой функции будет отсортирован с помощью операторов
 *      `<`, `=`, `>`.
 *    - `string`: Выражение Angular, которое вычисляет объект для сортировки по порядку, такому как 'name'
 *      для сортировки по свойству с именем 'name'. Может начинаться с `+` или `-` для возрастания
 *      и убывания сортировки (напр., +name или -name).
 *    - `Array`: Массивфункций или выражений. Первое выражение в массиве используется для сортировки,
 *      но если элементы эквиваленты, то используется следующее выражение.
 *
 * @param {boolean=} reverse Обратный порядок в массиве.
 * @returns {Array} отсортированная копия исходного массива.
 *
 * @example
   <doc:example>
     <doc:source>

Sorting predicate = {{predicate}}; reverse = {{reverse}}

         [ unsorted ]

             Name
                 (^)
             Phone Number
             Age

             {{friend.name}}
             {{friend.phone}}
             {{friend.age}}

     </doc:source>
     <doc:scenario>
       it('should be reverse ordered by aged', function() {
         expect(binding('predicate')).toBe('-age');
         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
           toEqual(['35', '29', '21', '19', '10']);
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
       });

       it('should reorder the table when user selects different predicate', function() {
         element('.doc-example-live a:contains("Name")').click();
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
           toEqual(['35', '10', '29', '19', '21']);

         element('.doc-example-live a:contains("Phone")').click();
         expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
           toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
       });
     </doc:scenario>
   </doc:example>
 */
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
  return function(array, sortPredicate, reverseOrder) {
    if (!isArray(array)) return array;
    if (!sortPredicate) return array;
    sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
    sortPredicate = map(sortPredicate, function(predicate){
      var descending = false, get = predicate || identity;
      if (isString(predicate)) {
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
          descending = predicate.charAt(0) == '-';
          predicate = predicate.substring(1);
        }
        get = $parse(predicate);
      }
      return reverseComparator(function(a,b){
        return compare(get(a),get(b));
      }, descending);
    });
    var arrayCopy = [];
    for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
    return arrayCopy.sort(reverseComparator(comparator, reverseOrder));

    function comparator(o1, o2){
      for ( var i = 0; i < sortPredicate.length; i++) {
        var comp = sortPredicate[i](o1, o2);
        if (comp !== 0) return comp;
      }
      return 0;
    }
    function reverseComparator(comp, descending) {
      return toBoolean(descending)
          ? function(a,b){return comp(b,a);}
          : comp;
    }
    function compare(v1, v2){
      var t1 = typeof v1;
      var t2 = typeof v2;
      if (t1 == t2) {
        if (t1 == "string") v1 = v1.toLowerCase();
        if (t1 == "string") v2 = v2.toLowerCase();
        if (v1 === v2) return 0;
        return v1 < v2 ? -1 : 1;
      } else {
        return t1 < t2 ? -1 : 1;
      }
    }
  }
}
<commit_msg>Update orderBy.js<commit_after>'use strict';

/**
 * @ngdoc function
 * @name ng.filter:orderBy
 * @function
 *
 * @description
 * Упорядочивание заданного массива `array` на основании выражения `expression`.
 *
 * Примечание: эта функция используется для расширения типа данных  `Array` в выражениях Angular.
 * См. так же {@link ng.$filter}
 *
 * @param {Array} array Массив для сортировки.
 * @param {function(*)|string|Array.<(function(*)|string)>} expression Выражение, использующееся для сравнения при
 *    определении порядка элементов.
 *
 *    Может быть одним из:
 *
 *    - `function`: Функция получатель. Результат этой функции будет отсортирован с помощью операторов
 *      `<`, `=`, `>`.
 *    - `string`: Выражение Angular, которое вычисляет объект для сортировки по порядку, такому как 'name'
 *      для сортировки по свойству с именем 'name'. Может начинаться с `+` или `-` для возрастания
 *      и убывания сортировки (напр., +name или -name).
 *    - `Array`: Массивфункций или выражений. Первое выражение в массиве используется для сортировки,
 *      но если элементы эквиваленты, то используется следующее выражение.
 *
 * @param {boolean=} reverse Обратный порядок в массиве.
 * @returns {Array} отсортированная копия исходного массива.
 *
 * @example
   <doc:example>
     <doc:source>

Sorting predicate = {{predicate}}; reverse = {{reverse}}

         [ unsorted ]

             Name
                 (^)
             Phone Number
             Age

             {{friend.name}}
             {{friend.phone}}
             {{friend.age}}

     </doc:source>
     <doc:scenario>
       it('should be reverse ordered by aged', function() {
         expect(binding('predicate')).toBe('-age');
         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
           toEqual(['35', '29', '21', '19', '10']);
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
       });

       it('should reorder the table when user selects different predicate', function() {
         element('.doc-example-live a:contains("Name")').click();
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
           toEqual(['35', '10', '29', '19', '21']);

         element('.doc-example-live a:contains("Phone")').click();
         expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
           toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
           toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
       });
     </doc:scenario>
   </doc:example>
 */
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
  return function(array, sortPredicate, reverseOrder) {
    if (!isArray(array)) return array;
    if (!sortPredicate) return array;
    sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
    sortPredicate = map(sortPredicate, function(predicate){
      var descending = false, get = predicate || identity;
      if (isString(predicate)) {
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
          descending = predicate.charAt(0) == '-';
          predicate = predicate.substring(1);
        }
        get = $parse(predicate);
      }
      return reverseComparator(function(a,b){
        return compare(get(a),get(b));
      }, descending);
    });
    var arrayCopy = [];
    for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
    return arrayCopy.sort(reverseComparator(comparator, reverseOrder));

    function comparator(o1, o2){
      for ( var i = 0; i < sortPredicate.length; i++) {
        var comp = sortPredicate[i](o1, o2);
        if (comp !== 0) return comp;
      }
      return 0;
    }
    function reverseComparator(comp, descending) {
      return toBoolean(descending)
          ? function(a,b){return comp(b,a);}
          : comp;
    }
    function compare(v1, v2){
      var t1 = typeof v1;
      var t2 = typeof v2;
      if (t1 == t2) {
        if (t1 == "string") v1 = v1.toLowerCase();
        if (t1 == "string") v2 = v2.toLowerCase();
        if (v1 === v2) return 0;
        return v1 < v2 ? -1 : 1;
      } else {
        return t1 < t2 ? -1 : 1;
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Insight is a generic web dashboard for smart city projects.
 *
 * It allows to combine different chart and visualization tools to better
 * understand what is going on in the city
 *
 * @namespace
 * @requires jQuery 1.11.1+
 */
niclabs.insight = (function ($) {
    "use strict";

     /**
      * Bind a jquery element to a template
      *
      * It binds the callback 'render' to a function that will render
      * an associative array into the element by using the data-bind
      * attributes
      *
      * You can use the attributes `data-bind` and `data-if`
      * indicate binding or dependency from a data element
      *
      * @example
      * ```html
      * <script id='template' type='text/html'>
      * Hello <span data-bind="name">friend</span><span data-if="name">!!!</span>,
      * this is a template <span data-if="app">for </span><i data-bind="app">your app</i>
      * </script>
      * <script type='text/html'>
      * $('#element').template('#template'); // Will show "Hello, this is a template"
      * $('#content').trigger('render', {name: 'John', app: 'John\'s App'}); // Updates content to "Hello John!!!, this is a template for John's app"
      * </script>
      * ```
      *
      * @param {string|jQuery} template - id for the template element, string with the template or jQuery selector to use as template
      * @returns {jQuery} this element
      */
    $.fn.template = function(template) {
        if (typeof template === 'undefined') template = this.html();
        else if (typeof template === 'string' && template.charAt(0) === '#') {
            template = $(template).html();
        }

        var $element = this;

        // Define the render() function
        this.bind('render', function(event, data) {
            $element.empty().append(template);

            // Get elements with data bindings
            $element.find('[data-bind],[data-if]').each(function() {
                var key = $(this).attr('data-bind') || $(this).attr('data-if');

                var tag = $(this).prop('tagName');
                if (key in data) {
                    if ($(this).attr('data-bind')) {
                        if (tag === 'img') $(this).attr('src', data[key]);
                        else if (tag === 'input') $(this).val(data[key]);
                        else $(this).text(data[key]);
                    }
                }
                else {
                    $(this).detach();
                }
            });
        });

        return this;
    };

    /**
     * Initial required configurations
     */

    /**
     * JQuery plugin to make an element resizable
     * TODO: missing documentation/source
     */
    $.fn.resizable = function (orientation) {
        var resizer = $('<div>').addClass('resizer').addClass(orientation + '-resize');
        this.append(resizer).addClass('resizable');

        resizer.on('mousedown', initDrag);

        var startX, startY, startWidth, startHeight;

        var resizable = this;

        function initDrag(e) {
            startX = e.clientX;
            startY = e.clientY;
            startWidth = resizable.outerWidth();
            startHeight = resizable.outerHeight();

            $(document).on('mousemove', doDrag);
            $(document).on('mouseup', stopDrag);
        }

        var east, south, north, west;
        south = orientation.search("s") >= 0;
        east = orientation.search("e") >= 0;
        north = orientation.search("n") >= 0;
        west = orientation.search("w") >= 0;

        function doDrag(e) {
            if (south)
                resizable.css('height', (startHeight + e.clientY - startY) + 'px');
            if (east)
                resizable.css('width', (startWidth + e.clientX - startX) + 'px');
            if (north)
                resizable.css('top', (e.clientY) + 'px')
                .css('height', (startHeight + startY - e.clientY) + 'px');
            if (west)
                resizable.css('left', (e.clientX) + 'px')
                .css('width', (startWidth - e.clientX) + 'px');
        }

        function stopDrag(e) {
            $(document).off('mousemove', doDrag);
            $(document).off('mouseup', stopDrag);
            resizable.trigger('resize');
        }

        this.scroll(function (e) {
            resizer.css('top', resizable.scrollTop());
        });

        return this;
    };

    /**
     * Set the id for the jQuery selector
     */
    $.fn.setID = function (selector) {
        if (selector.charAt(0) === '#')
            selector = selector.slice(1);
        this.attr('id', selector);
        return this;
    };

    /**
     * JQuery plugin to make an element movable
     * TODO: missing documentation/source
     */
    $.fn.movable = function () {

        var panel = this.children('.header');
        if (panel.length === 0) {
            panel = $('<div>').addClass('header');
            this.prepend(panel);
        }

        var up = $('<span>').addClass('button').attr('data-icon', 'up-arrow');
        var down = $('<span>').addClass('button').attr('data-icon', 'down-arrow');
        panel.append(up);
        panel.append(down);

        var _this = this;

        up.on('click', function () {
            _this.insertBefore(_this.prev());
        });

        down.on('click', function () {
            _this.insertAfter(_this.next());
        });

        return this;
    };

    /**
     * JQuery plugin to make an element closeable
     * TODO: Missing documentation
     */
    $.fn.closable = function (handler) {
        if (!handler) {
            handler = function () {
                return;
            };
        }

        var panel = this.children('.header');
        if (!panel.length) {
            panel = $('<span>').addClass('header');
            this.prepend(panel);
        }

        var close = $('<span>').addClass('button').attr('data-icon', 'close');

        panel.prepend(close);

        var _this = this;

        close.on('click', function () {
            _this.remove();
            handler();
        });

        return this;
    };

    /**
     * Check if indexOf exists in Array.prototype or use jQuery.inArray();
     */
    Array.prototype.indexOf = 'indexOf' in Array.prototype ? Array.prototype.indexOf : function (item, start) {
        return $.inArray(item, this, start);
    };

    /**
     * Calculate the size of the associative array
     */
    Object.size = function (obj) {
        var size = 0,
            key;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) size++;
        }
        return size;
    };

    /*
     * object.watch polyfill
     *
     * 2012-04-03
     *
     * By Eli Grey, http://eligrey.com
     * Public Domain.
     *
     * Modified by Nenad Damnjanović
     * Nov 9, 2014
     *
     * Source: https://gist.github.com/flackjap/f318e6a2b316e4d9fa44
     *
     * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
     */

    // object.watch
    if (!Object.prototype.watch) {
        Object.defineProperty(Object.prototype, "watch", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (prop, handler) {
                var
                    oldval = this[prop],
                    getter = function () {
                        return oldval;
                    },
                    setter = function (newval) {
                        if (oldval !== newval) {
                            handler.call(this, prop, oldval, newval);
                            oldval = newval;
                        } else {
                            return false;
                        }
                    };

                if (delete this[prop]) { // can't watch constants
                    Object.defineProperty(this, prop, {
                        get: getter,
                        set: setter,
                        enumerable: true,
                        configurable: true
                    });
                }
            }
        });
    }

    if (!Object.prototype.unwatch) {
        Object.defineProperty(Object.prototype, "unwatch", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (prop) {
                var val = this[prop];
                delete this[prop]; // remove accessors
                this[prop] = val;
            }
        });
    }

    /**
     * Spy an object using Object.watch
     */
    if (!Object.prototype.spy) {
        Object.defineProperty(Object.prototype, "spy", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (handler) {
                for (var prop in this) {
                    // Watch all the object properties
                    if (this.hasOwnProperty(prop)) {
                        this.watch(prop, handler);
                    }
                }
            }
        });
    }

    if (!Object.prototype.unspy) {
        Object.defineProperty(Object.prototype, "unspy", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function () {
                for (var prop in this) {
                    // Watch all the object properties
                    if (this.hasOwnProperty(prop)) {
                        this.unwatch(prop);
                    }
                }
            }
        });
    }

    /**
     *
     * Example usage:
     *
     *
    	var o = {p: 1};

    	o.spy(function (id, oldval, newval) {
    	    console.log( "o." + id + " changed from " + oldval + " to " + newval );
    	    return newval;
    	});

    	o.p = 2; // should log the change
    	o.p = 2; // should do nothing
     */

    var dashboard;

    // Store dashboard handlers by name
    var handlers = {};

    /**
     * Constructs an insight element (visualization, layer, etc.)
     *
     * @callback niclabs.insight~handler
     * @param {niclabs.insight.Dashboard} dashboard to assign to the handler
     * @param {Object} options - configuration options for the handler, depending on the kind
     */

    return {
        /**
         * Register a handler of a specific insight element ('layer', 'visualization', etc.)
         * to manage the creation, rendering of a specific part of the UI.
         *
         * Third-party extensions to the insight need only to register their visualization
         * elements with this function for the dashboard UI to correctly recognize them
         * TODO: improve this
         *
         * @memberof niclabs.insight
         * @param {string} name - name for the handler to return, register
         * @param {string=} kind - kind for the handler
         * @param {niclabs.insight~handler=} handler - callback to create the element
         * @returns {niclabs.insight~handler} handler for the registered name
         */
        handler: function (name, kind, handler) {
            if (name in handlers) {
                kind = typeof kind === 'undefined' ? handlers[name].kind : kind;
                handler = typeof handler === 'undefined' ? handlers[name].handler : handler;

                if (kind !== handlers[name].kind) {
                    throw new Error('There already exists a handler with name ' + name + ' for kind ' + handlers[name].kind);
                }
            } else if (typeof kind === 'undefined' && typeof handler === 'undefined') {
                throw new Error('Handler ' + name + ' does not exist');
            }

            handlers[name] = {
                'kind': kind,
                'handler': handler
            };
            return handler;
        },

        /**
         * Get/construct a {@link niclabs.insight.Dashboard}
         *
         * Returns the dashboard for the namespace if options are not provided
         *
         * @example
         * ```javascript
         * // Create a map with the info view to the left
         * var dashboard = niclabs.insight.dashboard({
         *     'anchor': '#dashboard',
         *     'layout': 'left'
         * });
         * ```
         *
         * @memberof niclabs.insight
         * @param {Object=} options - list of configuration options for the dashboard see {@link niclabs.insight.Dashboard}
         * @returns {niclabs.insight.Dashboard} dashboard object
         */
        dashboard: function (options) {
            if (typeof options === 'undefined') return dashboard;

            dashboard = niclabs.insight.Dashboard(options);
            return dashboard;
        },

    };
})(jQuery);
<commit_msg>Added Array.forEach<commit_after>/**
 * Insight is a generic web dashboard for smart city projects.
 *
 * It allows to combine different chart and visualization tools to better
 * understand what is going on in the city
 *
 * @namespace
 * @requires jQuery 1.11.1+
 */
niclabs.insight = (function ($) {
    "use strict";

    // Production steps of ECMA-262, Edition 5, 15.4.4.18
    // Reference: http://es5.github.io/#x15.4.4.18
    if (!Array.prototype.forEach) {

        Array.prototype.forEach = function (callback, thisArg) {

            var T, k;

            if (this === null) {
                throw new TypeError(' this is null or not defined');
            }

            // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
            var O = Object(this);

            // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
            // 3. Let len be ToUint32(lenValue).
            var len = O.length >>> 0;

            // 4. If IsCallable(callback) is false, throw a TypeError exception.
            // See: http://es5.github.com/#x9.11
            if (typeof callback !== "function") {
                throw new TypeError(callback + ' is not a function');
            }

            // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
            if (arguments.length > 1) {
                T = thisArg;
            }

            // 6. Let k be 0
            k = 0;

            // 7. Repeat, while k < len
            while (k < len) {

                var kValue;

                // a. Let Pk be ToString(k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                if (k in O) {

                    // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
                    kValue = O[k];

                    // ii. Call the Call internal method of callback with T as the this value and
                    // argument list containing kValue, k, and O.
                    callback.call(T, kValue, k, O);
                }
                // d. Increase k by 1.
                k++;
            }
            // 8. return undefined
        };
    }

     /**
      * Bind a jquery element to a template
      *
      * It binds the callback 'render' to a function that will render
      * an associative array into the element by using the data-bind
      * attributes
      *
      * You can use the attributes `data-bind` and `data-if`
      * indicate binding or dependency from a data element
      *
      * @example
      * ```html
      * <script id='template' type='text/html'>
      * Hello <span data-bind="name">friend</span><span data-if="name">!!!</span>,
      * this is a template <span data-if="app">for </span><i data-bind="app">your app</i>
      * </script>
      * <script type='text/html'>
      * $('#element').template('#template'); // Will show "Hello, this is a template"
      * $('#content').trigger('render', {name: 'John', app: 'John\'s App'}); // Updates content to "Hello John!!!, this is a template for John's app"
      * </script>
      * ```
      *
      * @param {string|jQuery} template - id for the template element, string with the template or jQuery selector to use as template
      * @returns {jQuery} this element
      */
    $.fn.template = function(template) {
        if (typeof template === 'undefined') template = this.html();
        else if (typeof template === 'string' && template.charAt(0) === '#') {
            template = $(template).html();
        }

        var $element = this;

        // Define the render() function
        this.bind('render', function(event, data) {
            $element.empty().append(template);

            // Get elements with data bindings
            $element.find('[data-bind],[data-if]').each(function() {
                var key = $(this).attr('data-bind') || $(this).attr('data-if');

                var tag = $(this).prop('tagName');
                if (key in data) {
                    if ($(this).attr('data-bind')) {
                        if (tag === 'img') $(this).attr('src', data[key]);
                        else if (tag === 'input') $(this).val(data[key]);
                        else $(this).text(data[key]);
                    }
                }
                else {
                    $(this).detach();
                }
            });
        });

        return this;
    };

    /**
     * Initial required configurations
     */

    /**
     * JQuery plugin to make an element resizable
     * TODO: missing documentation/source
     */
    $.fn.resizable = function (orientation) {
        var resizer = $('<div>').addClass('resizer').addClass(orientation + '-resize');
        this.append(resizer).addClass('resizable');

        resizer.on('mousedown', initDrag);

        var startX, startY, startWidth, startHeight;

        var resizable = this;

        function initDrag(e) {
            startX = e.clientX;
            startY = e.clientY;
            startWidth = resizable.outerWidth();
            startHeight = resizable.outerHeight();

            $(document).on('mousemove', doDrag);
            $(document).on('mouseup', stopDrag);
        }

        var east, south, north, west;
        south = orientation.search("s") >= 0;
        east = orientation.search("e") >= 0;
        north = orientation.search("n") >= 0;
        west = orientation.search("w") >= 0;

        function doDrag(e) {
            if (south)
                resizable.css('height', (startHeight + e.clientY - startY) + 'px');
            if (east)
                resizable.css('width', (startWidth + e.clientX - startX) + 'px');
            if (north)
                resizable.css('top', (e.clientY) + 'px')
                .css('height', (startHeight + startY - e.clientY) + 'px');
            if (west)
                resizable.css('left', (e.clientX) + 'px')
                .css('width', (startWidth - e.clientX) + 'px');
        }

        function stopDrag(e) {
            $(document).off('mousemove', doDrag);
            $(document).off('mouseup', stopDrag);
            resizable.trigger('resize');
        }

        this.scroll(function (e) {
            resizer.css('top', resizable.scrollTop());
        });

        return this;
    };

    /**
     * Set the id for the jQuery selector
     */
    $.fn.setID = function (selector) {
        if (selector.charAt(0) === '#')
            selector = selector.slice(1);
        this.attr('id', selector);
        return this;
    };

    /**
     * JQuery plugin to make an element movable
     * TODO: missing documentation/source
     */
    $.fn.movable = function () {

        var panel = this.children('.header');
        if (panel.length === 0) {
            panel = $('<div>').addClass('header');
            this.prepend(panel);
        }

        var up = $('<span>').addClass('button').attr('data-icon', 'up-arrow');
        var down = $('<span>').addClass('button').attr('data-icon', 'down-arrow');
        panel.append(up);
        panel.append(down);

        var _this = this;

        up.on('click', function () {
            _this.insertBefore(_this.prev());
        });

        down.on('click', function () {
            _this.insertAfter(_this.next());
        });

        return this;
    };

    /**
     * JQuery plugin to make an element closeable
     * TODO: Missing documentation
     */
    $.fn.closable = function (handler) {
        if (!handler) {
            handler = function () {
                return;
            };
        }

        var panel = this.children('.header');
        if (!panel.length) {
            panel = $('<span>').addClass('header');
            this.prepend(panel);
        }

        var close = $('<span>').addClass('button').attr('data-icon', 'close');

        panel.prepend(close);

        var _this = this;

        close.on('click', function () {
            _this.remove();
            handler();
        });

        return this;
    };

    /**
     * Check if indexOf exists in Array.prototype or use jQuery.inArray();
     */
    Array.prototype.indexOf = 'indexOf' in Array.prototype ? Array.prototype.indexOf : function (item, start) {
        return $.inArray(item, this, start);
    };

    /**
     * Calculate the size of the associative array
     */
    Object.size = function (obj) {
        var size = 0,
            key;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) size++;
        }
        return size;
    };

    /*
     * object.watch polyfill
     *
     * 2012-04-03
     *
     * By Eli Grey, http://eligrey.com
     * Public Domain.
     *
     * Modified by Nenad Damnjanović
     * Nov 9, 2014
     *
     * Source: https://gist.github.com/flackjap/f318e6a2b316e4d9fa44
     *
     * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
     */

    // object.watch
    if (!Object.prototype.watch) {
        Object.defineProperty(Object.prototype, "watch", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (prop, handler) {
                var
                    oldval = this[prop],
                    getter = function () {
                        return oldval;
                    },
                    setter = function (newval) {
                        if (oldval !== newval) {
                            handler.call(this, prop, oldval, newval);
                            oldval = newval;
                        } else {
                            return false;
                        }
                    };

                if (delete this[prop]) { // can't watch constants
                    Object.defineProperty(this, prop, {
                        get: getter,
                        set: setter,
                        enumerable: true,
                        configurable: true
                    });
                }
            }
        });
    }

    if (!Object.prototype.unwatch) {
        Object.defineProperty(Object.prototype, "unwatch", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (prop) {
                var val = this[prop];
                delete this[prop]; // remove accessors
                this[prop] = val;
            }
        });
    }

    /**
     * Spy an object using Object.watch
     */
    if (!Object.prototype.spy) {
        Object.defineProperty(Object.prototype, "spy", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function (handler) {
                for (var prop in this) {
                    // Watch all the object properties
                    if (this.hasOwnProperty(prop)) {
                        this.watch(prop, handler);
                    }
                }
            }
        });
    }

    if (!Object.prototype.unspy) {
        Object.defineProperty(Object.prototype, "unspy", {
            enumerable: false,
            configurable: true,
            writable: false,
            value: function () {
                for (var prop in this) {
                    // Watch all the object properties
                    if (this.hasOwnProperty(prop)) {
                        this.unwatch(prop);
                    }
                }
            }
        });
    }

    /**
     *
     * Example usage:
     *
     *
    	var o = {p: 1};

    	o.spy(function (id, oldval, newval) {
    	    console.log( "o." + id + " changed from " + oldval + " to " + newval );
    	    return newval;
    	});

    	o.p = 2; // should log the change
    	o.p = 2; // should do nothing
     */

    var dashboard;

    // Store dashboard handlers by name
    var handlers = {};

    /**
     * Constructs an insight element (visualization, layer, etc.)
     *
     * @callback niclabs.insight~handler
     * @param {niclabs.insight.Dashboard} dashboard to assign to the handler
     * @param {Object} options - configuration options for the handler, depending on the kind
     */

    return {
        /**
         * Register a handler of a specific insight element ('layer', 'visualization', etc.)
         * to manage the creation, rendering of a specific part of the UI.
         *
         * Third-party extensions to the insight need only to register their visualization
         * elements with this function for the dashboard UI to correctly recognize them
         * TODO: improve this
         *
         * @memberof niclabs.insight
         * @param {string} name - name for the handler to return, register
         * @param {string=} kind - kind for the handler
         * @param {niclabs.insight~handler=} handler - callback to create the element
         * @returns {niclabs.insight~handler} handler for the registered name
         */
        handler: function (name, kind, handler) {
            if (name in handlers) {
                kind = typeof kind === 'undefined' ? handlers[name].kind : kind;
                handler = typeof handler === 'undefined' ? handlers[name].handler : handler;

                if (kind !== handlers[name].kind) {
                    throw new Error('There already exists a handler with name ' + name + ' for kind ' + handlers[name].kind);
                }
            } else if (typeof kind === 'undefined' && typeof handler === 'undefined') {
                throw new Error('Handler ' + name + ' does not exist');
            }

            handlers[name] = {
                'kind': kind,
                'handler': handler
            };
            return handler;
        },

        /**
         * Get/construct a {@link niclabs.insight.Dashboard}
         *
         * Returns the dashboard for the namespace if options are not provided
         *
         * @example
         * ```javascript
         * // Create a map with the info view to the left
         * var dashboard = niclabs.insight.dashboard({
         *     'anchor': '#dashboard',
         *     'layout': 'left'
         * });
         * ```
         *
         * @memberof niclabs.insight
         * @param {Object=} options - list of configuration options for the dashboard see {@link niclabs.insight.Dashboard}
         * @returns {niclabs.insight.Dashboard} dashboard object
         */
        dashboard: function (options) {
            if (typeof options === 'undefined') return dashboard;

            dashboard = niclabs.insight.Dashboard(options);
            return dashboard;
        },

    };
})(jQuery);
<|endoftext|>


<commit_before>/**
 * PUSH Notification server
 * (c) Telefonica Digital, 2012 - All rights reserved
 * License: GNU Affero V3 (see LICENSE file)
 * Fernando Rodríguez Sela <frsela@tid.es>
 * Guillermo Lopez Leal <gll@tid.es>
 */

'use strict';

var DataStore = require('../common/DataStore'),
    Log = require('../common/Logger.js'),
    events = require('events'),
    util = require('util'),
    Connectors = require('./connectors/Connector.js').getConnector(),
    errorcodesWS = require('../common/constants.js').errorcodes.UAWS,
    connectionstate = require('../common/constants.js').connectionstate;

function DataManager() {

  this.start = function() {
    events.EventEmitter.call(this);
    this.ready = false;
    Log.info('dataManager --> In-Memory data manager loading.');

    var self = this;
    DataStore.on('ready', function() {
      self.emit('ready');
      self.ready = true;
      Log.info('dataManager --> In-Memory data manager loaded.');
    });
    DataStore.on('closed', function() {
      self.ready = false;
      self.emit('closed');
    });
    process.nextTick(function() {
      DataStore.start();
    });
  },

  this.stop = function() {
    DataStore.stop();
  },

  /**
   * Register a new node. As a parameter, we receive the connector object
   */
  this.registerNode = function(data, connection, callback) {
    Connectors.getConnector(data, connection, function(err, connector) {
      if (err) {
        connection.res({
          errorcode: errorcodesWS.ERROR_GETTING_CONNECTOR,
          extradata: { messageType: 'hello' }
        });
        Log.error(Log.messages.ERROR_WSERRORGETTINGCONNECTION);
        return;
      }

      Log.debug('dataManager::registerNode --> Registraton of the node into datastore ' + data.uaid);
      DataStore.registerNode(
        data.uaid,
        connector.getServer(),
        {
          wakeup_hostport: connector.getInterface(),
          mobilenetwork: connector.getMobileNetwork(),
          protocol: connector.getProtocol(),
          canBeWakeup: connector.canBeWakeup()
        },
        callback
      );
    });
  },

  /**
   * Unregisters (or inform about disconnection) a Node from the DDBB and memory
   */
  this.unregisterNode = function(uaid) {
    Log.debug('dataManager::unregisterNode --> Going to unregister a node');
    if (!uaid) {
      //Might be a connection closed that has no uaid associated (e.g. registerWA without registerUA before)
      Log.debug('dataManager::unregisterNode --> This connection does not have a uaid');
      return;
    }

    Log.debug('dataManager::unregisterNode --> Removing disconnected node uaid ' + uaid);
    //Delete from DDBB
    var connector = Connectors.getConnectorForUAID(uaid);
    var fullyDisconnected = connectionstate.DISCONNECTED;
    var server = '';
    if (!connector) {
      Log.debug('dataManager::unregisterNode --> No connector found for uaid=' + uaid);
    } else {
      fullyDisconnected = connector.canBeWakeup() ? connectionstate.WAKEUP : connectionstate.DISCONNECTED;
      server = connector.getServer();
    }
    DataStore.unregisterNode(
      uaid,
      server,
      fullyDisconnected,
      function(error) {
        if (!error) {
          Log.debug('dataManager::unregisterNode --> Unregistered');
        } else {
          Log.error(Log.messages.ERROR_DMERRORUNREGISTERUA, {
            "uaid": uaid
          });
        }
      }
    );
    if (connector) {
      Connectors.unregisterUAID(uaid);
    }
  },

  /**
   * Gets a node connector (from memory)
   */
  this.getNodeConnector = function (uaid) {
    Log.debug("dataManager::getNodeConnector --> getting node from memory: " + uaid);
    return Connectors.getConnectorForUAID(uaid);
  },

  /**
   * Gets a node info from DB
   */
  this.getNodeData = function(uaid, callback) {
    DataStore.getNodeData(uaid, callback);
  },

  /**
   * Register a new application
   */
  this.registerApplication = function (appToken, channelID, uaid, cert, callback) {
    // Store in persistent storage
    DataStore.registerApplication(appToken, channelID, uaid, cert, callback);
  },

 /**
   * Unregister an old application
   */
 this.unregisterApplication = function (appToken, uaid, callback) {
    // Remove from persistent storage
    DataStore.unregisterApplication(appToken, uaid, callback);
  },

  /**
   * Recover a list of WA associated to a UA
   */
  this.getApplicationsForUA = function (uaid, callback) {
    DataStore.getApplicationsForUA(uaid, callback);
  },

  /**
   * ACKs an ack'ed message
   */
  this.ackMessage = function(uaid, channelID, version) {
    DataStore.ackMessage(uaid, channelID, version);
  }
};

///////////////////////////////////////////
// Singleton
///////////////////////////////////////////
util.inherits(DataManager, events.EventEmitter);
var dm = new DataManager();
function getDataManager() {
  return dm;
}

module.exports = getDataManager();
<commit_msg>Delete datamanager.js<commit_after><|endoftext|>


<commit_before>import * as d3 from 'd3';
import _ from 'lodash';
import { t } from '../util/locale';
import { osmChangeset } from '../osm';
import { uiChangesetEditor } from './changeset_editor';
import { uiCommitChanges } from './commit_changes';
import { uiCommitWarnings } from './commit_warnings';
import { uiRawTagEditor } from './raw_tag_editor';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util';

var changeset;
var readOnlyTags = [
    /^changesets_count$/,
    /^created_by$/,
    /^imagery_used$/,
    /^host$/,
    /^locale$/
];

export function uiCommit(context) {
    var dispatch = d3.dispatch('cancel', 'save'),
        userDetails,
        _selection;

    var changesetEditor = uiChangesetEditor(context)
        .on('change', changeTags);
    var rawTagEditor = uiRawTagEditor(context)
        .on('change', changeTags);
    var commitChanges = uiCommitChanges(context);
    var commitWarnings = uiCommitWarnings(context);

    function commit(selection) {
        _selection = selection;

        var osm = context.connection();
        if (!osm) return;

        var comment = context.storage('comment') || '',
            commentDate = +context.storage('commentDate') || 0,
            hashtags = context.storage('hashtags'),
            currDate = Date.now(),
            cutoff = 2 * 86400 * 1000;   // 2 days

        // expire stored comment and hashtags after cutoff datetime - #3947
        if (commentDate > currDate || currDate - commentDate > cutoff) {
            comment = '';
            hashtags = undefined;
        }

        var tags;
        if (!changeset) {
            var detected = utilDetect();
            tags = {
                comment: comment,
                created_by: ('iD ' + context.version).substr(0, 255),
                imagery_used: context.history().imageryUsed().join(';').substr(0, 255),
                hashtags: hashtags,
                host: detected.host.substr(0, 255),
                locale: detected.locale.substr(0, 255)
            };

            changeset = new osmChangeset({ tags: tags });
        }

        tags = _.clone(changeset.tags);

        var header = selection.selectAll('.header')
            .data([0]);

        header.enter()
            .append('div')
            .attr('class', 'header fillL')
            .append('h3')
            .text(t('commit.title'));

        var body = selection.selectAll('.body')
            .data([0]);

        body = body.enter()
            .append('div')
            .attr('class', 'body')
            .merge(body);

        // Changeset Section
        var changesetSection = body.selectAll('.changeset-editor')
            .data([0]);

        changesetSection = changesetSection.enter()
            .append('div')
            .attr('class', 'modal-section changeset-editor')
            .merge(changesetSection);

        changesetSection
            .call(changesetEditor
                .changesetID(changeset.id)
                .tags(tags)
            );

        // Warnings
        body.call(commitWarnings);

        // Upload Explanation
        var saveSection = body.selectAll('.save-section')
            .data([0]);

        saveSection = saveSection.enter()
            .append('div')
            .attr('class','modal-section save-section fillL cf')
            .merge(saveSection);

        var prose = saveSection.selectAll('.commit-info')
            .data([0]);

        prose = prose.enter()
            .append('p')
            .attr('class', 'commit-info')
            .text(t('commit.upload_explanation'))
            .merge(prose);

        osm.userDetails(function(err, user) {
            if (err) return;

            var userLink = d3.select(document.createElement('div'));

            userDetails = user;

            if (user.image_url) {
                userLink
                    .append('img')
                    .attr('src', user.image_url)
                    .attr('class', 'icon pre-text user-icon');
            }

            userLink
                .append('a')
                .attr('class','user-info')
                .text(user.display_name)
                .attr('href', osm.userURL(user.display_name))
                .attr('tabindex', -1)
                .attr('target', '_blank');

            prose
                .html(t('commit.upload_explanation_with_user', { user: userLink.html() }));
        });

        var requestReview = saveSection.selectAll('.request-review')
            .data([0]);

        requestReview = requestReview.enter()
            .append('p')
            .attr('class', 'request-review')
            .text(t('commit.request_review'))
            .merge(requestReview);

        var requestReviewField = requestReview.selectAll('input')
            .data([0]);

        requestReviewField = requestReviewField.enter()
            .append('input')
            .attr('type', 'checkbox')
            .merge(requestReviewField);

        requestReviewField
            .property('checked', isReviewRequested(changeset.tags))
            .on('change', toggleRequestReview);

        // Buttons
        var buttonSection = saveSection.selectAll('.buttons')
            .data([0]);

        // enter
        var buttonEnter = buttonSection.enter()
            .append('div')
            .attr('class', 'buttons fillL cf');

        buttonEnter
            .append('button')
            .attr('class', 'secondary-action col5 button cancel-button')
            .append('span')
            .attr('class', 'label')
            .text(t('commit.cancel'));

        buttonEnter
            .append('button')
            .attr('class', 'action col5 button save-button')
            .append('span')
            .attr('class', 'label')
            .text(t('commit.save'));

        // update
        buttonSection = buttonSection
            .merge(buttonEnter);

        buttonSection.selectAll('.cancel-button')
            .on('click.cancel', function() {
                dispatch.call('cancel');
            });

        buttonSection.selectAll('.save-button')
            .attr('disabled', function() {
                var n = d3.select('#preset-input-comment').node();
                return (n && n.value.length) ? null : true;
            })
            .on('click.save', function() {
                dispatch.call('save', this, changeset);
            });

        // Raw Tag Editor
        var tagSection = body.selectAll('.tag-section.raw-tag-editor')
            .data([0]);

        tagSection = tagSection.enter()
            .append('div')
            .attr('class', 'modal-section tag-section raw-tag-editor')
            .merge(tagSection);

        var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();
        tagSection
            .call(rawTagEditor
                .expanded(expanded)
                .readOnlyTags(readOnlyTags)
                .tags(_.clone(changeset.tags))
            );

        // Change summary
        body.call(commitChanges);

        function toggleRequestReview() {
            var rr = requestReviewField.property('checked');
            updateChangeset({ review_requested: (rr ? 'yes' : undefined) });

            var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();

            tagSection
                .call(rawTagEditor
                    .expanded(expanded)
                    .readOnlyTags(readOnlyTags)
                    .tags(_.clone(changeset.tags))
                );
        }
    }

    function changeTags(changed, onInput) {
        if (changed.hasOwnProperty('comment')) {
            if (changed.comment === undefined) {
                changed.comment = '';
            }
            if (!onInput) {
                context.storage('comment', changed.comment);
                context.storage('commentDate', Date.now());
            }
        }

        updateChangeset(changed, onInput);

        if (_selection) {
            _selection.call(commit);
        }
    }

    function findHashtags(tags) {
        return _.unionBy(commentTags(), hashTags(), function (s) {
            return s.toLowerCase();
        });

        // Extract hashtags from `comment`
        function commentTags() {
            return tags.comment.match(/#[^\s\#]+/g);
        }

        // Extract and clean hashtags from `hashtags`
        function hashTags() {
            var t = tags.hashtags || '';
            return t
                .split(/[,;\s]+/)
                .map(function (s) {
                    if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
                    var matched = s.match(/#[^\s\#]+/g);  // match valid hashtags
                    return matched && matched[0];
                }).filter(Boolean);                       // exclude falsey
        }
    }

    function isReviewRequested(tags) {
        var rr = tags.review_requested;
        if (rr === undefined) return false;
        rr = rr.trim().toLowerCase();
        return !(rr === '' || rr === 'no');
    }

    function updateChangeset(changed, onInput) {
        var tags = _.clone(changeset.tags);

        _.forEach(changed, function(v, k) {
            k = k.trim().substr(0, 255);
            if (readOnlyTags.indexOf(k) !== -1) return;

            if (k !== '' && v !== undefined) {
                if (onInput) {
                    tags[k] = v;
                } else {
                    tags[k] = v.trim().substr(0, 255);
                }
            } else {
                delete tags[k];
            }
        });

        if (!onInput) {
            var arr = findHashtags(tags);
            if (arr.length) {
                tags.hashtags = arr.join(';').substr(0, 255);
                context.storage('hashtags', tags.hashtags);
            } else {
                context.storage('hashtags', null);
            }
        }

        // always update userdetails, just in case user reauthenticates as someone else
        if (userDetails && userDetails.changesets_count !== undefined) {
            tags.changesets_count = String(userDetails.changesets_count);
        } else {
            delete tags.changesets_count;
        }

        if (!_.isEqual(changeset.tags, tags)) {
            changeset = changeset.update({ tags: tags });
        }
    }

    commit.reset = function() {
        changeset = null;
    };

    return utilRebind(commit, dispatch, 'on');
}
<commit_msg>Write changeset tags for new mappers to indicate walkthrough progress These tags all start with `ideditor:` (closes #3968)<commit_after>import * as d3 from 'd3';
import _ from 'lodash';
import { t } from '../util/locale';
import { osmChangeset } from '../osm';
import { uiChangesetEditor } from './changeset_editor';
import { uiCommitChanges } from './commit_changes';
import { uiCommitWarnings } from './commit_warnings';
import { uiRawTagEditor } from './raw_tag_editor';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util';

var changeset;
var readOnlyTags = [
    /^changesets_count$/,
    /^created_by$/,
    /^ideditor:/,
    /^imagery_used$/,
    /^host$/,
    /^locale$/
];

export function uiCommit(context) {
    var dispatch = d3.dispatch('cancel', 'save'),
        userDetails,
        _selection;

    var changesetEditor = uiChangesetEditor(context)
        .on('change', changeTags);
    var rawTagEditor = uiRawTagEditor(context)
        .on('change', changeTags);
    var commitChanges = uiCommitChanges(context);
    var commitWarnings = uiCommitWarnings(context);

    function commit(selection) {
        _selection = selection;

        var osm = context.connection();
        if (!osm) return;

        var comment = context.storage('comment') || '',
            commentDate = +context.storage('commentDate') || 0,
            hashtags = context.storage('hashtags'),
            currDate = Date.now(),
            cutoff = 2 * 86400 * 1000;   // 2 days

        // expire stored comment and hashtags after cutoff datetime - #3947
        if (commentDate > currDate || currDate - commentDate > cutoff) {
            comment = '';
            hashtags = undefined;
        }

        var tags;
        if (!changeset) {
            var detected = utilDetect();
            tags = {
                comment: comment,
                created_by: ('iD ' + context.version).substr(0, 255),
                imagery_used: context.history().imageryUsed().join(';').substr(0, 255),
                hashtags: hashtags,
                host: detected.host.substr(0, 255),
                locale: detected.locale.substr(0, 255)
            };

            changeset = new osmChangeset({ tags: tags });
        }

        tags = _.clone(changeset.tags);

        var header = selection.selectAll('.header')
            .data([0]);

        header.enter()
            .append('div')
            .attr('class', 'header fillL')
            .append('h3')
            .text(t('commit.title'));

        var body = selection.selectAll('.body')
            .data([0]);

        body = body.enter()
            .append('div')
            .attr('class', 'body')
            .merge(body);

        // Changeset Section
        var changesetSection = body.selectAll('.changeset-editor')
            .data([0]);

        changesetSection = changesetSection.enter()
            .append('div')
            .attr('class', 'modal-section changeset-editor')
            .merge(changesetSection);

        changesetSection
            .call(changesetEditor
                .changesetID(changeset.id)
                .tags(tags)
            );

        // Warnings
        body.call(commitWarnings);

        // Upload Explanation
        var saveSection = body.selectAll('.save-section')
            .data([0]);

        saveSection = saveSection.enter()
            .append('div')
            .attr('class','modal-section save-section fillL cf')
            .merge(saveSection);

        var prose = saveSection.selectAll('.commit-info')
            .data([0]);

        prose = prose.enter()
            .append('p')
            .attr('class', 'commit-info')
            .text(t('commit.upload_explanation'))
            .merge(prose);

        osm.userDetails(function(err, user) {
            if (err) return;

            var userLink = d3.select(document.createElement('div'));

            userDetails = user;

            if (user.image_url) {
                userLink
                    .append('img')
                    .attr('src', user.image_url)
                    .attr('class', 'icon pre-text user-icon');
            }

            userLink
                .append('a')
                .attr('class','user-info')
                .text(user.display_name)
                .attr('href', osm.userURL(user.display_name))
                .attr('tabindex', -1)
                .attr('target', '_blank');

            prose
                .html(t('commit.upload_explanation_with_user', { user: userLink.html() }));
        });

        var requestReview = saveSection.selectAll('.request-review')
            .data([0]);

        requestReview = requestReview.enter()
            .append('p')
            .attr('class', 'request-review')
            .text(t('commit.request_review'))
            .merge(requestReview);

        var requestReviewField = requestReview.selectAll('input')
            .data([0]);

        requestReviewField = requestReviewField.enter()
            .append('input')
            .attr('type', 'checkbox')
            .merge(requestReviewField);

        requestReviewField
            .property('checked', isReviewRequested(changeset.tags))
            .on('change', toggleRequestReview);

        // Buttons
        var buttonSection = saveSection.selectAll('.buttons')
            .data([0]);

        // enter
        var buttonEnter = buttonSection.enter()
            .append('div')
            .attr('class', 'buttons fillL cf');

        buttonEnter
            .append('button')
            .attr('class', 'secondary-action col5 button cancel-button')
            .append('span')
            .attr('class', 'label')
            .text(t('commit.cancel'));

        buttonEnter
            .append('button')
            .attr('class', 'action col5 button save-button')
            .append('span')
            .attr('class', 'label')
            .text(t('commit.save'));

        // update
        buttonSection = buttonSection
            .merge(buttonEnter);

        buttonSection.selectAll('.cancel-button')
            .on('click.cancel', function() {
                dispatch.call('cancel');
            });

        buttonSection.selectAll('.save-button')
            .attr('disabled', function() {
                var n = d3.select('#preset-input-comment').node();
                return (n && n.value.length) ? null : true;
            })
            .on('click.save', function() {
                dispatch.call('save', this, changeset);
            });

        // Raw Tag Editor
        var tagSection = body.selectAll('.tag-section.raw-tag-editor')
            .data([0]);

        tagSection = tagSection.enter()
            .append('div')
            .attr('class', 'modal-section tag-section raw-tag-editor')
            .merge(tagSection);

        var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();
        tagSection
            .call(rawTagEditor
                .expanded(expanded)
                .readOnlyTags(readOnlyTags)
                .tags(_.clone(changeset.tags))
            );

        // Change summary
        body.call(commitChanges);

        function toggleRequestReview() {
            var rr = requestReviewField.property('checked');
            updateChangeset({ review_requested: (rr ? 'yes' : undefined) });

            var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();

            tagSection
                .call(rawTagEditor
                    .expanded(expanded)
                    .readOnlyTags(readOnlyTags)
                    .tags(_.clone(changeset.tags))
                );
        }
    }

    function changeTags(changed, onInput) {
        if (changed.hasOwnProperty('comment')) {
            if (changed.comment === undefined) {
                changed.comment = '';
            }
            if (!onInput) {
                context.storage('comment', changed.comment);
                context.storage('commentDate', Date.now());
            }
        }

        updateChangeset(changed, onInput);

        if (_selection) {
            _selection.call(commit);
        }
    }

    function findHashtags(tags) {
        return _.unionBy(commentTags(), hashTags(), function (s) {
            return s.toLowerCase();
        });

        // Extract hashtags from `comment`
        function commentTags() {
            return tags.comment.match(/#[^\s\#]+/g);
        }

        // Extract and clean hashtags from `hashtags`
        function hashTags() {
            var t = tags.hashtags || '';
            return t
                .split(/[,;\s]+/)
                .map(function (s) {
                    if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
                    var matched = s.match(/#[^\s\#]+/g);  // match valid hashtags
                    return matched && matched[0];
                }).filter(Boolean);                       // exclude falsey
        }
    }

    function isReviewRequested(tags) {
        var rr = tags.review_requested;
        if (rr === undefined) return false;
        rr = rr.trim().toLowerCase();
        return !(rr === '' || rr === 'no');
    }

    function updateChangeset(changed, onInput) {
        var tags = _.clone(changeset.tags);

        _.forEach(changed, function(v, k) {
            k = k.trim().substr(0, 255);
            if (readOnlyTags.indexOf(k) !== -1) return;

            if (k !== '' && v !== undefined) {
                if (onInput) {
                    tags[k] = v;
                } else {
                    tags[k] = v.trim().substr(0, 255);
                }
            } else {
                delete tags[k];
            }
        });

        if (!onInput) {
            var arr = findHashtags(tags);
            if (arr.length) {
                tags.hashtags = arr.join(';').substr(0, 255);
                context.storage('hashtags', tags.hashtags);
            } else {
                context.storage('hashtags', null);
            }
        }

        // always update userdetails, just in case user reauthenticates as someone else
        if (userDetails && userDetails.changesets_count !== undefined) {
            tags.changesets_count = String(userDetails.changesets_count);

            // first 100 edits - new user
            if (parseInt(tags.changesets_count, 10) < 100) {
                var s;
                s = context.storage('walkthrough_completed');
                if (s) {
                    tags['ideditor:walkthrough_completed'] = s;
                }

                s = context.storage('walkthrough_progress');
                if (s) {
                    tags['ideditor:walkthrough_progress'] = s;
                }

                s = context.storage('walkthrough_started');
                if (s) {
                    tags['ideditor:walkthrough_started'] = s;
                }
            }
        } else {
            delete tags.changesets_count;
        }

        if (!_.isEqual(changeset.tags, tags)) {
            changeset = changeset.update({ tags: tags });
        }
    }

    commit.reset = function() {
        changeset = null;
    };

    return utilRebind(commit, dispatch, 'on');
}
<|endoftext|>


<commit_before>angular.module('oauth.windowslive', ['oauth.utils'])
  .factory('$ngCordovaWindowslive', windowslive);

function windowslive($q, $http, $cordovaOauthUtility) {
  return { signin: oauthWindowslive };

  /*
   * Sign into the Windows Live Connect service
   *
   * @param    string clientId
   * @param    array appScope
   * @param    object options
   * @return   promise
  */
  function oauthWindowslive(clientId, appScope, options) {
    var deferred = $q.defer();
    if(window.cordova) {
      if($cordovaOauthUtility.isInAppBrowserInstalled()) {
        var redirect_uri = "https://login.live.com/oauth20_desktop.srf";
        if(options !== undefined) {
          if(options.hasOwnProperty("redirect_uri")) {
            redirect_uri = options.redirect_uri;
          }
        }
        var browserRef = window.cordova.InAppBrowser.open('https://login.live.com/oauth20_authorize.srf?client_id=' + clientId + "&scope=" + appScope.join(",") + '&response_type=token&display=touch' + '&redirect_uri=' + redirect_uri, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
        browserRef.addEventListener('loadstart', function (event) {
          if((event.url).indexOf(redirect_uri) === 0) {
            browserRef.removeEventListener("exit", function (event) { });
            browserRef.close();
            var callbackResponse = (event.url).split("#")[1];
            var responseParameters = (callbackResponse).split("&");
            var parameterMap = [];

            for (var i = 0; i < responseParameters.length; i++) {
              parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
            }

            if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
              deferred.resolve({ access_token: parameterMap.access_token, expires_in: parameterMap.expires_in });
            } else {
              deferred.reject("Problem authenticating");
            }
          }
        });
        browserRef.addEventListener('exit', function (event) {
          deferred.reject("The sign in flow was canceled");
        });
      } else {
        deferred.reject("Could not find InAppBrowser plugin");
      }
    } else {
      deferred.reject("Cannot authenticate via a web browser");
    }

    return deferred.promise;
  }
}

windowslive.$inject = ['$q', '$http', '$cordovaOauthUtility'];
<commit_msg>IIFE windowslive<commit_after>(function() {
  'use strict';

  angular.module('oauth.windowslive', ['oauth.utils'])
    .factory('$ngCordovaWindowslive', windowslive);

  function windowslive($q, $http, $cordovaOauthUtility) {
    return { signin: oauthWindowslive };

    /*
     * Sign into the Windows Live Connect service
     *
     * @param    string clientId
     * @param    array appScope
     * @param    object options
     * @return   promise
    */
    function oauthWindowslive(clientId, appScope, options) {
      var deferred = $q.defer();
      if(window.cordova) {
        if($cordovaOauthUtility.isInAppBrowserInstalled()) {
          var redirect_uri = "https://login.live.com/oauth20_desktop.srf";
          if(options !== undefined) {
            if(options.hasOwnProperty("redirect_uri")) {
              redirect_uri = options.redirect_uri;
            }
          }
          var browserRef = window.cordova.InAppBrowser.open('https://login.live.com/oauth20_authorize.srf?client_id=' + clientId + "&scope=" + appScope.join(",") + '&response_type=token&display=touch' + '&redirect_uri=' + redirect_uri, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
          browserRef.addEventListener('loadstart', function (event) {
            if((event.url).indexOf(redirect_uri) === 0) {
              browserRef.removeEventListener("exit", function (event) { });
              browserRef.close();
              var callbackResponse = (event.url).split("#")[1];
              var responseParameters = (callbackResponse).split("&");
              var parameterMap = [];

              for (var i = 0; i < responseParameters.length; i++) {
                parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
              }

              if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                deferred.resolve({ access_token: parameterMap.access_token, expires_in: parameterMap.expires_in });
              } else {
                deferred.reject("Problem authenticating");
              }
            }
          });
          browserRef.addEventListener('exit', function (event) {
            deferred.reject("The sign in flow was canceled");
          });
        } else {
          deferred.reject("Could not find InAppBrowser plugin");
        }
      } else {
        deferred.reject("Cannot authenticate via a web browser");
      }

      return deferred.promise;
    }
  }

  windowslive.$inject = ['$q', '$http', '$cordovaOauthUtility'];
})();
<|endoftext|>