<commit_before>/*
 * Copyright 2005-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// global vars
var $dialog = null;
var jq = jQuery.noConflict();

//clear out blockUI css, using css class overrides
jQuery.blockUI.defaults.css = {};
jQuery.blockUI.defaults.overlayCSS = {};

// validation init
var pageValidatorReady = false;

//sets up the validator with the necessary default settings and methods
//note the use of onClick and onFocusout for on the fly validation client side
function setupValidator(){
	jq('#kualiForm').dirty_form({changedClass: 'dirty'});

	//Make sure form doesn't have any unsaved data when user clicks on any other portal links, closes browser or presses fwd/back browser button
	jq(window).bind('beforeunload', function(evt){
		var validateDirty = jq("[name='validateDirty']").val();
		if (validateDirty == "true")
		{
			var dirty = jq(".kr-field-attribute").find("input.dirty");
			//methodToCall check is needed to skip from normal way of unloading (cancel,save,close)
			var methodToCall = jq("[name='methodToCall']").val();
			if (dirty.length > 0 && methodToCall == null)
			{
				return "Form has unsaved data. Do you want to leave anyway?";
			}
		}
	});

	jq('#kualiForm').validate(
	{
		onsubmit: false,
		ignore: ".ignoreValid",
		onclick: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
		},
		onfocusout: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
			if((element.type == "select-one" || element.type == "select-multiple") && jq(element).hasClass("valid")){
				applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
				showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
			}
		},
		wrapper: "li",
		highlight: function(element, errorClass, validClass) {
			jq(element).addClass(errorClass).removeClass(validClass);
            jq(element).attr("aria-invalid", "true");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 1, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 1);
		},
		unhighlight: function(element, errorClass, validClass) {
			jq(element).removeClass(errorClass).addClass(validClass);
            jq(element).attr("aria-invalid", "false");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
		},
		errorPlacement: function(error, element) {
			var id = getAttributeId(element.attr('id'), element[0].type);
			//check to see if the option to use labels is on
			if (!jq("#" + id + "_errors_div").hasClass("noLabels")) {
				var label = getLabel(id);
				label = jq.trim(label);
				if (label) {
					if (label.charAt(label.length - 1) == ":") {
						label = label.slice(0, -1);
					}
					error.find("label").before(label + " - ");
				}
			}
			jq("#" + id + "_errors_div").show();
			jq("#" + id + "_errors_errorMessages").show();
			var errorList = jq("#" + id + "_errors_errorMessages ul");
			error.appendTo(errorList);
		}
	});

    jq(".required").each(function(){
        jq(this).attr("aria-required", "true");
    });

	jq(document).trigger('validationSetup');
	pageValidatorReady = true;

	jq.watermark.showAll();
}

jQuery.validator.addMethod("minExclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value > param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxInclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value <= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("minLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) >= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) <= param[0];
	}
	else{
		return true;
	}
});

// data table initialize default sorting
jQuery.fn.dataTableExt.oSort['kuali_date-asc']  = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_date-desc'] = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_percent-asc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num1 - num2;
};

jQuery.fn.dataTableExt.oSort['kuali_percent-desc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num2 - num1;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-asc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x*1;
	y = isNaN(y) ? 0 : y*1;

	return x - y;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-desc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x;
	y = isNaN(y) ? 0 : y;

	return y - x;
};

jQuery.fn.dataTableExt.afnSortData['dom-text'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var input = jq(this).find('input:text');
		if(input.length != 0){
			aData.push( input.val() );
		}else{
            //match DataField or InputField CSS classes - respectively
			var input1 = jq(this).find('.kr-data-fld, .kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.find("span:first").text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the select options in a column */
jQuery.fn.dataTableExt.afnSortData['dom-select'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var selected = jq(this).find('select option:selected:first');
		if(selected.length != 0){
			aData.push( selected.text() );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the checkboxes in a column */
jQuery.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var checkboxes = jq(this).find('input:checkbox');
		if(checkboxes.length != 0){
			var str = "";
			for(i=0; i < checkboxes.length; i++){
				var check = checkboxes[i];
				if (check.checked == true && check.value.length > 0){
					str += check.value + " ";
				}
			}
			aData.push( str );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

jQuery.fn.dataTableExt.afnSortData['dom-radio'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var radioButtons = jq(this).find('input:radio');
		if(radioButtons.length != 0){
			var value = "";
			for(i=0; i < radioButtons.length; i++){
				var radio = radioButtons[i];
				if (radio.checked == true){
					value = radio.value;
					break;
				}
			}
			aData.push( value );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

// setup window javascript error handler
window.onerror = errorHandler;

function errorHandler(msg,url,lno)
{
  jq("#view_div").show();
  jq("#viewpage_div").show();
  var context = getContext();
  context.unblockUI();
  showGrowl(msg + '
' + url + '
' + lno, 'Javascript Error', 'errorGrowl');
  return false;
}

// common event registering done here through JQuery ready event
jq(document).ready(function() {
	setPageBreadcrumb();

	// buttons
	jq("input:submit").button();
	jq("input:button").button();
    jq("a.button").button();

    // common ajax setup
	jq.ajaxSetup({
		  beforeSend: function() {
		     createLoading(true);
		  },
		  complete: function(){
			 createLoading(false);
		  },
		  error: function(jqXHR, textStatus, errorThrown){
			 createLoading(false);
			 showGrowl('Status: ' + textStatus + '
' + errorThrown, 'Server Response Error', 'errorGrowl');
		  }
	});

	runHiddenScripts("");
    jq("#view_div").show();
    createLoading(false);
});

<commit_msg>KULRICE-5772 - preventing onchange validation on select boxes so that Firefox and Chrome don't kick the user out of the control immediately.<commit_after>/*
 * Copyright 2005-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// global vars
var $dialog = null;
var jq = jQuery.noConflict();

//clear out blockUI css, using css class overrides
jQuery.blockUI.defaults.css = {};
jQuery.blockUI.defaults.overlayCSS = {};

// validation init
var pageValidatorReady = false;

//sets up the validator with the necessary default settings and methods
//note the use of onClick and onFocusout for on the fly validation client side
function setupValidator(){
	jq('#kualiForm').dirty_form({changedClass: 'dirty'});

	//Make sure form doesn't have any unsaved data when user clicks on any other portal links, closes browser or presses fwd/back browser button
	jq(window).bind('beforeunload', function(evt){
		var validateDirty = jq("[name='validateDirty']").val();
		if (validateDirty == "true")
		{
			var dirty = jq(".kr-field-attribute").find("input.dirty");
			//methodToCall check is needed to skip from normal way of unloading (cancel,save,close)
			var methodToCall = jq("[name='methodToCall']").val();
			if (dirty.length > 0 && methodToCall == null)
			{
				return "Form has unsaved data. Do you want to leave anyway?";
			}
		}
	});

	jq('#kualiForm').validate(
	{
		onsubmit: false,
		ignore: ".ignoreValid",
		onclick: function(element) {
            if(element.type != "select-one") {
			    var valid = jq(element).valid();
			    dependsOnCheck(element, new Array());
            }
		},
		onfocusout: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
			if((element.type == "select-one" || element.type == "select-multiple") && jq(element).hasClass("valid")){
				applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
				showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
			}
		},
		wrapper: "li",
		highlight: function(element, errorClass, validClass) {
			jq(element).addClass(errorClass).removeClass(validClass);
            jq(element).attr("aria-invalid", "true");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 1, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 1);
		},
		unhighlight: function(element, errorClass, validClass) {
			jq(element).removeClass(errorClass).addClass(validClass);
            jq(element).attr("aria-invalid", "false");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
		},
		errorPlacement: function(error, element) {
			var id = getAttributeId(element.attr('id'), element[0].type);
			//check to see if the option to use labels is on
			if (!jq("#" + id + "_errors_div").hasClass("noLabels")) {
				var label = getLabel(id);
				label = jq.trim(label);
				if (label) {
					if (label.charAt(label.length - 1) == ":") {
						label = label.slice(0, -1);
					}
					error.find("label").before(label + " - ");
				}
			}
			jq("#" + id + "_errors_div").show();
			jq("#" + id + "_errors_errorMessages").show();
			var errorList = jq("#" + id + "_errors_errorMessages ul");
			error.appendTo(errorList);
		}
	});

    jq(".required").each(function(){
        jq(this).attr("aria-required", "true");
    });

	jq(document).trigger('validationSetup');
	pageValidatorReady = true;

	jq.watermark.showAll();
}

jQuery.validator.addMethod("minExclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value > param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxInclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value <= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("minLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) >= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) <= param[0];
	}
	else{
		return true;
	}
});

// data table initialize default sorting
jQuery.fn.dataTableExt.oSort['kuali_date-asc']  = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_date-desc'] = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_percent-asc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num1 - num2;
};

jQuery.fn.dataTableExt.oSort['kuali_percent-desc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num2 - num1;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-asc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x*1;
	y = isNaN(y) ? 0 : y*1;

	return x - y;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-desc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x;
	y = isNaN(y) ? 0 : y;

	return y - x;
};

jQuery.fn.dataTableExt.afnSortData['dom-text'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var input = jq(this).find('input:text');
		if(input.length != 0){
			aData.push( input.val() );
		}else{
            //match DataField or InputField CSS classes - respectively
			var input1 = jq(this).find('.kr-data-fld, .kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.find("span:first").text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the select options in a column */
jQuery.fn.dataTableExt.afnSortData['dom-select'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var selected = jq(this).find('select option:selected:first');
		if(selected.length != 0){
			aData.push( selected.text() );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the checkboxes in a column */
jQuery.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var checkboxes = jq(this).find('input:checkbox');
		if(checkboxes.length != 0){
			var str = "";
			for(i=0; i < checkboxes.length; i++){
				var check = checkboxes[i];
				if (check.checked == true && check.value.length > 0){
					str += check.value + " ";
				}
			}
			aData.push( str );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

jQuery.fn.dataTableExt.afnSortData['dom-radio'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var radioButtons = jq(this).find('input:radio');
		if(radioButtons.length != 0){
			var value = "";
			for(i=0; i < radioButtons.length; i++){
				var radio = radioButtons[i];
				if (radio.checked == true){
					value = radio.value;
					break;
				}
			}
			aData.push( value );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

// setup window javascript error handler
window.onerror = errorHandler;

function errorHandler(msg,url,lno)
{
  jq("#view_div").show();
  jq("#viewpage_div").show();
  var context = getContext();
  context.unblockUI();
  showGrowl(msg + '
' + url + '
' + lno, 'Javascript Error', 'errorGrowl');
  return false;
}

// common event registering done here through JQuery ready event
jq(document).ready(function() {
	setPageBreadcrumb();

	// buttons
	jq("input:submit").button();
	jq("input:button").button();
    jq("a.button").button();

    // common ajax setup
	jq.ajaxSetup({
		  beforeSend: function() {
		     createLoading(true);
		  },
		  complete: function(){
			 createLoading(false);
		  },
		  error: function(jqXHR, textStatus, errorThrown){
			 createLoading(false);
			 showGrowl('Status: ' + textStatus + '
' + errorThrown, 'Server Response Error', 'errorGrowl');
		  }
	});

	runHiddenScripts("");
    jq("#view_div").show();
    createLoading(false);
});

<|endoftext|>


<commit_before>var debug = require('debug')('tag');
var should = require('should');
var fs = require('fs');
var URL = require('url');
var express = require('express');

var runner = require('../spawner');
var tag = require('../tag');

var ports = {
	app: 3000,
	ngx: 3001,
	memc: 3002
};

describe("Tag", function suite() {
	var servers, app;
	var testPath = '/tag-test';
	var counters = {};

	function count(uri, inc) {
		if (typeof uri != "string") {
			if (uri.get) uri = uri.protocol + '://' + uri.get('Host') + uri.path;
			else uri = URL.format(Object.assign({
				protocol: 'http',
				hostname: 'localhost',
				pathname: uri.path
			}, uri));
		}
		var counter = counters[uri];
		if (counter == null) counter = counters[uri] = 0;
		if (inc) counters[uri] += inc;
		return counters[uri];
	}

	before(function(done) {
		servers = runner(ports, done);

		app = express();
		app.server = app.listen(ports.app);

		app.get('/a', tag('global'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.get(testPath, tag('test'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.post(testPath, tag(), function(req, res, next) {
			res.send('OK');
		});

		app.post("/a", tag('test'), function(req, res, next) {
			res.send('OK');
		});

		app.get('/multiple', tag('one'), tag('two'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.post("/multiple", tag(), function(req, res, next) {
			res.send('OK');
		});
	});

	after(function(done) {
		app.server.close();
		servers.close(done);
	});

	it("should cache a url", function() {
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req).then(function(res) {
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.get(req);
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', 'test');
			count(req).should.equal(1);
		});
	});

	it("should invalidate a tag using a post", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.post(req, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+test');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});

	it("should invalidate one tag on a route with multiple tags using a post", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: "/multiple"
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'one, two');
			return runner.post(req, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+one, +two');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});

	it("should invalidate a tag using a post to a different path", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.post({
				port: ports.ngx,
				path: "/a"
			}, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+test');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});
});
<commit_msg>Update tag test<commit_after>var debug = require('debug')('tag');
var should = require('should');
var fs = require('fs');
var URL = require('url');
var express = require('express');

var runner = require('../spawner');
var tag = require('../tag');

var ports = {
	app: 3000,
	ngx: 3001,
	memc: 3002
};

describe("Tag", function suite() {
	var servers, app;
	var testPath = '/tag-test';
	var counters = {};

	function count(uri, inc) {
		if (typeof uri != "string") {
			if (uri.get) uri = uri.protocol + '://' + uri.get('Host') + uri.path;
			else uri = URL.format(Object.assign({
				protocol: 'http',
				hostname: 'localhost',
				pathname: uri.path
			}, uri));
		}
		var counter = counters[uri];
		if (counter == null) counter = counters[uri] = 0;
		if (inc) counters[uri] += inc;
		return counters[uri];
	}

	before(function(done) {
		servers = runner(ports, done);

		app = express();
		app.server = app.listen(ports.app);

		app.get('/a', tag('global'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.get(testPath, tag('test'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.post(testPath, tag('test'), function(req, res, next) {
			res.send('OK');
		});

		app.post("/a", tag('test'), function(req, res, next) {
			res.send('OK');
		});

		app.get('/multiple', tag('one'), tag('two'), function(req, res, next) {
			count(req, 1);
			res.send({
				value: (req.path || '/').substring(1),
				date: new Date()
			});
		});

		app.post("/multiple", tag('two'), function(req, res, next) {
			res.send('OK');
		});
	});

	after(function(done) {
		app.server.close();
		servers.close(done);
	});

	it("should cache a url", function() {
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req).then(function(res) {
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.get(req);
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', 'test');
			count(req).should.equal(1);
		});
	});

	it("should invalidate a tag using a post", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.post(req, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+test');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});

	it("should invalidate one tag on a route with multiple tags using a post", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: "/multiple"
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'one, two');
			return runner.post(req, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+two');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});

	it("should invalidate a tag using a post to a different path", function() {
		var firstDate;
		var req = {
			port: ports.ngx,
			path: testPath
		};
		return runner.get(req)
		.then(function(res) {
			firstDate = Date.parse(res.body.date);
			res.headers.should.have.property('x-cache-tag', 'test');
			return runner.post({
				port: ports.ngx,
				path: "/a"
			}, 'postbody');
		}).then(function(res) {
			res.headers.should.have.property('x-cache-tag', '+test');
			return runner.get(req);
		}).then(function(res) {
			Date.parse(res.body.date).should.be.greaterThan(firstDate);
		});
	});
});
<|endoftext|>


<commit_before>import {
    TYPE_OPERATIONAL,
    TYPE_PAGE,
    TYPE_TRACK,
    TYPE_UI
} from '../../service/statistics/AnalyticsEvents';
import { getLogger } from 'jitsi-meet-logger';
import browser from '../browser';
import Settings from '../settings/Settings';

const MAX_CACHE_SIZE = 100;

// eslist-disable-line no-undef
const logger = getLogger(__filename);

/**
 * This class provides an API to lib-jitsi-meet and its users for sending
 * analytics events. It serves as a bridge to different backend implementations
 * ("analytics handlers") and a cache for events attempted to be sent before
 * the analytics handlers were enabled.
 *
 * The API is designed to be an easy replacement for the previous version of
 * this adapter, and is meant to be extended with more convenience methods.
 *
 *
 * The API calls are translated to objects with the following structure, which
 * are then passed to the sendEvent(event) function of the underlying handlers:
 *
 * {
 *    type,
 *
 *    action,
 *    actionSubject,
 *    actionSubjectId,
 *    attributes,
 *    categories,
 *    containerId,
 *    containerType,
 *    name,
 *    objectId,
 *    objectType,
 *    source,
 *    tags
 * }
 *
 * The 'type' is one of 'operational', 'page', 'track' or 'ui', and some of the
 * other properties are considered required according to the type.
 *
 * For events with type 'page', the required properties are: name.
 *
 * For events with type 'operational' and 'ui', the required properties are:
 * action, actionSubject, source
 *
 * For events with type 'page', the required properties are:
 * action, actionSubject, source, containerType, containerId, objectType,
 * objectId
 */
class AnalyticsAdapter {
    /**
     * Creates new AnalyticsAdapter instance.
     */
    constructor() {
        /**
         * Whether this AnalyticsAdapter has been disposed of or not. Once this
         * is set to true, the AnalyticsAdapter is disabled and does not accept
         * any more events, and it can not be re-enabled.
         * @type {boolean}
         */
        this.disposed = false;

        /**
         * The set of handlers to which events will be sent.
         * @type {Set<any>}
         */
        this.analyticsHandlers = new Set();

        /**
         * The cache of events which are not sent yet. The cache is enabled
         * while this field is truthy, and disabled otherwise.
         * @type {Array}
         */
        this.cache = [];

        /**
         * Map of properties that will be added to every event. Note that the
         * keys will be prefixed with "permanent.".
         */
        this.permanentProperties = {};

        /**
         * The name of the conference that this AnalyticsAdapter is associated
         * with.
         * @type {null}
         */
        this.conferenceName = '';

        this.addPermanentProperties({
            'callstats_name': Settings.callStatsUserName,
            'user_agent': navigator.userAgent,
            'browser_name': browser.getName()
        });
    }

    /**
     * Dispose analytics. Clears all handlers.
     */
    dispose() {
        logger.warn('Disposing of analytics adapter.');
        this.setAnalyticsHandlers([]);
        this.disposed = true;
    }

    /**
     * Sets the handlers that are going to be used to send analytics. Sends any
     * cached events.
     * @param {Array} handlers the handlers
     */
    setAnalyticsHandlers(handlers) {
        if (this.disposed) {
            return;
        }

        this.analyticsHandlers = new Set(handlers);
        this.analyticsHandlers.forEach(
            handler => {
                handler.setUserProperties(this.permanentProperties);
            });

        // Note that we disable the cache even if the set of handlers is empty.
        const cache = this.cache;

        this.cache = null;
        if (cache) {
            cache.forEach(event => this._sendEvent(event));
        }
    }

    /**
     * Adds a set of permanent properties to this this AnalyticsAdapter.
     * Permanent properties will be added as "attributes" to events sent to
     * the underlying "analytics handlers", and their keys will be prefixed
     * by "permanent_", i.e. adding a permanent property {key: "value"} will
     * result in {"permanent_key": "value"} object to be added to the
     * "attributes" field of events.
     *
     * @param {Object} properties the properties to add
     */
    addPermanentProperties(properties) {
        this.permanentProperties = {
            ...this.permanentProperties,
            ...properties
        };

        this.analyticsHandlers.forEach(handler => {
            handler.setUserProperties(this.permanentProperties);
        });
    }

    /**
     * Sets the name of the conference that this AnalyticsAdapter is associated
     * with.
     * @param name the name to set.
     */
    setConferenceName(name) {
        this.conferenceName = name;
        this.addPermanentProperties({ 'conference_name': name });
    }

    /**
     * Sends an event with a given name and given properties. The first
     * parameter is either a string or an object. If it is a string, it is used
     * as the event name and the second parameter is used at the attributes to
     * attach to the event. If it is an object, it represents the whole event,
     * including any desired attributes, and the second parameter is ignored.
     *
     * @param {String|Object} eventName either a string to be used as the name
     * of the event, or an event object. If an event object is passed, the
     * properties parameters is ignored.
     * @param {Object} properties the properties/attributes to attach to the
     * event, if eventName is a string.
     */
    sendEvent(eventName, properties = {}) {
        if (this.disposed) {
            return;
        }

        let event = null;

        if (typeof eventName === 'string') {
            event = {
                type: TYPE_OPERATIONAL,
                action: eventName,
                actionSubject: eventName,
                source: eventName,
                attributes: properties
            };
        } else if (typeof eventName === 'object') {
            event = eventName;
        }

        if (!this._verifyRequiredFields(event)) {
            logger.error(
                `Dropping a mis-formatted event: ${JSON.stringify(event)}`);

            return;
        }

        this._sendEvent(event);
    }

    /**
     * Checks whether an event has all of the required fields set, and tries
     * to fill in some of the missing fields with reasonable default values.
     * Returns true if after this operation the event has all of the required
     * fields set, and false otherwise (if some of the required fields were not
     * set and the attempt to fill them in with a default failed).
     *
     * @param event the event object.
     * @return {boolean} true if the event (after the call to this function)
     * contains all of the required fields, and false otherwise.
     * @private
     */
    _verifyRequiredFields(event) {
        if (!event) {
            return false;
        }

        if (!event.type) {
            event.type = TYPE_OPERATIONAL;
        }

        const type = event.type;

        if (type !== TYPE_OPERATIONAL && type !== TYPE_PAGE
            && type !== TYPE_UI && type !== TYPE_TRACK) {
            logger.error(`Unknown event type: ${type}`);

            return false;
        }

        if (type === TYPE_PAGE) {
            return Boolean(event.name);
        }

        // Try to set some reasonable default values in case some of the
        // parameters required by the handler API are missing.
        event.action = event.action || event.name || event.actionSubject;
        event.actionSubject = event.actionSubject || event.name || event.action;
        event.source = event.source || event.name || event.action
            || event.actionSubject;

        if (!event.action || !event.actionSubject || !event.source) {
            logger.error(
                'Required field missing (action, actionSubject or source)');

            return false;
        }

        // Track events have additional required fields.
        if (type === TYPE_TRACK) {
            event.objectType = event.objectType || 'generic-object-type';
            event.containerType = event.containerType || 'conference';
            if (event.containerType === 'conference' && !event.containerId) {
                event.containerId = this.conferenceName;
            }

            if (!event.objectType || !event.objectId
                || !event.containerType || !event.containerId) {
                logger.error(
                    'Required field missing (containerId, containerType, '
                        + 'objectId or objectType)');

                return false;
            }
        }

        return true;
    }

    /**
     * Saves an event to the cache, if the cache is enabled.
     * @param event the event to save.
     * @returns {boolean} true if the event was saved, and false otherwise (i.e.
     * if the cache was disabled).
     * @private
     */
    _maybeCacheEvent(event) {
        if (this.cache) {
            this.cache.push(event);

            // We limit the size of the cache, in case the user fails to ever
            // set the analytics handlers.
            if (this.cache.length > MAX_CACHE_SIZE) {
                this.cache.splice(0, 1);
            }

            return true;
        }

        return false;

    }

    /**
     *
     * @param event
     * @private
     */
    _sendEvent(event) {
        if (this._maybeCacheEvent(event)) {
            // The event was consumed by the cache.
        } else {
            this.analyticsHandlers.forEach(handler => {
                try {
                    handler.sendEvent(event);
                } catch (e) {
                    logger.warn(`Error sending analytics event: ${e}`);
                }
            });
        }
    }
}

export default new AnalyticsAdapter();
<commit_msg>fix(analytics): add try/catch for setUserProperties.<commit_after>import {
    TYPE_OPERATIONAL,
    TYPE_PAGE,
    TYPE_TRACK,
    TYPE_UI
} from '../../service/statistics/AnalyticsEvents';
import { getLogger } from 'jitsi-meet-logger';
import browser from '../browser';
import Settings from '../settings/Settings';

const MAX_CACHE_SIZE = 100;

// eslist-disable-line no-undef
const logger = getLogger(__filename);

/**
 * This class provides an API to lib-jitsi-meet and its users for sending
 * analytics events. It serves as a bridge to different backend implementations
 * ("analytics handlers") and a cache for events attempted to be sent before
 * the analytics handlers were enabled.
 *
 * The API is designed to be an easy replacement for the previous version of
 * this adapter, and is meant to be extended with more convenience methods.
 *
 *
 * The API calls are translated to objects with the following structure, which
 * are then passed to the sendEvent(event) function of the underlying handlers:
 *
 * {
 *    type,
 *
 *    action,
 *    actionSubject,
 *    actionSubjectId,
 *    attributes,
 *    categories,
 *    containerId,
 *    containerType,
 *    name,
 *    objectId,
 *    objectType,
 *    source,
 *    tags
 * }
 *
 * The 'type' is one of 'operational', 'page', 'track' or 'ui', and some of the
 * other properties are considered required according to the type.
 *
 * For events with type 'page', the required properties are: name.
 *
 * For events with type 'operational' and 'ui', the required properties are:
 * action, actionSubject, source
 *
 * For events with type 'page', the required properties are:
 * action, actionSubject, source, containerType, containerId, objectType,
 * objectId
 */
class AnalyticsAdapter {
    /**
     * Creates new AnalyticsAdapter instance.
     */
    constructor() {
        /**
         * Whether this AnalyticsAdapter has been disposed of or not. Once this
         * is set to true, the AnalyticsAdapter is disabled and does not accept
         * any more events, and it can not be re-enabled.
         * @type {boolean}
         */
        this.disposed = false;

        /**
         * The set of handlers to which events will be sent.
         * @type {Set<any>}
         */
        this.analyticsHandlers = new Set();

        /**
         * The cache of events which are not sent yet. The cache is enabled
         * while this field is truthy, and disabled otherwise.
         * @type {Array}
         */
        this.cache = [];

        /**
         * Map of properties that will be added to every event. Note that the
         * keys will be prefixed with "permanent.".
         */
        this.permanentProperties = {};

        /**
         * The name of the conference that this AnalyticsAdapter is associated
         * with.
         * @type {null}
         */
        this.conferenceName = '';

        this.addPermanentProperties({
            'callstats_name': Settings.callStatsUserName,
            'user_agent': navigator.userAgent,
            'browser_name': browser.getName()
        });
    }

    /**
     * Dispose analytics. Clears all handlers.
     */
    dispose() {
        logger.warn('Disposing of analytics adapter.');
        this.setAnalyticsHandlers([]);
        this.disposed = true;
    }

    /**
     * Sets the handlers that are going to be used to send analytics. Sends any
     * cached events.
     * @param {Array} handlers the handlers
     */
    setAnalyticsHandlers(handlers) {
        if (this.disposed) {
            return;
        }

        this.analyticsHandlers = new Set(handlers);

        this._setUserProperties();

        // Note that we disable the cache even if the set of handlers is empty.
        const cache = this.cache;

        this.cache = null;
        if (cache) {
            cache.forEach(event => this._sendEvent(event));
        }
    }

    /**
     * Set the user properties to the analytics handlers.
     *
     * @returns {void}
     */
    _setUserProperties() {
        this.analyticsHandlers.forEach(handler => {
            try {
                handler.setUserProperties(this.permanentProperties);
            } catch (error) {
                logger.warn('Error in setUserProperties method of one of the '
                    + `analytics handlers: ${error}`);
            }
        });
    }

    /**
     * Adds a set of permanent properties to this this AnalyticsAdapter.
     * Permanent properties will be added as "attributes" to events sent to
     * the underlying "analytics handlers", and their keys will be prefixed
     * by "permanent_", i.e. adding a permanent property {key: "value"} will
     * result in {"permanent_key": "value"} object to be added to the
     * "attributes" field of events.
     *
     * @param {Object} properties the properties to add
     */
    addPermanentProperties(properties) {
        this.permanentProperties = {
            ...this.permanentProperties,
            ...properties
        };

        this._setUserProperties();
    }

    /**
     * Sets the name of the conference that this AnalyticsAdapter is associated
     * with.
     * @param name the name to set.
     */
    setConferenceName(name) {
        this.conferenceName = name;
        this.addPermanentProperties({ 'conference_name': name });
    }

    /**
     * Sends an event with a given name and given properties. The first
     * parameter is either a string or an object. If it is a string, it is used
     * as the event name and the second parameter is used at the attributes to
     * attach to the event. If it is an object, it represents the whole event,
     * including any desired attributes, and the second parameter is ignored.
     *
     * @param {String|Object} eventName either a string to be used as the name
     * of the event, or an event object. If an event object is passed, the
     * properties parameters is ignored.
     * @param {Object} properties the properties/attributes to attach to the
     * event, if eventName is a string.
     */
    sendEvent(eventName, properties = {}) {
        if (this.disposed) {
            return;
        }

        let event = null;

        if (typeof eventName === 'string') {
            event = {
                type: TYPE_OPERATIONAL,
                action: eventName,
                actionSubject: eventName,
                source: eventName,
                attributes: properties
            };
        } else if (typeof eventName === 'object') {
            event = eventName;
        }

        if (!this._verifyRequiredFields(event)) {
            logger.error(
                `Dropping a mis-formatted event: ${JSON.stringify(event)}`);

            return;
        }

        this._sendEvent(event);
    }

    /**
     * Checks whether an event has all of the required fields set, and tries
     * to fill in some of the missing fields with reasonable default values.
     * Returns true if after this operation the event has all of the required
     * fields set, and false otherwise (if some of the required fields were not
     * set and the attempt to fill them in with a default failed).
     *
     * @param event the event object.
     * @return {boolean} true if the event (after the call to this function)
     * contains all of the required fields, and false otherwise.
     * @private
     */
    _verifyRequiredFields(event) {
        if (!event) {
            return false;
        }

        if (!event.type) {
            event.type = TYPE_OPERATIONAL;
        }

        const type = event.type;

        if (type !== TYPE_OPERATIONAL && type !== TYPE_PAGE
            && type !== TYPE_UI && type !== TYPE_TRACK) {
            logger.error(`Unknown event type: ${type}`);

            return false;
        }

        if (type === TYPE_PAGE) {
            return Boolean(event.name);
        }

        // Try to set some reasonable default values in case some of the
        // parameters required by the handler API are missing.
        event.action = event.action || event.name || event.actionSubject;
        event.actionSubject = event.actionSubject || event.name || event.action;
        event.source = event.source || event.name || event.action
            || event.actionSubject;

        if (!event.action || !event.actionSubject || !event.source) {
            logger.error(
                'Required field missing (action, actionSubject or source)');

            return false;
        }

        // Track events have additional required fields.
        if (type === TYPE_TRACK) {
            event.objectType = event.objectType || 'generic-object-type';
            event.containerType = event.containerType || 'conference';
            if (event.containerType === 'conference' && !event.containerId) {
                event.containerId = this.conferenceName;
            }

            if (!event.objectType || !event.objectId
                || !event.containerType || !event.containerId) {
                logger.error(
                    'Required field missing (containerId, containerType, '
                        + 'objectId or objectType)');

                return false;
            }
        }

        return true;
    }

    /**
     * Saves an event to the cache, if the cache is enabled.
     * @param event the event to save.
     * @returns {boolean} true if the event was saved, and false otherwise (i.e.
     * if the cache was disabled).
     * @private
     */
    _maybeCacheEvent(event) {
        if (this.cache) {
            this.cache.push(event);

            // We limit the size of the cache, in case the user fails to ever
            // set the analytics handlers.
            if (this.cache.length > MAX_CACHE_SIZE) {
                this.cache.splice(0, 1);
            }

            return true;
        }

        return false;

    }

    /**
     *
     * @param event
     * @private
     */
    _sendEvent(event) {
        if (this._maybeCacheEvent(event)) {
            // The event was consumed by the cache.
        } else {
            this.analyticsHandlers.forEach(handler => {
                try {
                    handler.sendEvent(event);
                } catch (e) {
                    logger.warn(`Error sending analytics event: ${e}`);
                }
            });
        }
    }
}

export default new AnalyticsAdapter();
<|endoftext|>


<commit_before>/*
Copyright (c) 2015, salesforce.com, inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable indent */

// This function will deliver an error message to the browser console about the removal of a property.
import warning from 'warning';

let deprecated = function () {};

if (process.env.NODE_ENV !== 'production') {
	const hasWarned = {};

	deprecated = function (control, propValue, oldProp, newProp, comment) {
		const additionalComment = comment ? ` ${comment}` : '';
		const newProperty = newProp ? `Use \`${newProp}\`` : '';
		if (!hasWarned[control + oldProp]) {
			/* eslint-disable max-len */
			warning(!propValue, `[Design System React] \`${oldProp}\` will be deprecated in the next major version of ${control}. ${newProperty} instead.${additionalComment}`);
			/* eslint-enable max-len */
			hasWarned[control + oldProp] = !!propValue;
		}
	};
}

export default deprecated;
<commit_msg>Make deprecated warning test if undefined<commit_after>/*
Copyright (c) 2015, salesforce.com, inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable indent */

// This function will deliver an error message to the browser console about the removal of a property.
import warning from 'warning';

let deprecated = function () {};

if (process.env.NODE_ENV !== 'production') {
	const hasWarned = {};

	deprecated = function (control, propValue, oldProp, newProp, comment) {
		const additionalComment = comment ? ` ${comment}` : '';
		const newProperty = newProp ? `Use \`${newProp}\`` : '';
		if (!hasWarned[control + oldProp]) {
			/* eslint-disable max-len */
			warning(propValue === undefined, `[Design System React] \`${oldProp}\` will be deprecated in the next major version of ${control}. ${newProperty} instead.${additionalComment}`);
			/* eslint-enable max-len */
			hasWarned[control + oldProp] = propValue !== undefined;
		}
	};
}

export default deprecated;
<|endoftext|>


<commit_before>    $(document).ready(function() {

        $('#figshareAddKey').on('click', function() {
            var self = $(this)[0];
            $.ajax({
                type: 'POST',
                url: nodeApiUrl + 'figshare/user_auth/',
                contentType: 'application/json',
                dataType: 'json',
                success: function(response) {
                  if(self.outerText == 'Authorize: Create Access Token')
                      window.location.href = nodeApiUrl + 'figshare/oauth/';
                  else
                      window.location.reload();
                }
            });

        });

        $('#figshareDelKey').on('click', function() {
            bootbox.confirm(
                'Are you sure you want to delete your Figshare access key? This will ' +
                'revoke the ability to modify and upload files to Figshare. If ' +
                'the associated repo is private, this will also disable viewing ' +
                'and downloading files from Figshare.',
                function(result) {
                    if (result) {
                        $.ajax({
                            url: nodeApiUrl + 'figshare/oauth/',
                            type: 'DELETE',
                            contentType: 'application/json',
                            dataType: 'json',
                            success: function() {
                                window.location.reload();
                            }
                        });
                    }
                }
            );
        });

        $('#figshareSelectProject').on('change', function() {
            var value = $(this).val();
            if (value) {
                $('#figshareId').val(value)
                $('#figshareTitle').val($('#figshareSelectProject option:selected').text())
            }
        });

    });
<commit_msg>Fixed project switching<commit_after>    $(document).ready(function() {

        $('#figshareAddKey').on('click', function() {
            var self = $(this)[0];
            $.ajax({
                type: 'POST',
                url: nodeApiUrl + 'figshare/user_auth/',
                contentType: 'application/json',
                dataType: 'json',
                success: function(response) {
                  if(self.outerText == 'Authorize: Create Access Token')
                      window.location.href = nodeApiUrl + 'figshare/oauth/';
                  else
                      window.location.reload();
                }
            });

        });

        $('#figshareDelKey').on('click', function() {
            bootbox.confirm(
                'Are you sure you want to delete your Figshare access key? This will ' +
                'revoke the ability to modify and upload files to Figshare. If ' +
                'the associated repo is private, this will also disable viewing ' +
                'and downloading files from Figshare.',
                function(result) {
                    if (result) {
                        $.ajax({
                            url: nodeApiUrl + 'figshare/oauth/',
                            type: 'DELETE',
                            contentType: 'application/json',
                            dataType: 'json',
                            success: function() {
                                window.location.reload();
                            }
                        });
                    }
                }
            );
        });

        $('#figshareSelectProject').on('change', function() {
            var value = $(this).val();
            if (value) {
                $('#figshareId').val(value)
                $('#figshareTitle').val($('#figshareSelectProject option:selected').text())
            }
        });

        $('#addonSettingsFigshare .addon-settings-submit').on('click', function() {
            if ($('#figshareId').val() == '-----') {
                return false;
            }
        });
    });
<|endoftext|>


<commit_before>/* global describe, it, before, beforeEach, after, afterEach */
var should = require('should');

var state = require('state');

// Load the state machine definition
var smUri = require('../uri');

describe('URI state machine', function() {
  // plain javascript object
  var uri = {};

  // wrap the object with a state machine
  before(function() {
    state(uri, smUri);
  });

  it('makes objects stateful', function() {
    should.exist(uri.state);
    uri.state.should.be.Function;
  });
});
<commit_msg>Tests: Retrieving the currect state's name<commit_after>/* global describe, it, before, beforeEach, after, afterEach */
var should = require('should');

var state = require('state');

// Load the state machine definition
var smUri = require('../uri');

describe('URI state machine', function() {
  // plain javascript object
  var uri = {};

  // wrap the object with a state machine
  before(function() {
    state(uri, smUri);
  });

  it('makes objects stateful', function() {
    should.exist(uri.state);
    uri.state.should.be.Function;
  });

  it('should have Initial state', function() {
    uri.state().name.should.equal('Initial');
  });
});
<|endoftext|>


<commit_before>var async = require('async');

var Transaction = require('../models/Transaction');
var Message = require('../models/Message');
var Review = require('../models/Review');
var activities = require('../config/activities');
var Enums = require('../models/Enums');
var messageController = require('../controllers/message');
var helpers = require('./helpers');
var moment = require('moment');

function dateString (date) {
    return [date.getDate(), date.getMonth() + 1, date.getFullYear()].join('/');
}

function getMessagesForTransaction (t_id, user_id, callback) {
    Message.find({'_transaction': t_id})
    .sort('timeSent')
    .populate('_sender')
    .exec(function (err, messages) {
        if (err) {
          callback(err);
        } else {
            callback(null, messages.map(function (message) {
                return {
                    message: message.message,
                    timeSent: message.timeSent,
                    author: {
                        name: message._sender.profile.name,
                        pic: message._sender.profile.picture,
                        id: message._sender._id,
                        isMe: (message._sender._id == user_id),
                    },
                };
            }));
        }
    });
}

exports.getMessages = function (req, res) {
    getMessagesForTransaction(req.params.id, req.user.id, helpers.respondToAjax(res));
}

exports.getReviews = function (req, res) {
    Review.find({'_transaction': req.params.id})
    .populate('_creator')
    .exec(function (err, reviews) {
        if (err) {
            res.json({error: err});
            return;
        }
        var curr_user_review = reviews.find((review) => review._creator._id == req.user.id);
        // If there is a review that is not ours, it must be another review about us
        var other_review = reviews.find((review) => review._creator._id != req.user.id);
        if (!curr_user_review) {
            res.json({curr_user_has_review: false});
            return;
        } else if (!other_review) {
            res.json({partner_has_review: false});
            return;
        } else {
            res.json({
                review: {
                    author: {
                        name: other_review._creator.profile.name,
                        picture: other_review._creator.profile.picture,
                        id: other_review._creator._id,
                    },
                    text: other_review.text,
                    rating: other_review.rating,
                    timestamp: moment(other_review.timeSent).startOf('day').fromNow(),
                }
            });
        }
    });
}

/**
 * GET /transactions
 * Show transactions for current user
 */
exports.showTransactions = function(req, res) {
  Transaction.find({$or: [{_creator: req.user.id}, {_participants: req.user.id}]})
    .populate('_creator')
    .populate('_participants')
    .exec(function (err, transactions) {
        transactions.forEach(function (t) {
            t.service_label = activities.getActivityLabelForName(t.service);
        });
        var data = transactions.reduce(function (map, t) {
            if (t.status === Enums.StatusType.PROPOSED) {
                map.proposed.push(t);
            } else if (t.status === Enums.StatusType.ACCEPTED) {
                map.upcoming.push(t);
            } else if (t.status === Enums.StatusType.REJECTED) {
              // Don't show rejected exchanges
            } else {
                map.complete.push(t);
            }
            return map;
        }, {
          'proposed': [],
          'upcoming': [],
          'complete': [],
        });

        res.render('users/transactions', {
            transactions: data,
            moment: moment,
            StatusType: Enums.StatusType,
        });
  });
};

/**
 * POST /submitReview
 * Add a review for a transaction
 */
exports.submitReview = function (req, res) {
    // FIXME: Add subject?
    var review = new Review({
        timeSent: new Date(),
        text: req.body.message,
        rating: req.body.rating,
        _transaction: req.body.t_id,
        _creator: req.user.id,
    });

    review.save(helpers.respondToAjax(res));
};

/**
 * GET /confirmExchange
 * Confirm that an exchange happened.
 */
exports.confirmExchange = function (req, res) {
    // FIXME: Add condition that current user is creator or participant
    Transaction.findById(req.params.id, function (err, transaction) {
        var partner_acknowledged_status, me_acknowledged_status;
        if (req.user.id.toString() == transaction._creator.toString()) {
            partner_acknowledged_status = Enums.StatusType.RECIPIENT_ACK;
            me_acknowledged_status = Enums.StatusType.SENDER_ACK;
        } else {
            partner_acknowledged_status = Enums.StatusType.SENDER_ACK;
            me_acknowledged_status = Enums.StatusType.RECIPIENT_ACK;
        }
        // The bulkWrite approach here ensures that avoid race conditions vis-a-vis
        // the updating of the 'status' field. See http://bit.ly/1pFiOVS
        Transaction.collection.bulkWrite(
            [
               { "updateOne": {
                   "filter": {
                       "_id": helpers.toObjectId(transaction.id),
                       "status": partner_acknowledged_status,
                   },
                   "update": {
                       "$set": { "status": Enums.StatusType.COMPLETE }
                   }
               }},
               { "updateOne": {
                   "filter": {
                       "_id": helpers.toObjectId(transaction.id),
                       "status": Enums.StatusType.ACCEPTED,
                   },
                   "update": {
                       "$set": { "status": me_acknowledged_status }
                   }
               }}
            ],
            { "ordered": false },
            function (err, data) {
                Transaction.findById(req.params.id, function (err, transaction) {
                    res.json({
                        status: transaction.status,
                    });
                });
            }
        );
    });
};

/**
 * POST /acceptRequest
 * Accept a request for an exchange.
 */
exports.postAccept = function (req, res) {
    // FIXME: Add condition that current user is not creator
    Transaction.findOneAndUpdate(
        {_id: req.body.id},
        {status: Enums.StatusType.ACCEPTED},
        {new: true},
        function (err, trans) {
            if (req.body.message) {
                messageController.addMessageToTransaction(req.user.id, req.body.message, trans._id, function () {});
            }
            res.json({
                id: trans._id,
                status: trans.status,
            });
        }
    );
};

/**
 * POST /rejectRequest
 * Accept a request for an exchange.
 */
exports.rejectRequest = function (req, res) {
    // FIXME: Add condition that current user is not creator
    Transaction.findOneAndUpdate(
      {_id: req.params.id,
        _participants: req.user.id},
      {
        status: Enums.StatusType.REJECTED,
      }, helpers.respondToAjax(res));
};

/**
 * POST /cancelRequest
 * Accept a request for an exchange.
 */
exports.cancelRequest = function (req, res) {
    Transaction.findOneAndUpdate(
      {_id: req.params.id,
        _creator: req.user.id},
      {
        status: Enums.StatusType.CANCELLED,
      }, helpers.respondToAjax(res));
};

/**
 * POST /transactions
 * Add a transaction for current user. This is the initial
 * request so status is PROPOSED.
 */
exports.postTransaction = function (req, res) {
    async.waterfall([
        function (callback) {
            var trans = new Transaction({
                timeSent: new Date(),
                service: req.body.service,
                _participants: [req.body.recipient, req.user.id],
                amount: 1,
                _creator: req.user.id,
                status: Enums.StatusType.PROPOSED,
            });
            trans.save(callback);
        },

        function (t, num, callback) {
            var message = req.body.message;
            if (message) {
              messageController.addMessageToTransaction(req.user.id, message, t._id, callback);
            } else {
              callback(null);
            }
        },
    ], helpers.respondToAjax(res));
};
<commit_msg>Add FIXME note<commit_after>// FIXME: make these const variables
var async = require('async');

var Transaction = require('../models/Transaction');
var Message = require('../models/Message');
var Review = require('../models/Review');
var activities = require('../config/activities');
var Enums = require('../models/Enums');
var messageController = require('../controllers/message');
var helpers = require('./helpers');
var moment = require('moment');

function dateString (date) {
    return [date.getDate(), date.getMonth() + 1, date.getFullYear()].join('/');
}

function getMessagesForTransaction (t_id, user_id, callback) {
    Message.find({'_transaction': t_id})
    .sort('timeSent')
    .populate('_sender')
    .exec(function (err, messages) {
        if (err) {
          callback(err);
        } else {
            callback(null, messages.map(function (message) {
                return {
                    message: message.message,
                    timeSent: message.timeSent,
                    author: {
                        name: message._sender.profile.name,
                        pic: message._sender.profile.picture,
                        id: message._sender._id,
                        isMe: (message._sender._id == user_id),
                    },
                };
            }));
        }
    });
}

exports.getMessages = function (req, res) {
    getMessagesForTransaction(req.params.id, req.user.id, helpers.respondToAjax(res));
}

exports.getReviews = function (req, res) {
    Review.find({'_transaction': req.params.id})
    .populate('_creator')
    .exec(function (err, reviews) {
        if (err) {
            res.json({error: err});
            return;
        }
        var curr_user_review = reviews.find((review) => review._creator._id == req.user.id);
        // If there is a review that is not ours, it must be another review about us
        var other_review = reviews.find((review) => review._creator._id != req.user.id);
        if (!curr_user_review) {
            res.json({curr_user_has_review: false});
            return;
        } else if (!other_review) {
            res.json({partner_has_review: false});
            return;
        } else {
            res.json({
                review: {
                    author: {
                        name: other_review._creator.profile.name,
                        picture: other_review._creator.profile.picture,
                        id: other_review._creator._id,
                    },
                    text: other_review.text,
                    rating: other_review.rating,
                    timestamp: moment(other_review.timeSent).startOf('day').fromNow(),
                }
            });
        }
    });
}

/**
 * GET /transactions
 * Show transactions for current user
 */
exports.showTransactions = function(req, res) {
  Transaction.find({$or: [{_creator: req.user.id}, {_participants: req.user.id}]})
    .populate('_creator')
    .populate('_participants')
    .exec(function (err, transactions) {
        transactions.forEach(function (t) {
            t.service_label = activities.getActivityLabelForName(t.service);
        });
        var data = transactions.reduce(function (map, t) {
            if (t.status === Enums.StatusType.PROPOSED) {
                map.proposed.push(t);
            } else if (t.status === Enums.StatusType.ACCEPTED) {
                map.upcoming.push(t);
            } else if (t.status === Enums.StatusType.REJECTED) {
              // Don't show rejected exchanges
            } else {
                map.complete.push(t);
            }
            return map;
        }, {
          'proposed': [],
          'upcoming': [],
          'complete': [],
        });

        res.render('users/transactions', {
            transactions: data,
            moment: moment,
            StatusType: Enums.StatusType,
        });
  });
};

/**
 * POST /submitReview
 * Add a review for a transaction
 */
exports.submitReview = function (req, res) {
    // FIXME: Add subject?
    var review = new Review({
        timeSent: new Date(),
        text: req.body.message,
        rating: req.body.rating,
        _transaction: req.body.t_id,
        _creator: req.user.id,
    });

    review.save(helpers.respondToAjax(res));
};

/**
 * GET /confirmExchange
 * Confirm that an exchange happened.
 */
exports.confirmExchange = function (req, res) {
    // FIXME: Add condition that current user is creator or participant
    Transaction.findById(req.params.id, function (err, transaction) {
        var partner_acknowledged_status, me_acknowledged_status;
        if (req.user.id.toString() == transaction._creator.toString()) {
            partner_acknowledged_status = Enums.StatusType.RECIPIENT_ACK;
            me_acknowledged_status = Enums.StatusType.SENDER_ACK;
        } else {
            partner_acknowledged_status = Enums.StatusType.SENDER_ACK;
            me_acknowledged_status = Enums.StatusType.RECIPIENT_ACK;
        }
        // The bulkWrite approach here ensures that avoid race conditions vis-a-vis
        // the updating of the 'status' field. See http://bit.ly/1pFiOVS
        Transaction.collection.bulkWrite(
            [
               { "updateOne": {
                   "filter": {
                       "_id": helpers.toObjectId(transaction.id),
                       "status": partner_acknowledged_status,
                   },
                   "update": {
                       "$set": { "status": Enums.StatusType.COMPLETE }
                   }
               }},
               { "updateOne": {
                   "filter": {
                       "_id": helpers.toObjectId(transaction.id),
                       "status": Enums.StatusType.ACCEPTED,
                   },
                   "update": {
                       "$set": { "status": me_acknowledged_status }
                   }
               }}
            ],
            { "ordered": false },
            function (err, data) {
                Transaction.findById(req.params.id, function (err, transaction) {
                    res.json({
                        status: transaction.status,
                    });
                });
            }
        );
    });
};

/**
 * POST /acceptRequest
 * Accept a request for an exchange.
 */
exports.postAccept = function (req, res) {
    // FIXME: Add condition that current user is not creator
    Transaction.findOneAndUpdate(
        {_id: req.body.id},
        {status: Enums.StatusType.ACCEPTED},
        {new: true},
        function (err, trans) {
            if (req.body.message) {
                messageController.addMessageToTransaction(req.user.id, req.body.message, trans._id, function () {});
            }
            res.json({
                id: trans._id,
                status: trans.status,
            });
        }
    );
};

/**
 * POST /rejectRequest
 * Accept a request for an exchange.
 */
exports.rejectRequest = function (req, res) {
    // FIXME: Add condition that current user is not creator
    Transaction.findOneAndUpdate(
      {_id: req.params.id,
        _participants: req.user.id},
      {
        status: Enums.StatusType.REJECTED,
      }, helpers.respondToAjax(res));
};

/**
 * POST /cancelRequest
 * Accept a request for an exchange.
 */
exports.cancelRequest = function (req, res) {
    Transaction.findOneAndUpdate(
      {_id: req.params.id,
        _creator: req.user.id},
      {
        status: Enums.StatusType.CANCELLED,
      }, helpers.respondToAjax(res));
};

/**
 * POST /transactions
 * Add a transaction for current user. This is the initial
 * request so status is PROPOSED.
 */
exports.postTransaction = function (req, res) {
    async.waterfall([
        function (callback) {
            var trans = new Transaction({
                timeSent: new Date(),
                service: req.body.service,
                _participants: [req.body.recipient, req.user.id],
                amount: 1,
                _creator: req.user.id,
                status: Enums.StatusType.PROPOSED,
            });
            trans.save(callback);
        },

        function (t, num, callback) {
            var message = req.body.message;
            if (message) {
              messageController.addMessageToTransaction(req.user.id, message, t._id, callback);
            } else {
              callback(null);
            }
        },
    ], helpers.respondToAjax(res));
};
<|endoftext|>


<commit_before>var Vue = require('vue');

describe('Vue.url', function () {

    it('data/:id', function () {

        expect(Vue.url('data/:id')).toBe('data');
        expect(Vue.url('data/:id', {id: 1})).toBe('data/1');

    });

    it('data{/id}', function () {

        expect(Vue.url('data{/id}')).toBe('data');
        expect(Vue.url('data{/id}', {id: 1})).toBe('data/1');

    });

    it('{+path}data', function () {

        expect(Vue.url('{+path}data')).toBe('data');
        expect(Vue.url('{+path}data', {path: 'path1/path2/'})).toBe('path1/path2/data');

    });

    it('{+base}data', function () {

        Vue.url.options.params.base = 'base/path/';
        expect(Vue.url('{+base}data')).toBe('base/path/data');

    });

});
<commit_msg>add url array testcase<commit_after>var Vue = require('vue');

describe('Vue.url', function () {

    it('data/:id', function () {

        expect(Vue.url('data/:id')).toBe('data');
        expect(Vue.url('data/:id', {id: 1})).toBe('data/1');

    });

    it('data{/id}', function () {

        expect(Vue.url('data{/id}')).toBe('data');
        expect(Vue.url('data{/id}', {id: 1})).toBe('data/1');

    });

    it('data{/array}', function () {

        expect(Vue.url('data{?array}')).toBe('data');
        expect(Vue.url('data{?array}', {array: [1,2,3]})).toBe('data?array=1,2,3');

    });

    it('{+path}data', function () {

        expect(Vue.url('{+path}data')).toBe('data');
        expect(Vue.url('{+path}data', {path: 'path1/path2/'})).toBe('path1/path2/data');

    });

    it('{+base}data', function () {

        Vue.url.options.params.base = 'base/path/';
        expect(Vue.url('{+base}data')).toBe('base/path/data');

    });

});
<|endoftext|>


<commit_before>// Copyright 2015 CareerBuilder, LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
//

//Converts Cloudseed 1 compatible stacks to cloudseed 2.0 stacks

var fs = require('fs');
var path = require('path');
var async = require('async');
global.config = require('../config.json');
var mysql = require('mysql');
var pool;
var dbName = global.config.DB.database;
if(global.config.DB.Type == 'mysql'|| global.config.DB.Type == 'aurora'){
  var pool = mysql.createPool(global.config.DB);
}

/*

if(process.argv.length !==3){
  console.error('please provide a partsfile');
  process.exit(1);
}

var filepath;
if(path.isAbsolute(process.argv[2])){
  filepath = process.argv[2];
}
else{
  filepath = path.join(process.cwd(), process.argv[2]);
}
*/

function replaceNames(obj, append){
  var newobj = {};
  for(var key in obj){
    newobj[key+""+append] = obj[key];
  }
  return newobj;
}

var types ={};
var stacks = [];

async.series([function(callback){
  pool.getConnection(function(err, connection) {
    if(err) {
      console.log('Error connecting to database');
      return callback(err);
    }
    var q = 'Select * from ' + mysql.escapeId(dbName + '.old_stacks')+' ORDER BY `Name`;';
    connection.query(q, function(err, results) {
      connection.release();
      if(err){
        return callback(err);
      }
      if(results.length<1){
        return callback('no stacks found!');
      }
      stacks = results.map(function(obj){
        obj.Template = JSON.parse(obj.Template);
        obj.Parts = JSON.parse(obj.Parts);
        return obj;
      });
      return callback();
    });
  });
}, function(callback){
  //Load new version of parts
  var partsdir = path.join(__dirname, '../Parts');
  var pfiles = fs.readdirSync(partsdir);
  var allparts = [];
  async.each(pfiles, function(f, cb){
    if(f.indexOf('.part')<0){
      allparts = allparts.concat(fs.readdirSync(path.join(partsdir, f)).map(function(x){return path.join(f, x);}));
      return cb();
    }
    else{
      allparts.push(f);
      return cb();
    }
  }, function(err){
    async.each(allparts, function(p, cb2){
      var part = JSON.parse(fs.readFileSync(path.join(partsdir, p)));
      if(p.indexOf('.subpart')>-1){
        part.Subpart = true;
      }
      if(p.indexOf('.subassembly')>-1){
        part.SubAssembly = true;
      }
      types[part.Type] = part;
      return cb2();
    }, function(err){
      if(err){
        return callback(err);
      }
      return callback();
    });
  });
}, function(callback){
  async.eachSeries(stacks, function(s, callback2){
    var cparts ={};
    //Read in partslist
    var parts = s.Parts;
    //foreach part, attempt to map to new version of part
    async.each(parts, function(ap, cb3){
      var newcount = ap.Count;
      var copy, type;
      if(!(ap.Type in types)){
        if(ap.Type == 'IGWRoute'){
          type = 'Route';
          copy = JSON.parse(JSON.stringify(types.Route));
        }
        else{
          console.log('No parallel for', ap.Type);
          return cb3();
        }
      }
      else{
        copy = JSON.parse(JSON.stringify(types[ap.Type]));
        type = ap.Type;
      }
      var connections = copy.Connections || {};
      var partstring = JSON.stringify(copy);
      for(var cond in copy.Conditions){
        var cre = new RegExp('"'+cond+'"', 'g');
        partstring = partstring.replace(cre, '"'+cond+''+newcount+'"');
      }
      var newresname = null;
      if(!copy.SubAssembly){
        newresname = ap.LogicalName;
      }
      for(var res in copy.Resources){
        var re = new RegExp('\\{\\s*"Ref"\\s*:\\s*'+ JSON.stringify(res) +'\\s*\\}', 'g');
        var re2 = new RegExp('\\{\\s*"Fn::GetAtt"\\s*:\\s*\\['+ JSON.stringify(res) +',\\s*', 'g');
        var re3 = new RegExp('"'+res+'\\|', 'g');
        partstring = partstring.replace(re, JSON.stringify({Ref:newresname||(res+""+newcount)}));
        partstring = partstring.replace(re2, '{"Fn::GetAtt":['+JSON.stringify(newresname||(res+""+newcount))+',');
        partstring = partstring.replace(re3, '"'+res+newcount+'|');
      }
      copy = JSON.parse(partstring);
      for(var resname in copy.Resources){
        copy.Resources[newresname||(resname+""+newcount)] = copy.Resources[resname];
        delete copy.Resources[resname];
      }
      copy.Outputs = replaceNames(copy.Outputs || {}, newcount);
      for(var par in copy.Parameters){
        var cpar = par;
        if(par == 'IGW'){
          cpar = 'Gateway';
        }
        if(par in ap.Definition.Parameters){
          if('Value' in ap.Definition.Parameters[par]){
            copy.Parameters[cpar].Value = ap.Definition.Parameters[par].Value;
            if('AllowedValues' in copy.Parameters[cpar]){
              for(var x=0; x<copy.Parameters[cpar].AllowedValues.length; x++){
                var av = copy.Parameters[cpar].AllowedValues[x];
                if(copy.Parameters[cpar].Value.toUpperCase()==av.toUpperCase()){
                  copy.Parameters[cpar].Value = av;
                  break;
                }
              }
            }
          }
          else{
            if(ap.Definition.Connections && ap.Definition.Connections.Substitutes && ap.Definition.Connections.Substitutes.length>0){
              for(var i=0; i<ap.Definition.Connections.Substitutes.length; i++){
                var sub = ap.Definition.Connections.Substitutes[i];
                if(sub.Parameter == par){
                  copy.Parameters[cpar].Value = sub.Reference;
                  break;
                }
              }
            }
          }
        }
      }
      var mod = {Type: type, Count: newcount, RefID:ap.LogicalName, Name:ap.LogicalName, Collapsed: false, Definition:copy, EditingName: false, Origin: 'Local'};
      if(ap.subparts){
        mod.subparts={};
        for(var subp in ap.subparts){
          mod.subparts[ap.LogicalName+'|'+subp] = ap.subparts[subp];
        }
      }
      //add to Parts object instead of List
      cparts[mod.RefID]= mod;
      return cb3();
    }, function(err){
      if(err){
        return callback2(err);
      }
      else{
        pool.getConnection(function(err, connection) {
          if(err) {
            return callback2(err);
          }
          var q ='INSERT INTO ' + mysql.escapeId(dbName + '.stacks')+' (`Name`, `Region`, `Ready`, `Template`, `Parts`) VALUES(?, ?, ?, ?, ?);';
          connection.query(q, [s.Name, s.Region, s.Ready||false, '{}', JSON.stringify(cparts)], function(err, result) {
            connection.release();
            if(err){
              return callback2(err);
            }
            return callback2();
          });
        });
      }
    });
  },function(err){
    if(err){
      return callback(err);
    }
    return callback();
  });
}], function(err){
  var returncode =0;
  if(err){
    console.log(err);
    returncode=1;
  }
  pool.end(function(error){
    return process.exit(returncode);
  });
});
<commit_msg>resolve boolean values to true booleans<commit_after>// Copyright 2015 CareerBuilder, LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
//

//Converts Cloudseed 1 compatible stacks to cloudseed 2.0 stacks

var fs = require('fs');
var path = require('path');
var async = require('async');
global.config = require('../config.json');
var mysql = require('mysql');
var pool;
var dbName = global.config.DB.database;
if(global.config.DB.Type == 'mysql'|| global.config.DB.Type == 'aurora'){
  var pool = mysql.createPool(global.config.DB);
}

/*

if(process.argv.length !==3){
  console.error('please provide a partsfile');
  process.exit(1);
}

var filepath;
if(path.isAbsolute(process.argv[2])){
  filepath = process.argv[2];
}
else{
  filepath = path.join(process.cwd(), process.argv[2]);
}
*/

function replaceNames(obj, append){
  var newobj = {};
  for(var key in obj){
    newobj[key+""+append] = obj[key];
  }
  return newobj;
}

var types ={};
var stacks = [];

async.series([function(callback){
  pool.getConnection(function(err, connection) {
    if(err) {
      console.log('Error connecting to database');
      return callback(err);
    }
    var q = 'Select * from ' + mysql.escapeId(dbName + '.old_stacks')+' ORDER BY `Name`;';
    connection.query(q, function(err, results) {
      connection.release();
      if(err){
        return callback(err);
      }
      if(results.length<1){
        return callback('no stacks found!');
      }
      stacks = results.map(function(obj){
        obj.Template = JSON.parse(obj.Template);
        obj.Parts = JSON.parse(obj.Parts);
        return obj;
      });
      return callback();
    });
  });
}, function(callback){
  //Load new version of parts
  var partsdir = path.join(__dirname, '../Parts');
  var pfiles = fs.readdirSync(partsdir);
  var allparts = [];
  async.each(pfiles, function(f, cb){
    if(f.indexOf('.part')<0){
      allparts = allparts.concat(fs.readdirSync(path.join(partsdir, f)).map(function(x){return path.join(f, x);}));
      return cb();
    }
    else{
      allparts.push(f);
      return cb();
    }
  }, function(err){
    async.each(allparts, function(p, cb2){
      var part = JSON.parse(fs.readFileSync(path.join(partsdir, p)));
      if(p.indexOf('.subpart')>-1){
        part.Subpart = true;
      }
      if(p.indexOf('.subassembly')>-1){
        part.SubAssembly = true;
      }
      types[part.Type] = part;
      return cb2();
    }, function(err){
      if(err){
        return callback(err);
      }
      return callback();
    });
  });
}, function(callback){
  async.eachSeries(stacks, function(s, callback2){
    var cparts ={};
    //Read in partslist
    var parts = s.Parts;
    //foreach part, attempt to map to new version of part
    async.each(parts, function(ap, cb3){
      var newcount = ap.Count;
      var copy, type;
      if(!(ap.Type in types)){
        if(ap.Type == 'IGWRoute'){
          type = 'Route';
          copy = JSON.parse(JSON.stringify(types.Route));
        }
        else{
          console.log('No parallel for', ap.Type);
          return cb3();
        }
      }
      else{
        copy = JSON.parse(JSON.stringify(types[ap.Type]));
        type = ap.Type;
      }
      var connections = copy.Connections || {};
      var partstring = JSON.stringify(copy);
      for(var cond in copy.Conditions){
        var cre = new RegExp('"'+cond+'"', 'g');
        partstring = partstring.replace(cre, '"'+cond+''+newcount+'"');
      }
      var newresname = null;
      if(!copy.SubAssembly){
        newresname = ap.LogicalName;
      }
      for(var res in copy.Resources){
        var re = new RegExp('\\{\\s*"Ref"\\s*:\\s*'+ JSON.stringify(res) +'\\s*\\}', 'g');
        var re2 = new RegExp('\\{\\s*"Fn::GetAtt"\\s*:\\s*\\['+ JSON.stringify(res) +',\\s*', 'g');
        var re3 = new RegExp('"'+res+'\\|', 'g');
        partstring = partstring.replace(re, JSON.stringify({Ref:newresname||(res+""+newcount)}));
        partstring = partstring.replace(re2, '{"Fn::GetAtt":['+JSON.stringify(newresname||(res+""+newcount))+',');
        partstring = partstring.replace(re3, '"'+res+newcount+'|');
      }
      copy = JSON.parse(partstring);
      for(var resname in copy.Resources){
        copy.Resources[newresname||(resname+""+newcount)] = copy.Resources[resname];
        delete copy.Resources[resname];
      }
      copy.Outputs = replaceNames(copy.Outputs || {}, newcount);
      for(var par in copy.Parameters){
        var cpar = par;
        if(par == 'IGW'){
          cpar = 'Gateway';
        }
        if(par in ap.Definition.Parameters){
          if('Value' in ap.Definition.Parameters[par]){
            copy.Parameters[cpar].Value = ap.Definition.Parameters[par].Value;
            if('AllowedValues' in copy.Parameters[cpar]){
              for(var x=0; x<copy.Parameters[cpar].AllowedValues.length; x++){
                var av = copy.Parameters[cpar].AllowedValues[x];
                if(copy.Parameters[cpar].Value.toUpperCase()==av.toUpperCase()){
                  copy.Parameters[cpar].Value = av;
                  break;
                }
              }
            }
            if(copy.Parameters[cpar].Type == 'Boolean'){
              if(typeof copy.Parameters[cpar].Value == 'string'){
                copy.Parameters[cpar].Value = (copy.Parameters[cpar].Value.toUpperCase()=='TRUE');
              }
            }
          }
          else{
            if(ap.Definition.Connections && ap.Definition.Connections.Substitutes && ap.Definition.Connections.Substitutes.length>0){
              for(var i=0; i<ap.Definition.Connections.Substitutes.length; i++){
                var sub = ap.Definition.Connections.Substitutes[i];
                if(sub.Parameter == par){
                  copy.Parameters[cpar].Value = sub.Reference;
                  break;
                }
              }
            }
          }
        }
      }
      var mod = {Type: type, Count: newcount, RefID:ap.LogicalName, Name:ap.LogicalName, Collapsed: false, Definition:copy, EditingName: false, Origin: 'Local'};
      if(ap.subparts){
        mod.subparts={};
        for(var subp in ap.subparts){
          mod.subparts[ap.LogicalName+'|'+subp] = ap.subparts[subp];
        }
      }
      //add to Parts object instead of List
      cparts[mod.RefID]= mod;
      return cb3();
    }, function(err){
      if(err){
        return callback2(err);
      }
      else{
        pool.getConnection(function(err, connection) {
          if(err) {
            return callback2(err);
          }
          var q ='INSERT INTO ' + mysql.escapeId(dbName + '.stacks')+' (`Name`, `Region`, `Ready`, `Template`, `Parts`) VALUES(?, ?, ?, ?, ?);';
          connection.query(q, [s.Name, s.Region, s.Ready||false, '{}', JSON.stringify(cparts)], function(err, result) {
            connection.release();
            if(err){
              return callback2(err);
            }
            return callback2();
          });
        });
      }
    });
  },function(err){
    if(err){
      return callback(err);
    }
    return callback();
  });
}], function(err){
  var returncode =0;
  if(err){
    console.log(err);
    returncode=1;
  }
  pool.end(function(error){
    return process.exit(returncode);
  });
});
<|endoftext|>


<commit_before>const Hash = require('./data.hash');

class FilterManager {
  constructor() {
    this._filters = new Hash();
  }

  /**
   * @param {string} name
   * @param {function} func
   */
  register(name, func) {
    if (typeof func === 'function') {
      this._filters.add(name, func);
    }
  }

  /**
   * @param {string} name
   * @return {function}
   * @throws {Error}
   */
  get(name) {
    if (this._filters.has(name)) {
      return this._filters.get(name);
    }

    throw new Error('filter "' + name + '" not found');
  }
}

module.exports = FilterManager;
<commit_msg>Register 'h' and 'raw' in FilterManager._registerDefaultFilters()<commit_after>const Hash = require('./data.hash');

class FilterManager {
  constructor() {
    this._filters = new Hash();

    this._registerDefaultFilters();
  }

  /**
   * @param {string} name
   * @param {function} func
   */
  register(name, func) {
    if (typeof func === 'function') {
      this._filters.add(name, func);
    }
  }

  /**
   * @param {string} name
   * @return {function}
   * @throws {Error}
   */
  get(name) {
    if (this._filters.has(name)) {
      return this._filters.get(name);
    }

    throw new Error('filter "' + name + '" not found');
  }

  _registerDefaultFilters() {
    this.register('h', (() => {
      const list = {
        '<': '<',
        '>': '>',
        '&': '&',
        '"': '"',
        "'": ''', // eslint-disable-line quotes
      };

      return (value) => {
        return value.toString().replace(/[<>&"']/g, (matched) => {
          return list[matched];
        });
      };
    })());

    this.register('raw', (value) => {
      return value.toString();
    });
  }
}

module.exports = FilterManager;
<|endoftext|>